{
  "markdown": "# The Crypto Port — MCP server\n\n**What crypto YouTube channels said, as a daily board — coin calls, stances, dated\ncatalysts — served over MCP. Free, no API key.**\n\n- **Live endpoint (streamable HTTP):** `https://cryptoport-mcp.cryptoport-port.workers.dev/mcp`\n- **Registry name:** `com.thecryptoport/cryptoport-port` (official MCP registry, registry.modelcontextprotocol.io)\n- **Website (the human board):** <https://thecryptoport.com>\n\n## Add to your client\n\nClaude Code:\n\n```bash\nclaude mcp add --transport http cryptoport https://cryptoport-mcp.cryptoport-port.workers.dev/mcp\n```\n\nAny MCP client that speaks streamable-HTTP remotes:\n\n```json\n{\n  \"mcpServers\": {\n    \"cryptoport\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://cryptoport-mcp.cryptoport-port.workers.dev/mcp\"\n    }\n  }\n}\n```\n\nFree tools: `latest_board`, `search`. Everything served is commentary on public statements by\nnamed channels; every payload carries `stale` / `stale_days` / `built_from_brief_date` — read\nthem. Not financial advice, not real-time.\n\n---\n\n## Build context — the rung-3 machine port\n\nMCP server + x402-payable HTTP endpoints over thecryptoport.com's daily board.\n\n**Network: Base Sepolia (`eip155:84532`) — testnet, throughout. No mainnet sign-off exists and\nnone is assumed. Nothing is charged in real money on any network this server talks to.**\nMaster's ruling of record for this build is **C+ (2026-08-24)**: build the paid-port mechanics\nnow on testnet only; the paid tier itself stays declined and the mainnet switch is the day-60\nruling. The prices below exist so the payment handshake is real, not because anyone is billed.\n\nRuntime owner: **Dex**. Ward wrote the money, screening and licensing logic; Dex stands the\nprocess up and keeps it alive. See \"Handoff to Dex\" below.\n\n---\n\n## What it is\n\n| Tool | Price (testnet USDC) | HTTP twin |\n|---|---|---|\n| `latest_board` | free | `GET /api/v1/board` |\n| `search(q, limit)` | free | `GET /api/v1/search?q=` |\n| `coin(ticker)` | 0.005 | `GET /api/v1/coin/:ticker` |\n| `stance_history(ticker)` | 0.01 | `GET /api/v1/stance_history/:ticker` |\n| `catalysts(window)` | 0.01 | `GET /api/v1/catalysts?window=` |\n| `board_snapshot(date)` | 0.02 — **not charged today** | `GET /api/v1/board_snapshot/:date` |\n\nFree meta: `GET /healthz`, `GET /x402/routes` (the price table), `GET /.well-known/mcp.json`.\nMCP: `POST /mcp` (streamable HTTP) and `npm run mcp-stdio` (stdio).\n\n**The MCP tools are thin proxies onto the HTTP twins inside the same process.** One code path for\npricing, screening, refusal and the published-field gate, so the two surfaces cannot disagree.\n\n### What the paid tier actually sells — read this before pricing anything\n\n`llms.txt` says, in the site's own words: *\"there is no hidden paid tier of the same rows.\"*\nThat sentence is a constraint on this build, and it holds: every paid payload names the free\nstatic file the same facts live in (`upstream`). What is priced is **form**, not exclusivity —\na parsed dated series instead of an `arc` string, a windowed catalyst query instead of a full\nfile. If a future paid tool ever returns a fact the free tier does not, that llms.txt line\nbecomes false and has to change first. → flagged to Larry, not decided here.\n\n---\n\n## Run it\n\n```bash\nnpm install\nnpm run sdn-refresh          # refresh the OFAC list (vendored copy already present)\nnode scripts/keygen.js       # writes .env.sepolia (mode 600); prints only the address\nX402_PAY_TO=0x… npm start    # http://localhost:8402\nnpm test                     # 50 tests\nnpm run demo                 # free call -> 402 -> (pay) -> data + receipt\n```\n\nData comes from the rung-0 emitter: `CRYPTOPORT_DATA_DIR`, else\n`<repo>/../../Documents/Claude/myPKA/infra/cryptoport-site/dist`, falling back to `staging`\nwhen `dist` has no `feed.json`. It is read **once at boot** into memory — the request path never\ntouches the filesystem, which is what makes `src/worker.js` (Cloudflare Workers) a deployment\nchoice rather than a rewrite.\n\n### Claude Code MCP config (stdio)\n\n```json\n{ \"mcpServers\": { \"thecryptoport\": {\n  \"command\": \"node\",\n  \"args\": [\"/home/ops/projects/cryptoport-port/src/mcp-stdio.js\"],\n  \"env\": { \"X402_PAY_TO\": \"0x…\" }\n} } }\n```\n\nStreamable HTTP instead: point the client at `http://localhost:8402/mcp`.\n\n---\n\n## Mechanism chosen, and what was rejected\n\n**Chosen:** x402 v2 middleware in front of the paid routes + the free public testnet facilitator\n+ USDC on Base Sepolia. **No smart contract, no bytecode of any kind.** A price, a receive\naddress and a verifier are the whole mechanism, and fewer contracts is a security property.\n\n| Rejected | Why |\n|---|---|\n| A bespoke access/subscription contract | Pure audit surface for something x402 does natively per call. |\n| A self-hosted facilitator | Right idea, wrong scale. Buys decentralisation we do not need and adds a service to keep alive. |\n| API keys + Stripe | Reintroduces accounts, PII and an operator in the loop; wrong shape for anonymous agents. |\n| Any token, subscription NFT or fractionalised instrument | Pre-CLARITY securities exposure for zero benefit. Hard no. |\n| `@x402/paywall` (a peer dep of `@x402/hono`) | Installs **716 packages** including WalletConnect, for a browser paywall UI no agent will ever see. `@x402/hono` works without it; verified. |\n\nHeader names are read out of `@x402/core`, not transcribed from a blog: request `X-PAYMENT`,\nresponse `X-PAYMENT-RESPONSE`, and the 402 carries the terms in the `payment-required` header\n(base64 JSON) — the terms are **also** repeated in the JSON body so an agent does not have to\nbase64-decode a header to learn a price. Package generation matters: the scoped `@x402/*` v2\npackages are pinned in `package.json` and coexist on npm with the v1 `x402-hono`/`x402-next`.\n\n**No license is signed and no license hash is referenced here.** This rung sells access to a\ncompilation of public statements; there is no geometry, no licensable work product, and no\nEIP-712 typed-data license in the design. The `license` string in every payload is rung 0's own\nattribution notice, passed through untouched.\n\n---\n\n## Gates (all three run before anything is served)\n\n1. **Advisory-input refusal — `src/inputs.js`.** Any argument named like portfolio / holdings /\n   position / allocation / balance / risk profile / cost basis / PnL / leverage / stop-loss /\n   \"should I buy\" is refused with **400** and a plain reason, on HTTP and MCP alike, *before*\n   payment is even considered. Values are never inspected — we refuse the shape, we do not read\n   the data. Every tool description says so.\n2. **OFAC SDN denied-party screening on the payer — `src/sdn.js`.** A local check of every\n   `0x…` address found anywhere in the decoded `X-PAYMENT` payload against OFAC's published SDN\n   digital-currency address list (991 addresses, 124 EVM, published 08/24/2026), fetched\n   read-only by `scripts/sdn-refresh.js` from OFAC's own endpoint and vendored as\n   `data/sdn-addresses.json`. A listed payer gets **403**, is never settled and is never served.\n   *Not* KYT: it screens the paying address, it does not trace funds. No KYC, no accounts, no PII\n   — collecting identity for a $0.005 data call creates a worse surface than it mitigates.\n3. **B-2 published-field gate — `src/allowlist.js`.** `quality`, `counterweight`, `rank` and\n   `presence` never leave the process, however they are spelled, and any *undeclared* key raises\n   too. The held-back list has one source of truth —\n   `infra/cryptoport-site/allowlist.py` — and `test/allowlist-parity.test.js` fails if the two\n   drift. Upstream rows are projected on load, so a widened emitter cannot silently widen this\n   surface.\n\n**Proof the gates can fail** (the point of having them):\n`test/sdn.test.js` refuses a *genuinely OFAC-listed* address end-to-end over HTTP, then removes\nthat one address from the list and asserts the same call is **not** refused — so the 403 is\ndemonstrably coming from the gate and not from somewhere else. `test/b2-leak.test.js` injects\neach held-back column into the upstream JSON and asserts every tool either raises or projects it\naway, and that the HTTP surface answers 500 with no leaked value in the body.\n\n---\n\n## Wallet surface\n\n* **One receive-only address** as `payTo`. It signs nothing, holds no approvals, calls no\n  contract. The server does not need its key and **refuses to start** if any\n  `*PRIVATE_KEY` / `*RECOVERY_KEY` / `MNEMONIC` / `SEED_PHRASE` variable is present in its\n  environment (`test/network-guard.test.js`).\n* **Testnet throwaway EOA**, per the `~/projects/cad-on-chain/scripts/keygen.js` pattern, with\n  one change: **the key is written straight to `.env.sepolia` (mode 600) and never printed.**\n  stdout ends up in terminals, transcripts and session logs, and \"no private key in a repo, a\n  note or a log\" has no testnet exemption.\n* **It does not graduate.** A mainnet `payTo` is a CDP-managed Server Wallet or it does not exist.\n* Chain id 84532 is hard-guarded and every mainnet id is denylisted by name.\n* Non-custodial by construction: no buyer funds, no buyer keys, no buyer files, no refunds\n  (settlement precedes delivery), and **no file custody, ever**.\n\n---\n\n## Mainnet gate list (verbatim from the Lane C spec §C7 — all must be true, none inferred)\n\n1. Master's explicit **per-deployment** mainnet sign-off, quoted verbatim in the deliverable.\n2. Paid-tier ruling reopened and granted.\n3. Pax/Master answers on §C5 (a)–(d), especially the investment-adviser question.\n4. `payTo` is a CDP-managed account; **no key material anywhere on disk**; the throwaway-EOA\n   pattern retired.\n5. SDN denied-party gate demonstrated **rejecting** a listed address.\n6. B-2 leak test green in CI, and demonstrated failing when a held-back column is introduced.\n7. Facilitator switched from `x402.org` (testnet-only) to CDP with credentials held outside the\n   repo.\n8. Ownership/receipt question settled on a domain that is not Master's.\n9. No new bytecode — if that changes, Slither + Foundry fuzz/invariant in CI **and** a named\n   external audit before mainnet.\n\nStatus against that list today: **5 and 6 are done and demonstrated. 1, 2, 3, 4, 7 and 8 are\nuntouched.** 9 holds — this build ships no bytecode, so no Slither/Foundry pass and no external\naudit is required; if that ever changes, item 9 applies in full.\n\n---\n\n## Handoff to Dex (runtime requirements, not a runtime decision)\n\n* **Uptime.** It must survive a reboot unattended, with no permanent operator clicks. On the box\n  that means a systemd unit — **gated live config → Master**. Ward did not create one.\n* **Secrets.** The process needs exactly one variable: `X402_PAY_TO`. It needs **no key**. Hold\n  it outside any repo. `.env.sepolia` / `.env.buyer` are mode-600 and git-ignored and the server\n  never reads them.\n* **Cold start.** The whole board is read once at boot (~60 JSON files, tens of ms). On Workers,\n  supply the same shape from KV via `src/worker.js` (`env.BOARD` keys: `feed`, `coins`,\n  `catalysts`, `sdn`) and pushed by the 07:50 cron. **No Cloudflare account was created for\n  this** — the file exists so the runtime choice stays a deployment decision.\n* **Outbound HTTPS** to `https://x402.org/facilitator` on every paid call (1–2 subrequests). The\n  datacenter-egress problem on this box is inbound reputation at YouTube, not outbound HTTPS.\n* **Data.** Point `CRYPTOPORT_DATA_DIR` at whatever the 07:50 job emits. The port re-reads only\n  at boot; restart it after the emit, or add a reload.\n* **SDN refresh** on the same cron: `npm run sdn-refresh` (read-only fetch from OFAC, ~30 s).\n* **Rate limiting** on the free tier is an edge concern → Dex. The paid tier is self-limiting.\n* **Anchoring stays rung 1 → Dex.** The port consumes anchors and passes them through untouched;\n  it never re-anchors, re-hashes or forks the passport record. **Note:** the current `dist` build\n  carries `anchor.root = null` while `staging` has real Merkle roots — the deployed build is\n  un-anchored. The server prints a warning at boot and serves the empty anchor faithfully. That\n  is Dex's to fix, not Ward's.\n* **Registry submission — DONE.** Listed as `com.thecryptoport/cryptoport-port` on\n  registry.modelcontextprotocol.io via HTTP domain proof (root `server.json`,\n  `scripts/registry-publish.sh`).\n\n## Cost\n\n**Expect ~$0.** Free testnet facilitator, free registry listing, testnet USDC, existing hosting,\nno contract, no vendor. The only recurring cost that ever appears is CDP's $0.001 per settlement\nabove 1,000/month at mainnet, which at any realistic volume for this board is zero. Revenue\nexpectation is also ~$0: at the ecosystem median a *thousand* paid calls a month is about $28;\nat these prices, $5–10. This lane is a demo of the stack and an option on a rail.\n\n## Known limitations and confidence flags\n\n* **Bazaar discovery does not work on testnet — CONFIRMED 2026-08-24.** The free\n  `x402.org` facilitator's `/supported` advertises\n  `['builder-code','eip2612GasSponsoring','erc20ApprovalGasSponsoring']` — no `bazaar` — and\n  `GET /facilitator/discovery/resources` returns **404**. Our 402s carry a valid `bazaar`\n  declaration (`registry/bazaar-mcp.json`, and `test/x402.test.js` asserts it), but nothing\n  indexes it. A real Bazaar listing needs the CDP facilitator, which needs an account. This\n  contradicts the \"Bazaar day one\" line in the Lane C spec §C3 and is a mainnet-gate item.\n* **The demo stops at the 402.** Both throwaway addresses hold 0 testnet USDC, and Base Sepolia\n  faucets require a human login. No bot-wall was attempted. Everything up to settlement is\n  proven; the remaining step is one human faucet visit, then `npm run demo` again.\n* **MCP registry manifest** is the root `server.json`; it validates against the live registry\n  schema and is what `scripts/registry-publish.sh` publishes. (The earlier unused draft at\n  `registry/server.json` was removed 2026-08-30.)\n* **`board_snapshot` is a stub** and is deliberately **not charged** while it is one: no anchored\n  historical board series exists yet.\n* **Settlement metadata arrives in the `X-PAYMENT-RESPONSE` header**, not the response body,\n  because the SDK settles after the handler has already written the body. The MCP layer merges\n  it into the payload as `settlement`; HTTP callers read the header. Documented rather than\n  worked around.\n* **`stance_history` derives from the published `arc` string.** If rung 0 ever stops publishing\n  `arc`, this tool has nothing to parse.\n* Ward writes no client-facing copy. The `disclaimer` sentence in every payload is engineering\n  boilerplate about what an anchor does and does not prove — **Rhea/Pax to ratify the wording**.\n\n## Open questions for Pax / Master (flagged, not decided)\n\nUnchanged from the Lane C spec §C5: (a) does charging for a summarised feed of named channels'\ncalls change the attribution / derivative-work posture; (b) is a paid crypto-calls feed exposed\nto investment-adviser framing that the free board is not; (c) money-transmission characterization\nof receiving USDC for data; (d) may the seven channel names appear in a *paid* product. None of\nthese are answered, and this build charges nobody.\n\n## Files\n\n```\nsrc/config.js       env + the chain-id guard + the no-keys-in-env rule\nsrc/allowlist.js    B-2 gate (mirrors infra/cryptoport-site/allowlist.py)\nsrc/data.js         snapshot, freshness, arc parsing, search — pure, Workers-safe\nsrc/inputs.js       advisory-input refusal\nsrc/sdn.js          OFAC SDN screening\nsrc/tools.js        the one tool table: prices, schemas, descriptions, handlers\nsrc/x402-routes.js  route config, 402 body, Bazaar declarations, price table\nsrc/app.js          Hono app: gates, paywall, HTTP twins, free meta\nsrc/mcp.js          MCP server + streamable-HTTP mount (proxies the HTTP twins)\nsrc/mcp-stdio.js    stdio transport entry\nsrc/node-server.js  Node entry: disk -> memory at boot\nsrc/worker.js       Cloudflare Workers entry (KV-backed; no account created)\nsrc/evm-keys.js     keygen/address, same pattern as cad-on-chain\nscripts/            keygen, sdn-refresh, balance, demo, gen-manifests\nregistry/           server.json (MCP registry), bazaar-mcp.json\ndata/               sdn-addresses.json (vendored OFAC export)\ntest/               50 tests\n```\n\n---\n\n## Free MCP server on Cloudflare Workers (deployed 2026-08-27)\n\n**Live:** `https://cryptoport-mcp.cryptoport-port.workers.dev/mcp` — MCP over HTTP,\nsix tools, **free**, no key and no account.\n\nMaster ruled on 2026-08-27 that the free MCP server ships before the paid x402 port\nis hosted: it is the \"free front door onto a human budget\" from the strategy memo,\nit is the only thing that produces the visitor number the day-30 checkpoint needs,\nand the descriptor the site publishes at `/.well-known/mcp.json` had been naming\ntools with no server behind them.\n\n**Free by construction, not by policy.** `wrangler.jsonc` sets no `X402_PAY_TO`, so\n`createApp` never mounts the x402 middleware, `isCharging()` reports `price_usdc: \"0\"`\nand `paid: false` on every response, and no tool description quotes a cost. Master's\nruling 4a — paid tier declined until the day-60 checkpoint — is therefore enforced by\nthe absence of a variable rather than by anyone remembering it.\n⛔ **Do not add `X402_PAY_TO` to this Worker.** The paid port is a separate deployment\nwhose hosting was deferred; turning payments on here would flip a declined tier by\nediting a config file.\n\n**Data path.** `scripts/kv-push.mjs` reads the built `dist/` and writes `feed`,\n`coins`, `catalysts` and `sdn` into the `BOARD` KV namespace. The Worker reads KV; it\nnever touches a filesystem and never calls back to Bluehost — which also means machine\nclients that Bluehost's mod_security rejects (`python-requests` gets a 406 there) can\nreach the board here.\n\n**No zone, no DNS.** It answers on the account's own `*.workers.dev` subdomain. No\nrecord on thecryptoport.com or any other portfolio domain was created or changed, which\nis what keeps this clear of the never-touch mail directive on those zones.\n\n### Daily refresh — INSTALLED 2026-08-27 on Master's sign-off\n\nKV holds whatever was last pushed, so the push rides the end of the existing 07:50\nchain — it runs only if the build, the allowlist gate and the deploy all succeeded.\n\n**Dry-run it under cron's environment before ever changing this line.** wrangler's\nshebang is `#!/usr/bin/env node`, and cron's minimal PATH has no `node`: the first\nattempt died with exit 127, the same trap that killed the channel_intel pipeline on\n2026-08-15. `kv-push.mjs` now extends PATH with its own `process.execPath` directory.\nThe check that catches it:\n\n```sh\nenv -i HOME=/home/ops PATH=/usr/bin:/bin SHELL=/bin/sh \\\n  /home/ops/.nvm/versions/node/v24.15.0/bin/node scripts/kv-push.mjs\n```\n\n\n```cron\n# CURRENT (installed):\n50 7 * * * /usr/bin/python3 generate.py >> /home/ops/.cache/cryptoport-site.log 2>&1 && /usr/bin/python3 test_allowlist.py --dir dist >> /home/ops/.cache/cryptoport-site.log 2>&1 && SSH_HOST=162.241.252.122 SSH_USER=rpbdbsmy REMOTE_DIR=/home4/rpbdbsmy/thecryptoport.com ./deploy.sh go >> /home/ops/.cache/cryptoport-site.log 2>&1\n\n# PROPOSED — same line with the KV push appended (runs only if the gate passed):\n50 7 * * * cd /home/ops/Documents/Claude/myPKA/infra/cryptoport-site && /usr/bin/python3 generate.py >> /home/ops/.cache/cryptoport-site.log 2>&1 && /usr/bin/python3 test_allowlist.py --dir dist >> /home/ops/.cache/cryptoport-site.log 2>&1 && SSH_HOST=162.241.252.122 SSH_USER=rpbdbsmy REMOTE_DIR=/home4/rpbdbsmy/thecryptoport.com ./deploy.sh go >> /home/ops/.cache/cryptoport-site.log 2>&1 && /home/ops/.nvm/versions/node/v24.15.0/bin/node /home/ops/projects/cryptoport-port/scripts/kv-push.mjs >> /home/ops/.cache/cryptoport-site.log 2>&1\n```\n\nRun it by hand any time: `node scripts/kv-push.mjs`.\n",
  "bytes": 19811,
  "sha": "6e75567547ef2ab7700acd6949078ea8a45b8268896b4ec1aaf1d99c3c7e9e99",
  "repo_slug": "thecryptoport-mcp/cryptoport-port",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_com_thecryptoport_cryptoport_port_0856db96/readme"
}