--- title: Tabstack for the Pi coding agent | Tabstack description: Give the Pi coding agent reliable web access with @tabstack/pi-agent: schema-enforced extraction, research, generation, and browser automation registered as native Pi tools. --- [`@tabstack/pi-agent`](https://www.npmjs.com/package/@tabstack/pi-agent) gives the [Pi coding agent](https://pi.dev) reliable web access: schema-enforced extraction, multi-source research, AI transformation, and browser automation, all registered as native Pi tools through a Pi extension, backed by the official [`@tabstack/sdk`](https://www.npmjs.com/package/@tabstack/sdk). A Pi extension is a `.ts` file whose default export Pi calls with its extension API. This package ships that default export, so wiring Tabstack into Pi is a one-line file. ## Install - [npm](#tab-panel-251) - [pnpm](#tab-panel-252) - [yarn](#tab-panel-253) Terminal window ``` npm install @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zod ``` Terminal window ``` pnpm add @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zod ``` Terminal window ``` yarn add @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zod ``` `@earendil-works/pi-coding-agent` (v0.82 or later), `typebox` (v1), and `zod` are peer dependencies, and all three are runtime dependencies of the adapter, not typecheck-only: it calls Pi’s `defineTool`, builds each tool’s `parameters` with Typebox’s `Type.Unsafe`, and validates tool input with Zod. Keep the installed `@earendil-works/pi-coding-agent` in step with the `pi` you actually run. A copy installed next to the adapter takes precedence for the adapter’s own imports, so a stale one means the tool objects handed to `registerTool` were built by an older `defineTool` than the Pi loading them. Pi’s loader does alias `@earendil-works/pi-coding-agent` and `typebox` to its own copies, but that applies to imports written directly in your extension file, and otherwise only as a fallback when neither is installed alongside the adapter. `zod` is never aliased: it always resolves from your project, so it is genuinely shared with your own code. Zod 4 is required: the adapter uses Zod 4’s native `z.toJSONSchema` to derive each tool’s Typebox parameters. A live Pi session also needs the `pi` CLI installed and a model provider key configured in Pi (for example `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`, per your Pi model config), alongside your Tabstack key. ## Create and Setup Your API Key Before you can start using Tabstack API, you’ll need to create an API key and set it up in your environment. ### 1. Create Your API Key 1. Visit the [Tabstack Console](https://console.tabstack.ai/) 2. Sign in to your account (or create one if you haven’t already) 3. Navigate to the API Keys section and click the “Manage API Keys” 4. Once you are on the API Keys page, Click “Create New API Key” 5. Give your key a descriptive name (e.g., “Development”, “Production”) and click the “Create API Key” 6. Copy the generated API key and store it securely Your API key will only be shown once. Make sure to copy and store it in a secure location. ### 2. Set Up Environment Variable For security and convenience, we recommend storing your API key as an environment variable rather than hardcoding it in your scripts. macOS/Linux Terminal window ``` # Add to your shell profile (~/.bashrc, ~/.zshrc, or ~/.bash_profile) export TABSTACK_API_KEY="your_api_key_here" # Or set it temporarily for the current session export TABSTACK_API_KEY="your_api_key_here" # Reload your shell or run: source ~/.bashrc # or ~/.zshrc ``` Windows (Command Prompt) ``` # Set temporarily for current session set TABSTACK_API_KEY=your_api_key_here # Set permanently (requires restart) setx TABSTACK_API_KEY "your_api_key_here" ``` Windows (PowerShell) ``` # Set temporarily for current session $env:TABSTACK_API_KEY = "your_api_key_here" # Set permanently for current user [Environment]::SetEnvironmentVariable("TABSTACK_API_KEY", "your_api_key_here", "User") ``` ### 3. Verify Your Setup Test that your environment variable is set correctly: **macOS/Linux/Windows (Git Bash):** Terminal window ``` echo $TABSTACK_API_KEY ``` **Windows (Command Prompt):** Terminal window ``` echo %TABSTACK_API_KEY% ``` **Windows (PowerShell):** Terminal window ``` echo $env:TABSTACK_API_KEY ``` You should see your API key printed in the terminal. ## Quickstart Re-export the package’s default extension from a file, and Pi registers all five Tabstack tools: tabstack.ts ``` export { default } from "@tabstack/pi-agent"; ``` Load it with the `pi` CLI: Terminal window ``` pi -e ./tabstack.ts ``` Then ask Pi something that needs the web (“Read and summarize what it does”) and it picks the tool itself. The tools resolve `TABSTACK_API_KEY` lazily on first call, so loading the extension never requires a key to be set. ### Always-on, without the `-e` flag Pi also auto-discovers extensions in two locations, but each has a condition worth knowing before you move the file there: - `.pi/extensions/*.ts` (project-local, checked into the repo). It loads only **after the project is trusted**, so on a fresh clone the Tabstack tools are absent until someone accepts Pi’s trust prompt. - `~/.pi/agent/extensions/*.ts` (global, applies to every project). Pi resolves an extension’s bare imports from the extension file’s own directory upward, not from the project you installed into, so `@tabstack/pi-agent` will not resolve here on its own. Give the directory a `package.json` and run the install there as well: Terminal window ``` cd ~/.pi/agent/extensions npm install @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zod ``` Without that, Pi reports `Failed to load extension`. The project-local path avoids it because your project’s `node_modules` is already in a parent directory. ## The tools | Tool name | Label in Pi | What it does | | -------------------------- | ------------------------ | ----------------------------------------------------------------- | | `extract_structured_data` | Extract Structured Data | Pull specific fields from a URL into a JSON shape you define. | | `extract_page_content` | Extract Page Content | Fetch a page as clean markdown. | | `research_question` | Research Question | Synthesize a cited answer across multiple pages. | | `generate_structured_data` | Generate Structured Data | Fetch a page, then AI-transform it into derived or reshaped JSON. | | `automate_browser_task` | Automate Browser Task | Run a multi-step, natural-language browser task. | ### Tool inputs The model fills these in, but it helps to know the shapes: - `extract_structured_data`: `url`, `json_schema_json` (a JSON-encoded JSON Schema string). - `extract_page_content`: `url`. - `research_question`: `query`. - `generate_structured_data`: `url`, `instructions`, `json_schema_json`. - `automate_browser_task`: `task`, plus optional `url`, `guardrails`, `data`, `country`, `max_iterations`, `max_validation_attempts`. See [Schema design](/guides/schema-design/index.md) for writing `json_schema_json` that extracts reliably. ## Optional inputs The model can pass these for finer control; they are sent to Tabstack only when present. - `extract_structured_data`, `extract_page_content`, `generate_structured_data`: - `effort`: `"min"`, `"standard"`, or `"max"`. Use `"max"` for JS-heavy pages. See [Effort levels](/guides/effort-levels/index.md). - `nocache`: bypass the cache. - `country`: ISO 3166-1 alpha-2 code for [geotargeting](/guides/geotargeting/index.md). - `research_question`: `mode` (`"fast"` or `"balanced"`), `nocache`. - `automate_browser_task`: `guardrails` (constraints on what the agent may do), `data` (context for form filling), `country`, `max_iterations`, `max_validation_attempts`. ## Registering a subset A coding agent that should read and research but never drive a browser can register just those tools. Pass `tools` to pick the subset; they are registered in the order given: tabstack.ts ``` import { createTabstackPiExtension } from "@tabstack/pi-agent"; // Read-and-research only, no browser automation. export default createTabstackPiExtension({ tools: ["extract_page_content", "research_question"], }); ``` For full control, register tools yourself in a hand-written extension. Each tool is exported as a Pi `ToolDefinition` bound to the default client, so it sits alongside your own tools: ``` import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { extractPageContentTool, researchQuestionTool } from "@tabstack/pi-agent"; export default function (pi: ExtensionAPI): void { pi.registerTool(extractPageContentTool); pi.registerTool(researchQuestionTool); // ...and your own tools alongside them. } ``` `createTabstackPiTools(config)` returns the same definitions keyed by tool name, for the same pattern with a custom client. ## Configuration For a custom API key or base URL, or to reuse one client, build the extension explicitly: tabstack.ts ``` import { createTabstackPiExtension } from "@tabstack/pi-agent"; export default createTabstackPiExtension({ apiKey: process.env.MY_KEY }); // or pass an SDK client you already have: createTabstackPiExtension({ client }) // combine with a subset: createTabstackPiExtension({ apiKey, tools: ["research_question"] }) ``` ## How the schemas work Pi types a tool’s `parameters` as a [Typebox](https://github.com/sinclairzx81/typebox) schema. Rather than hand-authoring duplicate Typebox schemas, the adapter converts each tool’s shared schema to JSON Schema with Zod 4’s native `z.toJSONSchema` and tags it with `Type.Unsafe`, which Pi sends straight to the provider. Pi validates the model’s arguments against that schema before it dispatches the call, rejecting a bad one with `Validation failed for tool ""` and a per-field reason. `Type.Unsafe` is opaque to TypeScript, not to Pi, so the tool body then re-parses the input to recover its type before the request runs, which doubles as defense in depth. ## Error handling When a tool call fails it throws `TabstackToolError` (a normalized message plus an HTTP `status` for API errors). Pi reports a failed tool by the thrown error, so the failure reaches the model as a tool result rather than aborting the session. Results come back as text content (markdown for `extract_page_content`, JSON for the rest), with the raw object also attached as the tool call’s `details`. ## Related - [Integrations overview](/integrations/index.md) - [Pi extensions](https://pi.dev/docs/latest/extensions) - [Tabstack TypeScript SDK](/sdks/typescript/quickstart/index.md) - [Claude Agent SDK integration](/integrations/claude-agent/index.md) and [eve integration](/integrations/eve/index.md)