Skip to content
sMCP~/sMCP / specBY

sMCP / SPEC

The sMCP specification.

The complete v0.1 wire contract: discovery, manifests, tasks, attestation, consent, and governance.

Wire v0.1 / complete referenceDownload Markdown ↓

SMCP — Wire Specification v0.1

SMCP — Service / Sub-agent / Secure Model-Context Protocol. The MCP-compatible governance layer for AI agents: a versioned, implementable wire spec for how agents declare capabilities, negotiate consent, exchange the minimum necessary data, and settle payment — over MCP and A2A, not instead of them. Status: v0.1 — DRAFT / pre-1.0. While < 1.0, any MINOR bump may carry breaking shape changes; this document is the source of truth for each version. Canonical strategy: `dev-platform-smcp.md` (§3 maps every SMCP concern to the machinery it reuses). Reference implementation: this repository (DiviDen). All shapes below are the exact ones emitted by the endpoints named.

0. Why SMCP exists

MCP standardized how a model calls a tool. A2A standardized how two agents talk. Neither standardizes the governance an agent platform needs the moment money, private user data, and third-party code enter the loop:

  • What may this agent do, and can it prove it hasn't changed? (attestation)
  • Exactly which fields of my data does it get, and for how long? (consent + minimization)
  • On what terms — price, hold, review window — does a task execute? (payment-as-contract)
  • What did it actually do with my data? (auditability)
  • How much autonomy has it earned, and who can see/relax that? (trust)

SMCP is wire-compatible with MCP and A2A — an SMCP node also serves an MCP server card and an A2A agent card — and adds the governance envelope around them. SMCP is designed to be implemented by other vendors; this spec is the contract.

1. Versioning & negotiation

1.1 Version string

MAJOR.MINOR (e.g. 0.1). Pre-1.0, MINOR may break. A node advertises the full set it can serve so a client picks the highest it understands.

1.2 How a client requests a version

Either transport (the header wins when both are present):

TransportForm
Request headerSMCP-Version: 0.1
Query param?smcp=0.1 or ?v=0.1

A requested value MAY carry a v or smcp/ prefix (v0.1, smcp/0.1); it is normalized away. An empty/garbage value is treated as "no request".

1.3 How a node responds (negotiation is LENIENT)

Discovery MUST NOT hard-fail on an unknown version.

  • No request → node responds with its preferred (latest) version.
  • Supported request → node responds with that version.
  • Unsupported request → node responds with its preferred version and flags the mismatch (versionNegotiation.ok = false). The client decides whether to downgrade, warn, or proceed.

Every negotiated response:

  • echoes SMCP-Version: <resolved> in the response headers,
  • sets Vary: SMCP-Version,
  • carries a machine-readable versionNegotiation block (see §2).

Reference negotiation logic: `src/lib/smcp/version.ts` (negotiateSmcpVersion, pickRequestedVersion).

2. Node discovery — GET /.well-known/dividen-agent.json

The version-negotiation anchor and entry point. Public, CORS-open (Access-Control-Allow-Origin: *), cacheable (max-age=300). Independent of any product feature flag — the protocol is discoverable the moment it ships.

