Repository navigation
fix: give every STATIC-mode client its own server - #27
Conversation
STATIC mode shared one McpServer across all clients. The MCP SDK binds a
server to one transport, so each new client's initialize closed the previous
client's session ("Session not found or expired"), and module loaders only
ever saw the base context, so sessionContext had no effect.
The bundle factory now builds a fresh server + orchestrator per client in
both modes; STATIC client orchestrators preload the startup toolsets with
the client's merged context. The transport awaits orchestrator readiness
before connect() since the SDK refuses tool capability registration after
connecting. The base server still validates startup toolsets (fail-fast) and
backs the /tools status endpoint.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pqcus1BTWLVAJaPavwdkGX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pqcus1BTWLVAJaPavwdkGX
|
CI failed in Checked locally after the merge: Generated by Claude Code |
|
Go/no-go from the MCP SDK & spec review: GO Reviewed against Correct by SDK semantics
Compatibility with #26: I merged this branch onto #26 ( Non-blocking follow-ups
Generated by Claude Code |
The next release carries #25, #26 and #27. #27 changes STATIC mode to a server per client, so it ships as a minor version: consumers pinned to ^0.6.x keep 0.6.3 until they opt in. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pqcus1BTWLVAJaPavwdkGX
c02385d to
df9cb41
Compare
Requested by Ben · project thread
Before: in STATIC mode, only one client could be connected at a time. When a second client sent
initialize, the transport closed the shared server's current connection, so the first client's next request failed withSession not found or expired. Per-session context (?config=) also never reached module loaders in STATIC mode, because tools were loaded once with the base context. This hits the FMP MCP server's default mode, ALL_TOOLS, which runs on toolception STATIC.After: every client gets its own
McpServerand orchestrator in both modes. In STATIC mode, each client orchestrator preloads the startup toolsets using that client's merged session context. Concurrent clients stay connected, and session context works in STATIC mode.The MCP SDK binds one
McpServerto one transport. That's why the olddisconnectServer()step had to close another client's transport before connecting a new one.How:
createBundleFactoryalways builds a fresh server and orchestrator per bundle. The base server still validates the startup toolsets at boot (fail-fast) and backs/tools.FastifyTransportawaitsbundle.orchestrator.isReady()beforeconnect(). STATIC bundles load tools asynchronously, and the SDK refuses to register tool capabilities after connecting.src/server,src/http).Trade-off: STATIC module loaders now run once per client instead of once per process.
ClientResourceCachestill caps the number of bundles (1000, 1h TTL).Release note: STATIC consumers that register things on the server after
createMcpServerreturns, rather than insidecreateServer, won't see them on clients any more, so this belongs in a minor release (0.7.0). The version bump and changelog are in #28.Tests:
mainwithSession not found or expired.?config=tokens each see their own context. It fails onmain.npm run typecheck,npm run buildandnpm run test:runall pass (331 tests when merged with Register all tools with registerTool #26).?config=had its own token used.Pre-publish checks for 0.7.0 (main + #26 + this PR with the version set to 0.7.0, on Node 25.3.0 and npm 11.6.2 as in release.yml)
npm ci, the version and private check, typecheck, 331/331 tests and build.npm publish --dry-run --access public, which also runsprepublishOnly, exits 0 with+ toolception@0.7.0. The tarball is 99.2 kB with 61 files..d.tsdoc comments changed.skipLibCheck: falsecompiles with bothnodenextandbundlerresolution, and it starts and closes on Node 25.npm audit --omit=devfinds 0 vulnerabilities.verify:toolception).createServer.Not covered:
PermissionAwareFastifyTransportdoesn't await readiness beforeconnect()either. Permission-based servers already build a server per client, so the race is the same one this PR fixes. I left it for a follow-up to keep this change small.There's no file overlap with #25, #26 or #28 apart from nearby test files.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Pqcus1BTWLVAJaPavwdkGX