|
| 1 | +--- |
| 2 | +title: Agent Servers |
| 3 | +description: "Exposing agents as MCP servers" |
| 4 | +--- |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +mcp-agent allows you to expose your agents and workflows as MCP servers. This enables any MCP-compatible client (Claude Desktop, VS Code, custom applications) to interact with your agents through the standard MCP protocol. |
| 9 | + |
| 10 | +{/* TODO: Add video demonstration of agent server in action */} |
| 11 | + |
| 12 | +## How It Works |
| 13 | + |
| 14 | +When you expose an agent as an MCP server, the framework: |
| 15 | +1. Creates an MCP server using FastMCP |
| 16 | +2. Registers workflows as MCP tools |
| 17 | +3. Provides standard workflow management tools |
| 18 | +4. Handles protocol communication |
| 19 | + |
| 20 | +## Default MCP Tools |
| 21 | + |
| 22 | +Every agent server automatically provides these workflow management tools: |
| 23 | + |
| 24 | +### workflows-list |
| 25 | +Lists all available workflow types with their parameters and capabilities. |
| 26 | + |
| 27 | +**Returns:** |
| 28 | +- Workflow names and descriptions |
| 29 | +- Parameter schemas |
| 30 | +- Available operations (run, resume, cancel, get_status) |
| 31 | +- Tool endpoints |
| 32 | + |
| 33 | +### workflows-runs-list |
| 34 | +Lists all running workflow instances with their current status. |
| 35 | + |
| 36 | +**Returns:** |
| 37 | +- Workflow instance IDs |
| 38 | +- Current state (running, paused, completed, failed) |
| 39 | +- Workflow type name |
| 40 | +- Execution metadata |
| 41 | + |
| 42 | +### workflows-run |
| 43 | +Executes a workflow with specified parameters. |
| 44 | + |
| 45 | +**Parameters:** |
| 46 | +- `workflow_name`: Name of the workflow to run |
| 47 | +- `run_parameters`: Arguments for the workflow |
| 48 | + |
| 49 | +**Returns:** |
| 50 | +- `workflow_id`: Workflow identifier |
| 51 | +- `run_id`: Unique run instance ID |
| 52 | + |
| 53 | +### workflows-get_status |
| 54 | +Retrieves the current status of a workflow instance. |
| 55 | + |
| 56 | +**Parameters:** |
| 57 | +- `run_id`: The run instance ID |
| 58 | +- `workflow_id`: Optional workflow identifier |
| 59 | + |
| 60 | +**Returns:** |
| 61 | +- Current state |
| 62 | +- Results (if completed) |
| 63 | +- Error details (if failed) |
| 64 | +- Execution progress |
| 65 | + |
| 66 | +### workflows-resume |
| 67 | +Resumes a paused workflow, optionally with input data. |
| 68 | + |
| 69 | +**Parameters:** |
| 70 | +- `run_id`: The run instance ID |
| 71 | +- `workflow_name`: Workflow identifier |
| 72 | +- `signal_name`: Signal to send (default: "resume") |
| 73 | +- `payload`: Optional data to provide |
| 74 | + |
| 75 | +### workflows-cancel |
| 76 | +Cancels a running workflow instance. |
| 77 | + |
| 78 | +**Parameters:** |
| 79 | +- `run_id`: The run instance ID |
| 80 | +- `workflow_name`: Workflow identifier |
| 81 | + |
| 82 | +## Custom Tools via Decorators |
| 83 | + |
| 84 | +### @app.tool - Synchronous Tools |
| 85 | + |
| 86 | +Synchronous tools execute immediately and return results: |
| 87 | + |
| 88 | +```python |
| 89 | +@app.tool |
| 90 | +def calculate_sum(a: int, b: int) -> int: |
| 91 | + """Add two numbers together.""" |
| 92 | + return a + b |
| 93 | +``` |
| 94 | + |
| 95 | +This creates a single MCP tool `calculate_sum` that executes synchronously. |
| 96 | + |
| 97 | +### @app.async_tool - Asynchronous Tools |
| 98 | + |
| 99 | +Asynchronous tools run as durable workflows: |
| 100 | + |
| 101 | +```python |
| 102 | +@app.async_tool |
| 103 | +async def research_topic(topic: str) -> str: |
| 104 | + """Research a topic using multiple sources.""" |
| 105 | + # Long-running research operation |
| 106 | + results = await gather_information(topic) |
| 107 | + return results |
| 108 | +``` |
| 109 | + |
| 110 | +This creates: |
| 111 | +- `research_topic`: Starts the workflow |
| 112 | +- Status tracking via `workflows-get_status` |
| 113 | +- Cancellation via `workflows-cancel` |
| 114 | + |
| 115 | +## Server Configuration |
| 116 | + |
| 117 | +### Basic Setup |
| 118 | + |
| 119 | +```python |
| 120 | +from mcp_agent.app import MCPApp |
| 121 | +from mcp_agent.server import create_mcp_server_for_app |
| 122 | + |
| 123 | +app = MCPApp(name="my_agent_server") |
| 124 | + |
| 125 | +# Register workflows |
| 126 | +@app.workflow |
| 127 | +class ResearchWorkflow: |
| 128 | + async def run(self, query: str): |
| 129 | + # Workflow implementation |
| 130 | + pass |
| 131 | + |
| 132 | +# Create MCP server |
| 133 | +mcp_server = create_mcp_server_for_app(app) |
| 134 | +``` |
| 135 | + |
| 136 | +### Running the Server |
| 137 | + |
| 138 | +#### Using stdio |
| 139 | +```bash |
| 140 | +uv run mcp-agent serve --app my_agent:app |
| 141 | +``` |
| 142 | + |
| 143 | +#### Using SSE |
| 144 | +```bash |
| 145 | +uv run mcp-agent serve --app my_agent:app --transport sse |
| 146 | +``` |
| 147 | + |
| 148 | +## Client Configuration |
| 149 | + |
| 150 | +### Claude Desktop |
| 151 | + |
| 152 | +Add to `claude_desktop_config.json`: |
| 153 | + |
| 154 | +```json |
| 155 | +{ |
| 156 | + "mcpServers": { |
| 157 | + "my-agent": { |
| 158 | + "command": "uv", |
| 159 | + "args": ["run", "mcp-agent", "serve", "--app", "my_agent:app"] |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | +``` |
| 164 | + |
| 165 | +### Programmatic Access |
| 166 | + |
| 167 | +```python |
| 168 | +from mcp import ClientSession |
| 169 | + |
| 170 | +async with ClientSession(server_params) as session: |
| 171 | + # List available workflows |
| 172 | + result = await session.call_tool("workflows-list", {}) |
| 173 | + |
| 174 | + # Run a workflow |
| 175 | + run_info = await session.call_tool("workflows-run", { |
| 176 | + "workflow_name": "research", |
| 177 | + "run_parameters": {"query": "quantum computing"} |
| 178 | + }) |
| 179 | + |
| 180 | + # Check status |
| 181 | + status = await session.call_tool("workflows-get_status", { |
| 182 | + "run_id": run_info["run_id"] |
| 183 | + }) |
| 184 | +``` |
| 185 | + |
| 186 | +## Workflow Registration |
| 187 | + |
| 188 | +Workflows are automatically discovered and registered as MCP tools: |
| 189 | + |
| 190 | +```python |
| 191 | +@app.workflow |
| 192 | +class DataProcessingWorkflow: |
| 193 | + """Process and analyze data files.""" |
| 194 | + |
| 195 | + async def run(self, file_path: str, options: dict): |
| 196 | + # Processing logic |
| 197 | + return processed_data |
| 198 | +``` |
| 199 | + |
| 200 | +This creates tools: |
| 201 | +- `workflows-data_processing-run`: Execute the workflow |
| 202 | +- `workflows-data_processing-get_status`: Check execution status |
| 203 | + |
| 204 | +## Human-in-the-Loop Patterns |
| 205 | + |
| 206 | +Agent servers support human interaction through pause/resume: |
| 207 | + |
| 208 | +```python |
| 209 | +@app.workflow |
| 210 | +class ApprovalWorkflow: |
| 211 | + async def run(self, ctx: WorkflowContext, request: dict): |
| 212 | + # Process initial request |
| 213 | + analysis = await analyze_request(request) |
| 214 | + |
| 215 | + # Pause for human approval |
| 216 | + approval = await ctx.wait_for_signal("approval") |
| 217 | + |
| 218 | + if approval["approved"]: |
| 219 | + return await execute_action(request) |
| 220 | + else: |
| 221 | + return {"status": "rejected", "reason": approval["reason"]} |
| 222 | +``` |
| 223 | + |
| 224 | +Clients can resume with: |
| 225 | +```python |
| 226 | +await session.call_tool("workflows-resume", { |
| 227 | + "run_id": run_id, |
| 228 | + "signal_name": "approval", |
| 229 | + "payload": '{"approved": true}' |
| 230 | +}) |
| 231 | +``` |
| 232 | + |
| 233 | +## Monitoring and Logging |
| 234 | + |
| 235 | +Agent servers provide real-time logging through MCP's logging capability: |
| 236 | + |
| 237 | +```python |
| 238 | +# Client can set logging level |
| 239 | +await session.set_logging_level("debug") |
| 240 | + |
| 241 | +# Server logs are forwarded to client |
| 242 | +logger.info("Processing request", extra={"request_id": "123"}) |
| 243 | +``` |
| 244 | + |
| 245 | +## Advanced Features |
| 246 | + |
| 247 | +### Custom Tool Names |
| 248 | + |
| 249 | +Override default tool names: |
| 250 | + |
| 251 | +```python |
| 252 | +@app.tool(name="custom_calculator") |
| 253 | +def add_numbers(a: int, b: int) -> int: |
| 254 | + return a + b |
| 255 | +``` |
| 256 | + |
| 257 | +### Tool Descriptions |
| 258 | + |
| 259 | +Provide detailed descriptions for better discoverability: |
| 260 | + |
| 261 | +```python |
| 262 | +@app.workflow |
| 263 | +class AnalysisWorkflow: |
| 264 | + """ |
| 265 | + Perform comprehensive data analysis. |
| 266 | + |
| 267 | + This workflow processes datasets, generates visualizations, |
| 268 | + and produces detailed statistical reports. |
| 269 | + """ |
| 270 | + async def run(self, data: dict): |
| 271 | + pass |
| 272 | +``` |
| 273 | + |
| 274 | +### Parameter Validation |
| 275 | + |
| 276 | +Parameters are automatically validated based on type hints: |
| 277 | + |
| 278 | +```python |
| 279 | +from typing import Literal |
| 280 | + |
| 281 | +@app.tool |
| 282 | +def process_data( |
| 283 | + format: Literal["json", "csv", "xml"], |
| 284 | + validate: bool = True |
| 285 | +) -> dict: |
| 286 | + """Process data in specified format.""" |
| 287 | + pass |
| 288 | +``` |
| 289 | + |
| 290 | +## Best Practices |
| 291 | + |
| 292 | +1. **Tool Naming**: Use clear, descriptive names for workflows and tools |
| 293 | +2. **Documentation**: Always include docstrings for workflows and tools |
| 294 | +3. **Error Handling**: Implement proper error handling in workflows |
| 295 | +4. **State Management**: Use workflow context for persistent state |
| 296 | +5. **Resource Cleanup**: Ensure proper cleanup in workflow finally blocks |
| 297 | +6. **Type Hints**: Use type hints for automatic parameter validation |
| 298 | + |
| 299 | + |
| 300 | +## Next Steps |
| 301 | + |
| 302 | +- [Configuration Guide](/configuration) |
| 303 | +- [Workflow Patterns](/workflows/overview) |
| 304 | +- [Cloud Deployment](/cloud/getting-started) |
0 commit comments