Endpoints: 28,729MCP servers: 18,413Payout addresses: 2,071Paid calls: 1,556Letters: 14Defects: 1,331counted 1 min ago
teppi

Server definition

Hash
sha256:be7170bb8d78f9b73b75543ba2fa063dffca4727ad28447352255e4780858190
What it is
What a remote MCP server returned when asked what it offers: 69 tools

The blob, as servednamed by its sha256

{ "instructions": "Roastify MCP — AI agent access to the Roastify coffee catalog and packaging artwork generation, monetized via Tollbooth DPYC Bitcoin Lightning micropayments.\n\n## Operating model (BLUF)\nYou do two things: read this merchant's Roastify catalog, and keep a private per-patron design LIBRARY. The Roastify API is read-only for products and design-write is browser-session-bound — but that is the shape of the TOOLS, not of the work. The merchant is the orchestrator: they create products in Roastify and Shopify, change plan tier, and author templates in Design Studio whenever the work calls for it. Design for what the merchant wants to BUILD, not for what their current catalog already holds; when a variant needs a product or plan that doesn't exist yet, say so plainly and keep going — don't treat it as a blocker or quietly narrow the work to fit.\nDivision of labor: a companion 'courier' the human runs on merchant.roastify.app does the two session-bound steps — stash a product's design up into the library, and apply a finished design back onto a product. YOU read catalog facts and edit a design's TEXT.\nTo make a branded variant of a saved design: `roastify_get_design_text(design_id)` to see the editable text layers → interview the human (read `roastify_get_catalog_product` for facts) → `roastify_update_design_text(design_id, edits={layer_id: text})` to save a NEW design → tell the human to apply it onto the product with the courier. NEVER call `roastify_fetch_design` to edit: it carries a ~2.3MB inline image and is the courier's, not yours — the field tools carry text only.\nTwo disciplines that save the merchant a manual pass: the text box does not resize, so keep each replacement within roughly ±10% of the character count of the text it replaces (`get_design_text` reports each layer's `chars`, `fontSize`, and `width`); and a stash label states INTENT, not content — a design labeled for one coffee may still hold a donor template's words, so read the layers, don't trust the name. A `tool_not_priced` error means a tool isn't registered yet, not that the patron owes anything — report it and stop; don't offer to fix pricing.\n\n## Bring your own Roastify key\nEvery patron uses their own Roastify API key. Roastify scopes the catalog, saved designs, and plan tier to the account behind the key, so this operator holds no key of its own.\n1. Call `roastify_get_patron_onboarding_status` to see what is missing.\n2. Call `roastify_request_patron_credentials` with your npub — you get a Nostr DM.\n3. Reply with your Roastify API key (a Base or Pro plan is required; find it in the Merchant App).\n4. Call `roastify_receive_patron_credentials` to vault it.\n\n## What this operator does and does not do\nIt reads the catalog, blends, variants, stock, and your own saved products, and it generates packaging artwork from designs you already saved in the Roastify Design Studio.\n\nIt does NOT create, update, delete, or sync products, and it does not place orders. Product creation and storefront sync have no Roastify API surface at all — they are Merchant App capabilities. Order placement is deliberately left to Shopify and Roastify.\n\n## Artwork is template personalization\n`roastify_generate_artwork` rewrites the named text and image placeholders of a design you authored in Design Studio. It cannot author a design from scratch, and the artwork URL it returns is not attached to a product — you carry it onward yourself. Rendering is asynchronous: `roastify_generate_artwork` hands back a job id and `roastify_artwork_status` checks it, free.\n\n## Design library (a GitHub repo YOU own)\nYour designs live in a GitHub repo you own; the operator is only the broker, committing on your behalf with a vaulted token. So you get version history, and you can browse, rename, and delete designs in GitHub itself. `roastify_stash_design` commits one, `roastify_list_designs` lists them, `roastify_fetch_design` returns one, `roastify_delete_design` removes one. Storage only — the operator never writes a design onto a Roastify product (that needs your Merchant App session, done by the browser courier).\nOne-time setup: deliver a GitHub fine-grained token (Contents read/write) and your 'owner/repo' via `roastify_request_patron_credentials` (fields `github_token`, `github_repo`). The repo needs at least one commit (a README) so its default branch exists.\n\n## Generate a branded variant in chat\nTo spin a new product from a saved design without moving megabytes of artwork: call `roastify_get_design_text` to see the design's editable text layers (id + current text + font — no images), interview the patron or read a catalog item (`roastify_get_catalog_product`) for the facts, then call `roastify_update_design_text(design_id, edits={layer_id: text})` to save a new, text-edited design. The patron applies that new design onto the new product with the browser courier. The original is never changed and the image never leaves the library.\n`get_design_text` also returns `elements` (non-text: background art, rules, a graphic roast scale), `panels` (the box's front/back/left/right columns), a real `face` per item, and per-layer geometry. A header with no text value is NOT necessarily a defect — its value may be a graphic in `elements`, and a layer may not print at all; don't report a missing value as a production error. Dimensions are MEASURED text bounds, not fixed frames: text grows rather than clips, so hold a layer's line count and longest line and its footprint won't change.\nTo ADD copy to an empty region (e.g. a bare side panel), `roastify_add_design_element(design_id, face, text, style_from, position)` places a new text element that inherits an existing layer's typography and is refused if it leaves the panel or overlaps anything. Position it absolutely {x,y} in an empty panel, or relative to a sibling {below: layer_id, gap: G}.\n\n## Pricing\nTool prices are set dynamically by the operator's pricing model. Use `roastify_check_price` to preview costs and `roastify_check_balance` to see your balance.\n\n## Troubleshooting Tool Failures\n\nWhen a tool call fails, **read the error message** — it tells you what went wrong and what to do next. Common causes:\n\n- **proof is required** → Call `request_npub_proof` then `receive_npub_proof` to prove npub ownership. The cache expires after ~1 hour; renew with a fresh request/receive cycle.\n- **Insufficient credit balance** → Call `purchase_credits` to top up.\n- **Operator credentials not delivered yet** → This is a lifecycle state, not an error. The operator needs to deliver API credentials via Secure Courier. Do NOT tell the patron to 'fix the deployment' or 'inject environment variables' — credential delivery is a Secure Courier flow, not a platform configuration issue.\n- **Service unavailable / persistence layer unreachable / only bootstrap tools** → The MCP is warming up after idle (serverless cold start). This is normal — retry in 10-15 seconds. Do NOT diagnose as an infrastructure outage or suggest the operator check their deployment. The second request almost always succeeds.\n- **Upstream API authentication failure** → Only if the error explicitly mentions token expiry or an upstream auth error.\n\n**IMPORTANT — Don't Pester Your Customer:** Do NOT ask the patron to re-authenticate, re-authorize, or re-do OAuth unless the error message specifically says the upstream token is expired or invalid. Token expiry is only one of many possible causes. Speculatively re-running OAuth or credential flows wastes the patron's time and is the opposite of what DPYC stands for.\n\n## Secure Courier — Human in the Loop\n\nAll Secure Courier flows (`request_credential_channel` / `receive_credentials`, `request_npub_proof` / `receive_npub_proof`) require a human to read a Nostr DM and reply manually. After calling a `request_*` tool, **STOP and wait** for the user to confirm they have replied. Do NOT poll, retry, or speculatively call `receive_*` — each receive call **destructively drains** all DMs from the relay. If you call it before the human replies, their message will be lost. One request, one wait, one receive.", "tools": [ { "description": "Generate a patron's account statement at this operator.\n\nReturns the patron's purchase history, active credit tranches,\nper-tool usage breakdown, and recent daily usage logs. This is\nthe patron's spending account — not the operator's Authority\ntax balance.\n\nFree — no credits consumed. Proof of npub ownership is required\nto prevent statement-scraping of arbitrary patrons.", "inputSchema": { "additionalProperties": false, "properties": { "days": { "default": 30, "description": "Number of days of daily usage history to include (default 30).", "type": "integer" }, "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" } }, "required": [ "npub", "dpop_token" ], "type": "object" }, "name": "roastify_account_statement", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Generate a visual SVG infographic of your account statement.\n\nReturns the same data as account_statement, rendered as a dark-themed\nSVG graphic with balance hero, metrics cards, health gauge, tranche\ntable, and tool usage breakdown. Costs 1 api_sat per call. Proof is\nverified by ``debit_or_deny`` before any cost is incurred.", "inputSchema": { "additionalProperties": false, "properties": { "days": { "default": 30, "description": "Number of days of daily usage history to include (default 30).", "type": "integer" }, "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The Nostr public key (npub1...) whose statement to render.", "type": "string" } }, "required": [ "npub", "dpop_token" ], "type": "object" }, "name": "roastify_account_statement_infographic", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Add a new element (text or image) to a design and commit a new version of it.\n\nThe store is configuration management: the element is committed back to the SAME\ndesign_id (git tracks the diff). The new element's id comes back, so you can\nimmediately edit a text element by id with update_design_text, or move/reorder\neither kind with move_elements.\nPlacement is validated server-side and REFUSED, not warned: the element must fall\ninside the named panel (with a default margin — the dieline carries no real safe\narea) — or, when the product has no panels (Tubes / continuous wrap), inside the\ndesign sheet — and must not overlap any existing element (the collider's id is\nnamed). Typography is inherited for text, so a new text element matches the\ntemplate. Image creation reuses a src already present in the design (no new asset\nupload) — pass `kind=\"image\"` with `src_from` (an existing image id) or `src` that\nalready appears on an image.", "inputSchema": { "additionalProperties": false, "properties": { "client_req_id": { "default": "", "description": "Your idempotency key.", "type": "string" }, "commit_message": { "default": "", "description": "A specific description of WHAT changed and WHY — a real commit\nmessage, not a placeholder like 'save this' or 'update'. Required.", "type": "string" }, "design_id": { "description": "The design to add to, from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "face": { "default": "", "description": "The panel to place it on — one of the `panels` from get_design_text\n(e.g. \"right\"). On multi-panel boxes the element must fall inside that\npanel; face is required there. On single-face products (Tubes: `panels`\nis empty) any face is accepted and containment uses the design's sheet\nbounds instead.", "type": "string" }, "height": { "default": 0, "description": "Image frame height in design units (kind=\"image\" only). Defaults to\nthe donor image's height when src_from is set.", "type": "integer" }, "kind": { "default": "text", "description": "\"text\" (default) or \"image\".", "type": "string" }, "label": { "default": "", "description": "Rename the design (optional). Defaults to keeping its current label.", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "position": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "description": "Where to place it. Either absolute {\"x\": N, \"y\": M} (top-left in\ndesign units), or relative to an existing element:\n{\"below\"|\"above\"|\"rightOf\"|\"leftOf\": \"layer_id\", \"gap\": G}. Relative is\nbest when there's a sibling to anchor to; an empty panel needs absolute." }, "src": { "default": "", "description": "An image src already present in this design (kind=\"image\"); alternative\nto src_from. New uploads are refused — only reuse an asset already on\nthe design.", "type": "string" }, "src_from": { "default": "", "description": "Existing IMAGE element id whose src to copy (kind=\"image\").", "type": "string" }, "style_from": { "default": "", "description": "An existing TEXT layer id (from get_design_text) whose font,\nsize, weight, colour, alignment, and leading the new text element inherits.\nRequired when kind=\"text\".", "type": "string" }, "text": { "default": "", "description": "The element's text (\\n for line breaks). Required when kind=\"text\".", "type": "string" }, "version_tag": { "default": "", "description": "The NEXT semver version (MAJOR.MINOR.PATCH, e.g. 1.3.0, no 'v') — call\nroastify_list_design_versions and increment. Required; reusing one is refused.", "type": "string" }, "width": { "default": 0, "description": "The text box (wrap) width, or the image frame width, in design units.\nDefaults to the panel width minus margins for text; required for images\nwhen not defaulting from the donor.", "type": "integer" } }, "required": [ "design_id" ], "type": "object" }, "name": "roastify_add_design_element", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check this operator's adoption-request status at a chosen Authority.\n\nFree. Polls the Authority MCP-to-MCP for the status of this operator's\nrequest (pending / approved / rejected / provisioned).", "inputSchema": { "additionalProperties": false, "properties": { "authority_npub": { "type": "string" }, "dpop_token": { "default": "", "type": "string" } }, "required": [ "authority_npub" ], "type": "object" }, "name": "roastify_adoption_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check a Roastify artwork job. Free — polling never costs anything.\n\nA finished job carries ``artwork_url``; a failed one carries ``error``.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "job_id": { "description": "The job id returned by roastify_generate_artwork.", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "roastify_artwork_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Browse the Roastify catalog: products and coffee blends together.\n\nPlan-gated items are returned with their tier marked, not hidden.\n\nNote that Roastify's catalog carries no origin, altitude, processing, or\nvarietal data — those live only in the Merchant App UI. Roast level and\ndecaf status come from the blend.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "type": "object" }, "name": "roastify_browse_catalog", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check this operator's tax balance at the Authority.\n\nReturns the sats available for certifying patron credit purchases.\nWhen this balance reaches zero, patron top-ups cannot be certified\nand the operator must call purchase_credits on the Authority.\n\nThis is the operator's own funding — not a patron balance. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_check_authority_balance", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check a patron's credit balance at this operator.\n\nThis is the patron's spending balance — credits purchased via\nLightning for tool calls at this operator. For the operator's\nown balance at the Authority (needed to certify patron purchases),\nuse authority_check_balance instead.\n\nFree — no credits required. Proof of npub ownership is required\nto prevent anyone-with-the-registry from enumerating balances.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The Nostr public key (npub1...) whose balance to check.", "type": "string" } }, "required": [ "npub", "dpop_token" ], "type": "object" }, "name": "roastify_check_balance", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check the payment status of a Lightning invoice.\n\nCall after paying the invoice from purchase_credits.\nFree — no credits required. Proof of npub ownership is required\nto prevent credit-grant front-running by an observer of the\ninvoice ID.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "invoice_id": { "description": "The invoice ID returned by purchase_credits.", "type": "string" }, "npub": { "description": "The Nostr public key (npub1...) that purchased the invoice.", "type": "string" } }, "required": [ "invoice_id", "npub", "dpop_token" ], "type": "object" }, "name": "roastify_check_payment", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Preview the effective cost of a tool call.\n\nShows the base cost and any constraint effects (discounts, free\ntrials, surge pricing). Free — no credits required.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "type": "string" }, "tool_id": { "description": "Either the tool's UUID (from the pricing model) or a\nbare capability string (e.g. ``\"deal_scenario\"``). FE\ncallers usually have the capability name; this resolves\nboth so the FE doesn't need to derive UUIDs locally.", "type": "string" }, "tool_kwargs": { "default": "", "description": "Optional JSON object with tool call parameters\nfor ad valorem / categorical-multiplier pricing preview\n(e.g. '{\"amount_sats\": 5000}' or\n'{\"difficulty\": \"sovereign\", \"mode\": \"live\"}').", "type": "string" } }, "required": [ "tool_id" ], "type": "object" }, "name": "roastify_check_price", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check whether a proof grant is still valid.\n\nMirrors ``check_oauth_status`` for the npub-proof flow: a calling\nagent can ask \"will my next paid call accept this dpop_token?\"\nbefore burning credits on a guaranteed failure. Expiry is read from\nthe grant itself; the only store consulted is the patron's\nrevocation watermark.\n\nFree, no side effects — touches no relay.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "description": "Required. The grant envelope returned by\n``receive_npub_proof``.", "type": "string" }, "patron_npub": { "default": "", "description": "Required. The patron's npub (npub1...).", "type": "string" } }, "type": "object" }, "name": "roastify_check_proof_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check Roastify stock for one SKU, or the whole stock list.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "sku": { "default": "", "description": "A variant SKU. Omit to get the full stock list.", "type": "string" } }, "type": "object" }, "name": "roastify_check_stock", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Delete a coupon. Cascades to all patron redemptions.\n\nAny chain step referencing the deleted coupon_id becomes a\nno-op (the constraint returns neutral on unknown ids) — the\nStudio surfaces orphan references as warnings.\n\nRESTRICTED to operator — requires proof.", "inputSchema": { "additionalProperties": false, "properties": { "coupon_id": { "type": "string" }, "dpop_token": { "default": "", "type": "string" } }, "required": [ "coupon_id" ], "type": "object" }, "name": "roastify_delete_coupon", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Delete one of your stored designs.", "inputSchema": { "additionalProperties": false, "properties": { "design_id": { "description": "The id from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "required": [ "design_id" ], "type": "object" }, "name": "roastify_delete_design", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Remove a single operator secret field.\n\nDeletes one key from the operator's encrypted credential blob without\ntouching the others — the field-level counterpart to\n``forget_credentials``, which wipes the whole row. Use it to retire a\nleftover after an SDK cutover (a Prefect key after Modal, or a stored\nbut untemplated orphan like ``anthropic_api_key``) without taking the\noperator down for a full re-delivery.\n\nStored-but-untemplated fields are first-class: the delete is keyed on\nwhat is vaulted, not on what the current template declares. Idempotent\n— already-absent fields report ``removed: false`` without rewriting\nthe vault. RESTRICTED to the operator — requires proof (nsec-signed\nkind-27235 or a cached dpop_token phrase); patron proofs are rejected.\nA deletion is as destructive as a write.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Operator proof for this tool.", "type": "string" }, "field": { "description": "The operator credential field to remove (templated or not).", "type": "string" } }, "required": [ "field", "dpop_token" ], "type": "object" }, "name": "roastify_delete_operator_credential", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Remove a single patron credential field.\n\nDeletes one field from stored credentials without affecting\nother fields. Free. Proof of npub ownership is required —\nthis is a write to the patron's sensitive credential vault.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "field": { "description": "The credential field name to remove.", "type": "string" }, "npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" } }, "required": [ "npub", "dpop_token", "field" ], "type": "object" }, "name": "roastify_delete_patron_credential", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Fetch one of your stored designs in full, with its images re-inlined.", "inputSchema": { "additionalProperties": false, "properties": { "design_id": { "description": "The id from roastify_stash_design or roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "ref": { "default": "", "description": "Optional git ref (a commit sha or version tag) to fetch a specific\nversion — from roastify_list_design_versions. Omit for the latest.", "type": "string" } }, "required": [ "design_id" ], "type": "object" }, "name": "roastify_fetch_design", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Remove a coupon from this patron's redemption list.\n\nCosmetic only — the coupon itself still exists at the operator,\nand the patron can re-redeem the same code later while the\nwindow allows. Free — requires proof of ``npub``.", "inputSchema": { "additionalProperties": false, "properties": { "coupon_id": { "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "type": "string" } }, "required": [ "npub", "coupon_id" ], "type": "object" }, "name": "roastify_forget_coupon", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Delete vaulted credentials for a specific service and npub.\n\nFor operator credentials, pass the operator's own npub. For patron\ncredentials, pass the patron's npub. Always requires proof of\nnpub ownership — a deletion is as destructive as a write.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The Nostr public key (npub1...) whose credentials to forget.", "type": "string" }, "service": { "description": "The credential service to forget.", "type": "string" } }, "required": [ "service", "npub", "dpop_token" ], "type": "object" }, "name": "roastify_forget_credentials", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Generate packaging artwork from one of your saved Design Studio designs.\n\nThis personalizes a template you already authored: it rewrites that design's\nnamed text and image placeholders. It cannot author a design from scratch, and\nthe artwork it produces is NOT attached to a product — Roastify's API has no\nproduct-create or storefront-sync surface. You get an artwork URL and carry it\nonward yourself.\n\nRoastify renders asynchronously, so this returns a job id straight away. Check\nit with roastify_artwork_status, which is free.", "inputSchema": { "additionalProperties": false, "properties": { "client_req_id": { "default": "", "description": "Your own idempotency key. Reusing it makes a repeated\nrequest safe — Roastify will not generate the artwork twice.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "fields": { "description": "Placeholders to rewrite, each\n{\"fieldId\": \"placeholder_title\", \"type\": \"text\"|\"image\",\n \"value\": \"...\"}. An image value must be an https:// URL.", "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "product_id": { "description": "A saved design's id, from list_my_products.", "type": "string" } }, "required": [ "product_id", "fields" ], "type": "object" }, "name": "roastify_generate_artwork", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get one coffee blend: roast level, decaf status, and its variants.", "inputSchema": { "additionalProperties": false, "properties": { "blend_id": { "description": "The blend id from browse_catalog.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "required": [ "blend_id" ], "type": "object" }, "name": "roastify_get_blend", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get one catalog product with its variants, sizes, prices, and dieline.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "product_id": { "description": "The catalog product id from browse_catalog.", "type": "string" } }, "required": [ "product_id" ], "type": "object" }, "name": "roastify_get_catalog_product", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List the editable text layers of a stored design — the fields you can change.\n\nReturns each text layer's id, current text, `chars` (its length), font, `align`,\nbox geometry (`fontSize`, `width`, `height`), and `z` (paint-order index in the\ntop-level `elements` array) — but NOT the design's images, so it stays small\nenough to reason over in a conversation. Each layer's current text is its own\nlabel: infer its role (product name, tagline, story, recipe, tasting notes, …)\nfrom the words it holds. The same name often appears in several layers and\ninside longer blurbs; change every id that should carry it.\n\nAlso returns `sheet` (the overall design extent), `panels` (the box's panel\ncolumns — front/back/left/right — recovered from the dieline, each with bounds;\nempty for single-face products like Tubes, where placement uses `sheet` instead),\nand a real `face` per layer/element (which panel its x-centre sits on, not the\nconstant \"sheet\"; when panels is empty, layers keep their native faceId such as\n\"wrap\"). And `elements` — the NON-text elements (images, shapes, rules), each with\nid, type, name, bounds, `z`, and its `fill`/`stroke` colours — so a roast scale can\nbe audited (a filled dot has a dark `fill`, an empty one none) and set with\nroastify_move_elements. `fonts` lists the families loaded on the design plus any\nfamily a layer already uses — pick from it when setting `fontFamily` via move_elements.\nRead those before judging the design: a header with no text value beneath it is\nNOT necessarily a defect — the value may be a graphic in `elements` (e.g. a\nfive-dot roast scale under a ROAST header), and it tells you where NOT to place\nnew text. Roastify's migrated format carries no visibility flag, so a layer that\nexists may still not print — do not report a missing value as a production error.\n\nGeometry: each layer's `x`/`y` is its top-left corner in design units (the sheet\norigin is its top-left). `width`/`height` are MEASURED text bounds, not fixed\nframes — text does not clip, it grows, so a revision that holds the line count\nand longest-line length keeps the footprint. `z` is paint order (lower draws\nfirst / behind); change it with roastify_move_elements `{\"id\", \"z\": \"front\"|\"back\"|N}`.\n\nTwo things to respect:\n- A stash label states INTENT, not content: a design labeled for one coffee may\n still hold a donor template's words. Trust these layers, not the label.\n- `width` is the fixed wrap frame; `height` is the grown extent and re-measures\n when you edit the text. Keep each replacement within roughly ±10% of the\n layer's `chars`; longer copy grows the box downward and can overrun its\n neighbour, which the merchant then fixes by hand. `fontSize`/`width` gauge how\n tight a layer is; to change a label's `fontSize`, wrap frame, `align`, or\n `fontFamily`, use roastify_move_elements.\n\nAlso returns `description` — the product's store-page prose (outside the design,\nsyncs to Shopify), versioned with the design. Refine it and write it back with\nroastify_set_product_description.\n\nPair with roastify_update_design_text to save text changes.", "inputSchema": { "additionalProperties": false, "properties": { "design_id": { "description": "The id from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "required": [ "design_id" ], "type": "object" }, "name": "roastify_get_design_text", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get one of your saved product designs in full, with all its variants.\n\nThe coffee's IDENTITY (which blend) is not a named field — it is encoded in the\nvariant SKU, e.g. `COF-WHB-12O-HGL-BOX` → `HGL` → the High Lakes blend. Decode\nthe SKU before writing origin/roast copy: a product's title can say one thing\nwhile its SKU is really a different blend.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "product_id": { "description": "Your product id from list_my_products.", "type": "string" } }, "required": [ "product_id" ], "type": "object" }, "name": "roastify_get_my_product", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Read an npub's public Nostr profile (NIP-01 kind-0 metadata).\n\nFree, no proof — the data is already public on relays. Returns the\nlatest metadata fields (name, display_name, about, picture, banner,\nnip05, website, lud16) or an empty profile if none is published.", "inputSchema": { "additionalProperties": false, "properties": { "npub": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_get_nostr_profile", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Generate a Merkle inclusion proof that a patron's balance was\nincluded in a Bitcoin-notarized snapshot.", "inputSchema": { "additionalProperties": false, "properties": { "notarization_id": { "description": "The notarization record ID.", "type": "string" }, "npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" } }, "required": [ "notarization_id", "npub" ], "type": "object" }, "name": "roastify_get_notarization_proof", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Report this operator's configuration readiness.\n\nShows which operator settings are configured, which are missing,\nand how to deliver each missing value. For patron-level credential\nstatus, use get_patron_onboarding_status instead. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_get_operator_onboarding_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List stored patron credential field names (not values).\n\nReturns the names of fields stored for a patron, plus each\nfield's ``delivered_at`` ISO-8601 timestamp when known (null\nfor secrets vaulted before timestamps were recorded). Values\nare never exposed — use this to verify which fields are\nconfigured and how old each one is. Free. Proof of npub\nownership is required: the list of configured fields is itself\nsensitive (reveals which integrations a patron has set up).", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" } }, "required": [ "npub", "dpop_token" ], "type": "object" }, "name": "roastify_get_patron_credential_fields", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Report a patron's credential readiness for this operator.\n\nFor set-once services (eXcalibur, TheBrain), shows which patron\nsecrets are configured and which are missing. For dynamic/OAuth2\nservices (Schwab), reports that no patron credentials are needed.\nFree. Proof of npub ownership is required because credential\npresence is sensitive information about the patron's setup.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "A kind-27235 Nostr event signed by patron_npub for this tool.", "type": "string" }, "patron_npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" } }, "required": [ "patron_npub", "dpop_token" ], "type": "object" }, "name": "roastify_get_patron_onboarding_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get the active pricing model for this operator. Free.\n\nIf no model exists, self-initializes a scaffold with all\nregistered tools at 0 sats. No economic data from code.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_get_pricing_model", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Return canonical (tool_id, mcp_name, …) for every tool the wheel exposes.\n\nThe authoritative source for any client (Studio, agents, FE)\nthat needs to know how this MCP identifies its tools.\nReconcile uses this output to UUID-join against the stored\npricing model — no name-based UUID derivation, no guessing.\n\nIncludes both ToolIdentity-seeded tools and any UUID recorded by\n``@paid_tool`` that is missing from the registry. The latter appear\nwith ``registered: false`` (and in the top-level ``unregistered``\narray) so Reconcile can flag deploy drift instead of silently\nreporting clean when a live tool was never seeded (#174).\n\nIf the operator renames a function or rebrands a slug, the\nmcp_name in this output changes but tool_id stays. That's the\nwhole point of the canonical-UUID design.\n\nAlso diffs the live FastMCP wire surface against the registry.\nTools exposed on the wire but absent from the registry appear in\n``unregistered`` so Reconcile can flag deploy drift instead of\nsilently under-reporting (issue #175).\n\nFree, no side effects.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_list_canonical_identities", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List all available constraint types and their parameter schemas.\n\nReturns the type, category, description, and parameter specs for\nevery constraint that can be used in a pricing pipeline.\nFree — no credits required.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_list_constraint_types", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List every coupon this operator has minted (newest first).\n\nEach row carries the current ``times_redeemed`` counter — the\nStudio renders a progress bar from this against ``total_uses``.\nRESTRICTED to operator — requires proof.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_list_coupons", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List a stored design's committed versions, newest first — the git history.\n\nEach version carries its `sha`, `short_sha`, `date`, commit `message`, `tag` (the\nversion tag if one was set at Commit), and `commit_url`. Pass a version's `sha` (or\n`tag`) as the `ref` to roastify_fetch_design to fetch that exact version.", "inputSchema": { "additionalProperties": false, "properties": { "design_id": { "description": "The id from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "required": [ "design_id" ], "type": "object" }, "name": "roastify_list_design_versions", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List your stored designs — metadata only, newest first.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "type": "object" }, "name": "roastify_list_designs", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List the coupons this patron has redeemed on this operator.\n\nReturns both active and exhausted redemptions with a per-row\n``status`` (``active`` / ``window_closed`` / ``patron_limit`` /\n``total_limit``). Free — requires proof of ``npub``.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "npub": { "type": "string" } }, "required": [ "npub" ], "type": "object" }, "name": "roastify_list_my_coupons", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List your own saved Roastify product designs, one page at a time.\n\nReturns has_next_page and end_cursor so you can tell a page from a\ncomplete list.", "inputSchema": { "additionalProperties": false, "properties": { "cursor": { "default": "", "description": "Page cursor from a previous call's end_cursor. Omit for page 1.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "limit": { "default": 20, "description": "Items per page (1-100, default 20).", "type": "integer" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" } }, "type": "object" }, "name": "roastify_list_my_products", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "List recent Bitcoin notarization records.", "inputSchema": { "additionalProperties": false, "properties": { "limit": { "default": 20, "description": "Maximum records to return (default 20).", "type": "integer" }, "status": { "default": "", "description": "Optional filter (e.g., 'submitted', 'confirmed').", "type": "string" } }, "type": "object" }, "name": "roastify_list_notarizations", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Create a new operator-owned discount coupon.", "inputSchema": { "additionalProperties": false, "properties": { "discount_percent": { "description": "Percentage off the base price (0-100).", "type": "number" }, "dpop_token": { "default": "", "type": "string" }, "name": { "description": "The catchy code patrons type to redeem\n(operator-scoped uniqueness).", "type": "string" }, "total_uses": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "description": "Aggregate cap across all patrons (default None =\nunlimited)." }, "uses_per_patron": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": 1, "description": "How many tool calls one patron can claim the\ndiscount on (default 1; pass null/None for unlimited\nwithin the window)." }, "valid_from": { "description": "ISO-8601 datetime when the coupon becomes active.", "type": "string" }, "valid_until": { "description": "ISO-8601 datetime when the coupon expires.", "type": "string" } }, "required": [ "name", "discount_percent", "valid_from", "valid_until" ], "type": "object" }, "name": "roastify_mint_coupon", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Move a group of elements together and/or resize elements; commit a new version.\n\nThe Designer can move only one layer at a time, so a block of layered content\n(a spec panel, a logo lockup) drifts out of alignment when its backing shape is\nmoved alone. This relocks that block: name the ids and shift them as one rigid\nobject, and separately re-centre or resize individual rectangles. The store is\nconfiguration management: the edit is committed back to the SAME design_id (git\ntracks the diff). Apply it onto the product with the browser courier.\n\nNothing is validated against panel bounds here (unlike add_design_element): you\nare re-aligning existing, deliberately-placed content, so the caller owns the\ncoordinates. The heavy background image is never moved unless you name its id.", "inputSchema": { "additionalProperties": false, "properties": { "commit_message": { "default": "", "description": "A specific description of WHAT changed and WHY — a real commit\nmessage, not a placeholder like 'save this' or 'update'. Required.", "type": "string" }, "design_id": { "description": "The design to edit, from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "edits": { "description": "A list of geometry edits, each one of:\n- group shift: {\"ids\": [\"a\", \"b\", ...], \"dx\": N, \"dy\": M} — add the same\n delta to every listed element's x/y (design units; +dy is down, +dx is\n right). Use this to move a whole block together.\n- absolute set: {\"id\": \"a\", \"x\": ?, \"y\": ?, \"width\": ?, \"height\": ?,\n \"fontSize\": ?, \"fill\": ?, \"stroke\": ?, \"align\": ?, \"fontFamily\": ?,\n \"z\": ?} — set only the keys you include.\n What the size keys mean depends on the element: on a RECTANGLE/line/image,\n width and height are the frame and set directly; on a TEXT layer, width is\n the wrap frame and fontSize the type size (both settable) while height is\n DERIVED — it re-measures from the reflowed text, and a height you pass for a\n text layer is ignored. Use fontSize to match one label's size to a peer.\n align (left|center|right|justify) and fontFamily apply to TEXT only — use\n them when a repurposed layer still carries a donor's right-align or face\n (read `fonts` from get_design_text for known families). fill/stroke are\n colour strings settable on any element — e.g. give a roast-scale dot a dark\n fill to fill it, or clear the fill to empty it (read each dot's current fill\n from roastify_get_design_text's `elements`). z reorders paint order in the\n elements array: an integer index, or \"front\" / \"back\".\nGet element ids and their current geometry from roastify_get_design_text.", "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, "label": { "default": "", "description": "Rename the design (optional). Defaults to keeping its current label.", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "version_tag": { "default": "", "description": "The NEXT semver version (MAJOR.MINOR.PATCH, e.g. 1.3.0, no 'v') — call\nroastify_list_design_versions and increment. Required; reusing one is refused.", "type": "string" } }, "required": [ "design_id", "edits" ], "type": "object" }, "name": "roastify_move_elements", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Build a Merkle tree of all patron balances and submit the root\nto Bitcoin via OpenTimestamps.\n\nOperator-only background task. Bitcoin confirmation takes 1-6 hours.\nFree — no credits required.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_notarize_ledger", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Describe the DPYC ecosystem via the Oracle. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_oracle_about", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get the current DPYC certification tax rate. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_oracle_get_tax_rate", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get DPYC onboarding instructions from the Oracle. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_oracle_how_to_join", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Look up a DPYC community member by npub. Free.", "inputSchema": { "additionalProperties": false, "properties": { "npub": { "type": "string" } }, "required": [ "npub" ], "type": "object" }, "name": "roastify_oracle_lookup_member", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Get active network advisories from the Oracle. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_oracle_network_advisory", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Publish a CLIENT-SIGNED kind-0 profile to relays for an npub.\n\nThe wheel never holds a patron nsec. The frontend signs the kind-0\nmetadata event with the patron's session key or a NIP-07 extension and\npasses the signed event (JSON) here; the wheel verifies the signature\nmatches the npub, then relays it to public relays. The signature is the\nauthorization — no proof token, no key custody. Free.", "inputSchema": { "additionalProperties": false, "properties": { "npub": { "default": "", "description": "The patron's Nostr public key the event must be signed by.", "type": "string" }, "signed_event": { "default": "", "description": "A JSON-encoded, client-signed kind-0 event.", "type": "string" } }, "type": "object" }, "name": "roastify_publish_nostr_profile", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Buy credits via Bitcoin Lightning.\n\nCreates a Lightning invoice. Pay it with any Lightning wallet,\nthen call check_payment to confirm. Proof of npub ownership is\nrequired so credits land in the correct ledger.\n\nFree — no credits required to call.", "inputSchema": { "additionalProperties": false, "properties": { "amount_sats": { "default": 1000, "description": "Satoshis to purchase (default 1000).", "type": "integer" }, "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "The Nostr public key (npub1...) the credits will fund.", "type": "string" } }, "required": [ "npub", "dpop_token" ], "type": "object" }, "name": "roastify_purchase_credits", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Pick up credentials from the Secure Courier.\n\nCompletes the CREDENTIAL-DELIVERY flow (the ownership-proof\ncounterpart is ``receive_npub_proof``).\n\n**Call this only after the user confirms they have replied.**\nDeterministic, one-shot retrieval: name the response you want with\n``(sender_npub, service, dpop_token)`` and the tool drains ONLY the\nrendezvous relay that channel was pinned to. Every popped DM with the\nwrong session phrase is deleted and its sender is NACK'd; the first DM\nwith the matching phrase is accepted (ACK'd) and the scan stops. If\nnone match, the queue is drained and a ``courier_not_found`` result is\nreturned. Do NOT poll, loop, or retry.\n\nIf a credential_card (ncred1...) is provided, it is redeemed directly\nwithout any relay access (dpop_token not required for that path). On\nsuccess, the payment processor client is reinitialized from the new\ncredentials — no server restart needed.", "inputSchema": { "additionalProperties": false, "properties": { "credential_card": { "default": "", "description": "Optional. An ncred1... card to redeem directly\n(bypasses the relay drain; dpop_token not needed).", "type": "string" }, "dpop_token": { "default": "", "description": "Required. The session phrase returned by\nrequest_credential_channel for this exact channel.", "type": "string" }, "sender_npub": { "default": "", "description": "Required. The npub that sent the credentials.", "type": "string" }, "service": { "default": "", "description": "Required. The credential service name (must match\nthe service used in request_credential_channel).", "type": "string" } }, "type": "object" }, "name": "roastify_receive_credentials", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Receive npub ownership confirmation from a patron.\n\nCompletes the npub-OWNERSHIP-PROOF flow (the credential-delivery\ncounterpart is ``receive_credentials``).\n\n**Call this only after the user confirms they have replied.**\nDeterministic, one-shot retrieval: name the response with\n``(patron_npub, dpop_token)`` — the ``dpop_token`` being the value\nreturned by ``request_npub_proof``. The tool drains ONLY the pinned\nrendezvous relay that challenge was published on, stopping at the DM\nwhose phrase matches. Mismatched DMs are deleted and NACK'd (without\nrevealing the expected phrase). If called before the user replies,\ntheir message will never be found. Do NOT poll, loop, or retry.\n\nThe signed DM itself proves npub ownership (the patron's nsec\nsigned it). On success, returns a new ``dpop_token``: an\nOperator-signed **proof grant envelope** ``{\"grant\": …, \"nonce\": …}``\nbound to the patron, this challenge, and the duration the patron\nnamed. The calling application MUST remember that whole string and\npass it as ``dpop_token`` on every subsequent paid tool call. The\ngrant is self-verifying; nothing about it is stored here. Free.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "description": "Required. The dpop_token returned by request_npub_proof.", "type": "string" }, "patron_npub": { "default": "", "description": "Required. The patron's npub to receive proof from.", "type": "string" } }, "type": "object" }, "name": "roastify_receive_npub_proof", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Pick up patron credentials from the Secure Courier.\n\nDeterministic, one-shot retrieval: name the response with\n``(sender_npub, dpop_token)`` and the tool drains ONLY the pinned\nrendezvous relay for that channel, stopping at the matching DM.\nProvide an ncred1... credential_card to redeem directly instead\n(dpop_token not required for that path). Do NOT poll or retry.\nFree.", "inputSchema": { "additionalProperties": false, "properties": { "credential_card": { "default": "", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "sender_npub": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_receive_patron_credentials", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Claim a coupon by its name (the code the operator shared).\n\nLooks up the operator's coupon by ``code``, validates the window\nand total cap, and records a per-patron redemption row.\nSubsequent paid tool calls on this MCP auto-apply the discount\nuntil ``uses_per_patron`` is exhausted.\n\nFree — no credits required. Requires proof of ``npub``.\nIdempotent: redeeming the same code twice returns the existing\nredemption.", "inputSchema": { "additionalProperties": false, "properties": { "code": { "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "npub": { "type": "string" } }, "required": [ "npub", "code" ], "type": "object" }, "name": "roastify_redeem_coupon", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "File a field report about this service as a GitHub issue on the operator's repo.\n\nFound a tool's metadata or response wrong or confusing? Report it where the tool\nlives. The **author of record is your npub** — no npub / no proof, no issue — and it\nis stamped into the issue so the report is attributed to you, not the operator. Costs\na small fee (a free write to an issue tracker would be abused). The report is PUBLIC\nand goes to the maintainers' normal triage; nothing is verified here.\n\nReturns the filed issue's repo, number, and url. If this operator has not enabled\nfield reports, returns an \"issue reporting not configured\" situation and you are not\ncharged.", "inputSchema": { "additionalProperties": false, "properties": { "body": { "description": "The details — which tool, what was wrong, what you expected.", "type": "string" }, "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "npub": { "description": "Your Nostr public key (npub1...); the report's author of record.", "type": "string" }, "title": { "description": "One-line summary of the problem.", "type": "string" }, "tool_name": { "default": "", "description": "Optional: the specific tool the report is about\n(e.g. \"schwab_get_option_chain\").", "type": "string" } }, "required": [ "npub", "dpop_token", "title", "body" ], "type": "object" }, "name": "roastify_report_issue", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Ask a chosen Authority to adopt this operator (deferred courtship).\n\nRESTRICTED to the operator — requires proof the caller controls this\noperator's npub. Resolves the Authority's MCP endpoint from the\ncommunity registry, mints an inline ownership proof with this\noperator's nsec, and delivers the request MCP-to-MCP. The Authority\nrecords it as pending; its owner approves on their own time. Poll\n``adoption_status`` for progress; the operator flips to ``ready``\nonce the Authority provisions it.", "inputSchema": { "additionalProperties": false, "properties": { "authority_npub": { "description": "npub of the Authority to request adoption from.", "type": "string" }, "dpop_token": { "default": "", "description": "operator-npub ownership proof (inline kind-27235 or cached token).", "type": "string" }, "note": { "default": "", "description": "optional message for the Authority owner.", "type": "string" }, "service_url": { "default": "", "description": "this operator's MCP endpoint (advertised to the Authority).", "type": "string" } }, "required": [ "authority_npub" ], "type": "object" }, "name": "roastify_request_adoption", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Open a Secure Courier channel for credential delivery.\n\nThis is the CREDENTIAL-DELIVERY flow — use it to hand over a service\nsecret (API keys, tokens). To merely prove you control an npub (the\nusual answer to a ``proof_required`` error), use ``request_npub_proof``\ninstead. Note: dynamic/OAuth2 services (e.g. Schwab) need NO couriered\nsecret — check ``service_status`` first.\n\nSends a welcome DM with a credential template. The recipient\nmust read the DM in their Nostr client, fill in the fields,\nand reply manually. **This is a human-in-the-loop flow.**\n\nAfter calling this tool, STOP and tell the user what to do.\nWait for the user to confirm they have replied before calling\n``receive_credentials``. Do NOT poll or retry — each\n``receive_credentials`` call destructively drains the relay\nmailbox.", "inputSchema": { "additionalProperties": false, "properties": { "sender_npub": { "default": "", "description": "Required. The npub to send the template to.", "type": "string" }, "service": { "default": "", "description": "Required. The credential service name (e.g.,\nfrom get_operator_onboarding_status or get_patron_onboarding_status).", "type": "string" } }, "type": "object" }, "name": "roastify_request_credential_channel", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Request npub ownership proof from a patron via Nostr DM.\n\nThis is the npub-OWNERSHIP-PROOF flow — use it when a call returns\n``proof_required``. It proves the caller controls an npub; it does\nNOT deliver any service secret. To hand an operator its API keys or\nOAuth secrets, use ``request_credential_channel`` instead.\n\nSends a challenge DM that the patron must sign and reply to\nusing their Nostr client. **This is a human-in-the-loop flow.**\n\nAfter calling this tool, STOP and tell the user to check their\nNostr client and reply to the challenge. Wait for the user to\nconfirm they have replied before calling ``receive_npub_proof``.\nDo NOT poll or retry — each ``receive_npub_proof`` call\ndestructively drains the relay mailbox.\n\n**Returns** a ``dpop_token`` — the one-time challenge nonce shown in\nthe DM. Pass it to ``receive_npub_proof`` to collect the reply. It\nis a matching token, not a credential: on its own it authorizes\nnothing.\n\n**Lifecycle:** ``receive_npub_proof`` returns the proof grant, which\nexpires after the duration the patron named in their reply. When it\nexpires, call ``request_npub_proof`` again for a fresh challenge,\nthen wait for the user, then call ``receive_npub_proof``.\n\nFree.", "inputSchema": { "additionalProperties": false, "properties": { "patron_npub": { "default": "", "description": "Required. The patron's npub to request proof from.", "type": "string" }, "reason": { "default": "", "description": "Optional. A human-readable purpose for the request\n(\"I'm working on your request XYZ and need the Operator to do\nABC for you\"). Signed into the provenance attestation and shown\nin the DM, so the recipient sees *why* they are being asked —\nespecially useful when the signer is unknown to them.", "type": "string" }, "verify_at": { "default": "", "description": "Optional. A free-form statement of WHERE you (the\ninitiating agent) already showed this proof's one-time code to\nthe user — a URL, or \"your Claude.ai conversation\", \"the Grok\nsession\". The OAuth 2.0 Device Grant ``verification_uri``,\ngeneralized: the user approves only if the code in the DM matches\nthe one you displayed there, so an unsolicited request they've\nnever seen is refused. Signed into the attestation.", "type": "string" } }, "type": "object" }, "name": "roastify_request_npub_proof", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Open a Secure Courier channel for patron credential delivery.\n\nSends a welcome DM with a credential template to the patron.\nFree.", "inputSchema": { "additionalProperties": false, "properties": { "sender_npub": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_request_patron_credentials", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Erase all pricing models and restore a viable default.\n\nDeletes every stored model, then self-initializes a fresh one\nfrom the tool registry — all tools at 0 sats with proper UUIDs.\nReturns the new model.\n\nRESTRICTED to operator — requires proof (nsec-signed).", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_reset_pricing_model", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Credit a patron's ledger from a BTCPay-settled invoice.\n\n**RESTRICTED to the operator** — the operator owns the books and is\nthe only party who can issue a manual credit grant. Patrons who\nbelieve they paid but never got credits must escalate to the\noperator's support, who then invokes this tool on their behalf.\n\nUse cases: cold-start vault races during check_payment, ncred\ndelivery hiccups, patrons closing Top-Off sheets before settle,\nany infrastructure incident that left an invoice settled at BTCPay\nbut uncredited on the operator's ledger.\n\nIdempotent — if the invoice is already credited (in the patron's\n``credited_invoices``), returns success with credits_granted=0.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "A kind-27235 Nostr event signed by the OPERATOR's nsec\nfor this tool. Patron proofs are rejected.", "type": "string" }, "invoice_id": { "description": "The BTCPay invoice ID to verify and credit.", "type": "string" }, "patron_npub": { "description": "The patron's npub whose ledger receives the grant.", "type": "string" } }, "required": [ "invoice_id", "patron_npub", "dpop_token" ], "type": "object" }, "name": "roastify_restore_credits", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Prepare this operator's database schema again, ignoring the breadcrumb.\n\nDiagnostic / recovery tool for when Neon keeps answering 4xx and the\noperator suspects a table or grant is missing. Every statement is\nidempotent, so a clean re-run is harmless; the breadcrumb is rewritten\nat the end. Returns one step per concern, with Neon's own error text\ninline for any that failed.\n\nRESTRICTED to operator — requires proof (nsec-signed).", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" } }, "type": "object" }, "name": "roastify_restore_neon_schema", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check the health and configuration of this service. Free.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "roastify_service_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Check operator readiness. Returns the operator lifecycle\nstate and clear guidance on what to do next. Free.\n\nLifecycle states:\n- ready: Vault AND pricing model verified on this call. Proceed with\n tool calls — this is the answer the moment the vault is up.\n- warming_up: Persistence could not be reached on this call (a\n transient failure, seen, not assumed). Retry; the next call\n completes the bootstrap. Never reported on a clock.\n- misconfigured: Persistence rejected a query with a permanent SQL\n error (permission denied, missing relation). Paid tools will fail\n until the operator repairs the database — retrying does not help.\n- quota_exceeded: The persistence provider (Neon) answered HTTP 402 —\n the operator's database has exhausted its compute/storage quota, so\n the books are locked for billing. Paid tools fail; retrying does NOT\n help. The operator's Authority must restore capacity (upgrade the\n plan or wait for the quota reset). Free tools remain available.\n- not_registered: Operator has no Authority relationship yet. Call register_operator first.\n- no_identity: Operator nsec is not configured. Deployment issue.", "inputSchema": { "additionalProperties": false, "properties": { "patron_npub": { "default": "", "description": "Optional. If supplied, the response includes an\n``upstream_oauth`` block with the patron's stored OAuth\ntoken expiry (runtime-derived from vault state) so a\nclient can refresh proactively rather than reactively\nafter a stale-token failure.", "type": "string" } }, "type": "object" }, "name": "roastify_session_status", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Set the active pricing model. RESTRICTED to operator.\n\nRequires a valid proof (Schnorr-signed kind-27235 event)\nproving the caller holds the operator's nsec.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "default": "", "type": "string" }, "model_json": { "type": "string" } }, "required": [ "model_json" ], "type": "object" }, "name": "roastify_set_pricing_model", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Set the product's store-page DESCRIPTION on a stored design, committed in place.\n\nThe description is the product's store-page prose — it lives OUTSIDE the design\nartwork and, on Roastify, syncs to Shopify. It is versioned WITH the design in your\ngit library: read the current one from `roastify_get_design_text` (its `description`\nfield — Roastify's own auto-generated copy is a helpful starting point), refine it,\nand write it back here. This commits a new version of the SAME design_id (git tracks\nthe diff); it does NOT touch Roastify. The browser courier applies it onto the product\non the next Fetch (skipped there if Shopify has locked the field).", "inputSchema": { "additionalProperties": false, "properties": { "commit_message": { "default": "", "description": "A specific description of WHAT changed and WHY — a real commit\nmessage, not a placeholder like 'save this' or 'update'. Required.", "type": "string" }, "description": { "description": "The new store-page description prose.", "type": "string" }, "design_id": { "description": "The design to edit, from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "label": { "default": "", "description": "Rename the design (optional). Defaults to keeping its current label.", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "version_tag": { "default": "", "description": "The NEXT semver version (MAJOR.MINOR.PATCH, e.g. 1.3.0, no 'v') — call\nroastify_list_design_versions and increment. Required; reusing one is refused.", "type": "string" } }, "required": [ "design_id", "description" ], "type": "object" }, "name": "roastify_set_product_description", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Store a Roastify design JSON in your library (a commit in your GitHub repo).\n\nThe browser courier reads a saved product's design and calls this to shuttle it\nup. On the way in, the design's fonts[] is REPAIRED — Roastify's own schema\nmigration leaves a lossy fonts[] (a dropped family, a bad weight), so a stashed\ndesign would otherwise carry that damage; the repair rebuilds fonts[] from the\nfamilies the text actually uses so it renders in its intended fonts. Only the\nload list changes; the text and its fonts are untouched. Inline images are\nde-duplicated. This does NOT touch Roastify.", "inputSchema": { "additionalProperties": false, "properties": { "commit_message": { "default": "", "description": "A specific description of WHAT changed and WHY — write a real\ncommit message, not a placeholder like 'save this' or 'update'. Required.", "type": "string" }, "description": { "default": "", "description": "The product's store-page description at stash time, versioned\nwith the design so Fetch can re-apply it to a target product.", "type": "string" }, "design": { "additionalProperties": true, "description": "The full Roastify design JSON object (elements/faceBackgrounds…).", "type": "object" }, "design_id": { "default": "", "description": "Optional explicit folder id. Omit and the id is the slug of the\nlabel, so re-stashing the same design commits a new version in place\ninstead of creating a duplicate.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "label": { "default": "", "description": "Your name for this design, e.g. \"Ethiopian — light\".", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "product_id": { "default": "", "description": "The Roastify product id it came from, for your reference.", "type": "string" }, "source_title": { "default": "", "description": "The product's title at stash time, for your reference.", "type": "string" }, "version_tag": { "default": "", "description": "The NEXT semver version (MAJOR.MINOR.PATCH like 1.2.3, no 'v'); check\nroastify_list_design_versions and increment. Required; reusing one is refused.", "type": "string" } }, "required": [ "design" ], "type": "object" }, "name": "roastify_stash_design", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Patch a coupon's editable fields.\n\nPass only the fields you want to change. To set a cap to\nunlimited (NULL in the schema), pass ``clear_uses_per_patron=true``\nor ``clear_total_uses=true``. Renaming the code is allowed —\nexisting patron redemption rows survive (they key on coupon id).\n\nRESTRICTED to operator — requires proof.", "inputSchema": { "additionalProperties": false, "properties": { "clear_total_uses": { "default": false, "type": "boolean" }, "clear_uses_per_patron": { "default": false, "type": "boolean" }, "coupon_id": { "type": "string" }, "discount_percent": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null }, "dpop_token": { "default": "", "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null }, "total_uses": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null }, "uses_per_patron": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null }, "valid_from": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null }, "valid_until": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null } }, "required": [ "coupon_id" ], "type": "object" }, "name": "roastify_update_coupon", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Apply text edits to a stored design and commit a new version of it.\n\nThe store is configuration management: the edit is committed back to the SAME\ndesign_id (git tracks the diff), not saved as a new file. Apply it onto a product\nwith the browser courier. Only the words change — fonts, layout, and images are\npreserved, and the heavy image is never moved (the design keeps referencing the\nsame content-addressed assets).\n\nThe box does not resize, so keep each new text within roughly ±10% of the\ncharacter count of the layer it replaces (see `chars` from get_design_text);\nlonger copy overflows and the merchant fixes it by hand.", "inputSchema": { "additionalProperties": false, "properties": { "commit_message": { "default": "", "description": "A specific description of WHAT changed and WHY — a real commit\nmessage, not a placeholder like 'save this' or 'update'. Required.", "type": "string" }, "design_id": { "description": "The design to edit, from roastify_list_designs.", "type": "string" }, "dpop_token": { "default": "", "type": "string" }, "edits": { "additionalProperties": { "type": "string" }, "description": "A map of {layer_id: new_text}, using ids from roastify_get_design_text.\nInclude every layer that should change, including ones that repeat a\nvalue or embed it in a longer blurb. A JSON object string is also\naccepted (some MCP clients serialize object args that way).", "type": "object" }, "label": { "default": "", "description": "Rename the design (optional). Defaults to keeping its current label.", "type": "string" }, "npub": { "default": "", "description": "Required. Your Nostr public key (npub1...) for credit billing.", "type": "string" }, "version_tag": { "default": "", "description": "The NEXT semver version (MAJOR.MINOR.PATCH, e.g. 1.3.0, no 'v') — call\nroastify_list_design_versions and increment. Required; reusing one is refused.", "type": "string" } }, "required": [ "design_id", "edits" ], "type": "object" }, "name": "roastify_update_design_text", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Add or update a single operator secret field.\n\nMerges into the operator's stored credentials without touching the\nothers — the field-level counterpart to re-delivering the whole\nbundle over Secure Courier. Use it to rotate one secret (a reissued\n``btcpay_api_key``, say) without restating the six you did not\nchange, where any field omitted from a courier reply is destroyed.\n\nThe value is never echoed back. RESTRICTED to the operator —\nrequires proof (nsec-signed kind-27235 or a cached dpop_token\nphrase); patron proofs are rejected.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Operator proof for this tool.", "type": "string" }, "field": { "description": "The operator credential field to set. Must be declared\nin the operator's credential template.", "type": "string" }, "value": { "description": "The value to store.", "type": "string" } }, "required": [ "field", "value", "dpop_token" ], "type": "object" }, "name": "roastify_update_operator_credential", "outputSchema": { "additionalProperties": true, "type": "object" } }, { "description": "Add or update a single patron credential field.\n\nMerges into existing stored credentials without affecting\nother fields. Useful for setting an account identifier\nafter OAuth, changing a default brain, etc. Free. Proof of\nnpub ownership is required — this is a write to the patron's\nsensitive credential vault.", "inputSchema": { "additionalProperties": false, "properties": { "dpop_token": { "description": "Raw JSON of a kind-27235 Nostr event signed by npub —\nnot base64, not NIP-98 'Authorization: Nostr <b64>' framing. Its\n`u` tag must hold THIS tool's exact name (from tools/list), not\nthe endpoint URL; content:\"\", created_at within 60s of now, and a\nrandom `nonce` tag recommended. Or a cached dpop_token phrase.", "type": "string" }, "field": { "description": "The credential field name to set.", "type": "string" }, "npub": { "description": "The patron's Nostr public key (npub1...).", "type": "string" }, "value": { "description": "The value to store.", "type": "string" } }, "required": [ "npub", "dpop_token", "field", "value" ], "type": "object" }, "name": "roastify_update_patron_credential", "outputSchema": { "additionalProperties": true, "type": "object" } } ] }
Verify it yourselfcurl -s https://api.teppi.xyz/v1/evidence/sha256:be7170bb8d78f9b73b75543ba2fa063dffca4727ad28447352255e4780858190 | sha256sum