{
  "markdown": "# nanoodle-mcp\n\n**Build a multi-model media pipeline visually at [nanoodle.com](https://nanoodle.com) — then hand the whole pipeline to your agent as ONE typed tool.**\n\n> **Skill or MCP?** Running (or designing) a *single* workflow? The\n> [nanoodle skill](https://github.com/nanoodlecom/nanoodle-skill) is one\n> command and no setup. This server is for when you've built up a *folder*\n> of saved graphs and want each one exposed to your agent as its own tool.\n\nPoint this MCP server at a folder of `noodle-graph.json` saves from the\nnanoodle editor and every graph becomes a callable tool with a derived input\nschema — in Claude Code, Grok, Claude Desktop, Cursor, VS Code, Windsurf, or\nanything else that speaks the [Model Context Protocol](https://modelcontextprotocol.io).\nIt speaks stdio to your own agent by default, or HTTP to everyone with\n[serve mode](#serve-mode--host-your-noodles-as-a-service---serve) — including\n[charging per call in Nano](#charging-per-call---charge-usd), so strangers'\nagents can pay to run your noodles with no account anywhere.\n\nNo middleman server, no telemetry — and with [wallet mode](#wallet-mode--no-account-no-api-key-x402),\nno account either. The MCP implementation here is hand-rolled (stdio +\nstreamable HTTP, JSON-RPC 2.0 — small enough to read). Two runtime dependencies:\n[`nanoodle`](https://github.com/nanoodlecom/nanoodle-js) (MIT), the zero-dep\nworkflow executor that does all the heavy lifting, and\n[`nanocurrency`](https://github.com/marvinroger/nanocurrency-js) (GPL-3.0) for\nsigning Nano blocks — loaded only on the x402 paths, see\n[Licensing](#licensing). Your NanoGPT API key goes straight from your\nmachine to [nano-gpt.com](https://nano-gpt.com); it is never logged and never\nappears on stdout.\n\n## Install\n\nYou need: **Node 20+**, a folder of saved graphs (say `~/noodles` — see\n[Making graphs](#making-graphs)), and a [nano-gpt.com](https://nano-gpt.com)\nAPI key in `NANOGPT_API_KEY` (or passed via `--key` / `--env-file`) — or no\nkey at all with a Nano wallet, see\n[wallet mode](#wallet-mode--no-account-no-api-key-x402).\n\n**Optional: `ffmpeg` on PATH.** Tools whose graphs include a local-media node\n(resize, video frames, combine, audio trim/extract) use a pure-JS path for the\ncommon case but fall back to `ffmpeg`/`ffprobe` for formats it can't handle —\nJPEG/WebP resize, reading video frames, compressed audio. Without ffmpeg those\nruns fail (and auto-refund in charge mode); the server prints a warning at\nstartup naming the affected tools. Pure image/LLM/vision tools don't need it.\nInstall with `apt install ffmpeg` (Debian/Ubuntu) / `brew install ffmpeg`.\n\n### Claude Code\n\n```bash\nclaude mcp add nanoodle --env NANOGPT_API_KEY=your-key-here -- npx -y nanoodle-mcp --graphs ~/noodles\n```\n\nOr install it as a plugin — Claude Code prompts for your noodles folder and\nAPI key, and also learns what a noodle is (this repo doubles as a plugin\nmarketplace):\n\n```\n/plugin marketplace add nanoodlecom/nanoodle-mcp\n/plugin install nanoodle@nanoodle\n```\n\n### Grok\n\n```bash\ngrok mcp add nanoodle -e NANOGPT_API_KEY=your-key-here -- npx -y nanoodle-mcp --graphs ~/noodles\n```\n\nOr, for a remote/serve endpoint (no local process, no API key on your machine):\n\n```bash\ngrok mcp add --transport http noodles https://mcp.nanoodle.com/mcp\n```\n\nThat writes `[mcp_servers.noodles]` into `~/.grok/config.toml` (or\n`.grok/config.toml` with `--scope project`). Same shape by hand:\n\n```toml\n[mcp_servers.noodles]\nurl = \"https://mcp.nanoodle.com/mcp\"\nenabled = true\n```\n\n### Cursor\n\n[Install in Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=nanoodle&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm5hbm9vZGxlLW1jcCIsIi0tZ3JhcGhzIiwifi9ub29kbGVzIl0sImVudiI6eyJOQU5PR1BUX0FQSV9LRVkiOiJZT1VSX05BTk9HUFRfS0VZIn19)\n(then edit the graphs path and key), or add to `.cursor/mcp.json` yourself:\n\n```json\n{\n  \"mcpServers\": {\n    \"nanoodle\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nanoodle-mcp\", \"--graphs\", \"/absolute/path/to/noodles\"],\n      \"env\": { \"NANOGPT_API_KEY\": \"your-key-here\" }\n    }\n  }\n}\n```\n\n### VS Code\n\n`.vscode/mcp.json` — note VS Code's root key is `servers`, not `mcpServers`:\n\n```json\n{\n  \"servers\": {\n    \"nanoodle\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nanoodle-mcp\", \"--graphs\", \"/absolute/path/to/noodles\"],\n      \"env\": { \"NANOGPT_API_KEY\": \"your-key-here\" }\n    }\n  }\n}\n```\n\n### Windsurf\n\n`~/.codeium/windsurf/mcp_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"nanoodle\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nanoodle-mcp\", \"--graphs\", \"/absolute/path/to/noodles\"],\n      \"env\": { \"NANOGPT_API_KEY\": \"your-key-here\" }\n    }\n  }\n}\n```\n\n### Claude Desktop\n\nSame shape as Cursor/Windsurf, in `claude_desktop_config.json` under\n`mcpServers`.\n\n### ChatGPT\n\nChatGPT only connects to remote HTTPS MCP servers. The hosted endpoint is\nalready live — add `https://mcp.nanoodle.com/mcp` (paid per call in Nano, no\nAPI key). Local stdio (`npx nanoodle-mcp --graphs …`) stays BYOK and is not\nreachable from ChatGPT.\n\n## ⚠️ This spends real money\n\nThe server runs on **your** money — either a nano-gpt.com API key (BYOK) or,\nkeyless, your own Nano wallet via\n[x402](#wallet-mode--no-account-no-api-key-x402). **Every `tools/call`\nexecutes a workflow against the NanoGPT API and spends from that balance** —\nand the caller is usually an AI agent deciding on its own when to call. Point\nit only at graphs you're happy to have run, and keep an eye on your balance.\nEach result ends with a `cost: $X.XXXX` line so the agent (and you) can see\nwhat a call cost.\n\n## Wallet mode — no account, no API key (x402)\n\nNanoGPT supports [x402 accountless payments](https://docs.nano-gpt.com/api-reference/miscellaneous/x402):\na keyless API call answers `HTTP 402` with a Nano invoice, you pay it, the\ncall completes. Give the server a wallet and it does this automatically —\n**no NanoGPT account, no API key, no signup anywhere**:\n\n```bash\nexport NANO_SEED=<64-hex seed>        # account 0 pays; or NANO_PRIVATE_KEY=<64-hex key>\nnanoodle-mcp --graphs ~/noodles --max-usd 0.50\n```\n\nPer call, the server: sees the 402 invoice → signs a Nano send block locally →\nbroadcasts it through a Nano RPC node (`--nano-rpc` / `NANO_RPC_URL`, default\n[rpc.nano.to](https://rpc.nano.to)) → NanoGPT detects the deposit and returns\nthe result. The seed/private key never leaves the process: only the *signed\nblock* goes to the RPC node, and neither secret is ever logged. An API key,\nif present, always wins — the wallet is only used keyless.\n\n**Proof-of-work reliability.** Every send block needs Nano proof-of-work. The\nserver asks the RPC node's `work_generate` first, but public nodes routinely\nrefuse or throttle it (no GPU, key required); the fallback is local\nsingle-threaded CPU work, which can take a minute. Two fast options, both via\n`--work-rpc` / `NANO_WORK_URL`:\n\n- **Hosted GPU work API** — sub-second work, no hardware. E.g.\n  [nano.to's PoW API](https://docs.nano.to/pow) (`--work-rpc https://rpc.nano.to`)\n  or [Nanswap Nodes](https://nanswap.com/nodes)\n  (`--work-rpc https://nodes.nanswap.com/XNO` — the fast work server is on\n  their paid plan; the free one queues for minutes). Put the API key in\n  `NANO_WORK_KEY` (env or `--env-file`, never a flag) — it's sent as both a\n  `key` body field (nano.to style) and a `nodes-api-key` header (Nanswap\n  style).\n- **Your own [nano-work-server](https://github.com/nanocurrency/nano-work-server)**\n  on the same machine (`nano-work-server --cpu-threads 8 -l 127.0.0.1:7076` →\n  work in a few seconds, free forever).\n\nOrder: `--work-rpc` (2-minute timeout, so a hung server can't stall a payment)\n→ `--nano-rpc` node → local CPU. The wallet also **precomputes work for the\nnext block** the moment one publishes, and serve mode prewarms at boot — with\nany healthy work source, callers effectively never wait on proof-of-work.\n\nThe local-CPU last resort runs on the Node main thread: in `--serve` mode a\nsend-difficulty grind (minutes) freezes the whole HTTP server while it runs.\nIf your remote work sources are dependable, pass `--no-local-work` — the send\nthen fails cleanly instead (an unpayable invoice is refused up front, and a\nfailed refund/payout send leaves the balance in the wallet for the next\nsettle to move).\n\n**This is a hot wallet.** Use a dedicated wallet holding pocket money, not\nyour savings: its balance is a natural spend ceiling, and `--max-usd` adds a\nper-call one on top. NanoGPT auto-refunds overpayments and failed generations\non its side.\n\n**Prefer prepay?** Wallet mode settles on-chain per call. If you'd rather pay\nonce and draw down a balance, that's exactly what a NanoGPT account is:\n[deposit crypto](https://docs.nano-gpt.com/api-reference/endpoint/crypto-deposits)\n(Nano included) into an account, take its API key, and run the server in\nnormal BYOK mode — same tools, one payment instead of many.\n\n## Serve mode — host your noodles as a service (`--serve`)\n\nEverything above speaks MCP over stdio to *your own* agent. `--serve` speaks\nMCP over **streamable HTTP** instead, turning a directory of noodles into a\nservice *anyone's* agent can connect to:\n\n```bash\nnanoodle-mcp --graphs ~/noodles --serve 8402\n```\n\nCallers connect with one command — no key, no signup:\n\n```bash\nclaude mcp add --transport http noodles https://your-host/mcp\n# or\ngrok mcp add --transport http noodles https://your-host/mcp\n```\n\n`GET /` serves a landing page with the tool list and that exact command (Claude\nand Grok behind a one-click toggle — same endpoint, same flags), so sharing\nyour server's bare URL *is* the onboarding. Every workflow on it links to its\nsource: an **open in editor** link (a share link minted from the exact graph\nfile being served — it loads the workflow in the\n[nanoodle editor](https://nanoodle.com) to inspect, remix, or run on your own\nkey) and its raw **graph JSON** at `/graph/<tool>.json`. The page also spells\nout the economics (deposits settle at metered cost + 20%; the markup is the\nauthor's cut on graphs that name a payout address, and the operator's on the\nrest, and the page says which of yours do) and how to self-host — this server, the editor, and the\nexecutor are MIT ([licensing](#licensing)). Generated media is served\nback under unguessable `/out/…` URLs (small images also ride inline in the\ntool result). Runs themselves are **not logged**: free serve mode keeps no\nrecord of who called what, and charge mode keeps only a payments ledger (money\nevents, never run telemetry — see below).\n\n**Generated media is deleted after `--out-ttl` hours** — a privacy backstop so\na hosted server doesn't hoard every caller's generations on disk forever. In\n`--serve` mode it defaults to **24h**; pass `--out-ttl <hours>` to change it\n(fractions like `0.5` are fine) or `--out-ttl 0` to keep everything. Stdio mode\ndefaults to **off** — files you generated locally are yours to keep — but honors\nthe flag if you pass it. The sweep only ever removes media artifacts (images,\nvideo, audio); the operator's own `costs.json`, `gate-state.json`, and\n`usage.jsonl` are never touched. Note the 24h default matches the charge gate's\nreplay window: a paid result stays replayable for 24h and references its `/out/`\nURL, so lowering `--out-ttl` below 24h in charge mode means a replayed result\ncan point at a file that's already been swept.\n\n### Privacy — what your server keeps\n\nRun with the defaults and the same promise the hosted server makes holds on\nyour box, every line checkable in this repo:\n\n- **No accounts, no API keys, no sign-ins.** In charge mode, paying is the only\n  thing that authorizes a call.\n- **Prompts and inputs are never written to disk or logs.** Input media rides\n  in memory only — it is never stored. In charge mode the gate keeps only a\n  short hash binding a payment to its call (`hashArgs`), never the arguments.\n- **Generated media auto-deletes after `--out-ttl` hours** (default 24h in\n  `--serve`, off for local stdio), served at unguessable `/out/…` URLs until\n  then. Set `--out-ttl 0` to keep everything.\n- **Results are held in memory for delivery, not stored** — the restart-state\n  file carries no result text or error detail.\n- **No analytics, no trackers, no IP logs** — the app keeps no request logs.\n  In charge mode the one thing it writes is `usage.jsonl`, a payments ledger of\n  money-lifecycle events that mirror the public Nano ledger — no run telemetry,\n  no error text. Free serve mode writes no ledger at all.\n\n`gate-state.json` itself never holds customer content. It exists to keep\nin-flight money safe across the restart a deploy causes — quotes and owed sends\nsurvive — but a paid tool's **text output and any upstream error detail are\nnever written to disk**. Media results persist only their `/out/` pointer and a\nreceipt; a text result persists as nothing, so a restart between a run and the\ncaller's retry simply re-runs the tool (charged once, delivered once — the\noperator eats one duplicate model call) rather than replaying content from disk,\nand a failed run replays a fixed placeholder plus its refund status, never the\noriginal error message.\n\nOne caveat, stated plainly: generation runs on [NanoGPT](https://nano-gpt.com)'s\nAPI, so prompt content in flight is governed by\n[their privacy policy](https://nano-gpt.com/privacy).\n\nFree serve mode runs on **your** balance — fine on a trusted network, ruinous\non the open internet. For that, charge for calls:\n\n### Charging per call (`--charge-usd`)\n\n```bash\nnanoodle-mcp --graphs ~/noodles --env-file wallet.env \\\n  --serve 0.0.0.0:8402 --public-url https://noodles.example.com --charge-usd 0.05\n```\n\nNow every tool call is paid in Nano (XNO) **by the caller**, with no accounts\non either side. The flow their agent walks through (the server's MCP\n`instructions` teach it automatically):\n\nOn each **PAYMENT REQUIRED** quote the tools/call **hangs up** with `payUrl`,\n`watchUrl`, and a `next` imperative ( `blocking: false` ):\n\n1. **Pay.** Show the user ONLY `payUrl` (QR for the exact amount); any Nano\n   wallet scans it. Never show them `watchUrl` or the wallet address.\n2. **Watch — immediately, on that same payment link.** The agent opens\n   `watchUrl` (`GET /x402/watch/<id>`, agent-only SSE) the moment it has the\n   quote. One `status` event per state change; when payment lands the stream\n   **closes** with `done: true` and a `next` field for phase 3. (The pay page\n   uses the same stream; poll `GET /x402/status/<id>?wait=1` as a fallback.)\n   Detection is RPC polling or push via `--nano-ws` — about a second.\n3. **Results.** ONLY after the watch closes paid: re-call the tool with the same\n   arguments plus `_payment_id`. That `tools/call` is the **results stream** —\n   it runs the workflow (progress heartbeats on a streaming transport) and\n   returns the result with a receipt. Do not open it while still watching for\n   payment. Re-calls with the same `_payment_id` replay the cached result free.\n\n**What they paid is a deposit, not the price**: the call settles at the run's\n*actual* metered model cost + 20%, and everything above that is sent back to the\npaying wallet as change — the same deposit→meter→refund model NanoGPT itself\nuses, one layer up. Nobody ever pays off an estimate.\n\nNano has no payment memo, so each quote's amount carries a few raw of random\ndust — **the amount is the memo**. Quotes expire after 15 minutes; a payment\nthat arrives late is bounced straight back, and **a run that fails after\npayment is refunded automatically**. Arguments are validated *before* a quote\nis issued (nobody pays for a typo), and `run_noodle` is withdrawn in charge\nmode — an arbitrary share link's cost can't be priced up front.\n\nPricing: `--charge-usd` sets the **ceiling** deposit, and the opening quote\nfor a tool that has never run. Once a tool has run, its deposit tracks its\nreal cost automatically: quotes become twice the settle price (worst observed\nmetered cost + 20%, from the `costs.json` sidecar), ceiled to a whole cent\nwith a 1¢ floor — a tool that meters $0.004 quotes a $0.01 deposit, not the\nflat ceiling. Size the ceiling generously; over-covering costs callers\nnothing since the difference comes back as change, while an under-sized\ndeposit means runs can cost more than was collected and *you* eat the\ndifference (the gate warns at startup when a graph's deposit is below its\nlast observed cost + 20%). The XNO conversion comes from **NanoGPT itself**:\nthe gate fires a keyless `x-x402` probe and keeps the invoice's exact\nraw-XNO/USD pair — the same rate your downstream payments settle at, so\nquotes and costs can never drift apart on FX, and no market-data service is\ninvolved (probe invoices are never paid; they just expire). Cached 60s, stale\ncache rides out probe outages, and `--xno-usd` forces a static rate if you\never need one. Per-graph overrides are a hand-added top-level block in the\ngraph JSON:\n\n```json\n\"x402\": { \"usd\": 0.10, \"author\": \"nano_1abc…\" }\n```\n\n`usd` overrides the deposit. `author` routes **the 20% markup of every\nsuccessful call to that address** — computed on what the run *actually* cost,\nnever on the deposit, and capped by whatever the deposit has left once the cost\nis taken out (a deposit too small to cover cost + 20% pays the author only the\nremainder). Nano has no network fees and this server takes no cut of the\nmarkup, so creators keep 100% of what does get routed. **With no `author`\nfield the markup stays with the wallet running the server** — it is opt-in per\ngraph, not the default. In exact raw, per settled call:\n\n```\ncost   = metered model cost, converted at the deposit's own oracle pair, rounded up\nmarkup = cost / 5                    (20%, integer floor)\ntake   = min(markup, deposit − cost) → author (or kept, if no author field)\nchange = deposit − cost − take       → back to the payer\n```\n\nA run costing more than its deposit keeps the whole deposit and the operator\nabsorbs the excess; a run whose model reports no cost settles at $0 and the\nwhole deposit is returned (the caller is never billed off a number the meter\ndidn't produce). The math is **integer-exact end to end**: USD decimals parse\nby string into nano-dollars, the rate is the oracle invoice's literal\nraw/USD pair, every conversion is BigInt ratio arithmetic with an explicit\nfloor or ceil, and floats never touch an on-chain value.\n\nThe wallet (`NANO_SEED` / `NANO_PRIVATE_KEY`, via `--env-file`) receives\npayments, sends refunds and author payouts, and — if you don't set an API\nkey — also pays NanoGPT per call via x402, making the whole service\nnano-in/nano-out. With an API key, runs spend from the key and the wallet only\nhandles the customer side.\n\n### Payment detection\n\nPolling `receivable` on your Nano RPC node (1s while quotes are pending) is\nthe always-on default. Add `--nano-ws wss://…` to subscribe to a Nano node\nwebsocket for push detection — settlement is then effectively instant and\npolling relaxes to a safety net. Public websockets exist (e.g.\n`wss://nanoslo.0x.no/websocket` worked at the time of writing); your own node\nis the dependable option. Either way the poller also checks `account_history`,\nso payments pocketed by a concurrently-running wallet are still found.\n\n### Payments ledger\n\nIn charge mode `<out>/usage.jsonl` is a **payments ledger**: one line per money\nevent only — `quote`, `paid`, `refund`, `change`, `author_payout` — your\nserver's own record of money moving, nothing client-side. It is deliberately\n**not** a usage log: there are no `run` events, no run timing, no\nsuccess/failure telemetry, and no upstream error strings (which can quote user\ncontent). A money event does carry `.tool` — the tool name is the *subject of\nthe payment*, not a record of a run — so you can see what earns, but not who\nran what when or whether it failed. Refunds record a fixed category\n(`run_failed`, `late_payment`), never the underlying error text — the full\nerror still reaches the caller and the operator's stderr. Free serve mode\nwrites no ledger at all. Some starters:\n\n```bash\njq -r 'select(.event==\"paid\") | .tool' usage.jsonl | sort | uniq -c | sort -rn  # paid calls per tool\njq 'select(.event==\"paid\") | .settleMs' usage.jsonl                             # quote→settle latency\njq 'select(.event==\"paid\") | .usd' usage.jsonl | jq -s add                      # gross deposits taken\njq -r 'select(.event==\"refund\") | .reason' usage.jsonl | sort | uniq -c         # refunds by category\n```\n\n### Hosting checklist\n\n- **Reverse proxy / tunnel**: bind localhost, put Caddy/nginx/cloudflared in\n  front for HTTPS, and set `--public-url` to the outside URL (it's what pay\n  links and media links are built from).\n- **systemd** (adjust paths):\n\n  ```ini\n  [Unit]\n  Description=nanoodle-mcp serve\n  After=network-online.target\n\n  [Service]\n  ExecStart=/usr/bin/npx nanoodle-mcp --graphs /srv/noodles --out /srv/noodle-out \\\n    --env-file /srv/wallet.env --serve 8402 --public-url https://noodles.example.com \\\n    --charge-usd 0.05 --work-rpc http://127.0.0.1:7076\n  Restart=on-failure\n\n  [Install]\n  WantedBy=multi-user.target\n  ```\n\n- **Keep the float small**: the wallet is hot. Sweep revenue to cold storage\n  regularly; if you run with an API key, cap that account's balance too.\n- **Work server**: refunds and payouts are Nano sends and need proof-of-work —\n  use a hosted GPU work API (nano.to, Nanswap — key in `NANO_WORK_KEY` in the\n  env file), or point `NANO_WORK_URL` at a `nano-work-server --gpu` on any box\n  you own over a tailnet (work requests carry only block hashes — nothing\n  secret). Pair either with `--no-local-work` so a work outage can't freeze\n  the server on CPU work. Wallet-material and key env vars all belong in the\n  `--env-file`, which also takes `NANO_WS_URL` for a key-bearing websocket URL\n  (e.g. Nanswap's `wss://nodes.nanswap.com/ws/?ticker=XNO&api_key=…`).\n- There is intentionally **no auth**: on a charged server, payment is the\n  authorization. Don't serve graphs you wouldn't want strangers running.\n\n## How it works\n\n```\n~/noodles/\n  generate-hero-image.json   →  tool \"generate-hero-image\"\n  make-jingle.json           →  tool \"make-jingle\"\n```\n\nEvery readable `*.json` graph in `--graphs` becomes one MCP tool:\n\n| Tool field | Derived from the graph |\n| --- | --- |\n| `name` | filename minus `.json`, sanitized to `[a-z0-9_-]` (duplicates get `-2`, `-3`, …) |\n| `description` | the graph's first comment (if any), its node chain in dependency order with node names (e.g. `text:Feature -> llm -> image:Mockup`), a `returns …` contract (output kinds with the sink's model/size and the saved-to-disk note), a spend warning, and — once the tool has run — its last observed cost (`last run $0.018`) |\n| `inputSchema` | one string property per unwired field, exactly like the nanoodle CLI's `inspect`; dropdown fields become `enum`s; only inputs without a baked-in default are `required` |\n| media inputs | image / audio / video inputs take a **file path or https URL** — local files ride inline as base64 |\n| result | text outputs as text blocks; media outputs saved into `--out` (default `./nanoodle-out`) with the absolute path returned; a final text block reports the run's cost; a leading `note:` block whenever the run changed something the caller asked for (see below) |\n\n**When the run does not use your prompt verbatim, the result says so.** Many\nimage and video models reject an over-long prompt outright, and in a graph the\nprompt is usually written by an upstream LLM — nobody typed it, so nobody can\nshorten it. `nanoodle` (0.8.0+) trims it to the model's cap at a sentence\nboundary instead of losing the run to a certain 400. That is only defensible if\nthe caller is told, so the trim leads the tool result:\n\n```\nnote: prompt trimmed for \"Poster\" (qwen-image-3) — the prompt was 1320 characters\nand this model accepts at most 800, so nanoodle cut it to 791 characters at a\nsentence boundary. The run used the shortened prompt.\n```\n\nIt matters most in charge mode, where that run is already paid for. The library\nalso emits a `process` warning, but stderr belongs to whoever started the server,\nnot to the agent that paid.\n\nProtocol behavior worth knowing: malformed calls (unknown tool, unknown /\nmissing / non-string argument) are rejected as JSON-RPC `-32602` **before any\nmoney is spent**; a run that fails (network, model error, missing key) comes\nback as a normal tool result with `isError: true`.\n\n```\nusage: nanoodle-mcp --graphs <dir> [--graphs <dir> …] [--out dir] [--key K] [--env-file path] [--nano-rpc url] [--work-rpc url] [--max-usd n]\n\n  --graphs dir   directory of noodle-graph.json saves (required; repeat to serve\n                 several dirs — scanned in order, so an earlier dir wins name clashes)\n  --out dir      where media outputs are saved (default ./nanoodle-out)\n  --key K        NanoGPT API key (defaults to NANOGPT_API_KEY)\n  --env-file p   read NANOGPT_API_KEY / NANO_SEED / NANO_PRIVATE_KEY / NANO_WORK_URL /\n                 NANO_WORK_KEY / NANO_WS_URL from a .env-style file\n  --nano-rpc u   Nano RPC node for wallet mode (default https://rpc.nano.to; NANO_RPC_URL)\n  --work-rpc u   dedicated work_generate endpoint — a local nano-work-server or a hosted\n                 GPU work API (NANO_WORK_URL; NANO_WORK_KEY carries its API key;\n                 falls back to --nano-rpc, then local CPU work)\n  --max-usd n    wallet mode: refuse any single x402 invoice above $n\n```\n\nKey precedence matches the nanoodle CLI: `--key` > `--env-file` >\n`NANOGPT_API_KEY`; wallet secrets come only from the environment or\n`--env-file`, never argv. The server refuses to start if no directory holds a\nrunnable graph, and says why per file on stderr; stdout is protocol only.\n\n### Per-project graphs + a shared library\n\n`--graphs` is repeatable, so one server can merge several folders. The pattern:\ncommit a `noodles/` dir to a repo for that project's own tools, then list your\nglobal library after it as a fallback:\n\n```\nnanoodle-mcp --graphs ./noodles --graphs ~/noodles\n```\n\nDirs are scanned in the order given, and **the earlier dir wins a name clash**:\nif both folders have a `changelog.json`, the project's becomes `changelog` and\nthe shared one becomes `changelog-2`. So a project can override a shared tool\njust by dropping a same-named graph in `./noodles`, while everything else in\n`~/noodles` stays available. An unreadable dir (a typo, a folder that isn't\nthere) is a hard startup error naming the offender — no silent half-load.\n\n#### Wiring it into your editor\n\nMCP config is per-project in most clients, so the two-dir pattern lives in the\nrepo, right next to the graphs it serves. For Claude Code that's a `.mcp.json`\nat the project root — commit it along with `noodles/` and everyone who clones\ngets the same tools:\n\n```json\n{\n  \"mcpServers\": {\n    \"nanoodle\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nanoodle-mcp\", \"--graphs\", \"./noodles\", \"--graphs\", \"/home/you/noodles\"]\n    }\n  }\n}\n```\n\nRelative paths resolve from the project root. A project-scoped server named\n`nanoodle` shadows a user-scoped one of the same name, so repos with a\n`.mcp.json` get their own graphs and every other directory falls back to your\nglobal install untouched.\n\nTwo things to keep out of a committed config: your **API key** (leave `env`\nout and let `NANOGPT_API_KEY` come from the environment, or point\n`--env-file` at a file outside the repo — never commit a key) and, for team\nrepos, your **personal library path** (the second `--graphs` above is an\nabsolute path on *your* machine; drop it from a shared repo's config and keep\njust `./noodles`, since `~` is not expanded in MCP args). Cursor\n(`.cursor/mcp.json`) and VS Code (`.vscode/mcp.json`) take the same `args` in\nthe config shapes shown under [Install](#install).\n\n## Run any share link\n\nEvery nanoodle share link is an executable tool. Alongside your saved graphs the\nserver always exposes one more tool, **`run_noodle`**, that takes any share link\nand runs it — no file needed:\n\n```\nrun_noodle(\"https://nanoodle.com/#g=…\", { \"Text\": \"a lighthouse at dawn\" })\n```\n\nPass the link as `url` and any workflow inputs as `inputs` (the same friendly\nkeys the graph's own tool would take; media inputs take a file path or https\nURL). It accepts `#g=`/`#j=` workflow links, `#a=` app links, and short links\nto one. Direct links decode locally; only fragment-less short links trigger a\nnetwork read, and it carries no credentials. Like every other tool, a run\n**spends real money** and ends with a `cost: $X.XXXX` line.\n\n## Making graphs\n\nBuild and test workflows in the [nanoodle editor](https://nanoodle.com), hit\n💾, and drop the downloaded `noodle-graph.json` into your `--graphs` folder\n(rename it — the filename becomes the tool name). Restart the server (or your\nMCP client) to pick up new files.\n\n### Describing your tools\n\nThe first comment node in a graph doubles as the MCP tool's description: its\ntext leads the auto-derived node chain, so the calling agent reads your words\nfirst. Keep it to one sentence saying what the tool produces from what inputs —\ne.g. *\"Renders a product mockup image from a one-line feature description.\"*\nLong text is truncated to 200 characters and whitespace is collapsed, so write\nfor one line. Graphs without a comment just get the node chain, as before.\n\nAfter each successful run the tool's real cost is recorded in\n`<out>/costs.json` (a small `{tool: {usd, at}}` sidecar next to your media\noutputs) and folded into the description as `last run $X` — observed, not\nestimated. The server announces the change with a\n`notifications/tools/list_changed` notification, so MCP clients that honor it\nshow updated prices mid-session; others catch up on restart.\n\n## Limitations\n\nHonest list — most of these are inherited from the executor:\n\n- **Feed-forward DAGs only.** nanoodle graphs are stateless pipelines; there\n  are no loops, no conversations, no memory between calls.\n- **One run per call, no streaming.** A `tools/call` blocks until the whole\n  workflow finishes — video graphs can take minutes. No MCP progress\n  notifications yet.\n- **Graphs load once at startup.** Adding or editing files in `--graphs`\n  needs a restart; the tool list doesn't change mid-session (no\n  `listChanged` notifications).\n- **Local media nodes need nanoodle ≥ 0.4** (this package's dependency).\n  Graphs using resize, combine, trim, extract-audio, video-frames, or\n  soundtrack run headlessly — pure JS where possible, ffmpeg on `PATH` for\n  the rest (see the [supported-nodes\n  table](https://github.com/nanoodlecom/nanoodle-js#supported-nodes)). A\n  graph with a node type the library doesn't know is still skipped at\n  startup with a stderr note.\n- **Media rides inline.** NanoGPT has no upload endpoint, so media inputs are\n  sent as base64 in the request body (~4 MB max, checked before spending).\n- **No cost cap in key mode.** The server won't stop a client from calling an\n  expensive graph repeatedly. Your NanoGPT balance is the only brake. (Wallet\n  mode is better here: `--max-usd` caps each call, and the wallet's balance\n  caps the total.)\n\nNo telemetry, no analytics; the API key and wallet secrets are never logged\nand never appear on stdout.\n\n## Testing\n\nFully offline — the suite spawns the real server against a canned local\nNanoGPT stub and drives the MCP handshake over stdio:\n\n```bash\nnpm test\n```\n\nIt also carries release guards: the three release manifests must agree on the\nversion, the landing page's chip table may only name node types the `nanoodle`\nlibrary still has, and no startup path may statically import the GPL-3.0\ndependency.\n\nThose guards are offline, so they compare repo files to each other and cannot\nsee npm. One more guard needs the network and therefore runs in the publish\nworkflow, not in `npm test`: `scripts/assert-npm-version.mjs` fails the registry\npublish unless `server.json`'s version is really on npm. See\n[PUBLISHING.md](PUBLISHING.md).\n\n## Registry\n\n`server.json` is the [official MCP registry](https://registry.modelcontextprotocol.io)\nmanifest (`io.github.nanoodlecom/nanoodle-mcp`); see [PUBLISHING.md](PUBLISHING.md)\nfor the release checklist.\n\n## Which repo do I want?\n\nThis server exposes saved workflows as typed MCP tools. If your agent supports\nAgent Skills rather than MCP servers,\n[nanoodle-skill](https://github.com/nanoodlecom/nanoodle-skill) (teaches your\nagent to build any graph) and\n[noodle-skills](https://github.com/nanoodlecom/noodle-skills) (prebuilt\none-task workflows) cover similar ground without running a server. Running\ngraphs in GitHub CI? →\n[run-noodle-action](https://github.com/nanoodlecom/run-noodle-action).\n\n## Licensing\n\n**MIT:** this server (see [LICENSE](LICENSE)), the\n[nanoodle editor](https://nanoodle.com), and the\n[`nanoodle`](https://github.com/nanoodlecom/nanoodle-js) executor this server\nruns graphs on. The sibling projects — [nanoodle-py](https://github.com/nanoodlecom/nanoodle-py),\n[nanoodle-skill](https://github.com/nanoodlecom/nanoodle-skill),\n[noodle-skills](https://github.com/nanoodlecom/noodle-skills) — are MIT too.\n\n**Graphs are not covered by any of that.** A `noodle-graph.json` is its\nauthor's work. Serving one as a tool, or downloading one from `/graph/<tool>.json`,\ndoes not place it under this repo's licence. The public library this project\nmaintains, [awesome-noodles](https://github.com/nanoodlecom/awesome-noodles), is\nMIT because that repo says so; the graphs on any other server carry whatever\nlicence their authors give them.\n\n**GPL-3.0:** one runtime dependency,\n[`nanocurrency`](https://github.com/marvinroger/nanocurrency-js). It derives\nNano keys, builds and signs blocks, and computes proof-of-work. It is used only\nby the optional x402 Nano wallet: [wallet mode](#wallet-mode--no-account-no-api-key-x402)\n(`NANO_SEED` / `NANO_PRIVATE_KEY`) and [charge mode](#charging-per-call---charge-usd)\n(`--charge-usd`).\n\nBe clear about what that means for you. **Every installer receives it.**\n`npm install nanoodle-mcp` and `npx -y nanoodle-mcp` both download and unpack\n`nanocurrency` onto your disk, whether or not you ever touch x402. Your\ndependency tree contains GPL-3.0 code from the moment you install, and it is\nredistributed with any bundle or image you build from this package.\n\nWhat the on-demand load changes is narrower, and only that: **which runs\nexecute it.** `src/wallet.mjs` and `src/gate.mjs` are the only modules that\ntouch `nanocurrency`, and `bin/nanoodle-mcp.mjs` reaches them through\n`await import()` inside the branches that already gate x402. So a BYOK\n(API-key) run never loads or runs GPL-3.0 code — it still has it installed.\n`tests/gpl-boundary.test.mjs` holds that line.\n\nWe keep it a normal dependency on purpose. npm installs `optionalDependencies`\nby default, so that would change nothing; the only way to skip it is an\noptional `peerDependency`, and then `npx -y nanoodle-mcp` — the documented\none-command install — could no longer run wallet or charge mode at all. Correct\nlabelling beats a broken accountless install.\n\nNot affiliated with NanoGPT or Anthropic. Build workflows at\n[nanoodle.com](https://nanoodle.com); run them from code with\n[nanoodle-js](https://github.com/nanoodlecom/nanoodle-js) /\n[nanoodle-py](https://github.com/nanoodlecom/nanoodle-py).\n",
  "bytes": 35212,
  "sha": "c06554da3a8c32893cf9eecee7de156dc414fe441c9e4cf596e4baa0df616a86",
  "repo_slug": "nanoodlecom/nanoodle-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nanoodlecom_nanoodle_mcp_4859b085/readme"
}