# Agent Compatibility Standard (ACS)

**ACS version:** `acs-1.0`
**Status:** Public · self-certifiable
**Audience:** Agent builders, SIs, and ISVs who want their agent to run **governed** inside an enterprise tenant and earn the **Agentics Certified** mark.

---

## 0. Why this exists (one paragraph)

You built an agent. An enterprise wants to run it, but only if every model call,
every tool call, and every outcome is **on the record** — attributable, scoped,
and verifiable. The ACS is the small, stable contract that makes your agent
*compatible with the record* without rewriting it. You keep your prompts, your
framework, your model choices. You change roughly **one line** (a base URL),
declare what your agent is allowed to touch, and emit a stable identity on every
call. In return your traffic is governed, your receipts are sealed, and you get
the **Agentics Certified** mark you can show an enterprise buyer. **Self-certify
in 1–2 days.**

The ACS is **additive and stable**. Conforming to `acs-1.0` never requires you to
change how the receipt hash-chain works, and it never adds a new request kind to
the platform — your agent attaches to the existing record, it does not modify it.

---

## 1. The five requirements

An agent is **ACS-compatible** when it satisfies all five. Each is independently
checkable; §6 maps each to a machine check in `validateAgentCompliance()`.

| # | Requirement | One-line summary |
|---|-------------|------------------|
| R1 | **MCP for tools** | Tool / service calls go through MCP, so every tool call is governable and receiptable. |
| R2 | **Gateway-routable models** | Model calls go through a gateway-routable `base_url` — a one-line integration. |
| R3 | **Outcome resolution** | Expose an outcome webhook **or** polling endpoint that resolves to `approved` / `reversed` / `incident`. |
| R4 | **Stable identity** | Send a stable `agent_id` and `agent_version` on every call. |
| R5 | **Scope declaration** | Declare your scope at registration; the gateway enforces it and **receipts every violation**. |

### R1 — Tool & service calls go through MCP

Every external tool or service your agent invokes is exposed as an **MCP**
(Model Context Protocol) tool and called through MCP, not via ad-hoc SDKs baked
into your agent. This is what makes a tool call *governable*: the gateway can see
it, scope it (R5), and write it to the record.

- **Do:** register your tools as MCP servers/tools; let the gateway broker the call.
- **Don't:** call a vendor API directly from inside the agent in a way the
  gateway can't see (that call is invisible to the record and will fail R1).

> You do not have to host your own MCP server for *every* tool. Tools the
> platform already brokers (the tool mesh) count as MCP-routed. You only need to
> expose **your own** tools over MCP.

### R2 — Model calls go through a gateway-routable `base_url`

Point your existing LLM client at the gateway by setting **one environment
variable**:

```bash
# Before
OPENAI_BASE_URL=https://api.openai.com/v1

# After (one line — the gateway is OpenAI-compatible)
OPENAI_BASE_URL=https://agentics.you/api/v1
```

That's the whole integration for R2. Keep your SDK, your model names, your
streaming. The gateway routes the call, applies guardrails and budget, and seals
a receipt. Because the gateway speaks the same wire format your SDK already
speaks, **no code changes** are required beyond the base URL.

- Any model, by architecture: the gateway is provider-agnostic. You name a model;
  routing, fallback, and cost attribution happen behind the base URL.
- Send your identity headers (R4) on these calls so the receipt is attributed to
  your `agent_id` / `agent_version`.

### R3 — Outcome resolution (webhook or polling)

A model call is not done when the tokens stop — it's done when you know whether
the **outcome** held. ACS requires your agent to make outcomes resolvable to
exactly one of three terminal states:

| Outcome | Meaning |
|---------|---------|
| `approved` | The action the agent took was accepted / held / shipped. |
| `reversed` | The action was rolled back, overridden, or undone. |
| `incident` | The action caused harm, a violation, or an escalation. |

You expose **one** of:

- **Webhook (push):** when an outcome resolves, POST it to the platform outcome
  endpoint with the `agent_id`, `agent_version`, the correlating call/receipt id,
  and `outcome` ∈ {`approved`,`reversed`,`incident`}.
