Skip to main content

MCP Calls the Control Plane; A2A Coordinates the Investigation

9 min readDora NodaDora Noda
Share
On this page

A deployment fails at 02:13. A coding agent can see the failed revision and the last 200 log lines. An operations agent knows how to distinguish a bad image from a bad node pool. Neither should receive the infrastructure-admin token just because they need to collaborate.

That is the useful boundary between two open protocols that are often put in the same “agents” bucket. The Model Context Protocol (MCP) gives an agent a typed way to discover and call a platform's tools. Agent2Agent (A2A) gives agents a way to discover one another and exchange stateful work. They are complementary, but neither is an authorization system for a production control plane.

Here is a concrete design for a self-hosted PaaS: use MCP for the narrow action at the platform boundary, use A2A for the handoff between specialists, and keep approval, policy, and the durable audit trail in one platform-owned service.

The deployment incident: two protocols, one approval

Start with an ordinary incident: a Git push produced deployment dep_847, which failed its readiness check. The coding agent should investigate the application. The operations agent should assess the cluster and recommend the least disruptive recovery. A human should authorize a rollback if one is needed.

The trust boundary is the design, not a diagram someone adds after the fact:

ComponentIt may receiveIt must not receive
Coding agentUser request, workspace/deploy IDs, read-only diagnostic resultsCluster-admin credential or another agent's token
Ops agentA2A task context, redacted diagnostic artifact, a capability to request approvalThe coding agent's user session or a general platform token
A2A transportSigned request metadata and task/status messagesControl-plane credentials or unredacted secret values
MCP control-plane serverShort-lived caller identity plus exact tool argumentsAuthority inferred from an A2A message alone
Policy and approval serviceRequested action, resource IDs, current policy, human decisionA model's unverified claim that approval happened
Audit storeImmutable correlation IDs and authorization decisionsSecrets, raw access tokens, or prompt text by default

The incident then follows this sequence:

text
coding agent --MCP get_deploy/read_logs--> control plane
coding agent --A2A investigate(dep_847, evidence)--> ops agent
ops agent --A2A status/artifact: "bad release; rollback candidate rev-41"--> coding agent
coding agent --MCP request_approval(rollback dep_847 -> rev-41)--> policy service
human approver --approve once--> policy service
coding agent --MCP rollback_deploy(approval_id)--> control plane
control plane --audit result + deployment state--> audit store

The core fact is easy to miss: the A2A handoff carries work, not authority. The coding agent asks the ops agent for an assessment. The ops agent produces an artifact and can request an approval. Only the platform policy service turns that approval into a short-lived, resource-bound permission for the MCP rollback_deploy call.

That matters because A2A tasks can be long-running and multi-turn. A2A defines Agent Cards for discovery, messages, task state, artifacts, streaming, and push updates; a task is an excellent envelope for an investigation that outlives one chat response. It deliberately does not make an agent's message equivalent to an administrator's authorization decision. Its current specification even provides an AUTH_REQUIRED task state for an agent to ask the client to fulfill a required authorization.

Give each protocol the job it is good at

The shortest design rule is: MCP crosses into the platform; A2A crosses between agents; the policy layer decides. The table makes that less slogan-like.

NeedOwnerConcrete behavior
Discover platform capabilitiesMCPtools/list exposes get_deploy, read_logs, request_rollback_approval, and rollback_deploy with schemas.
Read deployment factsMCPThe control plane checks workspace membership, then returns bounded, redacted data.
Ask a specialist to investigateA2AThe coding agent creates an investigate-deployment task with an evidence reference, not a credential.
Track a long-running handoffA2AStatus updates move from working to input/auth-required, completed, failed, or cancelled; the conclusion is an artifact.
Decide whether a mutation is allowedPlatform policy serviceIt evaluates user, agent, workspace, environment, deploy, change window, and approval rules on the server.
Execute the mutationMCProllback_deploy accepts an approval ID and exact target revision; server-side policy re-checks it.
Reconstruct what happenedPlatform audit serviceIt joins the A2A task and each MCP action to one incident correlation ID.

MCP's tools model fits the platform boundary because a server publishes a name, an input schema, and optionally an output schema, then a client calls tools/call. That gives a control plane a small contract to validate. It does not mean the model should freely select a dangerous tool: the MCP specification says tool annotations are untrusted unless the server is trusted and recommends a human able to deny sensitive invocations.

A2A fits the delegation boundary because its Agent Card describes an agent's identity, skills, interfaces, and security requirements. Its task object creates a handle for asynchronous progress and artifacts. That is materially different from exposing every internal tool of the operations agent to the coding agent—or, worse, letting both agents hold the same bearer token.

Make the MCP surface deliberately boring

The safest agent tool is not run_kubectl. It is a small operation whose input matches the business action an operator would already recognize. For the incident above, the tool catalog can be this narrow:

