In my previous two posts, I argued that MCP is more useful as a context distribution layer than as RPC, and that AGENTS.md should be a bootloader, not a knowledge base.
Both posts described context as something delivered in stages.
Startup context first.
Skill catalog next.
Domain rules when routed.
Authoritative documents when resolved.
Volatile state only when needed.
That description answered one question:
How many stages are there?
But it quietly avoided a second question:
On which side of the boundary does each stage run?
This is the question that actually matters when you build the server.
A stage is not just a point in time.
It is also a point in space.
Some things happen on the MCP server.
Some things happen on the client.
And the line between them is not arbitrary.
The boundary rule
When I built the MCP layer for XRefKit, I settled on one rule that decided almost everything else:
Only inert definitions cross the transport boundary.
An inert definition is executable guidance without execution.
It defines how work must be performed, but never performs the work itself.
It is not the result of doing the work.
Concretely, what crosses the boundary is things like:
- which domain rules apply
- which references are authoritative
- which assumptions are forbidden
- which unknowns must stop the work
- what evidence is required before closure
- what the closure condition is
What does not cross the boundary is the act of applying any of that.
The server can tell the client what counts as a valid review.
The server does not perform the review.
This splits the system into two planes.
Server-side plane: catalog and routing.
It knows what skills exist, what each skill requires, and how to map a work intent to a skill definition.
Client-side plane: execution.
It reads the actual code, walks the actual constraints, records the actual unknowns, and reaches the actual closure decision.
The server distributes judgment axes.
The client does the judging.
One request, traced
Abstractions hide where the boundary really is. So here is a single request, traced end to end.
A developer expresses an intent:
Validate this change against known constraints.
Step 1 — client asks the server to route the intent.
The client does not yet know which domain skill applies. It sends the intent to the catalog plane and asks for resolution.
This is semantic routing, not command routing. The client is not saying "run tool X." It is saying "this is the kind of work I am about to do."
Step 2 — server returns a skill definition. Inert.
The server resolves the intent to a domain skill and returns its definition:
- the constraint sources that are authoritative for this repository
- the document resolvers needed to load them
- the forbidden assumptions for this domain
- the unknown conditions that must halt the work
- the evidence required before closure
- the closure contract itself
Notice what did not come back: a validation result.
The server did not read the change. It did not check anything. It returned the rules of checking, not a check.
This is the whole point of "inert." The payload is a contract, not an outcome.
Step 3 — client executes against the definition.
Now the execution plane does the work the definition describes:
- it loads the authoritative constraints through the named resolvers
- it walks the actual diff against those constraints
- it records anything it cannot verify as an explicit unknown
- it checks the recorded state against the closure contract
All of this is local. None of it crossed the boundary.
Step 4 — closure is evaluated, not assumed.
The closure contract came from the server. But the act of deciding whether closure is allowed happens on the client, against real findings.
If an unresolved unknown remains, closure is blocked.
If a forbidden assumption was required to proceed, the work halts and escalates.
The server defined the stop condition.
The client encountered it.
Why this division is not just plumbing
It would be easy to read the two planes as an engineering convenience. Keep the heavy catalog on a server, keep the runtime light on the client. True, but not the real reason.
The real reason is that the boundary lines up with something deeper.
The server can only distribute judgment that has already been externalized.
A closure condition is distributable because someone wrote down what "done" means for this kind of work. A forbidden-assumption list is distributable because someone made the implicit rule explicit. An unknown-stop condition is distributable because someone decided, in advance, which gaps are not allowed to be papered over with fluent text.
These are externalized judgment axes. Inert. They cross.
But the act of judging against them at runtime — reading this specific code, weighing this specific risk, deciding whether this specific closure is honest — that does not become a transferable artifact just because the axes did.
So the boundary is really a test:
If a piece of judgment can be written as an inert definition, it belongs on the server.
If it cannot, it stays where the work happens.
The two-plane split is not a transport decision. It is a line drawn through judgment itself.
The question underneath: are we distributing the work, or the coordination?
Here is the part I could not write in the first two posts, because I had not located the boundary precisely enough.
A domain skill distributes closure conditions, stop rules, and evidence requirements. That looks like it distributes judgment.
But distributing the axis of a judgment is not the same as distributing the judgment.
When the server hands a client the closure contract, it is distributing the standardized part — the criteria that someone already coordinated, agreed on, and externalized. Applying those criteria to real findings is execution against a fixed axis. That part is delegable. It is, in the most precise sense, work that supports a decision.
What does not cross is the last step: deciding, with stakeholders, that this closure is acceptable in this situation — and owning that decision. No contract removes that. The moment a case falls outside the externalized axis, the work stops and returns to a human, because the coordination that would resolve it was never externalized in the first place.
By coordination, I do not mean communication. I mean the process of reaching new shared judgment that has not yet been externalized.
That is why the inert boundary matters beyond performance.
The MCP boundary ends up sitting exactly where externalized judgment ends and live coordination begins.
Everything that can be turned into a definition is distributable, and therefore delegable.
Everything that still requires coordination stays on the human side, and never crosses.
The two-plane design did not impose that line. It revealed it.
This is not really about MCP
I built this on MCP, but the boundary is not an MCP property.
This principle is not specific to MCP. Any system that distributes domain knowledge eventually discovers the same boundary.
Transport only distributes externalized judgment. Execution always remains local.
MCP made the boundary easy to see, because it gives named entry points and a clean transport. But a team sharing a wiki, a platform shipping policy bundles, a company writing runbooks — all of them hit the same wall the moment they try to distribute not just what is known but how to decide. The part that can be written down travels. The part that still needs a human to coordinate a new judgment does not.
The protocol changes how far the inert part can travel. It does not move the line.
Closing
Across these three posts the question kept narrowing.
First: is MCP RPC, or something else?
Then: should the bootloader hold knowledge, or point to it?
Now: where, physically, does judgment get distributed, and where does it refuse to?
The answer the implementation gave me is sharper than the one I started with.
A skill can distribute judgment axes.
It cannot distribute the act of judging.
The transport boundary, if you draw it honestly, is just the second sentence made mechanical.
Only inert definitions cross.
Everything that still needs coordination stays home.
Top comments (0)