My Two AI Agents Talk MCP to Each Other. There Is No Standalone Tool Server.

The default story for Model Context Protocol goes like this. You build a server. It exposes a catalog of tools — file access, a GitHub API, a database, a vector store. Then any MCP client — Claude Desktop, Cursor, your own agent — connects and uses them. One server, many clients. Most of the MCP content online is some version of this picture.
I built two AI agents that share a domain. When I needed them to talk to each other, that picture did not fit. What I ended up with: one of the agents speaks MCP from inside its own process. The other agent is the only client. There is no standalone tool server.
This post is about why that shape was the right one for the problem — and where it would be wrong.
What the Two Agents Do
I work as a software architect in automotive. Two of my agents are tools I use in different parts of my week.
The architecture agent is the primary one. I run it daily. It loads an AUTOSAR architecture model and lets me orchestrate the modelling tool’s plugins, query architecture artifacts — interfaces, components, traceability links — and run constraint-based component allocation against the model. It is a full LLM-driven CLI: tool registry, agentic loop, conversation manager, the works.
The requirements review agent is occasional. When a requirements review task arrives, this agent fetches the change task from the requirements management system and walks through a review checklist. It is also an LLM agent, with its own checklist logic and its own integrations.
There is one place where the work overlaps. When reviewing a requirement, knowing whether and how it is implemented in the architecture matters — which component allocates it, whether a referenced interface exists, whether the model and the requirement agree on shape. That information lives inside the architecture model, which the architecture agent already knows how to read.
The question was simple. How does the requirements review agent get to it?
Three Options

Option 1: a shared library. Extract the artifact-reading code into a package both agents import. Clean in theory. The reason it fails in practice: the architecture agent runs on Windows because it shells out to the modelling tool’s command-line interface to generate the artifacts in the first place. The requirements review agent often runs elsewhere. And even when they sit on the same machine, “import the same library” means each process carries its own loaded copy of the project — which loaded project, at which time, with which content hash — and the two copies drift the moment a plugin runs.
Option 2: a filesystem contract. The generated artifacts already live on disk: ARXML, XML, CSV, JSON. Give the consumer a directory path and let it read them directly. This is the option that almost worked. It dies on one specific point. The architecture agent has accumulated a real amount of logic over those files. Which artifact is authoritative for a given question — the aggregated table or the per-component XMLs? Which is fresher than the project file? How does a lookup dispatch to a JSON snapshot versus a CSV table? This logic is not incidental; it is most of what the agent is worth. A filesystem-level contract pushes all of it onto the consumer. Half of the architecture agent would get reimplemented inside the requirements review agent. Within a year, the two would slowly disagree about the answers.
Option 3: a function-level API. Don’t hand over files. Hand over find_interface(name) and get_traceability(req_id). Let the architecture agent answer, with its own routing, freshness checks, and parsing — and return a structured result with provenance. The consumer never touches an artifact directly.
Option 3 is what I chose. MCP is what I used to express it.
Why MCP, Specifically
A function-level API between two processes is a service. Once you accept that, the question is which protocol to put on the wire. HTTP would have worked. gRPC would have worked. A bespoke IPC scheme would have worked.
MCP fit for three reasons that have nothing to do with LLMs.
The shape is right. Typed tool schemas, structured arguments, structured results, a handshake to verify the server is the one you expect. The SDK gives me a server skeleton and a client out of the box. Stdio transport is a first-class case — which is exactly my deployment.
The same domain code already existed. The function find_interface is called from the architecture agent’s chat tool registry. The MCP server reuses it directly. Adding an MCP entry point was roughly thirty lines on top of code that already worked. There is no second implementation to keep in sync.
It surfaces the right metadata. Every response from my MCP server includes the loaded project’s path, name, content hash, and load timestamp. Every artifact response carries a freshness flag against the project file. That kind of provenance is what makes a cross-agent answer worth trusting in a safety-critical context. Most generic transports leave this to the consumer; MCP encourages putting it on the server side, where it belongs.
Why Not a Standalone MCP Server
The standard answer would have been to extract a dedicated artifact server, leaving both agents as clients. I considered it. Two things killed it.
The first is state ownership. The architecture agent already owns the canonical currently loaded project — its passport (path, name, content hash, load time) is part of how the architect works with it. A standalone server would have to acquire that ownership too, which means duplicating most of what the architecture agent already is. The chat agent and the artifact server become the same code with two entry points. At that point, the standalone server is not a separate service; it is a deployment shape.
The second is asymmetry. The architecture agent is the daily driver. The requirements review agent is the occasional consumer. The relationship is not symmetric, and the architecture should not pretend otherwise. The primary agent owning the data and exposing a narrow read-only surface to occasional consumers reflects how the work actually happens.
What This Costs
There are real costs. I want to name them plainly because the rest of the post sounds clean.
The MCP SDK is async-only, and the requirements review agent’s calling side is otherwise synchronous. The bridge is a dedicated background event loop on its own thread, with careful ownership of anyio cancel scopes and stderr captured through a temporary file because anyio needs a real file descriptor. It works, but it is the most subtle file in the codebase. If your need for function-level guarantees is not strong enough to justify that, the cost is real.
The MCP surface stays deliberately narrower than the chat surface. Read-only artifact queries are exposed; the agentic loop, model-modifying plugins, and free-form file reads are not. Keeping the two surfaces from drifting is a small but permanent maintenance task.
The two agents are tightly name-coupled. Rename find_interface and the consumer breaks. I own both repositories, so this is cheap to handle, but it is not free.
When the Standard Pattern Is the Right One
I want to be specific about this, because the post is not an argument against MCP servers in general.
Build it the standard way when:
- Clients are not under your control. You are publishing the server.
- Multiple tenants or sessions need to share the same server.
- The contract has to stay stable across versions because consumers cannot update in lockstep.
- The tools genuinely fit a generic catalog — filesystem, GitHub, a database — rather than a tight domain workflow.
None of that applies here. Two agents, both mine, one machine, a domain too specific to fit a generic catalog.
When those conditions do apply, building the standard way is right.
The Shape That Was Hiding in Plain Sight
The interesting part, looking back, is not the topology. It is what picking function-level over file-level forced.
Once the API is at the function level, the consumer cannot bypass the producer’s routing, freshness, and dispatch logic. There is one place where that logic lives. There is one place to fix it when it is wrong. There is one place that owns the contract.
If I had taken the filesystem option, I would today have two agents each parsing ARXML, each implementing capability routing, each computing staleness — and slowly disagreeing about the answers. The MCP channel buys me one source of truth at the cost of a few hundred lines of transport plumbing.
That trade-off is the actual lesson. MCP is the protocol I used to express it. The choice underneath is older than MCP and outlives it: when your domain has non-trivial logic over its artifacts, the API should be at the function level, not the file level. Hand over answers, not files.
If you are wiring up two agents that share a domain and find yourself reaching for the “fat MCP server, many clients” template by reflex — it is worth asking whether your problem actually looks like that, or whether you are about to push half of one agent’s logic into the other. Connect with me on LinkedIn if you are working through something similar.