Skip to content

Commit 4e5d627

Browse files
committed
Add MCPServer.capabilities property.
1 parent 18743cd commit 4e5d627

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

pydantic_ai_slim/pydantic_ai/_mcp.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ class ResourceTemplate(BaseResource):
7474
"""URI template (RFC 6570) for constructing resource URIs."""
7575

7676

77+
@dataclass(repr=False, kw_only=True)
78+
class ServerCapabilities:
79+
"""Capabilities that an MCP server supports."""
80+
81+
experimental: list[str] | None = None
82+
"""Experimental, non-standard capabilities that the server supports."""
83+
84+
logging: bool = False
85+
"""Whether the server supports sending log messages to the client."""
86+
87+
prompts: bool = False
88+
"""Whether the server offers any prompt templates."""
89+
90+
resources: bool = False
91+
"""Whether the server offers any resources to read."""
92+
93+
tools: bool = False
94+
"""Whether the server offers any tools to call."""
95+
96+
completions: bool = False
97+
"""Whether the server offers autocompletion suggestions for prompts and resources."""
98+
99+
__repr__ = _utils.dataclasses_no_defaults_repr
100+
101+
77102
def map_from_mcp_params(params: mcp_types.CreateMessageRequestParams) -> list[messages.ModelMessage]:
78103
"""Convert from MCP create message request parameters to pydantic-ai messages."""
79104
pai_messages: list[messages.ModelMessage] = []
@@ -217,3 +242,15 @@ def map_from_mcp_resource_template(mcp_template: mcp_types.ResourceTemplate) ->
217242
),
218243
meta=mcp_template.meta,
219244
)
245+
246+
247+
def map_from_mcp_server_capabilities(mcp_capabilities: mcp_types.ServerCapabilities) -> ServerCapabilities:
248+
"""Convert from MCP ServerCapabilities to native Pydantic AI ServerCapabilities."""
249+
return ServerCapabilities(
250+
experimental=list(mcp_capabilities.experimental.keys()) if mcp_capabilities.experimental else None,
251+
logging=mcp_capabilities.logging is not None,
252+
prompts=mcp_capabilities.prompts is not None,
253+
resources=mcp_capabilities.resources is not None,
254+
tools=mcp_capabilities.tools is not None,
255+
completions=mcp_capabilities.completions is not None,
256+
)

pydantic_ai_slim/pydantic_ai/mcp.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class MCPServer(AbstractToolset[Any], ABC):
113113
_read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
114114
_write_stream: MemoryObjectSendStream[SessionMessage]
115115
_server_info: mcp_types.Implementation
116+
_server_capabilities: _mcp.ServerCapabilities
116117

117118
def __init__(
118119
self,
@@ -191,6 +192,15 @@ def server_info(self) -> mcp_types.Implementation:
191192
)
192193
return self._server_info
193194

195+
@property
196+
def capabilities(self) -> _mcp.ServerCapabilities:
197+
"""Access the capabilities advertised by the MCP server during initialization."""
198+
if getattr(self, '_server_capabilities', None) is None:
199+
raise AttributeError(
200+
f'The `{self.__class__.__name__}.capabilities` is only instantiated after initialization.'
201+
)
202+
return self._server_capabilities
203+
194204
async def list_tools(self) -> list[mcp_types.Tool]:
195205
"""Retrieve tools that are currently active on the server.
196206
@@ -374,6 +384,7 @@ async def __aenter__(self) -> Self:
374384
with anyio.fail_after(self.timeout):
375385
result = await self._client.initialize()
376386
self._server_info = result.serverInfo
387+
self._server_capabilities = _mcp.map_from_mcp_server_capabilities(result.capabilities)
377388
if log_level := self.log_level:
378389
await self._client.set_logging_level(log_level)
379390

tests/test_mcp.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,3 +1568,18 @@ async def test_server_info(mcp_server: MCPServerStdio) -> None:
15681568
async with mcp_server:
15691569
assert mcp_server.server_info is not None
15701570
assert mcp_server.server_info.name == 'Pydantic AI MCP Server'
1571+
1572+
1573+
async def test_capabilities(mcp_server: MCPServerStdio) -> None:
1574+
with pytest.raises(
1575+
AttributeError, match='The `MCPServerStdio.capabilities` is only instantiated after initialization.'
1576+
):
1577+
mcp_server.capabilities
1578+
async with mcp_server:
1579+
assert mcp_server.capabilities is not None
1580+
assert mcp_server.capabilities.resources is True
1581+
assert mcp_server.capabilities.tools is True
1582+
assert mcp_server.capabilities.prompts is True
1583+
assert mcp_server.capabilities.logging is True
1584+
assert mcp_server.capabilities.completions is False
1585+
assert mcp_server.capabilities.experimental is None

0 commit comments

Comments
 (0)