← All posts

Meet MACP: One Invariant, Two Planes

One rule: if it is binding, it happens inside a session. What that rule says, what it leaves alone, and why it splits agent traffic into two planes.

By Ajit Koti

Last time I told a story about a 2:17 a.m. phone call. Four agents blocked a payment and nobody could say why, because the decision had never happened in one place. Then I replayed the same night with one change: the agents decided inside a session, and the whole decision could be handed over.

This post is about that one change. MACP has a lot of parts, but they all follow from a single sentence, and once you have that sentence the rest of the protocol reads as consequences.

The invariant

Here it is, as the spec states it:

Binding, convergent coordination MUST occur inside explicit, bounded Coordination Sessions.

Every word in that sentence is doing work.

  • Binding means the outcome counts. Someone will act on it: a payment is held, a deployment ships, a claim is paid.
  • Convergent means several parties end up at one answer. A single agent calling a tool is not convergence. Four agents agreeing on a hold is.
  • Explicit means the session is started on purpose, by a message that says "this is now a coordination session." There is no implicit coordination.
  • Bounded means it has a declared set of participants, a time limit and a defined way to end.

Notice what the sentence does not say. It does not tell your agents how to reason, which model to use, or how to talk to each other the rest of the time. It draws one boundary, around the moment where agreement becomes binding, and leaves everything else alone.

Two planes

If binding agreement can only happen in one place, everything else has to happen somewhere too. MACP splits agent traffic into two planes.

Two panels. On the left the ambient plane, where signals and ambient progress messages flow continuously and may not start, change or resolve a session. On the right the coordination plane, where a session opens with SessionStart, carries mode-defined messages, and ends in exactly one of resolved, expired or cancelled. Beneath both, the invariant: binding coordination happens only inside an explicit, bounded session. Signals stay ambient and non-binding; binding agreement lives only in a session.

The ambient plane carries Signals. These are the things agents tell each other all day: an anomaly was detected, a model was updated, capacity is running low, here is some new context. Signals can flow continuously, and anyone can observe them. What they cannot do is bind anyone. The spec is specific about this. A Signal must not start a session, change a session's state, change who is taking part, or resolve or expire a session. On the wire, a Signal carries an empty session ID and an empty mode, so it cannot be mistaken for part of a decision. Signals are also ephemeral: a runtime does not have to keep them in its replay history.

The coordination plane carries sessions. A session begins only when a SessionStart message is accepted. That message gives the session its shape: an intent, a declared list of participants, a mode (the kind of agreement being reached, such as a decision or a quorum), a time limit that must be above zero, and the versions of the mode, configuration and policy that govern it. From then on, the session accepts only the messages its mode allows, from the participants it declared, until it ends.

The night from the first post maps straight onto this. At 01:51 the growth model was updated. That is a Signal: worth knowing, and not binding on anyone. At 01:52 the transfer arrived and a session started. Everything that decided the transfer's fate happened inside it.

The rule that keeps the boundary honest

There is an obvious way to cheat. Agents could agree something over a side channel, then go through the motions in the session. MACP closes that door with a short rule: inside an active session, agents must not bypass the protocol to move a binding outcome forward. If something learned out of band should affect the result, it has to be reintroduced into the session as an accepted message before it can count.

This is what makes the session's record complete. You do not have to wonder whether some side conversation tipped the outcome, because a conforming participant cannot let it count unless it is in the record.

How a session ends

Every session starts OPEN, and every session ends in exactly one of three terminal states:

  • RESOLVED: the mode's terminal condition was met, and a Commitment records the outcome.
  • EXPIRED: the time limit ran out first.
  • CANCELLED: the initiator, or a role that policy allows, cancelled it.

Once a session is terminal, nothing more is accepted. Nothing reopens it, and nothing edits it. If a decision needs revising later, a new session makes a new commitment that points back at the old one. (There is also a fifth state, SUSPENDED, a pause that is not an ending. That comes up next time.)

So a session is not just a bag of messages. It has a birth, a roster, a rulebook, a deadline and exactly one ending. That is what lets you answer the questions from the first post. Who took part? Read the roster. What did each say? Read the accepted history, in order. Which rule applied? Its version was bound at the start. How did it end? There is one terminal state and, if it resolved, one commitment.

Why a boundary, not a framework

The manifesto that started MACP makes this point by analogy, and I think it is the right one. Operating systems do not tell programs what to do; they define how processes start and stop. TCP does not define what applications mean; it defines reliable transport. Container orchestration does not contain your business logic; it defines lifecycle and isolation. The abstractions that last tend to define boundaries without dictating behaviour.

MACP tries to be that kind of thing for agreement between agents. It is not an agent framework, and it does not compete with the protocols that connect agents to tools or to each other. You can keep LangGraph, the Claude Agent SDK or your own planner, and keep all your existing agent-to-agent traffic. What MACP adds is the place where the binding part happens. If you want the longer comparison, including when not to use MACP, it is on the positioning page.

Where things stand

I said I would be candid about status, so here it is. The spec is a draft. Its RFCs are all at draft versions, and nothing is declared wire-frozen yet. What is versioned and safe to pin today is the Protobuf packages. There is a reference runtime written in Rust, SDKs for Python and TypeScript, and a console that can run a session for you in the browser. It is maintained by one person, on a best-effort basis. I would rather you knew that before you build on it.

If you want to see the two planes for yourself, the architecture page goes one level deeper, and the core RFC is the normative text.

Next: what a session actually looks like, message by message, and the one state transition that surprises people.