# cryptoport-port [Health: Active]

**Category:** 💰 Finance & Fintech  
**Repository:** https://github.com/thecryptoport-mcp/cryptoport-port  
**GitHub Stars:** 0  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/cryptoport-port-2

## Description
Daily board of what crypto YouTube channels said: coin calls, stances, catalysts. Free, no key.

## Claude Desktop Quick Installation
Remote MCP endpoint (confidence: high). Install path detected from listing signals. Add as a URL/SSE server in your client:

```json
"mcpServers": {
  "cryptoport-port": {
    "url": "https://cryptoport-mcp.cryptoport-port.workers.dev/mcp"
  }
}
```

## Documentation & README

# The Crypto Port — MCP server

**What crypto YouTube channels said, as a daily board — coin calls, stances, dated
catalysts — served over MCP. Free, no API key.**

- **Live endpoint (streamable HTTP):** `https://cryptoport-mcp.cryptoport-port.workers.dev/mcp`
- **Registry name:** `com.thecryptoport/cryptoport-port` (official MCP registry, registry.modelcontextprotocol.io)
- **Website (the human board):** <https://thecryptoport.com>

## Add to your client

Claude Code:

```bash
claude mcp add --transport http cryptoport https://cryptoport-mcp.cryptoport-port.workers.dev/mcp
```

Any MCP client that speaks streamable-HTTP remotes:

```json
{
  "mcpServers": {
    "cryptoport": {
      "type": "streamable-http",
      "url": "https://cryptoport-mcp.cryptoport-port.workers.dev/mcp"
    }
  }
}
```

Free tools: `latest_board`, `search`. Everything served is commentary on public statements by
named channels; every payload carries `stale` / `stale_days` / `built_from_brief_date` — read
them. Not financial advice, not real-time.

---

## Build context — the rung-3 machine port

MCP server + x402-payable HTTP endpoints over thecryptoport.com's daily board.

**Network: Base Sepolia (`eip155:84532`) — testnet, throughout. No mainnet sign-off exists and
none is assumed. Nothing is charged in real money on any network this server talks to.**
Master's ruling of record for this build is **C+ (2026-08-24)**: build the paid-port mechanics
now on testnet only; the paid tier itself stays declined and the mainnet switch is the day-60
ruling. The prices below exist so the payment handshake is real, not because anyone is billed.

Runtime owner: **Dex**. Ward wrote the money, screening and licensing logic; Dex stands the
process up and keeps it alive. See "Handoff to Dex" below.

---

## What it is

| Tool | Price (testnet USDC) | HTTP twin |
|---|---|---|
| `latest_board` | free | `GET /api/v1/board` |
| `search(q, limit)` | free | `GET /api/v1/search?q=` |
| `coin(ticker)` | 0.005 | `GET /api/v1/coin/:ticker` |
| `stance_history(ticker)` | 0.01 | `GET /api/v1/stance_history/:ticker` |
| `catalysts(window)` | 0.01 | `GET /api/v1/catalysts?window=` |
| `board_snapshot(date)` | 0.02 — **not charged today** | `GET /api/v1/board_snapshot/:date` |

Free meta: `GET /healthz`, `GET /x402/routes` (the price table), `GET /.well-known/mcp.json`.
MCP: `POST /mcp` (streamable HTTP) and `npm run mcp-stdio` (stdio).

**The MCP tools are thin proxies onto the HTTP twins inside the same process.** One code path for
pricing, screening, refusal and the published-field gate, so the two surfaces cannot disagree.

### What the paid tier actually sells — read this before pricing anything

`llms.txt` says, in the site's own words: *"there is no hidden paid tier of the same rows."*
That sentence is a constraint on this build, and it holds: every paid payload names the free
static file the same facts live in (`upstream`). What is priced is **form**, not exclusivity —
a parsed dated series instead of an `arc` string, a windowed catalyst query instead of a full
file. If a future paid tool ever returns a fact the free tier does not, that llms.txt line
becomes false and has to change first. → flagged to Larry, not decided here.

