Connect: OpenAI hosted MCP (Responses API)¶
Recipe · through our public MCP server · their docs checked 2026-10-30
With the Responses API mcp tool, OpenAI's servers call our server for you. So our server
must be reachable from the internet, with auth. Data leaves our server to OpenAI with each
tool result.
sequenceDiagram
participant App as Your app
participant O as OpenAI Responses
participant S as public_app (/mcp)
App->>O: responses.create(tools=[{type: "mcp", …}])
O->>S: tools/list, tools/call + Authorization
S-->>O: result (hosted tools only)
O-->>App: output (+ mcp_approval_request when require_approval says so)
Our side¶
def public_app(tools: Registry, host: str, agent_token: str, reviewer_token: str) -> Any:
"""REST at /, MCP at /mcp, one bearer check for both: for a host on the internet."""
from mcp.server.transport_security import TransportSecuritySettings
from agent_adapter.server import create_app
tokens = {
agent_token: Principal("hosted-agent"), # the token the remote framework sends
reviewer_token: Principal("reviewer@example.com", scopes=frozenset({APPROVER_SCOPE})),
}
security = TransportSecuritySettings(allowed_hosts=[host], allowed_origins=[f"https://{host}"])
return create_app(
tools,
authenticate=bearer_auth(tokens),
host="0.0.0.0", # a public bind: the public-host checks apply
allowed_hosts=[host], # REST: any other Host header gets 421
mcp_options={"transport_security": security}, # MCP: the same rule
)
hosted = registry.subset(tags=["hosted"]) # no confirm tools: hosted runtimes cannot resume them
Serve public_app(hosted, host, agent_token, reviewer_token).
Their side¶
response = client.responses.create(
model=model,
input="What is the shipping rate to the US?",
tools=[
{
"type": "mcp",
"server_label": "shop_tools",
"server_description": "Shop lookups: shipping rates and invoices.",
"server_url": "https://tools.example.com/mcp",
"authorization": agent_token, # not stored by OpenAI: send it on every request
"allowed_tools": ["shipping_rate"],
"require_approval": "always",
}
],
)
| Key | Meaning |
|---|---|
type: "mcp", server_label, server_url |
which server, under which name |
authorization |
the token OpenAI sends to us; resend it on each request |
allowed_tools |
the tools the model may see |
require_approval |
"always", "never", or {"never": {"tool_names": [...]}} |
mcp_approval_request → mcp_approval_response |
OpenAI's approval round trip: reply with approval_request_id and approve, chained by previous_response_id |
Checklist¶
| Check | Why |
|---|---|
serve hosted and list it in allowed_tools |
OpenAI does not send our resume keys back; a confirm tool would never run |
require_approval is OpenAI's gate |
an approve: true there adds a step; it never approves our confirm tools (framework approvals) |
a token only for OpenAI, with no APPROVER_SCOPE |
it is sent through a third party; rotate it on its own |
| everything on the public-host checklist |
Sources¶
- OpenAI remote MCP guide: https://developers.openai.com/api/docs/guides/tools-remote-mcp