Skip to content

fix(mcpserver): advertise capabilities only for registered primitives#2499

Open
Zelys-DFKH wants to merge 1 commit intomodelcontextprotocol:mainfrom
Zelys-DFKH:fix/mcpserver-conditional-capabilities
Open

fix(mcpserver): advertise capabilities only for registered primitives#2499
Zelys-DFKH wants to merge 1 commit intomodelcontextprotocol:mainfrom
Zelys-DFKH:fix/mcpserver-conditional-capabilities

Conversation

@Zelys-DFKH
Copy link
Copy Markdown

Summary

  • MCPServer.__init__ unconditionally passes non-None list handlers to
    the lowlevel Server, causing get_capabilities() to always advertise
    tools, resources, and prompts, even when none are registered.
  • Adds an optional capability_filter parameter to the lowlevel Server
    that post-processes the computed ServerCapabilities.
  • MCPServer passes a filter that suppresses each entry when the
    corresponding manager is empty at capability-computation time.

Motivation and Context

Per the MCP schema spec and lifecycle docs, a ServerCapabilities entry should appear only when the server actually offers that primitive. Currently, an empty MCPServer advertises all three, and a server with only one tool still advertises resources and prompts it doesn't have.

The filter runs when create_initialization_options() is called at the start of run(), after all @mcp.tool() / @mcp.resource() / @mcp.prompt() decorators have been applied. Every transport path (stdio, SSE, streamable HTTP) calls server.create_initialization_options()get_capabilities(), so the filter applies without any per-transport changes.

How Has This Been Tested?

Six new tests in tests/server/mcpserver/test_server.py cover: empty server, tool-only, resource-only, resource-template-only, prompt-only, and all-registered cases.

uv run --frozen pytest tests/server/mcpserver/test_server.py -v

Full suite: ./scripts/test — 100% branch coverage.

Breaking Changes

None. Servers that previously over-advertised will now send correct capability sets.

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
  • 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

Closes #2473

MCPServer unconditionally passed non-None list handlers to the lowlevel
Server, which caused it to advertise tools/resources/prompts capabilities
even when none of those primitives had been registered.  Per the MCP
schema spec, a capability entry should only appear when the server
actually offers that primitive.

Adds a `capability_filter` hook to the lowlevel Server that, if set,
post-processes the computed ServerCapabilities before they are returned.
MCPServer uses this to suppress tools/resources/prompts entries when the
corresponding manager is empty at capability-computation time (i.e. when
create_initialization_options() is called, after all decorators have
been applied).

Fixes modelcontextprotocol#2473.

Co-Authored-By: Claude Sonnet 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 unconditionally declares prompts, resources, tools capabilities on initialize

1 participant