Part 21 of 22

How to use remote MCP servers with the OpenAI Responses API

LLM Mart · Sep 22, 2026 · 1 views 179 listing impressions
How to use remote MCP servers with the OpenAI Responses API

The OpenAI Responses API reaches remote MCP servers through a built-in tool of type mcp. You give it a server_url, a label, and an approval policy; the API fetches the server's tool list, exposes those tools to the model, and executes the calls the model chooses to make.

The integration itself is a few lines. The decisions that matter are which tools you import, whether calls require your approval, and what data leaves your control when they run.

The minimum request

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)

The same tool type covers two things: remote MCP servers, meaning any public-internet server implementing the protocol, and connectors, which are OpenAI-maintained MCP wrappers for services such as Google Workspace, Dropbox, and Outlook.

The API works with servers speaking either Streamable HTTP or the older HTTP/SSE transport. If MCP is new, what an MCP server is covers the model the rest of this assumes.

A note on the example above: require_approval: "never" is used here only because the server is a public dice roller with no data and no side effects. It is not the setting to reach for first, and the default exists for a reason.

Step 1: the API lists the server's tools

When you name a server in tools, the API fetches its tool list and emits an mcp_list_tools output item containing the tools it successfully imported:

{
  "id": "mcpl_68a6102a4968819c8177b05584dd627b0679e572a900e618",
  "type": "mcp_list_tools",
  "server_label": "dmcp",
  "tools": [
    {
      "name": "roll",
      "description": "Given a string of text describing a dice roll...",
      "input_schema": {
        "type": "object",
        "properties": { "diceRollExpression": { "type": "string" } },
        "required": ["diceRollExpression"],
        "additionalProperties": false
      },
      "annotations": null
    }
  ]
}

Keep this item in context. As long as mcp_list_tools is present in a request's context, the API does not re-fetch the tool list on each turn of a conversation. Dropping it costs a round trip and tokens on every turn.

It is also the artefact worth reading before you ship. This is the actual list of things a model can now do on your behalf — names, descriptions, and schemas — and it is the version you should compare against the server's documentation.

Step 2: filter the tools you import

Some MCP servers expose dozens of tools. Importing all of them raises cost and latency and gives the model more ways to pick wrong.

{
    "type": "mcp",
    "server_label": "dmcp",
    "server_url": "https://dmcp-server.deno.dev/mcp",
    "allowed_tools": ["roll"],
    "require_approval": "never",
}

allowed_tools is the cheapest safety control in the whole integration. An import list of the three tools your workflow actually uses removes every other tool from the model's reach, including ones the server adds in a later release.

For servers with very large tool sets, defer_loading: true works alongside tool search: the model still sees the server's label and description and can decide to search it, but individual function definitions load only when needed. That reduces token usage without narrowing what is reachable — so it complements allowed_tools rather than replacing it.

Step 3: tool calls and approvals

When the model calls a tool, the API makes the request to the remote server and records an mcp_call item with both the arguments and the output:

{
  "id": "mcp_68a6102d8948819c9b1490d36d5ffa4a0679e572a900e618",
  "type": "mcp_call",
  "approval_request_id": null,
  "arguments": "{\"diceRollExpression\":\"2d4 + 1\"}",
  "error": null,
  "name": "roll",
  "output": "4",
  "server_label": "dmcp"
}

Failed calls populate error with MCP protocol errors, tool execution errors, or connectivity failures. A model can make several calls in one request, so expect multiple items.

By default, OpenAI requests your approval before any data is shared with a connector or remote MCP server. That produces an mcp_approval_request item:

{
  "id": "mcpr_68a619e1d82c8190b50c1ccba7ad18ef0d2d23a86136d339",
  "type": "mcp_approval_request",
  "arguments": "{\"diceRollExpression\":\"2d4 + 1\"}",
  "name": "roll",
  "server_label": "dmcp"
}

You respond by creating a new Response with an mcp_approval_response item:

resp = client.responses.create(
    model="gpt-6-astra",
    tools=[{
        "type": "mcp",
        "server_label": "dmcp",
        "server_url": "https://dmcp-server.deno.dev/mcp",
        "require_approval": "always",
    }],
    previous_response_id="resp_682d498bdefc81918b4a6aa477bfafd904ad1e533afccbfa",
    input=[{
        "type": "mcp_approval_response",
        "approve": True,
        "approval_request_id": "mcpr_682d498e3bd4819196a0ce1664f8e77b04ad1e533afccbfa",
    }],
)

