Repository navigation
How do I pass user info like (user_id, company_id, ...) from agent into MCP request Context? #307
Replies: 6 comments 8 replies
|
did you get this sorted if yes please tell me how to |
headers = {
"authorization": req_state.get().get("auth_token"),
}
async with streamablehttp_client(MCP_URL, headers) as (reader, writer, _):
async with ClientSession(reader, writer) as session:
await session.initialize()
tools = await fetch_tools(session) |
|
@chungtran4078 If your MCP server will only be used by your own agents, then you can send custom headers to pass user_id, company_id. That works fine in a closed ecosystem where you control both client and server. But if you want your MCP server to be usable by standard/public MCP clients (e.g. Claude Desktop or other open-source clients), headers aren’t a reliable option. The MCP spec doesn’t currently define a way for clients to send arbitrary headers or context, so those clients wouldn’t know what to send. query_params may be an option. Ex: 127.0.0.1:8000/mcp?user_id=123 I am also facing the same problem. Please let me know if you find a solution. |
|
The user context problem in MCP is fundamental — you want to scope tool behavior per calling user without putting auth logic inside every tool. A few patterns that work: Request-scoped context injection via lifespan from contextlib import asynccontextmanager
from fastmcp import FastMCP, Context
@asynccontextmanager
async def lifespan(app):
yield {"user_id": None, "company_id": None}
mcp = FastMCP(lifespan=lifespan)
@mcp.tool
async def get_documents(ctx: Context) -> list[str]:
user_id = ctx.request_context.lifespan_context["user_id"]
return db.query(f"SELECT * FROM docs WHERE owner = {user_id}")Per-connection context with SSE transport Forwarding headers via proxy layer Tool-level permission checks with user context The open question is how MCP clients should convey user identity in a standardized way — right now it's convention-dependent. Worth following the MCP spec discussions on this. |
|
This is one of the trickiest parts of production MCP servers. We ran into this exact challenge when building our multi-agent system where each agent needs to identify itself to the MCP server. Approach 1: Custom Headers (our production solution) We add auth headers at the transport layer: # Client side - when creating the MCP connection
transport = StdioServerTransport(
env={
"MCP_USER_ID": current_user_id,
"MCP_COMPANY_ID": current_company_id,
"MCP_AGENT_ROLE": agent_role # e.g., "content-writer", "seo-analyst"
}
)On the server side, read them from the context: from fastmcp import FastMCP
mcp = FastMCP("our-server")
@mcp.tool()
def get_user_data(query: str, ctx: Context) -> str:
user_id = ctx.request_context.meta.get("user_id")
company_id = ctx.request_context.meta.get("company_id")
# Now filter data by user/companyApproach 2: Session-based middleware For HTTP transport, we use a middleware pattern: @app.middleware("http")
async def inject_user_context(request: Request, call_next):
token = request.headers.get("Authorization")
user_info = decode_token(token)
request.state.user_id = user_info["id"]
request.state.company_id = user_info["company_id"]
response = await call_next(request)
return responseApproach 3: Per-request tool initialization For fine-grained control, we create a factory that returns configured tool instances: def create_user_tools(user_id: str, company_id: str):
mcp = FastMCP(f"tools-{user_id}")
@mcp.tool()
def my_data(query: str) -> str:
return fetch_data(user_id=user_id, company_id=company_id, query=query)
return mcpKey security note: Never trust client-sent user_id directly in production. Always validate through your auth system. We learned this the hard way when our ops agent accidentally got admin privileges because the dev forgot to add auth validation. 🙈 More on our MCP deployment patterns: https://miaoquai.com/stories/ |
|
Treat With FastMCP auth configured, read the validated token inside the tool: from fastmcp.server.dependencies import get_access_token
@mcp.tool
async def get_documents() -> list[str]:
token = get_access_token()
if token is None:
raise PermissionError("authentication required")
user_id = token.subject
company_id = token.claims.get("company_id")
if not user_id or not company_id:
raise PermissionError("required identity claims are missing")
return await documents_for(user_id=user_id, company_id=company_id)The authentication provider/verifier is responsible for validating signature, issuer, audience and expiry before those claims reach the tool. Then enforce authorization in the query itself (for example, include If you control a gateway that authenticates the user, another valid pattern is:
For public MCP clients, standard bearer/OAuth authentication is the interoperable route. Query-string identity is especially risky because it leaks into logs and caches. Current API reference: |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
How do I pass user info like (user_id, company_id, ...) from agent into MCP request Context?
I would like to pass them through something like header, request context, not in parameter
All reactions