- **Polling (pull):** expose a read endpoint the platform can poll for the
  outcome of a given call id, returning the same shape.

The outcome resolves onto the record as a **sealed, chained outcome receipt** —
the original call receipt is never mutated. You provide the signal; the platform
provides the proof.

```jsonc
// outcome payload (webhook body, or polling response)
{
  "agent_id": "acme-refunds",
  "agent_version": "2.4.1",
  "call_id": "rcpt_0H1...",        // the call/receipt this resolves
  "outcome": "reversed",            // approved | reversed | incident
  "resolved_at": "2026-06-20T17:04:11Z",
  "detail": "Refund clawed back by finance review"   // optional, free text
}
```

### R4 — Stable identity on every call

Every model call (R2) and every tool call (R1) carries a **stable** pair:

| Field | Rule |
|-------|------|
| `agent_id` | Stable for the life of the agent. Lowercase, `[a-z0-9][a-z0-9_-]*`. Never reused for a different agent. |
| `agent_version` | Changes when the agent's behavior changes. Recommend semver (`2.4.1`); any monotonic version string is accepted. |

Send them as headers on gateway calls:

```http
X-Agent-Id: acme-refunds
X-Agent-Version: 2.4.1
```

These identify *you* on the record. The platform pins your certified
`agent_version` into the receipt's `agent_certification_version` provenance field
at write time, so an enterprise can later prove **which certified version** of
your agent produced a given receipt. (This is provenance only — it is **not**
part of the canonical hash leaf, so it never changes how receipts hash or chain.)

### R5 — Declare your scope at registration; the gateway enforces it

At registration you declare the **scope** your agent is allowed to operate in —
the tools it may call, the data classes it may touch, the spend ceiling, the
tenants it may be deployed into. The gateway **enforces** this scope at call time:

- A call **inside** scope is brokered normally.
- A call **outside** scope is **denied**, and the violation is **receipted** —
  it lands on the record as evidence, not as a silent drop.

> **This is the deal:** you tell the platform what your agent is allowed to do,
> and the platform holds you to it — and proves it held you to it. A clean
> certification means your declared scope and your actual behavior agree.

Scope is declared as a structured object:

```jsonc
{
  "tools":      ["mcp:crm.read", "mcp:refunds.issue"],  // MCP tools the agent may call
  "data_classes": ["pii.email", "financial.refund"],     // data it may touch
  "max_spend_usd": 50,                                    // per-call or per-window ceiling
  "tenants":    ["*"]                                     // deployable-into tenants ("*" = any, via deployment edge)
}
```

---

## 2. Scoping & isolation (the walls you inherit)

You don't build isolation — you inherit it. Two walls are non-negotiable and the
platform enforces them for you:

- **Per-tenant wall.** A partner reaches an enterprise tenant **only** through its
  own deployment edge. Your agent can never read enterprise B's row-level data
  while running in enterprise A's context. Cross-tenant aggregates (revenue,
  bureau) are themselves scoped per `(partner, tenant)`.
- **Per-partner wall.** Every partner is addressed by its own id. Your
  certifications, deployments, and receipts are scoped to **your** partner id.
  Partner P can never see partner Q's certs or deployments.

Certification itself is **per-tenant + per-partner**: an `(agent_id,
agent_version)` is certified under **your** partner id against an `acs_version`.
Nothing you do can cross either wall, and the certification record reflects that.

---

## 3. Self-certification flow (1–2 days)

1. **Register** your `agent_id`, `agent_version`, and **scope** (§1 R5) under your
   partner account.
2. **Point** your model client at the gateway `base_url` (R2) and route your tools
   over MCP (R1). Send `X-Agent-Id` / `X-Agent-Version` (R4) on every call.
3. **Wire** the outcome webhook or polling endpoint (R3).
4. **Submit** a compliance submission (the §5 shape) to
   `validateAgentCompliance()`. It returns a per-rule pass/fail with detail.
