@binaryandthread/mcp
v0.3.2
Published
Binary & Thread MCP server — buy a piece from inside your AI agent. 10 deterministic, read-mostly tools over the shared Store API client; the card never enters the chat and the agent can never self-complete a purchase.
Readme
@binaryandthread/mcp
Buy a Binary & Thread heavyweight piece from inside your AI agent (Claude, etc.). A small,
deterministic MCP server (stdio) exposing 10 read-mostly tools
over the same shared Store API client the bt CLI uses, so the agent can browse, stage, and
hand off a purchase without ever putting a card in the chat.
The agent can never complete a purchase or set a shipping address. The card is entered by you, on the storefront's Stripe page, in your own browser. The MCP just hands you the link.
The 10 tools
| tool | what it does |
| --- | --- |
| list_products | the catalog, text-only (no per-item images, keeps the agent's context lean) |
| get_product | one piece's specs + its image (rendered and the Blob URL as a text floor) |
| add_to_cart | create-or-reuse a cart; returns the cart_id as the durable handle |
| get_cart | the authoritative server cart + total (incl. VAT) |
| checkout | mints {checkout_url, session_id} instantly, never polls, never opens a browser, never sets an address |
| check_payment | polls the session once; the agent re-calls at its own cadence |
| get_order_status | status/timeline by display id + exact email (no address; not-found on a mismatch) |
| report_issue | file a bug report, emailed to the maintainer, server-redacted; returns a reference |
| teapot | 418, a deadpan easter egg (no input) |
| fortune | a deterministic developer aphorism (optional seed) |
Install / run
Distributed via npx from the verified @binaryandthread npm namespace, published from locked
CI with npm 2FA (hardware key) + publish provenance (signed releases). Always install the
scoped package; do not trust a bare bt-mcp binary from an unverified source (anti-impersonation,
SECURITY G11/G14/T10/T11). An MCP Registry listing is deferred until the API is stable.
Zero-config, the server ships pointed at the live store (the store URL + publishable key are baked in; the key is public by design). The minimal config is just:
// Claude Desktop / Claude Code MCP config (claude_desktop_config.json or .mcp.json)
{
"mcpServers": {
"binary-and-thread": {
"command": "npx",
"args": ["-y", "@binaryandthread/mcp"]
}
}
}Override any of it via env for local dev / staging, or to attach your customer token:
{
"mcpServers": {
"binary-and-thread": {
"command": "npx",
"args": ["-y", "@binaryandthread/mcp"],
"env": {
"BT_STORE_URL": "http://localhost:9000", // override the baked-in prod URL (dev/staging)
"BT_PUBLISHABLE_KEY": "pk_...", // PUBLIC by design, never a secret key
"BT_REGION": "se", // optional ISO-2 (se · us · any EU country e.g. de/fr/ie); region-resolved, never hardcoded
"BT_TOKEN": "..." // optional customer JWT (own scope), from your keychain, NEVER the chat
}
}
}
}Config (environment only, all optional; env overrides the baked-in prod defaults)
| var | required | notes |
| --- | --- | --- |
| BT_STORE_URL | no | the Medusa Store API base URL (default: production) |
| BT_PUBLISHABLE_KEY | no | the publishable key, public by design (default: production; a secret sk_ key is rejected) |
| BT_TOKEN | no | the customer JWT (own data only); read from the OS keychain / a configured env, never from the chat |
| BT_REGION | no | ISO-2 country code to resolve pricing — se (SEK), us (USD), or any EU country e.g. de/fr/ie (EUR); never a hardcoded currency literal. Sets the default; list_products / get_product / add_to_cart also take a per-call region arg that overrides it (a country we don't ship to is refused, not priced in the wrong currency). |
Onboarding for the agent
A cold agent doesn't have to infer the flow: the server exposes a getting-started resource
(bt://getting-started) describing the list_products → get_product → add_to_cart → checkout →
check_payment flow and the money boundary, and a buy prompt that walks the purchase (respecting
that the card + address are entered by the human in the browser, never in the chat).
Security posture (the MCP-specific red-team gates)
- No self-completing purchase (G8 confused-deputy):
checkoutreturns a hand-off URL + session id and says so in its result text, "I can't complete this for you." The human browser-hop is mandatory. - Cards never in the chat: there is no card / CVC / PAN field anywhere. The card is entered only on Stripe's hosted surface.
- Image path is SSRF / token-DoS safe (G9 / CLAUDE.md rule 6): images are fetched only from the
allowlisted Vercel Blob host (the same constant the CLI uses, in
@binaryandthread/store-client), over https, with a ≤2MB byte cap + a timeout. A non-allowlisted /data:/http:URL is never fetched, it degrades to the Blob-URL text floor. List views are text-only. - Untrusted backend strings (G8): every product title / lore / order note is treated as data, control-char-stripped, whitespace-collapsed, length-capped, so it can't steer the host agent.
- No token / PII in output or logs (G10): all output passes through the shared redactor; the JWT,
Authorization headers, and full emails never appear. Diagnostics go to stderr (stdout is the MCP
transport).
get_order_statusreturns status only, never a shipping address. - Nothing to escalate (T1/T10): the tools are thin, deterministic wrappers over
/store/*only, no admin op, no shell, no free-form execution.
Why both an image block and a URL
Claude renders MCP ImageContent natively on claude.ai / the API, but Claude Code has a base64-bloat
bug (#31208, ~15–25k tokens/image) and can
fail to render it. So get_product always emits the Blob URL as a text block too, the reliable
floor. The buy is never blocked on rendering.
