From d09a3ceb70a7b09f19192a922efd0e41547ef20a Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Fri, 14 Nov 2025 23:05:34 +0100 Subject: [PATCH 1/9] Expose mcp server instructions in MCPServer class as a property --- docs/mcp/client.md | 18 ++++++++++++++++++ pydantic_ai_slim/pydantic_ai/mcp.py | 11 +++++++++++ tests/test_mcp.py | 10 ++++++++++ 3 files changed, 39 insertions(+) diff --git a/docs/mcp/client.md b/docs/mcp/client.md index 71e15b77a2..0b1b78b32c 100644 --- a/docs/mcp/client.md +++ b/docs/mcp/client.md @@ -338,6 +338,24 @@ calculator_server = MCPServerSSE( agent = Agent('openai:gpt-5', toolsets=[weather_server, calculator_server]) ``` +## Server Instructions + +MCP servers can provide instructions during initialization that give context about how to best interact with the server's tools. These instructions are accessible via the [`instructions`][pydantic_ai.mcp.MCPServer.instructions] property after the server connection is established. + +```python {title="mcp_server_instructions.py"} +from pydantic_ai import Agent +from pydantic_ai.mcp import MCPServerStdio + +server = MCPServerStdio('python', args=['math_server.py']) +agent = Agent('openai:gpt-5', toolsets=[server]) + +async def main(): + async with agent: + # Access server instructions after connection is established + if server.instructions: + print(f"Server guidance: {server.instructions}") +``` + ## Tool metadata MCP tools can include metadata that provides additional information about the tool's characteristics, which can be useful when [filtering tools][pydantic_ai.toolsets.FilteredToolset]. The `meta`, `annotations`, and `output_schema` fields can be found on the `metadata` dict on the [`ToolDefinition`][pydantic_ai.tools.ToolDefinition] object that's passed to filter functions. diff --git a/pydantic_ai_slim/pydantic_ai/mcp.py b/pydantic_ai_slim/pydantic_ai/mcp.py index 3d2b607b3f..2e930a59da 100644 --- a/pydantic_ai_slim/pydantic_ai/mcp.py +++ b/pydantic_ai_slim/pydantic_ai/mcp.py @@ -122,6 +122,7 @@ class MCPServer(AbstractToolset[Any], ABC): _read_stream: MemoryObjectReceiveStream[SessionMessage | Exception] _write_stream: MemoryObjectSendStream[SessionMessage] _server_info: mcp_types.Implementation + _instructions: str | None def __init__( self, @@ -200,6 +201,15 @@ def server_info(self) -> mcp_types.Implementation: ) return self._server_info + @property + def instructions(self) -> str | None: + """Access the instructions sent by the MCP server during initialization.""" + if getattr(self, '_server_info', None) is None: + raise AttributeError( + f'The `{self.__class__.__name__}.instructions` is only available after initialization.' + ) + return self._instructions + async def list_tools(self) -> list[mcp_types.Tool]: """Retrieve tools that are currently active on the server. @@ -337,6 +347,7 @@ async def __aenter__(self) -> Self: with anyio.fail_after(self.timeout): result = await self._client.initialize() self._server_info = result.serverInfo + self._instructions = result.instructions if log_level := self.log_level: await self._client.set_logging_level(log_level) diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 9d0654f9df..c7e90631e9 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1757,6 +1757,16 @@ async def test_server_info(mcp_server: MCPServerStdio) -> None: assert mcp_server.server_info.name == 'Pydantic AI MCP Server' +async def test_instructions(mcp_server: MCPServerStdio) -> None: + with pytest.raises( + AttributeError, match='The `MCPServerStdio.instructions` is only available after initialization.' + ): + mcp_server.instructions + async with mcp_server: + # The test server doesn't provide instructions, so it should be None + assert mcp_server.instructions is None + + async def test_agent_run_stream_with_mcp_server_http(allow_model_requests: None, model: Model): server = MCPServerStreamableHTTP(url='https://mcp.deepwiki.com/mcp', timeout=30) agent = Agent(model, toolsets=[server], instructions='Be concise.') From 58e739d2391bb667671254f88953b0cfc25b0204 Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Fri, 14 Nov 2025 23:29:39 +0100 Subject: [PATCH 2/9] Fix test for docs examples --- docs/mcp/client.md | 6 +++--- tests/test_examples.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/mcp/client.md b/docs/mcp/client.md index 0b1b78b32c..f5b544c018 100644 --- a/docs/mcp/client.md +++ b/docs/mcp/client.md @@ -344,16 +344,16 @@ MCP servers can provide instructions during initialization that give context abo ```python {title="mcp_server_instructions.py"} from pydantic_ai import Agent -from pydantic_ai.mcp import MCPServerStdio +from pydantic_ai.mcp import MCPServerStreamableHTTP -server = MCPServerStdio('python', args=['math_server.py']) +server = MCPServerStreamableHTTP('http://localhost:8000/mcp') agent = Agent('openai:gpt-5', toolsets=[server]) async def main(): async with agent: # Access server instructions after connection is established if server.instructions: - print(f"Server guidance: {server.instructions}") + print(f'Server guidance: {server.instructions}') ``` ## Tool metadata diff --git a/tests/test_examples.py b/tests/test_examples.py index 85bae688d0..bdbcf45561 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -304,6 +304,10 @@ class MockMCPServer(AbstractToolset[Any]): def id(self) -> str | None: return None # pragma: no cover + @property + def instructions(self) -> str | None: + return None # pragma: no cover + async def __aenter__(self) -> MockMCPServer: return self From 99767c2ba496136a56c73fc75c7f4aed338b54b3 Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Fri, 14 Nov 2025 23:50:46 +0100 Subject: [PATCH 3/9] Remove mistaken no cover pragma --- tests/test_examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index bdbcf45561..407816b60a 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -306,7 +306,7 @@ def id(self) -> str | None: @property def instructions(self) -> str | None: - return None # pragma: no cover + return None async def __aenter__(self) -> MockMCPServer: return self From f067645704edd4b22024eff92e7781bede579554 Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Sat, 15 Nov 2025 18:01:43 +0100 Subject: [PATCH 4/9] Fix condition to check for _instructions attribute --- pydantic_ai_slim/pydantic_ai/mcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydantic_ai_slim/pydantic_ai/mcp.py b/pydantic_ai_slim/pydantic_ai/mcp.py index 2e930a59da..e671208d26 100644 --- a/pydantic_ai_slim/pydantic_ai/mcp.py +++ b/pydantic_ai_slim/pydantic_ai/mcp.py @@ -204,7 +204,7 @@ def server_info(self) -> mcp_types.Implementation: @property def instructions(self) -> str | None: """Access the instructions sent by the MCP server during initialization.""" - if getattr(self, '_server_info', None) is None: + if getattr(self, '_instructions', None) is None: raise AttributeError( f'The `{self.__class__.__name__}.instructions` is only available after initialization.' ) From ea9a6c3fd5e6f2506cbbbae1b0b0c412639abdae Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Sat, 15 Nov 2025 18:56:30 +0100 Subject: [PATCH 5/9] Fix test for instructions in test_mcp --- pydantic_ai_slim/pydantic_ai/mcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydantic_ai_slim/pydantic_ai/mcp.py b/pydantic_ai_slim/pydantic_ai/mcp.py index e671208d26..982847bacf 100644 --- a/pydantic_ai_slim/pydantic_ai/mcp.py +++ b/pydantic_ai_slim/pydantic_ai/mcp.py @@ -204,7 +204,7 @@ def server_info(self) -> mcp_types.Implementation: @property def instructions(self) -> str | None: """Access the instructions sent by the MCP server during initialization.""" - if getattr(self, '_instructions', None) is None: + if not hasattr(self, '_instructions'): raise AttributeError( f'The `{self.__class__.__name__}.instructions` is only available after initialization.' ) From 8d4b41f94a66e59539102be81091847710d0b7f1 Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Tue, 18 Nov 2025 22:04:52 +0100 Subject: [PATCH 6/9] Update example to show dynamic instructions --- docs/mcp/client.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/mcp/client.md b/docs/mcp/client.md index f5b544c018..d4447f9758 100644 --- a/docs/mcp/client.md +++ b/docs/mcp/client.md @@ -349,11 +349,13 @@ from pydantic_ai.mcp import MCPServerStreamableHTTP server = MCPServerStreamableHTTP('http://localhost:8000/mcp') agent = Agent('openai:gpt-5', toolsets=[server]) +@agent.instructions +async def mcp_server_instructions(): + return server.instructions + async def main(): - async with agent: - # Access server instructions after connection is established - if server.instructions: - print(f'Server guidance: {server.instructions}') + result = await agent.run('Use the server tools to help me') + print(result.output) ``` ## Tool metadata From 2238e39345fc1dc0b26bdab321a7aa9a939ba30c Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Wed, 19 Nov 2025 11:27:04 +0100 Subject: [PATCH 7/9] Update doc example to fix test --- docs/mcp/client.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/mcp/client.md b/docs/mcp/client.md index d4447f9758..515d4d74c8 100644 --- a/docs/mcp/client.md +++ b/docs/mcp/client.md @@ -354,8 +354,9 @@ async def mcp_server_instructions(): return server.instructions async def main(): - result = await agent.run('Use the server tools to help me') + result = await agent.run('What is 7 plus 5?') print(result.output) + #> The answer is 12. ``` ## Tool metadata From 3916338ffbf0967eea82497dacea9bd4564801f6 Mon Sep 17 00:00:00 2001 From: Motta Kin Date: Wed, 19 Nov 2025 19:30:56 +0100 Subject: [PATCH 8/9] Add instructions to the test server; tooltip to instructions example --- docs/mcp/client.md | 4 +++- tests/mcp_server.py | 2 +- tests/test_mcp.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/mcp/client.md b/docs/mcp/client.md index 515d4d74c8..39fd80bc4d 100644 --- a/docs/mcp/client.md +++ b/docs/mcp/client.md @@ -351,7 +351,7 @@ agent = Agent('openai:gpt-5', toolsets=[server]) @agent.instructions async def mcp_server_instructions(): - return server.instructions + return server.instructions # (1)! async def main(): result = await agent.run('What is 7 plus 5?') @@ -359,6 +359,8 @@ async def main(): #> The answer is 12. ``` +1. The server connection is guaranteed to be established by this point, so `server.instructions` is available. + ## Tool metadata MCP tools can include metadata that provides additional information about the tool's characteristics, which can be useful when [filtering tools][pydantic_ai.toolsets.FilteredToolset]. The `meta`, `annotations`, and `output_schema` fields can be found on the `metadata` dict on the [`ToolDefinition`][pydantic_ai.tools.ToolDefinition] object that's passed to filter functions. diff --git a/tests/mcp_server.py b/tests/mcp_server.py index 54b105ab29..eaec8eb9a2 100644 --- a/tests/mcp_server.py +++ b/tests/mcp_server.py @@ -16,7 +16,7 @@ ) from pydantic import AnyUrl, BaseModel -mcp = FastMCP('Pydantic AI MCP Server') +mcp = FastMCP('Pydantic AI MCP Server', instructions='Be a helpful assistant.') log_level = 'unset' diff --git a/tests/test_mcp.py b/tests/test_mcp.py index c7e90631e9..99bdcfb9ad 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1764,7 +1764,7 @@ async def test_instructions(mcp_server: MCPServerStdio) -> None: mcp_server.instructions async with mcp_server: # The test server doesn't provide instructions, so it should be None - assert mcp_server.instructions is None + assert mcp_server.instructions == 'Be a helpful assistant.' async def test_agent_run_stream_with_mcp_server_http(allow_model_requests: None, model: Model): From 3635790f7ef453b38866b933669a73ed0e360930 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 19 Nov 2025 13:31:49 -0600 Subject: [PATCH 9/9] Update tests/test_mcp.py --- tests/test_mcp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 99bdcfb9ad..2f05f419a6 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1763,7 +1763,6 @@ async def test_instructions(mcp_server: MCPServerStdio) -> None: ): mcp_server.instructions async with mcp_server: - # The test server doesn't provide instructions, so it should be None assert mcp_server.instructions == 'Be a helpful assistant.'