Skip to content

DocsMCP gateway

Connect an agent

Point an agent at the INFRO MCP gateway: recipes for OpenAI Agents SDK, Claude Agent SDK, LangChain, Vercel AI SDK, Google ADK, Mastra, CrewAI and editors.


INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.

The MCP gateway is served at https://mcp.infro.io/gateway/mcp and https://mcp.infro.io/gateway/<slug>/mcp, on the Business plan and above.

Every recipe below does the same three things: it points an MCP client at https://mcp.infro.io/gateway/mcp, sends Authorization: Bearer <gateway key>, and sends X-INFRO-Trace-Id so the agent's tool calls land on the same trace as its model calls. Use the gateway key the agent already uses for /v1, from the environment variable INFRO_API_KEY. To use one server under its own tool names, replace the URL with https://mcp.infro.io/gateway/<slug>/mcp.

The key sees only the tools an admin approved and granted to it. If the list is empty, the key has no grant yet: ask an owner or admin to grant it access to a server: Gateway keys, the key's Edit tool access, in the console.

To put model calls and tool calls of one run on one trace, send the same id on both: X-INFRO-Trace-Id (or metadata.trace_id) on your /v1 requests, and the header below on the MCP connection. Trace ids may contain letters, digits and _ . : -, up to 128 characters.

OpenAI Agents SDK

agent.py
import asyncio
import os
import uuid

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp


async def main() -> None:
    trace_id = f"run-{uuid.uuid4().hex}"
    async with MCPServerStreamableHttp(
        name="infro",
        params={
            "url": "https://mcp.infro.io/gateway/mcp",
            "headers": {
                "Authorization": f"Bearer {os.environ['INFRO_API_KEY']}",
                "X-INFRO-Trace-Id": trace_id,
            },
        },
    ) as infro:
        agent = Agent(
            name="Triage",
            instructions="Use the tools to answer questions about our issues.",
            mcp_servers=[infro],
        )
        result = await Runner.run(agent, "Which open issues are labeled bug?")
        print(result.final_output)


asyncio.run(main())

MCPServerStreamableHttp takes the URL and headers in params; the async with block connects and cleans up. The headers are fixed for the server instance, so create one per run when the trace id changes.

Claude Agent SDK

import { query } from "@anthropic-ai/claude-agent-sdk";

const traceId = `run-${crypto.randomUUID()}`;

for await (const message of query({
  prompt: "Which open issues are labeled bug?",
  options: {
    mcpServers: {
      infro: {
        type: "http",
        url: "https://mcp.infro.io/gateway/mcp",
        headers: {
          Authorization: `Bearer ${process.env.INFRO_API_KEY}`,
          "X-INFRO-Trace-Id": traceId,
        },
      },
    },
    allowedTools: ["mcp__infro__*"],
  },
})) {
  if (message.type === "result" && message.subtype === "success") {
    console.log(message.result);
  }
}

Tools appear to Claude as mcp__infro__<exposed name>, for example mcp__infro__github_list_issues. allowedTools: ["mcp__infro__*"] lets the agent call every tool the key was granted without asking; list single tools instead to keep the rest behind a permission prompt.

LangChain

langchain-mcp-adapters
import os
import uuid

from langchain_mcp_adapters.client import MultiServerMCPClient

trace_id = f"run-{uuid.uuid4().hex}"
client = MultiServerMCPClient(
    {
        "infro": {
            "transport": "http",
            "url": "https://mcp.infro.io/gateway/mcp",
            "headers": {
                "Authorization": f"Bearer {os.environ['INFRO_API_KEY']}",
                "X-INFRO-Trace-Id": trace_id,
            },
        }
    }
)
tools = await client.get_tools()  # pass these to your agent, e.g. create_agent(model, tools)

Only the http and sse transports of langchain-mcp-adapters send headers; use http. The package's README now says it is no longer actively maintained and that MCP support has moved into LangChain's langchain.mcp namespace. This recipe will be updated for that API once it is out of beta.

Vercel AI SDK

agent.ts
import { createMCPClient } from "@ai-sdk/mcp";
import { generateText } from "ai";

