From sdd-engineering-team
Add a new tool to the AI assistant. Use when: adding a new tool to the chat assistant, registering a new function the AI can call, extending assistant capabilities with a new tool, creating a new server-side tool for the chat API.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sdd-engineering-team:add-assistant-toolThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Adding a new tool the AI assistant can invoke
The assistant tool system has three parts that must be kept in sync:
<chat-api-route>.ts — e.g., app/api/chat/route.ts) — the tool definition with description, inputSchema, and execute function<assistant-thread-component>.tsx) — icon, display label, and visual rendering in the Chain of Thought<system-prompt-file>.md) — instructions telling the AI when and how to use the toolAll three must be updated when adding a new tool. Missing any part causes silent failures.
File: <chat-api-route>.ts (e.g., app/api/chat/route.ts)
Add the new tool to the tools: { ... } object in the streamText() call. Follow this pattern:
your_tool_name: tool({
description: "Clear, concise description of what the tool does and when to use it",
inputSchema: zodSchema(
z.object({
param1: z.string().describe("Description of param1"),
param2: z.boolean().optional().describe("Description of param2"),
})
),
execute: async ({ param1, param2 }) => {
// Call backend API or perform logic
// Return a serializable result object
return { result: "data" };
},
}),
Key rules:
snake_case (e.g., search_content, list_campaigns)description is what the LLM reads to decide whether to call the tool — be specific about when to use it.optional() for parameters that aren't required.describe() on every parameter — the LLM uses these descriptionserror field in the return valueBACKEND_URL (already defined in the file)File: <assistant-thread-component>.tsx
Find the two mapping objects near the ToolAdapter function:
TOOL_ICONSChoose from these icon categories, or use WrenchIcon as the fallback:
| Category | Icons | Example Tools |
|---|---|---|
| Discovery/search | SearchIcon | search, list, browse, query |
| File/content | FileTextIcon | read, view content |
| Navigation | MousePointerClickIcon | open, select, navigate |
| Fallback | WrenchIcon | any generic tool |
const TOOL_ICONS: Record<string, typeof WrenchIcon> = {
// ...existing entries...
your_tool_name: SearchIcon, // or FileTextIcon, MousePointerClickIcon, WrenchIcon
};
Import the icon from lucide-react if not already imported at the top of the file:
import { /* ...existing... */, YourNewIcon } from "lucide-react";
TOOL_LABELSProvide a short, user-friendly display name:
const TOOL_LABELS: Record<string, string> = {
// ...existing entries...
your_tool_name: "Friendly Name", // shown in the Chain of Thought UI
};
Label guidelines:
File: <system-prompt-file>.md
Add guidance under the ## Tool Usage Rules section about when and how the AI should use the new tool. Keep it concise — one or two bullet points.
Example additions:
- When a user asks to [do X], use the \your_tool_name` tool with [parameters].`- For [specific scenario], prefer \your_tool_name` over other methods.`If the new tool causes navigation in the UI (like select_content does):
File: <navigation-hook>.ts (e.g., hooks/use-select-content-navigation.ts)
Either extend the existing hook or create a new one following the same pattern:
parts for your tool's nameuseRef<Set<string>> for deduplicationsetSelectedFile, toggleCampaign, etc.)AssistantMessage in thread.tsxRebuild the Docker stack:
docker compose up -d --build <frontend-service>
Verify in the browser:
http://localhost:<dev-port>Check for build errors — missing imports or type mismatches will surface at build time
<chat-api-route>.ts in the tools objectTOOL_ICONS in <assistant-thread-component>.tsxlucide-react (if new)TOOL_LABELS in <assistant-thread-component>.tsx<system-prompt-file>.mdnpx claudepluginhub dzirkler/eng-team-pluginGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.