This path is shared, by design, with the agent-host attestation document (§3.4): it is the canonical "DiviDen agent manifest" any node serves. A platform node serves a superset discovery variant (below). An agent host serves the signed attestation variant. They are distinguished by their fields, not their path.
jsonc
{
  "smcp": "0.1",                       // negotiated version
  "smcpVersions": ["0.1"],             // everything this node can serve (newest last)
  "smcpMinVersion": "0.1",
  "smcpPreferredVersion": "0.1",

  "node": { "name": "DiviDen", "url": "https://dividen.ai", "kind": "platform", "description": "…" },

  "discovery": {
    "spec": "https://smcp.denominator.ventures/spec",
    "agentCard": "https://dividen.ai/.well-known/agent-card.json",
    "mcpServerCard": "https://dividen.ai/.well-known/mcp/server-card.json",
    "oauthProtectedResource": "https://dividen.ai/.well-known/oauth-protected-resource",
    "agentManifest": "https://dividen.ai/api/smcp/agents/{slug}/manifest"   // {slug} template
  },

  "protocols": {
    "mcp": { "supported": true, "transport": "streamable-http", "endpoint": "…/api/mcp", "serverCard": "…" },
    "a2a": { "supported": true, "endpoint": "…/api/a2a", "agentCard": "…" }
  },

  "governance": {                      // §5 — named, enforced promises
    "returnGuard": "deny-by-default, fail-closed moderation on every agent return",
    "secretFreeResolver": "credential resolution never returns key material",
    "relaxOnlyTrust": "earned-autonomy gate only ever relaxes; nothing silent by default",
    "signedAttestation": "ed25519-signed, fingerprinted capability manifests with drift detection",
    "consent": "execution requires explicit approval for non-owner runs (coarse whole-run consent active; field-level ConsentGrant projection implemented, gated behind SMCP_CONSENT_ENFORCE_DISPATCH)"
  },

  "authentication": { "schemes": ["bearer"], "description": "…" },

  "versionNegotiation": {
    "requested": "0.1" | null,
    "resolved": "0.1",
    "supported": ["0.1"],
    "min": "0.1",
    "ok": true,
    "header": "smcp-version",
    "query": ["smcp", "v"]
  }
}

Reference: `src/app/.well-known/dividen-agent.json/route.ts`.

2.1 Optional set-shaped agents[] section (NON-AUTHORITATIVE — verb-ladder S4)

A node MAY add an OPTIONAL, additive top-level agents[] array enumerating the agents it advertises, gated by an INDEPENDENT agentsVersion integer. It rides under the same negotiated sMCP 0.1 wire — smcpVersions is unchanged — and existing consumers ignore both keys. Absence is normal; this section is a discovery convenience, not a conformance requirement.

jsonc
{
  "smcp": "0.1",
  // …all §2 node-discovery keys unchanged…
  "agents": [
    {
      "slug": "invoice-parser",              // SLUG_RE; required
      "name": "Invoice Parser",              // required
      "endpoint": "https://host/agents/invoice-parser/execute",  // public http(s); required
      "description": "…",                    // optional
      "type": "webhook",                     // optional: webhook | mcp
      "inputFormat": "json",                 // optional: text | json | a2a | mcp
      "category": "documents",               // optional
      "pricePerTask": 0                      // optional; 0 / absent = free
    }
  ],
  "agentsVersion": 1
}

Rules a consumer applies (see parseDiscoveryDoc in `cli/connect/discovery.ts`): auth is never carried in the document; entries with an invalid slug, missing name, non-http(s) endpoint, or an endpoint resolving to a private/internal address are DROPPED to a skip ledger; duplicate slugs keep the first; the array is capped (~50) with a truncated notice. The platform serves this section built from its publicly-listed agents, exposing the public per-agent manifest URL as endpoint (never a raw provider endpoint). divi connect --from-discovery <url> consumes it to bulk-observe a host's agents locally (no account, no /register, no badge). A breaking change to the agents shape bumps agentsVersion — the wire shape itself is RC-ratified and change-controlled.

Shape reference: `src/lib/smcp/agents-discovery.ts`.

3. The four wire shapes

3.1 Conventions

  • Hashes are sha256:<64 lower-case hex> over the canonical JSON of the value (keys sorted lexicographically at every level, undefined dropped, null kept, no whitespace). Reference: canonicalJson in `agent-host-attestation.ts`.
  • All discovery/manifest shapes are secret-free by construction: a node never serializes auth tokens, the agent's raw dispatch endpoint, payout internals, or health URLs into any SMCP wire shape. Buyers execute through the platform's endpoints, never the developer's webhook.

3.2 Agent capability manifest — GET /api/smcp/agents/{slug}/manifest

The agent's capability declaration + its catalog of task contracts + the negotiation half (what it requests). Public, CORS-open, version-negotiated. A non-public agent (draft / private / suspended / paused) returns 404, indistinguishable from missing.

