fix(sequentialthinking): read server version from package.json (#4576)
* fix(sequentialthinking): read server version from package.json Replace the hardcoded 0.2.0 so initialize serverInfo tracks the package version (0.6.2 in-tree; CalVer after release), matching the approach used for memory server version sync. Skip the dist-layout assertion when dist/ is absent so CI's test-before-build job (and local runs without prepare) still pass. * test(sequentialthinking): smoke stdio initialize serverInfo.version Spawn the built server over stdio and assert getServerVersion() matches package.json (and is not the old hardcoded 0.2.0). Skip when dist/ is absent so test-before-build CI still passes.
This commit is contained in:
parent
11845d51cb
commit
40a8de87ed
3 changed files with 92 additions and 1 deletions
57
src/sequentialthinking/__tests__/server-version.test.ts
Normal file
57
src/sequentialthinking/__tests__/server-version.test.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { createRequire } from 'node:module';
|
||||
import { existsSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import { resolvePackageVersion, SERVER_VERSION } from '../version.js';
|
||||
|
||||
const packageJson = createRequire(import.meta.url)('../package.json') as { version: string };
|
||||
const packageRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const distVersionPath = path.join(packageRoot, 'dist', 'version.js');
|
||||
const distIndexPath = path.join(packageRoot, 'dist', 'index.js');
|
||||
|
||||
describe('server version', () => {
|
||||
it('uses package.json version instead of a hardcoded string', () => {
|
||||
expect(SERVER_VERSION).toBe(packageJson.version);
|
||||
expect(resolvePackageVersion()).toBe(packageJson.version);
|
||||
expect(SERVER_VERSION).not.toBe('0.2.0');
|
||||
});
|
||||
|
||||
// CI runs `npm test` before the dedicated build job. `npm ci` usually
|
||||
// materializes dist/ via prepare, but that is not guaranteed (e.g. local
|
||||
// `rm -rf dist && npm test`, or install with --ignore-scripts).
|
||||
it.skipIf(!existsSync(distVersionPath))(
|
||||
'resolves package.json from the dist layout after build',
|
||||
async () => {
|
||||
const distModule = (await import(pathToFileURL(distVersionPath).href)) as {
|
||||
SERVER_VERSION: string;
|
||||
};
|
||||
expect(distModule.SERVER_VERSION).toBe(packageJson.version);
|
||||
},
|
||||
);
|
||||
|
||||
it.skipIf(!existsSync(distIndexPath))(
|
||||
'stdio initialize reports package.json version in serverInfo',
|
||||
async () => {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [distIndexPath],
|
||||
cwd: packageRoot,
|
||||
stderr: 'pipe',
|
||||
});
|
||||
const client = new Client({ name: 'version-smoke', version: '0.0.0' });
|
||||
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const serverInfo = client.getServerVersion();
|
||||
expect(serverInfo?.name).toBe('sequential-thinking-server');
|
||||
expect(serverInfo?.version).toBe(packageJson.version);
|
||||
expect(serverInfo?.version).not.toBe('0.2.0');
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { z } from "zod";
|
||||
import { SequentialThinkingServer } from './lib.js';
|
||||
import { SERVER_VERSION } from './version.js';
|
||||
|
||||
/** Safe boolean coercion that correctly handles string "false" */
|
||||
const coercedBoolean = z.preprocess((val) => {
|
||||
|
|
@ -17,7 +18,7 @@ const coercedBoolean = z.preprocess((val) => {
|
|||
|
||||
const server = new McpServer({
|
||||
name: "sequential-thinking-server",
|
||||
version: "0.2.0",
|
||||
version: SERVER_VERSION,
|
||||
});
|
||||
|
||||
const thinkingServer = new SequentialThinkingServer();
|
||||
|
|
|
|||
33
src/sequentialthinking/version.ts
Normal file
33
src/sequentialthinking/version.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { createRequire } from 'node:module';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
/**
|
||||
* Resolve this package's version from package.json.
|
||||
*
|
||||
* Works both from source (`src/sequentialthinking/`) and from the published
|
||||
* layout (`dist/`), where package.json lives one directory up.
|
||||
*/
|
||||
export function resolvePackageVersion(): string {
|
||||
const require = createRequire(import.meta.url);
|
||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const candidates = [
|
||||
path.join(moduleDir, 'package.json'),
|
||||
path.join(moduleDir, '..', 'package.json'),
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
const pkg = require(candidate) as { version?: string };
|
||||
if (pkg.version) {
|
||||
return pkg.version;
|
||||
}
|
||||
} catch {
|
||||
// Try the next candidate when running from dist/ or source.
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Could not locate package.json for server version');
|
||||
}
|
||||
|
||||
export const SERVER_VERSION = resolvePackageVersion();
|
||||
Loading…
Reference in a new issue