Skip to content

This page takes a one-tool function package from nothing to a published version an agent can use. The starter tool, double_value, doubles a number; replace its body with your own logic once the loop works.

  • The Owner, Admin or Developer role in the workspace. These roles can create, build and publish tools.
  • For the CLI path: the caplo CLI (install it), signed in with caplo auth login and with a workspace selected with caplo workspace use. See Sign in from the CLI.
  • For the CLI path: Node.js 24 and npm. Tool packages run on Node 24, and caplo tool test refuses any other major version. Local tests run on macOS and Linux; on Windows, use WSL.
  1. Create the package. caplo tool init writes a package from the SDK template into a new or empty directory and names it after the directory:

    Terminal window
    caplo tool init double-value --type function
    cd double-value

    You get caplo.yaml, package.json, tsconfig.json, src/tool.ts (the tool), src/stdio.ts (runs it for local MCP clients) and tests/double-value.json (a fixture). Use --type mcp for an MCP package with several tools; see Define tools and use the call context.

  2. Install dependencies.

    Terminal window
    npm install

    This also writes package-lock.json. Keep it and commit it: Caplo installs exactly what the lockfile lists, and a push without a lockfile fails to build. yarn.lock and pnpm-lock.yaml also work.

  3. Look at the tool. src/tool.ts exports one defineTool:

    import { defineTool } from "@caplo/tools";
    export default defineTool<{ value: number }, { result: number }>({
    name: "double_value",
    description: "Doubles a number",
    inputSchema: { type: "object", properties: { value: { type: "number" } }, required: ["value"] },
    outputSchema: { type: "object", properties: { result: { type: "number" } }, required: ["result"] },
    async handler({ value }, { log }) {
    log.info("doubling", { value });
    return { result: value * 2 };
    },
    });

    caplo.yaml names the package and sets the tool’s policy: effect: read and timeoutSeconds: 5.

  4. Call it by hand. caplo tool dev opens MCP Inspector (started with npx) connected to your package, so you can list and call the tool:

    Terminal window
    caplo tool dev

    Secrets your caplo.yaml declares are read from environment variables of the same name. To use the package from another local MCP client instead, run caplo tool dev --no-inspector, which speaks MCP on standard input and output.

  5. Run the fixtures. caplo tool test bundles the package the way Caplo’s builder does, checks caplo.yaml against the tools the code lists, and runs every fixture in tests/:

    Terminal window
    caplo tool test
    bundle 7.5 KB (d64251e71094, esbuild 0.28.2)
    tool double_value__double_value (read, 5s)
    capabilities subject-context/1 effect-outcome/1 operation-status/1
    server advertises subject-context/1 effect-outcome/1
    ok tests/double-value.json (doubles 21)
    1 passed, 0 failed

    double_value__double_value is the name the model sees. See Test tools with fixtures.

  6. Push and build. caplo tool push uploads the folder as the package’s workspace draft (creating the package on the first push), builds it, and streams the build log. node_modules, .git and anything your .gitignore files ignore are left out.

    Terminal window
    caplo tool push

    The build installs from your lockfile, bundles the code, runs safety checks and lists the tools with no network access. It ends with Build <id> succeeded and the tools it found, or with an error code such as lockfile_required or tool_policy_missing.

  7. Publish. caplo tool publish turns the latest succeeded build into the next version:

    Terminal window
    caplo tool publish
    Published double-value version 1 from build <BUILD_ID>
    double_value__double_value (read, 5s)

A published tool appears on every agent’s Tools & actions tab, grouped under its package. Select it there, or list it in agent YAML by package and tool name:

tools:
- double-value/double_value

Then publish the agent. Its release pins version 1 of double-value; later versions reach the agent when you publish the agent again. See Give an agent tools.

If you already have a REST API, a coding agent can wrap it as a tool package with read and write tools, declared secrets and an egress list.

AI prompt

Wrap my REST API as a Caplo tool package

