A2A Cosmetics/Powered by gtinfetch.ai
DOCS · THE DOOR OF RECORD

Docs

The door at version 1.3.3: 20 tools in seven groups over one ontology, 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.

§01 · ONTOLOGY

Ontology

product × ingredient × market × actor.

  • Markets (18) — US · MX · CA · BR · UK · FR · DE · NL · IT · ES · PL · CH · AE · IN · SG · JP · KR · AU (UK is stored as ISO GB in machine fields and written UK everywhere a person reads).
  • Rule sets (13) — one per market; the six EU markets share one.
  • Actors — brand · manufacturer · retailer · distributor · 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_A_COSMETIC_HERE · DENY_INGREDIENT_BANNED · DENY_INGREDIENT_LIMIT · DENY_CLAIM · DENY_MARKET · DENY_ACTOR_CLASS · DENY_UNLICENSED_AGENT · DENY_NO_GTIN · DENY_NOT_VERIFIED.
§02 · GATE ORDER

Gate order

resolve_jurisdiction → resolve_actor → gate_transaction. No 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 (DENY_UNLICENSED_AGENT).

§03 · THE TOOLS — ONE BLOCK EACH

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. 'US', 'US-CA', 'UK', 'FR', 'EU-DE', 'JP') to one of the 18 markets and its rule set (13 rule sets; the six EU markets share one). 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), rule_set and rule_set_version, the member market when the ship-to is an EU code, and the regulator of record.
  • Reason codes — DENY_MARKET when the ship-to is not one of the 18 markets or an EU code without a member; BAD_INPUT when empty.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "resolve_jurisdiction", "arguments": {"ship_to": "US-CA"}}}

resolve_actor

  • Purpose — Who is asking? Brand, manufacturer, retailer or distributor (business to business); 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.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "resolve_actor", "arguments": {"actor_class": "retailer"}}}

gate_transaction

  • Purpose — Allow or deny, with the reason and the rule. Takes a product (GTIN or catalogue id; optional), the ship-to market, the actor class and, when no record exists yet, the product type, ingredients (INCI) and claims. Returns one of twelve reason codes (ALLOW · REQUIRE_NOTIFICATION · REQUIRE_RESPONSIBLE_PERSON · DENY_NOT_A_COSMETIC_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 · deny · require), reason_code, decision_id (carry it into every later call), rule_set and rule_set_version, market status, ingredient and claim findings where given, and the rule read.
  • Reason codes — ALLOW · REQUIRE_NOTIFICATION · REQUIRE_RESPONSIBLE_PERSON · DENY_NOT_A_COSMETIC_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-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "gate_transaction", "arguments": {"ship_to": "GB", "actor_class": "retailer", "product_type": "moisturiser"}}}

Regulatory truth — what the regulators' pages say, with the source on every field

get_ingredient_record

  • Purpose — One ingredient, and its status in every market: the standard ingredient name (INCI), aliases, and per rule set whether it is prohibited, restricted (with the limit or condition) or listed, each with the official list and its source page.
  • Inputs — ingredient (required): string
  • Outputs — the ingredient's standard name (INCI) and aliases, and one row per rule set: status (prohibited · restricted with its limit · listed · not listed), the list it comes from, source URL, read date.
  • Reason codes — BAD_INPUT when the name is empty; a name not on any list answers with empty rows, not an error.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_ingredient_record", "arguments": {"ingredient": "benzophenone-3"}}}

get_market_status

  • Purpose — Is this product type a cosmetic here? Product type by market: cosmetic, drug, special class (functional cosmetic, quasi-drug, grade 2, therapeutic good) 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 class in the market (cosmetic · drug · special_class · not_permitted), the rule set and version, the regulator and its page, and a note where the class depends on a claim.
  • Reason codes — DENY_MARKET for an unknown market; BAD_INPUT for an empty product type.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_market_status", "arguments": {"product_type": "sunscreen", "market": "US"}}}

get_notification_requirements

  • Purpose — What must be filed before sale in a market: the notification scheme, the responsible-person requirement and, for a product type in a special class, the extra step (review, registration, listing) — each with its source page.
  • Inputs — market (required): string; product_type: string
  • Outputs — the notification or registration scheme before sale, the responsible-person requirement, who files, the regulator's page and the rule set version.
  • Reason codes — DENY_MARKET for an unknown market.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_notification_requirements", "arguments": {"market": "FR"}}}

get_label_and_claims

  • Purpose — What the label must say, and which claims are allowed: the label requirements of the market and the claims rules of record (patterns that take a product out of the cosmetic class), each with its source page. Optional claims are checked against the rules.
  • Inputs — market (required): string; claims: string
  • Outputs — label lines the market requires, the allergen and INCI listing rules, the claims permitted and forbidden, each with its source and the rule set version.
  • Reason codes — DENY_MARKET for an unknown market.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_label_and_claims", "arguments": {"market": "DE", "product_type": "fragrance"}}}

