Merge pull request #3989 from mastertyko/fix/3914-mcp-sdk-js-subpaths
fix(mcp-server): use explicit sdk js subpaths
This commit is contained in:
commit
1b35b5ef44
3 changed files with 26 additions and 53 deletions
|
|
@ -19,10 +19,10 @@ export interface McpToolDef {
|
|||
// MCP SDK subpath imports use wildcard exports (./*) that NodeNext resolves
|
||||
// at runtime but TypeScript cannot statically type-check. We construct the
|
||||
// specifiers dynamically so tsc treats them as `any`.
|
||||
// Use createRequire to resolve wildcard subpaths — CJS resolver auto-appends
|
||||
// .js, which the ESM wildcard export map does not (#3603).
|
||||
import { createRequire } from 'node:module'
|
||||
const _require = createRequire(import.meta.url)
|
||||
//
|
||||
// Use explicit .js subpaths for modules that are loaded dynamically at runtime.
|
||||
// Recent Node / SDK combinations do not reliably resolve the extensionless
|
||||
// wildcard targets for `server/stdio` and `types` (#3914).
|
||||
const MCP_PKG = '@modelcontextprotocol/sdk'
|
||||
|
||||
/**
|
||||
|
|
@ -46,8 +46,8 @@ export async function startMcpServer(options: {
|
|||
const { tools, version = '0.0.0' } = options
|
||||
|
||||
const serverMod = await import(`${MCP_PKG}/server`)
|
||||
const stdioMod = await import(_require.resolve(`${MCP_PKG}/server/stdio`))
|
||||
const typesMod = await import(_require.resolve(`${MCP_PKG}/types`))
|
||||
const stdioMod = await import(`${MCP_PKG}/server/stdio.js`)
|
||||
const typesMod = await import(`${MCP_PKG}/types.js`)
|
||||
|
||||
const Server = serverMod.Server
|
||||
const StdioServerTransport = stdioMod.StdioServerTransport
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
/**
|
||||
* Regression test for #3603 — MCP server subpath imports via createRequire
|
||||
* Regression test for #3914 — MCP server uses explicit .js SDK subpaths.
|
||||
*
|
||||
* The ESM wildcard export map in @modelcontextprotocol/sdk does not resolve
|
||||
* subpath imports correctly. The fix uses createRequire from node:module to
|
||||
* resolve wildcard subpaths via the CJS resolver which auto-appends .js.
|
||||
*
|
||||
* Structural verification test — reads source to confirm createRequire import
|
||||
* and _require.resolve usage exist.
|
||||
* Extensionless wildcard exports for `server/stdio` and `types` do not resolve
|
||||
* reliably across current Node / SDK combinations. The runtime import strings
|
||||
* must include `.js`.
|
||||
*/
|
||||
|
||||
import { describe, test } from 'node:test';
|
||||
|
|
@ -20,29 +17,19 @@ const __dirname = dirname(__filename);
|
|||
|
||||
const source = readFileSync(join(__dirname, '..', 'mcp-server.ts'), 'utf-8');
|
||||
|
||||
describe('MCP server createRequire subpath resolution (#3603)', () => {
|
||||
test('createRequire is imported from node:module', () => {
|
||||
assert.match(source, /import\s*\{\s*createRequire\s*\}\s*from\s*['"]node:module['"]/,
|
||||
'createRequire should be imported from node:module');
|
||||
describe('MCP server SDK subpath imports (#3914)', () => {
|
||||
test('server/stdio import uses explicit .js subpath', () => {
|
||||
assert.match(source, /await import\(`\$\{MCP_PKG\}\/server\/stdio\.js`\)/,
|
||||
'server/stdio import should include the .js suffix');
|
||||
});
|
||||
|
||||
test('_require is created from import.meta.url', () => {
|
||||
assert.match(source, /createRequire\(import\.meta\.url\)/,
|
||||
'_require should be created using createRequire(import.meta.url)');
|
||||
test('types import uses explicit .js subpath', () => {
|
||||
assert.match(source, /await import\(`\$\{MCP_PKG\}\/types\.js`\)/,
|
||||
'types import should include the .js suffix');
|
||||
});
|
||||
|
||||
test('_require.resolve is used for subpath imports', () => {
|
||||
assert.match(source, /_require\.resolve\(/,
|
||||
'_require.resolve should be used for subpath resolution');
|
||||
});
|
||||
|
||||
test('server/stdio subpath uses _require.resolve', () => {
|
||||
assert.match(source, /_require\.resolve\(`\$\{MCP_PKG\}\/server\/stdio`\)/,
|
||||
'server/stdio import should use _require.resolve');
|
||||
});
|
||||
|
||||
test('types subpath uses _require.resolve', () => {
|
||||
assert.match(source, /_require\.resolve\(`\$\{MCP_PKG\}\/types`\)/,
|
||||
'types import should use _require.resolve');
|
||||
test('legacy createRequire-based resolution is gone', () => {
|
||||
assert.doesNotMatch(source, /createRequire|_require\.resolve/,
|
||||
'legacy createRequire-based subpath resolution should not remain');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,25 +30,11 @@ test('startMcpServer accepts the correct argument shape', async () => {
|
|||
assert.strictEqual(startMcpServer.length, 1, 'startMcpServer should accept one argument')
|
||||
})
|
||||
|
||||
test('startMcpServer can be called with mock tools', async () => {
|
||||
const { startMcpServer } = await import(distUrl('mcp-server.js'))
|
||||
test('compiled MCP runtime dependencies resolve with explicit .js subpaths', async () => {
|
||||
const stdioMod = await import('@modelcontextprotocol/sdk/server/stdio.js')
|
||||
const typesMod = await import('@modelcontextprotocol/sdk/types.js')
|
||||
|
||||
// Create a mock tool matching the McpToolDef interface
|
||||
const mockTool = {
|
||||
name: 'test_tool',
|
||||
description: 'A test tool',
|
||||
parameters: { type: 'object', properties: {} },
|
||||
execute: async () => ({
|
||||
content: [{ type: 'text', text: 'hello' }],
|
||||
}),
|
||||
}
|
||||
|
||||
// Verify the function can be called with the correct signature
|
||||
// without throwing during argument validation. It will attempt to
|
||||
// connect to stdin/stdout as an MCP transport, which won't work in
|
||||
// a test environment, but the Server instance is created successfully.
|
||||
assert.doesNotThrow(() => {
|
||||
void startMcpServer({ tools: [mockTool], version: '0.0.0-test' })
|
||||
.catch(() => { /* expected: no MCP client on stdin */ })
|
||||
})
|
||||
assert.strictEqual(typeof stdioMod.StdioServerTransport, 'function')
|
||||
assert.ok(typesMod.ListToolsRequestSchema, 'ListToolsRequestSchema should be exported')
|
||||
assert.ok(typesMod.CallToolRequestSchema, 'CallToolRequestSchema should be exported')
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue