Skip to content

Commit 8c4e2c8

Browse files
tconley1428jssmith
andauthored
MCP server support (#1021)
* Work in progress MCP call support * Moving mcp classes into plugin * Updated documentation * Add local test * Fix problems since MCP doesn't exist on python 3.9 * Remove debug log * Best effort import temporal server * Delay import of mcp servers * Change to a stateless implementation * Include stateful option * Add docstrings * Remove merge duplicate * Some cleanup * Lint * Fix up tests * Lint * Restructure based on feedback - docstring need updating still * Change workflow function names * Cleanup * Lint * Fixing python 3.9 * Lint * Some name changes and protected get_activities * Addressing feedback * Change server names, overhaul tests to use a custom MCPServer which tracks calls * Fixing 3.9 issues * Fix await * Revert core change * Remove print * Fail fast if stateful server hasn't been started * Overhaul stateful mcp server * Trying to fix core commit * Wait for cancellation * update readme * Change stateless to a provider model, add caching option * Remove caching from stateful. Underlying server can handle it * readme updates * Revert pyproject update * Fix no worker test --------- Co-authored-by: Johann Schleier-Smith <johann.schleiersmith@temporal.io>
1 parent 0e1fa4f commit 8c4e2c8

File tree

9 files changed

+1003
-32
lines changed

9 files changed

+1003
-32
lines changed

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ opentelemetry = [
2727
pydantic = ["pydantic>=2.0.0,<3"]
2828
openai-agents = [
2929
"openai-agents>=0.3,<0.4",
30-
"eval-type-backport>=0.2.2; python_version < '3.10'"
30+
"eval-type-backport>=0.2.2; python_version < '3.10'",
31+
"mcp>=1.9.4, <2; python_version >= '3.10'",
3132
]
3233

3334
[project.urls]

temporalio/contrib/openai_agents/README.md

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,111 @@ Of course, code running in the workflow can invoke a Temporal activity at any ti
351351
Tools that run in the workflow can also update OpenAI Agents context, which is read-only for tools run as Temporal activities.
352352

353353

354+
## MCP Support
355+
356+
This integration provides support for Model Context Protocol (MCP) servers through two wrapper approaches designed to handle different implications of failures.
357+
358+
While Temporal provides durable execution for your workflows, this durability does not extend to MCP servers, which operate independently of the workflow and must provide their own durability. The integration handles this by offering stateless and stateful wrappers that you can choose based on your MCP server's design.
359+
360+
### Stateless vs Stateful MCP Servers
361+
362+
You need to understand your MCP server's behavior to choose the correct wrapper:
363+
364+
**Stateless MCP servers** treat each operation independently. For example, a weather server with a `get_weather(location)` tool is stateless because each call is self-contained and includes all necessary information. These servers can be safely restarted or reconnected to without changing their behavior.
365+
366+
**Stateful MCP servers** maintain session state between calls. For example, a weather server that requires calling `set_location(location)` followed by `get_weather()` is stateful because it remembers the configured location and uses it for subsequent calls. If the session or the server is restarted, state crucial for operation is lost. Temporal identifies such failures and raises an `ApplicationError` to signal the need for application-level failure handling.
367+
368+
### Usage Example (Stateless MCP)
369+
370+
The code below gives an example of using a stateless MCP server.
371+
372+
#### Worker Configuration
373+
374+
```python
375+
import asyncio
376+
from datetime import timedelta
377+
from agents.mcp import MCPServerStdio
378+
from temporalio.client import Client
379+
from temporalio.contrib.openai_agents import (
380+
ModelActivityParameters,
381+
OpenAIAgentsPlugin,
382+
StatelessMCPServerProvider,
383+
)
384+
from temporalio.worker import Worker
385+
386+
async def main():
387+
# Create the MCP server provider
388+
filesystem_server = StatelessMCPServerProvider(
389+
lambda: MCPServerStdio(
390+
name="FileSystemServer",
391+
params={
392+
"command": "npx",
393+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"],
394+
},
395+
)
396+
)
397+
398+
# Register the MCP server with the OpenAI Agents plugin
399+
client = await Client.connect(
400+
"localhost:7233",
401+
plugins=[
402+
OpenAIAgentsPlugin(
403+
model_params=ModelActivityParameters(
404+
start_to_close_timeout=timedelta(seconds=60)
405+
),
406+
mcp_servers=[filesystem_server],
407+
),
408+
],
409+
)
410+
411+
worker = Worker(
412+
client,
413+
task_queue="my-task-queue",
414+
workflows=[FileSystemWorkflow],
415+
)
416+
await worker.run()
417+
418+
if __name__ == "__main__":
419+
asyncio.run(main())
420+
```
421+
422+
#### Workflow Implementation
423+
424+
```python
425+
from temporalio import workflow
426+
from temporalio.contrib import openai_agents
427+
from agents import Agent, Runner
428+
429+
@workflow.defn
430+
class FileSystemWorkflow:
431+
@workflow.run
432+
async def run(self, query: str) -> str:
433+
# Reference the MCP server by name (matches name in worker configuration)
434+
server = openai_agents.workflow.stateless_mcp_server("FileSystemServer")
435+
436+
agent = Agent(
437+
name="File Assistant",
438+
instructions="Use the filesystem tools to read files and answer questions.",
439+
mcp_servers=[server],
440+
)
441+
442+
result = await Runner.run(agent, input=query)
443+
return result.final_output
444+
```
445+
446+
The `StatelessMCPServerProvider` takes a factory function that creates new MCP server instances. The server name used in `stateless_mcp_server()` must match the name configured in the MCP server instance. In this example, the name is `FileSystemServer`.
447+
448+
### Stateful MCP Servers
449+
450+
For implementation details and examples, see the [samples repository](https://github.com/temporalio/samples-python/tree/main/openai_agents/mcp).
451+
452+
When using stateful servers, the dedicated worker maintaining the connection may fail due to network issues or server problems. When this happens, Temporal raises an `ApplicationError` and cannot automatically recover because it cannot restore the lost server state.
453+
To recover from such failures, you need to implement your own application-level retry logic.
454+
455+
### Hosted MCP Tool
456+
457+
For network-accessible MCP servers, you can also use `HostedMCPTool` from the OpenAI Agents SDK, which uses an MCP client hosted by OpenAI.
458+
354459
## Feature Support
355460

356461
This integration is presently subject to certain limitations.
@@ -403,14 +508,17 @@ As described in [Tool Calling](#tool-calling), context propagation is read-only
403508

404509
### MCP
405510

406-
Presently, MCP is supported only via `HostedMCPTool`, which uses the OpenAI Responses API and cloud MCP client behind it.
407-
The OpenAI Agents SDK also supports MCP clients that run in application code, but this integration does not.
511+
The MCP protocol is stateful, but many MCP servers are stateless.
512+
We let you choose between two MCP wrappers, one designed for stateless MCP servers and one for stateful MCP servers.
513+
These wrappers work with all transport varieties.
514+
515+
Note that when using network-accessible MCP servers, you also can also use the tool `HostedMCPTool`, which is part of the OpenAI Responses API and uses an MCP client hosted by OpenAI.
408516

409517
| MCP Class | Supported |
410518
|:-----------------------|:---------:|
411-
| MCPServerStdio | No |
412-
| MCPServerSse | No |
413-
| MCPServerStreamableHttp| No |
519+
| MCPServerStdio | Yes |
520+
| MCPServerSse | Yes |
521+
| MCPServerStreamableHttp| Yes |
414522

415523
### Guardrails
416524

temporalio/contrib/openai_agents/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
Use with caution in production environments.
99
"""
1010

11+
# Best Effort mcp, as it is not supported on Python 3.9
12+
try:
13+
from temporalio.contrib.openai_agents._mcp import (
14+
StatefulMCPServerProvider,
15+
StatelessMCPServerProvider,
16+
)
17+
except ImportError:
18+
pass
19+
1120
from temporalio.contrib.openai_agents._model_parameters import ModelActivityParameters
1221
from temporalio.contrib.openai_agents._temporal_openai_agents import (
1322
OpenAIAgentsPlugin,
@@ -27,6 +36,8 @@
2736
"ModelActivityParameters",
2837
"OpenAIAgentsPlugin",
2938
"OpenAIPayloadConverter",
39+
"StatelessMCPServerProvider",
40+
"StatefulMCPServerProvider",
3041
"TestModel",
3142
"TestModelProvider",
3243
"workflow",

0 commit comments

Comments
 (0)