Skip to content

Add native MCP and OpenAI Agents MCP v2 - #1793

Open
brianstrauch wants to merge 15 commits into
mainfrom
contrib-mcp-openai-agents
Open

Add native MCP and OpenAI Agents MCP v2#1793
brianstrauch wants to merge 15 commits into
mainfrom
contrib-mcp-openai-agents

Conversation

@brianstrauch

@brianstrauch brianstrauch commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

  • add public MCPPlugin and TemporalMCPClient APIs for calling MCP Python SDK v2 from native Temporal workflow code
  • expose tools, prompts, resources, and resource templates through durable Activities; fully paginate list operations in one Activity and cache tool discovery in replay-safe workflow state
  • support stdio, streamable HTTP, in-process servers, and custom MCP v2 transports through worker-side mcp.Client factories
  • share the Activity implementation with OpenAI Agents while retaining its existing Activity names and deprecated MCP v1 APIs for source and history compatibility
  • wait for plugin run-context cleanup during worker shutdown so pooled MCP transports close without leaking cancellation into callers
  • document retry, idempotency, connection reuse, factory arguments, and the non-durable session boundary

Issue coverage

This implements the initial scope of #1056:

  1. native workflow code has a framework-neutral MCP component
  2. the workflow API returns standard MCP v2 result models without requiring a third-party agent SDK
  3. stdio MCP servers are supported and integration-tested
  4. streamable HTTP MCP servers are supported and integration-tested

Each MCP request is durably represented by an Activity. Transport connections remain worker-local optimizations rather than durable sessions; process restarts reconnect while completed results replay from workflow history.

Closes #1056

Testing

  • poe test -s tests/contrib/mcp tests/contrib/openai_agents/test_mcp_v2.py tests/test_plugins.py::test_worker_awaits_plugin_run_context_cleanup (28 passed)
  • poe test -s tests/test_plugins.py tests/worker/test_worker.py::test_worker_fatal_error_with tests/worker/test_worker.py::test_worker_cancel_run (15 passed)
  • poe test-mcp-v1 -s tests/contrib/openai_agents/test_openai.py -m mcp_v1 (8 passed, 5 skipped)
  • poe lint
  • poe gen-docs

brianstrauch and others added 5 commits August 27, 2026 11:01
A failed operation evicted the pooled connection by closing it outright,
which unwound the owning task's `async with backend` while concurrent
operations were still using the same connection. Since `CancelledError` is
a `BaseException`, activity cancellation and worker shutdown hit this path
too, so cancelling one MCP activity aborted every other in-flight activity
sharing the connection. The failing operation also never released its slot.

Failures now retire the record: it is dropped from the cache so no later
operation reuses it, and the last operation to release it closes it.

Also drop a dead uniqueness check in `_MCPActivities` (`set()` over a dict
compares key counts, so it never fired) and correct the deprecation
directives to 1.32, the unreleased version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`temporalio.contrib.mcp` is an implementation detail shared by the contrib
integrations, not a public API, but it was advertised as one: the CHANGELOG
called it stable, `workflow.py`/`MCPClient` were the only non-underscore
names in an otherwise private package, and pydoctor rendered the package
into the published docs. Rename to `_workflow._MCPClient`, mark the package
PRIVATE for pydoctor, and describe the release in terms of the OpenAI Agents
surface users actually call.

`meta` was threaded through all seven operations, but only `call_tool` has a
caller that supplies it (the OpenAI Agents base `MCPServer` resolves it per
tool call). The other six accepted it and dropped it on the floor, so a
caller passing metadata would silently get none. Drop the parameter there;
`_CallToolRequest` now owns the field.

Replace the `-k 'legacy_mcp_apis_are_deprecated or mcp_server'` CI selection
with `@pytest.mark.mcp_v1`. Substring matching silently under-selects on a
rename while still passing, and `mcp_server` also matches `mcp_servers` and
any future `test_hosted_mcp_server_*`. The marker selects the same 13 tests
today and stays correct as tests are renamed or added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An idle-eviction task armed before an operation acquired a pooled record
could close the connection while that operation was still in flight: once
a peer failure unmapped the record, `_unmap` skipped the idle check and
reported the record closable. Apply the idle check regardless of whether
the record is still mapped.

Restore the legacy `inspect.signature` handling for MCP server factories,
now shared by both backends. A factory declaring a positional parameter
receives the `factory_argument` (`None` when a workflow supplied none), and
a parameterless factory is called bare. Signatures that could satisfy
neither form raise when the plugin is built, and passing a
`factory_argument` to a parameterless factory raises a non-retryable error
instead of a bare `TypeError` that retries forever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The event loop holds only a weak reference to a task, so the unretained
task created by _schedule_evict could be collected part way through,
leaving an idle connection open until the pool closed. Hold these tasks
on the pool, and cancel and await the current loop's evictions in
close(), where every record is being closed anyway, rather than
orphaning them on a closing loop.

close() also dropped only the records for its loop, never the locks
keyed alongside them, so a long-lived plugin kept every event loop it
had ever served alive. Prune both.

Also drop the unreferenced _mcp_client_backend_factory and annotate
_MCPActivities.run_context.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@brianstrauch
brianstrauch requested review from a team as code owners August 27, 2026 20:43
@brianstrauch
brianstrauch marked this pull request as draft August 27, 2026 20:47
@brianstrauch brianstrauch changed the title Add durable MCP v2 support for OpenAI Agents Add durable native MCP and OpenAI Agents support Aug 27, 2026
@brianstrauch brianstrauch changed the title Add durable native MCP and OpenAI Agents support Add native MCP and OpenAI Agents MCP v2 Aug 27, 2026
@brianstrauch
brianstrauch marked this pull request as ready for review August 27, 2026 23:27
brianstrauch and others added 4 commits August 27, 2026 16:57
Fix the proto Docker build, which pinned googleapis-common-protos into
the dev group while the conflicting requirement now lives in dev-common,
leaving the following uv sync unsatisfiable.

Bound the MCP pool close during worker shutdown. The run context swallows
the shutdown cancellation, so an MCP server that never finishes closing
its transport would hang the worker with no cancellation left to break
out with.

Stop treating activity cancellation as a transport failure in the
connection pool. The MCP client cancels the in-flight request on the
wire, so the shared connection stays healthy and should not be retired
out from under every other workflow using it.

Reject a callable tool_filter on a worker-side MCPServer. It needs the
run context and agent, which exist only in the workflow, so the Agents
SDK raises for it and the list-tools Activity would retry forever.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add workflow MCP client

1 participant