McpServer re-registers capabilities after connect, blocking dynamic registration even when capabilities were supplied at construction
Opened this issue · 1 comments
Summary
When creating McpServer with ServerOptions.capabilities that include resources and tools, calling registerResource or registerTool after connect() fails with “Cannot register capabilities after connecting to transport”. The high-level server re-calls server.registerCapabilities(...) the first time handlers are installed, even post-connect.
Steps to Reproduce
- Create
McpServerwith capabilities:
const capabilities = {
resources: { subscribe: true, listChanged: true },
tools: { listChanged: true },
};
const server = new McpServer({ name: "demo", version: "1.0.0" }, { capabilities });await server.connect(transport);- After connect, call
server.registerResource(...)(orserver.registerTool(...)) - Observe HTTP 500 with error “Cannot register capabilities after connecting to transport”
Expected Behavior
- If capabilities were provided at construction, registering resources/tools/prompts after connect should work. The server should not attempt to re-register capabilities post-connect.
Actual Behavior
- First registration after connect triggers handler initialization in
McpServer, which unconditionally callsserver.registerCapabilities(...). SinceServer.registerCapabilitiesforbids post-connect, it throws.
Notes
- Calls occur in
typescript-sdk/src/server/mcp.tswithin:setResourceRequestHandlers()→this.server.registerCapabilities({ resources: { listChanged: true } })setToolRequestHandlers()→this.server.registerCapabilities({ tools: { listChanged: true } })setPromptRequestHandlers()→this.server.registerCapabilities({ prompts: { listChanged: true } })
- Guard that throws is in
typescript-sdk/src/server/index.tsregisterCapabilities().
Proposed Fix
- Make
this.server.registerCapabilities(...)idempotent if capabilities are already registered.
Environment
- @modelcontextprotocol/sdk 1.17.3
- Transport: Streamable HTTP (client error surfaced as HTTP 500)
@inverted-capital I faced the same issue when working on a project that needed to create tools/resources/prompts at runtime.
The workaround I used was to register dummies (1 of each type of tool/resource/prompt) before a transport is established. This seemed to enable the capability for each type in the SDK permitting later things to be registered even after connection. NOTE: They can even be removed after connection and things still work - though since they are disabled, it is fine to keep them around until other tools/resources/prompts are registered.
It is not ideal but its working well for me.