Lesson 25: Orchestrate and Integrate Our Capstone Agents
Throughout this course, we have built two specialized agents: Nova for research and Brown for writing. We have run them separately, but they were always designed to work in sequence. Nova gathers the information, and Brown uses that information to write an article. Now it is time to integrate them into a unified system.
In this lesson, we will explore how to orchestrate both agents so that a single LLM can use their tools together. The beauty of the Model Context Protocol (MCP) lies in its straightforward integration. We will explore two approaches: a multi-server MCP client that connects to multiple independent servers, and a composed MCP server that unifies multiple agents into a single endpoint. By the end of this lesson, you will understand when to use each approach.
From Separate Agents to Unified System
Before we dive into the implementation, let's understand the orchestration model we are using. We are implementing a Central LLM Orchestration pattern. In this approach, a single, central LLM, like the one that may be in your IDE, has access to tools from multiple specialized agents. When you assign a task, the LLM dynamically determines which agent's tools to use, maintaining a single conversation context and orchestrating the workflow by selecting the appropriate tools as needed [6], [7].
This pattern is different from others. A Supervisor Agent would have one agent explicitly delegate sub-tasks to worker agents [9]. In a Peer-to-Peer or Network Architecture, agents communicate dynamically without a central controller, offering flexibility but increasing the complexity of coordination [9]. Our central LLM orchestration is simpler: the LLM acts as an intelligent tool selector rather than an explicit coordinator [10].
This pattern works well with MCP. Both Nova and Brown expose their capabilities as MCP tools with clear descriptions, allowing the central LLM to discover all available tools through a single, standardized protocol [11]. This unified interface eliminates the need for custom integration code. Because both agents speak MCP, we do not need to write adapters or APIs. The client connects to both servers and aggregates their tools.
The Rationale: Why Choose Central LLM Orchestration?
This orchestration pattern offers several advantages that make it ideal for integrating Nova and Brown. The most immediate benefit is simplicity and maintainability. All decision-making happens in one place: the central LLM. This means the workflow is transparent and easy to understand. There is no need for complex state management or inter-agent communication protocols, which reduces the cognitive overhead of debugging the system [12].
The pattern also enables natural task decomposition. The central LLM can break down complex requests on the fly without requiring predefined workflows. More importantly, it can adapt its strategy based on intermediate results. For example, if research reveals unexpected information, the LLM can adjust its approach accordingly by doing further research on that topic. This adaptive behavior is valuable for human-in-the-loop workflows, which are common in IDE environments. You can provide feedback at any point, and the LLM incorporates it naturally [5].
The central LLM maintains a single, unified context window , which means it can reference information from Nova's research when calling Brown's tools without requiring explicit data passing between agents [13]. This avoids the "siloed knowledge" problem, where critical information gets trapped in one agent's context and becomes unavailable to others.
Finally, this pattern is perfect for sequential, interdependent tasks. Our research workflow, followed by writing, is inherently sequential, and the writing task heavily depends on the research results. A central orchestrator can easily manage these dependencies because it sees the entire workflow and can make informed decisions about when to transition between phases [14], [15].
The Trade-offs: Understanding the Limitations
While central LLM orchestration is powerful, it is essential to understand its limitations. The first is tool overload. As the number of available tools increases, LLMs struggle to make reliable selections of tools. They may choose suboptimal tools or miss relevant ones entirely. Research shows that performance can degrade significantly when models must choose from a large, diverse set of tools [1], [2]. Our system, with 14 tools total (11 from Nova and 3 from Brown), is comfortably within the typical limit of 15-20 tools.
Another limitation is the pattern's sequential execution model. The LLM executes tools one at a time. If you needed to research 50 companies simultaneously, this approach would be inefficient. In such scenarios, a Supervisor-Worker pattern with parallel execution would be better [8].
The pattern also reduces opportunities for human-in-the-loop interaction compared to our earlier single-agent workflows. As we delegate more control to the central orchestrator, the system becomes more autonomous. The LLM now decides not just how to use individual tools, but when to transition between research and writing phases. This shift up the autonomy spectrum means humans have fewer natural intervention points.
The pattern also struggles with complex inter-agent dependencies. If agents need to negotiate with each other, engage in debate, or iteratively refine each other's work through back-and-forth exchanges, direct agent-to-agent communication would be more natural. Our pattern handles simple, linear dependencies well, but complex multi-way interactions would become awkward [7].
Despite these limitations, central LLM orchestration is the correct default choice for most agent integration scenarios. It is simple, maintainable, and uses the LLM's natural reasoning without adding unnecessary complexity. You should only consider more elaborate patterns when you hit clear scaling limits or have fundamentally different requirements.
We’ll dive more into multi-agent architectures in later lessons.
Integrating Nova and Brown
Now that we understand the central LLM orchestration pattern and its rationale, let's see how it works in practice. Throughout this course, we've built two specialized agents: Nova for research (which ingests article guidelines, performs web research, scrapes sources, and compiles comprehensive research files) and Brown for writing (which takes research and guidelines to generate, review, and edit articles with human-in-the-loop feedback).
These agents were designed from the start to work in sequence (Nova gathers the research, and Brown uses that research to write the article), but we've been running them separately. The beauty of MCP is that integration is now straightforward. Because both agents already expose their capabilities as MCP tools with clear descriptions, the central LLM can discover and use all available tools through the standardized protocol. We don't need custom integration code, adapters, or APIs. We simply connect to both servers and let the central LLM decide which tools to use and when.
We'll explore two different approaches for achieving this integration, each with distinct deployment patterns:
- Multi-Server MCP Client : A single client connects directly to multiple independent MCP servers simultaneously. The client aggregates all tools from both servers and presents them to the LLM.
- Composed MCP Server : We create a new server that internally connects to both Nova and Brown, exposing their combined capabilities as a single unified endpoint.
Both approaches implement the same central LLM orchestration pattern, which gives one LLM access to all tools from both agents. However, they differ in how the integration is configured and deployed. Let's examine each one.
Approach 1: Multi-Server MCP Client
Image 1: Architecture diagram depicting the Multi-Server MCP Client approach.
To teach this, we prepared a modified version of our MCP client in lessons/agents_integration/mcp_client. This MCP client can be run by specifying the MCP server’s configuration file, and it would automatically connect to all the MCP servers configured.
Let's look at the configuration file for this first approach (from lessons/25_integrate_agents/mcp_servers_config_http.json).
This configuration tells the MCP client how to connect to both servers via HTTP. Each server has a unique name and specifies the HTTP URL where the server is listening.
Now let's see how the MCP client code loads this configuration and connects to both servers (from lessons/agents_integration/mcp_client/src/client.py).
The key insight here is that Client(config) accepts a multi-server configuration. When you call list_tools(), list_resources(), or list_prompts(), the client automatically aggregates capabilities from all connected servers.
The client supports several interactive commands:
/tools,/resources,/prompts- List available capabilities;/prompt/<name>?arg=value- Load and execute an MCP prompt with optional arguments;/resource/<uri>- Read a resource's content;/model-thinking-switch- Toggle the LLM's thinking mode;/quit- Exit the client.
When a prompt is loaded via /prompt/<name>, the client retrieves its content from the MCP server and sends it to the LLM, which then executes the workflow using the available tools.
How Capabilities Are Named
When you have multiple servers, how do you distinguish which tool belongs to which server? FastMCP handles this automatically by prefixing the capability names with the server name from your configuration.
For example, with our configuration having nova-research-agent and brown-writing-workflow as server names:
- Nova's
extract_guidelines_urlstool becomesnova-research-agent_extract_guidelines_urls; - Brown's
generate_articletool becomesbrown-writing-workflow_generate_article; - Nova's
full_research_instructions_promptprompt becomesnova-research-agent_full_research_instructions_prompt; - Brown's
generate_article_promptprompt becomesbrown-writing-workflow_generate_article_prompt.
This naming convention makes it easy to identify which capability comes from which server, and ensures there are no naming collisions if both servers happen to expose capabilities with the same base name.
Running from the Terminal
When you run the multi-server client from your terminal, you will see it connect to both servers and aggregate their tools.
It outputs:
When you type /prompts, you'll see all prompts from both servers:
This demonstrates that the client successfully connected to both servers and aggregated their capabilities. Notice that each prompt is prefixed with its server name, making it clear which agent provides each capability.
Running the Complete Workflow: Two Prompts in Sequence
To run both agents together—first Nova for research, then Brown for article generation—you need to execute their prompts in sequence.
Step 1: Run Nova's Research Workflow
First, trigger Nova's research prompt:
The LLM receives the prompt instructions and responds:
You then provide the research directory path and let Nova complete the research workflow.
Step 2: Run Brown's Article Generation Workflow
Once Nova finishes, you trigger Brown's article generation prompt with the same directory:
The LLM receives Brown's prompt and calls the article generation tool:
Brown then generates the article using the research files Nova created.
The Limitation: No Single Unified Command
While this two-step approach works, it has a significant limitation: you must manually run two separate prompts in the correct order. There's no single command that orchestrates the entire research-to-article workflow.
You might think: "Why not just add a combined prompt to one of the servers?" But this creates a design problem:
- Adding to Nova? Nova is a research agent. It shouldn't need to know about Brown's article generation internals. This would create tight coupling between agents.
- Adding to Brown? Similarly, Brown is a writing workflow. It shouldn't need to know how Nova's research steps work.
- Writing a manual prompt? You could craft a long prompt that describes both workflows, but this duplicates the logic already encoded in each agent's prompts. It's error-prone and hard to maintain.
The clean solution is Approach 2 : create a composed MCP server that proxies both Nova and Brown. This composed server can own the combined workflow prompt without polluting either agent's codebase. The orchestration logic lives in the right place—the integration layer—not in the individual agents.
Approach 2: Composed MCP Server
The second approach is to create a new MCP server that combines the Nova and Brown servers. Instead of the client connecting to multiple servers, you create a single composed server that internally proxies requests to the underlying servers. This approach is useful when you want to package multiple agents as a single deployable unit, simplify the client-side configuration, or add a layer of coordination logic between agents.
Image 2: Architecture diagram illustrating the Composed MCP Server approach.
First, we define which servers to compose. The composed server will connect to Nova and Brown via HTTP (from lessons/25_integrate_agents/mcp_servers_config_http.json). This file is identical to the one used by the multi-server client, but its purpose is different: it tells the composed server which underlying servers to proxy to via HTTP.
Now let's see how to create a composed server using FastMCP's composition features (code from lessons/agents_integration/mcp_server/src/main.py).
Let's break down the key steps. We first create a FastMCP instance for our composed server. Then, for each server defined in our configuration, we create a Client that connects to it, use FastMCP.as_proxy(client) to create a proxy object, and finally use mcp.mount(proxy) to mount it without a prefix. When you mount a proxy without a prefix, all capabilities keep their original names. This means tools, resources, and prompts from both Nova and Brown are exposed exactly as they were defined in each agent [3], [4].
After mounting both servers, we add a new capability: the full_research_and_writing_workflow prompt. This is the key advantage of the composed server approach—we can add orchestration logic that coordinates both agents without modifying either agent's code. The @mcp.prompt() decorator registers a new MCP prompt that combines instructions for both Nova's research workflow and Brown's article generation. When a user triggers this prompt, the LLM receives comprehensive instructions to run both agents end-to-end in a single conversation.
Mounting creates a live, dynamic link between the parent server and the subserver. Instead of copying components, requests for tools are delegated to the mounted server at runtime. This means any changes to the subserver are immediately reflected in the parent [3].
Running the Client from the Terminal
To use the composed server, the client configuration is much simpler, pointing to a single endpoint (from lessons/25_integrate_agents/mcp_composed_server_config_http.json).
When you run the client with this configuration, it connects to the single composed server.
It outputs:
The output looks similar to the multi-server approach, but there's a key difference: the client is connecting to only one server (i.e., the composed server), which internally delegates to Nova and Brown. The composed server successfully exposes the same 14 tools, 4 resources, and 5 prompts. This demonstrates how composition provides the same capabilities with a simpler client-side configuration.
Notice that:
- The composed server mounts both agents without prefixes , so capabilities keep their original names
- There are now 5 prompts (not 4). The composed server has added a new
full_research_and_writing_workflowprompt - The client sees a single unified interface with all capabilities from both agents plus the new orchestration prompt
This is the key advantage over the multi-server approach: with a composed server, you can add a single command that orchestrates both agents end-to-end.
Listing Prompts from the Composed Server
When you type /prompts, you'll see all 5 prompts available:
Notice that full_research_and_writing_workflow is new —it's not from Nova or Brown, but added by the composed server itself. This prompt orchestrates both agents, instructing the LLM to:
- Run the complete Nova research workflow
- Then use Brown to generate an article from that research
This is the key advantage of the composed server approach: you can add coordination logic at the integration layer without modifying the individual agents.
Running the Complete Workflow with One Prompt
Now let's see the power of the composed server approach. Instead of running two separate prompts like in Approach 1, we can trigger the complete end-to-end workflow with a single command:
The LLM receives the combined prompt and immediately understands it needs to execute both phases:
The LLM continues executing the Nova research workflow tools, then seamlessly transitions to Brown's article generation. All with a single prompt command.
This addresses the limitation we identified in Approach 1, where you had to manually run two prompts in sequence. The composed server encapsulates the orchestration logic, providing a clean, single-command interface to the complete workflow.
Comparing the Approaches: When to Use Each
Both approaches achieve the same goal of integrating Nova and Brown, but they have different use cases and trade-offs.
| Aspect | Multi-Server Client | Composed Server |
|---|---|---|
| How it works | The client connects to multiple independent servers simultaneously. | A single server internally proxies to multiple underlying servers. |
| Orchestration prompts | No built-in way to add combined prompts—must run agent prompts sequentially. | Can add prompts that orchestrate multiple agents (e.g., full_research_and_writing_workflow). |
| Single command workflow | Requires running multiple prompts in sequence (Nova, then Brown). | Single full_research_and_writing_workflow prompt runs both agents end-to-end. |
| Client configuration | Client needs to know about all servers. | Single endpoint, simplifying client configuration. |
| Flexibility | Easy to add or remove servers dynamically by changing the client config. | Requires code changes to modify the server-side composition. |
| Use when | • Developing or testing agents • Quickly combining existing agents • Need flexibility to add/remove agents dynamically | • Deploying agents as a unified system • Need coordination logic between agents • Want simpler client experience • Building a product that packages multiple agents |
In general, the composed server pattern is better suited for production if you want to package the entire multi-agent system into a single, deployable unit with a clean, unified interface, which is ideal for integration into applications like Cursor or Claude Desktop.
Conclusion
In this lesson, we explored how to integrate the Nova research agent and Brown writing workflow using MCP. We learned:
- Multi-Server MCP Client : How to connect a single client to multiple MCP servers using FastMCP's multi-server configuration. Capabilities are prefixed with server names, and you run agent workflows by triggering their prompts sequentially.
- Composed MCP Server : How to use FastMCP's composition features (
as_proxy()andmount()) to create a unified server that proxies multiple agents. Crucially, composed servers can add new orchestration prompts—likefull_research_and_writing_workflow—that coordinate multiple agents without modifying their code. - Use Cases : When to use a multi-server client vs a composed server. The multi-server approach is better for flexibility and independence, while the composed server is better for having a unified interface, orchestration capabilities, and single-command workflows.
The key insight is that MCP makes agent integration straightforward. Because both Nova and Brown are already MCP servers, we don't need to write custom integration code. We simply leverage MCP's standardized protocol and FastMCP's composition features. The composed server approach goes further by allowing us to add orchestration logic at the integration layer—providing a single-command interface to run both agents end-to-end—without polluting either agent's codebase.
In the next lesson, you'll see how to use both agents from Cursor to work on an article end-to-end. In Part 3 of this course, we'll explore production deployment, including how to deploy these composed servers remotely, add monitoring with Opik, and implement security measures.
References
- Berkeley Function Calling Leaderboard. (n.d.). Gorilla. gorilla.cs.berkeley.edu/leaderboard.html
- Evaluating Tool Calling Capabilities in Large Language Models: A Literature Review. (n.d.). Quotient AI. blog.quotientai.co/evaluating-tool-calling-capabilities-in-larg...
- Server Composition. (n.d.). FastMCP. gofastmcp.com/servers/composition
- The FastMCP Server. (n.d.). FastMCP. gofastmcp.com/python-sdk/fastmcp-server-server
- Multi-Agent Orchestration with Reinforcement Learning. (n.d.). arXiv. arxiv.org/abs/2505.19591
- Designing Multi-Agent Intelligence. (n.d.). Microsoft Developer Blog. developer.microsoft.com/blog/designing-multi-agent-intelligence
- LLM Multi-Agent Reinforcement Learning. (n.d.). Xue Guang. xue-guang.com/post/llm-marl
- Multi-Agent Research System. (n.d.). Anthropic. anthropic.com/engineering/multi-agent-research-system
- Multi-Agent LLM Systems for Enterprises. (n.d.). Fiddler AI. fiddler.ai/articles/multi-agent-llm-systems-for-enterpr...
- Multi-Agent. (n.d.). OpenAI Agents Python. openai.github.io/openai-agents-python/multi_agent
- Architecture. (n.d.). Model Context Protocol. modelcontextprotocol.io/docs/learn/architecture
- Architectures for Multi-Agent Systems. (n.d.). Galileo AI. galileo.ai/blog/architectures-for-multi-agent-systems
- A Practical Guide to Building Agents. (n.d.). OpenAI. cdn.openai.com/business-guides-and-resources/a-practical-gu...
- LLM Agent Orchestration with LangChain and Granite. (n.d.). IBM Think. ibm.com/think/tutorials/llm-agent-orchestration-with...
- AI Agent Design Patterns. (n.d.). Microsoft Learn. learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agen...
- Model Context Protocol Specification. (2025, November 25). Model Context Protocol. modelcontextprotocol.io/specification/2025-11-25