Stop opening dashboards.
Ask your agent.

Code mode for all your APIs

Install your APIs once, for every agent. Faster and cheaper than MCP tool calls, and the same answer every time.

> Read https://getwunder.dev/llms.txt and set up Wunder in this repo.
See how it works

Paste into Claude Code, Codex or any agent with a shell. Credentials stay in your OS keychain.

Triage 81 open issues across GitHub, Linear and PylonSame task and answers: every issue classified and summarized, matched to Linear work and affecting customers · 3 runs each, Oct 2026
First runStatus quo: the agent calls each tool, one at a time. Wunder: script new writes, checks and runs one script.
Time · lower is better1.7× faster
wunder123 s
status quo215 s
Cost · lower is better3.1× cheaper
wunder$0.64
status quo$1.99
Next day · rerun the saved scriptLive API data, no agent, no script writing. The status quo has no saved script, so it starts over.
Time · lower is better3.7× faster
wunder58 s
status quo215 s
Cost · lower is better~200× cheaper
wunder$0.01
status quo$1.99
For agents

Let your agent set it up.

Paste one line into Claude Code, Codex or Cursor. Your agent runs the steps; you only finish the browser sign-ins.

Agents can read this page as markdown or start from llms.txt.

Read https://getwunder.dev/llms.txt and set up Wunder in this repo.
What's inside llms.txt
# Wunder

> Code mode for all your APIs. Wunder is a CLI that installs OpenAPI, GraphQL and MCP APIs like packages; the agent searches for the few operations a task needs and writes one script instead of a chain of tool calls. Credentials stay in the OS keychain.

Wunder works with any agent that can run a shell command: Claude Code, Codex, Cursor and others. Packages are immutable org/name versions pinned in a lockfile. Installing a schema never calls the API.

## Agent quickstart

```
Set up Wunder in this project.
1. npm i -g wunder
   # check: `wunder version` prints a version
2. export WUNDER_REGISTRY=https://getwunder.dev
   # points the CLI at this registry; keep it set for the steps below
3. wunder auth login
   # opens a browser; ask the user to finish sign-in (--no-browser prints the URL)
4. wunder init
   # creates wunder.json and AGENTS.md in the current directory
5. wunder capability search "<what you need>"
   # suggests catalogue APIs with their install line; exit 4 with suggestions is expected here, continue
6. wunder install <org/api>
   # pins the schema in the lockfile; never calls the API
7. wunder credentials login <org/api>
   # the user approves access; never paste tokens into chat
8. wunder script new "<task>"
   # writes, checks and runs a saved script; quick questions: wunder run -e '…'
Run each step and stop on the first failure.
Add --json to any command for one structured result; JSON mode never asks interactive questions.
Exit code 2 means sign-in is required: run wunder auth login. Do not work around sign-in or consent.
```

## Pages

