# Use remote MCP servers in the OpenAI API and Agents SDK

> Call remote MCP servers from the OpenAI Responses API with the mcp tool type: server_url, allowed_tools, approvals and authorization, plus Agents SDK examples.

Published: 2026-10-04 · By: Divy Chaurasia · Level: intermediate · Time: 20 min · Tags: mcp, openai-api, agents-sdk

The Responses API can use any remote MCP server as a tool. You pass the server's URL in the request, and OpenAI's side lists the server's tools, lets the model call them, and puts the results back into the model's context. Your code doesn't need an MCP client.

This guide covers the `mcp` tool type in the Responses API, then the same thing in the Agents SDK, with Python and TypeScript examples.

## Before you start

```bash
pip install --upgrade openai           # Python
npm install openai                     # TypeScript
export OPENAI_API_KEY=sk-...
```

You also need a remote MCP server that speaks Streamable HTTP or HTTP/SSE. The examples use two public servers that need no auth: OpenAI's dice demo (`https://dmcp-server.deno.dev/mcp`) and DeepWiki (`https://mcp.deepwiki.com/mcp`). The model is `gpt-6-astra`, which OpenAI's MCP guide uses; check the models page for MCP tool support if you pick another.

> **Note:** You pay only for the tokens used to import tool definitions and make tool calls. There's no extra fee per MCP call.

## 1. Make your first call

```python
from openai import OpenAI

client = OpenAI()

resp = client.responses.create(
    model="gpt-6-astra",
    tools=[
        {
            "type": "mcp",
            "server_label": "dmcp",
            "server_description": "A Dungeons and Dragons MCP server to assist with dice rolling.",
            "server_url": "https://dmcp-server.deno.dev/mcp",
            "require_approval": "never",
        }
    ],
    input="Roll 2d4+1",
)
print(resp.output_text)
```

```typescript
import OpenAI from "openai";

const client = new OpenAI();

const resp = await client.responses.create({
  model: "gpt-6-astra",
  tools: [
    {
      type: "mcp",
      server_label: "dmcp",
      server_description: "A Dungeons and Dragons MCP server to assist with dice rolling.",
      server_url: "https://dmcp-server.deno.dev/mcp",
      require_approval: "never",
    },
  ],
  input: "Roll 2d4+1",
});
console.log(resp.output_text);
```

The fields:

| Field | Purpose |
| --- | --- |
| `type` | Always `"mcp"`. |
| `server_label` | Required. A short name used in output items and approvals. |
| `server_url` | The remote endpoint, including its path. |
| `server_description` | Optional. Helps the model decide when to use the server. |
| `require_approval` | `"always"`, `"never"`, or a per-tool filter (step 4). |
| `allowed_tools` | Optional list that limits which tools are imported (step 3). |
| `authorization` | Optional OAuth token or API key for the server (step 5). |

## 2. Read the output items

MCP activity shows up as items in `resp.output`:

- **`mcp_list_tools`**: the tools imported from the server, with their input schemas. The API fetches this on the first turn. As long as this item stays in the conversation context, the API won't fetch the list again on later turns, so keep it there to save latency.
- **`mcp_call`**: one per tool call, with `name`, the `arguments` the model sent, the `output` the server returned, and an `error` field if something failed.
- **`mcp_approval_request`**: a call waiting for your approval.

```python
for item in resp.output:
    if item.type == "mcp_call":
        print(item.name, item.arguments, "->", item.output, item.error)
```

## 3. Limit the tools with allowed_tools

Servers can expose dozens of tools. Every imported definition costs tokens and gives the model more ways to go wrong. Import only what you need:

```python
tool = {
    "type": "mcp",
    "server_label": "deepwiki",
    "server_url": "https://mcp.deepwiki.com/mcp",
    "allowed_tools": ["ask_question", "read_wiki_structure"],
    "require_approval": "never",
}
```

If you use tool search, `"defer_loading": True` on the MCP tool defers loading its definitions until the model needs them.