const traceId = `run-${crypto.randomUUID()}`;
const infro = await createMCPClient({
  transport: {
    type: "http",
    url: "https://mcp.infro.io/gateway/mcp",
    headers: {
      Authorization: `Bearer ${process.env.INFRO_API_KEY}`,
      "X-INFRO-Trace-Id": traceId,
    },
  },
});

try {
  const { text } = await generateText({
    model, // your model
    tools: await infro.tools(),
    prompt: "Which open issues are labeled bug?",
  });
  console.log(text);
} finally {
  await infro.close();
}

createMCPClient comes from @ai-sdk/mcp. Close the client when the call is done; with streamText, close it when the stream ends.

Google ADK

agent.py
import os

from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams


def infro_headers(context):
    # Called for every request with the run's ReadonlyContext: one trace id per session.
    return {"X-INFRO-Trace-Id": context.state.get("trace_id", context.invocation_id)}


root_agent = LlmAgent(
    model="gemini-flash-latest",
    name="triage",
    instruction="Use the tools to answer questions about our issues.",
    tools=[
        McpToolset(
            connection_params=StreamableHTTPConnectionParams(
                url="https://mcp.infro.io/gateway/mcp",
                headers={"Authorization": f"Bearer {os.environ['INFRO_API_KEY']}"},
            ),
            header_provider=infro_headers,
        )
    ],
)

header_provider is called with the run's context and its headers are added to the connection's, so the trace id can follow the session instead of being fixed when the agent is built. Set trace_id in the session state, or the sketch above falls back to ADK's invocation id. header_provider is documented in ADK's source rather than on its MCP page.

Mastra

tools.ts
import { MCPClient } from "@mastra/mcp";

const infro = new MCPClient({
  id: "infro",
  servers: {
    infro: {
      url: new URL("https://mcp.infro.io/gateway/mcp"),
      requestInit: {
        headers: {
          Authorization: `Bearer ${process.env.INFRO_API_KEY}`,
          "X-INFRO-Trace-Id": traceId,
        },
      },
    },
  },
});

const tools = await infro.listTools(); // give these to your Mastra agent
// ... when you are done:
await infro.disconnect();

Mastra prefixes each tool with the server's key, so tools appear as infro_<exposed name>.

CrewAI

crew.py
import os
import uuid

from crewai import Agent
from crewai.mcp import MCPServerHTTP

trace_id = f"run-{uuid.uuid4().hex}"
analyst = Agent(
    role="Issue triage analyst",
    goal="Answer questions about our open issues",
    backstory="Reads the issue tracker through the tools the organization approved.",
    mcps=[
        MCPServerHTTP(
            url="https://mcp.infro.io/gateway/mcp",
            headers={
                "Authorization": f"Bearer {os.environ['INFRO_API_KEY']}",
                "X-INFRO-Trace-Id": trace_id,
            },
            streamable=True,
        ),
    ],
)

Use MCPServerHTTP rather than a URL string in mcps: the string form cannot send headers.

Editors and assistants for people

People can use the organization's approved MCP servers from their editor through INFRO instead of configuring each server, and its secret, on their own machine. Use a personal gateway key with its own grants; sign-in instead of a key comes later.

claude mcp add --transport http infro https://mcp.infro.io/gateway/mcp --header "Authorization: Bearer $INFRO_API_KEY"
  • Claude Code: add --scope user to use it in every project. In a shared .mcp.json, write "headers": {"Authorization": "Bearer ${INFRO_API_KEY}"} so the key stays out of the repository.
  • Cursor: ~/.cursor/mcp.json (every project) or .cursor/mcp.json; set INFRO_API_KEY in the environment Cursor starts from.
  • VS Code: .vscode/mcp.json. VS Code asks for the key once and stores it securely.

Provider-hosted connectors (not supported yet)

Some model APIs can call an MCP server themselves, from the provider's infrastructure: the Anthropic API's MCP connector and the mcp tool of OpenAI's Responses API. These are not supported yet. They would have to carry your INFRO gateway key to the provider, and today that key can also call models. Keys that can reach the MCP gateway and nothing else come later, with recipes for both connectors. Until then, connect MCP from your own agent code, as in the recipes above.

↑ ↓ navigate↵ open/ anywhere in docs