jsonc
{
  "smcp": "0.1",
  "agent": {
    "id": "…", "slug": "company-research", "name": "Company Research",
    "description": "…", "longDescription": null,
    "category": "research", "version": "1.0.0", "qualityTier": "gold"
  },
  "declares": {
    "formats": ["a2a", "json"],            // deduped, sorted; derived from supportsA2A/MCP + inputFormat
    "supportsA2A": true,
    "supportsMCP": false,
    "inlinePurchaseEnabled": true,
    "declaredWidgetTypes": ["checkout_card", "data_table"],   // sorted
    "inputSchemaHash": "sha256:…",         // null when none declared
    "outputSchemaHash": "sha256:…",
    "tasks": [ /* §3.3 task contracts */ ],
    "presentation": { "avatar": "leather-divi" }  // OPTIONAL + additive (Den asset A3): the
                                           // Den-catalog avatar the agent asks to be RENDERED as.
                                           // OMITTED when undeclared — an undeclared manifest is
                                           // byte-identical to pre-A3 (capabilityHash unchanged).
                                           // Declared at register via `presentation: { avatar }`,
                                           // validated against the CLOSED catalog at ingest AND
                                           // emit; consumers treat an unknown id as absent (default
                                           // body), never as an error. A Den operator's LOCAL
                                           // avatar assignment always overrides the declaration.
  },
  "requests": {                            // the negotiation half — what the agent asks FOR
    "dataScopes": [ { "scope": "board.card.read", "duration": "execution", "purpose": "task" } ],
    "credentials": [ /* { kind, provider, mode } — full wiring is roadmap Phase 3 */ ]
  },
  "execution": {
    "manifest":  "https://dividen.ai/api/smcp/agents/company-research/manifest",
    "publicPage":"https://dividen.ai/a/company-research",
    "a2a":       "https://dividen.ai/api/a2a",        // dispatch is PROXIED by the platform
    "mcp":       "https://dividen.ai/api/mcp"
  },
  "attestation": { "status": "verified" | null, "manifestHash": "sha256:…" | null },

  "capabilityHash": "sha256:…"             // stable hash over the PUBLIC declaration (NOT the private endpoint)
}

capabilityHash lets a client pin / cache-validate the declared capabilities. It is distinct from the attestation manifestHash (§3.4), which binds the agent's private dispatch endpoint and is signed by the host.

Reference: `src/lib/smcp/manifest.ts`, [src/app/api/smcp/agents/[slug]/manifest/route.ts](../src/app/api/smcp/agents/[slug]/manifest/route.ts).

3.3 Task contract — payment authorization as part of the execution contract

One per purchasable task; appears inside declares.tasks[] and is pinned into the execution ledger at quote time.

jsonc
{
  "smcp": "0.1",
  "taskId": "…", "slug": "company-brief",
  "outcome": "A 2-page company brief",
  "inputSchema":  { /* declared JSON-Schema-ish */ },
  "outputSchema": { /* declared JSON-Schema-ish */ },
  "permissions": { "dataScopes": ["board.card.read", "calendar.event.title"] },
  "resultWidgets": ["data_table"],
  "pricing": { "model": "per_task", "amountCents": 2500, "currency": "USD" },
                  // model ∈ free | per_task | subscription | dynamic
                  // free omits amount/currency; dynamic ⇒ two-phase price_quote at runtime
  "sla": { "expectedRuntimeMs": 120000 | null },
  "settlement": { "hold": true, "reviewWindowHours": 48, "callbackSigned": true }
}

settlement states platform invariants (held PaymentIntent, 48h review window, HMAC-signed callback), not per-task config.

3.4 Capability attestation — GET https://<agent-host>/.well-known/dividen-agent.json

Served by the agent host (not the platform). The host signs its OWN manifest fingerprint; the platform verifies against the public key it registered (TOFU at register, co-signed at review). The platform NEVER holds the private key.

jsonc
{
  "manifestHash": "sha256:…",   // SHA-256 over the canonical, secret-free manifest (endpoint + widgets + schema hashes + capability flags)
  "keyId": "…" | null,
  "alg": "ed25519",
  "signature": "<base64 ed25519 over the manifestHash ASCII bytes>"
}

