Docs
The door at version 1.0.0: 19 tools in six groups over the SCHEMA algo record, answering from records whose every field carries its source page. This page is the reference; the Agent Card is the discovery document; the setup page is the connection page.
The record
SCHEMA algo: six datasets — SKUs · Chains · HITL · Economics · Makers · Acts. Spine: temperature (ambient · chilled · frozen · fresh) → department → super category → category → item; the attribute lane (organic · free-from · plant-based · artisan · origin · premium) cross-cuts; channel (discounter · e-commerce · local · in-store · traditional).
- Markets (20) — FR · DE · IT · ES · PL · UK · NL · BE · CH · US · CA · MX · BR · JP · KR · IN · VN · TH · SG · AU (UK is stored as ISO GB in machine fields and written UK everywhere a person reads).
- Rule sets (14) — one per market; the seven EU markets share one food-law set with national riders.
- Actors — maker · brand_owner · private_label · distributor · importer · broker · retailer_buyer · auditor (read-only) · agent (inherits the class of the credential it carries). Public and consumer are refused.
- Reason codes — ALLOW · REQUIRE_NOTIFICATION · REQUIRE_RESPONSIBLE_PERSON · DENY_NOT_PERMITTED_HERE · DENY_INGREDIENT_BANNED · DENY_INGREDIENT_LIMIT · DENY_CLAIM · DENY_MARKET · DENY_ACTOR_CLASS · DENY_UNLICENSED_AGENT · DENY_NO_GTIN · DENY_NOT_VERIFIED.
- Source class — register > publisher > trade > engine on every cited field; verified needs register or publisher.
Gate order
resolve_jurisdiction → resolve_actor → gate_transaction. No buyer, supplier, availability, price, documents, order or handoff tool answers before an allow decision exists; the decision_id travels with every later call. Read-only tools answer without a bearer; create_order_intent, a2a_handoff and log_audit refuse without a registry bearer carrying grocery.transact (DENY_UNLICENSED_AGENT).
The tools — one block each
Purpose and inputs are read from the door's live tools/list at build; outputs, reason codes and the example call are written against the door's code. Every tool carries readOnlyHint and destructiveHint on the wire.
Gate — called first; allow or deny with the reason and the rule
resolve_jurisdiction
- Purpose — Where is this going? Resolve a ship-to (e.g. 'DE', 'US-CA', 'UK', 'FR', 'EU-NL', 'JP') to one of the 20 markets and its rule set (14 rule sets; the seven EU markets share one food-law set with national riders). UK is the label; machine fields carry the ISO code GB.
- Inputs —
ship_to(required): string - Outputs — market (machine code; GB for the UK), market_label (UK written for people), market_name, region, rule_set and rule_set_version, the member market when the ship-to is an EU code, the national rider, and the regulator of record.
- Reason codes — DENY_MARKET when the ship-to is not one of the 20 markets or an EU code without a member; BAD_INPUT when empty.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "resolve_jurisdiction", "arguments": {"ship_to": "DE"}}}
resolve_actor
- Purpose — Who is asking? Maker, brand owner, private label, distributor, importer, broker or retail buyer (trade only); auditor (read-only); agent (inherits the class of the credential it presents — none returns DENY_UNLICENSED_AGENT). The consumer is never the counterparty. Returns communication_class: trade_information or public.
- Inputs —
actor_class(required): string;credential: string - Outputs — actor_class, channel (B2B, or A2A for an agent), communication_class, credential_presented, and when a registry bearer is presented its client_id, client_name, scope and issuer.
- Reason codes — DENY_ACTOR_CLASS for public or consumer, or an unknown class; DENY_UNLICENSED_AGENT for an agent with no credential.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "resolve_actor", "arguments": {"actor_class": "retailer_buyer"}}}
gate_transaction
- Purpose — Allow or deny, with the reason and the rule. Takes an item (GTIN or catalogue id; optional), the ship-to market, the actor class and, when no record exists yet, the product type, ingredients and claims. Returns one of twelve reason codes (ALLOW · REQUIRE_NOTIFICATION · REQUIRE_RESPONSIBLE_PERSON · DENY_NOT_PERMITTED_HERE · DENY_INGREDIENT_BANNED · DENY_INGREDIENT_LIMIT · DENY_CLAIM · DENY_MARKET · DENY_ACTOR_CLASS · DENY_UNLICENSED_AGENT · DENY_NO_GTIN · DENY_NOT_VERIFIED), the rule-set version, the source page and a decision_id for the downstream tools.
- Inputs —
ship_to(required): string;actor_class(required): string;product: string;product_type: string;ingredients: string;claims: string;channel: string;credential: string - Outputs — result (allow · allow_with_condition · deny), reason_code, decision_id (carry it into every later call), rule_set and rule_set_version, market status, ingredient and claim findings where given, temperature and lanes of the item, and the rule read.
- Reason codes — ALLOW · REQUIRE_NOTIFICATION · REQUIRE_RESPONSIBLE_PERSON · DENY_NOT_PERMITTED_HERE · DENY_INGREDIENT_BANNED · DENY_INGREDIENT_LIMIT · DENY_CLAIM · DENY_MARKET · DENY_ACTOR_CLASS · DENY_UNLICENSED_AGENT · DENY_NO_GTIN · DENY_NOT_VERIFIED
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "gate_transaction", "arguments": {"ship_to": "DE", "actor_class": "retailer_buyer", "product_type": "dry pasta"}}}
SKUs — the item
search_cleared_items
- Purpose — Find items cleared for this market: validated SKU records in a market (query by product name, brand, maker or category; filters: temperature ambient · chilled · frozen · fresh, attribute lane organic · free-from · plant-based · artisan · origin · premium, department). Only validated rows are served; each item carries its claim status, market status and source class.
- Inputs —
market(required): string;query: string;temperature: string;lane: string;department: string;product_type: string;limit: integer;decision_id: string - Outputs — validated SKU rows for the market and filters: catalogue id, GTIN, product name, brand, maker, temperature, department, category path, lanes, pack, claim_status, market status, source class and the source each field was read from.
- Reason codes — BAD_INPUT when no query, temperature, lane, department or product type is given, or a temperature or lane is not on the list; DENY_MARKET for an unknown market.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "search_cleared_items", "arguments": {"market": "DE", "lane": "organic", "temperature": "ambient", "product_type": "pasta"}}}
get_item
- Purpose — One item by GTIN (EAN in European copy), fully resolved: temperature, department → super category → category, attribute lanes, pack, brand and maker, market, claim status, market status, availability; after the gate also ingredients, allergens, claims, origin, certificates, variants and offers.
- Inputs —
gtin(required): string;market: string;decision_id: string - Outputs — one item resolved: GTIN, temperature, department → super category → category, lanes, pack, brand and maker, market, claim_status, market status, availability block; after the gate also ingredients, allergens, claims, origin, certificates, variants and offers.
- Reason codes — BAD_INPUT when the GTIN is empty; a GTIN not on the record answers NOT_ON_GRAPH.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_item", "arguments": {"gtin": "08076809513722"}}}
verify_gtin
- Purpose — Is this GTIN real, and whose is it? Check-digit validation, the GS1 prefix, and the validated record(s) that carry it on this hub with their brand, maker and claim status.
- Inputs —
gtin(required): string - Outputs — check-digit result, the GS1 prefix, and the validated record(s) on this hub that carry the GTIN with brand, maker and claim status.
- Reason codes — BAD_INPUT when empty; a malformed GTIN answers checksum_valid false with DENY_NO_GTIN.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "verify_gtin", "arguments": {"gtin": "08076809513722"}}}
get_label_and_claims
- Purpose — What the label must say, and which claims are allowed: the labelling requirements of the market (food information, allergens, origin, nutrition) and the claims rules of record (nutrition and health claims, organic, free-from), each with its source page. Optional claims are checked against the rules.
- Inputs —
market(required): string;claims: string;product_type: string - Outputs — the label lines the market requires, the allergen rule, the national rider, the claims rules of record, and each given claim checked: deny (DENY_CLAIM), verify (a certificate or the conditions are checked by the documents lane) or no rule matched.
- Reason codes — DENY_MARKET for an unknown market.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_label_and_claims", "arguments": {"market": "FR", "claims": ["organic", "low fat"]}}}
compare_items
- Purpose — Compare items, variants and pack sizes side by side (two to eight GTINs or catalogue ids).
- Inputs —
gtins(required): array;market: string;decision_id: string - Outputs — the given items side by side: shared fields aligned, each value with its source.
- Reason codes — BAD_INPUT with fewer than two or more than eight ids.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "compare_items", "arguments": {"gtins": ["08076809513722", "08076800195057"]}}}
Chains — the buyer desks and the makers
find_verified_buyer
- Purpose — Retail buyer desks for this market: banner records (banner, parent group, channel discounter · e-commerce · local · in-store · traditional, local format, cited POS count or NULL, buyer desk by department) with their claim status — verified first, then claimed, then listed. Filters: channel, department. Takes the decision_id of an allow decision.
- Inputs —
market(required): string;decision_id(required): string;channel: string;department: string;query: string;limit: integer - Outputs — banner records for the market: banner, parent group, channel (discounter · e-commerce · local · in-store · traditional), local format, cited POS count or NULL, buyer desks by department, claim_status and source class — verified first, then claimed, then listed. Names live on the door only.
- Reason codes — DENY_NOT_VERIFIED / GATE_REQUIRED without an allow decision_id; DENY_MARKET for an unknown market; BAD_INPUT for a channel not on the list.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "find_verified_buyer", "arguments": {"market": "FR", "channel": "discounter", "decision_id": "gate_…"}}}
find_verified_supplier
- Purpose — Verified makers, brand owners, private-label owners, distributors and importers for this item or category in a market: maker records with their claim status — verified first, then claimed, then listed. Takes the decision_id of an allow decision.
- Inputs —
market(required): string;decision_id(required): string;gtin: string;query: string;lane: string;limit: integer - Outputs — maker records for the item or lane in the market: name, maker id, country, segment, lanes, site, claim_status and source class — verified first. Names live on the door only.
- Reason codes — DENY_NOT_VERIFIED / GATE_REQUIRED without an allow decision_id; DENY_MARKET for an unknown market.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "find_verified_supplier", "arguments": {"market": "DE", "lane": "organic", "decision_id": "gate_…"}}}
HITL — the auditor and documents lane
get_product_documents
- Purpose — Certificates and paper: the documents on an item record (organic certificate, PDO/PGI, halal, kosher, specification, allergen statement, origin declaration, lot where it applies), each a URL with its read date and source class; decision_id required.
- Inputs —
gtin(required): string;decision_id(required): string;market: string - Outputs — the documents on the item record: organic certificate, PDO/PGI registration, halal, kosher, specification, allergen statement, origin declaration, lot where it applies — each with URL, read date, state and source class.
- Reason codes — DENY_NOT_VERIFIED / GATE_REQUIRED without an allow decision_id; DENY_ACTOR_CLASS for an auditor; NOT_ON_GRAPH for an unknown GTIN.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_product_documents", "arguments": {"gtin": "08076809513722", "decision_id": "gate_…"}}}
log_audit
- Purpose — Write a line to the audit record for a gate decision, an order intent or a handoff (returns the line id).
- Inputs —
kind(required): string;reference_id(required): string;note: string - Outputs — an audit entry id and the UTC time it landed; the entry names the caller's client id, the kind (gate · order_intent · handoff) and the reference.
- Reason codes — DENY_UNLICENSED_AGENT without a registry bearer carrying grocery.transact; BAD_INPUT when kind is not gate, order_intent or handoff, or reference_id is not a decision_id or intent_id from this door.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "log_audit", "arguments": {"kind": "order_intent", "reference_id": "oi_…", "note": "reviewed by the buyer's compliance agent"}}}
Economics — after the gate
get_price
- Purpose — The price for this buyer: the price basis (list · contract · promotion window) from the maker's offers on the record, once gate_transaction has answered allow (decision_id). Plain $ on human surfaces. Tiers buy services, never rank.
- Inputs —
gtin(required): string;decision_id(required): string;market: string - Outputs — the price basis (list · contract · promotion window) from the maker's offers on the record for this buyer, with currency and terms and the decision_id echoed.
- Reason codes — DENY_NOT_VERIFIED / GATE_REQUIRED without an allow decision_id.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_price", "arguments": {"gtin": "08076809513722", "decision_id": "gate_…"}}}
get_availability
- Purpose — In stock, and where: the availability block of an item (banners on the shelf of record, product URLs read live at load, POS counts where the banner record carries a cited one) once gate_transaction has answered allow (decision_id).
- Inputs —
gtin(required): string;decision_id(required): string;market: string - Outputs — the availability block after the gate: banners on the shelf of record, product URLs read live at load, chains in the market and banners on the record, with the decision_id echoed.
- Reason codes — DENY_NOT_VERIFIED / GATE_REQUIRED without an allow decision_id; NOT_ON_GRAPH for an unknown GTIN.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_availability", "arguments": {"gtin": "08076809513722", "decision_id": "gate_…"}}}
create_order_intent
- Purpose — Open an order between two agents: an order intent for a cleared item after the gate; the intent carries reason_code + rule_set_version and settles by x402 on a2a-x402.ai.
- Inputs —
gtin(required): string;quantity(required): integer;decision_id(required): string;market: string;ship_to: string - Outputs — intent_id, the item, market, quantity, ship-to, the reason_code and rule set version of the gate decision it rests on, and the settlement pointer (x402).
- Reason codes — DENY_UNLICENSED_AGENT without a registry bearer carrying grocery.transact; DENY_NOT_VERIFIED without an allow decision_id or on a record that is not verified; BAD_INPUT for a quantity outside 1–100000.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "create_order_intent", "arguments": {"gtin": "08076809513722", "quantity": 240, "decision_id": "gate_…"}}}
a2a_handoff
- Purpose — Hand the buyer to the right agent over Agent-to-Agent (A2A): with an allow decision the order intent goes to the counterparty's agent (envelope + card URL); with a deny, the market agent of the ship-to — a referral, never a sale to a consumer.
- Inputs —
intent_id: string;decision_id: string;counterparty_card_url: string - Outputs — with an allow decision: the envelope (card, kid, reason code, rule set version, decision id, market) and the counterparty's card URL; with a deny: the market agent of the ship-to as a referral.
- Reason codes — DENY_UNLICENSED_AGENT without a registry bearer carrying grocery.transact; GATE_REQUIRED without a decision_id.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "a2a_handoff", "arguments": {"intent_id": "oi_…", "decision_id": "gate_…"}}}
Acts — the rules
get_market_status
- Purpose — Is this product type a food here, and under which regime? Product type by market: food, special class (novel food, food supplement, infant formula, fortified, organic-certified, alcohol excise) or not permitted — with the rule-set version and the regulator's page.
- Inputs —
product_type(required): string;market(required): string - Outputs — the product type's regime in the market (food · special_class · not_permitted), the rule set and version, the regulator and its page, and a note where the regime depends on the class.
- Reason codes — DENY_MARKET for an unknown market; BAD_INPUT for an empty product type.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_market_status", "arguments": {"product_type": "raw milk", "market": "US"}}}
get_notification_requirements
- Purpose — What must be filed before sale in a market: the food business registration or import notification scheme, the food business operator of record, and, for a product type in a special class, the extra step (novel-food authorisation, organic certification, supplement notification) — each with its source page.
- Inputs —
market(required): string;product_type: string - Outputs — the food business registration or import notification scheme before sale, the food business operator of record, the organic and novel-food regimes, the national rider, the law and the rule set version.
- Reason codes — DENY_MARKET for an unknown market.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_notification_requirements", "arguments": {"market": "JP"}}}
get_enforcement_watch
- Purpose — Recalls, withdrawals and food-safety alerts, dated: enforcement events by market, category, brand or since a date, each with its source page.
- Inputs —
market: string;query: string;since: string - Outputs — enforcement events on the record for the filter given: date, register, event class, title, the notice URL read live, and the count of events matched.
- Reason codes — BAD_INPUT when since is not YYYY, YYYY-MM or YYYY-MM-DD; DENY_MARKET for an unknown market.
- Example —
POST https://mcp.a2a-grocery.ai/mcpwith body{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_enforcement_watch", "arguments": {"market": "UK", "since": "2026-01"}}}
Troubleshooting
- The call answers DENY_MARKET. — The ship-to is not one of the 20 markets, or an EU code came without a member (use EU-DE, not EU). UK is written UK for people and stored GB in machine fields; both are accepted as input.
- The call answers DENY_NOT_VERIFIED or GATE_REQUIRED. — Buyer, supplier, price, availability, documents, order and handoff tools need the decision_id of an allow decision. Call resolve_jurisdiction → resolve_actor → gate_transaction first and pass its decision_id.
- The call answers DENY_UNLICENSED_AGENT. — The three write tools (create_order_intent, a2a_handoff, log_audit) need a registry bearer: register once at https://a2a-registry.ai/oauth/register (RFC 7591) with hub 'grocery', mint a client-credentials token at https://a2a-registry.ai/oauth/token with scope grocery.transact (grocery.read and grocery.audit are read-only and are refused by the write tools), present it as Authorization: Bearer.
- The call answers DENY_ACTOR_CLASS. — The door is trade only. public and consumer are refused; use maker, brand_owner, private_label, distributor, importer, broker, retailer_buyer, auditor (read-only) or agent (with the credential of the class it acts for).
- BAD_INPUT on an id or a filter. — Ids are never empty: a GTIN of 8–14 digits or the catalogue id from search_cleared_items; a quantity is a whole number from 1 to 100000; since is YYYY, YYYY-MM or YYYY-MM-DD; temperature is ambient · chilled · frozen · fresh; lane is organic · free-from · plant-based · artisan · origin · premium; channel is discounter · e-commerce · local · in-store · traditional.
- My client rejects the tool schemas. — The schemas are open (no additionalProperties:false, no oneOf) so the stock clients of the three major clouds attach without an adapter. The stock google-genai SDK deep-copies its config object; pass the config as a dict when attaching this door as a tool.
- I get HTTP 402 from the hub's /api. — That is the x402 offer, not an error: the metered call settles in USDC on Base through the facilitator named in the offer; the receipt is signed and resolves forever at the payments door.
- Counts on the page and in my answers differ. — Every count is produced from the record at request time; the page bakes the count at build and refreshes it from the door's health in the browser. A difference means the record moved since the last build.
Three worked prompts
A discounter's buying agent, Germany, organic dry pasta
"Find cleared organic dry pasta suppliers for a discounter in Germany, with certificates and availability."
resolve_jurisdiction(DE) → resolve_actor(retailer_buyer) → gate_transaction(DE, retailer_buyer, dry pasta) → search_cleared_items(DE, lane=organic, product_type=pasta) → find_verified_supplier(DE, lane=organic, decision_id) → get_product_documents(gtin, decision_id) → get_availability(gtin, decision_id).
A maker's export agent, France, artisan chilled
"Which retail buyer desks in France take artisan chilled products, and what does each market require on the label?"
resolve_jurisdiction(FR) → resolve_actor(maker) → gate_transaction(FR, maker, chilled hummus) → find_verified_buyer(FR, department=Deli, decision_id) → get_label_and_claims(FR, claims) → get_notification_requirements(FR).
A distributor's agent, UK, origin products
"Which protected-origin products are cleared for the UK, what must be filed before sale, and are there recalls on this category?"
resolve_jurisdiction(UK) → resolve_actor(distributor) → gate_transaction(GB, distributor, PDO cheese) → search_cleared_items(UK, lane=origin) → get_notification_requirements(UK) → get_enforcement_watch(UK, cheese).
Data handling
- What is stored — Request logs at the origin (method, path, status, timing — no message bodies); x402 receipts, agent registrations (client id, name, contact, scope), audit entries (client id, tool or skill, outcome, UTC day) and sealed review packs; the record itself (SKUs, Chains, HITL documents, Economics, Makers, Acts), every field with its source page, read date and source class.
- For how long — Origin console logs: the retention set on the Log Analytics workspace that receives them (read at the first roll). Receipts, registrations, audit entries and packs: no retention period is configured — no lifecycle policy, soft delete off — so they are kept until the operator removes them. There is no database behind this hub and no backup schedule: the record is a set of files built into the door and held on the operator's systems.
- Where — Microsoft Azure, South Central US: the door, the hub, the issuer, the payments door and one storage account for receipts, registrations, audit entries and packs. Cloudflare handles DNS, TLS, caching and request handling at the edge. Form submissions go to the form provider and the operator's mailbox. Payment verification and settlement run through the x402 facilitator on Base.
- What is never stored — Consumer data: the door is trade only and refuses public and consumer actors. Message bodies of tool calls are not logged. No name of a maker, banner or retailer renders on any web page; names live on the door and the agents only. Licensed third-party figures a tenant loads stay in that tenant's record and are never redistributed.
On the wire
- Transport — streamable-HTTP, stateless; server name
a2a-grocery; endpointhttps://mcp.a2a-grocery.ai/mcp. - Schemas — open (no additionalProperties:false, no oneOf), so the stock clients of the three major clouds attach without an adapter.
- Headers — the GSC wire envelope on every response.
- Signing — the Agent Card is signed ES256 under kid
a2ag-2026-10; keyringhttps://a2a-grocery.ai/.well-known/jwks.json. - Metering and settlement — create_order_intent settles by x402; receipts carry decision_id and reason_code and resolve at the payments door.
- Record primitive — every field is
{value, source_url, read_by, read_at, state, source_class}; counts are produced from records at request time, never typed.