-
Notifications
You must be signed in to change notification settings - Fork 771
Temporal MCP upstream-session proxy #414
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
Changes from 18 commits
778c857
bccedcd
874898e
27513ad
29893d1
38d23a3
09099f9
90265ae
75589d5
5d7ecfc
e2cd642
67025a1
5ff81d3
24bc4f7
463ddb5
624320b
b71467c
6135e95
f9bf8dc
015395d
905b450
c1e7d1d
7f26c78
44dd1e0
766de68
7b00d95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,17 @@ | ||||||||||||||||||||||||||||||
| import asyncio | ||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||
| from mcp.types import CallToolResult | ||||||||||||||||||||||||||||||
| from mcp_agent.app import MCPApp | ||||||||||||||||||||||||||||||
| from mcp_agent.config import MCPServerSettings | ||||||||||||||||||||||||||||||
| from mcp_agent.executor.workflow import WorkflowExecution | ||||||||||||||||||||||||||||||
| from mcp_agent.mcp.gen_client import gen_client | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| from datetime import timedelta | ||||||||||||||||||||||||||||||
| from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream | ||||||||||||||||||||||||||||||
| from mcp import ClientSession | ||||||||||||||||||||||||||||||
| from mcp_agent.mcp.mcp_agent_client_session import MCPAgentClientSession | ||||||||||||||||||||||||||||||
| from mcp.types import CallToolResult, LoggingMessageNotificationParams | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||
| from exceptiongroup import ExceptionGroup as _ExceptionGroup # Python 3.10 backport | ||||||||||||||||||||||||||||||
| except Exception: # pragma: no cover | ||||||||||||||||||||||||||||||
|
|
@@ -24,21 +29,45 @@ async def main(): | |||||||||||||||||||||||||||||
| logger = client_app.logger | ||||||||||||||||||||||||||||||
| context = client_app.context | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Connect to the workflow server | ||||||||||||||||||||||||||||||
| logger.info("Connecting to workflow server...") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Override the server configuration to point to our local script | ||||||||||||||||||||||||||||||
| context.server_registry.registry["basic_agent_server"] = MCPServerSettings( | ||||||||||||||||||||||||||||||
| name="basic_agent_server", | ||||||||||||||||||||||||||||||
| description="Local workflow server running the basic agent example", | ||||||||||||||||||||||||||||||
| transport="sse", | ||||||||||||||||||||||||||||||
| url="http://0.0.0.0:8000/sse", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Connect to the workflow server | ||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||
| logger.info("Connecting to workflow server...") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Override the server configuration to point to our local script | ||||||||||||||||||||||||||||||
| context.server_registry.registry["basic_agent_server"] = MCPServerSettings( | ||||||||||||||||||||||||||||||
| name="basic_agent_server", | ||||||||||||||||||||||||||||||
| description="Local workflow server running the basic agent example", | ||||||||||||||||||||||||||||||
| transport="sse", | ||||||||||||||||||||||||||||||
| url="http://0.0.0.0:8000/sse", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Connect to the workflow server | ||||||||||||||||||||||||||||||
| # Define a logging callback to receive server-side log notifications | ||||||||||||||||||||||||||||||
| async def on_server_log(params: LoggingMessageNotificationParams) -> None: | ||||||||||||||||||||||||||||||
| # Pretty-print server logs locally for demonstration | ||||||||||||||||||||||||||||||
| level = params.level.upper() | ||||||||||||||||||||||||||||||
| name = params.logger or "server" | ||||||||||||||||||||||||||||||
| # params.data can be any JSON-serializable data | ||||||||||||||||||||||||||||||
| print(f"[SERVER LOG] [{level}] [{name}] {params.data}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Provide a client session factory that installs our logging callback | ||||||||||||||||||||||||||||||
| def make_session( | ||||||||||||||||||||||||||||||
| read_stream: MemoryObjectReceiveStream, | ||||||||||||||||||||||||||||||
| write_stream: MemoryObjectSendStream, | ||||||||||||||||||||||||||||||
| read_timeout_seconds: timedelta | None, | ||||||||||||||||||||||||||||||
| ) -> ClientSession: | ||||||||||||||||||||||||||||||
| return MCPAgentClientSession( | ||||||||||||||||||||||||||||||
| read_stream=read_stream, | ||||||||||||||||||||||||||||||
| write_stream=write_stream, | ||||||||||||||||||||||||||||||
| read_timeout_seconds=read_timeout_seconds, | ||||||||||||||||||||||||||||||
| logging_callback=on_server_log, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Connect to the workflow server | ||||||||||||||||||||||||||||||
| async with gen_client( | ||||||||||||||||||||||||||||||
| "basic_agent_server", context.server_registry | ||||||||||||||||||||||||||||||
| "basic_agent_server", | ||||||||||||||||||||||||||||||
| context.server_registry, | ||||||||||||||||||||||||||||||
| client_session_factory=make_session, | ||||||||||||||||||||||||||||||
| ) as server: | ||||||||||||||||||||||||||||||
| # Call the BasicAgentWorkflow | ||||||||||||||||||||||||||||||
| run_result = await server.call_tool( | ||||||||||||||||||||||||||||||
|
|
@@ -56,6 +85,17 @@ async def main(): | |||||||||||||||||||||||||||||
| f"Started BasicAgentWorkflow-run. workflow ID={execution.workflow_id}, run ID={run_id}" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| get_status_result = await server.call_tool( | ||||||||||||||||||||||||||||||
| "workflows-BasicAgentWorkflow-get_status", | ||||||||||||||||||||||||||||||
| arguments={"run_id": run_id}, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| execution = WorkflowExecution(**json.loads(run_result.content[0].text)) | ||||||||||||||||||||||||||||||
| run_id = execution.run_id | ||||||||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||||||||
| f"Started BasicAgentWorkflow-run. workflow ID={execution.workflow_id}, run ID={run_id}" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
Comment on lines
+105
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There appears to be a duplicate code block at lines 105-114 that repeats the same workflow execution processing. The code retrieves The second block (starting at line 115) already handles the workflow status polling correctly, so the first duplicate block should be removed to prevent:
Removing lines 105-114 would resolve this issue while preserving the intended functionality. Spotted by Diamond
Comment on lines
+105
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate parsing/log line; likely copy/paste error. You re-parse - get_status_result = await server.call_tool(
- "workflows-BasicAgentWorkflow-get_status",
- arguments={"run_id": run_id},
- )
-
- execution = WorkflowExecution(**json.loads(run_result.content[0].text))
- run_id = execution.run_id
- logger.info(
- f"Started BasicAgentWorkflow-run. workflow ID={execution.workflow_id}, run ID={run_id}"
- )
+ get_status_result = await server.call_tool(
+ "workflows-BasicAgentWorkflow-get_status",
+ arguments={"run_id": run_id},
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Wait for the workflow to complete | ||||||||||||||||||||||||||||||
| while True: | ||||||||||||||||||||||||||||||
| get_status_result = await server.call_tool( | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,10 @@ class Context(BaseModel): | |
| # Token counting and cost tracking | ||
| token_counter: Optional[TokenCounter] = None | ||
|
|
||
| # Dynamic gateway configuration (per-run overrides via Temporal memo) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might make more sense to be done in TemporalSettings instead of the root context There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rholinshead I think you're thinking of the config, not context. This does need to be a runtime value, though possibly the value could be loaded from a config in the future. IMO this is fine as is. |
||
| gateway_url: str | None = None | ||
| gateway_token: str | None = None | ||
|
|
||
| model_config = ConfigDict( | ||
| extra="allow", | ||
| arbitrary_types_allowed=True, # Tell Pydantic to defer type evaluation | ||
|
|
||
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.
Fix client URL: 0.0.0.0 is a bind address, not a connect target.
Use localhost/127.0.0.1; current value will often fail to connect.
Apply:
context.server_registry.registry["basic_agent_server"] = MCPServerSettings( name="basic_agent_server", description="Local workflow server running the basic agent example", transport="sse", - url="http://0.0.0.0:8000/sse", + url="http://127.0.0.1:8000/sse", )📝 Committable suggestion
🤖 Prompt for AI Agents