Skip to content

fix: allow registering tools/resources/prompts after connect when capabilities pre-declared (#893)#1666

Open
Vadaski wants to merge 3 commits intomodelcontextprotocol:mainfrom
Vadaski:fix/893-post-connect-registration
Open

fix: allow registering tools/resources/prompts after connect when capabilities pre-declared (#893)#1666
Vadaski wants to merge 3 commits intomodelcontextprotocol:mainfrom
Vadaski:fix/893-post-connect-registration

Conversation

@Vadaski
Copy link

@Vadaski Vadaski commented Mar 12, 2026

Summary

Fixes #893.

When McpServer is constructed with pre-declared capabilities (e.g. { capabilities: { tools: { listChanged: true } } }), calling registerTool() / registerResource() / registerPrompt() after connect() used to throw:

SdkError: Cannot register capabilities after connecting to transport

Because the first handler registration always called server.registerCapabilities() unconditionally, which fails post-connect.

Root Cause

setToolRequestHandlers(), setResourceRequestHandlers(), setPromptRequestHandlers(), and setCompletionRequestHandler() all unconditionally called this.server.registerCapabilities() on first invocation — even when those capabilities were already declared at construction time.

Fix

  • Introduce withDefaultListChangedCapabilities() helper that applies listChanged defaults when capabilities are pre-declared in options.
  • When capabilities are pre-declared, eagerly call the corresponding set*RequestHandlers() in the McpServer constructor (before connect() is ever called), so the handlers are already registered when the user later calls register*() after connect.
  • Guard every registerCapabilities() call inside set*RequestHandlers() and setCompletionRequestHandler() with !this.server.transport, so post-connect invocations skip the now-redundant re-registration.

Test Plan

  • New regression test file test/integration/test/issues/test893.post-connect-registration.test.ts — 5 tests covering:
    1. Register tool after connect when capabilities.tools pre-declared
    2. Register resource after connect when capabilities.resources pre-declared
    3. Register prompt after connect when capabilities.prompts pre-declared
    4. Register completable prompt after connect (tests setCompletionRequestHandler guard)
    5. Register completable resource template after connect
  • All 391 existing integration tests pass (pnpm test:all)
  • pnpm check:all passes (typecheck + lint)

🤖 Generated with Claude Code

…abilities pre-declared (modelcontextprotocol#893)

When McpServer is constructed with pre-declared capabilities (e.g.
`{ capabilities: { tools: { listChanged: true } } }`), calling
`registerTool()` / `registerResource()` / `registerPrompt()` after
`connect()` used to throw "Cannot register capabilities after connecting
to transport" because the first handler registration always called
`server.registerCapabilities()` unconditionally.

Fix:
- Pass capabilities through `withDefaultListChangedCapabilities()` so
  `listChanged` defaults are applied before `connect()`.
- Eagerly initialize the corresponding request handlers in the
  constructor when capabilities are pre-declared, so the lazy path in
  `set*RequestHandlers()` is already done before `connect()`.
- Guard every `registerCapabilities()` call inside the four
  `set*RequestHandlers()` / `setCompletionRequestHandler()` methods with
  `!this.server.transport`, so post-connect invocations skip the
  now-redundant re-registration.

Fixes modelcontextprotocol#893

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Vadaski Vadaski requested a review from a team as a code owner March 12, 2026 05:32
@changeset-bot
Copy link

changeset-bot bot commented Mar 12, 2026

⚠️ No Changeset found

Latest commit: b6fa68f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 12, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1666

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1666

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1666

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1666

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1666

commit: 6614172

Vadaski and others added 2 commits March 11, 2026 22:34
…lcontextprotocol#893)

{ listChanged: X ?? true, ...capabilities.tools } puts the default
first so the spread overwrites it — explicit undefined defeats the
default. Fix to { ...capabilities.tools, listChanged: X ?? true }
so the ?? true only applies when the user did not set listChanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

McpServer re-registers capabilities after connect, blocking dynamic registration even when capabilities were supplied at construction

1 participant