previous_response_id chains the new Response to the one that raised the request. You can also pass the outputs of one response as inputs to another when you want full control over what enters context.

Step 4: relax approvals deliberately, not globally

Once you trust a server, you can skip approvals for latency — but the useful form is per-tool, not per-server:

{
    "type": "mcp",
    "server_label": "deepwiki",
    "server_url": "https://mcp.deepwiki.com/mcp",
    "require_approval": {
        "never": { "tool_names": ["ask_question", "read_wiki_structure"] }
    },
}

This is the setting most integrations should land on: read-only tools auto-approved, anything that writes, sends, publishes, or spends still gated. require_approval: "never" across a whole server gives up the control for every tool the server currently has and every tool it adds later.

Authentication

Most servers require a credential. The authorization field carries an OAuth access token, sent as a bearer token:

import os
from openai import OpenAI

client = OpenAI()

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"],
    }],
)

Read the token from your environment or secret manager, never from a literal in source. Request the narrowest scopes the workflow needs, and remember that a token scoped for writes plus require_approval: "never" is an agent that can spend money without asking. How those scopes are negotiated on the MCP side is covered in MCP authentication explained.

Connectors follow a different path: they use OpenAI's own authorisation flow for the underlying service rather than a server URL you supply.

For a server that is private, on-premises, or behind a firewall, Secure MCP Tunnel connects it to supported OpenAI products without exposing it to the public internet — which is the supported answer to "our MCP server is internal."

Risks worth taking seriously

OpenAI states the position plainly: remote MCP servers are third-party services that OpenAI has not verified, subject to their own terms, and able to access, send, and receive data and take action.

Prompt injection. Giving a model access to servers that hold sensitive data or take actions makes injection materially more dangerous, especially when the prompt contains user-provided content. Malicious servers may include hidden instructions designed to make the model behave unexpectedly. Built-in safeguards exist, but reviewing inputs and outputs remains your job — and prompt injection is not an XSS problem explains why the usual sanitisation instincts do not transfer.

URLs in tool output. Requesting URLs or embedding image URLs that came from a tool result is dangerous unless you trust the domains. Treat tool output as untrusted content, because that is what it is.

Aggregators. Prefer official servers hosted by the service provider — Stripe's server at mcp.stripe.com rather than a third party's Stripe wrapper. Where you must use an aggregator, do the due diligence on how it handles your data, since your requests pass through it.

Tool behaviour can change. A server can alter what a tool does between one call and the next without changing its name. This is the argument for allowed_tools and for re-reading mcp_list_tools after a server updates.

Log what you send. With store=true, data is logged via the API for 30 days unless Zero Data Retention is enabled. Logging in your own systems and reviewing periodically is the only way to know what has actually been shared.

If you are evaluating an unfamiliar server, the checklist in how to vet AI agent skills before installing them transfers almost directly.

Zero Data Retention and data residency stop at the server

The MCP tool is compatible with Zero Data Retention and Data Residency, with one boundary that is easy to miss. If your organisation has Data Residency in Europe, OpenAI limits inference and storage of Customer Content to Europe up until the point data is sent to the MCP server. Beyond that, the server's own retention and residency policies apply, and ensuring they meet your requirements is your responsibility.

For a regulated workload, that makes the server's hosting location part of your compliance boundary, not an implementation detail.

A safe first integration

  1. Start with a public, read-only server and no credential.
  2. Set allowed_tools to the one or two tools you need.
  3. Leave approvals on and read the first few mcp_approval_request items in full.
  4. Inspect mcp_list_tools and compare it with the server's documentation.
  5. Only then decide which specific tools are safe to auto-approve.
  6. Add authentication last, with the narrowest scopes that work.

Next step: Connect the Responses API to LLM Mart's public MCP endpoint at https://llmmart.ai/mcp and begin with a read-only catalogue search — no credential, no write path, and a real tool list to inspect.

Sources

0 0 0 0 Sign in to react

Comments (0)

Sign in to join the conversation.

No comments yet.