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

Source: https://infro.io/docs/mcp-gateway/connect

> **Note:** 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](https://infro.io/early-access).

> **Note:** 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`.

> **Note:** 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

```python
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

**TypeScript**

```typescript
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);
  }
}
```

**Python**

```python
import asyncio
import os
import uuid

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query


async def main() -> None:
    trace_id = f"run-{uuid.uuid4().hex}"
    options = ClaudeAgentOptions(
        mcp_servers={
            "infro": {
                "type": "http",
                "url": "https://mcp.infro.io/gateway/mcp",
                "headers": {
                    "Authorization": f"Bearer {os.environ['INFRO_API_KEY']}",
                    "X-INFRO-Trace-Id": trace_id,
                },
            }
        },
        allowed_tools=["mcp__infro__*"],
    )
    async for message in query(prompt="Which open issues are labeled bug?", options=options):
        if isinstance(message, ResultMessage):
            print(message.result)


asyncio.run(main())
```

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

```python
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

```typescript
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

```python
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

```typescript
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

```python
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 Code**

```bash
claude mcp add --transport http infro https://mcp.infro.io/gateway/mcp --header "Authorization: Bearer $INFRO_API_KEY"
```

**Cursor**

```json
{
  "mcpServers": {
    "infro": {
      "url": "https://mcp.infro.io/gateway/mcp",
      "headers": {
        "Authorization": "Bearer ${env:INFRO_API_KEY}"
      }
    }
  }
}
```

**VS Code**

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "infro_key",
      "description": "INFRO gateway key",
      "password": true
    }
  ],
  "servers": {
    "infro": {
      "type": "http",
      "url": "https://mcp.infro.io/gateway/mcp",
      "headers": {
        "Authorization": "Bearer ${input:infro_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.