Builds an MCP tool package with read and write tools for an existing REST API, with declared secrets, an egress list and fixtures.

Show prompt
Goal: turn the REST API in this repository (or the one I describe) into a Caplo tool package that agents can call. Read operations become `read` tools, operations that change data become `write` tools that the customer confirms. The package declares the secret names it needs and the exact hosts it calls.

Read these pages first:

- https://docs.caplo.si/tools/first-tool.md
- https://docs.caplo.si/tools/define-tool-and-context.md
- https://docs.caplo.si/tools/write-tools.md
- https://docs.caplo.si/tools/secrets-and-egress.md
- https://docs.caplo.si/tools/testing.md
- https://docs.caplo.si/reference/caplo-yaml.md

Values I supply: package name `<PACKAGE_NAME>` (lowercase letters, digits and dashes), API base URL `<API_BASE_URL>`, and the operations to expose `<OPERATIONS>` (if I leave this out, propose a list).

Steps:

1. Find the API's routes, request and response shapes, and authentication scheme (an OpenAPI file, route handlers, or client code). List the operations you plan to expose, each marked `read` or `write`, with one sentence on what the agent would use it for. Mark anything that changes data, sends a message or costs money as `write`. Show me the list and wait for my answer.
2. Check that `node --version` reports v24. Then create the package and install dependencies:

   ```sh
   caplo tool init tools/<PACKAGE_NAME> --type mcp
   cd tools/<PACKAGE_NAME>
   npm install
   ```

3. In `src/server.ts`, register one tool per operation with `server.registerTool`. Tool names are lowercase with underscores (for example `get_order`, `cancel_order`). For each tool:
   - Write a description that says what it does and when to use it.
   - Give every input a description; use enums, patterns and length limits; keep inputs to what the model can know.
   - Define an `outputSchema` and return `structuredContent` with only the fields an agent needs, mapped explicitly from the API response.
   - Get secrets with `context(extra).secrets.get("<SECRET_NAME>")` and pass `signal` to every `fetch`.
   - For write tools, send `idempotencyKey` from the context to the API (as an `Idempotency-Key` header if the API supports it). Throw on any response you cannot interpret, so the outcome counts as unknown rather than failed.
   Keep `capabilities: { logging: {} }` on the server and log with `context(extra).log`, never `console.log`, and never log secrets or personal data.
4. Edit `caplo.yaml`: one `tools` entry per tool with `effect` and `timeoutSeconds` (1 to 30); `confirmation: required` on every write tool; `egress` with the API's exact host names (no scheme or path); `secrets` with the names the code reads, such as `<SECRET_NAME>`. Remove the template's example tools, fixtures and secret.
5. Update `src/stdio.ts` so `serveStdio` lists the same secret names.
6. Write one fixture per read tool in `tests/`, plus one for an expected error such as an unknown ID. Only use fixture `secrets` values that I give you for a test or sandbox environment; if I give none, ask me.
7. Run `caplo tool test` and fix every failure. Then ask me before running `caplo tool test --remote`, which pushes the package as a workspace draft and runs the fixtures on Caplo with the egress list enforced. Fix any `egress denied` line by correcting the `egress` list.

Verify: `caplo tool test` passes, `caplo tool test --remote` passes with no `egress denied` lines, and every tool has an `effect` that matches what it does.

Rules:

- Never write a secret value into code, `caplo.yaml`, fixtures, commit messages or this chat. Tell me to set values myself with `caplo tool secret set <SECRET_NAME>` (it prompts without echoing) or to bind a workspace secret with `caplo tool secret bind <SECRET_NAME> <workspace-secret>`.
- Do not run `caplo tool publish`, `caplo tool push` or `caplo publish` without my explicit approval. Publishing creates a version agents can pin.
- Do not mark a write tool `confirmation: none` unless I ask for it.
- Keep the package's existing lockfile in the commit; the build installs only what it lists.