diff --git a/README.md b/README.md index d76d3d267..6aea45102 100644 --- a/README.md +++ b/README.md @@ -877,13 +877,13 @@ The MCP protocol defines three core primitives that servers can implement: MCP servers declare capabilities during initialization: -| Capability | Feature Flag | Description | -|-------------|------------------------------|------------------------------------| -| `prompts` | `listChanged` | Prompt template management | -| `resources` | `subscribe`
`listChanged`| Resource exposure and updates | -| `tools` | `listChanged` | Tool discovery and execution | -| `logging` | - | Server logging configuration | -| `completion`| - | Argument completion suggestions | +| Capability | Feature Flag | Description | +|--------------|------------------------------|------------------------------------| +| `prompts` | `listChanged` | Prompt template management | +| `resources` | `subscribe`
`listChanged`| Resource exposure and updates | +| `tools` | `listChanged` | Tool discovery and execution | +| `logging` | - | Server logging configuration | +| `completions`| - | Argument completion suggestions | ## Documentation diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py index b98e3dd1a..5386040be 100644 --- a/src/mcp/server/lowlevel/server.py +++ b/src/mcp/server/lowlevel/server.py @@ -186,6 +186,7 @@ def get_capabilities( resources_capability = None tools_capability = None logging_capability = None + completions_capability = None # Set prompt capabilities if handler exists if types.ListPromptsRequest in self.request_handlers: @@ -209,12 +210,17 @@ def get_capabilities( if types.SetLevelRequest in self.request_handlers: logging_capability = types.LoggingCapability() + # Set completions capabilities if handler exists + if types.CompleteRequest in self.request_handlers: + completions_capability = types.CompletionsCapability() + return types.ServerCapabilities( prompts=prompts_capability, resources=resources_capability, tools=tools_capability, logging=logging_capability, experimental=experimental_capabilities, + completions=completions_capability, ) @property diff --git a/src/mcp/types.py b/src/mcp/types.py index 4f5af27b9..5ad0ec463 100644 --- a/src/mcp/types.py +++ b/src/mcp/types.py @@ -267,6 +267,12 @@ class LoggingCapability(BaseModel): model_config = ConfigDict(extra="allow") +class CompletionsCapability(BaseModel): + """Capability for completions operations.""" + + model_config = ConfigDict(extra="allow") + + class ServerCapabilities(BaseModel): """Capabilities that a server may support.""" @@ -280,6 +286,10 @@ class ServerCapabilities(BaseModel): """Present if the server offers any resources to read.""" tools: ToolsCapability | None = None """Present if the server offers any tools to call.""" + completions: CompletionsCapability | None = None + """ + Present if the server offers autocompletion suggestions for prompts and resources. + """ model_config = ConfigDict(extra="allow") diff --git a/tests/server/test_session.py b/tests/server/test_session.py index 1375df12f..c6bca7395 100644 --- a/tests/server/test_session.py +++ b/tests/server/test_session.py @@ -11,8 +11,13 @@ from mcp.shared.session import RequestResponder from mcp.types import ( ClientNotification, + Completion, + CompletionArgument, + CompletionsCapability, InitializedNotification, + PromptReference, PromptsCapability, + ResourceReference, ResourcesCapability, ServerCapabilities, ) @@ -88,6 +93,7 @@ async def test_server_capabilities(): caps = server.get_capabilities(notification_options, experimental_capabilities) assert caps.prompts is None assert caps.resources is None + assert caps.completions is None # Add a prompts handler @server.list_prompts() @@ -97,6 +103,7 @@ async def list_prompts(): caps = server.get_capabilities(notification_options, experimental_capabilities) assert caps.prompts == PromptsCapability(listChanged=False) assert caps.resources is None + assert caps.completions is None # Add a resources handler @server.list_resources() @@ -106,6 +113,21 @@ async def list_resources(): caps = server.get_capabilities(notification_options, experimental_capabilities) assert caps.prompts == PromptsCapability(listChanged=False) assert caps.resources == ResourcesCapability(subscribe=False, listChanged=False) + assert caps.completions is None + + # Add a complete handler + @server.completion() + async def complete( + ref: PromptReference | ResourceReference, argument: CompletionArgument + ): + return Completion( + values=["completion1", "completion2"], + ) + + caps = server.get_capabilities(notification_options, experimental_capabilities) + assert caps.prompts == PromptsCapability(listChanged=False) + assert caps.resources == ResourcesCapability(subscribe=False, listChanged=False) + assert caps.completions == CompletionsCapability() @pytest.mark.anyio