# Build with an agent

> Set up a coding agent to build, price, test and ship an extension on Toolaby.

A coding agent, such as Claude Code, Codex, Cursor, VS Code with Copilot, Gemini CLI or Windsurf, can do most of the work of selling an extension on Toolaby. It reads the files the command line writes into your project, acts on your tools through Toolaby's MCP server, and checks its own work with `check`.

This page covers the setup, what an agent does and what you do, how it verifies its work, and the reference: every MCP tool and command, the docs for agents, safety and troubleshooting.

## Before you begin

* A workspace on Toolaby, and Node.js 20 or later.
* A coding agent that connects to MCP servers.

## 1. Set up your agent

```bash
npx -y toolaby@latest agents
```

`agents` registers Toolaby's MCP server in each coding agent it finds on this machine: Claude Code, Codex, Cursor, VS Code and Gemini CLI. It registers it for your user, so it works in every project. Run again, it changes nothing.

| Option            | Does                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------- |
| `--client <name>` | Only that client: `claude-code`, `codex`, `cursor`, `vscode`, `gemini`, or `all`.     |
| `--project`       | In this project instead: `.mcp.json` and `.cursor/mcp.json`, to commit for your team. |
| `--json`          | What was done and what failed, as JSON.                                               |

Restart the agent, or reload its MCP servers. Its tools now include `toolaby_get_tool` and the others below.

`wire` and `create` print this command when they finish.

## 2. Or add the server by hand

The server is the command `npx -y toolaby@latest mcp`, on stdio. It acts on Test. For Live, add a second server named `toolaby-live` that runs `npx -y toolaby@latest mcp --live`.

### Claude Code

Toolaby's plugin brings the server, the skills and five commands (`/toolaby:sell`, `/toolaby:check`, `/toolaby:test-unlock`, `/toolaby:ship`, `/toolaby:move-from-extensionpay`). In Claude Code:

```
/plugin marketplace add toolaby/claude-plugin
/plugin install toolaby@toolaby
```

Or the server alone:

```bash
claude mcp add --scope user toolaby -- npx -y toolaby@latest mcp
```

Use one or the other: the plugin carries the same server.

### Codex

```bash
codex mcp add toolaby -- npx -y toolaby@latest mcp
```

Or in `~/.codex/config.toml`:

```toml
[mcp_servers.toolaby]
command = "npx"
args = ["-y", "toolaby@latest", "mcp"]
```

### Cursor

[Add to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=toolaby\&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInRvb2xhYnlAbGF0ZXN0IiwibWNwIl19), or in `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "toolaby": { "command": "npx", "args": ["-y", "toolaby@latest", "mcp"] }
  }
}
```

### VS Code

[Install in VS Code](vscode:mcp/install?%7B%22name%22%3A%22toolaby%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22toolaby%40latest%22%2C%22mcp%22%5D%7D), or in `.vscode/mcp.json`:

```json
{
  "servers": {
    "toolaby": { "type": "stdio", "command": "npx", "args": ["-y", "toolaby@latest", "mcp"] }
  }
}
```

### Gemini CLI

```bash
gemini mcp add toolaby npx -y toolaby@latest mcp
```

### Windsurf

In `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "toolaby": { "command": "npx", "args": ["-y", "toolaby@latest", "mcp"] }
  }
}
```

### Other clients

Any client that starts an MCP server from a command: `npx` with the arguments `-y toolaby@latest mcp`.

## 3. Sign in once

The server acts as you, with the command line's session, on the tools of your workspaces. Without a session, a call answers that you are not signed in.

* In the agent: `toolaby_sign_in` answers a link and a code. Open the link, check the code and approve. The server waits for your approval; the agent's next call works.
* From a terminal, or an agent without MCP: `npx -y toolaby@latest login --no-wait --json` answers `{ url, code, deviceCode, expiresIn }` and exits at once. After you approve, `npx -y toolaby@latest login --poll <deviceCode>` ends with exit code `0`.

