在定义FastMCP 工具资源、资源模板或提示时,您的函数可能需要与底层MCP会话交互或访问服务器功能。为此,FastMCP提供了Context对象。

什么是上下文?

Context对象提供了一个简洁的接口,用于在您的函数中访问MCP功能,包括:

  • 日志记录: 将调试、信息、警告和错误消息发送回客户端
  • 进度报告: 向客户端更新长时间运行操作的进度
  • 资源访问: 从服务器注册的资源中读取数据
  • LLM采样: 请求客户端的LLM根据提供的消息生成文本
  • 请求信息: 访问当前请求的元数据
  • 服务器访问: 在需要时访问底层FastMCP服务器实例

访问上下文

通过依赖注入

要在您的任何函数中使用上下文对象,只需在函数签名中添加一个参数并将其类型提示为Context。当调用您的函数时,FastMCP会自动注入上下文实例。

关键点:

  • 参数名称(例如ctxcontext)无关紧要,只有类型提示Context才是重要的。
  • context参数可以放在函数签名的任何位置;它不会作为有效参数暴露给MCP客户端。
  • 上下文是可选的 - 不需要上下文的函数可以完全省略该参数。
  • 上下文方法是异步的,因此您的函数通常也需要是异步的。
  • 类型提示可以是联合类型(Context | None)或使用Annotated[],它仍然可以正常工作。
  • 上下文仅在请求期间可用;在请求之外尝试使用上下文方法会引发错误。如果您需要在请求之外调试或调用上下文方法,可以将变量类型声明为Context | None=None以避免缺少参数的错误。

工具

from fastmcp import FastMCP, Context

mcp = FastMCP(name="ContextDemo")

@mcp.tool()
async def process_file(file_uri: str, ctx: Context) -> str:
    """Processes a file, using context for logging and resource access."""
    # Context is available as the ctx parameter
    return "Processed file"

资源与模板

New in version: 2.2.5
@mcp.resource("resource://user-data")
async def get_user_data(ctx: Context) -> dict:
    """Fetch personalized user data based on the request context."""
    # Context is available as the ctx parameter
    return {"user_id": "example"}

@mcp.resource("resource://users/{user_id}/profile")
async def get_user_profile(user_id: str, ctx: Context) -> dict:
    """Fetch user profile with context-aware logging."""
    # Context is available as the ctx parameter
    return {"id": user_id}

提示词

New in version: 2.2.5
@mcp.prompt()
async def data_analysis_request(dataset: str, ctx: Context) -> str:
    """Generate a request to analyze data with contextual information."""
    # Context is available as the ctx parameter
    return f"Please analyze the following dataset: {dataset}"

通过依赖函数

New in version: 2.2.11

虽然访问上下文最简单的方式是通过如上所示的函数参数注入,但在某些情况下,您需要访问代码中的上下文,而这些代码可能不易修改以接受上下文参数,或者嵌套在函数调用更深处。

FastMCP 提供了依赖函数,允许您从服务器请求执行流程中的任何位置获取活动上下文:

from fastmcp import FastMCP, Context
from fastmcp.server.dependencies import get_context

mcp = FastMCP(name="DependencyDemo")

# Utility function that needs context but doesn't receive it as a parameter
async def process_data(data: list[float]) -> dict:
    # Get the active context - only works when called within a request
    ctx = get_context()    
    await ctx.info(f"Processing {len(data)} data points")
    
@mcp.tool()
async def analyze_dataset(dataset_name: str) -> dict:
    # Call utility function that uses context internally
    data = load_data(dataset_name)
    await process_data(data)

重要提示:

  • get_context 函数应仅在服务器请求的上下文中使用。在请求之外调用会引发 RuntimeError
  • get_context 函数仅限服务器端使用,不应在客户端代码中使用。

上下文能力

日志记录

将日志消息发送回MCP客户端。这对于调试和在请求期间提供函数执行的可视性非常有用。

@mcp.tool()
async def analyze_data(data: list[float], ctx: Context) -> dict:
    """Analyze numerical data with logging."""
    await ctx.debug("Starting analysis of numerical data")
    await ctx.info(f"Analyzing {len(data)} data points")
    
    try:
        result = sum(data) / len(data)
        await ctx.info(f"Analysis complete, average: {result}")
        return {"average": result, "count": len(data)}
    except ZeroDivisionError:
        await ctx.warning("Empty data list provided")
        return {"error": "Empty data list"}
    except Exception as e:
        await ctx.error(f"Analysis failed: {str(e)}")
        raise

可用的日志记录方法:

  • ctx.debug(message: str): 用于调试的低级详细信息
  • ctx.info(message: str): 关于执行的一般信息
  • ctx.warning(message: str): 不影响执行的潜在问题
  • ctx.error(message: str): 执行过程中发生的错误
  • ctx.log(level: Literal["debug", "info", "warning", "error"], message: str, logger_name: str | None = None): 支持自定义记录器名称的通用日志方法

进度报告

对于长时间运行的操作,向客户端通知进度。这允许客户端显示进度指示器并提供更好的用户体验。

@mcp.tool()
async def process_items(items: list[str], ctx: Context) -> dict:
    """Process a list of items with progress updates."""
    total = len(items)
    results = []
    
    for i, item in enumerate(items):
        # Report progress as percentage
        await ctx.report_progress(progress=i, total=total)
        
        # Process the item (simulated with a sleep)
        await asyncio.sleep(0.1)
        results.append(item.upper())
    
    # Report 100% completion
    await ctx.report_progress(progress=total, total=total)
    
    return {"processed": len(results), "results": results}

