Documentation Index
Fetch the complete documentation index at: https://chatjs.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
Allow users to explicitly request tools like Canvas, Web Search, or Image Generation from the UI.
Problem
Users want to control which AI capabilities are used for their request. Without explicit selection, the AI decides autonomously which tools to invoke based on the message content.
Solution
Add a tool selector in the chat input that:
- Shows available tools based on model capabilities
- Stores the selection in message metadata
- Expands UI selections to actual tool names
- Appends a prompt to the message requesting those tools
How it works
- UI Selection: User picks a tool (e.g., “Canvas”) from the dropdown
- Metadata Storage: Selection stored as
selectedTool in message metadata
- Tool Expansion: Single UI choice expands to multiple related tools
- Prompt Injection: Text appended to message requesting those tools
Flow
components/chat-features-definitions.ts
import { Edit3, GlobeIcon, Images, type LucideIcon, Telescope } from "lucide-react";
import type { UiToolName } from "@/lib/ai/types";
type ToolDefinition = {
name: string;
icon: LucideIcon;
shortName: string;
};
export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
webSearch: { name: "Web Search", icon: GlobeIcon, shortName: "Search" },
deepResearch: { name: "Deep Research", icon: Telescope, shortName: "Research" },
generateImage: { name: "Create an image", icon: Images, shortName: "Image" },
createTextDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
// ... other document tools share the same Canvas definition
};
// Tools shown in the selector
export const enabledTools: UiToolName[] = [
"webSearch",
"deepResearch",
"generateImage",
"createTextDocument",
];
A single UI selection can map to multiple actual tools. For example, “Canvas” enables all document creation and editing tools.
lib/ai/determine-explicitly-requested-tools.ts
import type { ChatTools, ToolName } from "./types";
export function determineExplicitlyRequestedTools(
selectedTool: keyof ChatTools | null
): ToolName[] | null {
if (selectedTool === "deepResearch") {
return ["deepResearch"];
}
if (selectedTool === "webSearch") {
return ["webSearch"];
}
if (selectedTool === "generateImage") {
return ["generateImage"];
}
if (selectedTool === "createTextDocument") {
// Canvas enables all document tools
return [
"createTextDocument",
"createCodeDocument",
"createSheetDocument",
"editTextDocument",
"editCodeDocument",
"editSheetDocument",
];
}
return null;
}
When tools are explicitly requested, append a text part to the user’s message instructing the model.
app/(chat)/api/chat/add-explicit-tool-request-to-messages.ts
import type { ChatMessage, ToolName } from "@/lib/ai/types";
export function addExplicitToolRequestToMessages(
messages: ChatMessage[],
explicitlyRequestedTools: ToolName[] | null
) {
const lastMessage = messages.at(-1);
if (!lastMessage) return;
let toolsToRequest: ToolName[] = [];
if (explicitlyRequestedTools) {
toolsToRequest = explicitlyRequestedTools;
}
if (toolsToRequest.length > 0) {
lastMessage.parts.push({
type: "text",
text: `I want to use the tools ${toolsToRequest.join(", or ")}`,
});
}
}
4. Wire It Together in the API Route
app/(chat)/api/chat/route.ts
import { determineExplicitlyRequestedTools } from "@/lib/ai/determine-explicitly-requested-tools";
// In POST handler:
const selectedTool = userMessage.metadata.selectedTool ?? null;
const explicitlyRequestedTools = determineExplicitlyRequestedTools(selectedTool);
// Pass to core chat agent
const { result } = await createCoreChatAgent({
// ...other params
explicitlyRequestedTools,
});
lib/ai/core-chat-agent.ts
import { addExplicitToolRequestToMessages } from "@/app/(chat)/api/chat/add-explicit-tool-request-to-messages";
// Inside createCoreChatAgent:
addExplicitToolRequestToMessages(messages, explicitlyRequestedTools);
5. Build the UI Component
components/responsive-tools.tsx
import { Settings2, X } from "lucide-react";
import { enabledTools, toolDefinitions } from "./chat-features-definitions";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./ui/dropdown-menu";
import { Button } from "./ui/button";
export function ResponsiveTools({
tools,
setTools,
selectedModelId,
}: {
tools: UiToolName | null;
setTools: Dispatch<SetStateAction<UiToolName | null>>;
selectedModelId: string;
}) {
const activeTool = tools;
return (
<div className="flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
<Settings2 size={14} />
<span>Tools</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{enabledTools.map((key) => {
const tool = toolDefinitions[key];
const Icon = tool.icon;
return (
<DropdownMenuItem
key={key}
onClick={() => setTools(tools === key ? null : key)}
>
<Icon size={14} />
<span>{tool.name}</span>
{tools === key && <span>✓</span>}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
{/* Show active tool chip */}
{activeTool && (
<Button variant="ghost" size="sm" onClick={() => setTools(null)}>
{createElement(toolDefinitions[activeTool].icon, { size: 14 })}
<span>{toolDefinitions[activeTool].shortName}</span>
<X size={12} />
</Button>
)}
</div>
);
}
Key files
| File | Purpose |
|---|
components/chat-features-definitions.ts | UI tool definitions (name, icon, shortName) |
components/responsive-tools.tsx | Tool selector dropdown component |
lib/ai/determine-explicitly-requested-tools.ts | Maps UI selection to actual tool names |
app/(chat)/api/chat/add-explicit-tool-request-to-messages.ts | Appends tool request prompt to message |
lib/ai/core-chat-agent.ts | Calls the prompt injection function |
Extending
To add a new tool option:
- Add the tool definition to
toolDefinitions in chat-features-definitions.ts
- Add the key to
enabledTools array if it should appear in the selector
- Add a mapping case in
determineExplicitlyRequestedTools