---

## Run it

```bash
npm install
npm run sdn-refresh          # refresh the OFAC list (vendored copy already present)
node scripts/keygen.js       # writes .env.sepolia (mode 600); prints only the address
X402_PAY_TO=0x… npm start    # http://localhost:8402
npm test                     # 50 tests
npm run demo                 # free call -> 402 -> (pay) -> data + receipt
```

Data comes from the rung-0 emitter: `CRYPTOPORT_DATA_DIR`, else
`<repo>/../../Documents/Claude/myPKA/infra/cryptoport-site/dist`, falling back to `staging`
when `dist` has no `feed.json`. It is read **once at boot** into memory — the request path never
touches the filesystem, which is what makes `src/worker.js` (Cloudflare Workers) a deployment
choice rather than a rewrite.

### Claude Code MCP config (stdio)

```json
{ "mcpServers": { "thecryptoport": {
  "command": "node",
  "args": ["/home/ops/projects/cryptoport-port/src/mcp-stdio.js"],
  "env": { "X402_PAY_TO": "0x…" }
} } }
```

Streamable HTTP instead: point the client at `http://localhost:8402/mcp`.

---

## Mechanism chosen, and what was rejected

**Chosen:** x402 v2 middleware in front of the paid routes + the free public testnet facilitator
+ USDC on Base Sepolia. **No smart contract, no bytecode of any kind.** A price, a receive
address and a verifier are the whole mechanism, and fewer contracts is a security property.

