From 4a1f3788ed336f81e8763a6394d60b472918cf2e Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:36:36 -0400 Subject: [PATCH 1/9] Update mcp_agent_server/temporal example --- examples/mcp_agent_server/asyncio/README.md | 12 ++++++------ examples/mcp_agent_server/temporal/README.md | 8 ++++---- .../temporal/basic_agent_server_worker.py | 2 +- .../temporal/{basic_agent_server.py => main.py} | 0 4 files changed, 11 insertions(+), 11 deletions(-) rename examples/mcp_agent_server/temporal/{basic_agent_server.py => main.py} (100%) diff --git a/examples/mcp_agent_server/asyncio/README.md b/examples/mcp_agent_server/asyncio/README.md index b3d6000f1..920a427c4 100644 --- a/examples/mcp_agent_server/asyncio/README.md +++ b/examples/mcp_agent_server/asyncio/README.md @@ -125,7 +125,7 @@ uv run client.py This will: -1. Start the basic_agent_server.py as a subprocess +1. Start the agent server (main.py) as a subprocess 2. Connect to the server 3. Run the BasicAgentWorkflow 4. Monitor and display the workflow status @@ -137,10 +137,10 @@ You can also run the server and client separately: 1. In one terminal, start the server: ``` -uv run basic_agent_server.py +uv run main.py # Optionally, run with the example custom FastMCP settings -uv run basic_agent_server.py --custom-fastmcp-settings +uv run main.py --custom-fastmcp-settings ``` 2. In another terminal, run the client: @@ -299,7 +299,7 @@ npx @modelcontextprotocol/inspector \ uv \ --directory /path/to/mcp-agent/examples/mcp_agent_server/asyncio \ run \ - basic_agent_server.py + main.py ``` This will launch the MCP Inspector UI where you can: @@ -323,7 +323,7 @@ To use this server with Claude Desktop: "--directory", "/path/to/mcp-agent/examples/mcp_agent_server/asyncio", "run", - "basic_agent_server.py" + "main.py" ] } ``` @@ -354,7 +354,7 @@ mcp: ## Code Structure -- `basic_agent_server.py` - Defines the workflows and creates the MCP server +- `main.py` - Defines the workflows and creates the MCP server - `client.py` - Example client that connects to the server and runs workflows - `mcp_agent.config.yaml` - Configuration for MCP servers and execution engine - `mcp_agent.secrets.yaml` - Contains API keys (not included in repository) diff --git a/examples/mcp_agent_server/temporal/README.md b/examples/mcp_agent_server/temporal/README.md index 0ec7a378b..60564d483 100644 --- a/examples/mcp_agent_server/temporal/README.md +++ b/examples/mcp_agent_server/temporal/README.md @@ -141,7 +141,7 @@ To run this example, you'll need to: 4. In another terminal, start the MCP server: ```bash - uv run basic_agent_server.py + uv run main.py ``` 5. In a fourth terminal, run the client: @@ -215,7 +215,7 @@ npx @modelcontextprotocol/inspector \ uv \ --directory /path/to/mcp-agent/examples/mcp_agent_server/temporal \ run \ - basic_agent_server.py + main.py ``` This will launch the MCP Inspector UI where you can: @@ -239,7 +239,7 @@ To use this server with Claude Desktop: "--directory", "/path/to/mcp-agent/examples/mcp_agent_server/temporal", "run", - "basic_agent_server.py" + "main.py" ] } ``` @@ -250,7 +250,7 @@ To use this server with Claude Desktop: ## Code Structure -- `basic_agent_server.py` - Defines the workflows and creates the MCP server +- `main.py` - Defines the workflows and creates the MCP server - `basic_agent_server_worker.py` - Sets up the Temporal worker to process workflow tasks - `client.py` - Example client that connects to the server and runs workflows - `mcp_agent.config.yaml` - Configuration for MCP servers and the Temporal execution engine diff --git a/examples/mcp_agent_server/temporal/basic_agent_server_worker.py b/examples/mcp_agent_server/temporal/basic_agent_server_worker.py index 992765da4..a156522b2 100644 --- a/examples/mcp_agent_server/temporal/basic_agent_server_worker.py +++ b/examples/mcp_agent_server/temporal/basic_agent_server_worker.py @@ -12,7 +12,7 @@ from mcp_agent.executor.temporal import create_temporal_worker_for_app -from basic_agent_server import app +from examples.mcp_agent_server.temporal.main import app # Initialize logging logging.basicConfig(level=logging.INFO) diff --git a/examples/mcp_agent_server/temporal/basic_agent_server.py b/examples/mcp_agent_server/temporal/main.py similarity index 100% rename from examples/mcp_agent_server/temporal/basic_agent_server.py rename to examples/mcp_agent_server/temporal/main.py From 867679681c68e9ede1084c7d4b950f9b3c2200bb Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:44:30 -0400 Subject: [PATCH 2/9] Add openai to requirements.txt --- examples/mcp_agent_server/temporal/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/mcp_agent_server/temporal/requirements.txt b/examples/mcp_agent_server/temporal/requirements.txt index 069c34fae..2fa590b39 100644 --- a/examples/mcp_agent_server/temporal/requirements.txt +++ b/examples/mcp_agent_server/temporal/requirements.txt @@ -2,4 +2,5 @@ mcp-agent @ file://../../../ # Link to the local mcp-agent project root # Additional dependencies specific to this example -temporalio \ No newline at end of file +openai +temporalio From dbb651bcb5c6dd2ff8c116e7aeb5b0faaf63294a Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:24:50 -0400 Subject: [PATCH 3/9] More updates --- examples/mcp_agent_server/temporal/README.md | 10 ++++++++-- .../temporal/basic_agent_server_worker.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/mcp_agent_server/temporal/README.md b/examples/mcp_agent_server/temporal/README.md index 60564d483..a775273da 100644 --- a/examples/mcp_agent_server/temporal/README.md +++ b/examples/mcp_agent_server/temporal/README.md @@ -318,10 +318,16 @@ async with gen_client("basic_agent_server", context.server_registry) as server: ) # The workflow will pause - to resume it, send the resume signal - run_id = pause_result.content[0].text + execution = WorkflowExecution( + **json.loads(pause_result.content[0].text) + ) + + run_id = execution.run_id + workflow_id = exection.workflow_id + await server.call_tool( "workflows-resume", - arguments={"workflow_id": "PauseResumeWorkflow", "run_id": run_id} + arguments={"workflow_id": workflow_id, "run_id": run_id} ) ``` diff --git a/examples/mcp_agent_server/temporal/basic_agent_server_worker.py b/examples/mcp_agent_server/temporal/basic_agent_server_worker.py index a156522b2..39b2a3c67 100644 --- a/examples/mcp_agent_server/temporal/basic_agent_server_worker.py +++ b/examples/mcp_agent_server/temporal/basic_agent_server_worker.py @@ -12,7 +12,7 @@ from mcp_agent.executor.temporal import create_temporal_worker_for_app -from examples.mcp_agent_server.temporal.main import app +from main import app # Initialize logging logging.basicConfig(level=logging.INFO) From 385868140090d2dde3eda4257df7ebc88e55e77c Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:18:10 -0400 Subject: [PATCH 4/9] Some updates --- examples/mcp_agent_server/temporal/README.md | 4 ++-- examples/mcp_agent_server/temporal/main.py | 15 ++++++++++++--- src/mcp_agent/executor/executor.py | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/mcp_agent_server/temporal/README.md b/examples/mcp_agent_server/temporal/README.md index a775273da..ea3edc861 100644 --- a/examples/mcp_agent_server/temporal/README.md +++ b/examples/mcp_agent_server/temporal/README.md @@ -271,8 +271,8 @@ class PauseResumeWorkflow(Workflow[str]): print(f"Workflow is pausing, workflow_id: {self.id}, run_id: {self.run_id}") # Wait for the resume signal - this will pause the workflow - await app.context.executor.signal_bus.wait_for_signal( - Signal(name="resume", workflow_id=self.id, run_id=self.run_id), + await app.context.executor.wait_for_signal( + signal_name="resume", workflow_id=self.id, run_id=self.run_id, ) print("Signal received, workflow is resuming...") diff --git a/examples/mcp_agent_server/temporal/main.py b/examples/mcp_agent_server/temporal/main.py index 468a01430..9fa1909a9 100644 --- a/examples/mcp_agent_server/temporal/main.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -15,7 +15,6 @@ from mcp_agent.app import MCPApp from mcp_agent.agents.agent import Agent from mcp_agent.core.context import Context -from mcp_agent.executor.workflow_signal import Signal from mcp_agent.server.app_server import create_mcp_server_for_app from mcp_agent.executor.workflow import Workflow, WorkflowResult from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM @@ -29,12 +28,19 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) + +def signal_notification_callback(**kwargs): + """Callback to notify when a signal is received in a workflow.""" + logger.info(f"SIGNAL NOTIFICATION CALLBACK: {kwargs}") + + # Create a single FastMCPApp instance (which extends MCPApp) app = MCPApp( name="basic_agent_server", description="Basic agent server example", human_input_callback=console_input_callback, # for local sampling approval elicitation_callback=console_elicitation_callback, # for local elicitation + signal_notification=signal_notification_callback, ) @@ -160,8 +166,11 @@ async def run( ) # Wait for the resume signal - this will pause the workflow until the signal is received - await app.context.executor.signal_bus.wait_for_signal( - Signal(name="resume", workflow_id=self.id, run_id=self.run_id), + await app.context.executor.wait_for_signal( + signal_name="resume", + workflow_id=self.id, + run_id=self.run_id, + timeout_seconds=60, ) print("Signal received, workflow is resuming...") diff --git a/src/mcp_agent/executor/executor.py b/src/mcp_agent/executor/executor.py index b68b1d38b..f8ec62101 100644 --- a/src/mcp_agent/executor/executor.py +++ b/src/mcp_agent/executor/executor.py @@ -218,7 +218,7 @@ async def wait_for_signal( workflow_id=workflow_id, run_id=run_id, ) - return await self.signal_bus.wait_for_signal(signal) + return await self.signal_bus.wait_for_signal(signal, timeout_seconds) def uuid(self) -> uuid.UUID: """ From 41c4a8f6f05b492736daf864db9c545ae39e4542 Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Thu, 18 Sep 2025 16:07:16 -0400 Subject: [PATCH 5/9] Move servers into example dirs --- examples/mcp_agent_server/asyncio/main.py | 8 +--- .../nested_elicitation_server.py | 0 .../nested_sampling_server.py | 0 examples/mcp_agent_server/temporal/main.py | 8 +--- .../temporal/nested_elicitation_server.py | 31 ++++++++++++++ .../temporal/nested_sampling_server.py | 40 +++++++++++++++++++ 6 files changed, 75 insertions(+), 12 deletions(-) rename examples/mcp_agent_server/{shared => asyncio}/nested_elicitation_server.py (100%) rename examples/mcp_agent_server/{shared => asyncio}/nested_sampling_server.py (100%) create mode 100644 examples/mcp_agent_server/temporal/nested_elicitation_server.py create mode 100644 examples/mcp_agent_server/temporal/nested_sampling_server.py diff --git a/examples/mcp_agent_server/asyncio/main.py b/examples/mcp_agent_server/asyncio/main.py index 0d5350e12..5542851fe 100644 --- a/examples/mcp_agent_server/asyncio/main.py +++ b/examples/mcp_agent_server/asyncio/main.py @@ -128,9 +128,7 @@ async def sampling_demo(topic: str, app_ctx: Optional[AppContext] = None) -> str # Register a simple nested server that uses sampling in its get_haiku tool nested_name = "nested_sampling" nested_path = os.path.abspath( - os.path.join( - os.path.dirname(__file__), "..", "shared", "nested_sampling_server.py" - ) + os.path.join(os.path.dirname(__file__), "nested_sampling_server.py") ) _app.context.config.mcp.servers[nested_name] = MCPServerSettings( name=nested_name, @@ -168,9 +166,7 @@ async def elicitation_demo( nested_name = "nested_elicitation" nested_path = os.path.abspath( - os.path.join( - os.path.dirname(__file__), "..", "shared", "nested_elicitation_server.py" - ) + os.path.join(os.path.dirname(__file__), "nested_elicitation_server.py") ) _app.context.config.mcp.servers[nested_name] = MCPServerSettings( name=nested_name, diff --git a/examples/mcp_agent_server/shared/nested_elicitation_server.py b/examples/mcp_agent_server/asyncio/nested_elicitation_server.py similarity index 100% rename from examples/mcp_agent_server/shared/nested_elicitation_server.py rename to examples/mcp_agent_server/asyncio/nested_elicitation_server.py diff --git a/examples/mcp_agent_server/shared/nested_sampling_server.py b/examples/mcp_agent_server/asyncio/nested_sampling_server.py similarity index 100% rename from examples/mcp_agent_server/shared/nested_sampling_server.py rename to examples/mcp_agent_server/asyncio/nested_sampling_server.py diff --git a/examples/mcp_agent_server/temporal/main.py b/examples/mcp_agent_server/temporal/main.py index b185b34e2..017db8bfa 100644 --- a/examples/mcp_agent_server/temporal/main.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -200,9 +200,7 @@ async def call_nested_sampling(topic: str) -> str: ) nested_name = "nested_sampling" nested_path = os.path.abspath( - os.path.join( - os.path.dirname(__file__), "..", "shared", "nested_sampling_server.py" - ) + os.path.join(os.path.dirname(__file__), "nested_sampling_server.py") ) app_ctx.config.mcp.servers[nested_name] = MCPServerSettings( name=nested_name, @@ -240,9 +238,7 @@ async def call_nested_elicitation(action: str) -> str: ) nested_name = "nested_elicitation" nested_path = os.path.abspath( - os.path.join( - os.path.dirname(__file__), "..", "shared", "nested_elicitation_server.py" - ) + os.path.join(os.path.dirname(__file__), "nested_elicitation_server.py") ) app_ctx.config.mcp.servers[nested_name] = MCPServerSettings( name=nested_name, diff --git a/examples/mcp_agent_server/temporal/nested_elicitation_server.py b/examples/mcp_agent_server/temporal/nested_elicitation_server.py new file mode 100644 index 000000000..34d477d83 --- /dev/null +++ b/examples/mcp_agent_server/temporal/nested_elicitation_server.py @@ -0,0 +1,31 @@ +from pydantic import BaseModel +from mcp.server.fastmcp import FastMCP +from mcp.server.elicitation import elicit_with_validation, AcceptedElicitation + +mcp = FastMCP("Nested Elicitation Server") + + +class Confirmation(BaseModel): + confirm: bool + + +@mcp.tool() +async def confirm_action(action: str) -> str: + """Ask the user to confirm an action via elicitation.""" + ctx = mcp.get_context() + res = await elicit_with_validation( + ctx.session, + message=f"Do you want to {action}?", + schema=Confirmation, + ) + if isinstance(res, AcceptedElicitation) and res.data.confirm: + return f"Action '{action}' confirmed by user" + return f"Action '{action}' declined by user" + + +def main(): + mcp.run() + + +if __name__ == "__main__": + main() diff --git a/examples/mcp_agent_server/temporal/nested_sampling_server.py b/examples/mcp_agent_server/temporal/nested_sampling_server.py new file mode 100644 index 000000000..32953079f --- /dev/null +++ b/examples/mcp_agent_server/temporal/nested_sampling_server.py @@ -0,0 +1,40 @@ +from mcp.server.fastmcp import FastMCP +from mcp.types import ModelPreferences, ModelHint, SamplingMessage, TextContent + +mcp = FastMCP("Nested Sampling Server") + + +@mcp.tool() +async def get_haiku(topic: str) -> str: + """Use MCP sampling to generate a haiku about the given topic.""" + result = await mcp.get_context().session.create_message( + messages=[ + SamplingMessage( + role="user", + content=TextContent( + type="text", text=f"Generate a quirky haiku about {topic}." + ), + ) + ], + system_prompt="You are a poet.", + max_tokens=100, + temperature=0.7, + model_preferences=ModelPreferences( + hints=[ModelHint(name="gpt-4o-mini")], + costPriority=0.1, + speedPriority=0.8, + intelligencePriority=0.1, + ), + ) + + if isinstance(result.content, TextContent): + return result.content.text + return "Haiku generation failed" + + +def main(): + mcp.run() + + +if __name__ == "__main__": + main() From 1f44d95b94e0ce1c404eb9e5f052ce50b9ebc418 Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Thu, 18 Sep 2025 16:19:42 -0400 Subject: [PATCH 6/9] Format --- examples/mcp_agent_server/temporal/main.py | 6 -- tests/utils/test_config_preload.py | 69 ++++++++++++++-------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/examples/mcp_agent_server/temporal/main.py b/examples/mcp_agent_server/temporal/main.py index 017db8bfa..538ecfb8f 100644 --- a/examples/mcp_agent_server/temporal/main.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -31,18 +31,12 @@ logger = logging.getLogger(__name__) -def signal_notification_callback(**kwargs): - """Callback to notify when a signal is received in a workflow.""" - logger.info(f"SIGNAL NOTIFICATION CALLBACK: {kwargs}") - - # Create a single FastMCPApp instance (which extends MCPApp) app = MCPApp( name="basic_agent_server", description="Basic agent server example", human_input_callback=console_input_callback, # for local sampling approval elicitation_callback=console_elicitation_callback, # for local elicitation - signal_notification=signal_notification_callback, ) diff --git a/tests/utils/test_config_preload.py b/tests/utils/test_config_preload.py index dec8f83b8..897f41b7e 100644 --- a/tests/utils/test_config_preload.py +++ b/tests/utils/test_config_preload.py @@ -140,12 +140,13 @@ def test_default_sets_global_state(self, sample_config): """Test that get_settings() with default parameters sets global state.""" # Verify global settings is None initially import mcp_agent.config + assert mcp_agent.config._settings is None # Mock file operations yaml_content = yaml.dump(sample_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): # Load settings with default behavior settings = get_settings(config_path="/fake/path/config.yaml") @@ -157,12 +158,15 @@ def test_default_sets_global_state(self, sample_config): def test_set_global_false_no_global_state(self, sample_config): """Test that set_global=False doesn't modify global state.""" import mcp_agent.config + assert mcp_agent.config._settings is None yaml_content = yaml.dump(sample_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): - settings = get_settings(config_path="/fake/path/config.yaml", set_global=False) + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): + settings = get_settings( + config_path="/fake/path/config.yaml", set_global=False + ) # Global state should remain None assert mcp_agent.config._settings is None @@ -173,12 +177,15 @@ def test_set_global_false_no_global_state(self, sample_config): def test_explicit_set_global_true(self, sample_config): """Test explicitly passing set_global=True.""" import mcp_agent.config + assert mcp_agent.config._settings is None yaml_content = yaml.dump(sample_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): - settings = get_settings(config_path="/fake/path/config.yaml", set_global=True) + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): + settings = get_settings( + config_path="/fake/path/config.yaml", set_global=True + ) assert mcp_agent.config._settings is not None assert mcp_agent.config._settings == settings @@ -186,8 +193,8 @@ def test_explicit_set_global_true(self, sample_config): def test_returns_cached_global_when_set(self, sample_config): """Test that subsequent calls return cached global settings.""" yaml_content = yaml.dump(sample_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): # First call sets global state settings1 = get_settings(config_path="/fake/path/config.yaml") @@ -197,18 +204,23 @@ def test_returns_cached_global_when_set(self, sample_config): # They should be the same object assert settings1 is settings2 import mcp_agent.config + assert mcp_agent.config._settings is settings1 def test_no_cached_return_when_set_global_false(self, sample_config): """Test that set_global=False always loads fresh settings.""" yaml_content = yaml.dump(sample_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): # First call with set_global=False - settings1 = get_settings(config_path="/fake/path/config.yaml", set_global=False) + settings1 = get_settings( + config_path="/fake/path/config.yaml", set_global=False + ) # Second call with set_global=False - settings2 = get_settings(config_path="/fake/path/config.yaml", set_global=False) + settings2 = get_settings( + config_path="/fake/path/config.yaml", set_global=False + ) # They should be different objects (not cached) assert settings1 is not settings2 @@ -216,6 +228,7 @@ def test_no_cached_return_when_set_global_false(self, sample_config): assert settings1 == settings2 # Global should remain None import mcp_agent.config + assert mcp_agent.config._settings is None def test_preload_with_set_global_false(self, sample_config, monkeypatch): @@ -227,6 +240,7 @@ def test_preload_with_set_global_false(self, sample_config, monkeypatch): # Global state should not be set import mcp_agent.config + assert mcp_agent.config._settings is None # Settings should be loaded from preload @@ -295,8 +309,7 @@ def test_no_warning_from_main_thread(self): # Should not have thread-related warnings thread_warnings = [ - warn for warn in w - if "non-main thread" in str(warn.message) + warn for warn in w if "non-main thread" in str(warn.message) ] assert len(thread_warnings) == 0 @@ -306,8 +319,8 @@ def test_multiple_threads_independent_settings(self, simple_config): def load_settings(thread_id, config_path): yaml_content = yaml.dump(simple_config) - with patch('builtins.open', mock_open(read_data=yaml_content)): - with patch('pathlib.Path.exists', return_value=True): + with patch("builtins.open", mock_open(read_data=yaml_content)): + with patch("pathlib.Path.exists", return_value=True): settings = get_settings(config_path=config_path, set_global=False) thread_settings[thread_id] = settings @@ -315,8 +328,7 @@ def load_settings(thread_id, config_path): threads = [] for i in range(3): thread = threading.Thread( - target=load_settings, - args=(i, "/fake/path/config.yaml") + target=load_settings, args=(i, "/fake/path/config.yaml") ) threads.append(thread) thread.start() @@ -327,6 +339,7 @@ def load_settings(thread_id, config_path): # Verify all threads got settings but global state wasn't set import mcp_agent.config + assert mcp_agent.config._settings is None assert len(thread_settings) == 3 for i in range(3): @@ -356,23 +369,28 @@ def config_data_with_secrets(self): } return config_data, secrets_data - def test_config_and_secrets_merge_with_set_global_false(self, config_data_with_secrets): + def test_config_and_secrets_merge_with_set_global_false( + self, config_data_with_secrets + ): """Test that config and secrets merge correctly without setting global state.""" config_data, secrets_data = config_data_with_secrets # Merge the data as the config loader would merged_data = config_data.copy() - merged_data['openai'] = secrets_data['openai'] # Secrets override config + merged_data["openai"] = secrets_data["openai"] # Secrets override config # Mock the config file read with already merged data merged_yaml = yaml.dump(merged_data) - with patch('builtins.open', mock_open(read_data=merged_yaml)): - with patch('pathlib.Path.exists', return_value=True): - settings = get_settings(config_path="/fake/path/config.yaml", set_global=False) + with patch("builtins.open", mock_open(read_data=merged_yaml)): + with patch("pathlib.Path.exists", return_value=True): + settings = get_settings( + config_path="/fake/path/config.yaml", set_global=False + ) # Global state should not be set import mcp_agent.config + assert mcp_agent.config._settings is None # Settings should have the merged values @@ -386,6 +404,7 @@ def test_default_settings_with_set_global_false(self): # Global state should not be set import mcp_agent.config + assert mcp_agent.config._settings is None # Should get default settings From 317d6d50129e66beb5a12bb8414c1fa75b147ffa Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:28:16 -0400 Subject: [PATCH 7/9] update comment to test --- examples/mcp_agent_server/temporal/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mcp_agent_server/temporal/main.py b/examples/mcp_agent_server/temporal/main.py index 538ecfb8f..f2dbeb52a 100644 --- a/examples/mcp_agent_server/temporal/main.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -98,7 +98,7 @@ async def finder_tool(request: str, app_ctx: Context | None = None) -> str: """ Run the basic agent workflow using the app.tool decorator to set up the workflow. The code in this function is run in workflow context. - LLM calls are executed in the activity context. + LLM calls are executed in the activity context.. You can use the app_ctx to access the executor to run activities explicitly. Functions decorated with @app.workflow_task will be run in activity context. From 35189aa67eb329019177576785830f63bfa217f7 Mon Sep 17 00:00:00 2001 From: rholinshead <5060851+rholinshead@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:28:42 -0400 Subject: [PATCH 8/9] remove comment change --- examples/mcp_agent_server/temporal/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mcp_agent_server/temporal/main.py b/examples/mcp_agent_server/temporal/main.py index f2dbeb52a..538ecfb8f 100644 --- a/examples/mcp_agent_server/temporal/main.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -98,7 +98,7 @@ async def finder_tool(request: str, app_ctx: Context | None = None) -> str: """ Run the basic agent workflow using the app.tool decorator to set up the workflow. The code in this function is run in workflow context. - LLM calls are executed in the activity context.. + LLM calls are executed in the activity context. You can use the app_ctx to access the executor to run activities explicitly. Functions decorated with @app.workflow_task will be run in activity context. From d7d71600f6222da9cb7834756eb1e45606c96422 Mon Sep 17 00:00:00 2001 From: Ryan Holinshead <5060851+rholinshead@users.noreply.github.com> Date: Fri, 19 Sep 2025 10:49:36 -0400 Subject: [PATCH 9/9] Update examples/mcp_agent_server/temporal/README.md Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> --- examples/mcp_agent_server/temporal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mcp_agent_server/temporal/README.md b/examples/mcp_agent_server/temporal/README.md index ea3edc861..d79401fc7 100644 --- a/examples/mcp_agent_server/temporal/README.md +++ b/examples/mcp_agent_server/temporal/README.md @@ -323,7 +323,7 @@ async with gen_client("basic_agent_server", context.server_registry) as server: ) run_id = execution.run_id - workflow_id = exection.workflow_id + workflow_id = execution.workflow_id await server.call_tool( "workflows-resume",