get_enforcement_watch

  • Purpose — Recalls, bans and safety alerts, dated: enforcement events by market, ingredient, 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.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_enforcement_watch", "arguments": {"market": "UK", "since": "2026-01"}}}

Catalogue — only validated rows are served

search_cleared_items

  • Purpose — Find products cleared for this market: validated product records in a market (query by product name, brand, product type or lane; optional lane filter: suncare · fragrance · dermocosmetics · peptides · korean · sustainable). Only validated rows are served; each item carries its claim status and market status.
  • Inputs — market (required): string; query: string; lane: string; product_type: string; limit: integer; decision_id: string
  • Outputs — validated product rows for the market and query: catalogue id, GTIN, product type, taxonomy path, lanes, claim_status, and the source each field was read from.
  • Reason codes — BAD_INPUT when no query, lane or product type is given; DENY_MARKET for an unknown market.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "search_cleared_items", "arguments": {"market": "FR", "product_type": "sunscreen"}}}

get_item

  • Purpose — One product by GTIN, fully resolved: taxonomy path, variant or form, brand, market, claim status, market status, availability; after the gate also ingredients, claims, attributes, shades (parent and child) and offers.
  • Inputs — gtin (required): string; market: string; decision_id: string
  • Outputs — one product resolved: GTIN, taxonomy path, variant or form, market, claim_status, market status, availability block, documents, and every field's source and read date.
  • Reason codes — BAD_INPUT when the GTIN is empty; a GTIN not on the record answers not_found with the verify_gtin result.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_item", "arguments": {"gtin": "3337875597197"}}}

compare_items

  • Purpose — Compare products, variants and shades side by side (two to eight GTINs or catalogue ids).
  • Inputs — gtins (required): array; market: string; decision_id: string
  • Outputs — the given products side by side: shared fields aligned, differences marked, each value with its source.
  • Reason codes — BAD_INPUT with fewer than two or more than eight ids.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "compare_items", "arguments": {"gtins": ["3337875597197", "3337875597203"]}}}

Supply — after the gate

find_verified_supplier

  • Purpose — Verified makers and brands for this product or product type in a market: supplier records (brand · manufacturer) 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; limit: integer
  • Outputs — supplier records (brand · manufacturer) for the product or product type in the market: claim_status, site, how the row was validated and by which reader, and the source of each field. Names live on the door only.
  • Reason codes — DENY_MARKET for an unknown market.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "find_verified_supplier", "arguments": {"market": "KR", "product_type": "essence"}}}

get_availability

  • Purpose — In stock, and where: the availability block of a product (banners on the shelf of record, product URLs read live at load, store counts where the retailer record carries 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, retailers in the market and banners on the record, with the decision_id echoed.
  • Reason codes — DENY_NOT_VERIFIED without an allow decision_id; DENY_NO_GTIN when the item has none.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_availability", "arguments": {"gtin": "3337875597197", "decision_id": "gate_…"}}}

get_price

  • Purpose — The price for this buyer: the price basis (list · contract) from the supplier's offers on the record, once gate_transaction has answered allow (decision_id). Tiers buy services, never rank.
  • Inputs — gtin (required): string; decision_id (required): string; market: string
  • Outputs — the price basis (list · contract) from the supplier's offers on the record for this buyer, with currency and the decision_id echoed.
  • Reason codes — DENY_NOT_VERIFIED without an allow decision_id; DENY_UNLICENSED_AGENT for an agent without a registry bearer.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_price", "arguments": {"gtin": "3337875597197", "decision_id": "gate_…"}}}

Documents — the papers behind a product

get_product_documents

  • Purpose — Safety report, certificate of analysis, data sheet: the documents on a product record (product information file pointer, safety assessment, certificate of analysis, safety data sheet), each a URL with its read date; decision_id required.
  • Inputs — gtin (required): string; decision_id (required): string; market: string
  • Outputs — the documents on the product record: product information file pointer, safety assessment, certificate of analysis, data sheet — each with URL, read date and state.
  • Reason codes — BAD_INPUT when the id is empty.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_product_documents", "arguments": {"gtin": "3337875597197"}}}

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 and claim status.
  • Inputs — gtin (required): string
  • Outputs — check-digit result, the GS1 prefix and its licensee country, and the validated record(s) on this hub that carry the GTIN.
  • Reason codes — BAD_INPUT when empty; a well-formed GTIN not on the record answers with valid true and records empty.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "verify_gtin", "arguments": {"gtin": "3337875597197"}}}

Commerce — an order between two agents; the handoff