方法签名:

  • ctx.report_progress(progress: float, total: float | None = None)
    • progress: 当前进度值(例如:24)
    • total: 可选的总数值(例如100)。如果提供,客户端可能会将其解释为百分比。

进度报告要求客户端在初始请求中发送一个progressToken。如果客户端不支持进度报告,这些调用将不会产生任何效果。

资源访问

从已注册到FastMCP服务器的资源中读取数据。这使得函数能够访问文件、配置或动态生成的内容。

@mcp.tool()
async def summarize_document(document_uri: str, ctx: Context) -> str:
    """Summarize a document by its resource URI."""
    # Read the document content
    content_list = await ctx.read_resource(document_uri)
    
    if not content_list:
        return "Document is empty"
    
    document_text = content_list[0].content
    
    # Example: Generate a simple summary (length-based)
    words = document_text.split()
    total_words = len(words)
    
    await ctx.info(f"Document has {total_words} words")
    
    # Return a simple summary
    if total_words > 100:
        summary = " ".join(words[:100]) + "..."
        return f"Summary ({total_words} words total): {summary}"
    else:
        return f"Full document ({total_words} words): {document_text}"

方法签名:

  • ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]
    • uri: 要读取的资源URI
    • 返回资源内容部分的列表(通常只包含一个项目)

返回的内容通常通过content_list[0].content访问,根据资源类型可能是文本或二进制数据。

大语言模型采样

New in version: 2.0.0

请求客户端的LLM基于提供的消息生成文本。当您的函数需要利用LLM的能力来处理数据或生成响应时,这非常有用。

@mcp.tool()
async def analyze_sentiment(text: str, ctx: Context) -> dict:
    """Analyze the sentiment of a text using the client's LLM."""
    # Create a sampling prompt asking for sentiment analysis
    prompt = f"Analyze the sentiment of the following text as positive, negative, or neutral. Just output a single word - 'positive', 'negative', or 'neutral'. Text to analyze: {text}"
    
    # Send the sampling request to the client's LLM
    response = await ctx.sample(prompt)
    
    # Process the LLM's response
    sentiment = response.text.strip().lower()
    
    # Map to standard sentiment values
    if "positive" in sentiment:
        sentiment = "positive"
    elif "negative" in sentiment:
        sentiment = "negative"
    else:
        sentiment = "neutral"
    
    return {"text": text, "sentiment": sentiment}

方法签名:

  • ctx.sample(messages: str | list[str | SamplingMessage], system_prompt: str | None = None, temperature: float | None = None, max_tokens: int | None = None) -> TextContent | ImageContent
    • messages: 要发送给LLM的字符串或字符串/消息对象列表
    • system_prompt: 可选的系统提示词,用于引导大语言模型的行为
    • temperature: 可选的采样温度(控制随机性)
    • max_tokens: 可选参数,指定生成的最大token数量(默认为512)
    • 返回LLM的响应作为TextContent或ImageContent

当提供一个简单字符串时,它会被视为用户消息。对于更复杂的场景,您可以提供具有不同角色的消息列表。

@mcp.tool()
async def generate_example(concept: str, ctx: Context) -> str:
    """Generate a Python code example for a given concept."""
    # Using a system prompt and a user message
    response = await ctx.sample(
        messages=f"Write a simple Python code example demonstrating '{concept}'.",
        system_prompt="You are an expert Python programmer. Provide concise, working code examples without explanations.",
        temperature=0.7,
        max_tokens=300
    )
    
    code_example = response.text
    return f"```python\n{code_example}\n```"

有关客户端如何处理这些请求的更多详情,请参阅客户端采样

请求信息

访问当前请求和客户端的元数据。

@mcp.tool()
async def request_info(ctx: Context) -> dict:
    """Return information about the current request."""
    return {
        "request_id": ctx.request_id,
        "client_id": ctx.client_id or "Unknown client"
    }

可用属性:

  • ctx.request_id -> str: 获取当前MCP请求的唯一ID
  • ctx.client_id -> str | None: 获取发起请求的客户端ID(如果在初始化时提供了该信息)

高级访问

FastMCP 服务器与会话

@mcp.tool()
async def advanced_tool(ctx: Context) -> str:
    """Demonstrate advanced context access."""
    # Access the FastMCP server instance
    server_name = ctx.fastmcp.name
    
    # Low-level session access (rarely needed)
    session = ctx.session
    request_context = ctx.request_context
    
    return f"Server: {server_name}"

HTTP请求

New in version: 2.2.7

ctx.get_http_request() 方法已弃用,将在未来版本中移除。 请改用 get_http_request() 依赖函数。 更多详情请参阅 HTTP Requests pattern

对于Web应用程序,您可以访问底层的HTTP请求:

@mcp.tool()
async def handle_web_request(ctx: Context) -> dict:
    """Access HTTP request information from the Starlette request."""
    request = ctx.get_http_request()
    
    # Access HTTP headers, query parameters, etc.
    user_agent = request.headers.get("user-agent", "Unknown")
    client_ip = request.client.host if request.client else "Unknown"
    
    return {
        "user_agent": user_agent,
        "client_ip": client_ip,
        "path": request.url.path,
    }

高级属性参考

  • ctx.fastmcp -> FastMCP: 访问上下文所属的服务器实例
  • ctx.session: 访问原始的mcp.server.session.ServerSession对象
  • ctx.request_context: 访问原始的mcp.shared.context.RequestContext对象

直接使用sessionrequest_context需要理解底层的MCP Python SDK,可能比直接在Context对象上提供的方法稳定性更低。