Agent frameworks#
Agent frameworks that speak MCP use wardowl through its MCP search tool, with no adapter: the framework is an MCP client, and wardowl’s server decides whose permissions apply from the access token on each request. The model never chooses the subject, and the framework never sees a chunk the user may not read.
The pattern#
Run the wardowl MCP server over streamable HTTP with a token verifier, as in MCP.
In your application, get an access token for the user the agent is acting for (for example through OAuth token exchange), not a shared service token.
Connect the framework’s MCP client to the server’s URL with that token in the
Authorization: Bearerheader, and give the agent the server’s tools.
A token issued to the agent alone makes every user’s search run with the agent’s permissions; Whose permissions apply explains how to carry both identities.
OpenAI Agents SDK#
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def answer(question: str, user_token: str) -> str:
async with MCPServerStreamableHttp(
name="knowledge",
params={
"url": "https://knowledge.example.com/mcp",
"headers": {"Authorization": f"Bearer {user_token}"},
},
) as knowledge:
agent = Agent(name="assistant", mcp_servers=[knowledge])
result = await Runner.run(agent, question)
return result.final_output
The connection carries one user’s token, so open it per user or per session, never once for the whole process.
Other frameworks#
The same three steps apply to any client that can send a header with streamable HTTP. Set the
Authorization header through each framework’s MCP client options, which change between releases,
so follow its current documentation:
AutoGen:
StreamableHttpServerParamstakes the URL and aheadersdict, andMcpWorkbenchgives the server’s tools to an agent.Semantic Kernel: MCP plugins (
pip install "semantic-kernel[mcp]"):MCPStreamableHttpPluginconnects to a remote server.DSPy: MCP: open the connection with the
mcppackage’s streamable HTTP client and aClientSession, as below, then wrap each tool withdspy.Tool.from_mcp_tool; run the agent inside the session, since the tools call back into it.LangChain and LangGraph:
MultiServerMCPClientfromlangchain-mcp-adapterstakes the header in a server’s connection settings ("transport": "http","url","headers"). As of October 2026 it requiresmcp1.x, so install it where the agent runs, not besidewardowl[mcp].LlamaIndex:
BasicMCPClient(url, headers=...)fromllama-index-tools-mcp, withMcpToolSpecto turn the server’s tools into LlamaIndex tools.Haystack:
MCPToolorMCPToolsetfrommcp-haystack, withStreamableHttpServerInfo(url=..., token=user_token), which sends the token as a bearer header.
For LangChain and LangGraph, LlamaIndex and Haystack, wardowl also has in-process integrations that bind the subject per session: LangChain, LlamaIndex, Haystack.
The MCP Python SDK#
In version 2 of the mcp package, the streamable HTTP client takes no headers argument. Put the
header on the httpx2.AsyncClient (the HTTP library mcp 2 uses) and pass that client as
http_client:
import httpx2
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def list_knowledge_tools(user_token: str):
headers = {"Authorization": f"Bearer {user_token}"}
async with httpx2.AsyncClient(headers=headers, timeout=httpx2.Timeout(30.0, read=300.0)) as http:
async with streamable_http_client(KNOWLEDGE_URL, http_client=http) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
return await session.list_tools()
A client you pass replaces the SDK’s default one, timeouts included, so set them as the SDK’s own examples do (30 seconds, and 300 for reading the event stream).
Check it#
Call the tool once with a token for a user who may read nothing: it must return no results. A call without a token must fail. Both are covered by wardowl’s own MCP tests, but they also prove that your client sends the header you intended.