5. **Fix** any failing checks (the `detail` tells you exactly what's missing).
6. **Certify.** On an all-pass submission, call `certifyAgent(partnerId, agentId,
   agentVersion)`. It writes a `partner_certifications` row (`status='certified'`,
   `acs_version='acs-1.0'`) under your partner id. You're **Agentics Certified**.

A passing self-certification is what you show an enterprise buyer; the cert is
listable (optionally public) in the partner directory so an enterprise can verify
your agent **before** deploying it.

---

## 4. The "Agentics Certified" mark

| | |
|---|---|
| **What it asserts** | This `(agent_id, agent_version)` passed every ACS `acs-1.0` check: MCP tools, gateway-routable models, resolvable outcomes, stable identity, and an enforced scope declaration. |
| **What it does NOT assert** | That the agent is "good", accurate, or safe in absolute terms. ACS certifies **compatibility with the record** — that the agent is governable and verifiable — not quality. |
| **Lifecycle** | `pending → certified → revoked` / `expired`. A new `agent_version` is a **new** certification; certifying a version never silently re-certifies another. |
| **Revocation** | A cert can be `revoked` (e.g. scope drift, repeated violations) without touching the underlying receipts, which remain sealed. |

---

## 5. Submission shape (input to validation)

`validateAgentCompliance(submission)` accepts:

```jsonc
{
  "agent_id": "acme-refunds",                 // R4 — required, stable id
  "agent_version": "2.4.1",                   // R4 — required, version string
  "acs_version": "acs-1.0",                   // optional; defaults to current ACS version

  "tools_via_mcp": true,                      // R1 — all tool calls go through MCP
  "non_mcp_tools": [],                         // R1 — any tool NOT routed via MCP (must be empty to pass)

  "model_base_url": "https://agentics.you/api/v1",  // R2 — gateway-routable base URL

  "outcome": {                                 // R3 — one of webhook | polling
    "mode": "webhook",                         //   "webhook" or "polling"
    "url": "https://acme.example/acs/outcome", //   reachable endpoint
    "resolves": ["approved", "reversed", "incident"]  // must cover all three terminal states
  },

  "scope": {                                   // R5 — declared scope
    "tools": ["mcp:crm.read", "mcp:refunds.issue"],
    "data_classes": ["pii.email", "financial.refund"],
    "max_spend_usd": 50,
    "tenants": ["*"]
  }
}
```

---

## 6. Rule → check mapping

`validateAgentCompliance()` runs one check per rule and returns
`{ certified, acs_version, checks: [{ rule, pass, detail }], failures }`.
`certified` is true **iff** every check passes.

| Rule id | Requirement | Passes when |
|---------|-------------|-------------|
| `R1.tools_via_mcp` | R1 | `tools_via_mcp === true` **and** `non_mcp_tools` is empty. |
| `R2.gateway_base_url` | R2 | `model_base_url` is a gateway-routable HTTPS base URL (host on the gateway, `/v1`-style path). |
| `R3.outcome_resolution` | R3 | `outcome.mode` ∈ {`webhook`,`polling`}, `outcome.url` is HTTPS, and `outcome.resolves` covers all three of `approved`/`reversed`/`incident`. |
| `R4.stable_identity` | R4 | `agent_id` matches the id grammar **and** a non-empty `agent_version` is present. |
| `R5.scope_declared` | R5 | `scope` is present with a non-empty `tools` array (the gateway has something to enforce). |

---

## 7. Hard guarantees (what ACS never does to the record)

ACS is deliberately small so it can be **stable**. Conforming to it — and the
machinery that certifies it — guarantees:

- It **never** alters the receipt hash-chain, Merkle fold, or anchor. Partner
  attribution rides on additive, nullable provenance fields only; a receipt
  written without them hashes byte-for-byte identically.
- It **never** adds a new request kind. The frozen request-kind set
  (`parent|routing|cache|guardrail|budget|tool`) is not extended by ACS.
- It **never** crosses the per-tenant or per-partner wall. Certification is scoped
  to your partner id; enforcement is scoped through the deployment edge.

> ACS gives you compatibility, not a back door. You attach to the record on the
> record's terms — which is exactly what makes the **Agentics Certified** mark
> worth showing.
