Skip to content

Commit e9f7650

Browse files
Nikita BelliniNikita Bellini
authored andcommitted
Passed context to runtime tools generator
1 parent b5e4fcc commit e9f7650

File tree

3 files changed

+36
-8
lines changed

3 files changed

+36
-8
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,10 @@ It is also possible to define tools at runtime, allowing for dynamic modificatio
390390
```python
391391
from mcp.server.fastmcp import FastMCP
392392
from mcp.server.fastmcp.tools.base import Tool
393+
from mcp.server.fastmcp.server import Context
393394

394395

395-
async def runtime_mcp_tools_generator() -> list[Tool]:
396+
async def runtime_mcp_tools_generator(ctx: Context) -> list[Tool]:
396397
"""Generate runtime tools."""
397398

398399
def list_cities() -> list[str]:
@@ -405,7 +406,14 @@ async def runtime_mcp_tools_generator() -> list[Tool]:
405406
return 22.5
406407
# Returns: {"result": 22.5}
407408

408-
return [Tool.from_function(list_cities), Tool.from_function(get_temperature)]
409+
tools = [Tool.from_function(list_cities)]
410+
411+
# Tool added only after authorization
412+
request = ctx.request_context.request
413+
if request and request.header.get("Authorization") == "Bearer auth_token_123":
414+
tools.append(Tool.from_function(get_temperature))
415+
416+
return tools
409417

410418

411419
mcp = FastMCP(

src/mcp/server/fastmcp/server.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ def __init__(
140140
event_store: EventStore | None = None,
141141
*,
142142
tools: list[Tool] | None = None,
143-
runtime_mcp_tools_generator: Callable[[], Awaitable[list[Tool]]] | None = None,
143+
runtime_mcp_tools_generator: Callable[[Context[ServerSession, object, Request]], Awaitable[list[Tool]]]
144+
| None = None,
144145
**settings: Any,
145146
):
146147
self.settings = Settings(**settings)
@@ -249,7 +250,8 @@ async def list_tools(self) -> list[MCPTool]:
249250
tools = self._tool_manager.list_tools()
250251

251252
if self._runtime_mcp_tools_generator:
252-
tools.extend(await self._runtime_mcp_tools_generator())
253+
context = self.get_context()
254+
tools.extend(await self._runtime_mcp_tools_generator(context))
253255

254256
# Check if there are no duplicated tools
255257
if len(tools) != len({tool.name for tool in tools}):
@@ -287,7 +289,7 @@ async def call_tool(self, name: str, arguments: dict[str, Any]) -> Sequence[Cont
287289

288290
# Try to call a runtime tool
289291
if self._runtime_mcp_tools_generator:
290-
runtime_tools = await self._runtime_mcp_tools_generator()
292+
runtime_tools = await self._runtime_mcp_tools_generator(context)
291293
for tool in runtime_tools:
292294
if tool.name == name:
293295
return await tool.run(arguments=arguments, context=context, convert_result=True)

tests/server/fastmcp/test_runtime_tools.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44

55
from mcp.server.fastmcp import FastMCP
6+
from mcp.server.fastmcp.server import Context
67
from mcp.server.fastmcp.tools.base import Tool
78
from mcp.shared.memory import create_connected_server_and_client_session
89
from mcp.types import TextContent
@@ -12,7 +13,7 @@
1213
async def test_runtime_tools():
1314
"""Test that runtime tools work correctly."""
1415

15-
async def runtime_mcp_tools_generator() -> list[Tool]:
16+
async def runtime_mcp_tools_generator(ctx: Context) -> list[Tool]:
1617
"""Generate runtime tools."""
1718

1819
def runtime_tool_1(message: str):
@@ -21,7 +22,17 @@ def runtime_tool_1(message: str):
2122
def runtime_tool_2(message: str):
2223
return message
2324

24-
return [Tool.from_function(runtime_tool_1), Tool.from_function(runtime_tool_2)]
25+
def runtime_tool_3(message: str):
26+
return message
27+
28+
tools = [Tool.from_function(runtime_tool_1), Tool.from_function(runtime_tool_2)]
29+
30+
# Tool added only after authorization
31+
request = ctx.request_context.request
32+
if request and request.header.get("Authorization") == "Bearer test_auth":
33+
tools.append(Tool.from_function(runtime_tool_3))
34+
35+
return tools
2536

2637
# Create server with various tool configurations, both static and runtime
2738
mcp = FastMCP(name="RuntimeToolsTestServer", runtime_mcp_tools_generator=runtime_mcp_tools_generator)
@@ -31,7 +42,7 @@ def runtime_tool_2(message: str):
3142
def static_tool(message: str) -> str:
3243
return message
3344

34-
# Start server and connect client
45+
# Start server and connect client without authorization
3546
async with create_connected_server_and_client_session(mcp._mcp_server) as client:
3647
await client.initialize()
3748

@@ -71,3 +82,10 @@ def static_tool(message: str) -> str:
7182
content = result.content[0]
7283
assert isinstance(content, TextContent)
7384
assert content.text == "Unknown tool: non_existing_tool"
85+
86+
# Check not authorized tool
87+
result = await client.call_tool("runtime_tool_3", {"message": "This is a test"})
88+
assert len(result.content) == 1
89+
content = result.content[0]
90+
assert isinstance(content, TextContent)
91+
assert content.text == "Unknown tool: runtime_tool_3"

0 commit comments

Comments
 (0)