Sessions are per side. Live needs its own: `npx -y toolaby@latest login --live`.

## 4. Ask for the work

1. Make the tool: in the dashboard, or ask the agent (`toolaby_create_tool`).
2. Wire the extension: `npx -y toolaby@latest wire <tool-id>` in its folder, or `npx -y toolaby@latest create <tool-id>` for a new one. Both write the [files agents read](#the-files-agents-read).
3. Ask in one sentence: &#x2A;"Make exporting the counts as CSV a Pro feature, $5 once."*
4. The agent reads `TOOLABY.md`, adds the plan (`toolaby_add_plan`), then the feature and the plan that unlocks it (`toolaby_set_features`), puts `gate({ feature: 'export' })` before the export, and [verifies](#verify).
5. It ends with what changed and the steps left for you.

## What an agent does, and what you do

| An agent does alone                                                                         | You do, once                                                  |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Create and change tools, plans, trials, seats, features, the Free plan and coupons, on Test | Approve the command line's sign-in, on each side              |
| Write the code behind `gate()`, and fix what `check` reports                                | Connect Stripe on Live, under **Configure → Payments**        |
| Prove a paid feature unlocks on Test: `test-unlock`, then `check --browser --as <plan>`     | Make a purchase in the browser, when you want to see Checkout |
| Read customers, grant access, refund and cancel, with your confirmation on Live             | Upload the zip to the Chrome Web Store, and submit it         |
| Copy the tool to Live, write the Live key, pack the store zip                               | Make the first real purchase on Live                          |

The skills and `TOOLABY.md` tell the agent never to report your steps as done.

## The files agents read

`create`, `wire` and `upgrade` write these at the project's root: the folder with `package.json`, above a folder a build copies, such as `public/`.

| File                               | For                                                                                                                                                                                                                                                                                                                                                                                                                     | Written                     |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `TOOLABY.md`                       | Every agent. The files Toolaby owns, where `toolaby.js` is imported from in this kind of project (plain, a bundler, WXT, CRXJS, Plasmo), `gate()` and the paywall, subscription and trial states, this tool's features and plans, the commands, the verify checklist, what only you can do, and links to these docs as Markdown. With `--from-extensionpay`, the move's steps. On Live, every command carries `--live`. | Every time.                 |
| `.claude/skills/toolaby*/SKILL.md` | Claude Code, Cursor and Copilot: the skills below.                                                                                                                                                                                                                                                                                                                                                                      | Every time.                 |
| `.agents/skills/toolaby*/SKILL.md` | Codex, Cursor and Copilot: the same skills.                                                                                                                                                                                                                                                                                                                                                                             | Every time.                 |
| `AGENTS.md`                        | Every agent reads it first.                                                                                                                                                                                                                                                                                                                                                                                             | Where the project has none. |
| `CLAUDE.md`                        | Claude Code. It reads `AGENTS.md`.                                                                                                                                                                                                                                                                                                                                                                                      | Where the project has none. |

Where the project has its own `AGENTS.md` or `CLAUDE.md`, the command adds a short block to it, between `<!-- TOOLABY START -->` and `<!-- TOOLABY END -->`: where `TOOLABY.md` and the skills are, and the check. Run again, it replaces only what is between the markers. A `CLAUDE.md` that already reads `AGENTS.md` (`@AGENTS.md`) gets no block of its own.

### Skills

| Skill                        | For                                                                                                                           |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `toolaby`                    | What Toolaby is, which skill fits, setup, sign-in and safety.                                                                 |
| `toolaby-sell-a-feature`     | Making something Pro: the plan, the feature, `gate({ feature })`, a free or daily allowance.                                  |
| `toolaby-pricing`            | Plans one-time or by subscription, trials, devices, seats, coupons, the Free plan.                                            |
| `toolaby-test-a-purchase`    | `test-unlock` and `check --browser --as <plan>`; a purchase with a test card.                                                 |
| `toolaby-ship`               | Copy to Live, `login --live`, `upgrade --live`, `pack`, the store listing address.                                            |
| `toolaby-extensionpay-move`  | Moving from ExtensionPay, and your steps after it.                                                                            |
| `toolaby-extension-patterns` | Where `gate()` goes in a background, popup, side panel or content script, module scripts, the client's port, offline answers. |

To add the skills to any project, without wiring it: `npx skills add https://toolaby.app`. Toolaby serves them at `/.well-known/agent-skills/index.json`.

## Verify

An agent says the work is done only after these pass.

1. `npx -y toolaby@latest check --json` in the extension's folder. It exits `0`, and `ok` is `true`.
2. `toolaby_get_tool`: the plan at the price asked, and the feature the code gates among the plan's features.
3. Built, `npx -y toolaby@latest check --json --browser --built <output folder>`: Chromium loads the extension, its background answers, and its pages open without an error. The first run downloads Playwright and Chromium into `~/.config/toolaby/playwright`, never into the project.
4. On Test, `npx -y toolaby@latest test-unlock <tool-id> --plan <plan-id>`, then `npx -y toolaby@latest check --browser --as <plan-id>`: the extension, signed in as your Test buyer with that plan, allows the paid feature.

`test-unlock` makes a buyer of your own on Test, at your address with `+toolaby-test` (`you+toolaby-test@example.com`), and gives it the plan. It is refused on Live.

`check --json` answers one object:

```json
{
  "ok": false,
  "side": "Test",
  "folder": "/Users/you/word-count",
  "built": null,
  "findings": [
    { "code": "CSP_BLOCKS_TOOLABY", "level": "error", "message": "manifest.json's content security policy leaves https://quiet-otter-4172.toolaby.app out of connect-src: every call of Toolaby's fails to fetch. `npx -y toolaby@latest upgrade acme--word-count` adds it.", "file": "manifest.json", "fix": "npx -y toolaby@latest upgrade acme--word-count", "docs": "https://toolaby.app/docs/troubleshooting#in-the-extension" }
  ]
}
```

For each `error` finding, an agent runs its `fix`, a command, when it has one; otherwise it changes what the `message` says, and reads `docs`. The codes never change. These are the ones an agent meets most; [Command line](https://toolaby.app/docs/cli.md#check-folder) lists every one.

| Code                             | Means                                                                                                                                                                |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FEATURE_KEY_UNKNOWN`            | The code gates on a feature key the tool does not have: every buyer is refused.                                                                                      |
| `BACKGROUND_NOT_STARTED`         | Nothing starts Toolaby in the background: none is named, it never starts Toolaby, a build's carries none of it, or it did not answer Toolaby's client (`--browser`). |
| `EXTERNALLY_CONNECTABLE_MISSING` | Toolaby's address is not under `externally_connectable`: sign-in and the purchase never reach the extension.                                                         |
| `CSP_BLOCKS_TOOLABY`             | The content security policy's `connect-src` (or `default-src`) leaves Toolaby's address out: every call of Toolaby's fails to fetch.                                 |
| `POPUP_NOT_MODULE`               | A page loads a script with `import` or `export` lines without `type="module"`: Chrome refuses it and the page opens blank.                                           |
| `PAGE_THREW`                     | A page of the extension threw when it opened (`--browser`).                                                                                                          |
| `NO_PAYWALL_POSSIBLE`            | Nothing in the extension can ever ask anyone to pay. An error when the tool sells plans; a warning when it has none.                                                 |
| `TEST_KEY_IN_LIVE_BUILD`         | A store copy (its manifest has `update_url`) carries a Test or development key: an error. A production build that carries one: a warning.                            |
| `HOST_PERMISSION_LEFT`           | The manifest gives Toolaby's address, or another side's Test address, a host permission or an entry Toolaby does not need.                                           |
| `KEY_OUTDATED`                   | The tool key is older than Toolaby's: the Free plan, the plans or the signing key changed since it was written.                                                      |
| `NOT_SIGNED_IN`                  | No session on the side the key is on: the checks that need one were not made.                                                                                        |
| `CLI_OUTDATED`                   | This command is older than the one Toolaby serves.                                                                                                                   |

### Exit codes

| Code | Means                                                                                  |
| ---- | -------------------------------------------------------------------------------------- |
| `0`  | Done. `check` with warnings only exits `0`.                                            |
| `1`  | Failed, or `check` found an error.                                                     |
| `2`  | Done, with steps left for a person: `wire` and `upgrade` when they end *Almost wired*. |

## MCP tools

`npx -y toolaby@latest mcp` acts on Test, or on Live when started with `--live`. Every tool has a title, annotations, an input schema and an output schema, and answers structured content with the same JSON as text. Every tool has a command, below: an agent without MCP runs it with `--json`.

| Tool                          | Command              | Does                                                                                                                                    |
| ----------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `toolaby_whoami`              | `whoami`             | The signed-in developer and their workspaces.                                                                                           |
| `toolaby_list_tools`          | `tools`              | Your tools, with the ids the others take.                                                                                               |
| `toolaby_get_tool`            | `tool get`           | One tool: the Free plan, features, plans, the tool key, the extension's id, the checkout page, whether a copy has checked in.           |
| `toolaby_list_customers`      | `customers`          | The tool's customers, by a search text.                                                                                                 |
| `toolaby_get_customer`        | `customer`           | One buyer: entitled or not, plan, licences, subscription status and renewal, grants, devices.                                           |
| `toolaby_docs`                | `docs`               | A docs page as Markdown, or the index.                                                                                                  |
| `toolaby_search_docs`         | `docs search`        | The docs pages and passages that match a query.                                                                                         |
| `toolaby_check`               | `check`              | A folder's wiring checked, with the findings above.                                                                                     |
| `toolaby_sign_in`             | `login`              | A sign-in link and code for you; done once you approve.                                                                                 |
| `toolaby_create_tool`         | `tool create`        | A new tool.                                                                                                                             |
| `toolaby_update_tool`         | `tool update`        | The tool's name, line or store listing address.                                                                                         |
| `toolaby_set_access`          | `access`             | What the Free plan holds, and whether an account comes first.                                                                           |
| `toolaby_set_features`        | `features`           | The tool's whole list of features, and the plans that unlock each. Add the plans first.                                                 |
| `toolaby_add_plan`            | `plan add`           | A plan, one-time or a subscription, with a trial, a device count, per seat. It answers the plan's id; a tool's first plan is `default`. |
| `toolaby_update_plan`         | `plan update`        | A plan's name, trial, devices or per seat.                                                                                              |
| `toolaby_retire_plan`         | `plan retire`        | Takes a plan off sale. Holders keep it. Destructive.                                                                                    |
| `toolaby_grant`               | `grant`              | Access without paying, to an address; answers the grant's id.                                                                           |
| `toolaby_revoke_grant`        | `grant revoke`       | Takes a grant back. Destructive.                                                                                                        |
| `toolaby_create_coupon`       | `coupon create`      | A coupon code for checkout, such as one for a store reviewer.                                                                           |
| `toolaby_refund`              | `refund`             | Refunds a buyer's licence and revokes it. Destructive.                                                                                  |
| `toolaby_cancel_subscription` | `cancel`             | Cancels a buyer's subscription, at the period's end or now. Destructive.                                                                |
| `toolaby_copy_to_live`        | `copy-to-live`       | Copies the tool from Test to Live: the Free plan, features, plans, versions and the extension's identity. Needs both sessions.          |
| `toolaby_stripe_status`       | `stripe`             | Whether Stripe is connected, and the link for you to connect it.                                                                        |
| `toolaby_create_api_key`      | `api-keys create`    | A server API key, written into an env file and never shown.                                                                             |
| `toolaby_test_unlock`         | `test-unlock`        | Your Test buyer given a plan, for `check --browser --as <plan>`. Test only.                                                             |
| `toolaby_list_webhook_events` | `webhooks events`    | The events a registered endpoint receives, each with an example of what it carries.                                                     |
| `toolaby_list_webhooks`       | `webhooks endpoints` | The webhook endpoints registered, with the events each listens for.                                                                     |
| `toolaby_add_webhook`         | `webhooks add`       | Registers your server's endpoint, and writes its signing secret into an env file without showing it.                                    |
| `toolaby_send_test_event`     | `webhooks trigger`   | Sends an event's example to the registered endpoints, as a real delivery.                                                               |
| `toolaby_remove_webhook`      | `webhooks remove`    | Removes an endpoint: deliveries to it stop at once. Destructive.                                                                        |

The first eight only read, as do `toolaby_stripe_status` and the two webhook lists, and say so in their annotations (`readOnlyHint`). The five marked destructive say so too (`destructiveHint`), so a client that asks before such a change can ask before these.

When `toolaby_set_access` or `toolaby_add_plan` changes what the tool key carries, the answer says `keyChanged`: run `npx -y toolaby@latest upgrade <tool-id>`, then build and reload. Installed copies follow a change to the Free plan without a release.

So while copies are installed, `toolaby_set_access` does not open a Free plan that counts uses: copies that gate their work with a bare `gate()` would run all of it free, what your plans sell included, until they update. It answers `copies_installed` with the versions that checked in this week. Ship the build that gates by feature first. Opening the Free plan then is the person's step, once the build is live on the store: the tool's **Access** card, or `access <tool-id> --access open --now` in their own terminal. The MCP server never offers `now` to an agent. Copy to Live keeps Live's Free plan counting uses the same way, and says so.

The server also has resources, `toolaby://docs/{page}` (Markdown) and `toolaby://tools/{tool}` (JSON), and prompts: `sell-a-feature`, `move-from-extensionpay`, `ship-to-store` and `fix-wiring`.

## Commands

Every command acts on Test unless given `--live`, and prints its raw answer as JSON with `--json`.

```bash
# Read
npx -y toolaby@latest whoami
npx -y toolaby@latest tools
npx -y toolaby@latest tool get <tool-id>
npx -y toolaby@latest customers <tool-id> --query <text> --limit 20
npx -y toolaby@latest customer <tool-id> <email>
npx -y toolaby@latest docs gating
npx -y toolaby@latest docs search "free trial"
npx -y toolaby@latest check --json --browser --built <output folder>

# Change
npx -y toolaby@latest tool create --name "Word Counter" --tagline "Counts the words on a page"
npx -y toolaby@latest tool update <tool-id> --store-url <listing address>
npx -y toolaby@latest access <tool-id> --access free_uses --free-uses 10
npx -y toolaby@latest features <tool-id> --features '[{"key":"export","name":"Export to CSV"}]' --plans '[{"id":"default","features":["export"]}]'
npx -y toolaby@latest plan add <tool-id> --billing subscription --interval month --amount 4 --currency usd --name Pro --trial-days 7
npx -y toolaby@latest plan update <tool-id> <plan-id> --devices 3
npx -y toolaby@latest plan retire <tool-id> <plan-id>
npx -y toolaby@latest grant <tool-id> <email> --days 30
npx -y toolaby@latest grant revoke <tool-id> <grant-id>
npx -y toolaby@latest coupon create <tool-id> --percent-off 100 --code REVIEW --max-redemptions 1 --expires-days 30
npx -y toolaby@latest refund <tool-id> <email>
npx -y toolaby@latest cancel <tool-id> <email>
npx -y toolaby@latest api-keys create --env-file .env --name server

# Test and Live
npx -y toolaby@latest test-unlock <tool-id> --plan <plan-id>
npx -y toolaby@latest stripe --live
npx -y toolaby@latest copy-to-live <tool-id>
npx -y toolaby@latest upgrade <tool-id> --live

# Sign-in and setup
npx -y toolaby@latest login --no-wait --json
npx -y toolaby@latest login --poll <deviceCode>
npx -y toolaby@latest agents --client cursor --project
```

[Command line](https://toolaby.app/docs/cli.md) covers `wire`, `upgrade`, `create`, `pack` and `webhooks`.

## The docs for agents

* `/llms.txt`: what an agent should know first, when to use Toolaby, and every page with its Markdown address.
* `/llms-full.txt`: the same, with every page, in one file.
* Any page as Markdown: its address with `.md`, such as `/docs/gating.md`. A request for a page with `Accept: text/markdown` gets the same Markdown, and every page names its copy in a `Link` header (`rel="alternate"`, `type="text/markdown"`).
* On every page, under its title: **Copy page**, **View as Markdown**, **Open in Claude** and **Open in ChatGPT**.

### By address, over MCP

For a client that connects to an MCP server by its address, such as a Claude or ChatGPT connector, the docs are also an MCP server at `https://toolaby.app/api/mcp` (Streamable HTTP). It has two tools, `toolaby_docs` and `toolaby_search_docs`, and reads nothing but these docs, so it asks for no sign-in and holds no key. Where a client asks how to authenticate, choose none.

```bash
claude mcp add --transport http toolaby-docs https://toolaby.app/api/mcp
```

In Cursor: [Add the docs server](cursor://anysphere.cursor-deeplink/mcp/install?name=toolaby-docs\&config=eyJ1cmwiOiJodHRwczovL3Rvb2xhYnkuYXBwL2FwaS9tY3AifQ%3D%3D).

The tools that read and change your tools stay with `npx -y toolaby@latest mcp` on your machine, under your own sign-in.

## Safety

* **Test by default.** Every command and the MCP server act on Test unless told `--live`. Test buyers, cards and keys never reach Live by themselves.
* **Live asks first.** On Live, every change through MCP first answers a summary and a `confirm` code, valid for 5 minutes and for those arguments only. The call made again with the code does it. These tools also carry `_meta: { "anthropic/requiresUserInteraction": true }`. On the command line, `refund` on Live asks you to confirm.
* **No secret in a chat or an extension.** `api-keys create --env-file .env` writes `TOOLABY_API_KEY=…` into the file and never prints it. The extension holds the tool key alone (`tk_…`), which is public.
* **Your roles hold.** Every change goes through the same checks as the dashboard. A member whose role cannot change a tool is refused.

## Troubleshooting

| Symptom                                   | Fix                                                                                                                                            |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| The agent has no `toolaby_*` tools        | Restart it or reload its MCP servers. `npx -y toolaby@latest agents --json` says which clients failed. Check Node.js 20 or later.              |
| A call answers that you are not signed in | `toolaby_sign_in`, or `login --no-wait --json` and then `login --poll <deviceCode>`. Approve the code in the browser.                          |
| A change on Live answers a `confirm` code | Show the summary to the person, and call again with the code only on their word.                                                               |
| An answer says `keyChanged`               | `npx -y toolaby@latest upgrade <tool-id>`, then build and reload.                                                                              |
| `check` exits `1`                         | For each `error` finding, run its `fix`, or change what its `message` says.                                                                    |
| `test-unlock` is refused                  | It runs on Test only. A project with the Live key: `upgrade <tool-id>` writes the Test key; `upgrade <tool-id> --live` before the store build. |
| The tools act on the wrong side           | `npx -y toolaby@latest mcp` is Test; `npx -y toolaby@latest mcp --live` is Live.                                                               |
| `npx` runs another `toolaby`              | In a repository you did not write, install the command (`npm i -g toolaby`) and type `toolaby-cli`.                                            |

See [Troubleshoot an extension](https://toolaby.app/docs/troubleshooting.md) for what an extension says when something is wrong.

## See also

* [Command line](https://toolaby.app/docs/cli.md)
* [Gate your extension](https://toolaby.app/docs/gating.md)
* [Pricing](https://toolaby.app/docs/pricing.md)
* [Testing](https://toolaby.app/docs/testing.md)
* [Ship to the Chrome Web Store](https://toolaby.app/docs/ship.md)
