BuildConnect
Bridge Between Protocols
Bridge A2A, ACP, MCP, and OpenAI calls through one kernel while preserving capability scope and signed receipts.
Prerequisites
Why Bridge Protocols
In a single-protocol deployment, traffic enters over MCP, the MCP server runs the tool call, and the kernel records one receipt. The capability chain and authority boundary remain local to that protocol.
Partner ecosystems may expose A2A skills, editor integrations may use ACP, and OpenAI-compatible models return tool_calls. When a call crosses a protocol boundary, avoid these failure modes:
- Two independent governed sessions. One kernel on the inbound edge, another on the outbound edge, no shared capability chain. Receipts are signed on both sides but they do not reference each other. You cannot prove, post-hoc, that the A2A request and the MCP call were the same logical act.
- An ad-hoc translator. A shim that rewrites envelopes between protocols without any policy or receipts of its own. The kernel sees two unrelated hops. Attenuation is not enforced. A capability can silently widen when it crosses the shim.
- Protocol lock-in. Pick one protocol for your whole estate, refuse to integrate anything else, and lose access to the tooling gravity of the other ecosystems.
Cross-protocol bridging is Chio's fourth option. A single signed route through the kernel, with a CrossProtocolCapabilityEnvelope that records the source protocol, the target protocol, and the attenuated scope, and a CrossProtocolTraceContext that links receipts from each hop in one tree.
How the Shared Cross-Protocol Crate Works
Shared types live in chio-cross-protocol. Each edge crate uses them for provenance, attenuation, and receipt lineage instead of defining a different representation.
The main types are:
TargetProtocolRegistry. Maps each knownDiscoveryProtocol(Native,Http,Mcp,A2a,Acp,OpenAi) to a registeredTargetProtocolExecutor. Resolution is driven by thex-chio-target-protocolschema extension on the tool definition, with a configured default for tools that don't declare one.plan_authoritative_route. The control-plane route planner. Given a source protocol, a requested target protocol, an optionalGovernedTransactionIntent, and a map ofRouteAvailabilityStatusvalues, it returns aRoutePlanningOutcomewith aRouteSelectionDecisionofSelect,Attenuate, orDeny. The outcome is captured as signedRouteSelectionEvidencethat rides in the receipt.CrossProtocolOrchestrator. The runtime. It extracts aCrossProtocolCapabilityReffrom the inbound envelope, builds the attenuatedCrossProtocolCapabilityEnvelope, callsplan_authoritative_route, hands aCrossProtocolTargetRequestto the selected executor, and returns anOrchestratedToolCallwhose receipt carriesauthority_path = "cross_protocol_orchestrator".
The kernel never sees two unrelated requests. It sees one evaluation whose route_selection metadata records the source protocol, the requested target, the selected target, and every candidate that was considered and rejected. If the route planner denies (for example, because a governed intent disallowed projected protocols and no native fallback existed), the receipt is a denial receipt with the reason populated; the target executor is never invoked.
The Four Bridges Today
Four edge crates currently use the shared cross-protocol types. The table distinguishes shipped behavior from planned work.
| Bridge | Crate | Direction | Status |
|---|---|---|---|
| A2A | chio-a2a-edge, chio-a2a-adapter | Inbound (A2A caller → native/MCP target) | Shipped. Supports claims on these endpoints: message/send, message/stream, task/get, task/cancel. |
| ACP | chio-acp-edge, chio-acp-proxy | Inbound (ACP session → native/MCP target) | Shipped. Supports these endpoints: tool/invoke, tool/stream, tool/resume, tool/cancel. |
| OpenAI | chio-openai-adapter | Inbound (OpenAI tool_calls → native target) | Shipped. Projects allowed calls as function_call_output items with a receipt_ref for chain-of-custody. |
| MCP | chio-mcp-edge | Inbound (MCP tools/call → native target) | Shipped for the MCP-to-native path. Projecting outward from MCP to A2A or ACP as a target protocol is planned; the shared crate already exposes the executor interface. |
HTTP is not a fifth bridge in its own right — it rides as a transport under the MCP and OpenAI edges. If you want to govern a REST API for agent consumption instead, see Bridge OpenAPI to MCP or Protect an API.
Shared types across protocol edges
chio-cross-protocol. To add a protocol edge, implement CapabilityBridge and optionally a TargetProtocolExecutor; you inherit the receipt envelope, the trace context, and the attenuation behavior from the shared crate.Wire a Bridge
At the crate level, wiring a cross-protocol bridge has three moves: register the target executors your deployment should support, declare which routes are currently available, and invoke the authoritative route planner before executing the hop.
The following conceptual Rust sketch uses the public API in chio-cross-protocol. Exact wiring in a given edge crate adds a per-protocol CapabilityBridge implementation — see chio-a2a-edge, chio-acp-edge, and chio-openai-adapter for the production shapes.
use std::collections::BTreeMap;
use chio_cross_protocol::{
CrossProtocolOrchestrator, DiscoveryProtocol, OpenAiTargetExecutor,
RouteAvailabilityStatus, TargetProtocolRegistry,
plan_authoritative_route,
};
use chio_kernel::ChioKernel;
fn build_orchestrator<'a>(kernel: &'a ChioKernel) -> CrossProtocolOrchestrator<'a> {
// 1. Register target executors for every non-native protocol this
// deployment is willing to dispatch into.
let openai = OpenAiTargetExecutor::default();
let registry = TargetProtocolRegistry::new(DiscoveryProtocol::Native)
.with_executor(&openai);
// .with_executor(&mcp_executor) // add MCP target executor
// .with_executor(&a2a_executor) // add A2A target executor
// 2. Declare which protocol families are available right now. The
// route planner refuses to select an unavailable target and will
// attenuate to the native fallback where allowed.
let orchestrator = CrossProtocolOrchestrator::new(kernel)
.with_registry(registry)
.with_protocol_availability(
DiscoveryProtocol::Native,
RouteAvailabilityStatus::available(),
)
.with_protocol_availability(
DiscoveryProtocol::OpenAi,
RouteAvailabilityStatus::available(),
)
.with_protocol_availability(
DiscoveryProtocol::A2a,
RouteAvailabilityStatus::unavailable("a2a executor not configured"),
);
orchestrator
}The planner is a standalone function you can call before dispatch to materialise the route decision and its evidence. Edge crates use this both inside the orchestrator and in tests:
let mut availability = BTreeMap::new();
availability.insert(DiscoveryProtocol::Native, RouteAvailabilityStatus::available());
availability.insert(DiscoveryProtocol::OpenAi, RouteAvailabilityStatus::available());
let planning = plan_authoritative_route(
"req-01HXYZ", // origin request id
DiscoveryProtocol::A2a, // source protocol
DiscoveryProtocol::OpenAi, // requested target
governed_intent.as_ref(), // optional GovernedTransactionIntent
®istry,
&availability,
)?;
match planning.evidence.decision {
RouteSelectionDecision::Select => { /* ship it */ },
RouteSelectionDecision::Attenuate => { /* fell back to native or alt target */ },
RouteSelectionDecision::Deny => { /* policy said no path is safe */ },
}The planner emits canonical JSON that is embedded in the receipt via route_selection_metadata. That is the piece that makes a denied route just as auditable as an allowed one — every candidate considered, along with its availability status and reason, is signed.
The CapabilityBridge trait supplies the protocol-specific glue: extracting a CrossProtocolCapabilityRef from the inbound envelope, injecting it back out when building the outbound envelope, and optionally surfacing protocol context such as A2A task ids or ACP session ids. You do not implement route planning or attenuation there; the shared crate handles them.
Receipt Lineage Across a Bridge
A cross-protocol receipt is the same ChioReceipt you already know, with two additions: the metadata carries the chio.cross-protocol-cap.v1 capability envelope, and the authority_path is the constant cross_protocol_orchestrator. That pair tells downstream verifiers that this decision flowed through the shared orchestrator instead of a direct edge call.
{
"version": "chio.receipt.v1",
"receipt_id": "01HXYZ...7K4",
"decision": "allow",
"capability_id": "cap_01HXYZ...4J8",
"authority_path": "cross_protocol_orchestrator",
"authoritative": true,
"metadata": {
"chio": {
"bridge": {
"bridgeId": "bridge_a2a_to_native_01",
"sourceProtocol": "a2a",
"targetProtocol": "native",
"terminalProtocol": "native",
"capabilityEnvelope": {
"schema": "chio.cross-protocol-cap.v1",
"targetProtocol": "native",
"attenuatedScope": {
"grants": [
{ "serverId": "srv-files", "toolName": "read_file" }
],
"resourceGrants": [],
"promptGrants": []
},
"bridgedAt": 1744820523,
"bridgeId": "bridge_a2a_to_native_01"
},
"trace": {
"traceId": "trc_01HXYZ...W2R",
"sessionFingerprint": "sha256:3f2c...",
"hops": [
{ "protocol": "a2a", "requestId": "a2a-msg-84", "bridgeId": "bridge_a2a_to_native_01", "timestamp": 1744820522 },
{ "protocol": "native", "requestId": "krn-req-517", "bridgeId": "bridge_a2a_to_native_01", "timestamp": 1744820523 }
]
}
},
"routeSelection": {
"routeSelectionId": "rsel_01HXYZ...Q9",
"decision": "select",
"sourceProtocol": "a2a",
"requestedTargetProtocol": "native",
"selectedTargetProtocol": "native",
"selectedProtocols": ["native"],
"candidates": [
{ "routeId": "a2a->native", "targetProtocol": "native", "available": true, "selectedProtocols": ["native"] },
{ "routeId": "a2a->openai", "targetProtocol": "open_ai", "available": false, "availabilityReason": "openai executor not configured", "selectedProtocols": ["open_ai"] }
]
}
}
},
"signature": "ed25519:..."
}Two things to notice. First, the attenuatedScope inside the envelope is a strict subset of the inbound capability's scope — only the one grant the target tool actually needed survives the bridge. Second, the rejected candidate is still in the receipt, along with why it was rejected. A denial at the bridge stores the rejected candidate and its reason in the signed receipt.
The trace.traceId is how you stitch hops together across a multi-step workflow. Query the receipt log by traceId and you get the tree of every receipt that participated in that logical act, regardless of which protocol each hop happened to speak.
Attenuation only narrows
attenuatedScope on the envelope must be a subset of the inbound capability's scope. The bridge does not manufacture authority; it only filters the parent scope down to the grant(s) the target tool requires. An envelope whose attenuated scope is not a subset of the parent is rejected by the kernel as Deny. Tokens can narrow across a bridge; they must never widen.Policy Patterns
Because the bridged call lands on the kernel as a normal tool invocation (with extra metadata), everything in your HushSpec policy still applies. But a few patterns are specific to cross-protocol routing and worth calling out.
- Narrow the allowed target protocols per inbound caller. A public A2A endpoint should probably only reach
Nativetargets. An internal ACP session might reachNativeorMcp, but neverOpenAi. Encode this as a policy rule on thechio.bridge.targetProtocolmetadata field. - Require prior attenuation. Refuse to cross a bridge unless the inbound subject's capability has already been attenuated and is not the root delegation. This keeps the blast radius of a compromised token from crossing a protocol boundary untouched. Combine with capability delegation.
- Disallow projected protocols. Set
disallow_projected_protocolson theGovernedTransactionIntentwhen the caller explicitly wants a native-only execution. The planner willAttenuatetoNativeif available andDenyotherwise. This is the kill-switch for scenarios where bridging is a known-bad shape (regulated workflows, evidence-only calls). - Bind to a BridgeFidelity gate. Tools are published to a target protocol only if the shared crate classifies their
BridgeFidelityasLosslessorAdapted. Write policy that refusesAdaptedbridges for workflows that cannot tolerate caveats (streamed output collapsing, cancellation semantics dropping, permission prompts disappearing). See Write a Policy for the rule shapes.
The mechanical rule is always the same: the kernel is the single decision point, and the cross-protocol envelope is evidence. A policy that doesn't want to permit a bridge simply denies the call as it would any other.
Fidelity and Truthful Publication
Not every tool maps cleanly into every protocol. A tool whose contract depends on streaming partial output cannot be faithfully projected onto an A2A compatibility interface that only returns collated terminal payloads. The shared crate captures this explicitly through BridgeFidelity and BridgeSemanticHints:
Lossless— the tool can be projected with no loss of semantics; published by default.Adapted { caveats }— published with caveats in the outward discovery metadata (for example: "streaming output is collected into a final payload").Unsupported { reason }— not published on that target protocol at all. The tool remains available through its source protocol and is omitted from the outward manifest for bridges that cannot represent it honestly.
The inputs to the fidelity decision come from the tool's x-chio-* schema extensions: x-chio-publish, x-chio-approval-required, x-chio-streaming, x-chio-cancellation, x-chio-partial-output, and x-chio-target-protocol. The shared crate infers defaults from the tool's latency hint and schema. Set these fields for tools with nontrivial lifecycle behavior.
One receipt lineage
tool_calls, or MCP tools/call, every hop reaches the same kernel, writes into the same traceId, and produces a receipt you can verify with the same tooling. Audit, billing, and incident response all look the same on the other side of the bridge. Audit, billing, and incident-response tools can use the same receipt-query pattern for each protocol.Next Steps
- Architecture · how the shared cross-protocol crate relates to the kernel and the per-protocol edges
- Capabilities and Delegation · the attenuation math the bridge enforces before a call reaches a target executor
- Wrap an MCP Server · an inbound pattern that composes with cross-protocol routing
- Wrap an ACP Server · how the ACP endpoint registers with the shared crate
- Govern OpenAI Tool Calls · the OpenAI inbound bridge, including the
function_call_outputprojection andreceipt_reftrail