shelf-smoke
API/MCP de vérification d'URL achetable par machine. Crédits TEST prépayés. Clé d'exemple via POST /keys (aucun secret codé en dur). Aucune intervention humaine en cours de boucle.
Serveur MCP hébergé
npx add-mcp 'https://shelf.kymac.co/mcp'S’installe dans Claude Code, Codex, Cursor et plus
Documentation
shelf-smoke
Machine-buyable URL smoke for other AI bots. Live host
https://shelf.kymac.co. Sole advertised entry: this file (GET /llms.txt). Do not start at OpenAPI, MCP, or/.
Bot-alone path: this file → get key → quote → free-try smoke. No human mid-loop. Prepaid checkout is only after the free credit is spent.
GET /health is public. It reports billing (test | live | gated) and runner_ready (Playwright/Chromium can launch — not just process-up). Probe fails closed when runner_ready is false. Live Stripe stays gated (unlock-live); current hosts report test when TEST secrets are present, otherwise gated. Issue a caller key at POST /keys (ssm_… shown once). First issue grants 1 free credit (credit.grant reason=first_key). Prefer Authorization: Bearer ssm_… on gated routes. X-API-Key: ssm_… is an alias for the same secret. Key id is the stable caller_id for Ledger events.
Install sequence (no human mid-loop):
- GET
/llms.txt(this file — sole advertised entry) - POST
/keys— secretssm_…returned once;idiscaller_id. First issue grants 1 free credit. - Authenticate with
Authorization: Bearer ssm_…(X-API-Keyis an alias only) - GET
/credits/quote?op=smoke—cost,balance,would_succeedwithout debit - POST
/smokeagainst the golden URLhttps://example.com/ - GET
/credits/packsthen checkout/confirm only after the free try is exhausted - GET
/credits/balanceand GET/credits/events— Ledger metering-truth
Golden proof
Known-good URL: https://example.com/
Every POST /smoke HTTP 200 includes credits_burned and credits_remaining.
Pass (HTTP 200; compare ok; burns 1 credit):
{"ok":true,"url":"https://example.com/","viewports":[{"name":"desktop","width":1280,"url":"https://example.com/","ok":true,"status":200},{"name":"phone","width":390,"url":"https://example.com/","ok":true,"status":200}],"credits_burned":1,"credits_remaining":0}
Fail (HTTP 200, ok:false; runner finished — failed smokes do not debit). Example when a URL returns HTTP 404:
{"ok":false,"url":"https://example.com/missing","viewports":[{"name":"desktop","width":1280,"url":"https://example.com/missing","ok":false,"status":404,"error":"HTTP 404"},{"name":"phone","width":390,"url":"https://example.com/missing","ok":false,"status":404,"error":"HTTP 404"}],"credits_burned":0,"credits_remaining":1}
HTTP 200 always means the runner finished — read ok for the verdict.
Fail shapes
Every HTTP fail body has code, message, retryable, and error (alias of message). No bare text 500s.
401 on gated routes (WWW-Authenticate: Bearer on both):
- missing:
{"code":"missing_api_key","message":"missing API key","retryable":false,"error":"missing API key"} - invalid:
{"code":"invalid_api_key","message":"invalid API key","retryable":false,"error":"invalid API key"}
402 when smoke would run without enough credits:
{"code":"insufficient_credits","message":"insufficient credits","retryable":false,"error":"insufficient credits","caller_id":"k_…","balance":0,"required":1}
400 invalid request: {"code":"invalid_request","message":"…","retryable":false,"error":"…"}
404 unknown route: {"code":"not_found","message":"not found","retryable":false,"error":"not found"}
500 unhandled: {"code":"internal_error","message":"…","retryable":true,"error":"…"}
504 timeout: {"code":"timeout","message":"smoke timed out","retryable":true,"error":"smoke timed out"}
503 Stripe TEST missing: {"code":"stripe_not_configured","message":"stripe test mode not configured","retryable":true,"error":"stripe test mode not configured"}
Ledger events (append-only, this caller_id):
credit.grant— caller_id, credits_added, reason (first_key), tscredit.purchase— caller_id, pack_id, credits_added, amount_cents, currency, stripe_payment_intent_id (TEST), tscredit.debit— caller_id, credits_burned, reason (smoke_success), request_id, tscredit.balance_snapshot(optional) — caller_id, balance, ts (or derive from events)
Stripe TEST only (STRIPE_SECRET_KEY=sk_test_…). Live keys fail closed (unlock-live).
MCP Streamable HTTP is POST /mcp (not an install entry). Machine transport discovery: GET /.well-known/mcp.json. Copy-paste client snippet: GET /mcp.json (this file is the only advertised pointer to the snippet; not an install entry). Tools: issue_key, verify_key, quote_credits, list_credit_packs, checkout_credits, confirm_credits, credit_balance, credit_events, smoke, plus optional health. smoke returns the same credit fields and fail JSON as HTTP. Gated tools prefer Authorization: Bearer or env SHELF_SMOKE_API_KEY; X-API-Key is an alias. Stdio: npx tsx src/mcp-stdio.ts with SHELF_SMOKE_API_KEY.