
Key takeaways
- Use hosted MCP for public remote tools, and SDK-managed stdio or streamable HTTP for local or private servers.
- Put approval around sensitive actions and keep the allowed tool set narrow.
- Match state, tracing, and cleanup to the runtime that owns the connection.
Why an Open AI Agent SDK MCP connection is not readiness
Making tools appear is a useful milestone. It is not the finish line. An OpenAI Agents SDK MCP integration still has to place the connection, reach the server, identify who may act, keep discovery current, handle contract changes, expose failure, and close the resources it opened.
That is the role of the harness. The model may decide to call a tool, but the surrounding system owns authority, observation, and recovery. If the protocol itself is the unfamiliar part, start with our MCP server primer. Here, the narrower question is whether an MCP OpenAI integration is ready to move beyond a successful connection test.
A connection demo answers a small question: can this agent see and call something from this server under the conditions of the demo? It does not settle the operating boundaries around that call. A live integration needs named owners for the connection, the credentials, the action, the trace, and the resources left behind when work ends.
We find it useful to divide that responsibility across three moments. Before a call, the harness chooses the runtime, establishes identity and authority, and obtains the tool contract. During a call, it observes what the agent and server do while containing access to the intended tools. After a call, it preserves the useful execution record and closes what the runtime opened. This is a design discipline, not a claim that the SDK performs each responsibility automatically.
The boundary matters because “connected” can describe several different states. A public endpoint may be reachable from one runtime but not another. A server may publish a tool whose definition later changes. An authenticated user may still face an approval boundary for a sensitive action. A finished response may arrive while a local process or tunnel remains open. Treating all of these as connection details makes the integration difficult to reason about when one of them fails.
The practical alternative is an acceptance record that a second engineer can inspect. It should name the selected runtime, the server being trusted, the authentication and approval scope, the freshness policy, the contracts that move together, the expected failure signals, the trace setting, and the cleanup owner. A code snippet can accompany that record, but it cannot replace those decisions.
This approach also keeps the smallest controllable architecture as the baseline. Add hosting, caching, approval, or live evaluation because the workload calls for it. Do not treat more tools, more infrastructure, or more recorded events as a maturity score. The useful question is whether every extra moving part has an explicit boundary and a testable reason to be there.
Before live traffic, require a named owner and a known failure signal at each boundary. Revisit those decisions whenever the server contract, runtime, or authority model changes. An apparently successful connection can still leave the application without a clear owner for authority, observation, or teardown.
Keep the decisions close to the code path they govern so a change can be reviewed against the right boundary. If a transport moves from an application environment to a hosted service, revisit reachability. If tool definitions begin changing more often, revisit discovery freshness. If a read-only tool gains a write action, revisit authority and approval. A change should alter the decision it actually touches without obscuring the others.
It also makes partial success easier to describe. A server can pass discovery while failing initialization in another runtime. A tool can be callable while its user or action scope remains too broad. A run can return a result while leaving teardown unresolved. “MCP works” hides those distinctions. A gate-by-gate decision shows which part works, which part does not, and which owner has work left.
That level of precision is enough for an initial acceptance decision. It avoids claiming that one successful path proves reliability across every external service or failure mode. It also avoids demanding a large framework before the first controlled integration exists. Start with the connection you can place, authorize, observe, and close. Expand only when another supported workload requires a wider boundary.
Run the integration-readiness checklist before live traffic
Treat the seven gates as one acceptance decision. A pass requires concrete answers in every row, not merely a populated tool list.

| Gate | Decision question | Pass condition | Failure signal |
|---|---|---|---|
| 1. Placement and reachability | Where does the MCP connection run, and can that runtime reach the server? | Agents API HTTP with connection_origin: "service" runs at OpenAI and needs no environment. HTTP with connection_origin: "environment" runs in the session environment and requires one. Stdio runs as a process in the session environment and also requires an environment. An OpenAI-hosted HTTP server is reachable from OpenAI. Public remote tools may use hosted MCP; local or private servers may use SDK-managed stdio or streamable HTTP. | The chosen placement needs an environment that is absent, or the hosted runtime cannot reach its HTTP server. |
| 2. Identity, authentication, and approval | Who operates the server, who is the user, and which actions may proceed? | Prefer the service provider's official server over a third-party proxy. Plugins exposing customer data or write actions use the expected OAuth 2.1 pattern. allowed_tools and require_approval bound sensitive actions. | Server identity, user identity, allowed tools, or approval scope is unclear. |
| 3. Discovery freshness | Can the agent rely on the current definitions? | Each run calls list_tools() on every server. Enable cache_tools_list only when definitions change infrequently, and call invalidate_tools_cache() when a cached list must be refreshed. | The agent sees a stale contract, or repeated remote discovery adds avoidable latency. |
| 4. Contract handling | Do registration, schemas, and consumers change together? | In the AWS cookbook example, Python schemas, MCP registrations, MCP response schemas, and the widget update together while preserving strict validation, read-only annotations, and credential isolation. | One component in that example still expects an older shape. |
| 5. Output URL trust | What happens to a URL returned by a tool? | Trust its domain and service before the application requests, embeds, or otherwise uses it. | An untrusted URL triggers another application action. |
| 6. External failure behavior | What happens when a dependency is slow, unavailable, or returns an error? | Define behavior using established patterns such as timeouts, retries, and circuit breakers. | The dependency stalls or errors without a planned response. |
| 7. Observation and teardown | Can you reconstruct MCP activity and close the right resources? | Tracing captures MCP tool-list requests and MCP-related tool-call information. Cleanup matches the selected resource because Agents API sessions, SDK sessions, Responses conversations, and sandboxes differ. In the AWS example, stop the MCP server and tunnel after testing. | MCP activity is opaque or an owned resource remains open. |
How does one MCP-backed tool call move through the system?
A bounded example should show the whole lifecycle, not stop when discovery succeeds. An MCP server publishes tool definitions and runs calls. The agent side discovers those definitions, calls the server, and returns the result to the agent. For a non-hosted Streamable HTTP, SSE, or stdio server, the reported SDK connection pattern uses either an async context manager or explicit server.connect() and server.cleanup() calls.

