Agent-run estate end to end
Handshake → search → fetch → the exec gate — an agent drives the estate catalog keyless through the one MCP server, against the sandbox world.
The AI pillar is not a second product — it is the same catalog behind one more
door. The estate MCP server at apis.vin/mcp speaks JSON-RPC 2.0 over plain
POST, keyless, stateless, and exposes a code-mode trio — search · fetch
· exec — never a sprawling tool menu. This journey is an agent's first
session: no key, no OAuth ceremony, no SDK install. It shakes hands, finds a
door, dereferences it, drives a second pillar through the same tool, and runs
into exactly one gate — shown below, not detoured around.
The cast
This journey rides the curated world — deterministic, replayable, every payload provenance-labeled. The full trigger register is at sandbox test triggers.
| World key | Role in this journey |
|---|---|
JM3KFBXY1S0597748 | The flagship — 2025 Mazda CX-5 2.5 Turbo Signature. Every dereference below rides it: the decode, the record composition, the services read. |
| the keyless caller | The agent itself. The MCP door answers initialize, search, and fetch in full with no credential; the exec gate adjudicates the caller's tier — that refusal is the journey's final step. |
1 — Handshake
One unauthenticated POST and the server declares itself — protocol version negotiated, tool capabilities posted, and its own operating instructions in the body. No session id comes back because none exists: every message stands alone.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"agent","version":"1.0.0"}}}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"serverInfo": { "name": "apis.vin", "title": "apis.vin — the platform API" },
"instructions": "One MCP server for the whole platform. search finds a capability across the catalog; fetch dereferences an address and answers the typed envelope (OK / EMPTY / BLOCKED / OFFER). Keyless throughout; POST /keys mints a sandbox key when you want account context."
}
}2 — Search: find the door
The search tool ranks the query against every live door in the one manifest
that also generates the capability card and the OpenAPI contract — evidence in
a door's address and summary outweighs words buried in prose, so a "decode"
query answers the decode doors, never a transaction verb that happens to share
vocabulary.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search","arguments":{"query":"decode a vin","limit":3}}}'{
"query": "decode a vin",
"count": 3,
"results": [
{ "method": "GET", "path": "/decode/{vin}" },
{ "method": "GET", "path": "/listings/{vin}" },
{ "method": "GET", "path": "/{vin}" }
],
"openapi": "https://apis.vin/openapi.json"
}3 — Fetch: dereference it
fetch dispatches the same application in-process — no socket, no second
implementation — so the tool answer IS the door's answer: the typed envelope,
money as decimal strings, the provenance block intact. This is the same call
Start opens with, arriving through the agent door.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"fetch","arguments":{"path":"/decode/JM3KFBXY1S0597748"}}}'{
"status": 200,
"path": "/decode/JM3KFBXY1S0597748",
"envelope": {
"type": "OK",
"decode": {
"vin": "JM3KFBXY1S0597748",
"year": 2025,
"make": "Mazda",
"model": "CX-5",
"retailValue": "35887.00",
"msrp": "43400.00",
"priceCurrency": "USD"
},
"provenance": { "environment": "sandbox" }
}
}4 — Drive a second pillar through the same tool
The trio is not a data-pillar convenience — any addressable door dereferences.
Here the agent reads a Services capability, and the answer is byte-identical
with what an HTTP caller gets on the door directly: the same
MaintenanceIntervalLookup@1 claim the journeys roster frames as
its exemplar.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"fetch","arguments":{"path":"/maintenances/interval-lookup?vin=JM3KFBXY1S0597748"}}}'{
"status": 200,
"path": "/maintenances/interval-lookup?vin=JM3KFBXY1S0597748",
"envelope": {
"type": "OK",
"answers": "MaintenanceIntervalLookup@1",
"value": {
"vin": "JM3KFBXY1S0597748",
"intervals": [
{ "service": "oilChange", "everyMiles": 7500, "everyMonths": 12 },
{ "service": "tireRotation", "everyMiles": 7500 }
]
},
"provenance": { "simulated": true, "environment": "sandbox", "source": "services-sandbox" }
}
}5 — Find the write doors
fetch is GET-only by design — the transaction verbs are HTTP POST doors, and
search names them. An agent that wants to open a deal takes the answer below
to plain HTTP (the same POST /buy/deals every caller uses), or to the exec
isolate — whose gate is the next step.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"search","arguments":{"query":"open a deal","limit":3}}}'{
"query": "open a deal",
"count": 3,
"results": [
{ "method": "GET", "path": "/buy/deals/{dealId}/esign/ceremony" },
{ "method": "POST", "path": "/buy/deals", "summary": "Open a deal on a VIN" },
{ "method": "GET", "path": "/buy/deals/{dealId}/checkout" }
]
}6 — Where the gate stands: exec
exec runs agent-written code against the bound estate SDK inside a sandboxed
isolate — and it is a licensed-tier tool. The keyless caller this journey
rides (and a plain sandbox key alike) answers a typed 402 OFFER naming the
upgrade: a mandate ceremony at the auth rail — the human-authority moment that
attaches a licensed mandate to the account
behind the key. The refusal is the journey's honest ending: the gate is shown,
never papered. One more honest variance: exec is feature-detected per
deployment — where the isolate rail is not armed at all (a local checkout's
dev:sandbox, for instance), the same call answers a typed
BLOCKED { code: "unconfigured" } instead, and tools/list omits the tool —
declared absence, never a silent 404.
curl -X POST 'https://apis.vin/mcp' \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"exec","arguments":{"code":"const r = await vin.data.decode(\"JM3KFBXY1S0597748\"); return r.type"}}}'{
"type": "OFFER",
"handoff": { "kind": "reauthorize", "url": "https://auth.vin/device" },
"code": "upgrade-required",
"status": 402,
"reason": "exec is a licensed-tier tool — sandbox keys and keyless callers get search + fetch in full; upgrade at the auth.vin mandate rail and present the upgraded key",
"upgrade": {
"url": "https://auth.vin/device",
"how": "mint a key at POST /keys, then complete the auth.vin device-rail ceremony (the human-authority moment) to attach a licensed mandate to the account behind it"
}
}What the agent now holds
One endpoint and no credential bought the whole read estate: a declared tool
surface, a ranked catalog search, and door answers byte-identical with what
HTTP callers get — typed envelopes, decimal-string money, provenance intact.
The only refusal was typed, priced, and carried its own handoff. When the agent
wants account context, POST /keys mints a sandbox key — no email, no
approval, no human — and the same three verbs answer keyed. Compose what the
world knows about the flagship on the record face, GET /{vin}, at
the record.
Next
- Journeys — the roster this walk belongs to.
ai.estateMcp— the capability's own reference page: identity strip, mode, contract.- Sandbox test triggers — the full deterministic register.
- The chassis — the MCP server beside the SDK and the CLI it cannot drift from.
F&I product lifecycle end to end
Rate → sell → administer → cancel → refund — a vehicle service contract's whole life, every step a catalog capability, runnable against the sandbox world.
The Catalog
The faceted finder over the one queryable catalog — 3947 typed capability descriptors across 317 nouns, every face generated from the same rows.