From 4e634d555266bed861c6b52c7d16ffd8101b251c Mon Sep 17 00:00:00 2001 From: VickyXAI <115643921+VickyXAI@users.noreply.github.com> Date: Tue, 1 Sep 2026 23:31:20 -0500 Subject: [PATCH] =?UTF-8?q?0.45.1=20=E2=80=94=20give=20the=20context=20num?= =?UTF-8?q?ber=20its=20own=20card,=20not=20badge=20nineteen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.45.0 published the figure and then buried it: a shields badge sitting second in a row of nineteen, between "MIT License" and "TypeScript 5". Those are table stakes. This is the one number nothing else in the ecosystem publishes, and no amount of colour rescues something in a badge wall. It now sits directly under the install command as a card — figure large, with a reference unit that makes it mean something (6% of a 200K window), the --profile trading alternative at -57%, and the line that does the work: every turn, whether or not you call a tool. The shields badge is retired; the wall is down to eighteen. Generated, never hand-drawn: `npm run brand:card` renders both variants from the live measurement, so the card cannot outlive the number it states. test/schema-tokens.test.ts checks the headline figure, the reference percentage, the trading figure and the saving against a fresh in-process measurement, in BOTH variants — verified by doctoring a card and confirming the build fails. Light and dark are separate SVGs behind , because an SVG cannot restyle itself from the host page. Relative paths are safe here: raw.githubusercontent now serves SVG as image/svg+xml, so npm's rewriting of relative image URLs renders the card rather than dumping markup. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01WUL3ExR4Nz7uebxKaKwjDi --- CHANGELOG.md | 17 ++++++++++++ README.md | 10 ++++++- assets/context-cost-dark.svg | 10 +++++++ assets/context-cost.svg | 10 +++++++ package.json | 3 ++- scripts/measure-tool-schema.mjs | 47 +++++++++++++++++++++++++++++++++ test/schema-tokens.test.ts | 28 +++++++++++++++----- 7 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 assets/context-cost-dark.svg create mode 100644 assets/context-cost.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index b321f87..7c649b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to BlockRun MCP will be documented in this file. +## 0.45.1 + +**The context number gets its own card instead of being the second of nineteen +badges.** 0.45.0 published the figure but buried it — a shields badge in a row +with `MIT License`, `TypeScript 5` and `Node ≥20.19`, which are table stakes, +next to the one number nothing else in the ecosystem publishes. It now sits +directly under the install command as a card: the figure large, a reference +unit that makes it mean something (6% of a 200K context window), the +`--profile trading` alternative at −57%, and the line that matters — *every +turn, whether or not you call a tool*. + +The card is generated by `npm run brand:card`, never drawn by hand, so it +cannot outlive the number it states; `test/schema-tokens.test.ts` checks both +the light and dark variants against a live measurement and fails the build if +either drifts. Light and dark ship as separate SVGs behind ``, because +an SVG cannot restyle itself from the host page. + ## 0.45.0 **Every tool schema got smaller, and the README now tells you by how much.** diff --git a/README.md b/README.md index d9088ec..3020757 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ Agents can only sign transactions.