A MINOR of SMCP MAY extend this with smcp, declares, and requests blocks (mirroring §3.2) while keeping manifestHash + signature for backward compat. Reference + canonicalization rule: `agent-host-attestation.ts`.

4. Governance model (enforced today; some enforcement flag-gated/phased)

4.1 Identity

Bearer tokens (dvd_ / dvdn_). Agents may hold a scoped, revocable JWT (AgentScopes: board/threads/knowledge read, card create, dispatch, config-post), epoch-revocable per owner. SMCP does not require OAuth (see /.well-known/oauth-protected-resource).

  • A non-owner run requires explicit approval today (coarse, whole-run consent) — this is the default-active behavior.
  • SMCP's durable, field-level, time-boxed, revocable grant — `ConsentGrant` (subject, agent, scopes, fields, purpose, expiresAt, revokedAt, audit ref) — is specified here and IMPLEMENTED, but gated behind the default-off `SMCP_CONSENT_ENFORCE_DISPATCH` flag (on top of SMCP_CONSENT_V1). With the flag off — the shipped default — coarse whole-run consent is what runs. With it on, the pure engine (evaluateConsentGrant deny-before-egress + the projectToGrantedScopes minimal-field redact in `src/lib/smcp/consent.ts`) projects the runtime payload an agent receives down to the granted scopes before dispatch, so an agent cannot receive a field it wasn't granted. The single egress choke point is enforceConsentDispatch; both paid run paths share it.
  • requests.dataScopes[] (§3.2) is the agent's declared ask; a ConsentGrant is the human's durable answer. The two are intersected with the JWT's coarse scopes at every data-access boundary.

4.3 Trust (earned autonomy)

A per-user × domain time-decayed score advances observe → suggest → ask-once → auto. The gate is relax-only — it never tightens silently, and an operator can see/cap it. Reference: `trust-engine.ts`.

4.4 Safety (agent returns)

Every value an agent returns passes a deny-by-default, fail-closed moderation guard (deterministic threat assessment + optional LLM judge). Reference: `agent-return-guard.ts`.

4.5 Auditability

Per-execution ledger + audit log with privacy-preserving anonymization. A unified, queryable SMCP audit API + a customer-facing "what did this agent do with my data" view is specified and delivered in a later phase (roadmap Phase 3).

4.6 Settlement & spend invariants (enforced)