- [Overview](https://getwunder.dev/index.md): what Wunder is, how it works, scripts, models, security, registry and FAQ
- [Explore APIs](https://getwunder.dev/explore): search the API catalogue (sign-in required); agents should use `wunder capability search` instead
Why one script

One script, not a chain of tool calls.

When every API is a set of tools, every tool definition sits in the context window and every call goes through the model, one at a time, each result read back in. With Wunder the agent searches for the few operations the task needs and writes one script. The tools aren't the problem. Calling them one by one through the model is.

Tool calls through the model

215 s · $1.99
model → list_issues
result → model
model → search_tickets
result → model
model → search_tickets
result → model
model → get_account
… one round trip per call

Every tool definition in context. Every result read back in. Gone when the chat ends.

One script with Wunder

123 s · $0.64
wunder capability search "open issues"
wunder script new "customers hit by open bugs"
ReliableTyped calls, no wrong tool
ReproducibleSaved and version-pinned
Cheap again$0.01 per rerun

The agent sees only the operations it needs. The plumbing runs in Wunder, not in the model.

How it works

Install, search, run, rerun.

  1. 1install

    Add the APIs

    Like npm dependencies, pinned in a lockfile. Installing never calls the API.

    wunder install github/graphql
  2. 2search

    Find the operations

    Plain words in, a typed call out. The agent sees only what the task needs.

    wunder capability search "open issues"
  3. 3run

    Get the answer

    Quick question: one command. Bigger task: Wunder writes, checks and runs a saved script.

    wunder run -e '…'
    wunder script new "…"
  4. 4rerun

    Run it again for cents

    The script lives in your repo. Reruns skip search and script writing and reuse cached model answers.

    wunder run scripts/customers-hit.js
What agents write

Short scripts, no plumbing.

What Claude Code or Codex writes after a search. Wunder fills in the plumbing at run time.

Join data

GitHub issues joined with Pylon support tickets. Two APIs, two formats, one import.

wunder run scripts/join.js
import { openIssues, searchTickets } from "wunder";

// Which customer accounts mention each open issue?
export default async () => {
  const issues = await openIssues.all({ owner: "acme", name: "api", states: ["OPEN"] });
  return Promise.all(issues.map(async (issue) => {
    const tickets = await searchTickets.all({ search_text: issue.title }, { max: 5 });
    return { issue: issue.title, accounts: tickets.map((t) => t.account?.id) };
  }));
};

Classify

Labels from the decisions model, with a confidence per item. Cached, so reruns don't pay again.

wunder run scripts/classify.js
import { openIssues } from "wunder";
import { decisions } from "wunder:model";

// Label every open issue as bug, feature request or question.
export default async () => {
  const issues = await openIssues.all({ owner: "acme", name: "api", states: ["OPEN"] });
  return Promise.all(issues.map(async (issue) => {
    const label = await decisions.choice({ title: issue.title }, "Classify this issue",
      { bug: "a defect", feature: "a feature request", question: "a question" });
    return { issue: issue.title, label: label.choice, confidence: label.confidence };
  }));
};

Web search

Live web results next to your own data, with a budget on web calls.

wunder run scripts/web.js
import { openIssues } from "wunder";
import { web } from "wunder:web";

// Find public reports of each open issue from the last month.
export default async () => {
  const issues = await openIssues.all({ owner: "acme", name: "api", states: ["OPEN"] });
  return Promise.all(issues.map(async (issue) => {
    const found = await web.search(issue.title, { count: 3, freshness: "month" });
    return { issue: issue.title, links: found.results.map((r) => r.url) };
  }));
};

LLM output

Typed text from any model you configure, validated against a schema.

wunder run scripts/notes.js
import { closedIssues } from "wunder";
import { llm } from "wunder:model";
import { z } from "wunder:zod";

// Turn last week's closed issues into release-note lines.
export default async () => {
  const issues = await closedIssues.all({ owner: "acme", name: "api" }, { max: 50 });
  const notes = await llm.json({
    prompt: "One release-note line per issue:\n" + issues.map((i) => i.title).join("\n"),
    schema: z.object({ lines: z.array(z.string()) }),
  });
  return notes.lines;
};
Models and tools

One script. Any model, any search engine.

An agent on its own gives you its provider's models and built-in tools. A Wunder script can pick the right one for each step, from different providers, in the same run.

  • Reason and writeGemini 3.8 Flash, Gemini 3.1 Pro, Claude Opus 5.5llm.text, llm.json
  • Cheap bulk workGemini 3.1 Flash-Litellm.json
  • Classify and decideTypeSafe Jevdecisions.choice, yesNo
  • EmbedOpenAI text-embedding-3-smallembeddings
  • Search the webPerplexity, Firecrawl, Gemini grounded searchweb.search
  • Read pagesFirecrawlweb.fetch

Through the optional Wunder AI gateway, or point any of them at your own keys. The broker adds the key; the script never sees it.

import { web } from "wunder:web";
import { llm, decisions } from "wunder:model";

// Three providers, one run. Keys stay with the broker.
export default async () => {
  // Perplexity finds the pages
  const { results } = await web.search(
    "open-source agent sandboxes",
    { endpoint: "search-perplexity", count: 10 },
  );

  // TypeSafe Jev, the default decisions model, sorts them
  const projects = [];
  for (const page of results) {
    const kind = await decisions.choice(page, "What is this page?", {
      project: "An open-source project",
      other: "Anything else",
    });
    if (kind.choice === "project") projects.push(page);
  }

  // Gemini Pro writes the comparison
  return llm.text({
    model: "google/gemini-3.1-pro-preview",
    prompt: "Compare these projects in five bullets.",
    input: projects,
  });
};
Architecture

Same agent. Different boundaries.

Switch between the two setups. The boxes stay where they are, so you can see where the boundaries move: what code can touch, where keys live, what the model sees and who approves writes.

YOUR MACHINETools · run as you, full accessYouasks, approvesAgentin one harnessCONTEXTtool definitionsresponse 1response 2response 3response n ...grows per callMCP serverper vendorMCP serverper vendorgh CLIshell and filesConfigper harness,per toolits own tokensModel providerits models and toolsOrigin APIsGitHub, Linear, PylonRemote servershosted toolssee your calls,data and tokensNo registryfind, install andconfigure eachtool yourselfasksmodel, built-in toolscallcallcallHTTPS + tokenor a remote serverevery call and every response goes through the model
The usual setup today: the agent gets each API as a set of tools, through MCP servers or CLIs, and calls them one at a time. Nothing is wrong with the tools themselves. The trouble is the pattern: every tool runs with your permissions and its own token, and every request and response passes through the model.
  • What code can touch

    Everything you can. MCP servers and CLIs like gh run as ordinary processes with your files, network and environment.

  • Where keys live

    Config files. A token per tool, often in plain text or env vars. Remote servers hold their own.

  • What the model sees

    Everything. Every tool definition and every response is read into context, and grows with each call.

  • Who approves writes

    Each harness. Safety rests on each harness's own permission system and each tool's behavior.

  • Switching agents

    Set it up again. One config per harness and per tool, each with its own copy of your tokens.

YOUR MACHINE · RUNS LOCALLYwunder CLIYouasks, approvesAgentany harnessCONTEXTwunder guidancesearch resultsscriptfinal resultstays smallSandboxruns the scriptno network,no files, no keysBrokeradds credentialslimits, redactionapproves writesOS keychainAPI tokensmodel keysonly broker readsProjectlockfile, typesscripts, journalWUNDER.mdModel providerits models and toolsOrigin APIsGitHub, Linear, Pyloncalled directlyfrom your machineWunder AI gatewayGemini, Claude, JevPerplexity, Firecrawlor your own keysWunder registryAPI schemascapability searchnever calls APIsasksmodel, built-in toolslogs in, approves writesscriptresultapi calldatatokenHTTPS+ tokenmodel,webinstall, search: schemas only, no API calls
The script runs in a sandbox with no network, files or environment. Every API call goes through the broker, which reads the token from the OS keychain at the last step. API traffic never passes through Wunder. The registry never sees your data; the AI gateway sees only the model and web calls you route to it.
  • What code can touch

    Only the broker. The sandbox has no network, files or environment. Every call goes through the broker.

  • Where keys live

    The OS keychain. Only the broker reads them, at the last step, and redacts them from responses and logs.

  • What the model sees

    What it needs. Short guidance, search results, its script and what the script returns. Raw API responses stay in the script.

  • Who approves writes

    You, in one place. The broker checks every write against the approvals you granted, whatever the harness. Test runs can't write.

  • Switching agents

    Nothing to redo. Any agent with a shell uses the same project, keychain and approvals.

Built in

The plumbing lives in the runtime.

  • Paging and retries

    .all() pages through any list. Retries, backoff and pacing run in the runtime, not in the script.

  • Resumable scripts

    API and model calls are journaled. A rerun after an error doesn't redo finished work, and a write ledger stops duplicates.

  • WUNDER.md

    The agent records what it learned, such as rate limits and working scripts, and reuses it next run.

  • Views

    wunder serve turns a saved script's results into a local page you can reopen.

The registry

Versioned API packages, pinned like dependencies.

Every package is an immutable org/name version with a hash in your lockfile. When an API ships a new schema, wunder capability check tells you what would break before you upgrade. Search finds the right operation first for 446 of 476 test prompts.

github/graphqlGraphQLgithub/openapiOpenAPIlinear/graphqlGraphQLposthog/restOpenAPIpylon/restOpenAPIgithub/mcpMCP

Publish your own API

wunder init --package --name acme/api \
  --format openapi --schema openapi.yaml
wunder publish

Wunder computes the semver bump from the schema diff and refuses to publish a breaking change as a patch.

Common questions

Is Wunder against MCP?

No. MCP is how an API describes itself, and Wunder installs MCP schemas next to OpenAPI and GraphQL. What we replace is tools called one at a time through the model.

Does the agent or the script see my API tokens?

No. Tokens live in your OS keychain and are injected at the boundary when a request leaves the sandbox. A credential is only ever sent to its configured destination; anything else is blocked.

Which agents and models does it work with?

Any agent that can run a shell command: Claude Code, Codex, Cursor and others. Your agent keeps its own model. Switching agents or models never touches your data access, keys or saved scripts.

What if my API isn't in the registry?

If it has an OpenAPI 3.x or Swagger 2.0 document, a GraphQL schema or an MCP server, publish it yourself in two commands. There are no provider-specific special cases.

Get started

Try it in one paste.

Run it in any folder. Two browser sign-ins, then your agent lists the issues assigned to you.

export WUNDER_REGISTRY=https://getwunder.dev \
  && npm i -g wunder && wunder auth login && wunder init \
  && wunder install github/graphql \
  && wunder credentials login github/graphql \
  && claude "Summarize the open GitHub issues assigned to me"
  • Installs the CLI, prebuilt binary for macOS, Linux and Windows
  • Signs you in to Wunder, once, in the browser
  • Sets up the project, adds guidance for your agent
  • Adds the GitHub API, installing never calls the API
  • Logs in to GitHub, the token goes to your OS keychain
  • Asks the question, works for any GitHub account; use codex or any agent with a shell

Try it on the repo you have open.

Paste this into your agent. It installs the CLI and adds your first API; then ask for something that used to take a chain of tool calls.

> Read https://getwunder.dev/llms.txt and set up Wunder in this repo.
Explore APIs