PluginBench
Skill
Pass
Audit score 90

create-client-tool

cognitedata/builder-skills

Scaffold and wire AtlasTool client-side tools for Atlas agents with TypeBox schema validation.

What is create-client-tool?

Creates a new AtlasTool for browser-side execution in Atlas agents, handling TypeBox schema definition, execute function, and hook wiring into useAtlasChat. Use this whenever adding a client tool that fetches data, renders UI, calls APIs, shows charts, or performs any browser-side action.

  • Generates typed AtlasTool constant with TypeBox schema for parameter validation
  • Wires tool into useAtlasChat tools array automatically
  • Provides execute() function template for browser-side logic
  • Handles tool result output and structured details for UI rendering
  • Validates arguments at runtime using TypeBox/AJV stack from integrate-atlas-chat

How to install create-client-tool

npx skills add https://github.com/cognitedata/builder-skills --skill create-client-tool
Prerequisites
  • integrate-atlas-chat skill must be complete (src/atlas-agent/ vendored with TypeBox/AJV)
  • @sinclair/typebox package installed
  • useAtlasChat hook already integrated in application
Claude Code
Cursor
Windsurf
Cline

How to use create-client-tool

  1. 1.Read the file where useAtlasChat is called to locate the tools array
  2. 2.Review existing tool definitions to match naming conventions
  3. 3.Define the tool as a typed constant using Type from @sinclair/typebox with name, description, parameters schema, and execute function
  4. 4.Add the tool to the tools array in the useAtlasChat call
  5. 5.Optionally render tool results by accessing message.toolCalls in your message list component

Use cases

Good for
  • Create a tool that fetches and displays chart data from a local API
  • Build a tool that queries application state and returns formatted results
  • Add a tool that triggers UI navigation or shows a modal panel
  • Implement a tool that processes user input and updates local state
  • Create a tool that calls external APIs and formats responses for the agent
Who it's for
  • Frontend developers building Atlas agent applications
  • Teams extending agent capabilities with client-side logic
  • Developers integrating Atlas chat into React applications

create-client-tool FAQ

When should I use this skill vs. manually writing AtlasTool definitions?

Always use this skill when creating any AtlasTool. Do not manually write AtlasTool definitions or wire them into useAtlasChat — this skill ensures proper TypeBox schema, execute function, and hook wiring.

What is the difference between output and details in the execute return?

output is a plain text string sent back to the agent as the tool result; details is any structured data available on message.toolCalls for the UI to render.

How do I make a parameter optional?

Wrap the TypeBox type with Type.Optional(), e.g., Type.Optional(Type.Number({ description: "..." }))

What path should I use for importing from atlas-agent?

Use ./atlas-agent/types if the tool is directly under src/ next to the atlas-agent folder, or adjust the path relative to your tool file location (e.g., ../atlas-agent/types from src/tools/).

Can the execute function call external APIs?

Yes, execute() runs in the browser and can call APIs, query local state, render UI, trigger navigation, or perform any browser-side action.

Full instructions (SKILL.md)

Source of truth, from cognitedata/builder-skills.


name: create-client-tool description: "MUST be used whenever creating an AtlasTool (client-side tool) for an Atlas agent. Do NOT manually write AtlasTool definitions or wire them into useAtlasChat — this skill handles the TypeBox schema, execute function, and hook wiring. Prerequisite: integrate-atlas-chat (vendored src/atlas-agent + TypeBox/AJV deps). This includes tools that fetch data, render UI, call APIs, show charts, query local state, or perform any browser-side action. Triggers: AtlasTool, client tool, add tool, create tool, new tool, tool definition, agent tool." allowed-tools: Read, Glob, Grep, Edit, Write metadata: argument-hint: "[tool-name] [brief description of what it does]"

Create a Client Tool

Scaffold a new AtlasTool named $ARGUMENTS and wire it into the app.

Prerequisite

integrate-atlas-chat must already be complete: the app should vend the atlas-agent sources under src/atlas-agent/ (including react.ts) and have @sinclair/typebox installed as in that skill.

Background

Client tools let the Atlas Agent invoke logic that runs in the browser — rendering charts, querying local state, showing UI panels, triggering navigation, etc. The agent decides when to call the tool; the app executes it and returns a result.

The flow is:

  1. Agent responds with a clientTool action
  2. The library validates the arguments against the TypeBox schema
  3. execute() runs in the browser and returns { output, details }
  4. output (string) is sent back to the agent as the tool result
  5. details (any shape) is available on message.toolCalls for the UI to render

Step 1 — Understand the codebase

Before writing anything, read:

  • The file where useAtlasChat is called (often src/App.tsx or a chat hook) to find where tools is passed — imports are typically from ./atlas-agent/react after integrate-atlas-chat
  • Any existing tool definitions to match the file/naming conventions

Step 2 — Define the tool

Create the tool as a typed constant. Use Type from @sinclair/typebox to define the parameters schema — this gives both compile-time types and runtime validation (same stack as the vendored atlas-agent from integrate-atlas-chat).

import { Type } from "@sinclair/typebox";
import type { AtlasTool } from "./atlas-agent/types";

export const myTool: AtlasTool = {
  name: "my_tool",            // snake_case — this is what the agent uses to invoke it
  description:
    "One sentence describing what this tool does and when the agent should call it.",
  parameters: Type.Object({
    exampleParam: Type.String({ description: "What this param is for" }),
    optionalNum: Type.Optional(Type.Number({ description: "..." })),
  }),
  execute: async (args) => {
    // args is fully typed from the schema above
    // Do the work here — call APIs, update state, render UI, etc.
    return {
      output: "Plain text summary sent back to the agent",
      details: {
        // Any structured data you want available in the UI via message.toolCalls
      },
    };
  },
};

Adjust the ./atlas-agent/... path if the tool file is not directly under src/ next to the atlas-agent folder (for example ../atlas-agent/types from src/tools/).

TypeBox quick reference

SchemaUsage
Type.String()string
Type.Number()number
Type.Boolean()boolean
Type.Literal("foo")exact value
Type.Union([Type.Literal("a"), Type.Literal("b")])enum
Type.Array(Type.String())string[]
Type.Object({ ... })object
Type.Optional(...)mark any field optional

Always add a description to each field — the agent uses these to understand what to pass.


Step 3 — Wire into useAtlasChat

Find the useAtlasChat call and add the tool to the tools array:

const { messages, send, ... } = useAtlasChat({
  client: isLoading ? null : sdk,
  agentExternalId: AGENT_EXTERNAL_ID,
  tools: [myTool],   // add here
});

Step 4 — Render tool results (if needed)

If the tool returns structured details, render them in the message list. message.toolCalls is a ToolCall[] — one entry per tool call (client-side and server-side) in call order.

{msg.toolCalls?.map((tc, i) => (
  // tc.name    — tool name
  // tc.output  — the string sent back to the agent
  // tc.details — your structured data (cast to your known shape)
  <MyToolOutput key={i} data={tc.details as MyToolDetails} />
))}

Done

The agent can now invoke $ARGUMENTS. Describe what it does clearly in the description field — the agent relies on that string to decide when and how to call the tool.