Insights
AI Engineering Jan 2025

Building Multi-Agent Systems with MCP: Lessons from Three Enterprise Pilots

What we learned wiring up the Model Context Protocol across three enterprise multi-agent pilots - the orchestration patterns that held up in production and the ones that quietly fell apart.

The Problem

Every "build us an AI assistant" engagement we took on last year turned, within two weeks, into a multi-agent project. The client wanted one assistant. What they actually needed was six to twelve specialised agents - sales, support, finance, operations, compliance - sharing context and handing off cleanly.

Stitching that together without a standard wire protocol meant a lot of bespoke glue. Every new tool integration was a fresh adapter. Every new agent was a fresh prompt-and-retry harness. The Model Context Protocol (MCP) was meant to fix that. Mostly it did. Some of the failure modes were new.

What MCP Actually Is

MCP is an open standard for connecting LLM hosts to tools, data sources and prompts. It standardises what was previously a per-vendor mess - one client SDK, one server contract, JSON-RPC over a few transport choices.

For a single agent, MCP is straightforward: spin up an MCP server for each capability (CRM, document store, ticketing), point the agent at them, let the model decide which tools to call. For a multi-agent system, MCP only solves the agent-to-tool layer. Agent-to-agent coordination is still your problem.

The Multi-Agent Complication

Across the three pilots, the same three issues recurred.

Tool name collisions. When two agents both have access to a lookup_customer tool from different MCP servers, the model frequently picks the wrong one. Vendor-prefixed names help but make prompts brittle.

Context window economics. A naive design loads every relevant MCP server into every agent's context. With six agents and twelve servers each exposing eight tools, that's nearly 600 tool descriptors competing with the actual user task.

Handoff fidelity. When agent A finishes intake and passes to agent B for quoting, what exactly gets passed? The full conversation? A summary? Structured state? Each option breaks differently when the conversation gets long.

What We Built Across the Three Pilots

The pilots: an insurance broker (four agents handling intake, policy lookup, quoting and fraud checks), a regional logistics firm (six agents covering dispatch, routing, customer comms, billing, exceptions and reporting) and an internal IT helpdesk (three agents - triage, knowledge base, ticketing).

Three patterns held up across all of them.

1. One MCP server per bounded context, not per agent. We started with per-agent MCP servers and ended up duplicating data access logic. Switching to per-domain servers (one for "customer data", one for "policies", one for "billing") let multiple agents share the same well-tested integration.

2. Supervisor and workers, not a flat peer network. A flat network of agents that route to each other looks elegant on a whiteboard. In production, the supervisor pattern - one orchestrator agent that picks which worker to invoke - was dramatically easier to debug. Every handoff goes through one place, so observability has one chokepoint.

# The supervisor decides which worker handles the turn,
# then injects a constrained context for that worker.
async def supervisor_step(conversation, available_workers):
    plan = await orchestrator.plan(conversation, available_workers)
    worker = available_workers[plan.next_agent]
    handoff = build_handoff(
        goal=plan.goal,
        relevant_facts=conversation.extract_facts(plan.fact_keys),
        return_schema=plan.return_schema,
    )
    return await worker.run(handoff)

3. A persistent shared scratchpad keyed by conversation. Instead of relying on conversation history, agents read and write structured state to a shared store. Handoffs become "here are the keys you care about" rather than "here's the whole conversation, good luck."

What We'd Do Differently

If we were starting fresh, we would build observability before the first agent ships. We retro-fitted distributed tracing across MCP calls after the logistics pilot went live and reconstructing what happened in a bad conversation was painful for weeks.

We would also treat MCP server versioning as a first-class concern from day one. A breaking change to a single tool's schema took down two agents in one of the pilots because we had no contract test in CI.

Takeaway

MCP makes the wiring possible, but it doesn't make the architecture decisions for you. Multi-agent systems still need the boring fundamentals - clear boundaries, owned data and visibility into what's happening when something goes wrong. The protocol is the easy bit.