Durable MCP Connector
Connects MCP clients and Temporal Workflows to Temporal Nexus services: it exposes Nexus operations as MCP tools (inbound) and proxies upstream MCP servers behind a Nexus service so that every upstream call runs in a standalone Temporal activity (outbound). Tool names map to Nexus operation names, with support for both sync Nexus operations and asynchronous workflow-run operations.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Durable MCP Connectorget the weather in San Francisco"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Durable MCP Connector
MCP connector for Temporal Nexus. The MCP client can be a Temporal Workflow or any other MCP client.
Inbound: the MCP server is a Nexus service.
Outbound: a Nexus proxy fronts an upstream MCP server at a URL. Every upstream call runs in a standalone activity.
This repository is a prototype. See ARCHITECTURE.md for the design.
flowchart LR
C["MCP client<br>not on Temporal"] -->|"MCP"| K["Connector"]
W["Agent Workflow"] --> A["in_workflow_client"]
K -->|"Nexus"| S["Inbound:<br>Nexus service with MCP tools"]
A -->|"Nexus"| S
K -->|"Nexus"| P["Outbound:<br>Nexus proxy"]
A -->|"Nexus"| P
P -->|"standalone activities"| U["Upstream MCP server<br>at a URL"]Callers use the same connector and in-Workflow client for both. The tool name is the Nexus operation name in both cases.
Components
Directory | Component | Serves |
| Connector. A Go library and a binary. MCP server over stdio or Streamable HTTP, stateless or stateful. | Non-Temporal callers |
|
| Temporal callers |
|
| Tool authors |
| A Nexus-backed MCP server, a Nexus proxy MCP server, non-Temporal callers (OpenAI Agents SDK, Pydantic AI, LangChain, Anthropic SDK), and a Temporal caller |
Related MCP server: SelfHeal MCP
Requirements
Go 1.26 or later
Python 3.11 or later and uv
Temporal CLI 1.9.1 or later. The dev server must enable standalone Nexus operations, standalone activities, and activity callbacks.
just temporalinexamples/does this.
Build and test
uv sync
(cd examples && just build) # builds bin/durable-mcp-connector
(cd tests && just test) # Go and Python unit testsInbound: author a tool service
Keep the nexusrpc decorators. Add the MCP decorators below them.
from datetime import timedelta
import nexus_backed_mcp as nexus_mcp
import nexusrpc
import nexusrpc.handler
import temporalio.nexus
@nexusrpc.service(name="my-tools")
@nexus_mcp.service # declares list_tools
class MyService:
lookup: nexusrpc.Operation[LookupInput, str]
run_report: nexusrpc.Operation[ReportInput, Report]
@nexusrpc.handler.service_handler(service=MyService)
@nexus_mcp.service_handler # implements list_tools
class MyTools:
@nexus_mcp.tool(title="Look up a key")
@nexusrpc.handler.sync_operation
async def lookup(self, ctx, input: LookupInput) -> str:
"""Short tool. A sync Nexus operation."""
...
@nexus_mcp.tool(title="Run a report", schedule_to_close_timeout=timedelta(minutes=30))
@temporalio.nexus.workflow_run_operation
async def run_report(self, ctx, input: ReportInput) -> temporalio.nexus.WorkflowHandle[Report]:
"""Long tool. ReportWorkflow does the work."""
return await ctx.start_workflow(ReportWorkflow.run, input, id=f"report-{ctx.request_id}")Decorator | Put it | Does |
| Below | Adds the |
| Below | Adds the |
| Above a sync or async Nexus operation decorator | Marks the operation as a tool. Sets title, annotations, metadata, and |
| Above a Nexus operation decorator | Keeps the operation out of the tools of an |
Tools are opt-in by default. An operation without @nexus_mcp.tool is not a tool. With
@nexus_mcp.service_handler(expose="all"), every operation is a tool, except those with
@nexus_mcp.exclude. The tool name is the Nexus operation name. The description
defaults to the method docstring.
schedule_to_close_timeout bounds each call of the tool. The connector and the
in-Workflow client set it on the Nexus operation.
Outbound: front an upstream MCP server
MCPProxyPlugin(name, client_factory) is a Worker plugin. It registers a Nexus
service named name and the activities that call the upstream server. The upstream
server needs no change.
from datetime import timedelta
from nexus_proxy_mcp import MCPProxyPlugin, ToolPolicy, http_client_factory
from temporalio.common import RetryPolicy
proxy = MCPProxyPlugin(
"weather-tools",
http_client_factory("https://mcp.example.com/mcp", headers={"Authorization": f"Bearer {token}"}),
tool_policy_overrides={
"get_weather": ToolPolicy(start_to_close_timeout=timedelta(seconds=3)),
"get_forecast_report": ToolPolicy(retry_policy=RetryPolicy(maximum_attempts=3)),
},
)
worker = Worker(client, task_queue="weather-proxy", plugins=[proxy])client_factoryreturns a new MCP client for each upstream call. Upstream credentials go there, so they stay in the Worker process. Activity inputs and results do not carry them.http_client_factory(url, headers=..., auth=...)covers Streamable HTTP with headers or anhttpx2.Auth, for example an MCP OAuth provider.ToolPolicyholds the activity options:start_to_close_timeout,schedule_to_close_timeout,schedule_to_start_timeout,heartbeat_timeout, andretry_policy. They have the names and types ofClient.start_activity, and the proxy passes them to the activity as they are.tool_policysets the default.tool_policy_overridessets the policy for named tools.list_toolsreturns the upstream tool list, read live on each call.Any other operation name is an upstream tool name. The proxy forwards the call.
Every tool call is an async Nexus operation. It runs in a standalone activity, and shows in
temporal activity list.
Limits:
Python only. The proxy accepts any tool name through nexusrpc internals. A nexusrpc upgrade can break this.
tests/test_proxy.pychecks it.The upstream server cannot see the identity of the MCP caller. It sees only the credentials of the client factory.
Non-text upstream content (images, resources) becomes a placeholder line.
See ARCHITECTURE.md.
Call the tools
From Workflow code, use InWorkflowClient. It returns MCP tool definitions and MCP tool
results:
from in_workflow_client import InWorkflowClient
client = InWorkflowClient({"my-tools": "my-tools-endpoint"})
tools = await client.list_tools()
result = await client.call_tool("lookup", {"key": "a"})To give the tools to an AI SDK agent, wrap the client in the MCP server shape of that
SDK. examples/mcp_clients/temporal_agent.py
has a wrapper for the OpenAI Agents SDK.
From any MCP host, add the connector as a stdio MCP server:
{
"mcpServers": {
"my-tools": {
"command": "durable-mcp-connector",
"args": ["--service", "my-tools=my-tools-endpoint"],
"env": {"TEMPORAL_ADDRESS": "localhost:7233", "TEMPORAL_NAMESPACE": "default"}
}
}
}Connector flags
Flag | Default | Meaning |
| none | Nexus service and endpoint. Repeat for more services. |
|
|
|
|
| Listen address for |
|
| Legacy. Keep MCP sessions and send the session ID to the Nexus handler. stdio: one session per connector process. |
|
| Longest time a tool call waits for a result before it returns |
| none | URL of a remote codec server. Set it when the Nexus handler encodes payloads, for example to encrypt them. The codec must match the codec of the handler. |
The connector reads Temporal connection settings from the environment and from a
temporal.toml profile: TEMPORAL_ADDRESS, TEMPORAL_NAMESPACE, TEMPORAL_PROFILE,
TEMPORAL_CONFIG_FILE, and related variables. Set the caller namespace with
TEMPORAL_NAMESPACE.
Use the connector as a Go library
The packages resolver, sano, and server in src/connector/ are public. Build the
Temporal client yourself, so you control credentials, namespace, and the data converter.
Then serve the MCP server with your own transport and middleware, for example auth. See
server/example_test.go.
Examples
See examples.
This server cannot be deployed
Maintenance
Related MCP Connectors
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Stateless MCP gateway and OTel span-streaming bridge for hosted MCP servers.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceAn MCP server that proxies tool calls to a running SentinelX Core instance with OIDC/OAuth authentication. It enables secure integration of SentinelX Core's HTTP agent capabilities through the MCP protocol.1MIT
- AlicenseAqualityDmaintenanceSelf-healing proxy for MCP servers that wraps tool calls with automatic retry, circuit breaker protection, and observability.543 npmMIT
- FlicenseAqualityDmaintenanceUniversal MCP proxy server that discovers, searches, and executes tools across all configured MCP servers from a single entry point.7-
- FlicenseAqualityDmaintenanceAn MCP server for interacting with Temporal Cloud workflows across multiple regions, providing tools to list, describe, terminate, and retrieve step results from workflows via natural language.71-