| Rejected | Why |
|---|---|
| A bespoke access/subscription contract | Pure audit surface for something x402 does natively per call. |
| A self-hosted facilitator | Right idea, wrong scale. Buys decentralisation we do not need and adds a service to keep alive. |
| API keys + Stripe | Reintroduces accounts, PII and an operator in the loop; wrong shape for anonymous agents. |
| Any token, subscription NFT or fractionalised instrument | Pre-CLARITY securities exposure for zero benefit. Hard no. |
| `@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. |

Header names are read out of `@x402/core`, not transcribed from a blog: request `X-PAYMENT`,
response `X-PAYMENT-RESPONSE`, and the 402 carries the terms in the `payment-required` header
(base64 JSON) — the terms are **also** repeated in the JSON body so an agent does not have to
base64-decode a header to learn a price. Package generation matters: the scoped `@x402/*` v2
packages are pinned in `package.json` and coexist on npm with the v1 `x402-hono`/`x402-next`.

**No license is signed and no license hash is referenced here.** This rung sells access to a
compilation of public statements; there is no geometry, no licensable work product, and no
EIP-712 typed-data license in the design. The `license` string in every payload is rung 0's own
attribution notice, passed through untouched.

---

## Gates (all three run before anything is served)

1. **Advisory-input refusal — `src/inputs.js`.** Any argument named like portfolio / holdings /
   position / allocation / balance / risk profile / cost basis / PnL / leverage / stop-loss /
   "should I buy" is refused with **400** and a plain reason, on HTTP and MCP alike, *before*
   payment is even considered. Values are never inspected — we refuse the shape, we do not read
   the data. Every tool description says so.
2. **OFAC SDN denied-party screening on the payer — `src/sdn.js`.** A local check of every
   `0x…` address found anywhere in the decoded `X-PAYMENT` payload against OFAC's published SDN
   digital-currency address list (991 addresses, 124 EVM, published 08/24/2026), fetched
   read-only by `scripts/sdn-refresh.js` from OFAC's own endpoint and vendored as
   `data/sdn-addresses.json`. A listed payer gets **403**, is never settled and is never served.
   *Not* KYT: it screens the paying address, it does not trace funds. No KYC, no accounts, no PII
   — collecting identity for a $0.005 data call creates a worse surface than it mitigates.
3. **B-2 published-field gate — `src/allowlist.js`.** `quality`, `counterweight`, `rank` and
   `presence` never leave the process, however they are spelled, and any *undeclared* key raises
   too. The held-back list has one source of truth —
   `infra/cryptoport-site/allowlist.py` — and `test/allowlist-parity.test.js` fails if the two
   drift. Upstream rows are projected on load, so a widened emitter cannot silently widen this
   surface.

**Proof the gates can fail** (the point of having them):
`test/sdn.test.js` refuses a *genuinely OFAC-listed* address end-to-end over HTTP, then removes
that one address from the list and asserts the same call is **not** refused — so the 403 is
demonstrably coming from the gate and not from somewhere else. `test/b2-leak.test.js` injects
each held-back column into the upstream JSON and asserts every tool either raises or projects it
away, and that the HTTP surface answers 500 with no leaked value in the body.

---

## Wallet surface

* **One receive-only address** as `payTo`. It signs nothing, holds no approvals, calls no
  contract. The server does not need its key and **refuses to start** if any
  `*PRIVATE_KEY` / `*RECOVERY_KEY` / `MNEMONIC` / `SEED_PHRASE` variable is present in its
  environment (`test/network-guard.test.js`).
* **Testnet throwaway EOA**, per the `~/projects/cad-on-chain/scripts/keygen.js` pattern, with
  one change: **the key is written straight to `.env.sepolia` (mode 600) and never printed.**
  stdout ends up in terminals, transcripts and session logs, and "no private key in a repo, a
  note or a log" has no testnet exemption.
* **It does not graduate.** A mainnet `payTo` is a CDP-managed Server Wallet or it does not exist.
* Chain id 84532 is hard-guarded and every mainnet id is denylisted by name.
* Non-custodial by construction: no buyer funds, no buyer keys, no buyer files, no refunds
  (settlement precedes delivery), and **no file custody, ever**.

---

## Mainnet gate list (verbatim from the Lane C spec §C7 — all must be true, none inferred)

1. Master's explicit **per-deployment** mainnet sign-off, quoted verbatim in the deliverable.
2. Paid-tier ruling reopened and granted.
3. Pax/Master answers on §C5 (a)–(d), especially the investment-adviser question.
4. `payTo` is a CDP-managed account; **no key material anywhere on disk**; the throwaway-EOA
   pattern retired.
5. SDN denied-party gate demonstrated **rejecting** a listed address.
6. B-2 leak test green in CI, and demonstrated failing when a held-back column is introduced.
7. Facilitator switched from `x402.org` (testnet-only) to CDP with credentials held outside the
   repo.
8. Ownership/receipt question settled on a domain that is not Master's.
9. No new bytecode — if that changes, Slither + Foundry fuzz/invariant in CI **and** a named
   external audit before mainnet.

Status against that list today: **5 and 6 are done and demonstrated. 1, 2, 3, 4, 7 and 8 are
untouched.** 9 holds — this build ships no bytecode, so no Slither/Foundry pass and no external
audit is required; if that ever changes, item 9 applies in full.

---

## Handoff to Dex (runtime requirements, not a runtime decision)

* **Uptime.** It must survive a reboot unattended, with no permanent operator clicks. On the box
  that means a systemd unit — **gated live config → Master**. Ward did not create one.
* **Secrets.** The process needs exactly one variable: `X402_PAY_TO`. It needs **no key**. Hold
  it outside any repo. `.env.sepolia` / `.env.buyer` are mode-600 and git-ignored and the server
  never reads them.
* **Cold start.** The whole board is read once at boot (~60 JSON files, tens of ms). On Workers,
  supply the same shape from KV via `src/worker.js` (`env.BOARD` keys: `feed`, `coins`,
  `catalysts`, `sdn`) and pushed by the 07:50 cron. **No Cloudflare account was created for
  this** — the file exists so the runtime choice stays a deployment decision.
* **Outbound HTTPS** to `https://x402.org/facilitator` on every paid call (1–2 subrequests). The
  datacenter-egress problem on this box is inbound reputation at YouTube, not outbound HTTPS.
* **Data.** Point `CRYPTOPORT_DATA_DIR` at whatever the 07:50 job emits. The port re-reads only
  at boot; restart it after the emit, or add a reload.
* **SDN refresh** on the same cron: `npm run sdn-refresh` (read-only fetch from OFAC, ~30 s).
* **Rate limiting** on the free tier is an edge concern → Dex. The paid tier is self-limiting.
* **Anchoring stays rung 1 → Dex.** The port consumes anchors and passes them through untouched;
  it never re-anchors, re-hashes or forks the passport record. **Note:** the current `dist` build
  carries `anchor.root = null` while `staging` has real Merkle roots — the deployed build is
  un-anchored. The server prints a warning at boot and serves the empty anchor faithfully. That
  is Dex's to fix, not Ward's.
* **Registry submission — DONE.** Listed as `com.thecryptoport/cryptoport-port` on
  registry.modelcontextprotocol.io via HTTP domain proof (root `server.json`,
  `scripts/registry-publish.sh`).

## Cost

**Expect ~$0.** Free testnet facilitator, free registry listing, testnet USDC, existing hosting,
no contract, no vendor. The only recurring cost that ever appears is CDP's $0.001 per settlement
above 1,000/month at mainnet, which at any realistic volume for this board is zero. Revenue
expectation is also ~$0: at the ecosystem median a *thousand* paid calls a month is about $28;
at these prices, $5–10. This lane is a demo of the stack and an option on a rail.

## Known limitations and confidence flags

* **Bazaar discovery does not work on testnet — CONFIRMED 2026-08-24.** The free
  `x402.org` facilitator's `/supported` advertises
  `['builder-code','eip2612GasSponsoring','erc20ApprovalGasSponsoring']` — no `bazaar` — and
  `GET /facilitator/discovery/resources` returns **404**. Our 402s carry a valid `bazaar`
  declaration (`registry/bazaar-mcp.json`, and `test/x402.test.js` asserts it), but nothing
  indexes it. A real Bazaar listing needs the CDP facilitator, which needs an account. This
  contradicts the "Bazaar day one" line in the Lane C spec §C3 and is a mainnet-gate item.
* **The demo stops at the 402.** Both throwaway addresses hold 0 testnet USDC, and Base Sepolia
  faucets require a human login. No bot-wall was attempted. Everything up to settlement is
  proven; the remaining step is one human faucet visit, then `npm run demo` again.
* **MCP registry manifest** is the root `server.json`; it validates against the live registry
  schema and is what `scripts/registry-publish.sh` publishes. (The earlier unused draft at
  `registry/server.json` was removed 2026-08-30.)
* **`board_snapshot` is a stub** and is deliberately **not charged** while it is one: no anchored
  historical board series exists yet.
* **Settlement metadata arrives in the `X-PAYMENT-RESPONSE` header**, not the response body,
  because the SDK settles after the handler has already written the body. The MCP layer merges
  it into the payload as `settlement`; HTTP callers read the header. Documented rather than
  worked around.
* **`stance_history` derives from the published `arc` string.** If rung 0 ever stops publishing
  `arc`, this tool has nothing to parse.
* Ward writes no client-facing copy. The `disclaimer` sentence in every payload is engineering
  boilerplate about what an anchor does and does not prove — **Rhea/Pax to ratify the wording**.

## Open questions for Pax / Master (flagged, not decided)

Unchanged from the Lane C spec §C5: (a) does charging for a summarised feed of named channels'
calls change the attribution / derivative-work posture; (b) is a paid crypto-calls feed exposed
to investment-adviser framing that the free board is not; (c) money-transmission characterization
of receiving USDC for data; (d) may the seven channel names appear in a *paid* product. None of
these are answered, and this build charges nobody.

## Files

```
src/config.js       env + the chain-id guard + the no-keys-in-env rule
src/allowlist.js    B-2 gate (mirrors infra/cryptoport-site/allowlist.py)
src/data.js         snapshot, freshness, arc parsing, search — pure, Workers-safe
src/inputs.js       advisory-input refusal
src/sdn.js          OFAC SDN screening
src/tools.js        the one tool table: prices, schemas, descriptions, handlers
src/x402-routes.js  route config, 402 body, Bazaar declarations, price table
src/app.js          Hono app: gates, paywall, HTTP twins, free meta
src/mcp.js          MCP server + streamable-HTTP mount (proxies the HTTP twins)
src/mcp-stdio.js    stdio transport entry
src/node-server.js  Node entry: disk -> memory at boot
src/worker.js       Cloudflare Workers entry (KV-backed; no account created)
src/evm-keys.js     keygen/address, same pattern as cad-on-chain
scripts/            keygen, sdn-refresh, balance, demo, gen-manifests
registry/           server.json (MCP registry), bazaar-mcp.json
data/               sdn-addresses.json (vendored OFAC export)
test/               50 tests
```

---

## Free MCP server on Cloudflare Workers (deployed 2026-08-27)

**Live:** `https://cryptoport-mcp.cryptoport-port.workers.dev/mcp` — MCP over HTTP,
six tools, **free**, no key and no account.

Master ruled on 2026-08-27 that the free MCP server ships before the paid x402 port
is hosted: it is the "free front door onto a human budget" from the strategy memo,
it is the only thing that produces the visitor number the day-30 checkpoint needs,
and the descriptor the site publishes at `/.well-known/mcp.json` had been naming
tools with no server behind them.

**Free by construction, not by policy.** `wrangler.jsonc` sets no `X402_PAY_TO`, so
`createApp` never mounts the x402 middleware, `isCharging()` reports `price_usdc: "0"`
and `paid: false` on every response, and no tool description quotes a cost. Master's
ruling 4a — paid tier declined until the day-60 checkpoint — is therefore enforced by
the absence of a variable rather than by anyone remembering it.
⛔ **Do not add `X402_PAY_TO` to this Worker.** The paid port is a separate deployment
whose hosting was deferred; turning payments on here would flip a declined tier by
editing a config file.

**Data path.** `scripts/kv-push.mjs` reads the built `dist/` and writes `feed`,
`coins`, `catalysts` and `sdn` into the `BOARD` KV namespace. The Worker reads KV; it
never touches a filesystem and never calls back to Bluehost — which also means machine
clients that Bluehost's mod_security rejects (`python-requests` gets a 406 there) can
reach the board here.

**No zone, no DNS.** It answers on the account's own `*.workers.dev` subdomain. No
record on thecryptoport.com or any other portfolio domain was created or changed, which
is what keeps this clear of the never-touch mail directive on those zones.

### Daily refresh — INSTALLED 2026-08-27 on Master's sign-off

KV holds whatever was last pushed, so the push rides the end of the existing 07:50
chain — it runs only if the build, the allowlist gate and the deploy all succeeded.

**Dry-run it under cron's environment before ever changing this line.** wrangler's
shebang is `#!/usr/bin/env node`, and cron's minimal PATH has no `node`: the first
attempt died with exit 127, the same trap that killed the channel_intel pipeline on
2026-08-15. `kv-push.mjs` now extends PATH with its own `process.execPath` directory.
The check that catches it:

```sh
env -i HOME=/home/ops PATH=/usr/bin:/bin SHELL=/bin/sh \
  /home/ops/.nvm/versions/node/v24.15.0/bin/node scripts/kv-push.mjs
```


```cron
# CURRENT (installed):
50 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

# PROPOSED — same line with the KV push appended (runs only if the gate passed):
50 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
```

Run it by hand any time: `node scripts/kv-push.mjs`.

