| Internet-Draft | MCP-AX | May 2026 |
| Abbott | Expires 5 November 2026 | [Page] |
This document specifies MCP-AX, an aggregation protocol for Model Context Protocol (MCP) servers. MCP-AX enables hierarchical composition of tool namespaces across heterogeneous networks of MCP servers, from cloud services to resource-constrained embedded devices. The protocol defines a master-subagent architecture directly inspired by the AgentX protocol (RFC 2741), adapted for the tool/resource model of MCP rather than the OID/MIB model of SNMP.¶
MCP-AX introduces recursive namespace delegation, capability-aware routing, transport bridging for constrained nodes, and irreversibility-gated tool dispatch suitable for autonomous and semi-autonomous AI agent systems.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 5 November 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
The Model Context Protocol (MCP) [MCP] defines a client-server architecture in which an AI model (the client) discovers and invokes tools, reads resources, and receives notifications from MCP servers. As deployment scales from single-server configurations to enterprise, IoT, and hybrid cloud/edge topologies, the need arises for hierarchical aggregation of MCP servers behind a unified namespace.¶
This problem is not new. SNMP encountered identical scaling constraints in the 1990s and produced AgentX [RFC2741], which defined a master agent / subagent architecture for delegating OID subtrees. The structural correspondence is direct:¶
| AgentX Concept | MCP-AX Concept |
|---|---|
| Master Agent | Root Aggregator |
| Subagent | Subserver |
| OID Subtree | Tool Namespace Prefix |
| MIB Registration | Tool Registration |
| ax.Register PDU | mcpax/register |
| ax.Get/GetNext | tools/call (routed) |
| MIB Walk | tools/list (recursive) |
| ax.Notify PDU | MCP notification (prefixed) |
| ax.Ping PDU | mcpax/heartbeat |
| Session ID | session_id |
This document specifies the MCP-AX protocol, adapting the AgentX model for MCP's tool/resource/notification primitives.¶
HTTP reverse proxies and API gateways (nginx, Envoy, Kong) route requests by URI path or header. MCP-AX differs in four respects that are not achievable through gateway configuration alone:¶
MCP-AX is a spiritual successor to AgentX [RFC2741], not a profile or extension of it. The wire protocol is MCP JSON-RPC, not AgentX PDUs. However, the registration semantics, namespace delegation model, and master/subagent lifecycle are directly derived from AgentX, which is cited as a normative architectural reference.¶
The term "agent" -- used independently in SNMP network management (1988), AgentX subagent delegation (1999), and contemporary AI agent systems (2024) -- names the same abstraction at three different layers of the stack. MCP-AX makes this recursion explicit.¶
MCP originated at Anthropic in November 2024. In December 2025, Anthropic, Block, and OpenAI contributed MCP and the Agent-to-Agent (A2A) protocol to the Agentic AI Foundation (AAIF), a Linux Foundation project. MCP-AX treats MCP as a stable external specification and cites it accordingly. Should MCP be submitted as an IETF RFC in future, the normative reference herein should be updated.¶
Several IETF Internet-Drafts have already referenced MCP as an external specification for network management use cases [I-D.zeng-mcp-network-mgmt] [I-D.zeng-mcp-network-measurement] [I-D.zeng-mcp-troubleshooting]. MCP-AX follows this precedent.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
MCP-AX defines a tree topology rooted at one or more root aggregators. The model client connects to the root aggregator and sees a flat, fully-qualified tool namespace.¶
Model Client
|
| (standard MCP)
|
Root Aggregator (R)
/ \
/ \
Regional Agg (A) Local Server (L)
/ \
/ \
Cloud (C) Edge Gateway (G)
/ \
/ \
MCU Stub (M1) MCU Stub (M2)
(UART/CBOR) (BLE/CBOR)
¶
Each non-leaf node is an aggregator. Each aggregator accepts registrations from downstream subservers, prepends its namespace prefix to registered tool names, exposes the merged namespace upstream, and routes tools/call requests to the owning subserver.¶
The model client issues standard MCP requests. It receives tools/list responses containing fully qualified (prefixed) tool names and capability annotations. It issues tools/call with the fully qualified name. The aggregation hierarchy is opaque to the client except as revealed by namespace structure and capability metadata.¶
Tool names in MCP-AX use dot-delimited hierarchical segments:¶
<root-segment>.<aggregator-segment>*.<local-name>¶
Example: infra.edge.stm32h7.dma2d_status¶
Segments MUST match the regular expression
[a-z0-9_-]{1,63}. The total fully qualified name MUST
NOT exceed 255 characters.¶
When a subserver registers with an aggregator, it provides its local tool list (via tools/list) and a requested namespace segment. The aggregator validates the segment for uniqueness among current registrations. First-registered-wins semantics apply, consistent with AgentX [RFC2741] Section 7.1.5.1. A conflict MUST result in rejection with error "namespace_conflict".¶
A subserver MAY include an "authority" field in its registration request, using DNS name syntax (e.g., "dns:edge01.factory.example.com"). When present, the (authority, segment) pair constitutes the ownership claim. An aggregator MAY reject re-registration of a segment by a different authority, even after the original session has expired. This provides stable namespace ownership across session churn, failover, and multi-root federation without requiring a global registry.¶
An authority claim is an assertion, not proof. Without verification, a rogue subserver connecting after a reboot can claim an authority string it does not own. For namespaces containing mutable or irreversible-mutable tools, aggregators SHOULD require cryptographic proof of authority: a signed JWT bearing the authority DNS name as subject, a client certificate whose SAN matches the claimed authority, or equivalent. Aggregators MAY accept unverified authority claims for read-only or development namespaces where the risk of impersonation is acceptably low.¶
A subserver MUST NOT register tools with names containing the dot separator. Hierarchical depth is achieved only through recursive aggregation, not through subserver self-prefixing. This prevents namespace spoofing: the aggregator is the sole authority for prefix assignment.¶
Upon receiving tools/list from upstream (or from the model client), the aggregator returns its cached merged namespace if valid. Otherwise, it issues tools/list to each registered subserver, prefixes results, merges, caches (RECOMMENDED TTL: 60 seconds), and returns.¶
Upon receiving tools/call for a fully qualified tool name, the aggregator looks up the tool in its routing table, advances the route cursor (Section 7.2), forwards the request to the downstream session, and returns the response. If the tool name is not found, the aggregator MUST return error code -32601 (method not found).¶
An aggregator MAY itself register as a subserver of a parent aggregator, presenting its merged namespace for further prefixing. There is no protocol-imposed depth limit; implementations SHOULD support at least 8 levels.¶
Implementations MUST detect and reject registration cycles. When an aggregator registers as a subserver of a parent, it MUST include its own aggregator_id and the aggregator_ids of all its downstream subservers in the registration request's "x-mcpax-subtree-ids" field. The parent MUST reject the registration if its own aggregator_id appears in that set. This provides cycle detection at registration time with no runtime overhead on tool dispatch.¶
{
"jsonrpc": "2.0",
"method": "mcpax/register",
"id": 1,
"params": {
"subserver_id": "550e8400-e29b-41d4-a716-446655440000",
"segment": "stm32h7",
"authority": "dns:edge01.factory.example.com",
"capabilities": {
"tools": true,
"resources": false,
"notifications": true
},
"heartbeat_interval_ms": 5000,
"transport_class": "uart_cbor",
"version": "2026-05-01"
}
}
¶
The "subserver_id" field is a stable UUID that persists across sessions and reboots. The "authority" field is OPTIONAL; see Section 4.2. Together with the aggregator's own identity, these fields enable deterministic split-brain detection (Section 13.4).¶
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "registered",
"assigned_segment": "stm32h7",
"session_id": "a3f7c...",
"heartbeat_deadline_ms": 15000,
"budget": {
"max_calls_per_minute": 60,
"max_mutable_calls_per_session": 10
}
}
}
¶
If heartbeat_interval_ms is non-zero, the subserver MUST send "mcpax/heartbeat" within each interval. Three consecutive missed heartbeats trigger automatic deregistration. A subserver may also send "mcpax/deregister" explicitly. Upon deregistration, the aggregator MUST remove all tools from that subserver's namespace within one heartbeat interval and re-advertise upstream.¶
These semantics mirror AgentX session timeout (Section 7.1.1 of [RFC2741]).¶
{
"infra.edge.stm32h7.dma2d_status": {
"downstream_session": "a3f7c...",
"downstream_name": "dma2d_status",
"capability": { ... },
"registered_at": "2026-05-01T12:00:00Z"
}
}
¶
Routing uses a cursor model rather than string manipulation. Each tools/call request carries an "x-mcpax-route" array containing the full sequence of namespace segments from root to leaf, and an "x-mcpax-cursor" integer indicating the current position in that array. Each aggregator increments the cursor by one and forwards the request downstream. The downstream server uses the segment at the cursor position to identify itself and dispatches to the tool named by the final segment.¶
Example for tool "infra.edge.stm32h7.dma2d_status":¶
"x-mcpax-route": ["infra", "edge", "stm32h7", "dma2d_status"], "x-mcpax-cursor": 0¶
Root aggregator matches "infra" at cursor 0, increments to 1, forwards. Regional aggregator matches "edge" at cursor 1, increments to 2, forwards. Gateway matches "stm32h7" at cursor 2, increments to 3, dispatches tool "dma2d_status".¶
This avoids string parsing errors, supports non-string namespace encodings in future extensions (integer IDs, hashes), and preserves the full route for diagnostics at every hop.¶
The response is forwarded upstream without modification to the result field. The aggregator MAY add "x-mcpax-latency-ms" to response metadata.¶
The aggregator SHOULD set downstream request timeouts based on the capability annotation's latency_class:¶
| latency_class | Timeout |
|---|---|
| realtime | 500 ms |
| fast | 5 s |
| standard | 30 s |
| slow | 120 s |
| batch | caller-managed |
A gateway translates between MCP's native transport (HTTP+SSE or stdio) and constrained transports used by embedded stubs. Defined bridged transport classes:¶
The gateway maintains a bidirectional mapping between MCP JSON-RPC and a compact CBOR [RFC7049] representation. Tool names are replaced with integer IDs assigned at registration time. JSON-RPC envelope fields "jsonrpc", "method", "id" map to CBOR map keys 0, 1, 2 respectively. Schema validation occurs at the gateway, not on the stub.¶
The gateway MUST strip all "x-mcpax-*" metadata (route array, cursor, hop count, latency annotations) before translating a request to the stub transport. A constrained stub receives only its integer tool ID and the CBOR-encoded arguments. Routing metadata is a gateway concern; it MUST NOT leak onto constrained transports where every byte has a cost.¶
The combination of integer tool IDs, typed argument schemas, and gateway-side validation constitutes a minimal interface description for constrained environments -- functionally equivalent to the subset of IDL semantics required for RPC dispatch, without requiring a separate schema language or code generator on the stub.¶
An MCP-AX stub MUST NOT be required to parse JSON, implement HTTP or SSE, manage sessions, perform authentication, or store fully qualified tool names. These responsibilities are fully delegated to the gateway. Two stub profiles are defined:¶
Profile A target resource budget: effectively zero dynamic RAM for protocol state; the command table is ROM-resident and the gateway handles everything else. Profile B target: fewer than 2 KB flash code, fewer than 256 bytes RAM for request/response buffers. See Appendix "Appendix B: Embedded Stub Reference Profiles" for reference descriptor structures for both profiles.¶
{
"latency_class": "realtime|fast|standard|slow|batch",
"consistency": "strong|eventual|best_effort",
"mutable": boolean,
"reversible": boolean,
"idempotent": boolean,
"transport": "native|uart_cbor|ble_cbor|...",
"auth_scope": "read|write|admin",
"cost_class": "free|metered|expensive",
"availability": "always|scheduled|best_effort|degraded",
"schema_version": "<semver>"
}
¶
The "consistency" field indicates the data consistency model of the tool's backing state. Agents SHOULD use this to determine whether parallel invocations across tools in the same subtree may observe stale state, and whether retry after failure requires read-before-write verification.¶
The "availability" value "degraded" indicates that the tool remains listed but may return structured error responses for some or all invocations. This supports partial-failure semantics required by industrial control and observability pipelines where binary present/absent is insufficient. See Section 13.2.¶
The aggregator MUST propagate capability annotations upstream without modification. It MUST add "x-mcpax-hops" indicating the aggregation depth. It MUST adjust "latency_class" upward if the aggregation path introduces latency that changes the effective class; it MUST NOT adjust latency_class downward.¶
Each aggregation boundary is an authentication boundary. Credentials MUST NOT be forwarded across aggregation hops. This is the critical lesson from SNMP's community string model [RFC3414]: transitive credential forwarding violates least privilege. MCP-AX mandates independent authentication at each hop, with tokens scoped to the immediate downstream session per [RFC8707].¶
An aggregator MUST scope the authorization context per registered subserver. A subserver registered with auth_scope "read" MUST NOT receive requests that imply "write" or "admin" operations.¶
The prohibition on subserver self-prefixing (Section 4.3) prevents a malicious subserver from registering tools that appear to belong to a different subtree. The aggregator is the sole authority for prefix assignment.¶
Aggregator-to-aggregator links MUST use TLS 1.3 or equivalent. Gateway-to-stub links (UART, SPI, BLE) operate in physically constrained environments where TLS may be infeasible. Gateways MUST treat stub-provided data as untrusted and validate all inputs against the registered schema before forwarding upstream.¶
When the model client is an autonomous AI agent, tool invocations may have real-world consequences that are difficult or impossible to reverse. The aggregation hierarchy is the natural enforcement point for safety policy: the aggregator has visibility into the blast radius of downstream operations that individual leaf servers lack.¶
Tools with "reversible": false and "mutable": true are classified as irreversible-mutable and MUST be flagged with "x-mcpax-safety": "irreversible_mutable" in the namespace.¶
An aggregator operating in "gated" mode MUST intercept tools/call requests targeting irreversible-mutable tools, return a "confirmation_required" response to the client (including tool name, arguments, capability annotation, and route), and await "mcpax/confirm" before dispatching downstream. Unconfirmed requests expire after a configurable timeout (default: 300 seconds).¶
The "mcpax/confirm" request MUST include a "proof" field containing a cryptographic token obtained from an authority external to the requesting model client. Acceptable proof types include: a signature from a human operator's key pair, a signed nonce from an external policy engine, or an approval token from an out-of-band authorization service. The aggregator MUST validate the proof against a configured trust anchor before dispatching the gated request.¶
Without this requirement, an autonomous model client can trivially self-confirm by issuing "mcpax/confirm" immediately after receiving "confirmation_required", rendering the gate ineffective. The proof field ensures that confirmation requires a cryptographic assertion that the model client cannot manufacture from its own context.¶
Safety Gate Monotonicity: once a request is classified as requiring confirmation at any hop in the aggregation hierarchy, all upstream hops MUST preserve that requirement. No aggregator or client MAY remove or downgrade a gate introduced by a downstream aggregator. Any aggregator MAY escalate an ungated request to gated; none MAY de-escalate. This ensures that the most conservative policy in the path governs.¶
Budget enforcement (max_calls_per_minute, max_mutable_calls_per_session, max_cost_units_per_session) is per-session, declared in the registration response, and non-negotiable. A subserver exceeding its budget MUST receive error -32003 ("budget_exceeded").¶
Subserver notifications are prefixed with the originating namespace segment and forwarded upstream. The aggregator MUST NOT reorder notifications from a single subserver.¶
The aggregator MUST implement per-subserver rate limiting (default: 100/second). If exceeded, the aggregator buffers up to a configurable depth (default: 1000), then drops oldest notifications, increments a "notifications_dropped" counter (exposed as a tool on the aggregator), and emits a synthetic "notification_overflow" notification upstream. This directly addresses the SNMP trap storm problem [RFC3413].¶
Notifications MAY include "x-mcpax-event-id" (UUID) and "x-mcpax-causal-parent" (UUID or null) fields. These do not impose cross-subserver ordering but enable downstream consumers to reconstruct causal chains when notifications from multiple subservers relate to the same triggering event. Aggregators MUST forward these fields without modification if present. Aggregators MUST NOT hold causal parent notifications in state or attempt to correlate them; they are opaque pass-through values. Causal reconstruction is a consumer concern, not an aggregator concern.¶
On heartbeat timeout or connection loss, the aggregator emits "subserver_lost" upstream. On reconnection, tools are re-added. The aggregator MUST NOT cache tool definitions across sessions.¶
Rather than immediately removing all tools from a failed subserver's namespace, an aggregator MAY transition those tools to "availability": "degraded". In degraded mode, tools remain listed in tools/list responses but tools/call requests return a structured error:¶
{
"code": -32002,
"message": "tool_degraded",
"data": {
"reason": "subserver_unreachable",
"since": "2026-05-01T12:05:00Z",
"retry_after_ms": 5000
}
}
¶
The aggregator MUST remove degraded tools entirely after a configurable grace period (RECOMMENDED: 5 minutes) if the subserver does not recover. Degraded-mode semantics are essential for industrial control and observability pipelines where abrupt namespace changes can trigger cascading failures in upstream consumers.¶
The parent detects heartbeat loss and removes the entire subtree. Subservers MAY reconnect to a configured backup aggregator. Convergence time after failure MUST be less than three heartbeat intervals across the affected subtree.¶
Each subserver carries a stable "subserver_id" (UUID) in its registration request. Each aggregator maintains its own "aggregator_id" (UUID). An aggregator MUST reject a registration from a subserver_id that is already registered with a different aggregator_id in the same tree.¶
For cloud deployments, detection SHOULD use a shared registration store (e.g., DynamoDB, etcd) keyed by subserver_id. For edge deployments where shared state is unavailable, the aggregator SHOULD query the subserver for its current parent_id and reject if it differs. This provides deterministic behavior without mandating a specific distributed consensus system.¶
This document requests the following registrations:¶
| AgentX Concept | MCP-AX Concept |
|---|---|
| Master Agent | Root Aggregator |
| Subagent | Subserver |
| OID Subtree | Namespace Prefix |
| MIB Registration | Tool Registration |
| ax.Register PDU | mcpax/register |
| ax.Unregister PDU | mcpax/deregister |
| ax.Get/GetNext/GetBulk | tools/call (routed) |
| ax.Notify PDU | MCP notification (prefixed) |
| ax.Ping PDU | mcpax/heartbeat |
| ax.Response PDU | JSON-RPC response |
| Session ID | session_id |
| Context | Namespace segment |
| sysUpTime | registered_at timestamp |
| AgentX socket (unix/tcp) | HTTP+SSE / stdio / bridged |
Key divergences from AgentX:¶
Profile A targets devices with no self-description capability, including legacy 8-bit microcontrollers (8051-class, 6800-class). The gateway owns all tool schemas and name mappings. The device exposes only numeric command IDs over a minimal frame protocol.¶
Resource budget: effectively zero dynamic RAM for protocol state. The command dispatch table is ROM-resident. The gateway configuration file defines the mapping from command IDs to MCP-AX tool names, argument schemas, and capability annotations.¶
Reference wire frame (gateway-to-stub and stub-to-gateway):¶
+-----+-----+------+---------+-----+---------+-----+
| SOF | LEN | TYPE | TOOL_ID | SEQ | PAYLOAD | CRC |
| 1B | 1-2B| 1B | 1B | 1B | NB | 1-2B|
+-----+-----+------+---------+-----+---------+-----+
TYPE: 0x01 = call request
0x02 = call response
0x03 = registration (Profile B only)
0x04 = heartbeat
PAYLOAD: fixed structs, TLV, or raw bytes.
Interpretation is defined in gateway config.
¶
Profile A devices need not use CBOR. The gateway translates between the device's native byte layout and MCP-AX JSON-RPC. The stub's only contract is: receive (TOOL_ID, PAYLOAD), execute, return (SEQ, PAYLOAD, status byte).¶
<CODE BEGINS>
/* Profile A: ROM command table -- no CBOR, no schemas on device */
typedef struct {
uint8_t cmd_id;
uint8_t req_len; /* fixed request payload length */
uint8_t resp_len; /* fixed response payload length */
int (*handler)(const uint8_t *req, uint8_t *resp);
} mcpax_cmd_t;
static const mcpax_cmd_t commands[] = {
{ 0x01, 0, 4, read_sensor_handler },
{ 0x02, 1, 0, set_led_handler },
};
<CODE ENDS>¶
Profile B targets 16/32-bit microcontrollers (Cortex-M0+, AVR, MSP430) that can announce tool IDs and compact argument schemas at boot via CBOR registration frames.¶
Resource budget: fewer than 2 KB flash code, fewer than 512 bytes ROM for schema tables, fewer than 256 bytes RAM for request/response buffers. Transport: UART at 9600 baud or higher, COBS framing. Encoding: CBOR [RFC7049], map-only payloads.¶
<CODE BEGINS>
/* Profile B: self-describing tool table with CBOR type hints */
typedef struct {
uint8_t tool_id;
uint8_t arg_count;
uint8_t arg_types[4]; /* CBOR major types */
uint8_t ret_type;
int (*handler)(const uint8_t *args, uint8_t *resp,
uint16_t *resp_len);
} mcpax_tool_t;
static const mcpax_tool_t tools[] = {
{ 0x01, 0, {}, CBOR_MAP, dma2d_status_handler },
{ 0x02, 1, {CBOR_UINT}, CBOR_MAP, set_led_handler },
};
<CODE ENDS>¶
Profile A:¶
Profile B:¶
For both profiles, the gateway handles all protocol complexity:¶
Model Client
|
Root Aggregator
/ | \
Google Jira Salesforce
Drive Server Server
Namespace: google_drive.*, jira.*, salesforce.*
¶
Model Client
|
Cloud Aggregator (AWS)
|
Edge Aggregator (Jetson)
/ | \
PLC Sensor Actuator
(Modbus Array Controller
gateway) (BLE) (UART/CBOR)
Irreversible-mutable actuator tools:
confirmation gate at cloud aggregator.
¶
Model Client
|
Global Aggregator
/ \
US Regional EU Regional
/ \ / \
US-E US-W EU-W EU-E
Namespace: global.us.east.*, global.us.west.*,
global.eu.west.*, global.eu.east.*
Model infers geographic locality from namespace.
¶
The authors gratefully acknowledge the designers of AgentX [RFC2741], whose architectural insights proved remarkably durable across a quarter-century gap and an entirely different application domain. The term "agent" has, it turns out, aged exceptionally well.¶
The irreversibility gate mechanism draws on safety-critical HMI design principles and the concept of useful friction at irreversibility boundaries in autonomous agent planning systems.¶