Skip to content

Agent-to-Agent Communication

Every tool you connect to an agent the naive way, writing a custom Python wrapper around one specific API, is a wrapper you'll maintain forever, and one that only works inside the one framework you wrote it for. Switch from LangChain to a hand-rolled loop, or from Claude to Gemini, and every tool integration you built has to be rewritten. This gets expensive fast once an agent needs to reach a database, a filesystem, three SaaS APIs, and another agent someone else on your team built. This page covers the standard that fixes that, and the simpler pattern underneath it for when an agent just needs to hand work to another agent.

Gnome mascot

What you'll learn

  • Why hard-coded tool wrappers don't survive a framework or model change
  • The Model Context Protocol (MCP): a real, model-agnostic standard for connecting agents to tools
  • The simplest possible pattern for one agent to call another directly

The problem: every framework wants its own tool format

Function calling itself isn't the hard part, every major model API supports it, and the schema looks roughly the same everywhere. What's expensive is everything around it: authentication, credential handling, permission boundaries, and the fact that a tool wrapper written for one agent framework usually can't be dropped into another without rewriting it. A team that builds a "check the internal database" tool for their LangChain agent has to build it again, from scratch, the day someone wants the same capability from a Claude Code session or a custom loop like the one from Day 1.

The Model Context Protocol (MCP) , an open standard from Anthropic, exists specifically to break that coupling. It separates the tool from the agent framework calling it, the same way a REST API separates a service from whichever client happens to call it.

How it's structured

MCP splits the world into two roles. An MCP server wraps a real capability, a database, a filesystem, a SaaS API, and exposes it through a standard interface. An MCP client, embedded in your agent, talks to any MCP server using the same protocol regardless of what that server wraps. Write the server once, and any MCP-compatible agent (Claude Code, and a growing list of others) can use it without custom integration code.

Two things make this worth the extra layer instead of just writing a direct API wrapper: the client never sees the server's credentials (your database password lives on the server side, not inside the agent's context), and the server can enforce its own boundaries, reject any file read outside a specific directory, for instance, independent of what the agent asks for. The security boundary lives with the tool, not with whichever agent happens to be calling it that day.

Under the hood it's JSON-RPC over stdio (for a local server on your own machine) or SSE (for a remote one). You don't usually write this by hand, you configure a server and a client library handles the wire format, but it's worth seeing once, because it demystifies what "MCP-compatible" actually means:

json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "query_customer_database",
    "arguments": { "customer_id": 4821 }
  },
  "id": 1
}

The server executes the real query, keeping the connection string and credentials entirely on its own side, and replies with plain structured content the agent can read.

The simpler case: one agent calling another directly

MCP solves the tool interoperability problem. Sometimes what you actually need is smaller: one agent handing a subtask to another agent, in-process, with no protocol involved at all. If your research agent needs a second opinion from a specialized coding agent, you don't need MCP for that, you just call it like any other tool:

python
def delegate_to_coding_agent(task: str) -> str:
    """Tool the research agent can call to hand off implementation work."""
    return run_agent(task, max_steps=15)  # the same loop from Day 1, a second time

RESEARCH_AGENT_TOOLS = [
    read_file_tool,
    web_search_tool,
    {
        "name": "delegate_to_coding_agent",
        "description": "Hand an implementation task to a specialized coding agent",
        "input_schema": {
            "type": "object",
            "properties": {"task": {"type": "string"}},
            "required": ["task"],
        },
    },
]

From the calling agent's point of view, another agent is just a tool that happens to be smart. This is the seed of the pattern Day 3's multi-agent workflows builds on properly, with shared state, checkpoints, and a human-approval gate before anything consequential happens. Reach for MCP when you're standardizing access to a tool that multiple agents or frameworks need to share; reach for direct delegation when it's just your own agents handing work to each other inside one system.

What goes wrong

MistakeHow you notice itThe fix
Writing a custom wrapper for every tool, every frameworkThe same integration gets rebuilt from scratch every time you switch tools or frameworksUse MCP for anything more than one agent or framework needs to reach
Letting the agent hold credentials directlyAn API key or DB password shows up in the agent's context or logsKeep credentials on the MCP server side, the client should never see them
No boundary on what a delegated agent can doA "helper" agent takes an action outside the scope it was delegatedGive a delegated agent its own restricted tool list, not the full set the parent agent has
Infinite delegation loopsAgent A delegates to Agent B, which delegates back to Agent ACap delegation depth, same as you'd cap max_steps on a single agent loop

Confirm it worked

For an MCP server, the fastest check is whether a client can discover its tools without any hand-written glue code:

bash
# Using Claude Code's MCP inspector, or any MCP-compatible client
mcp-client list-tools --server ./my-database-server
# should list every tool the server exposes, with schemas, no custom code required

For direct agent delegation, confirm the handoff actually happens and comes back with real output, not a stub:

python
result = delegate_to_coding_agent("Write a function that validates an email address")
assert "def " in result  # the delegated agent actually produced code, not a placeholder

Next: Day 3 , Agent Skills, where multi-agent coordination and memory come together properly.