Add native MCP and OpenAI Agents MCP v2 - #1793
Open
brianstrauch wants to merge 15 commits into
Open
Conversation
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
marked this pull request as draft
August 27, 2026 20:47
brianstrauch
marked this pull request as ready for review
August 27, 2026 23:27
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MCPPluginandTemporalMCPClientAPIs for calling MCP Python SDK v2 from native Temporal workflow codemcp.ClientfactoriesIssue coverage
This implements the initial scope of #1056:
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 lintpoe gen-docs