Integrate an agent with DMC-12
This is the how-to companion to the dmc-12.ai overview and the machine-readable spec at dmc12.ai. It walks a partner agent — CarEdge, Cox / Dealer.com, a broker, or an OEM agent — from discovery to a completed deal hand-off against the live Mark Miller Subaru endpoint.
inventory:read, quote:write, reservation:write,
deal:handoff, pricing:read.
1. The lifecycle
Every transaction-class flow is the same four steps. Reads (inventory, pricing disclosure) can happen at any point.
Quotes and reservations both carry a 30-minute TTL, so an agent should run the quote → reserve → hand-off tail back-to-back once the buyer has committed. The deep-dive pages cover each step: Inventory → Quotes → Reservations → Deal hand-off.
2. Pick a rail: A2A or MCP
The same DMC-12 capabilities are served over two wires from one Cloud Run service. Choose by who your caller is.
MCP_BASE = https://mm-inventory-mcp-572952183767.us-central1.run.app.
A2A calls go to MCP_BASE/a2a/, MCP to MCP_BASE/mcp/, and the
well-knowns to MCP_BASE/.well-known/…. (Prefer discovering the endpoint from the
dealer's published /.well-known/ucp rather than hard-coding it.)
| Rail | Transport | Who | Surface |
|---|---|---|---|
| /a2a/ | JSON-RPC 2.0 | Named partner agents (machine-to-machine) | Full write surface — read + quote + reservation + deal hand-off, scope-gated. |
| /mcp/ | Streamable HTTP | LLM clients (Claude, ChatGPT, Gemini) and your own MCP agent | Public callers are read-only; partner-audience tokens get the write surface. |
This guide uses the A2A rail for every worked example — it is the path partners integrate against. The wire envelope is a standard JSON-RPC tools/call:
POST /a2a/ Authorization: Bearer <token>
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_inventory",
"arguments": { "query": "2024 Outback AWD under 35k", "limit": 5 }
}
}
And tools/list (no params) returns the live, scope-filtered tool catalog. Every response is wrapped in a standard envelope:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": { /* the tool payload */ },
"_metadata": {
"trace_id": "trc_…", // log this — it correlates to the audit row
"tool": "search_inventory",
"agent_id": "…@clients",
"protocol": "a2a",
"timestamp": "2026-05-27T18:04:11+00:00"
}
}
}
data with a _metadata sidecar so a downstream LLM
treats it as content, never as instructions. Always log _metadata.trace_id — it is the only
way MM can correlate a complaint with the 90-day audit log.
3. Discovery — check the well-knowns
Every claim about this deployment is backed by an unauthenticated, cacheable document at a canonical URL. Fetch these first; they are the source of truth for tool count, capability versions, scopes, and the auth server.
What each one returns, in brief:
| Well-known | Returns | Shape (abridged) |
|---|---|---|
| agent-card.json | A2A v1.0 Agent Card — display name, protocols, policies, the scope-filtered tools array, and authentication (issuer / audience / scopes). JWS-signed when signing is configured. | { schema_version, name, description, protocols:["a2a"], endpoints, tools:[…], authentication:{ type:"oauth2_client_credentials", issuer, audience, scopes:[…] } } |
| mcp | SEP-1960 MCP Server Card — endpoint, transports, auth methods (OAuth + legacy shared-key), and the public/partner scope split. | { mcpVersion:"2025-11-25", name, endpoints:{ http }, transports:["streamable-http"], auth:{ sharedKey, oauth2 }, scopes:{ public:[…], partner:[…] } } |
| ucp | UCP v1 manifest — merchant, capability list (ai.dmc12.automotive.* with versions + schema URLs), policies, and inventory freshness SLA. | { ucp:{ version, services, capabilities:[{ name, version, spec, schema }] }, merchant:{…}, policies:{…} } |
| oauth-protected-resource | RFC 9728 metadata — the resource identifier (must match token aud), scopes_supported, and the authorization server. | { resource, authorization_servers:[…], scopes_supported:[…], bearer_methods_supported:["header"] } |
| jwks.json | The public JWK set used to verify the Agent Card's JWS signature (not the IdP's JWKS). | { keys:[{ kty, kid, use:"sig", alg:"RS256", n, e }] } |
4. Auth, in one paragraph
Partners authenticate with OAuth 2.1 client-credentials against Auth0 tenant
mmbrain-prod.us.auth0.com, audience mm-inventory-mcp. Mint an RS256 access token
(1-hour TTL), cache it for ~55 minutes, and send it as a Bearer token on every
/a2a/ call. Your effective permissions are the intersection of the
scopes seeded on your agent row and the scopes in your token. Onboarding is an email to Ben — see
Partner onboarding for the full credential bundle, the mint command, rate
limits, and the scope matrix.
5. Data formats
Every DMC-12 field follows one of these conventions. The per-capability pages
(Inventory, Quotes,
Reservations, Deal hand-off) link back here
rather than restating them. All enum values are lower-case and case-sensitive —
available, not Available.
| Format | Rule | Example |
|---|---|---|
| VIN | 17 chars, ^[A-HJ-NPR-Z0-9]{17}$ — upper-case; letters I, O, Q are excluded (they look like 1 / 0). | 4S4BTGUD8R3201234 |
| Money | { "amount": number, "currency": string } — amount is a decimal in major units (dollars, 2-dp cents), currency is ISO-4217 ^[A-Z]{3}$. Never a bare number. | { "amount": 445.0, "currency": "USD" } |
| Timestamp | ISO-8601, UTC — either a trailing Z or +00:00. Always UTC, never a local offset. | 2026-05-27T18:04:11+00:00 |
| Phone | E.164 — +, country code, national number, no spaces or punctuation (deal hand-off only). | +18015551234 |
| Standard RFC-5322 address (deal hand-off only). | buyer@example.com | |
| Enums | Lower-case, case-sensitive — status values, kind, payee, sort fields, error codes' lower portions. Send and compare exactly. | available · vehicle_price · government |
| Opaque tokens | quote_id, reservation_token, handoff_token are opaque strings — treat as black boxes; never parse or construct them. | qte_8f1c… · rsv_… · hnd_… |