@mvoom/mcp
Your agent measures. You sign. Four tools, one of which can spend money — and that one asks a person first.
Your agent measures. You sign. The mvoom connector for any MCP client — four tools, one of which can spend money, and that one asks a person first.
npm i -g @mvoom/mcp # the binary is `mvoom-mcp`
api.mvoom.com opens, point MVOOM_API at a service you run yourself (npm run api in the mvoom repository). The package is published now so the install path is real from day one.{
"mcpServers": {
"mvoom": { "command": "mvoom-mcp" }
}
}
Run mvoom auth once (from [@mvoom/cli](https://www.npmjs.com/package/@mvoom/cli)) and the connector is signed in — it reads the same key, from MVOOM_API_KEY or ~/.mvoom/credentials. No arguments, no config file.
The four tools
| Tool | What it does | Money |
|---|---|---|
mvoom_get_wallet_balance | credits available, the ceiling your agent may spend alone, last movements | none |
mvoom_discover_page | what moves on a page — each piece with a selector and a price range | none |
mvoom_inspect_component | measures one piece in a real browser and returns the brief plus a signed quote | none — spends one read of your allowance |
mvoom_purchase_and_inject | buys the component behind a quote and writes its files into the project | debits credits |
Quoting and buying are separate tools on purpose: an agent that could only learn the price by buying would buy to learn the price.
mvoom budget).How the signature reaches you
Two channels, in this order:
- Through the client — MCP's own
elicitation/create. If your client declared it at
initialize, the question appears in its interface, whatever that is, and the answer comes back to the connector. Only accept with authorize: true is a signature. Decline, cancel, an empty answer or anything that is not exactly true is a no.
- In the terminal — if the connector was launched from one. It opens the console device
(/dev/tty, or CONIN$/CONOUT$ on Windows) alongside the protocol channel and prints:
MVOOM · WALLET GATE Component: art_7f3a This will debit: 1,860 credits (≈ $18.60 USD) Your balance: 4,200 credits Do you authorize your agent to debit this component from your wallet? [y/N]
NO_SIGNATURE_CHANNEL with the numbers and where to buy instead (mvoom.cc). It does not assume yes, does not lower the ceiling, and does not ask the model. Set MVOOM_HEADLESS=1 when the connector is launched by an application that is not a terminal: an inherited console nobody is looking at would otherwise hold the question forever.The server enforces it too. POST /v1/purchase above the ceiling without signed: true answers SIGNATURE_REQUIRED — a patched client does not get around the rule, it just gets the refusal from somewhere else.
Refusals carry numbers
A model given "not enough credits" has to guess the figures, and when it cannot find them it invents them. So every refusal carries a context:
{
"ok": false,
"code": "INSUFFICIENT_CREDITS",
"message": "not enough credits: 1860 needed, 100 available",
"context": { "requiredCredits": 1860, "availableCredits": 100, "missingCredits": 1760, "rechargeUrl": "https://mvoom.cc/wallet" }
}
| Code | When | What to do |
|---|---|---|
INSUFFICIENT_CREDITS | the wallet cannot cover the piece | a person recharges at the URL — no signature is asked for something that cannot be paid |
USER_DENIED_TRANSACTION | the person said no | nothing was charged; do not retry |
NO_SIGNATURE_CHANNEL | above the ceiling and no way to ask | buy at mvoom.cc |
SIGNATURE_REQUIRED | the server was asked without a signature | you should not see this from this connector |
What goes into the context window
A summary, never the spec. Each response lands in someone's context and every token there is paid by them and degrades their model. A piece travels as its brief — what moves, how long, on what curve, and the price. The code, which is what is sold, travels once at purchase and goes to disk.
mvoom_purchase_and_inject writes only inside the project — the directory the connector was started in. A targetPath with ../ in it, or an absolute path elsewhere, is refused before the service is even asked. The service never learns where your files live.
Optional: adapt with your own local model
Off by default. Set MVOOM_LOCAL_MODEL=http://localhost:11434 (Ollama, or anything that speaks its /api/generate) and, if you like, MVOOM_LOCAL_MODEL_NAME=qwen2.5-coder:7b, and a fifth tool appears: mvoom_adapt_locally. It takes a file you already own and an instruction — port this to Vue, move it to CSS modules, restyle it — and writes the result next to the original (Hero.adapted.tsx), never over it. It runs on your machine and costs nothing.
What it does not do: name a piece, price it, or write the recipe that carries mvoom's name. Those are measured and signed by the service; what your model writes is your text, and the output says so in its first line.
Zero dependencies, including the protocol
MCP over stdio is JSON-RPC 2.0, one message per line. A server with four tools needs five methods, and they fit in one file you can read in a sitting — which is the only honest way to trust a program your agent runs with your key. stdout carries protocol and nothing else; logs go to stderr.
Support
| Versioning | Semantic, with a hand-written changelog |
| Security | security@vorluno.dev — first response within 48 hours |
| Licence | MIT. The engine that produces the measurements is not — see ADR-MV016 |
<sub>Built and maintained by Vorluno Software, S.A. Every figure the connector shows was measured in a real browser, never inferred from a screenshot.</sub>