|
1 | 1 | --- |
2 | | -title: Build Your Own Pattern |
3 | | -sidebarTitle: "Build Your Own" |
4 | | -description: "Create custom agent workflow patterns" |
| 2 | +title: "Build Your Own Pattern" |
| 3 | +description: "Compose custom agent workflows from the core building blocks" |
5 | 4 | icon: wand-magic-sparkles |
6 | 5 | --- |
7 | 6 |
|
8 | | -<Info> |
9 | | -Content to be added showing how to compose custom patterns from existing workflows. |
10 | | -</Info> |
| 7 | +mcp-agent patterns are deliberately composable. You can mix routers, parallel fan-outs, evaluators, orchestrators, and plain Python callables to create flows that match your product requirements—without authoring new workflow classes. |
11 | 8 |
|
12 | | -## Overview |
| 9 | +## Building blocks recap |
13 | 10 |
|
14 | | -All workflow patterns in mcp-agent are composable. You can combine them to create custom patterns tailored to your use case. |
| 11 | +- [`create_llm(...)`](https://github.com/lastmile-ai/mcp-agent/blob/main/src/mcp_agent/workflows/factory.py#L63) – wrap an `AgentSpec` in an AugmentedLLM tied to a provider (OpenAI, Anthropic, Azure, Google, Bedrock, Ollama). |
| 12 | +- [`create_router_llm(...)`](/mcp-agent-sdk/effective-patterns/router) / `create_router_embedding(...)` – dispatch requests to the best specialist. |
| 13 | +- [`create_intent_classifier_llm(...)`](/mcp-agent-sdk/effective-patterns/intent-classifier) – bucket requests before routing. |
| 14 | +- [`create_parallel_llm(...)`](/mcp-agent-sdk/effective-patterns/map-reduce) – run multiple workers in parallel and aggregate their outputs. |
| 15 | +- [`create_evaluator_optimizer_llm(...)`](/mcp-agent-sdk/effective-patterns/evaluator-optimizer) – iterate until a reviewer approves the response. |
| 16 | +- [`create_orchestrator(...)`](/mcp-agent-sdk/effective-patterns/planner) – break complex objectives into sequenced steps. |
| 17 | +- [`create_deep_orchestrator(...)`](/mcp-agent-sdk/effective-patterns/deep-research) – add knowledge extraction, policy engines, and budgets. |
| 18 | +- [`create_swarm(...)`](https://github.com/lastmile-ai/mcp-agent/blob/main/src/mcp_agent/workflows/factory.py#L501) – OpenAI/Anthropic-compatible handoffs between agents. |
| 19 | +- [`load_agent_specs_from_dir(...)`](https://github.com/lastmile-ai/mcp-agent/blob/main/src/mcp_agent/workflows/factory.py#L553) – hydrate agents from YAML/JSON specs. |
15 | 20 |
|
16 | | -## Example: Custom Pattern |
| 21 | +## Design playbook |
| 22 | + |
| 23 | +1. **Model your specialists** as `AgentSpec` (name, instruction, tool access). Keep prompts short and behaviour-specific. |
| 24 | +2. **Pick a routing strategy**: intent classifier for lightweight gating, router for multi-skill dispatch, or orchestrator for complex plans. |
| 25 | +3. **Layer guardrails**: wrap high-risk steps in an evaluator-optimizer loop, or add policy agents inside an orchestrator step. |
| 26 | +4. **Add determinism**: integrate `fan_out_functions` for repeatable checks or use embedding routers for fixed scoring. |
| 27 | +5. **Expose the composition** with `@app.tool` / `@app.async_tool` so MCP clients can call it as a single tool. |
| 28 | +6. **Instrument** with the token counter (`await workflow.get_token_node()`) and tracing (`otel.enabled: true`) before shipping. |
| 29 | + |
| 30 | +## Example: router ➝ parallel research ➝ evaluator |
17 | 31 |
|
18 | 32 | ```python |
19 | | -# Content to be added |
| 33 | +from mcp_agent.app import MCPApp |
| 34 | +from mcp_agent.workflows.factory import ( |
| 35 | + AgentSpec, |
| 36 | + create_evaluator_optimizer_llm, |
| 37 | + create_parallel_llm, |
| 38 | + create_router_llm, |
| 39 | +) |
| 40 | + |
| 41 | +app = MCPApp(name="composed_pattern") |
| 42 | + |
| 43 | +# Cache long-lived components so we don't recreate them per request. |
| 44 | +router = None |
| 45 | +parallel_research = None |
| 46 | +research_loop = None |
| 47 | + |
| 48 | +@app.async_tool(name="answer_question") |
| 49 | +async def answer(request: str) -> str: |
| 50 | + global router, parallel_research, research_loop |
| 51 | + async with app.run() as running_app: |
| 52 | + ctx = running_app.context |
| 53 | + |
| 54 | + if router is None: |
| 55 | + router = await create_router_llm( |
| 56 | + name="triage", |
| 57 | + agents=[ |
| 58 | + AgentSpec(name="qa", instruction="Answer factual questions concisely."), |
| 59 | + AgentSpec( |
| 60 | + name="analysis", |
| 61 | + instruction="Perform deep research with citations before answering.", |
| 62 | + ), |
| 63 | + ], |
| 64 | + provider="openai", |
| 65 | + context=ctx, |
| 66 | + ) |
| 67 | + |
| 68 | + if parallel_research is None: |
| 69 | + parallel_research = create_parallel_llm( |
| 70 | + name="research_parallel", |
| 71 | + fan_in=AgentSpec( |
| 72 | + name="aggregator", |
| 73 | + instruction="Blend researcher outputs into a single structured brief.", |
| 74 | + ), |
| 75 | + fan_out=[ |
| 76 | + AgentSpec( |
| 77 | + name="news", |
| 78 | + instruction="Search recent press releases.", |
| 79 | + server_names=["fetch"], |
| 80 | + ), |
| 81 | + AgentSpec( |
| 82 | + name="financials", |
| 83 | + instruction="Lookup filings and key metrics.", |
| 84 | + server_names=["fetch"], |
| 85 | + ), |
| 86 | + ], |
| 87 | + context=ctx, |
| 88 | + ) |
| 89 | + |
| 90 | + if research_loop is None: |
| 91 | + research_loop = create_evaluator_optimizer_llm( |
| 92 | + name="research_with_qc", |
| 93 | + optimizer=parallel_research, |
| 94 | + evaluator=AgentSpec( |
| 95 | + name="editor", |
| 96 | + instruction=( |
| 97 | + "Score the brief from 1-5. Demand improvements if it lacks citations, " |
| 98 | + "actionable insights, or policy compliance." |
| 99 | + ), |
| 100 | + ), |
| 101 | + min_rating=4, |
| 102 | + max_refinements=3, |
| 103 | + context=ctx, |
| 104 | + ) |
| 105 | + |
| 106 | + decision = await router.route(request, top_k=1) |
| 107 | + top = decision[0] |
| 108 | + |
| 109 | + if top.category == "agent" and top.result.name == "analysis": |
| 110 | + return await research_loop.generate_str(request) |
| 111 | + |
| 112 | + if top.category == "agent": |
| 113 | + async with top.result: |
| 114 | + return await top.result.generate_str(request) |
| 115 | + |
| 116 | + # Fallback: let the router destination handle it directly |
| 117 | + return await top.result.generate_str(request) |
20 | 118 | ``` |
21 | 119 |
|
22 | | -[See Composition →](/mcp-agent-sdk/advanced/composition) |
| 120 | +Highlights: |
| 121 | + |
| 122 | +- Router, parallel workflow, and evaluator are created once and reused across requests. |
| 123 | +- The evaluator-loop wraps the parallel workflow, so quality checks happen before the response leaves the system. |
| 124 | +- The entire composition is exposed as an MCP tool via `@app.async_tool`, making it callable from Claude, Cursor, or other MCP clients. |
| 125 | + |
| 126 | +## Patterns that mix well |
| 127 | + |
| 128 | +- **Intent classifier ➝ router**: Use the classifier for coarse gating (“is this support vs. billing?”) then route to specialists. |
| 129 | +- **Parallel ➝ evaluator**: Run multiple evaluators in parallel (policy, clarity, bias) and feed their combined verdict back to the optimizer. |
| 130 | +- **Orchestrator ➝ evaluator**: Wrap the final synthesis step in an evaluator loop so the orchestrator keeps iterating until the review passes. |
| 131 | +- **Router ➝ orchestrator**: Route strategic tasks to an orchestrator for deep execution, while simple tasks go to lightweight agents. |
| 132 | +- **Swarm handlers**: Use `create_swarm(...)` to hand off between agents mid-conversation, while still using MCP tools for capabilities. |
| 133 | + |
| 134 | +## Operational tips |
| 135 | + |
| 136 | +- **Share the context**: keep compositions inside `async with app.run()` so every component reuses the same `Context` (server registry, executor, secrets, tracing). |
| 137 | +- **Tune once, reuse everywhere**: store provider/model defaults in `mcp_agent.config.yaml`; override per pattern only when necessary. |
| 138 | +- **Observe everything**: `await workflow.get_token_node()` shows token spend for nested workflows; enable OTEL tracing to follow router choices, parallel branches, and evaluator scores. |
| 139 | +- **Blend deterministic helpers**: pass `fan_out_functions` or router `functions` for cheap heuristics (regex, lookups) alongside LLM-heavy steps. |
| 140 | +- **Think in tools**: once composed, wrap the entire pattern with `@app.tool` / `@app.async_tool` so it becomes an MCP tool. Other agents, orchestrators, or human operators can call it without knowing how it is assembled. |
| 141 | + |
| 142 | +## Examples to study |
| 143 | + |
| 144 | +- [workflow_orchestrator_worker](https://github.com/lastmile-ai/mcp-agent/tree/main/examples/workflows/workflow_orchestrator_worker) – combines routing, parallel tasks, and orchestration to grade student assignments. |
| 145 | +- [workflow_evaluator_optimizer](https://github.com/lastmile-ai/mcp-agent/tree/main/examples/workflows/workflow_evaluator_optimizer) – demonstrates evaluator loops, tool exposure, and cloud deployment. |
| 146 | +- [workflow_parallel](https://github.com/lastmile-ai/mcp-agent/tree/main/examples/workflows/workflow_parallel) – shows how to compose `AgentSpec`, AugmentedLLMs, and deterministic helpers inside one tool. |
| 147 | +- [Temporal examples](https://github.com/lastmile-ai/mcp-agent/tree/main/examples/temporal) – walk through exposing custom compositions as durable Temporal workflows. |
0 commit comments