Python 玩转 MCP:在工具函数中获取请求 Headers 的正确姿势
一、问题出现:工具函数里怎么拿请求信息?
假设你用 FastMCP 写了一个最简单的工具函数:
| from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My Server")
@mcp.tool(name="打招呼",description="简单的打招呼")
async def hello() -> str:
return "hello world"
|
这个函数很简单,运行也没问题。
但是一旦你想要在函数里获取请求头、用户信息、IP、Token 等信息(一般用于一些权限校验,数据打点等操作),就会发现:
函数没有直接的 Request 对象!
这时就要用到关键角色 —— ctx: Context
二、Context 到底是什么?
在 FastMCP 框架中,每个工具函数都会自动注入一个 ctx: Context 参数。
你可以把它理解成一次 调用上下文(context),它内部封装了这次请求的关键信息,包括但不限于:
简单来说:ctx 是 MCP 工具函数与外部世界沟通的桥梁。
有了这个认识,我们就可以轻松拿到 headers:
| from mcp.server.fastmcp import FastMCP, Context
from starlette.requests import Request
mcp = FastMCP("My Server")
@mcp.tool(name="显示 headers 信息", description="显示请求的 headers信息")
async def echo_headers(ctx: Context) -> str:
"""获取请求的 header 信息"""
# 访问请求对象
if ctx.request_context.request and isinstance(ctx.request_context.request, Request):
headers = dict(ctx.request_context.request.headers)
return str(headers)
return "No headers available"
|
如果工具函数中有参数应该如何传递?
Context 注入机制会自动检测函数参数中的 Context 类型注解,当工具被调用时,FastMCP 会自动将 Context 对象注入到该参数中。 Context 参数的名称可以是任意的(如 ctx、context、my_ctx),只要它带有 Context 类型注解即可。
| from mcp.server.fastmcp import FastMCP, Context
from starlette.requests import Request
mcp = FastMCP("My Server")
@mcp.tool(name="打招呼", description="打招呼")
async def say_hello(
name: str = Field(description="你的名字", examples=["小王"]),
ctx: Context=Context(),
):
# 访问请求对象
if ctx.request_context.request and isinstance(ctx.request_context.request, Request):
headers = dict(ctx.request_context.request.headers)
print( headers)
return f"hello {name}"
|
ctx 在工具函数中的位置无所谓,但必须是使用 Context 注解的,你也可以写成
| @mcp.tool(name="打招呼", description="打招呼")
async def say_hello(
ctx: Context=Context(),
name: str = Field(description="你的名字", examples=["小王"])
):
...
|
但是推荐放到最后或者第一个参数,不要和混合在正常的工具参数中。
五、总结一下
通过这次小例子,我们了解了:
-
ctx: Context 是工具函数的上下文入口
-
通过 ctx.request_context.request 可以拿到 Starlette 的 Request 对象,这个对象包含了请求的上下文信息,header, cookies 等
-
MCP SDK 使用 Context 来统一不同通信模式下的请求访问方式
在实际开发中,你可以基于这个思路进一步扩展,比如: