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#

  1. Run the wardowl MCP server over streamable HTTP with a token verifier, as in MCP.

  2. 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.

  3. Connect the framework’s MCP client to the server’s URL with that token in the Authorization: Bearer header, 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:

  • Pydantic AI: MCP client

  • CrewAI: streamable HTTP MCP servers

  • AutoGen: StreamableHttpServerParams takes the URL and a headers dict, and McpWorkbench gives the server’s tools to an agent.

  • Semantic Kernel: MCP plugins (pip install "semantic-kernel[mcp]"): MCPStreamableHttpPlugin connects to a remote server.

  • DSPy: MCP: open the connection with the mcp package’s streamable HTTP client and a ClientSession, as below, then wrap each tool with dspy.Tool.from_mcp_tool; run the agent inside the session, since the tools call back into it.

  • LangChain and LangGraph: MultiServerMCPClient from langchain-mcp-adapters takes the header in a server’s connection settings ("transport": "http", "url", "headers"). As of October 2026 it requires mcp 1.x, so install it where the agent runs, not beside wardowl[mcp].

  • LlamaIndex: BasicMCPClient(url, headers=...) from llama-index-tools-mcp, with McpToolSpec to turn the server’s tools into LlamaIndex tools.

  • Haystack: MCPTool or MCPToolset from mcp-haystack, with StreamableHttpServerInfo(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.