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
- Start with a public, read-only server and no credential.
- Set
allowed_toolsto the one or two tools you need. - Leave approvals on and read the first few
mcp_approval_requestitems in full. - Inspect
mcp_list_toolsand compare it with the server's documentation. - Only then decide which specific tools are safe to auto-approve.
- 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
Comments (0)
Sign in to join the conversation.
No comments yet.