> **MCP server:** [DeepWiki](https://chatgptplugins.directory/mcp/deepwiki): Ask questions about any public GitHub repo using DeepWiki's generated docs
## 4. Handle approvals

By default the API asks for your approval before sending any data to a remote MCP server. Instead of calling the tool, the response contains `mcp_approval_request` items. You review them and reply with `mcp_approval_response` items in a follow-up request:

```python
tool = {
    "type": "mcp",
    "server_label": "dmcp",
    "server_url": "https://dmcp-server.deno.dev/mcp",
    "require_approval": "always",
}

resp = client.responses.create(model="gpt-6-astra", tools=[tool], input="Roll 2d4+1")

approvals = []
for item in resp.output:
    if item.type == "mcp_approval_request":
        print(f"{item.server_label}.{item.name} wants: {item.arguments}")
        approvals.append({
            "type": "mcp_approval_response",
            "approve": True,  # decide per call in real code
            "approval_request_id": item.id,
        })

if approvals:
    resp = client.responses.create(
        model="gpt-6-astra",
        tools=[tool],
        previous_response_id=resp.id,
        input=approvals,
    )
print(resp.output_text)
```

```typescript
const tool = {
  type: "mcp" as const,
  server_label: "dmcp",
  server_url: "https://dmcp-server.deno.dev/mcp",
  require_approval: "always" as const,
};

let resp = await client.responses.create({ model: "gpt-6-astra", tools: [tool], input: "Roll 2d4+1" });

const approvals = resp.output.flatMap((item) =>
  item.type === "mcp_approval_request"
    ? [{
        type: "mcp_approval_response" as const,
        approve: true, // decide per call in real code
        approval_request_id: item.id,
      }]
    : []
);

if (approvals.length > 0) {
  resp = await client.responses.create({
    model: "gpt-6-astra",
    tools: [tool],
    previous_response_id: resp.id,
    input: approvals,
  });
}
console.log(resp.output_text);
```

Once you trust a server, skip approvals for its read-only tools and keep them for everything else:

```python
"require_approval": {"never": {"tool_names": ["ask_question", "read_wiki_structure"]}}
```

`"never"` skips approvals for all tools on that server.

## 5. Authenticate

Most real servers need a token. Pass it in `authorization`:

```python
import os

resp = client.responses.create(
    model="gpt-6-astra",
    input="Create a payment link for $20",
    tools=[
        {
            "type": "mcp",
            "server_label": "stripe",
            "server_url": "https://mcp.stripe.com",
            "authorization": os.environ["STRIPE_OAUTH_ACCESS_TOKEN"],
        }
    ],
)
```

The API doesn't store this value and doesn't echo it in the response object, so you must send it on **every** request, including approval follow-ups. Your application is responsible for getting the token, for example through the service's OAuth flow.

> **MCP server:** [Stripe](https://chatgptplugins.directory/mcp/stripe): Query and act on your Stripe account and search Stripe docs from any agent
## 6. Private servers and legacy connectors

- **Private or on-premises servers.** Use Secure MCP Tunnel instead of exposing the server publicly. Run OpenAI's `tunnel-client` next to the server and pass `tunnel_id` in place of `server_url`.
- **Built-in connectors** (`connector_id`, for example `connector_gmail`) are deprecated for models released after September 1, 2026. Older models keep them. For new work, use the service's own remote MCP server; GitHub's, for example, is `https://api.githubcopilot.com/mcp/`.

## 7. Use MCP from the Agents SDK

The Agents SDK supports two patterns.

**Hosted MCP tools** send the round trip through the Responses API, exactly as above:

```python
import asyncio
from agents import Agent, HostedMCPTool, Runner

agent = Agent(
    name="Repo expert",
    instructions="Answer questions about GitHub repositories using DeepWiki.",
    model="gpt-6-astra",
    tools=[
        HostedMCPTool(
            tool_config={
                "type": "mcp",
                "server_label": "deepwiki",
                "server_url": "https://mcp.deepwiki.com/mcp",
                "allowed_tools": ["ask_question"],
                "require_approval": "never",
            }
        )
    ],
)

async def main():
    result = await Runner.run(agent, "How does routing work in vercel/next.js?")
    print(result.final_output)

asyncio.run(main())
```

```typescript
import { Agent, hostedMcpTool, run } from "@openai/agents";

const agent = new Agent({
  name: "Repo expert",
  instructions: "Answer questions about GitHub repositories using DeepWiki.",
  model: "gpt-6-astra",
  tools: [
    hostedMcpTool({
      serverLabel: "deepwiki",
      serverUrl: "https://mcp.deepwiki.com/mcp",
      allowedTools: ["ask_question"],
      requireApproval: "never",
    }),
  ],
});

const result = await run(agent, "How does routing work in vercel/next.js?");
console.log(result.finalOutput);
```

> **Note:** In the TypeScript SDK, `hostedMcpTool` defaults `requireApproval` to `"never"`, unlike the raw Responses API. Set it explicitly so the behavior is obvious to the next reader.

**SDK-managed servers** connect from your own process. Use this for servers on `localhost` or a private network, where OpenAI can't reach them:

```python
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async def main():
    async with MCPServerStreamableHttp(
        name="dice",
        params={"url": "http://localhost:8787/mcp"},
    ) as server:
        agent = Agent(name="Dice", instructions="Use the tools to roll dice.",
                      model="gpt-6-astra", mcp_servers=[server])
        result = await Runner.run(agent, "Roll 3d6")
        print(result.final_output)

asyncio.run(main())
```

```typescript
import { Agent, MCPServerStreamableHttp, run } from "@openai/agents";

const server = new MCPServerStreamableHttp({ url: "http://localhost:8787/mcp", name: "dice" });
await server.connect();
try {
  const agent = new Agent({ name: "Dice", instructions: "Use the tools to roll dice.",
                            model: "gpt-6-astra", mcpServers: [server] });
  const result = await run(agent, "Roll 3d6");
  console.log(result.finalOutput);
} finally {
  await server.close();
}
```

`MCPServerStdio` works the same way for servers started as a local command. Tracing is on by default, so every MCP call shows up on the Platform's Traces page.

## Safety

> **Warning:** Remote MCP servers aren't verified by OpenAI. A malicious server can try to pull data out of anything in the model's context, or hide instructions in tool descriptions and results.

- Prefer official servers run by the service itself (Stripe's at `mcp.stripe.com`, not a third-party proxy).
- Keep approvals on for anything that writes, sends or spends.
- Limit tools with `allowed_tools`.
- Log what you send to each server and review it.
- Be careful with URLs that come back in tool output before fetching or embedding them.
- Data sent to an MCP server falls under that server's retention and residency policies, not OpenAI's.

## Troubleshooting

| Problem | Fix |
| --- | --- |
| No `mcp_list_tools` item, or an error listing tools | Check the URL and path, and that the server speaks Streamable HTTP or SSE. Test it with [MCP Inspector](/tutorials/test-mcp-server-with-inspector). |
| Model never calls the tool | Add a `server_description`, improve tool descriptions, or name the tool in the prompt. |
| `401` errors in `mcp_call.error` | The `authorization` value is missing or expired. Send it on every request. |
| Approval loop never ends | Reuse `previous_response_id` and include every pending `approval_request_id`. |
| Agents SDK can't reach `localhost` | Hosted tools run on OpenAI's side. Use `MCPServerStreamableHttp` instead. |

> **MCP server:** [GitHub](https://chatgptplugins.directory/mcp/github): Manage repos, issues, pull requests and Actions from your AI agent
## Next steps

- Find remote servers to call in the [MCP directory](/mcp).
- Build your own server: [Build your first ChatGPT app](/tutorials/build-your-first-chatgpt-app).
- Use the same servers in ChatGPT: [How to add a custom MCP server to ChatGPT](/tutorials/add-mcp-server-to-chatgpt).

## Listings in this tutorial

- [DeepWiki](https://chatgptplugins.directory/mcp/deepwiki.md): Ask questions about any public GitHub repo using DeepWiki's generated docs
- [Stripe](https://chatgptplugins.directory/mcp/stripe.md): Query and act on your Stripe account and search Stripe docs from any agent
- [GitHub](https://chatgptplugins.directory/mcp/github.md): Manage repos, issues, pull requests and Actions from your AI agent

---
Source: https://chatgptplugins.directory/tutorials/mcp-in-the-openai-api · Index: https://chatgptplugins.directory/llms.txt · Independent and community-run. Not affiliated with or endorsed by OpenAI. ChatGPT is a trademark of OpenAI.
