Tabstack for the Pi coding agent
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 gives the
Pi coding agent 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.
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
Section titled “Install”npm install @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zodpnpm add @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zodyarn 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.
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
- Visit the Tabstack Console
- Sign in to your account (or create one if you haven’t already)
- Navigate to the API Keys section and click the “Manage API Keys”
- Once you are on the API Keys page, Click “Create New API Key”
- Give your key a descriptive name (e.g., “Development”, “Production”) and click the “Create API Key”
- Copy the generated API key and store it securely
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
# 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 sessionexport TABSTACK_API_KEY="your_api_key_here"
# Reload your shell or run:source ~/.bashrc # or ~/.zshrcWindows (Command Prompt)
# Set temporarily for current sessionset 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):
echo $TABSTACK_API_KEYWindows (Command Prompt):
echo %TABSTACK_API_KEY%Windows (PowerShell):
echo $env:TABSTACK_API_KEYYou should see your API key printed in the terminal.
Quickstart
Section titled “Quickstart”Re-export the package’s default extension from a file, and Pi registers all five Tabstack tools:
export { default } from "@tabstack/pi-agent";Load it with the pi CLI:
pi -e ./tabstack.tsThen ask Pi something that needs the web (“Read https://tabstack.ai 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
Section titled “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-agentwill not resolve here on its own. Give the directory apackage.jsonand run the install there as well:
cd ~/.pi/agent/extensionsnpm install @tabstack/pi-agent @earendil-works/pi-coding-agent typebox zodWithout 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
Section titled “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
Section titled “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 optionalurl,guardrails,data,country,max_iterations,max_validation_attempts.
See Schema design for writing json_schema_json that extracts reliably.
Optional inputs
Section titled “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.nocache: bypass the cache.country: ISO 3166-1 alpha-2 code for geotargeting.
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
Section titled “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:
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
Section titled “Configuration”For a custom API key or base URL, or to reuse one client, build the extension explicitly:
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
Section titled “How the schemas work”Pi types a tool’s parameters as a 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 "<tool_name>" 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
Section titled “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.