Two payment-side guarantees that govern money rather than the data/return envelope, hence stated here under the governance model (not in §5's discovery list):

  • Settlement invariant. Every paid task carries the same settlement block in its task contract (§3.3): a held PaymentIntent, a 48-hour review window, and an HMAC-signed callback. These are stated as platform constants (hold: true, reviewWindowHours: 48, callbackSigned: true), not per-task config — they mirror the live MarketplaceExecution lifecycle (Stripe hold/capture, reviewStatus, per-execution callbackSecret). Reference: `src/lib/smcp/manifest.ts` (buildSmcpTaskContract).
  • Spend-budget invariant (agent-as-buyer). When an agent spends money on a principal's behalf, the authority is a bounded, revocable `AgentSpendBudget` (total cap, optional per-task cap, optional task-type allowlist, currency, expiry, revokedAt) and the authorization engine is deny-by-default: it denies on any ambiguity (missing/invalid budget, malformed charge, revocation, expiry, out-of-scope task type, over per-task cap, over total cap), reasons in integer cents only, and accumulates settled spend idempotently. The pure engine is shipped; the IO/fund-movement layer is gated behind the default-off `A2A_MONEY_V1`. Reference: `src/lib/smcp/agent-spend.ts` (evaluateAgentSpend, accumulateSpend).

5. Named guarantees (the marketed SMCP promises)

These are the unconditionally-enforced promises surfaced in the node discovery governance block (§2) — they hold in every flag state, on every run:

  1. Deny-by-default return guard — fail-closed moderation on every agent return.
  2. Secret-free resolver boundary — credential resolution returns status/metadata, never key material.
  3. Relax-only trust gate — earned autonomy only ever relaxes; nothing silent by default.
  4. Signed, fingerprinted attestation — ed25519-signed capability manifests with drift detection.
Scope note: this list enumerates exactly the four discovery-block guarantees that are enforced unconditionally. The discovery block's fifth key, consent, is the coarse-whole-run promise plus the flag-gated field-level projection (§4.2). The settlement and spend-budget invariants are stated, and cited to their engines, in §4.6 — they govern payment terms and delegated spend rather than the data/return envelope, so they live under the governance model, not in this discovery list.

6. Conformance

An implementation is SMCP v0.1 node-conformant if it:

  1. Serves GET /.well-known/dividen-agent.json with the §2 shape, CORS-open, and honors §1 version negotiation (header + query, lenient, echoes SMCP-Version, Varys on it).
  2. Serves GET /api/smcp/agents/{slug}/manifest with the §3.2 shape for every publicly-listed agent, 404-indistinguishable for non-public agents, secret-free.
  3. Emits task contracts (§3.3) with a pricing model from the enumerated set and the settlement invariants.
  4. Advertises MCP + A2A compatibility truthfully in protocols.
  5. Never serializes secrets (auth tokens, raw endpoints, payout internals) into any SMCP wire shape.

An agent host is attestation-conformant if it serves §3.4 with a valid ed25519 signature over its canonical manifest fingerprint.

Consent (ConsentGrant) and minimal-field projection are v0.1-specified and IMPLEMENTED but flag-gated (DiviDen ships them behind the default-off SMCP_CONSENT_ENFORCE_DISPATCH; §4.2). The unified audit API remains v0.1-specified, later-phase-delivered (§4.5). An implementation MAY advertise coarse whole-run consent in the interim (as DiviDen does by default) without losing node conformance.

7. Change log

  • v0.1.2 (2026-06-30) — additive, OPTIONAL agents[] node-discovery section (verb-ladder S4; §2.1), gated by an INDEPENDENT agentsVersion. No sMCP wire-version change (smcpVersions stays ["0.1"]); existing consumers ignore both keys. Consumed by divi connect --from-discovery <url> for bulk local observe.
  • v0.1.1 (2026-06-27) — doc reconciliation with shipped behavior (no wire-shape change). Reflects the following merges:
  • Consent enforcement & minimal-field projection — moved from "roadmap Phase 3" to implemented, flag-gated behind the default-off SMCP_CONSENT_ENFORCE_DISPATCH (on top of SMCP_CONSENT_V1); coarse whole-run consent stays the shipped default (§2 governance / §4.2 / §6). The deny-before-egress and projectToGrantedScopes minimal-field redact share one choke point, enforceConsentDispatch (src/lib/smcp/consent.ts).
  • Settlement invariant (#590) — settlement block documented as enforced platform constants (held PaymentIntent / 48h review window / HMAC-signed callback) mirroring the MarketplaceExecution lifecycle; own-distribution public-page runs settle 100% fee-free to the developer behind OWN_DISTRIBUTION_SETTLEMENT_V1. New §4.6.
  • Spend-budget invariant (#591 / L15) — bounded, revocable AgentSpendBudget with a deny-by-default, integer-cents authorization engine (src/lib/smcp/agent-spend.ts), IO gated behind default-off A2A_MONEY_V1. New §4.6.
  • Attestation honesty gate (#589) — attestation.status now distinguishes trust-on-first-use (attested-tofu) from a real ed25519 signatureVerified grade; the "signed…verified and drift-free" promise is reserved for genuine signature verification, so the badge can no longer overstate assurance off admin approval alone.
  • Agent-card visibility gate (#586) — the public, CORS-open /.well-known/agent-card.json now applies the shared registry visibility gate (certifiedAgentWhere() + isCertifiedAgentVisible()), closing the owner-bypass leak of installed private/draft agent metadata, consistent with §3.2's 404-indistinguishable rule for non-public agents.
  • v0.1 (2026-06-26) — initial draft. Node discovery + version negotiation, per-agent capability manifest, task contract, attestation doc shape, governance model (identity/consent/trust/safety/audit), named guarantees, conformance. Consent enforcement, minimal-field projection, and the unified audit API are specified but phased.

This spec is versioned wire contract, not marketing. Where it says a shape is emitted, the cited endpoint emits exactly that shape.