Skip to content

fix(server): track the current registry key when a handle is renamed - #2761

Open
krishnendu-2003 wants to merge 1 commit into
modelcontextprotocol:mainfrom
krishnendu-2003:fix/2723-remove-after-rename
Open

fix(server): track the current registry key when a handle is renamed#2761
krishnendu-2003 wants to merge 1 commit into
modelcontextprotocol:mainfrom
krishnendu-2003:fix/2723-remove-after-rename

Conversation

@krishnendu-2003

Copy link
Copy Markdown

This fixes an issue where renaming a registered prompt, resource, or resource template would break remove().

When a handle is renamed, the captured registry key is now updated as well. This means a later remove() deletes the entry from the key it actually lives under.

Fixes #2723.

Motivation and Context

Previously, calling remove() after renaming a handle with update() would silently do nothing.

The problem was that the update closure kept referencing the original registration key. After a rename, the entry had already moved to a new key, but remove() was still doing:

delete this._registeredX[key]

using the old key.

As a result, the renamed entry stayed registered, remained callable, and still appeared in list responses. A list_changed notification was also emitted even though nothing had actually been removed.

The stale key caused another issue as well. The updates.name !== name check could incorrectly prevent a handle from being renamed back to its original name.

RegisteredTool already handles this correctly by keeping track of its current registry key in the tool update closure in mcp.ts.

This change applies the same approach to:

  • Prompts
  • Resources
  • Resource templates

There is no new abstraction and no public API change.

How Has This Been Tested?

Added:

test/integration/test/issues/test2723.remove-after-rename.test.ts

The test covers four cases:

  • Rename a prompt, then call remove()
  • Rename a resource by URI, then call remove()
  • Rename a resource template, then call remove()
  • Rename a handle, rename it back to its original name, then call remove()

The tests verify the behaviour through the protocol using:

  • prompts/list
  • resources/list
  • resources/templates/list

rather than inspecting internal registry state.

All four cases fail on main and pass with this change.

The existing tests only covered updates to non-key fields, which is why this behaviour was not caught earlier.

Full local test results:

  • packages/server: 482 passing
  • test/integration: 374 passing
  • test/e2e: 2639 passing
  • typecheck:all: passing
  • lint:all: passing
  • build:all: passing

I also see two local failures that appear to be environmental and reproduce on an unmodified main branch:

  • protocol:timeout:max-total [sse]
    Appears related to SSE and fake-timer scheduling on macOS.

  • cloudflareWorkers
    Appears related to Wrangler cold start behaviour because workerd's build script is skipped by pnpm locally.

Neither test suite exercises the code changed in this PR.

Breaking Changes

There is no API change.

The only behavioural change is the bug fix itself: after a handle is renamed, calling remove() now actually removes it.

Previously, the renamed entry incorrectly remained registered. Any code relying on that behaviour was relying on the bug.

One related issue is intentionally left out of scope: renaming a handle onto an already occupied key currently has no duplicate-key protection, so the existing entry can be displaced.

I can open a separate issue for that if needed.

v1.x also has the same problem in all four registration closures, including tools. The RegisteredTool fix currently only exists on main.

I can open a backport PR for v1.x once this change lands.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the [MCP Documentation](https://modelcontextprotocol.io)
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

The update closure for prompts, resources and resource templates captured the
registration key and never reassigned it, so remove() deleted the key the entry
had already vacated and left the live entry listed and callable. RegisteredTool
already tracked its current key; these three now do too.

Fixes modelcontextprotocol#2723
@krishnendu-2003
krishnendu-2003 requested a review from a team as a code owner September 6, 2026 18:50
@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 34f91cb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

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

@pkg-pr-new

pkg-pr-new Bot commented Sep 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2761

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2761

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2761

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2761

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2761

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2761

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2761

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2761

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2761

commit: 34f91cb

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.

remove() is a no-op after a handle is renamed, and the entry stays callable

1 participant