Context
The Context is an object the SDK gives a tool so it can reach the server's own resources, report progress and talk back to the client, without any of that showing up in the model's arguments.
Last updated: 29 Sep, 2026 · MCP 2.2
The Context, Lifespan, Progress and Elicitation lessons each show one feature on a small server of its own, saved in its own file: context_demo.py here. Keep shop.py as it is; the stdio lesson goes back to it.
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
mcp = MCPServer("Shop support")
@mcp.resource("policy://refunds")
def refund_policy() -> str:
"""The shop's refund policy."""
return "Full refund within 30 days of delivery."
@mcp.tool()
async def refund_rules(ctx: Context) -> str:
"""Tell the model the refund rules before it promises anything."""
[policy] = await ctx.read_resource("policy://refunds")
return f"Policy: {policy.content}"A parameter annotated Context is filled in by the SDK on every request. ctx.read_resource reads one of the server's own resources through the same path a client uses, so the policy text lives in one place and both the application and the tool read it.
import asyncio
from mcp import Client
from context_demo import mcp
async def main():
async with Client(mcp) as client:
tools = await client.list_tools()
print(tools.tools[0].input_schema)
result = await client.call_tool("refund_rules", {})
print(result.content[0].text)
asyncio.run(main()){'type': 'object', 'properties': {}, 'title': 'refund_rulesArguments'}
Policy: Full refund within 30 days of delivery.- The input schema is empty. The tool was called with
{}and its schema has no properties, so the model never seesctx. - The policy came from the resource.
ctx.read_resourcereadpolicy://refundsthrough the same path a client uses, so the text lives in one place.
The tool is async def because read_resource is awaited. A plain def tool is fine for work that does not wait on anything; the SDK runs it in a separate thread so it never blocks the server.
What else is on it
ctx.report_progress(...): tell the client how far a slow tool has got. See the Progress lesson.ctx.request_context.lifespan_context: objects the server built at startup, such as a database. See the Lifespan lesson.ctx.headers: the HTTP headers of the request, orNonewhen the server is not reached over HTTP (the Streamable HTTP lesson).ctx.session: the channel back to this client, for notifications like a changed tool list.
Context vs a normal argument
| A normal argument | The Context | |
|---|---|---|
| In the input schema | Yes, the model fills it | No, the SDK fills it |
| Comes from | The model's tool call | The server and the request |
| Good for | Data the tool acts on | Resources, progress, headers, the session |
When to reach for the Context
- Reading one of the server's own resources inside a tool, so a value lives in one place.
- Reporting progress on a slow tool (the Progress lesson) or reading request headers (the Streamable HTTP lesson).
- Sending notifications back to the client through the session.
context instead of ctx works, but dropping its type annotation does not. Without Context in the annotation the SDK cannot inject it, and the value stays unfilled.Related
- Name the parameter
contextinstead ofctx. Does anything change? - Print
ctx.request_idfrom inside the tool. - Give
refund_rulesanorder_id: strargument and include it in the answer.
Slow is fine. Stopping is the only problem.