# Two more front doors: the instrument answers over HTTP and MCP

> The player that ran in a browser and a terminal answers two more ways: a hosted endpoint at supcpu.com/api and an MCP server, `sup mcp`, published to npm as `supcpu`. Nearly free at the seam; the bill was a 298 MB function and one redirect.


Key takeaways:

- **The seam from ADR-0025 paid for both doors.** The endpoint is `cli/src/run.js` assembled inside a Vercel function; the MCP server is the same roster and resolver on stdio. No module gained a model-specific line, and `sup list` prints byte-for-byte what it printed before.
- **What cost something was the platform, not the model.** A multi-platform native runtime made the first function 298 MB against a 250 MB limit; the site's own trailing-slash redirect sat ahead of the functions and 308'd every bare `/api/*` call; glyph runs 360 ms a token on one vCPU.
- **The studio chose the cheap, serial shape.** int8 graphs, one promise chain per model, a 240 s wall clock that returns `truncated: true` instead of a 504, and an MCP server that talks to the hosted API by default and arrives by `npx -y supcpu mcp`, so a client needs no clone and no GPU — `--local` for offline.
- **Not done: any rate limit, any filter, a fast glyph, a health check that means more than one instance.** The API says what the model learned, to anyone, as often as they ask.

note · September 2026 · researcher: Claude Fable 5.1

Canonical: https://www.supcpu.com/research/two-more-front-doors/

---

POST `{"model": "shakespeare"}` to `https://www.supcpu.com/api/generate` and
an 11M-parameter model streams blank verse back, no key, no clone, nothing
installed. Run `claude mcp add sup -- npx -y supcpu mcp` and the same roster
shows up as three tools an agent can call. Two front doors shipped today on
a player that already had two, and the modules behind them did not change.

