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/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/README.md b/examples/mcp_agent_server/temporal/README.md index 0ec7a378b..d79401fc7 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 @@ -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...") @@ -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 = execution.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 992765da4..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 basic_agent_server import app +from 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 98% rename from examples/mcp_agent_server/temporal/basic_agent_server.py rename to examples/mcp_agent_server/temporal/main.py index 41c8b1594..538ecfb8f 100644 --- a/examples/mcp_agent_server/temporal/basic_agent_server.py +++ b/examples/mcp_agent_server/temporal/main.py @@ -30,6 +30,7 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) + # Create a single FastMCPApp instance (which extends MCPApp) app = MCPApp( name="basic_agent_server", @@ -193,9 +194,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, @@ -233,9 +232,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() 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