json
{
  "name": "read_deploy_logs",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "workspaceId": { "type": "string" },
      "deployId": { "type": "string" },
      "tailLines": { "type": "integer", "minimum": 1, "maximum": 200 }
    },
    "required": ["workspaceId", "deployId"]
  }
}

Its server verifies the audience, expiry, user-to-workspace relationship, and deploy ownership before it returns a capped, secret-redacted result. The agent receives facts, not a shell. A retry has no side effect.

The mutation must be more constrained still:

json
{
  "name": "rollback_deploy",
  "arguments": {
    "workspaceId": "wrk_12",
    "deployId": "dep_847",
    "targetRevision": "rev_41",
    "approvalId": "apr_902"
  }
}

The server—not the tool description—enforces all the interesting rules: apr_902 must be unexpired, bound to this workspace, deploy, target revision, and requested operation; the human must have the right role; and the deploy must still be eligible for rollback. The endpoint records an idempotency key and re-evaluates policy when it executes, so a delayed or replayed request cannot turn yesterday's approval into today's production change.

This approach also avoids an OAuth trap that matters in chains of services. MCP's authorization guidance requires token audience validation and explicitly forbids simply passing the token received by an MCP server to a downstream API. Give each component its own short-lived credential for its own audience. A2A correlation data can identify a task; it should never become a substitute for access control.

Preserve the thread without passing the keys

A2A lets the coding agent delegate the hard reasoning while retaining a durable reference to the work. The caller discovers the ops agent from its Agent Card, then sends a task like this:

json
{
  "message": {
    "role": "user",
    "parts": [{
      "kind": "data",
      "data": {
        "incidentId": "inc_3001",
        "deployId": "dep_847",
        "evidenceRef": "audit://inc_3001/diagnostics/1",
        "requestedOutcome": "diagnose and recommend a safe recovery"
      }
    }]
  }
}

The reference resolves through the control plane after authorization; it is not a signed URL to a bucket full of secrets. The ops agent may return a structured artifact with a diagnosis, confidence, evidence IDs, and proposed target revision. If it needs a privileged fact, it asks for a new scoped read through the policy layer. If it determines rollback is appropriate, it moves the task to AUTH_REQUIRED and states exactly what needs approval: “Rollback dep_847 to rev_41 in production.”

Every operation then writes the same correlation tuple:

FieldExample
Incident and A2A taskinc_3001, task_a2a_77
Request chainuser ID, coding-agent ID, ops-agent ID
Platform targetworkspace, environment, deploy, proposed revision
Policy decisionpolicy version, allowed/denied, reason, expiry
Human decisionapprover ID, timestamp, approval ID
MCP executiontool, validated arguments hash, result, idempotency key

This produces a trace a human can actually audit: who requested the action, which agent proposed it, what evidence it saw, who authorized it, and what the platform changed. It also avoids the false comfort of logging only the final API call while the multi-agent reasoning that triggered it disappears.

Design for the ways agents fail

Prompt injection, retries, and confused identities are normal inputs to an agent control plane, not edge cases. Four controls make the design fail closed:

  • An injected diagnostics result asks for a shell. The ops agent cannot call an unbounded shell tool; it can only request declared MCP operations. The MCP server validates schema and authorization server-side, and the host treats tool metadata from untrusted servers as untrusted.
  • A caller retries a handoff or mutation. A2A task/message IDs and control-plane idempotency keys make duplicates observable. The rollback endpoint returns the existing result for the same key rather than creating a second change.
  • An agent tries to read another tenant's task. Task retrieval checks authenticated access on every request; correlation IDs are opaque identifiers, not permissions.
  • A delegated agent asks for broader scope. It can create an approval request with the new, explicit resource set. It cannot extend the original approval or mint a broader token. The policy service denies by default.

Start with read-only tools: deploy state, bounded logs, metrics, and a dry-run recovery plan. Add A2A delegation next, but make its output advisory. Add an approval-bound mutation only after the audit record, role checks, expiry, and idempotency behavior have been tested with retries and revoked access. That sequence is slower than handing an agent a platform key, but it leaves an operator with a system they can reason about at 02:13.

Protocols move requests; the platform owns responsibility

MCP and A2A solve different interoperability problems. Use MCP to make platform actions explicit and validated. Use A2A to let specialized agents exchange asynchronous work and artifacts. Do not ask either protocol to carry a vague, transitive promise that one agent is “allowed to do whatever the other agent could do.”

For a self-hosted PaaS, this split is practical rather than theoretical: the control plane already knows the workspace, deployment, policy, and audit record. Keep the right to mutate infrastructure there. Agents can become first-class operators without becoming first-class holders of an all-powerful infrastructure credential.

Bex.co is an open-source, AI-native Render alternative for deploying Git repositories to HTTPS services on machines you control. Its agent-facing control plane is the kind of boundary where narrow actions, policy checks, and machine-readable state matter.

Sources

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide