> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polpo.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Tools

> Author TypeScript tools that run in the project sandbox

<Warning>
  Custom tools are a beta surface. Validate them in the sandbox before assigning them to production agents.
</Warning>

A custom tool is a TypeScript module that default-exports `defineTool`. Its name must match `^[a-z][a-z0-9_]*$` and an agent must list that exact name in `allowedTools`.

```typescript create_invoice.ts theme={null}
import { defineTool } from "@polpo-ai/tools";
import { Type } from "@sinclair/typebox";

export default defineTool({
  name: "create_invoice",
  description: "Create an invoice for a customer",
  parameters: Type.Object({
    customerId: Type.String(),
    amount: Type.Number(),
  }),
  async execute(ctx, params) {
    const key = ctx.connections.getToken("billing");
    if (!key) throw new Error("Missing billing credential");

    const response = await fetch("https://api.example.com/invoices", {
      method: "POST",
      headers: {
        authorization: `Bearer ${key}`,
        "content-type": "application/json",
      },
      body: JSON.stringify(params),
    });
    if (!response.ok) throw new Error(`Invoice API returned ${response.status}`);
    const invoice = await response.json();
    return `Created invoice ${invoice.id}`;
  },
});
```

`execute` returns a string or a `ToolResult`.

## Execution context

| Field             | Description                                              |
| ----------------- | -------------------------------------------------------- |
| `ctx.fs`          | Filesystem rooted in the project workspace               |
| `ctx.shell`       | Shell capability for workspace commands                  |
| `ctx.connections` | Project Connections resolved for this tool call          |
| `ctx.env`         | Safe environment variables with platform secrets removed |
| `ctx.workDir`     | Absolute workspace directory                             |
| `ctx.polpo`       | Optional project-scoped Polpo SDK client                 |
| `ctx.signal`      | Abort signal for this call                               |
| `ctx.onUpdate`    | Callback for partial tool updates                        |

There are no ambient platform credentials. Custom tools read external secrets through `ctx.connections`; tokens are resolved server-side from project Connections and injected only for the tool call.

```typescript theme={null}
const token = ctx.connections.getToken("github"); // provider id, connection id, or unique connection name
const headers = ctx.connections.getHeaders("linear-mcp");
const available = ctx.connections.list(); // non-secret metadata only
```

## Dependencies and bundling

Cloud detects imported npm packages, installs them with lifecycle scripts disabled, and bundles the tool with esbuild for Node 22. Pure JavaScript dependencies are supported. Native addons containing `.node` binaries or `binding.gyp` are rejected; choose a pure JavaScript alternative.

`@polpo-ai/tools` and `@sinclair/typebox` are runtime externals and do not need to be bundled.

## Manage tools

```bash theme={null}
polpo tools push .polpo/tools/create_invoice.ts
polpo tools list
polpo tools get create_invoice
polpo tools run create_invoice --args '{"customerId":"c_1","amount":4200}'
polpo tools rm create_invoice
```

`polpo deploy` also synchronizes `.polpo/tools/*.ts`.

The Agent API exposes the same registry:

| Method   | Path                        | Description                 |
| -------- | --------------------------- | --------------------------- |
| `POST`   | `/v1/tools`                 | Save `{ name, source }`     |
| `POST`   | `/v1/tools/{name}/deploy`   | Bundle with progress events |
| `GET`    | `/v1/tools`                 | List tools                  |
| `GET`    | `/v1/tools/{name}`          | Read source and metadata    |
| `POST`   | `/v1/tools/{name}/run`      | Run with `{ args }`         |
| `POST`   | `/v1/tools/{name}/generate` | Generate test inputs        |
| `POST`   | `/v1/tools/{name}/example`  | Generate an example         |
| `DELETE` | `/v1/tools/{name}`          | Delete a tool               |

```json theme={null}
{
  "name": "billing-agent",
  "allowedTools": ["read", "write", "create_invoice"]
}
```

Saving source and deploying a bundle are distinct API operations. The CLI and dashboard perform both steps for you.