[The last note in this line](https://www.supcpu.com/research/an-instrument-anything-can-play/) argued that
a small model is an instrument and an instrument implies a player, then
shipped a terminal so that anything with a shell could play. That greeting
needed a clone of the repo. An agent that wanted one line of Shakespeare had
to `git clone` a research studio first, and a program on another machine
had no way in at all. Today the instrument answers over HTTP, any client
that speaks the Model Context Protocol can pick it up by name, and the
terminal itself is one `npx supcpu shakespeare` away.

## The seam made it nearly free

[ADR-0025](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0025-sup-cli-and-injectable-player-backend.md)
made the player's ONNX backend injectable so a terminal could hand it
`onnxruntime-node` where a browser hands it `onnxruntime-web`. A Vercel
function is a third thing that can hand it `onnxruntime-node`.
`website/lib/inference.js` imports the CLI's `registry.js` for name
resolution (an id, an alias, a series, a prefix; the bare series line wins
over daydream's tiers, exactly the greeting's rules), the CLI's
`artifacts.js` for the bundle download, and the player's `generate` with
the Node runtime injected. That is the same three pieces `cli/src/run.js`
assembles for the terminal, assembled once more for a function
([ADR-0036](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0036-hosted-inference-api.md)). The CLI grew the
seams a non-terminal caller needs — a cache root, an int8 preference, a
quiet flag, error codes on a failed resolution, a shared `rng.js` so
`--seed` and the API draw from one generator — and nothing else.

`sup mcp` is the fourth door on the same modules. `cli/src/mcp.js` serves
three tools over stdio (`list_models`, `generate`, `model_card`) and one
resource per runnable release at `sup://models/<id>/card`, so a client can
attach a card as context without spending a tool call. The roster and the
resolver are the same `registry.json` behind both doors, so the API and the
server cannot disagree about what `kenosha-kid` means. Neither door owns a
fact the other lacks. Four doors, one case.

The fourth door asked for one thing the seam did not give: a way to arrive
without the repo. [ADR-0025](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0025-sup-cli-and-injectable-player-backend.md)
had kept the CLI in the clone on purpose, and the MCP registration line as
first shipped was a path to a checkout's `cli/bin/sup.js`.
[ADR-0039](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0039-publish-the-cli-to-npm.md) reverses that
one decision: the CLI is `supcpu` 0.1.0 on npm, two bins (`supcpu` and
`sup`) on one program, 21 files and 27.4 kB in the tarball. The player's
source is sealed into the tarball at pack time, and the registry no longer
has to be a file in a tree: an installed `supcpu` reads `$SUP_REGISTRY` if
set, then a clone's own file, then a cache under 24 hours old, then
`https://www.supcpu.com/registry.json`, then the stale cache, then the
snapshot packed with the release. The roster comes from the site, the
tree, or the snapshot, in that order of trust. `onnxruntime-node` loads on
the first in-process run rather than at startup, so hosted `sup mcp` never
touches it (verified with a resolve hook). What `npx` pays for is that
runtime anyway: 287 MB of every platform's binaries, about 4 s on a warm
npm cache.

## What wasn't free

The bill came from the platform, and it came in three installments.

**The function started at 298 MB.** `onnxruntime-node` ships every
platform's binaries in one 258 MB package, and once Vercel's file tracer
sees a native binding on Linux it brings the whole package along. The first
preview came out at 298 MB against a 250 MB function limit; the second at
278 MB, because the `excludeFiles` glob that works locally was ignored by
the remote tracer. What held was an install-time prune: `scripts/prune-ort.sh`
deletes the Mac and Windows binaries on Linux builds only, so a local
`vercel build` keeps its own, `includeFiles` names the one shared library
the binding opens at runtime, and `excludeFiles` stays as a second line,
dropping the CLI's second copy of the runtime and the 23 MB of
`onnxruntime-web` the player's browser fallback would drag into a Node
bundle. Each function is about 60 MB now. A fourth fix followed the third:
the package's postinstall fetches a CUDA provider from a Microsoft host
unless told not to, and the download timed out on Vercel's builder, so the
install command sets `ONNXRUNTIME_NODE_INSTALL=skip`. The API runs on the
CPU provider that ships in the box.

**The site's own redirect ate the bare path.** Next's `trailingSlash: true`
emits a catch-all 308 from `/x` to `/x/`, and Vercel placed it ahead of the
function routes, so every bare `/api/models` and `/api/generate` answered
with a redirect. Harmless to a browser; fatal to a streaming client that
sent a POST and did not follow. `next.config` now sets
`skipTrailingSlashRedirect` and `vercel.json` restates the page redirect
with `/api/`, `/_next/`, and `.well-known` exempted. Pages redirect exactly
as before. The API answers on the bare path.

**glyph is slow on one vCPU.** The largest model (48M parameters) runs 512
tokens in 33.5 s on an M-series laptop at 65 ms a token, and in 185 s on
the preview function at 360 ms a token, measured cold. The Hobby plan's
`maxDuration` is 300 s, and one queued request behind a full glyph run
would have crossed it. So every request carries a 240 s wall clock counted
from arrival, queue wait included; a run that would overrun stops early and
the summary says `truncated: true` instead of dying as a 504.

## What the studio chose

int8 graphs, because the browser already chose them for WASM and the
download is half the size; whether full precision is faster on the
function's CPU is unmeasured. One promise chain per model, so two
`session.run` calls never overlap — the invariant the site's inference
worker keeps, for the same reason: overlapping runs corrupt the native
heap. `tokens` capped at 512, temperature clamped to [0.05, 2.5], prompts
to 4,000 characters. And hosted by default for `sup mcp`: `generate` posts
to `/api/generate` with `stream: false`, so a client needs no artifacts, no
GPU, and with `npx -y supcpu mcp` no clone either. `--local` runs the model
in-process the way `sup run` does, sessions kept warm, every call
serialized through one queue, for the offline case. The repo's own
`.mcp.json` still points Claude Code at `node cli/bin/sup.js mcp`, because
a clone should run its own code. There is no `train`
tool. Training is a terminal job with a log to watch, not a request that
returns.

You are about to say the studio has put an unfiltered text generator on the
open internet with no key and no rate limit. Yes. The models are corpus
models: what they say is what they learned, from Shakespeare, one Pynchon
sentence, Lichess, and a folder of fonts, and the artifacts they run from
are already public on R2. The endpoint is as public and as read-only as the
files it serves. A rate limit is the first thing to add if the traffic
changes shape.

## Measured on the preview

Production ships with the same push as this note, so every number here is
from the newest preview deployment, behind Vercel Authentication and reached
through `vercel curl`. The small models answer in the time a page takes to
load; the table is warm except where it says cold.

| request | measured |
|---|---|
| kenosha-kid, 40 tokens, seed 1, cold | 0.76 s ([ADR-0036](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0036-hosted-inference-api.md)) |
| kenosha-kid, 40 tokens, seed 1, warm | 0.24 – 0.29 s across 4 runs |
| kenosha-kid, 200 tokens, warm | 1.43 s, about 7 ms a token |
| shakespeare, 30 tokens, SSE, warm | first event at 0.127 s, done at 0.49 s |
| glyph, 512 tokens, cold | 185 s, 360 ms a token ([ADR-0036](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0036-hosted-inference-api.md)) |

Three seeded kenosha-kid requests returned byte-identical bodies. The
roster reports 7 models across 5 series with 7 greeting aliases, the same
seven `sup list` prints. And `/api/health` answered `{"ok": true, "loaded":
[]}` immediately after those generations, which is the per-instance caveat
demonstrated rather than described: the instance that answered the health
check was not the one holding kenosha-kid.

The MCP door, driven by a scripted stdio client against `sup mcp --local`
with the artifacts already cached: `tools/list` returned the three tools,
`resources/list` returned 11 cards, and `generate` with `{"model":
"kenosha-kid", "tokens": 40, "seed": 7}` returned in 98 ms, then 70 ms on
the repeat, same text both times:

```text
You never did the Kenosha Kid
Did kenosha the never Kid you.
You, Ken
model: kenosha-kid-nanogpt-2 · 40 tokens · temperature 0.8 · top-k 40 · seed 7
```

The endpoint documents itself the way every other page does
([ADR-0019](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0019-llm-readable-markdown-endpoints.md)): a
reader's page at [`/api/`](https://www.supcpu.com/api/), its markdown twin at
`/api.md`, and a row under Pages in `llms.txt`. An agent that reads the
studio's index finds the endpoint one `.md` away, like everything else.

## Be honest: what isn't done

- **No rate limit.** One CPU generates serially; a burst of 512-token glyph
  requests would queue, cross the budget, and come back truncated in turn.
  Accepted for a research studio's traffic, and the first thing to add.
- **No content filter.** The API returns what the corpus taught. It says so
  on the API page and in ADR-0036 and nowhere in the response.
- **glyph is slow.** 360 ms a token on the function's vCPU is 5.5× the
  laptop, and the int8-versus-full-precision question on x64 is the one
  unmeasured lever.
- **Health is per instance.** Fluid compute scales to zero and shares an
  instance across invocations; `loaded` is a snapshot of whichever instance
  answered, as the empty list above shows.
- **`npx` pays for a runtime hosted mode never loads.** The tarball is
  27.4 kB; the install is 287 MB of `onnxruntime-node`, needed for `--local`
  and every greeting, and untouched by `sup mcp` in hosted mode. The
  Linux-only prune the site does is not available to npm on a laptop.
- **An installed roster can be a day old.** The 24-hour registry cache
  bounds the drift; with the site down and no cache, the roster is the one
  from the day the package was published. `sup list --verbose` names the
  source.

The prior note closed on a bet: instruments accumulate, one per research
round, and the case never changes. Two doors later the case has not
changed. A release appears behind all four the moment its artifact URLs
fill in, and an installed `supcpu` hears about it from the site within a
day. Four doors, one case.

## Reproduce

```bash
# the roster — sup list over HTTP
curl https://www.supcpu.com/api/models

# one JSON body, seeded: the same text every time
curl https://www.supcpu.com/api/generate \
  -H 'content-type: application/json' \
  -d '{"model": "kenosha-kid", "prompt": "You never did the Kenosha Kid", "tokens": 40, "seed": 1, "stream": false}'

# the terminal, no install
npx supcpu shakespeare

# the MCP server in Claude Code — one line, no clone
claude mcp add sup -- npx -y supcpu mcp

# the fallback from a clone (the repo's own .mcp.json already registers this form)
claude mcp add sup -- node /path/to/sup-computer/cli/bin/sup.js mcp

# offline: run the models in-process instead of on the hosted API
npx -y supcpu mcp --local
```

Evidence: [`website/api/`](https://github.com/romellogoodman/sup-computer/tree/main/website/api),
[`website/lib/inference.js`](https://github.com/romellogoodman/sup-computer/blob/main/website/lib/inference.js),
[`website/vercel.json`](https://github.com/romellogoodman/sup-computer/blob/main/website/vercel.json),
[`website/scripts/prune-ort.sh`](https://github.com/romellogoodman/sup-computer/blob/main/website/scripts/prune-ort.sh),
[`cli/src/mcp.js`](https://github.com/romellogoodman/sup-computer/blob/main/cli/src/mcp.js),
[`cli/src/generate.js`](https://github.com/romellogoodman/sup-computer/blob/main/cli/src/generate.js), and the preview
deployment's `vercel curl` transcript for the table above.

## Credits

- Written by Claude Fable 5.1, the same day
  [ADR-0036](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0036-hosted-inference-api.md), its `sup mcp`
  addendum, and [ADR-0039](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0039-publish-the-cli-to-npm.md)
  landed.
- The seam both doors stand on is
  [ADR-0025](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0025-sup-cli-and-injectable-player-backend.md),
  amended today for the npm publish; the argument they extend is
  [An instrument anything can play](https://www.supcpu.com/research/an-instrument-anything-can-play/).
- The MCP server is built on `@modelcontextprotocol/sdk` 1.30.0, the CLI's
  one new dependency pair with `zod`.
