Skip to content

Commit fcf1051

Browse files
committed
use ToolAnnotations instead of dict throughout
1 parent dd10fa1 commit fcf1051

File tree

6 files changed

+32
-31
lines changed

6 files changed

+32
-31
lines changed

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ dependencies = [
2929
"starlette>=0.27",
3030
"sse-starlette>=1.6.1",
3131
"pydantic-settings>=2.5.2",
32-
"uvicorn>=0.23.1",
32+
"uvicorn>=0.23.1; sys_platform != 'emscripten'",
3333
]
3434

3535
[project.optional-dependencies]
@@ -89,8 +89,8 @@ venv = ".venv"
8989
strict = ["src/mcp/**/*.py"]
9090

9191
[tool.ruff.lint]
92-
select = ["E", "F", "I", "UP"]
93-
ignore = []
92+
select = ["C4", "E", "F", "I", "PERF", "UP"]
93+
ignore = ["PERF203"]
9494

9595
[tool.ruff]
9696
line-length = 88
@@ -113,5 +113,7 @@ filterwarnings = [
113113
# This should be fixed on Uvicorn's side.
114114
"ignore::DeprecationWarning:websockets",
115115
"ignore:websockets.server.WebSocketServerProtocol is deprecated:DeprecationWarning",
116-
"ignore:Returning str or bytes.*:DeprecationWarning:mcp.server.lowlevel"
116+
"ignore:Returning str or bytes.*:DeprecationWarning:mcp.server.lowlevel",
117+
# this is a problem in starlette 0.27, which we're currently pinned to
118+
"ignore:Please use `import python_multipart` instead.:PendingDeprecationWarning",
117119
]

src/mcp/server/fastmcp/server.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
GetPromptResult,
4444
ImageContent,
4545
TextContent,
46+
ToolAnnotations,
4647
)
4748
from mcp.types import Prompt as MCPPrompt
4849
from mcp.types import PromptArgument as MCPPromptArgument
@@ -172,17 +173,13 @@ def _setup_handlers(self) -> None:
172173

173174
async def list_tools(self) -> list[MCPTool]:
174175
"""List all available tools."""
175-
from mcp.types import ToolAnnotations
176-
177176
tools = self._tool_manager.list_tools()
178177
return [
179178
MCPTool(
180179
name=info.name,
181180
description=info.description,
182181
inputSchema=info.parameters,
183-
annotations=ToolAnnotations.model_validate(info.annotations)
184-
if info.annotations
185-
else None,
182+
annotations=info.annotations,
186183
)
187184
for info in tools
188185
]
@@ -251,7 +248,7 @@ def add_tool(
251248
fn: AnyFunction,
252249
name: str | None = None,
253250
description: str | None = None,
254-
annotations: dict[str, Any] | None = None,
251+
annotations: ToolAnnotations | None = None,
255252
) -> None:
256253
"""Add a tool to the server.
257254
@@ -262,7 +259,7 @@ def add_tool(
262259
fn: The function to register as a tool
263260
name: Optional name for the tool (defaults to function name)
264261
description: Optional description of what the tool does
265-
annotations: Optional annotations providing additional tool information
262+
annotations: Optional ToolAnnotations providing additional tool information
266263
"""
267264
self._tool_manager.add_tool(
268265
fn, name=name, description=description, annotations=annotations
@@ -272,7 +269,7 @@ def tool(
272269
self,
273270
name: str | None = None,
274271
description: str | None = None,
275-
annotations: dict[str, Any] | None = None,
272+
annotations: ToolAnnotations | None = None,
276273
) -> Callable[[AnyFunction], AnyFunction]:
277274
"""Decorator to register a tool.
278275
@@ -283,7 +280,7 @@ def tool(
283280
Args:
284281
name: Optional name for the tool (defaults to function name)
285282
description: Optional description of what the tool does
286-
annotations: Optional annotations providing additional tool information
283+
annotations: Optional ToolAnnotations providing additional tool information
287284
288285
Example:
289286
@server.tool()

src/mcp/server/fastmcp/tools/base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from mcp.server.fastmcp.exceptions import ToolError
1010
from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
11+
from mcp.types import ToolAnnotations
1112

1213
if TYPE_CHECKING:
1314
from mcp.server.fastmcp.server import Context
@@ -30,7 +31,7 @@ class Tool(BaseModel):
3031
context_kwarg: str | None = Field(
3132
None, description="Name of the kwarg that should receive context"
3233
)
33-
annotations: dict[str, Any] | None = Field(
34+
annotations: ToolAnnotations | None = Field(
3435
None, description="Optional annotations for the tool"
3536
)
3637

@@ -41,10 +42,10 @@ def from_function(
4142
name: str | None = None,
4243
description: str | None = None,
4344
context_kwarg: str | None = None,
44-
annotations: dict[str, Any] | None = None,
45+
annotations: ToolAnnotations | None = None,
4546
) -> Tool:
4647
"""Create a Tool from a function."""
47-
from mcp.server.fastmcp import Context
48+
from mcp.server.fastmcp.server import Context
4849

4950
func_name = name or fn.__name__
5051

src/mcp/server/fastmcp/tools/tool_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from mcp.server.fastmcp.tools.base import Tool
88
from mcp.server.fastmcp.utilities.logging import get_logger
99
from mcp.shared.context import LifespanContextT
10+
from mcp.types import ToolAnnotations
1011

1112
if TYPE_CHECKING:
1213
from mcp.server.fastmcp.server import Context
@@ -35,7 +36,7 @@ def add_tool(
3536
fn: Callable[..., Any],
3637
name: str | None = None,
3738
description: str | None = None,
38-
annotations: dict[str, Any] | None = None,
39+
annotations: ToolAnnotations | None = None,
3940
) -> Tool:
4041
"""Add a tool to the server."""
4142
tool = Tool.from_function(

tests/server/fastmcp/test_tool_manager.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.server.fastmcp.tools import ToolManager
1010
from mcp.server.session import ServerSessionT
1111
from mcp.shared.context import LifespanContextT
12+
from mcp.types import ToolAnnotations
1213

1314

1415
class TestAddTools:
@@ -331,27 +332,27 @@ def read_data(path: str) -> str:
331332
"""Read data from a file."""
332333
return f"Data from {path}"
333334

334-
annotations = {
335-
"title": "File Reader",
336-
"readOnlyHint": True,
337-
"openWorldHint": False,
338-
}
335+
annotations = ToolAnnotations(
336+
title="File Reader",
337+
readOnlyHint=True,
338+
openWorldHint=False,
339+
)
339340

340341
manager = ToolManager()
341342
tool = manager.add_tool(read_data, annotations=annotations)
342343

343344
assert tool.annotations is not None
344-
assert tool.annotations["title"] == "File Reader"
345-
assert tool.annotations["readOnlyHint"] is True
346-
assert tool.annotations["openWorldHint"] is False
345+
assert tool.annotations.title == "File Reader"
346+
assert tool.annotations.readOnlyHint is True
347+
assert tool.annotations.openWorldHint is False
347348

348349
@pytest.mark.anyio
349350
async def test_tool_annotations_in_fastmcp(self):
350351
"""Test that tool annotations are included in MCPTool conversion."""
351352

352353
app = FastMCP()
353354

354-
@app.tool(annotations={"title": "Echo Tool", "readOnlyHint": True})
355+
@app.tool(annotations=ToolAnnotations(title="Echo Tool", readOnlyHint=True))
355356
def echo(message: str) -> str:
356357
"""Echo a message back."""
357358
return message

uv.lock

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)