Provider modules
Provider modules are self-contained tool bundles that ship their own schemas, executors, status labels, and connection gates. The dispatcher checks them before falling through to the legacy native catalog. Two modules ship today: hitl for approvals and stripe for billing.
Overview
- Module type
ToolModuleinsrc/lib/tools/types.ts- Dispatcher
src/lib/tools/registry.ts- Modules folder
src/lib/tools/providers/- Shipped modules
- hitl (1 tool), stripe (7 tools)
- Resolution order
- module first, native fallback
- Schema format
- OpenAI function calling
The native tool catalog grew into a single thousand-line file. Provider modules are how new tools get added without making that file longer. Each module owns its own surface end to end: the schemas Claude sees, the executor that runs when Claude calls a tool, the live status label rendered in the activity sidebar, and the integration gate that decides whether the user is allowed to use the module at all.
The contract is small enough that a module fits in a single file. The dispatcher is dumb on purpose; it knows how to find the right executor by name and nothing else. That lets new modules ship without touching shared code.
Module shape
Four fields. provider is the short id used in logs and status labels (for example, hitl or stripe). schemas is the array of OpenAI-style function definitions exposed to Claude. executors is a map from tool name to async executor function. statusLabels is an optional map from tool name to a function that produces a short verb phrase rendered while the tool runs.
The dispatcher
Three entry points. getModuleSchemas returns every schema from every module, flattened. The chat handler uses it when building the tool catalog. dispatchModuleTool looks up an executor by name and returns the result, or null if no module owns the name. The executor index is built once at module load; dispatch is constant time.
Call lifecycle
How a modular tool call actually runs from the model's tool_use block to a tool result.
The legacy executor in chat-tools.ts tries the module dispatcher first. If a module handles the tool name, its return value is the tool result. Otherwise control falls through to the historical switch statement, which still owns every name that has not been migrated. Migration is one tool at a time; both paths coexist.
Schemas
Every modular tool exposes an OpenAI-style schema. The model only sees these; the executor never appears in the prompt.
Executors
One async function per tool, signed against the ToolContext interface.
The executor receives the tool context (Supabase client, user id, conversation id, turn index, model tier) and the parsed arguments. It returns a string. Errors are caught upstream and turned into tool errors the model can read. Anything that takes more than a few seconds should stream progress through the context's logging hook so the activity sidebar stays responsive.
Status labels
A short verb phrase rendered while the tool is running.
Modules in production
The two modules wired in MODULES today.
hitl
The human-in-the-loop module exports one tool: wait_for_approval. The executor writes a row to pause_points and returns the sentinel that pauses the turn. The detailed contract lives on the Human in the loop page.
stripe
The stripe module exports seven tools that wrap the connected user's Stripe account: list customers, list payments, list subscriptions, create payment link, create invoice, create subscription, revenue metrics. Each tool checks the connection gate before calling the Stripe SDK and produces a structured string the model can summarise.
| Tool | Operation |
|---|---|
stripe_list_customers | List recent customers with optional email filter |
stripe_list_payments | List recent payments and charges |
stripe_list_subscriptions | List subscriptions filtered by status |
stripe_create_payment_link | Create a payment link for a deal |
stripe_create_invoice | Create and send a Stripe invoice |
stripe_create_subscription | Create a recurring subscription |
stripe_revenue_metrics | Return MRR, ARR, 30-day collected, outstanding |
Module vs native
Both ship tools to the model. When to pick which.
| Property | Module | Native catalog |
|---|---|---|
| File location | src/lib/tools/providers/<provider>.ts | src/lib/chat-tools.ts |
| Schema declaration | Inline in the module | Inline in the catalog file |
| Executor location | Same file as schema | Same file as schema |
| Status label | Per-module statusLabels map | Inside getToolStatusLabel |
| Connection gate | Checked inside the executor | Checked by toolPassesConnectionGate |
| Best for | New providers, isolated surfaces | Existing surface, shared helpers, deprecation candidates |
File map
Glossary
- Provider module
- A self-contained tool bundle exposing schemas, executors, and status labels under a single provider id.
- Dispatcher
- The function dispatchModuleTool that looks up an executor by tool name. Returns null when no module owns the name.
- Executor
- An async function that runs when Claude calls a tool. Receives ToolContext plus parsed args, returns a string result.
- Status label
- A short verb phrase computed from args. Rendered in the activity sidebar while the tool runs.
- Schema
- The OpenAI-style function definition the model sees. Name plus description plus a JSON-Schema parameters block.
- Connection gate
- The runtime check that removes a tool from the catalog when the user has not connected the underlying provider.