-
Notifications
You must be signed in to change notification settings - Fork 768
Temporal plugin implementation #518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
StreetLamb
wants to merge
35
commits into
main
Choose a base branch
from
feat/temporal-plugin
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 16 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
9a9b001
first draft
StreetLamb 95487d7
make temporal plugin work with asyncio execution engine and mcp-agent…
StreetLamb d211248
Add more passthrough modules
StreetLamb 29d35c7
draft scripts
StreetLamb 2ea85fd
Update `configure_worker` to work with temporal execution engine only
StreetLamb 8cd266c
Change prompt of example workflow
StreetLamb 9b03bb9
Refactor workflow and worker
StreetLamb 5051dd3
Update configuration for temporal execution engine
StreetLamb 6a75bde
Add single-file temporal plugin example implementation
StreetLamb 0e5f157
Update temploral plugin example readme
StreetLamb b3b9f56
Move plugin to client instead of worker
StreetLamb 5de198a
Improve MCPAgentPlugin docstring, simplify plugin initialisation, upd…
StreetLamb 9494112
delete obsolete workflow library
StreetLamb b9fd945
Move MCPAgentPlugin under executor.temporal directory. Refactor MCPAg…
StreetLamb b5fca11
Reduce passthrough modules
StreetLamb b24ec16
add examples for orchestrator and parallel_agent workflow patterns
StreetLamb ed56e3f
wip evaluator_optimizer and router workflow example
StreetLamb 529dee3
Move upstream session handling from Workflow class to MCPAgentPlugin
StreetLamb d467cda
remove unused import in evaluator_optimizer
StreetLamb de7603b
Update MCPAgentPlugin to configure interceptors, workflows, and runne…
StreetLamb 04aab67
Rename example workflow classes for clarity
StreetLamb e4588cd
Add temporal replay test example
StreetLamb 9d64710
draft support for registering temporal workflows via MCPApp
StreetLamb 02735f0
Rename register_workflows to register_temporal_workflows and update l…
StreetLamb d596208
Deduplicate workflows registered via Worker and register_temporal_wor…
StreetLamb 46f7a00
Update README and add basic_agent_server.py for MCP server integratio…
StreetLamb efb282c
Refactor MCPAgentPlugin to register unregistered Temporal workflows w…
StreetLamb fc3613c
Add execution status filter to workflow listing in replay.py
StreetLamb ab21d00
Update README to clarify MCP server setup and introduce workflow repl…
StreetLamb 0c453f9
Fix variable name in EvaluatorOptimizerWorkflow
StreetLamb 3dd5722
Fix AnthropicAugmentedLLM to use executor for streaming completion
StreetLamb d22223f
Fix router example return type
StreetLamb af39096
Merge branch 'main' of https://github.com/lastmile-ai/mcp-agent into …
StreetLamb aef8aa3
Fix broken AnthropicAugmentedLLM tests
StreetLamb 0a97ce1
Refactor instructions in ParallelAgentWorkflow and update workflow im…
StreetLamb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# MCP-Agent | ||
mcp_agent.secrets.yaml | ||
*.secrets.yaml | ||
.mcp-agent/ | ||
|
||
# Python | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
*.so | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Virtual Environment | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# PyCharm | ||
.idea/ | ||
|
||
# VS Code | ||
.vscode/ | ||
*.code-workspace | ||
|
||
# Vim | ||
[._]*.s[a-v][a-z] | ||
[._]*.sw[a-p] | ||
[._]s[a-rt-v][a-z] | ||
[._]ss[a-gi-z] | ||
[._]sw[a-p] | ||
*~ | ||
|
||
# Logs | ||
logs/ | ||
*.log | ||
*.jsonl | ||
|
||
# OS | ||
.DS_Store | ||
.DS_Store? | ||
._* | ||
.Spotlight-V100 | ||
.Trashes | ||
ehthumbs.db | ||
Thumbs.db | ||
|
||
# Testing | ||
.pytest_cache/ | ||
.coverage | ||
htmlcov/ | ||
.tox/ | ||
.hypothesis/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
# Local environment variables | ||
.env.local | ||
.env.*.local |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# MCP-Agent with Temporal Plugin | ||
|
||
This example demonstrates multiple ways to use the Temporal plugin with MCP-Agent for workflow orchestration. | ||
|
||
## Prerequisites | ||
|
||
1. **Temporal Server**: Ensure you have a Temporal server running locally: | ||
```bash | ||
temporal server start-dev | ||
``` | ||
This starts a development server at `localhost:7233` | ||
|
||
2. **API Keys**: Add your API keys to `mcp_agent.secrets.yaml`: | ||
```yaml | ||
OPENAI_API_KEY: "your-key-here" | ||
ANTHROPIC_API_KEY: "your-key-here" # optional | ||
``` | ||
|
||
3. **Configuration**: Set the execution engine to `temporal` in `mcp_agent.config.yaml`: | ||
```yaml | ||
execution_engine: temporal | ||
|
||
temporal: | ||
host: "localhost:7233" | ||
namespace: "default" | ||
task_queue: "mcp-agent" | ||
``` | ||
|
||
## Usage Methods | ||
|
||
### Method 1: Separate Worker and Workflow Files | ||
|
||
This approach separates the worker and workflow execution into different processes, useful for distributed systems. | ||
|
||
**Step 1: Define your workflow** (`basic_workflow.py`): | ||
```python | ||
from temporalio import workflow | ||
from mcp_agent.agents.agent import Agent | ||
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM | ||
|
||
@workflow.defn | ||
class BasicWorkflow: | ||
@workflow.run | ||
async def run(self, prompt: str) -> str: | ||
simple_agent = Agent( | ||
name="finder", | ||
instruction="You are a helpful agent", | ||
server_names=["fetch"], | ||
) | ||
|
||
async with simple_agent: | ||
llm = await simple_agent.attach_llm(OpenAIAugmentedLLM) | ||
result = await llm.generate_str(prompt) | ||
return result | ||
``` | ||
|
||
**Step 2: Run the worker** (`run_worker.py`): | ||
```bash | ||
uv run run_worker.py | ||
``` | ||
|
||
**Step 3: Execute the workflow** (in another terminal): | ||
```bash | ||
uv run run_basic_workflow.py | ||
``` | ||
|
||
### Method 2: Single File Execution (temporal_agent.py) | ||
|
||
This approach combines worker and workflow execution in a single file, ideal for simpler deployments or testing. | ||
|
||
```bash | ||
uv run temporal_agent.py | ||
``` | ||
|
||
This file: | ||
- Defines the workflow | ||
- Starts the worker | ||
- Executes the workflow | ||
- All within the same process using `async with Worker(...)` | ||
|
||
**Key difference**: The single-file approach runs both the worker and client in the same process: | ||
```python | ||
async with Worker( | ||
client, | ||
task_queue=running_app.config.temporal.task_queue, | ||
workflows=[BasicWorkflow], | ||
): | ||
# Execute workflow while worker is running | ||
output = await client.execute_workflow(...) | ||
``` | ||
|
||
## Important Configuration Notes | ||
|
||
### Execution Engine Setting | ||
|
||
The `execution_engine` in `mcp_agent.config.yaml` **MUST** be set to `temporal` for the Temporal plugin to work: | ||
|
||
```yaml | ||
execution_engine: temporal # Required for Temporal plugin | ||
``` | ||
|
||
Without this setting, MCP-Agent will use the default `asyncio` engine and Temporal features won't be available. | ||
|
||
### Temporal Configuration | ||
|
||
Configure Temporal settings in `mcp_agent.config.yaml`: | ||
|
||
```yaml | ||
temporal: | ||
host: "localhost:7233" # Temporal server address | ||
namespace: "default" # Temporal namespace | ||
task_queue: "mcp-agent" # Task queue name | ||
max_concurrent_activities: 10 # Concurrency limit | ||
rpc_metadata: | ||
X-Client-Name: "mcp-agent" # Client identification | ||
``` | ||
|
||
## File Structure | ||
|
||
``` | ||
temporal_plugin/ | ||
├── basic_workflow.py # Workflow definition | ||
├── run_worker.py # Worker process (Method 1) | ||
├── run_basic_workflow.py # Workflow client (Method 1) | ||
├── temporal_agent.py # Single-file approach (Method 2) | ||
├── main.py # MCP-Agent app setup | ||
├── mcp_agent.config.yaml # Configuration (MUST set execution_engine: temporal) | ||
└── mcp_agent.secrets.yaml # API keys | ||
``` | ||
|
||
## When to Use Each Method | ||
|
||
- **Separate Files (Method 1)**: Use when you need: | ||
- Distributed workers across multiple machines | ||
- Independent scaling of workers and clients | ||
- Clear separation of concerns | ||
- Production deployments | ||
|
||
- **Single File (Method 2)**: Use when you need: | ||
- Quick prototyping and testing | ||
- Simple deployments | ||
- All-in-one execution for demos | ||
- Development and debugging | ||
|
||
## Troubleshooting | ||
|
||
1. **Temporal not working**: Ensure `execution_engine: temporal` in config | ||
2. **Connection refused**: Start Temporal server with `temporal server start-dev` | ||
3. **Task queue mismatch**: Verify task queue names match between worker and client | ||
|
||
## Further Resources | ||
|
||
- [Temporal Documentation](https://docs.temporal.io/) | ||
- [MCP-Agent Documentation](https://docs.mcp-agent.com/) | ||
- [MCP-Agent GitHub](https://github.com/lastmile-ai/mcp-agent) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from temporalio import workflow | ||
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM | ||
from mcp_agent.agents.agent import Agent | ||
|
||
|
||
@workflow.defn | ||
class BasicWorkflow: | ||
@workflow.run | ||
async def run(self, prompt: str) -> str: | ||
simple_agent = Agent( | ||
name="finder", | ||
instruction="You are a helpful agent", | ||
server_names=["fetch"], | ||
) | ||
|
||
async with simple_agent: | ||
llm = await simple_agent.attach_llm(OpenAIAugmentedLLM) | ||
result = await llm.generate_str(prompt) | ||
return result | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## Graded Report: The Battle of Glimmerwood | ||
|
||
### Proofreading Feedback: | ||
1. **Sentence Structure:** | ||
- Correction: "Amidst the chaos, a young girl named Elara stood her ground. She rallied the villagers and devised a clever plan." | ||
- **Feedback:** Correct use of punctuation for independent clauses. | ||
|
||
2. **Clause Clarity:** | ||
- Improved Version: "Elara and the villagers cleverly used the forest's natural defenses to lure the marauders into a trap." | ||
- **Feedback:** Enhances clarity and strategic emphasis. | ||
|
||
3. **Sentence Flow and Punctuation:** | ||
- Correction: "As the bandits approached the village square, a herd of Glimmerfoxes emerged and blinded the marauders with their dazzling light. The villagers then seized the opportunity to capture the invaders." | ||
- **Feedback:** Breaks run-on for clearer sequence. | ||
|
||
Overall, the story is well-written; improvements focus on sentence structure and clarity for better readability. | ||
|
||
### Factual Consistency & Logical Coherence: | ||
1. **Glimmerwood Setting:** Consistent depiction as a mystical forest. | ||
2. **Conflict and Elara’s Strategy:** Plausible yet could explore villagers' preparation. | ||
3. **Capture Aftermath:** Lacks detail on what happens to marauders. | ||
4. **Glimmerstones:** Intriguing but underexplored. | ||
5. **Character Consistency:** Elara remains consistent in her role. | ||
|
||
While internally consistent, expanding underdeveloped areas can enhance depth. | ||
|
||
### APA Style Adherence: | ||
1. **Title/Sections:** Missing APA elements like a title page. | ||
2. **Formatting:** Ensure compliance with APA font/margins. | ||
3. **Narrative Flow:** Clear sequence, can benefit from improved transitions. | ||
|
||
### Suggestions: | ||
- Add a title page following APA. | ||
- Clarify ambiguous plot elements for better coherence. | ||
|
||
In conclusion, the story maintains good narrative quality and internal logic, but formatting and APA compliance need attention to meet academic standards. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from mcp_agent.app import MCPApp | ||
|
||
# Initialize the app to get context | ||
app = MCPApp(name="mcp_basic_agent") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# MCP-Agent Configuration File | ||
# Config definition: https://github.com/lastmile-ai/mcp-agent/blob/main/src/mcp_agent/config.py | ||
$schema: https://raw.githubusercontent.com/lastmile-ai/mcp-agent/refs/heads/main/schema/mcp-agent.config.schema.json | ||
|
||
# Execution engine: asyncio or temporal | ||
# For temporal mode, see: https://github.com/lastmile-ai/mcp-agent/blob/main/examples/temporal/README.md | ||
execution_engine: temporal | ||
|
||
temporal: | ||
host: "localhost:7233" # Default Temporal server address | ||
namespace: "default" # Default Temporal namespace | ||
task_queue: "mcp-agent" # Task queue for workflows and activities | ||
max_concurrent_activities: 10 # Maximum number of concurrent activities | ||
rpc_metadata: | ||
X-Client-Name: "mcp-agent" | ||
|
||
logger: | ||
transports: [console, file] | ||
level: info | ||
path: logs/mcp-agent.log | ||
|
||
# Configure MCP Servers connections (supports stdio, sse, streamable_http, and websockets) | ||
mcp: | ||
servers: | ||
# Filesystem access server | ||
filesystem: | ||
command: npx | ||
args: ["-y", "@modelcontextprotocol/server-filesystem", "."] | ||
|
||
# Web fetch server | ||
fetch: | ||
command: uvx | ||
args: ["mcp-server-fetch"] | ||
#env: # Environment variables passed to the stdio server | ||
# ROOT_PATH: "/workspace" | ||
|
||
# sse_server: | ||
# transport: "sse" | ||
# url: "https://api.example.com/sse" | ||
# headers: | ||
# Authorization: "Bearer ${API_TOKEN}" | ||
|
||
# streamable_http_server: | ||
# transport: streamable_http | ||
# url: "https://api.example.com/mcp" | ||
# headers: | ||
# Authorization: "Bearer ${API_TOKEN}" | ||
# Content-Type: "application/json" | ||
# http_timeout_seconds: 30 | ||
# read_timeout_seconds: 120 | ||
# terminate_on_close: true | ||
|
||
# Optional: Define Agent definitions in config | ||
agents: | ||
definitions: | ||
- name: filesystem_helper | ||
instruction: "You can read files and summarize their contents." | ||
server_names: [filesystem] | ||
- name: web_helper | ||
instruction: "You can fetch web pages and summarize their content." | ||
server_names: [fetch] | ||
|
||
# Model provider defaults (API keys go in mcp_agent.secrets.yaml) | ||
openai: | ||
default_model: gpt-4o-mini | ||
|
||
anthropic: | ||
default_model: claude-sonnet-4-0 | ||
# google: | ||
# default_model: "gemini-1.5-pro" | ||
|
||
# OpenTelemetry configuration (optional) | ||
# otel: | ||
# enabled: true | ||
# exporters: ["file", "otlp"] | ||
# otlp_settings: | ||
# endpoint: "http://localhost:4318/v1/traces" |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Workflow determinism/I-O safety: ensure all LLM/network calls run as activities (not in workflow code).
Agent(...); async with simple_agent; llm.generate_str(...)
will perform I/O unless your executor reroutes to activities underexecution_engine: temporal
. If not fully offloaded, this violates Temporal workflow rules and will fail nondeterminism/sandbox checks.Please confirm your executor uses
workflow.execute_activity
/local activities for model calls and MCP tool use whenexecution_engine=temporal
. If not, we should refactor the workflow to call explicit activities.🌐 Web query:
💡 Result:
Short answer: No — Temporal Python Workflows must not perform network I/O directly. Use Activities (or Local Activities / child workflows / async Activities) to do external I/O such as HTTP calls or LLM requests. Workflows remain deterministic and only orchestrate; Activities execute non‑deterministic, blocking, or network work. [1][2][3]
Recommended pattern (concise):
Sources:
Critical: Move LLM/HTTP/network I/O out of the workflow — invoke via Activities
Temporal Python workflows must not perform network I/O. examples/temporal_plugin/basic_workflow.py (lines 10–19) calls simple_agent.attach_llm(...) and llm.generate_str(...) inside the workflow; this performs external I/O and will break Temporal determinism/sandbox checks. Refactor those LLM/HTTP calls into Activities (or execute_local_activity) and invoke them from the workflow using workflow.execute_activity / workflow.execute_local_activity. If an executor already transparently offloads these calls to Temporal Activities, point to that implementation; otherwise refactor.
🤖 Prompt for AI Agents