Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/sequentialthinking/__tests__/server-version.test.ts
Original file line number Diff line number Diff line change
@@ -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();
}
},
);
});
3 changes: 2 additions & 1 deletion src/sequentialthinking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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();
Expand Down
33 changes: 33 additions & 0 deletions src/sequentialthinking/version.ts
Original file line number Diff line number Diff line change
@@ -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