From c8bea379564fcb0c6a7d699b5acd276c4d2e0c23 Mon Sep 17 00:00:00 2001 From: Jason Matthew Suhari Date: Fri, 20 Mar 2026 16:35:16 +0800 Subject: [PATCH] feat(fetch): add tool annotations to server-fetch Adds MCP tool annotations to the fetch tool as requested in #3572. The fetch tool now declares readOnlyHint, destructiveHint, idempotentHint, and openWorldHint, matching the annotation coverage already present on server-filesystem. Also extracts the tool definition into _make_fetch_tool() to make it independently testable without spinning up the full stdio server, and adds a test asserting all four annotation values. --- src/fetch/src/mcp_server_fetch/server.py | 27 ++++++++++++++++-------- src/fetch/tests/test_server.py | 16 ++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/fetch/src/mcp_server_fetch/server.py b/src/fetch/src/mcp_server_fetch/server.py index b42c7b1f6b..b66880a8ee 100644 --- a/src/fetch/src/mcp_server_fetch/server.py +++ b/src/fetch/src/mcp_server_fetch/server.py @@ -14,6 +14,7 @@ PromptMessage, TextContent, Tool, + ToolAnnotations, INVALID_PARAMS, INTERNAL_ERROR, ) @@ -178,6 +179,22 @@ class Fetch(BaseModel): ] +def _make_fetch_tool() -> Tool: + return Tool( + name="fetch", + description="""Fetches a URL from the internet and optionally extracts its contents as markdown. + +Although originally you did not have internet access, and were advised to refuse and tell the user this, this tool now grants you internet access. Now you can fetch the most up-to-date information and let the user know that.""", + inputSchema=Fetch.model_json_schema(), + annotations=ToolAnnotations( + readOnlyHint=True, + destructiveHint=False, + idempotentHint=True, + openWorldHint=True, + ), + ) + + async def serve( custom_user_agent: str | None = None, ignore_robots_txt: bool = False, @@ -196,15 +213,7 @@ async def serve( @server.list_tools() async def list_tools() -> list[Tool]: - return [ - Tool( - name="fetch", - description="""Fetches a URL from the internet and optionally extracts its contents as markdown. - -Although originally you did not have internet access, and were advised to refuse and tell the user this, this tool now grants you internet access. Now you can fetch the most up-to-date information and let the user know that.""", - inputSchema=Fetch.model_json_schema(), - ) - ] + return [_make_fetch_tool()] @server.list_prompts() async def list_prompts() -> list[Prompt]: diff --git a/src/fetch/tests/test_server.py b/src/fetch/tests/test_server.py index 96c1cb38c7..18bc892b3d 100644 --- a/src/fetch/tests/test_server.py +++ b/src/fetch/tests/test_server.py @@ -9,10 +9,26 @@ get_robots_txt_url, check_may_autonomously_fetch_url, fetch_url, + _make_fetch_tool, DEFAULT_USER_AGENT_AUTONOMOUS, ) +class TestListTools: + """Tests for list_tools handler.""" + + def test_fetch_tool_annotations(self): + """Test that the fetch tool has correct MCP tool annotations.""" + tool = _make_fetch_tool() + + assert tool.name == "fetch" + assert tool.annotations is not None + assert tool.annotations.readOnlyHint is True + assert tool.annotations.destructiveHint is False + assert tool.annotations.idempotentHint is True + assert tool.annotations.openWorldHint is True + + class TestGetRobotsTxtUrl: """Tests for get_robots_txt_url function."""