20 tools  -12.9K context tokens  Agent native  No API keys  Read and trade Polymarket  @@ -41,6 +40,15 @@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest
Wallet auto-created on first run. Fund with $5 USDC. Ask Claude anything.
+
+ + + Context cost: 12.9K tokens, 6% of a 200K context window, charged every turn whether or not you call a tool. 5.6K with --profile trading, 57% less. + +
+ +
Every MCP server costs you this. Almost none of them tell you. How we measure it →
+ --- > **BlockRun MCP** is an open-source [Model Context Protocol](https://modelcontextprotocol.io) server that gives Claude — and any MCP-compatible agent — 20 tools for real-time data and real actions: 71 LLMs, image & video generation, prediction-market data, live web/X search, on-chain queries across 40 chains, and **the ability to place real, USDC-settled bets on Polymarket**. Authentication is a wallet signature (no API keys); you pay per call in USDC via the [x402](https://x402.org) protocol (no credit cards, no subscriptions). One self-custody wallet on Base or Solana. MIT licensed. diff --git a/assets/context-cost-dark.svg b/assets/context-cost-dark.svg new file mode 100644 index 0000000..00f1b3f --- /dev/null +++ b/assets/context-cost-dark.svg @@ -0,0 +1,10 @@ + + + + + CONTEXT COST + 12.9K tokens + 6% of a 200K context window · every turn, whether or not you call a tool + 5.6K with --profile trading — 57% less + measured, not estimated + diff --git a/assets/context-cost.svg b/assets/context-cost.svg new file mode 100644 index 0000000..baa95f3 --- /dev/null +++ b/assets/context-cost.svg @@ -0,0 +1,10 @@ + + + + + CONTEXT COST + 12.9K tokens + 6% of a 200K context window · every turn, whether or not you call a tool + 5.6K with --profile trading — 57% less + measured, not estimated + diff --git a/package.json b/package.json index 61365d4..3d03399 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blockrun/mcp", - "version": "0.45.0", + "version": "0.45.1", "mcpName": "io.github.BlockRunAI/blockrun-mcp", "description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, and crypto data. Paid via x402 micropayments.", "type": "module", @@ -24,6 +24,7 @@ "prepublishOnly": "npm run build", "verify:prices": "tsx scripts/verify-prices.ts", "measure:schema": "node scripts/measure-tool-schema.mjs", + "brand:card": "node scripts/measure-tool-schema.mjs --svg", "e2e:polymarket:readonly": "tsx scripts/polymarket-e2e-readonly.ts", "e2e:polymarket:approvals": "tsx scripts/polymarket-e2e-verify-approvals.ts", "e2e:polymarket:approve": "tsx scripts/polymarket-e2e-approve.ts", diff --git a/scripts/measure-tool-schema.mjs b/scripts/measure-tool-schema.mjs index 9a4bdd3..1669f85 100755 --- a/scripts/measure-tool-schema.mjs +++ b/scripts/measure-tool-schema.mjs @@ -34,6 +34,7 @@ * percent higher on JSON, so every number here is a slight UNDER-count. */ import { spawn } from "node:child_process"; +import { writeFileSync } from "node:fs"; import { fileURLToPath, pathToFileURL } from "node:url"; import { encode } from "gpt-tokenizer/encoding/o200k_base"; @@ -129,6 +130,41 @@ export function measure(tools, prefix = "") { /** 12900 -> "12.9K". The form the README badge publishes. */ export const asK = (n) => `${(Math.round(n / 100) / 10).toFixed(1)}K`; +/* ── the context-cost card ────────────────────────────────────────────────── + * + * Generated, never hand-drawn: the figures come from the measurement above, so + * the card cannot say something the server stopped doing. `--svg` rewrites + * both variants; test/schema-tokens.test.ts fails if they drift. + * + * Two files because GitHub picks between them with ; an SVG + * cannot restyle itself from the host page's colour scheme. Text is left at a + * system font stack rather than converted to paths so it stays selectable and + * legible at any zoom — the tradeoff is that positions are hand-tuned with + * enough slack that a wider glyph set cannot collide. + */ +const REFERENCE_WINDOW = 200_000; + +export function renderCard({ total, tradingTotal, cut, dark }) { + const c = dark + ? { bg: "#0B0A0F", stroke: "#26242E", label: "#8A8797", value: "#FFFFFF", accent: "#5B9BF6", muted: "#6E6B7B" } + : { bg: "#FFFFFF", stroke: "#E4E2E8", label: "#6E6B7B", value: "#0B0A0F", accent: "#2563EB", muted: "#8A8797" }; + const pct = Math.round((total / REFERENCE_WINDOW) * 100); + const sans = "-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif"; + const mono = "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace"; + + return ` + + + + CONTEXT COST + ${asK(total)} tokens + ${pct}% of a 200K context window · every turn, whether or not you call a tool + ${asK(tradingTotal)} with --profile trading — ${cut}% less + measured, not estimated + +`; +} + // Importable: test/schema-tokens.test.ts reuses measure()/asK() to pin the // published number, so the CLI half must not run on import. const isCli = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href; @@ -143,6 +179,17 @@ for (const profile of PROFILES) { results[profile ?? userCmd.join(" ")] = measure(await listTools(cmd), PREFIX); } +if (flags.has("--svg")) { + const full = results.full, trading = results.trading; + if (!full || !trading) throw new Error("--svg needs the full and trading profiles (drop `-- `)"); + const cut = Math.round((1 - trading.total / full.total) * 100); + for (const dark of [false, true]) { + const file = new URL(`../assets/context-cost${dark ? "-dark" : ""}.svg`, import.meta.url); + writeFileSync(file, renderCard({ total: full.total, tradingTotal: trading.total, cut, dark })); + console.log(`wrote ${fileURLToPath(file).split("/").slice(-2).join("/")}`); + } +} + if (asJson) { console.log(JSON.stringify(results, null, 2)); } else { diff --git a/test/schema-tokens.test.ts b/test/schema-tokens.test.ts index 1ea48ce..4f036f0 100644 --- a/test/schema-tokens.test.ts +++ b/test/schema-tokens.test.ts @@ -44,14 +44,28 @@ async function measureProfile(profile: string) { }; } -test("the README badge states the measured context cost", async () => { +test("the context-cost card states the measured numbers", async () => { + // The card is generated by `npm run brand:card`, never hand-drawn, so the + // only way it can lie is if someone edits the SVG or forgets to regenerate. + // Both variants are checked: a dark-mode reader sees a different file. const full = await measureProfile("full"); - const shown = asK(full.total) as string; - assert.ok( - README.includes(`badge/🧮_${shown}_Context_Tokens`), - `badge URL should show ${shown} (measured ${full.total} tokens)`, - ); - assert.ok(README.includes(`alt="${shown} context tokens"`), "badge alt text"); + const trading = await measureProfile("trading"); + const cut = Math.round((1 - trading.total / full.total) * 100); + const pct = Math.round((full.total / 200_000) * 100); + + for (const variant of ["context-cost.svg", "context-cost-dark.svg"]) { + const svg = readFileSync(new URL(`../assets/${variant}`, import.meta.url), "utf8"); + assert.ok(svg.includes(`${asK(full.total)} tokens`), `${variant}: headline figure`); + assert.ok(svg.includes(`${pct}% of a 200K context window`), `${variant}: reference unit`); + assert.ok(svg.includes(`${asK(trading.total)} with`), `${variant}: trading figure`); + assert.ok(svg.includes(`${cut}% less`), `${variant}: saving`); + } +}); + +test("the README shows both card variants", () => { + // Without the , dark-mode readers get the light card on a dark page. + assert.ok(README.includes('srcset="assets/context-cost-dark.svg"'), "dark source"); + assert.ok(README.includes('src="assets/context-cost.svg"'), "light fallback"); }); test("the profile table states each profile's measured cost", async () => {