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:
karl4c 2026-08-28 13:01:24 -04:00 • committed by GitHub
commit 40a8de87ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 92 additions and 1 deletions

View 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();
}
},
);
});

View file

@ -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();

View 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();