- Connection. Enter the server's async context manager, or call
server.connect()explicitly. The connection boundary now sits in the application runtime. - Discovery. Let the run obtain the server's published tool definitions through
list_tools(). - Execution. When the agent selects an exposed tool, send that call to the MCP server. The server runs the tool.
- Result return. Return the server's result to the agent so the run can continue with that result in context.
- Teardown. Leave the async context manager, or pair the explicit connection with
server.cleanup().
The two lifecycle patterns are alternatives:
- Scoped lifetime: the async context manager places connection and teardown around the work as one bounded block.
- Explicit lifetime:
server.connect()opens the connection, andserver.cleanup()closes it when the owning application reaches its teardown path.
This five-stage shape makes ownership visible. It also exposes the gap in snippets that demonstrate discovery but omit teardown. The reported non-hosted connection patterns join those two ends of the lifecycle. In a larger agent harness, that same boundary is where connection ownership and cleanup can remain inspectable.
There is a practical reason to keep the stages visible even when a library wraps them. Discovery and execution are server work exposed to the agent, while connection and teardown belong to the runtime that opened the server. Returning a result is also part of the path: the server's work must come back to the agent before the run can continue with it.
For an async context manager, the scope expresses that lifetime directly. Work happens while the context is open, and leaving it reaches the paired end of the pattern. With explicit calls, the application has more visible control over when server.connect() and server.cleanup() run. Preserve that pairing in the application's flow.
Use five literal questions to review the path. Did the connection open in the intended runtime? Did discovery obtain the published tools? Did execution reach the MCP server? Did the result return to the agent? Did the owner reach teardown? Those questions stay within the mechanics the lifecycle pattern actually exposes. They do not assume that discovery proves authorization, tracing, contract quality, or external-service behavior.
What can fail after the tools appear?
| Failure case | Observable signal or consequence | Boundary to inspect | Action |
|---|---|---|---|
| Required server does not initialize | The Agents API reports agent.session.turn.failed; a stdio process may also leave useful process logs. | Network access, credentials, executable dependencies, and the configured working directory. | Inspect the failed turn. For stdio, inspect the process logs, then check each named runtime dependency instead of treating the failure as a tool-discovery problem. |
| Remote server has excessive reach | A model can access, send, or receive data, or take actions in an external service through a server that OpenAI has not verified. | Server operator, user authentication, allowed_tools, and require_approval. | Prefer the service provider's official server. Authenticate plugin access to customer data or write actions; an authenticated plugin server is expected to use an OAuth 2.1 flow that follows the MCP authorization specification. Require approval for sensitive actions. |
| Input or action breaks the expected contract | A malformed input or unsupported action fails an evaluation fixture in the OpenAI AWS cookbook pattern. | In that example, Python schemas, tool registrations, response schemas, and the widget move together while strict validation, read-only annotations, and credential isolation remain in place. | Cover success, malformed input, and unsupported action cases. Run the local checks before evaluating the real agent. |
| Cached discovery is stale | The agent receives tool definitions that no longer match the server's current definitions. | The cache_tools_list policy and the cache held by that server instance. | Enable caching only when tool definitions change infrequently. When a cached list must be refreshed, call invalidate_tools_cache() on the server instance before relying on discovery again. |
| A returned URL crosses an untrusted boundary | An MCP tool returns a URL that the application could request, embed, or otherwise use. | The domain and service behind the URL, plus the application action that would follow it. | Trust the domain and service first. Do not let the returned string trigger another application action merely because it came from a tool result. |
| MCP activity is opaque | The run lacks the MCP tool-list request or MCP-related tool-call information needed to inspect that path. | Agents SDK tracing controls, the run configuration, and the organization's data-retention policy. | Tracing captures MCP tool-list requests and related tool-call information. It is enabled by default but can be disabled globally or per run, and organizations using OpenAI APIs under Zero Data Retention cannot use it. |
| External call hangs or runs slowly | The external system is unavailable, responds slowly, or returns an error. | The application boundary around that dependency, including the point where waiting ends and recovery begins. | Choose explicit timeout, retry, and circuit-breaker behavior for the dependency. Do not assume that the SDK supplies one universal failure contract for every external system. |
| Tool output is malformed | The returned value does not match the response contract the application expects. | Application-side output validation and rejection behavior, without assuming a particular SDK exception. | Exercise malformed output as its own acceptance case. Record what the application actually accepts, rejects, and surfaces before that tool receives live traffic. |
| Google Developer Knowledge returns HTTP 429 | The Developer Knowledge API or MCP server rate-limits the request. | The client behavior for that Google service, kept separate from general unavailable or slow dependency handling. | Apply truncated exponential backoff with jitter so retries do not overload the service. Keep this response scoped to HTTP 429 from that API or MCP server. |
| A local MCP process remains alive | The agent run ends while the process or an example tunnel is still open. | Runtime-specific cleanup. Agents API sessions, SDK sessions, Responses conversations, and sandboxes are different resources, so their state and cleanup instructions differ. | For a non-hosted server, pair server.connect() with server.cleanup() or use the async context manager. In the AWS example, stop both the MCP server and tunnel client after testing. |
Use the matrix as one acceptance record, but keep the passes independent. Discovery does not settle authority, a returned value does not prove that malformed output is rejected, and a completed response does not prove that a local process closed. For each case, record the runtime, the owner, the signal you observed, and the response that ran. That keeps an initialization fault, an authority problem, a contract break, a slow dependency, a rate limit, and a teardown leak from collapsing into the same vague report that "MCP failed."
Trace the run before you evaluate it
Tracing has two jobs. It helps reconstruct one workflow run, and it can supply higher-signal examples for systematic evaluation. Those jobs are related, but they answer different questions: what happened here, and which runs are worth turning into evaluation material?
The OpenAI Agents SDK records LLM generations, tool calls, handoffs, guardrails, and custom events during a run. MCP activity adds tool-list requests and MCP-related tool-call information. Together, those event classes let you inspect the path around a tool call, not just its final text result.
Tracing is enabled by default. It can be disabled globally through an environment variable or code, or disabled for one run through RunConfig. There is also a firm availability boundary: organizations using OpenAI APIs under a Zero Data Retention policy cannot use Agents SDK tracing. If that policy applies, tracing cannot be assumed to carry the observation plan.
Reconstruct one run
For a single run, inspect the events as one execution path. A generation may lead to a tool call; the run may also include a handoff, a guardrail, or a custom event. The trace preserves those event classes for debugging the workflow run.
Start with the event that matters to the failure being investigated. For an MCP discovery problem, the captured tool-list request is closer to the boundary than a final generated answer. For an unexpected action, the tool-call information sits alongside the generations, handoffs, guardrails, and custom events recorded during the run. The goal is not to admire a busy trace. It is to reconstruct the particular path that produced the behavior.
That reconstruction can be written as a small run record: which generation preceded the call, which tool was invoked, which guardrail or handoff appeared, and what custom event marked application state. Each item comes from an event class tracing records. The record stays about one run, which makes it suitable for diagnosis before anyone generalizes from it.
The examples show what this can look like without turning either example into a universal interface promise. In the Databricks MCP cookbook example, the OpenAI API dashboard's Traces view can show every function invoked by the agent. The example is useful because it exposes function invocation directly, but its exact view belongs to that cookbook setup.
Build evaluation material from observed runs
A trace can also become a better input to later workflow evaluation. Instead of judging only a final answer, you can select examples that contain the recorded generations, tool calls, handoffs, guardrails, and custom events relevant to the behavior under study. OpenAI's guidance describes traces as both a debugging aid for one run and a source of higher-signal examples for systematic evaluation.
The AWS cookbook example keeps local and live checks distinct. Local checks validate sample logic and integration contracts without cloud credentials. Optional live steps then evaluate current responses, trace delivery, and the private ChatGPT connection. That division gives contract checks a place before live dependencies enter the run; it does not turn the cookbook into a turnkey test for every MCP configuration.
The Databricks and AWS examples also demonstrate different uses of observation. The Databricks cookbook exposes invoked functions in the dashboard's Traces view. The AWS cookbook checks whether trace delivery occurs during optional live work. One helps inspect what the agent invoked in that example; the other includes trace delivery among the live conditions being evaluated. Neither example changes the default tracing controls or the Zero Data Retention boundary.
Observation still ends at a lifecycle boundary. Agents API sessions, SDK sessions, Responses conversations, and sandboxes are different resources. Cleanup instructions must match the runtime in use. Apply this integration-readiness checklist before connecting an MCP server to a live agent. If the MCP boundary changes a broader architecture decision, compare OpenAI Agents SDK with LangGraph by control surface before committing to that design.