Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.2.0 instead.

Temporal MCP integration

This package is experimental and may change in future versions.

temporalio.contrib.mcp lets native Temporal workflow code use MCP Python SDK v2 clients. The workflow sees a durable proxy, while MCP transports, processes, network connections, and credentials remain in worker-side Activities.

Install the integration with:

uv add temporalio-mcp

Usage

Create the workflow-side client with a durable name and call the regular MCP request/response operations:

from mcp.types import TextContent
from temporalio import workflow
from temporalio.contrib.mcp import TemporalMCPClient


@workflow.defn
class WeatherWorkflow:
    @workflow.run
    async def run(self, city: str) -> str:
        mcp = TemporalMCPClient("weather")
        tools = await mcp.list_tools()
        assert any(tool.name == "get_weather" for tool in tools.tools)

        result = await mcp.call_tool("get_weather", {"city": city})
        content = result.content[0]
        assert isinstance(content, TextContent)
        return content.text

Register a worker-side factory under the same name. Streamable HTTP is the simplest network transport:

from mcp import Client
from temporalio.contrib.mcp import MCPPlugin
from temporalio.worker import Worker

plugin = MCPPlugin(
    {
        "weather": lambda: Client("https://example.com/mcp"),
    }
)

worker = Worker(
    temporal_client,
    task_queue="weather",
    workflows=[WeatherWorkflow],
    plugins=[plugin],
)

For a stdio server, return a fresh Client and transport from the factory:

from mcp import Client, StdioServerParameters, stdio_client
from temporalio.contrib.mcp import MCPPlugin

parameters = StdioServerParameters(
    command="python",
    args=["weather_mcp_server.py"],
)
plugin = MCPPlugin(
    {
        "weather": lambda: Client(stdio_client(parameters)),
    }
)

MCPPlugin also accepts in-process MCP servers and custom MCP v2 transports through the same mcp.Client API.

Operations and durability

The proxy exposes these MCP operations, each backed by a named Activity:

Workflow method Activity suffix Result
list_tools() list-tools ListToolsResult
call_tool() call-tool CallToolResult
list_prompts() list-prompts ListPromptsResult
get_prompt() get-prompt GetPromptResult
list_resources() list-resources ListResourcesResult
list_resource_templates() list-resource-templates ListResourceTemplatesResult
read_resource() read-resource ReadResourceResult

List operations follow every server pagination cursor within one Activity and return a complete result with next_cursor=None. list_tools() is cached per TemporalMCPClient instance by default, so repeated calls on the same object schedule no further Activities (a new instance starts with an empty cache). Set cache_tools_list=False to schedule an Activity for every call. Every Activity asks the server directly; the MCP client's own response cache is bypassed because workflow history is the durable record. For a multi-page result, response metadata is merged in page order, the shortest ttl_ms is retained, and cache_scope is private when any page is private.

All operations default to a one-minute start-to-close timeout per Activity attempt. Override this with an ActivityConfig; the default is added only when both start_to_close_timeout and schedule_to_close_timeout are omitted or None:

from datetime import timedelta
from temporalio.contrib.mcp import TemporalMCPClient

mcp = TemporalMCPClient(
    "weather",
    activity_config={"start_to_close_timeout": timedelta(seconds=20)},
)

Activities have at-least-once execution semantics. An MCP tool can therefore run more than once when a worker loses its completion response. Tools with side effects should be idempotent, usually by accepting a stable idempotency key.

MCP operations do not heartbeat. Do not set heartbeat_timeout in activity_config: it does not make these Activities heartbeat or receive cancellation. Temporal records an Activity timeout in the service, but that timeout does not cancel an MCP request already running on a worker. Configure the MCP Client's read_timeout_seconds when the request itself must be bounded.

Errors and retries

A tool that fails returns a normal CallToolResult with is_error=True; the workflow decides what to do with it. A JSON-RPC error response (an unknown tool, prompt or resource, invalid arguments, a server-side failure) fails the Activity with an ApplicationError of type MCPProtocolError whose details[0] is the JSON-RPC error code. When the response includes JSON-RPC error data, details[1] preserves it. Schema-invalid server responses use the same error type, include Pydantic validation errors in details[0], and are non-retryable. Errors a retry cannot fix (parse and invalid-request errors, unknown methods, invalid params, protocol-version, header and capability mismatches, and URL elicitation) are also non-retryable.

Internal server errors, closed connections, request timeouts and transport exceptions stay retryable. The default start-to-close timeout limits each attempt, not the complete series of retries; with Temporal's default retry policy and no schedule-to-close timeout, retryable failures can retry indefinitely. Set schedule_to_close_timeout to bound the total time including retries, and/or set a retry_policy to bound the number of attempts:

from datetime import timedelta
from temporalio.common import RetryPolicy
from temporalio.contrib.mcp import TemporalMCPClient

mcp = TemporalMCPClient(
    "weather",
    activity_config={
        "start_to_close_timeout": timedelta(seconds=20),
        "schedule_to_close_timeout": timedelta(minutes=1),
        "retry_policy": RetryPolicy(maximum_attempts=3),
    },
)

Most JSON-RPC error responses leave the shared worker connection in place. A closed connection, request timeout or unsupported negotiated protocol version makes the worker reconnect before the next operation.

Connections and configuration

Parameterless factories reuse modern, sessionless MCP connections until they have been idle for five minutes. Set connection_idle_timeout=None to retain them until plugin shutdown, or timedelta(0) to close them whenever they become idle. Connections using a legacy MCP handshake are not shared between Activities.

A factory may instead declare one positional parameter. The matching workflow client passes factory_argument to it:

plugin = MCPPlugin(
    {
        "weather": lambda tenant: Client(endpoint_for(tenant)),
    }
)

mcp = TemporalMCPClient("weather", factory_argument="acme")

A non-None argument creates a fresh client for every Activity. It is recorded in workflow history, so use only a stable, non-secret identifier. Resolve URLs, tokens, and other secrets inside the worker-side factory.

Connection reuse is an optimization, not durable session storage. A process restart creates a new connection, while completed MCP results remain in workflow history and replay without reconnecting.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

temporalio_mcp-0.1.0.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

temporalio_mcp-0.1.0-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file temporalio_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: temporalio_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 16.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for temporalio_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 48fc7d87e55598f0c6dab7414ad214b4fe6271bdd90dfa56e8ae0d42fe6942cd
MD5 dfaea80fbff1ba81dee9c55e7b1b8677
BLAKE2b-256 f1482572cf8c7890346d42a2016c6e31cd0eb733397a4edf11d95a0ebc398cb6

See more details on using hashes here.

Provenance

The following attestation bundles were made for temporalio_mcp-0.1.0.tar.gz:

Publisher: release-python.yml on temporalio/ai-integrations

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file temporalio_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: temporalio_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for temporalio_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0bad91e4e22cb930a18a0480829629f86119b2156b2eb0bff79c7646b3057923
MD5 777b67fad710e8a6b54177f29833cc15
BLAKE2b-256 8ffe944125a5ffb58d956106ffc1171b695f282f5b2611c52dc3d257a35ccb45

See more details on using hashes here.

Provenance

The following attestation bundles were made for temporalio_mcp-0.1.0-py3-none-any.whl:

Publisher: release-python.yml on temporalio/ai-integrations

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page