<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     category="exp"
     docName="draft-abbott-mcp-ax-00"
     ipr="trust200902"
     obsoletes=""
     updates=""
     submissionType="independent"
     xml:lang="en"
     tocInclude="true"
     tocDepth="3"
     symRefs="true"
     sortRefs="true"
     version="3">

  <front>
    <title abbrev="MCP-AX">
      MCP Aggregation Protocol (MCP-AX):
      Hierarchical Tool Namespace Delegation
      for Model Context Protocol Servers
    </title>

    <seriesInfo name="Internet-Draft" value="draft-abbott-mcp-ax-00"/>

    <author fullname="Ira Abbott" initials="I." surname="Abbott">
      <organization>SoftOboros</organization>
      <address>
        <postal>
          <city>Ottawa</city>
          <region>Ontario</region>
          <country>Canada</country>
        </postal>
        <email>ira@softoboros.com</email>
      </address>
    </author>

    <date year="2026" month="May" day="4"/>

    <area>Applications and Real-Time</area>
    <workgroup>Independent Submission</workgroup>

    <keyword>MCP</keyword>
    <keyword>Model Context Protocol</keyword>
    <keyword>AI agents</keyword>
    <keyword>tool aggregation</keyword>
    <keyword>AgentX</keyword>
    <keyword>namespace delegation</keyword>
    <keyword>embedded</keyword>
    <keyword>IoT</keyword>

    <abstract>
      <t>
        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.
      </t>
      <t>
        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.
      </t>
    </abstract>
  </front>

  <middle>

    <section anchor="introduction" numbered="true" toc="default">
      <name>Introduction</name>
      <t>
        The Model Context Protocol (MCP) <xref target="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.
      </t>
      <t>
        This problem is not new.  SNMP encountered identical scaling
        constraints in the 1990s and produced AgentX <xref target="RFC2741"/>,
        which defined a master agent / subagent architecture for delegating OID
        subtrees.  The structural correspondence is direct:
      </t>
      <table align="center">
        <thead>
          <tr><th>AgentX Concept</th><th>MCP-AX Concept</th></tr>
        </thead>
        <tbody>
          <tr><td>Master Agent</td><td>Root Aggregator</td></tr>
          <tr><td>Subagent</td><td>Subserver</td></tr>
          <tr><td>OID Subtree</td><td>Tool Namespace Prefix</td></tr>
          <tr><td>MIB Registration</td><td>Tool Registration</td></tr>
          <tr><td>ax.Register PDU</td><td>mcpax/register</td></tr>
          <tr><td>ax.Get/GetNext</td><td>tools/call (routed)</td></tr>
          <tr><td>MIB Walk</td><td>tools/list (recursive)</td></tr>
          <tr><td>ax.Notify PDU</td><td>MCP notification (prefixed)</td></tr>
          <tr><td>ax.Ping PDU</td><td>mcpax/heartbeat</td></tr>
          <tr><td>Session ID</td><td>session_id</td></tr>
        </tbody>
      </table>
      <t>
        This document specifies the MCP-AX protocol, adapting the AgentX
        model for MCP's tool/resource/notification primitives.
      </t>

      <section anchor="design-goals" numbered="true" toc="default">
        <name>Design Goals</name>
        <ol type="(%c)">
          <li>
            A model client MUST be able to interact with an MCP-AX aggregator
            using unmodified MCP protocol.  Aggregation is transparent to the
            client.
          </li>
          <li>
            Namespace delegation MUST be recursive: an aggregator MAY register
            as a subserver of a parent aggregator, forming an arbitrarily deep
            hierarchy.
          </li>
          <li>
            Resource-constrained devices, including those with RAM budgets
            below 4 KiB, MUST be supportable through a gateway aggregator
            by means of a bounded stub profile.  The protocol MUST NOT
            assume that participating nodes can parse JSON, manage sessions,
            perform authentication, negotiate schemas, or store fully
            qualified tool names.  These are gateway responsibilities, not
            leaf requirements.  The stub profile defines the minimum
            contract between a constrained device and its gateway; the
            gateway maps that contract into MCP-AX on behalf of the device.
          </li>
          <li>
            Tool dispatch MUST carry capability metadata sufficient for the
            client to reason about latency, mutability, and reversibility
            without knowledge of the routing topology.
          </li>
          <li>
            Security context MUST NOT bleed across aggregation boundaries.
            Each hop authenticates independently.
          </li>
        </ol>
      </section>

      <section anchor="not-api-gateway" numbered="true" toc="default">
        <name>Distinction from API Gateways</name>
        <t>
          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:
        </t>
        <ol type="(%c)">
          <li>
            Recursive namespace delegation: aggregation depth is unbounded
            and each hop is itself an MCP-AX participant, not a transparent
            proxy.
          </li>
          <li>
            Capability propagation: tool metadata (latency class,
            mutability, reversibility, consistency) is a first-class
            protocol element, not an out-of-band annotation.
          </li>
          <li>
            Safety semantics: irreversibility gates and agent budget
            enforcement operate on tool-call semantics, not HTTP verbs.
          </li>
          <li>
            Embedded transport bridging: sub-TCP transports (UART, BLE,
            CAN, SPI) with CBOR encoding are protocol-native, not bolted
            on via sidecar.
          </li>
        </ol>
      </section>

      <section anchor="agentx-relationship" numbered="true" toc="default">
        <name>Relationship to AgentX</name>
        <t>
          MCP-AX is a spiritual successor to AgentX <xref target="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.
        </t>
        <t>
          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.
        </t>
      </section>

      <section anchor="mcp-governance" numbered="true" toc="default">
        <name>MCP Governance Note</name>
        <t>
          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.
        </t>
        <t>
          Several IETF Internet-Drafts have already referenced MCP as an
          external specification for network management use cases
          <xref target="I-D.zeng-mcp-network-mgmt"/>
          <xref target="I-D.zeng-mcp-network-measurement"/>
          <xref target="I-D.zeng-mcp-troubleshooting"/>.
          MCP-AX follows this precedent.
        </t>
      </section>
    </section>

    <section anchor="terminology" numbered="true" toc="default">
      <name>Terminology</name>
      <t>
        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 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
        only when, they appear in all capitals, as shown here.
      </t>
      <dl newline="false" spacing="normal">
        <dt>Aggregator:</dt>
        <dd>
          An MCP server that exposes a unified tool namespace composed from
          one or more downstream MCP servers (subservers).  Analogous to an
          AgentX master agent.
        </dd>
        <dt>Subserver:</dt>
        <dd>
          An MCP server that registers its tools with an upstream aggregator.
          Analogous to an AgentX subagent.
        </dd>
        <dt>Leaf Server:</dt>
        <dd>
          A subserver that does not itself aggregate other servers.  Terminal
          node in the hierarchy.
        </dd>
        <dt>Gateway:</dt>
        <dd>
          An aggregator that additionally performs transport bridging (e.g.,
          UART/CBOR to HTTP/JSON) for constrained subservers.
        </dd>
        <dt>Namespace Prefix:</dt>
        <dd>
          A dot-delimited string prepended to tool names during registration
          to establish hierarchical ownership.  Analogous to an OID subtree
          in AgentX.
        </dd>
        <dt>Tool Route:</dt>
        <dd>
          The ordered list of aggregator segments from root to the leaf server
          that owns a tool.
        </dd>
        <dt>Capability Annotation:</dt>
        <dd>
          Metadata attached to a tool registration describing latency class,
          mutability, reversibility, and transport characteristics.
        </dd>
        <dt>Stub:</dt>
        <dd>
          A minimal MCP implementation on a constrained device, supporting
          only tools/list and tools/call over a compact transport encoding.
        </dd>
      </dl>
    </section>

    <section anchor="architecture" numbered="true" toc="default">
      <name>Architecture Overview</name>

      <section anchor="topology" numbered="true" toc="default">
        <name>Topology</name>
        <t>
          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.
        </t>
        <artwork align="center" name="" type="ascii-art"><![CDATA[
      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)
        ]]></artwork>
        <t>
          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.
        </t>
      </section>

      <section anchor="transparency" numbered="true" toc="default">
        <name>Transparency</name>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="namespace" numbered="true" toc="default">
      <name>Namespace Model</name>

      <section anchor="ns-syntax" numbered="true" toc="default">
        <name>Namespace Syntax</name>
        <t>
          Tool names in MCP-AX use dot-delimited hierarchical segments:
        </t>
        <artwork align="center"><![CDATA[
<root-segment>.<aggregator-segment>*.<local-name>
        ]]></artwork>
        <t>
          Example: <tt>infra.edge.stm32h7.dma2d_status</tt>
        </t>
        <t>
          Segments MUST match the regular expression
          <tt>[a-z0-9_-]{1,63}</tt>.  The total fully qualified name MUST
          NOT exceed 255 characters.
        </t>
      </section>

      <section anchor="ns-registration" numbered="true" toc="default">
        <name>Registration and Ownership</name>
        <t>
          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 <xref target="RFC2741"/> Section 7.1.5.1.  A conflict
          MUST result in rejection with error "namespace_conflict".
        </t>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
      </section>

      <section anchor="ns-isolation" numbered="true" toc="default">
        <name>Namespace Isolation</name>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="aggregator-behavior" numbered="true" toc="default">
      <name>Aggregator Behavior</name>

      <section anchor="tools-list" numbered="true" toc="default">
        <name>tools/list Handling</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="tools-call" numbered="true" toc="default">
        <name>tools/call Routing</name>
        <t>
          Upon receiving tools/call for a fully qualified tool name, the
          aggregator looks up the tool in its routing table, advances the
          route cursor (<xref target="request-transform"/>), 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).
        </t>
      </section>

      <section anchor="recursion" numbered="true" toc="default">
        <name>Recursive Aggregation</name>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="registration-protocol" numbered="true" toc="default">
      <name>Registration Protocol</name>

      <section anchor="reg-request" numbered="true" toc="default">
        <name>Registration Request</name>
        <sourcecode type="json"><![CDATA[
{
  "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"
  }
}
        ]]></sourcecode>
        <t>
          The "subserver_id" field is a stable UUID that persists across
          sessions and reboots.  The "authority" field is OPTIONAL; see
          <xref target="ns-registration"/>.  Together with the aggregator's
          own identity, these fields enable deterministic split-brain
          detection (<xref target="failure-splitbrain"/>).
        </t>
      </section>

      <section anchor="reg-response" numbered="true" toc="default">
        <name>Registration Response</name>
        <sourcecode type="json"><![CDATA[
{
  "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
    }
  }
}
        ]]></sourcecode>
      </section>

      <section anchor="reg-heartbeat" numbered="true" toc="default">
        <name>Heartbeat and Deregistration</name>
        <t>
          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.
        </t>
        <t>
          These semantics mirror AgentX session timeout (Section 7.1.1 of
          <xref target="RFC2741"/>).
        </t>
      </section>
    </section>

    <section anchor="dispatch" numbered="true" toc="default">
      <name>Tool Dispatch and Routing</name>

      <section anchor="routing-table" numbered="true" toc="default">
        <name>Routing Table</name>
        <sourcecode type="json"><![CDATA[
{
  "infra.edge.stm32h7.dma2d_status": {
    "downstream_session": "a3f7c...",
    "downstream_name":    "dma2d_status",
    "capability":         { ... },
    "registered_at":      "2026-05-01T12:00:00Z"
  }
}
        ]]></sourcecode>
      </section>

      <section anchor="request-transform" numbered="true" toc="default">
        <name>Request and Response Transformation</name>
        <t>
          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.
        </t>
        <t>
          Example for tool "infra.edge.stm32h7.dma2d_status":
        </t>
        <sourcecode type="json"><![CDATA[
"x-mcpax-route":  ["infra", "edge", "stm32h7", "dma2d_status"],
"x-mcpax-cursor": 0
        ]]></sourcecode>
        <t>
          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".
        </t>
        <t>
          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.
        </t>
        <t>
          The response is forwarded upstream without modification to the
          result field.  The aggregator MAY add "x-mcpax-latency-ms" to
          response metadata.
        </t>
      </section>

      <section anchor="timeout" numbered="true" toc="default">
        <name>Timeout Propagation</name>
        <t>
          The aggregator SHOULD set downstream request timeouts based on the
          capability annotation's latency_class:
        </t>
        <table align="center">
          <thead>
            <tr><th>latency_class</th><th>Timeout</th></tr>
          </thead>
          <tbody>
            <tr><td>realtime</td><td>500 ms</td></tr>
            <tr><td>fast</td><td>5 s</td></tr>
            <tr><td>standard</td><td>30 s</td></tr>
            <tr><td>slow</td><td>120 s</td></tr>
            <tr><td>batch</td><td>caller-managed</td></tr>
          </tbody>
        </table>
      </section>
    </section>

    <section anchor="transport" numbered="true" toc="default">
      <name>Transport Bridging</name>

      <section anchor="gateway-role" numbered="true" toc="default">
        <name>Gateway Role</name>
        <t>
          A gateway translates between MCP's native transport (HTTP+SSE or
          stdio) and constrained transports used by embedded stubs.  Defined
          bridged transport classes:
        </t>
        <ul>
          <li><tt>uart_cbor</tt>: UART with COBS framing, CBOR payload</li>
          <li><tt>ble_cbor</tt>: BLE GATT characteristics, CBOR payload</li>
          <li><tt>spi_cbor</tt>: SPI with length-prefix framing, CBOR payload</li>
          <li><tt>can_cbor</tt>: CAN bus with ISO-TP segmentation, CBOR payload</li>
          <li><tt>mqtt_json</tt>: MQTT topics, JSON payload</li>
        </ul>
      </section>

      <section anchor="cbor-mapping" numbered="true" toc="default">
        <name>CBOR-MCP Mapping</name>
        <t>
          The gateway maintains a bidirectional mapping between MCP JSON-RPC
          and a compact CBOR <xref target="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.
        </t>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
      </section>

      <section anchor="stub-requirements" numbered="true" toc="default">
        <name>Stub Requirements</name>
        <t>
          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:
        </t>
        <dl newline="false" spacing="normal">
          <dt>Profile A -- Table Stub:</dt>
          <dd>
            No self-description capability.  The gateway owns all tool
            schemas and maps integer command IDs to MCP-AX tool names
            from its own configuration.  The device exposes numeric
            command IDs only.  Suitable for devices with no dynamic
            discovery requirement, including legacy 8-bit
            microcontrollers.
          </dd>
          <dt>Profile B -- Self-Describing Stub:</dt>
          <dd>
            Announces tool IDs and compact schemas at boot or on gateway
            request via CBOR registration frames.  Suitable for
            Cortex-M0+, AVR, MSP430, and equivalent 16/32-bit devices.
          </dd>
        </dl>
        <t>
          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 <xref target="appendix-stub"/> for reference
          descriptor structures for both profiles.
        </t>
      </section>
    </section>

    <section anchor="capabilities" numbered="true" toc="default">
      <name>Capability Metadata</name>

      <section anchor="cap-schema" numbered="true" toc="default">
        <name>Capability Annotation Schema</name>
        <sourcecode type="json"><![CDATA[
{
  "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>"
}
        ]]></sourcecode>
        <t>
          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.
        </t>
        <t>
          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 <xref target="failure-degraded"/>.
        </t>
      </section>

      <section anchor="cap-obligations" numbered="true" toc="default">
        <name>Aggregator Annotation Obligations</name>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="security" numbered="true" toc="default">
      <name>Security Considerations</name>

      <section anchor="sec-per-hop" numbered="true" toc="default">
        <name>Per-Hop Authentication</name>
        <t>
          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
          <xref target="RFC3414"/>: transitive credential forwarding violates
          least privilege.  MCP-AX mandates independent authentication at
          each hop, with tokens scoped to the immediate downstream session
          per <xref target="RFC8707"/>.
        </t>
      </section>

      <section anchor="sec-scope" numbered="true" toc="default">
        <name>Scope Restriction</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="sec-spoofing" numbered="true" toc="default">
        <name>Namespace Spoofing Prevention</name>
        <t>
          The prohibition on subserver self-prefixing
          (<xref target="ns-isolation"/>) prevents a malicious subserver from
          registering tools that appear to belong to a different subtree.  The
          aggregator is the sole authority for prefix assignment.
        </t>
      </section>

      <section anchor="sec-transport" numbered="true" toc="default">
        <name>Transport Security</name>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="safety" numbered="true" toc="default">
      <name>Irreversibility and Safety Gates</name>

      <section anchor="safety-motivation" numbered="true" toc="default">
        <name>Motivation</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="safety-classification" numbered="true" toc="default">
        <name>Irreversibility Classification</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="safety-gate" numbered="true" toc="default">
        <name>Gate Enforcement</name>
        <t>
          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).
        </t>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
      </section>

      <section anchor="safety-budget" numbered="true" toc="default">
        <name>Agent Budget Propagation</name>
        <t>
          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").
        </t>
      </section>
    </section>

    <section anchor="notifications" numbered="true" toc="default">
      <name>Notification Aggregation</name>
      <t>
        Subserver notifications are prefixed with the originating namespace
        segment and forwarded upstream.  The aggregator MUST NOT reorder
        notifications from a single subserver.
      </t>
      <t>
        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
        <xref target="RFC3413"/>.
      </t>
      <t>
        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.
      </t>
    </section>

    <section anchor="failure" numbered="true" toc="default">
      <name>Failure Modes and Recovery</name>

      <section anchor="failure-subserver" numbered="true" toc="default">
        <name>Subserver Failure</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="failure-degraded" numbered="true" toc="default">
        <name>Degraded Mode</name>
        <t>
          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:
        </t>
        <sourcecode type="json"><![CDATA[
{
  "code": -32002,
  "message": "tool_degraded",
  "data": {
    "reason": "subserver_unreachable",
    "since": "2026-05-01T12:05:00Z",
    "retry_after_ms": 5000
  }
}
        ]]></sourcecode>
        <t>
          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.
        </t>
      </section>

      <section anchor="failure-aggregator" numbered="true" toc="default">
        <name>Aggregator Failure</name>
        <t>
          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.
        </t>
      </section>

      <section anchor="failure-splitbrain" numbered="true" toc="default">
        <name>Split-Brain Prevention</name>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
      </section>
    </section>

    <section anchor="iana" numbered="true" toc="default">
      <name>IANA Considerations</name>
      <t>
        This document requests the following registrations:
      </t>
      <ol type="(%c)">
        <li>
          MCP-AX method namespace "mcpax/*" in the MCP method registry
          (to be established by the Agentic AI Foundation or a future IETF
          working group).
        </li>
        <li>
          MCP-AX capability annotation keys "x-mcpax-*" in the MCP metadata
          registry (to be established).
        </li>
        <li>
          MCP-AX transport class identifiers in a new "MCP-AX Transport
          Classes" registry.
        </li>
      </ol>
    </section>

  </middle>

  <back>

    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author initials="S." surname="Bradner" fullname="S. Bradner"/>
            <date year="1997" month="March"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>

        <reference anchor="RFC2741">
          <front>
            <title>Agent Extensibility (AgentX) Protocol Version 1</title>
            <author initials="M." surname="Daniele"/>
            <author initials="B." surname="Wijnen"/>
            <author initials="M." surname="Ellison"/>
            <author initials="D." surname="Francisco"/>
            <date year="2000" month="January"/>
          </front>
          <seriesInfo name="RFC" value="2741"/>
          <seriesInfo name="DOI" value="10.17487/RFC2741"/>
        </reference>

        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/>
            <date year="2017" month="May"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>

        <reference anchor="RFC8707">
          <front>
            <title>Resource Indicators for OAuth 2.0</title>
            <author initials="B." surname="Campbell"/>
            <author initials="J." surname="Bradley"/>
            <date year="2020" month="February"/>
          </front>
          <seriesInfo name="RFC" value="8707"/>
          <seriesInfo name="DOI" value="10.17487/RFC8707"/>
        </reference>

        <reference anchor="MCP" target="https://modelcontextprotocol.io/specification/">
          <front>
            <title>Model Context Protocol Specification</title>
            <author>
              <organization>
                Agentic AI Foundation / Linux Foundation
              </organization>
            </author>
            <date year="2025" month="November"/>
          </front>
          <refcontent>
            Originally authored by D. Soria Parra and J. Spahr-Summers,
            Anthropic.  Governance transferred to the Agentic AI Foundation,
            a Linux Foundation project, December 2025.
          </refcontent>
        </reference>

      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="RFC3413">
          <front>
            <title>Simple Network Management Protocol (SNMP) Applications</title>
            <author initials="D." surname="Levi"/>
            <author initials="P." surname="Meyer"/>
            <author initials="B." surname="Stewart"/>
            <date year="2002" month="December"/>
          </front>
          <seriesInfo name="STD" value="62"/>
          <seriesInfo name="RFC" value="3413"/>
          <seriesInfo name="DOI" value="10.17487/RFC3413"/>
        </reference>

        <reference anchor="RFC3414">
          <front>
            <title>User-based Security Model (USM) for version 3 of the
            Simple Network Management Protocol (SNMPv3)</title>
            <author initials="U." surname="Blumenthal"/>
            <author initials="B." surname="Wijnen"/>
            <date year="2002" month="December"/>
          </front>
          <seriesInfo name="STD" value="62"/>
          <seriesInfo name="RFC" value="3414"/>
          <seriesInfo name="DOI" value="10.17487/RFC3414"/>
        </reference>

        <reference anchor="RFC7049">
          <front>
            <title>Concise Binary Object Representation (CBOR)</title>
            <author initials="C." surname="Bormann"/>
            <author initials="P." surname="Hoffman"/>
            <date year="2013" month="October"/>
          </front>
          <seriesInfo name="RFC" value="7049"/>
          <seriesInfo name="DOI" value="10.17487/RFC7049"/>
        </reference>

        <reference anchor="I-D.zeng-mcp-network-mgmt"
          target="https://www.ietf.org/archive/id/draft-zeng-mcp-network-mgmt-01.html">
          <front>
            <title>Model Context Protocol (MCP) Extensions for Network
            Equipment Management</title>
            <author surname="Zeng" initials="X." fullname="Zeng et al."/>
            <date year="2025" month="October"/>
          </front>
          <seriesInfo name="Internet-Draft"
            value="draft-zeng-mcp-network-mgmt-01"/>
        </reference>

        <reference anchor="I-D.zeng-mcp-network-measurement"
          target="https://datatracker.ietf.org/doc/html/draft-zeng-mcp-network-measurement-00">
          <front>
            <title>MCP-based Network Measurement Framework</title>
            <author surname="Zeng" initials="X." fullname="Zeng et al."/>
            <date year="2025" month="October"/>
          </front>
          <seriesInfo name="Internet-Draft"
            value="draft-zeng-mcp-network-measurement-00"/>
        </reference>

        <reference anchor="I-D.zeng-mcp-troubleshooting"
          target="https://www.ietf.org/archive/id/draft-zeng-mcp-troubleshooting-00.html">
          <front>
            <title>Using MCP for Intent-Based Network Troubleshooting
            Automation</title>
            <author surname="Zeng" initials="X." fullname="Zeng et al."/>
            <date year="2025" month="October"/>
          </front>
          <seriesInfo name="Internet-Draft"
            value="draft-zeng-mcp-troubleshooting-00"/>
        </reference>

      </references>
    </references>

    <section anchor="appendix-agentx" numbered="false" toc="default">
      <name>Appendix A: AgentX Full Correspondence Table</name>
      <table align="center">
        <thead>
          <tr><th>AgentX Concept</th><th>MCP-AX Concept</th></tr>
        </thead>
        <tbody>
          <tr><td>Master Agent</td><td>Root Aggregator</td></tr>
          <tr><td>Subagent</td><td>Subserver</td></tr>
          <tr><td>OID Subtree</td><td>Namespace Prefix</td></tr>
          <tr><td>MIB Registration</td><td>Tool Registration</td></tr>
          <tr><td>ax.Register PDU</td><td>mcpax/register</td></tr>
          <tr><td>ax.Unregister PDU</td><td>mcpax/deregister</td></tr>
          <tr><td>ax.Get/GetNext/GetBulk</td><td>tools/call (routed)</td></tr>
          <tr><td>ax.Notify PDU</td><td>MCP notification (prefixed)</td></tr>
          <tr><td>ax.Ping PDU</td><td>mcpax/heartbeat</td></tr>
          <tr><td>ax.Response PDU</td><td>JSON-RPC response</td></tr>
          <tr><td>Session ID</td><td>session_id</td></tr>
          <tr><td>Context</td><td>Namespace segment</td></tr>
          <tr><td>sysUpTime</td><td>registered_at timestamp</td></tr>
          <tr><td>AgentX socket (unix/tcp)</td><td>HTTP+SSE / stdio / bridged</td></tr>
        </tbody>
      </table>
      <t>Key divergences from AgentX:</t>
      <ol type="(%c)">
        <li>Wire format is JSON-RPC (or CBOR for bridged transports), not binary PDUs.</li>
        <li>Namespaces are locally unique by first-registration, not globally by IANA OID assignment.</li>
        <li>Capability annotations (latency, mutability, reversibility) have no AgentX equivalent.</li>
        <li>Irreversibility gates and agent budget enforcement are MCP-AX additions with no SNMP analog.</li>
      </ol>
    </section>

    <section anchor="appendix-stub" numbered="false" toc="default">
      <name>Appendix B: Embedded Stub Reference Profiles</name>

      <section numbered="false" toc="default">
        <name>B.1 Profile A -- Table Stub</name>
        <t>
          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.
        </t>
        <t>
          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.
        </t>
        <t>
          Reference wire frame (gateway-to-stub and stub-to-gateway):
        </t>
        <artwork align="center" type="ascii-art"><![CDATA[
+-----+-----+------+---------+-----+---------+-----+
| 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.
        ]]></artwork>
        <t>
          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).
        </t>
        <sourcecode type="c" markers="true"><![CDATA[
/* 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      },
};
        ]]></sourcecode>
      </section>

      <section numbered="false" toc="default">
        <name>B.2 Profile B -- Self-Describing Stub</name>
        <t>
          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.
        </t>
        <t>
          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 <xref target="RFC7049"/>, map-only payloads.
        </t>
        <sourcecode type="c" markers="true"><![CDATA[
/* 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 },
};
        ]]></sourcecode>
      </section>

      <section numbered="false" toc="default">
        <name>B.3 Boot Sequence</name>
        <t>Profile A:</t>
        <ol>
          <li>Stub powers on; gateway detects presence (UART RTS/CTS, GPIO, or poll).</li>
          <li>Gateway loads tool mappings and schemas from its own configuration.</li>
          <li>Gateway sends mcpax/register upstream on behalf of stub.</li>
          <li>Gateway begins heartbeat on behalf of stub.</li>
          <li>Stub enters idle state, awaiting framed command bytes.</li>
        </ol>
        <t>Profile B:</t>
        <ol>
          <li>Stub powers on; sends CBOR registration frame listing tool_ids and schemas.</li>
          <li>Gateway maps tool_ids to fully qualified names from its configuration.</li>
          <li>Gateway sends mcpax/register upstream.</li>
          <li>Gateway begins heartbeat on behalf of stub.</li>
          <li>Stub enters idle state, awaiting CBOR tool call frames.</li>
        </ol>
      </section>

      <section numbered="false" toc="default">
        <name>B.4 Gateway Responsibilities</name>
        <t>
          For both profiles, the gateway handles all protocol complexity:
        </t>
        <ul>
          <li>CBOR/byte-frame to JSON-RPC bidirectional translation</li>
          <li>Tool ID to fully qualified name mapping</li>
          <li>Schema validation of arguments before forwarding to stub</li>
          <li>Stripping of all x-mcpax-* routing metadata at the stub boundary</li>
          <li>Authentication context for upstream aggregator</li>
          <li>Heartbeat generation on behalf of stub</li>
          <li>Buffering and retry for unreliable transports (BLE, CAN)</li>
        </ul>
      </section>
    </section>

    <section anchor="appendix-topologies" numbered="false" toc="default">
      <name>Appendix C: Example Topologies</name>

      <section numbered="false" toc="default">
        <name>C.1 Enterprise SaaS Aggregation</name>
        <artwork align="center" type="ascii-art"><![CDATA[
Model Client
     |
Root Aggregator
 /       |        \
Google  Jira    Salesforce
Drive  Server    Server

Namespace: google_drive.*, jira.*, salesforce.*
        ]]></artwork>
      </section>

      <section numbered="false" toc="default">
        <name>C.2 Industrial IoT with Edge Gateway</name>
        <artwork align="center" type="ascii-art"><![CDATA[
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.
        ]]></artwork>
      </section>

      <section numbered="false" toc="default">
        <name>C.3 Recursive Regional Hierarchy</name>
        <artwork align="center" type="ascii-art"><![CDATA[
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.
        ]]></artwork>
      </section>
    </section>

    <section anchor="acknowledgments" numbered="false" toc="default">
      <name>Acknowledgments</name>
      <t>
        The authors gratefully acknowledge the designers of AgentX
        <xref target="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.
      </t>
      <t>
        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.
      </t>
    </section>

  </back>
</rfc>