create_order_intent

  • Purpose — Open an order between two agents: an order intent for a cleared product 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; DENY_NOT_VERIFIED without an allow decision_id; BAD_INPUT for a quantity outside 1–100000.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "create_order_intent", "arguments": {"gtin": "3337875597197", "quantity": 12, "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 (the regulator's home page beside it) — a referral, never a sale to a consumer.
  • Inputs — intent_id: string; decision_id: string; counterparty_card_url: string
  • Outputs — the counterparty agent's card URL and A2A endpoint, the intent handed over, and a handoff id; the buyer's agent continues over A2A.
  • Reason codes — DENY_UNLICENSED_AGENT without a registry bearer; DENY_NOT_VERIFIED without an allow decision or intent.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "a2a_handoff", "arguments": {"intent_id": "oi_…"}}}

Operations — warnings and the audit line

get_safety_label

  • Purpose — Warnings and age limits: the warning statements the market requires for a product type (sunscreen, hair dye, aerosols, children's products) and the safety-alert scheme of the regulator, each with its source page.
  • Inputs — market (required): string; product_type: string
  • Outputs — the safety and information label lines for the product in the market: precautions, warnings, period-after-opening, batch and responsible-person lines, each with source.
  • Reason codes — DENY_MARKET for an unknown market; BAD_INPUT when the id is empty.
  • Example — POST https://mcp.a2a-cosmetics.ai/mcp with body {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_safety_label", "arguments": {"gtin": "3337875597197", "market": "FR"}}}

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 day file it landed in; the entry names the caller's client id, the tool or skill and the outcome.
  • Reason codes — DENY_UNLICENSED_AGENT without a registry bearer; 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-cosmetics.ai/mcp with 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"}}}
§04 · TROUBLESHOOTING

Troubleshooting

  • The call answers DENY_MARKET. — The ship-to is not one of the 18 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. — Price, availability, 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) and get_price for an agent need a registry bearer: register once at https://a2a-registry.ai/oauth/register (RFC 7591), mint a client-credentials token at https://a2a-registry.ai/oauth/token with scope cosmetics.transact (cosmetics.read and cosmetics.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 business to business. public and consumer are refused; use brand, manufacturer, retailer, distributor, auditor (read-only) or agent (with the credential of the class it acts for).
  • BAD_INPUT on an id. — 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.
  • 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.
§05 · THREE WORKED PROMPTS

Three worked prompts

A retailer's buying agent, France, sun care

"We are a French retailer. Is this sunscreen (GTIN …) a cosmetic in France, which UV filters does it carry and are they within the limits, what must our supplier have filed before sale, and can we open an order for 240 units?"

resolve_jurisdiction(FR) → resolve_actor(retailer) → gate_transaction(FR, retailer, sunscreen, gtin) → get_ingredient_record for each filter → get_notification_requirements(FR) → create_order_intent(gtin, 240, decision_id) with the registry bearer → a2a_handoff(intent_id).

A brand's export agent, Korea

"We make a functional cosmetic for whitening. Is it a functional cosmetic in Korea, what does the label have to say, which claims are allowed, and are there enforcement events on this product type?"

resolve_jurisdiction(KR) → resolve_actor(brand) → gate_transaction(KR, brand, product_type) → get_market_status(product_type, KR) → get_label_and_claims(KR, product_type) → get_enforcement_watch(KR, product_type).

A distributor's agent, United Kingdom, fragrance

"Which fragrance products are cleared for the UK, which allergens must be on the label, who are the verified suppliers, and what documents travel with the product?"

resolve_jurisdiction(UK) → resolve_actor(distributor) → gate_transaction(GB, distributor, fragrance) → search_cleared_items(UK, fragrance) → get_label_and_claims(UK, fragrance) → find_verified_supplier(UK, fragrance) → get_product_documents(gtin).

§06 · DATA HANDLING

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 (products, ingredients, suppliers, retailers, rule sets, enforcement events), every field with its source page and read date.
  • For how long — Origin console logs: 30 days (the Log Analytics workspace retention, read 2026-09-21). 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; each market agent answers from its own Azure region (or from Southeast Asia for the four markets so ruled). 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 business to business and refuses public and consumer actors. Message bodies of tool calls are not logged. No name of a brand, supplier or retailer renders on any web page; names live on the door and the agents only.
§07 · RECORD PRIMITIVE

Record primitive

Every field on every record is {value, source_url, read_by, read_at, state}; a value with no source page does not exist on this graph. Counts are produced from records at request time, never typed.

§08 · ON THE WIRE

On the wire

  • Transport — streamable-HTTP, stateless; server name a2a-cosmetics; endpoint https://mcp.a2a-cosmetics.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 a2ac-2026-09; keyring https://a2a-cosmetics.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.

The agentic commerce hub for cosmetics.

Agent-to-Agent (A2A) + Model Context Protocol (MCP) hub for cosmetics.