Claude Agent
Build an agent that uses Claude to decide when to show a UI, pushes a ggui interface to the user, collects the response, and continues the conversation.
npm install @ggui-ai/mcp-client @anthropic-ai/sdkSet environment variables:
export ANTHROPIC_API_KEY="sk-ant-..."export GGUI_API_KEY="ggui_sk_..."export GGUI_APP_ID="app_..."import Anthropic from "@anthropic-ai/sdk";import { GguiClient } from "@ggui-ai/mcp-client";
const anthropic = new Anthropic();const ggui = new GguiClient({ apiKey: process.env.GGUI_API_KEY!, appId: process.env.GGUI_APP_ID!,});
// Define ggui as a tool that Claude can useconst tools: Anthropic.Tool[] = [ { name: "show_ui", description: "Show an interactive UI to the user. Use this when you need to collect structured data, " + "show a form, display information visually, or present choices. " + "Describe the UI you want in natural language.", input_schema: { type: "object" as const, properties: { prompt: { type: "string", description: "Natural language description of the UI to show", }, context: { type: "object", description: "Data to make available to the generated UI", }, }, required: ["prompt"], }, },];
async function handleToolCall( toolName: string, toolInput: { prompt: string; context?: Record<string, unknown> }): Promise<string> { if (toolName !== "show_ui") { return JSON.stringify({ error: `Unknown tool: ${toolName}` }); }
const { sessionId, url } = await ggui.push({ story: { intent: toolInput.prompt, prompt: toolInput.prompt, context: toolInput.context, }, });
console.log(`\n--- UI Ready ---`); console.log(`Open this URL: ${url}`); console.log(`Waiting for user response...\n`);
const events = await ggui.waitForCompletion(sessionId, { pollInterval: 2000, maxWait: 300_000, });
await ggui.close(sessionId);
const submitEvent = events.find((e) => e.type === "data:submit"); if (submitEvent) { return JSON.stringify({ status: "submitted", data: submitEvent.payload }); }
return JSON.stringify({ status: "no_submission", events: events.map((e) => ({ type: e.type, payload: e.payload })), });}
async function chat(userMessage: string) { console.log(`\nUser: ${userMessage}`);
const messages: Anthropic.MessageParam[] = [{ role: "user", content: userMessage }];
let response = await anthropic.messages.create({ model: "claude-sonnet-4-5-20250929", max_tokens: 1024, system: "You are a helpful assistant. When you need to collect information from the user, " + "show them an interactive form UI using the show_ui tool. " + "Always use the UI for structured data collection rather than asking via text.", tools, messages, });
while (response.stop_reason === "tool_use") { const toolUseBlock = response.content.find( (block): block is Anthropic.ToolUseBlock => block.type === "tool_use" );
if (!toolUseBlock) break;
const toolResult = await handleToolCall( toolUseBlock.name, toolUseBlock.input as { prompt: string; context?: Record<string, unknown> } );
messages.push({ role: "assistant", content: response.content }); messages.push({ role: "user", content: [ { type: "tool_result", tool_use_id: toolUseBlock.id, content: toolResult, }, ], });
response = await anthropic.messages.create({ model: "claude-sonnet-4-5-20250929", max_tokens: 1024, tools, messages, }); }
const textBlock = response.content.find( (block): block is Anthropic.TextBlock => block.type === "text" ); console.log(`\nAssistant: ${textBlock?.text ?? "(no response)"}`);}
// Run the agentchat("I want to book a restaurant reservation for this weekend").catch(console.error);npx tsx claude-agent.tsWhat Happens
Section titled “What Happens”- You ask Claude to book a restaurant reservation
- Claude decides to use
show_uito collect the details - ggui generates a reservation form (date, time, party size, restaurant preferences)
- You open the URL, fill out the form, and submit
- Claude receives the form data and confirms the reservation details
Key Patterns
Section titled “Key Patterns”- Tool definition: Claude learns about ggui through the
show_uitool description - Agentic loop: The
while (stop_reason === 'tool_use')loop handles multi-turn tool calls - Context passing: Use
contextto pass data into the generated UI (e.g., available restaurants, user preferences)