Build a self-contained tool that the chatjs add CLI command can install into any ChatJS project.
For the product-level overview of the registry and when to use it, start with Tool Registry.
Overview
The registry system lets you package a backend AI tool and its frontend renderer as a distributable unit. Once installed, the tool is available to the AI model and its output is rendered with your custom component — with full TypeScript type safety end to end. This recipe covers how the system works and what files make up an installable tool.How it works
Installing a tool does three things:- Copies the tool implementation and renderer into the project
- Injects the tool into
tools/chatjs/tools.tsandtools/chatjs/ui.ts, the two files the CLI manages - The static wrappers (
installed-tools.ts,tool-renderer-registry.ts) automatically pick up the new entries while built-in tools stay undertools/platform
File structure
Each tool lives in its own folder. The CLI copies two files and injects entries for all three into the registry index.| File | What it contains |
|---|---|
tools/chatjs/{name}/tool.ts | Backend tool: schema + execute function |
tools/chatjs/{name}/renderer.tsx | Frontend renderer component |
tools/chatjs/tools.ts | Server registry file — CLI injects tool imports and entries here |
tools/chatjs/ui.ts | Client registry file — CLI injects renderer imports and entries here |
Tool environment variables
If a tool needs environment variables, declare them intool.ts with toolEnvVars.
tools/chatjs/retrieve-url/tool.ts
- The registry build extracts
toolEnvVarsinto the published manifest asenvRequirements. chatjs adduses that metadata to warn about missing env vars during installation.check-envvalidates installed tools by readingtoolEnvVarsfrom each installedtool.ts.
description is optional. If omitted, ChatJS formats a readable fallback from options, for example OPENAI_COMPATIBLE_BASE_URL + OPENAI_COMPATIBLE_API_KEY or TAVILY_API_KEY or FIRECRAWL_API_KEY.
Code
1. Backend tool
Registry tools usetool() from the AI SDK directly. They are plain tools that take input and return output.
tools/chatjs/word-count/tool.ts
2. Frontend renderer
The renderer importsWordCountOutput from the tool file and defines a local WordCountPart type. This avoids the circular import that would arise from importing through @/lib/ai/types (which depends on the tools registry).
tools/chatjs/word-count/renderer.tsx
3. Registry files (CLI-managed)
The CLI appends imports and entries to the managed server and client registry files. You do not edit these files manually.tools/chatjs/tools.ts
tools/chatjs/ui.ts
Type flow
installed-tools.ts derives InstalledTools from the tools export via a mapped InferUITool type. types.ts intersects this with the built-in ChatTools, so installed tools become first-class typed members of the union.
{ tool: unknown } and cast to a locally defined type. The local type is built from the exported WordCountOutput type in tool.ts, keeping the renderer isolated from the @/lib/ai/types import chain (which would cause a circular dependency via installed-tools → tools/chatjs/index). The output field is fully typed with no unknown beyond the initial cast.
Path configuration
The CLI readspaths.tools from chat.config.ts to know where to copy files and which import alias to write into the registry index. The default matches the out-of-the-box project layout.
chat.config.ts
Platform vs installable tools
ChatJS now separates tool code into two buckets:| Location | Purpose |
|---|---|
tools/platform | Built-in product tools and internal helpers |
tools/chatjs | CLI-managed installable tools from the registry |
tools/chatjs/tools.ts and tools/chatjs/ui.ts are managed by chatjs add.
Architecture note
Treattools/platform and tools/chatjs as different ownership layers:
- Put a tool in
tools/platformwhen it is part of the product runtime, depends tightly on app infrastructure, or needs richer internal contracts. - Put a tool in
tools/chatjswhen it is a self-contained installable unit that can be copied into another ChatJS project bychatjs add.
- Installable tools are the extension surface.
- Platform tools are the built-in product surface.
tools/chatjs/tools.tsandtools/chatjs/ui.tsare the CLI-managed registry files.paths.toolsconfigures the installable tools namespace, not the platform tool namespace.
tools/chatjs unless it remains useful as a portable package with a small public contract.
Registry manifest
When you publish a tool to a registry, the manifest is a self-contained JSON payload with the source files, dependency list, and any extracted environment requirements the CLI will install and validate.Related
- Tool Part — how built-in tools connect a backend definition to a frontend component