Scaffolds a new WebMCP tool component using useMcpTool with Zod schema, handler, annotations, and wires it into the WebMCPProvider tree. Use when the user wants to expose functionality as an MCP tool, make something callable by AI, add a new tool, or create an AI-accessible action.
Add this skill
npx mdskills install MCPCat/webmcp-add-toolComprehensive scaffolding guide for WebMCP tool creation with clear templates, annotations, and integration patterns
1---2name: webmcp-add-tool3description: Scaffolds a new WebMCP tool component using useMcpTool with Zod schema, handler, annotations, and wires it into the WebMCPProvider tree. Use when the user wants to expose functionality as an MCP tool, make something callable by AI, add a new tool, or create an AI-accessible action.4---56# Add a WebMCP Tool78## Overview910Each tool is a React component that calls `useMcpTool`. It registers on mount, unregisters on unmount. Tools can be headless (return `null`) or render UI showing execution state.1112## Workflow13141. **Determine** the tool's name, description, input schema, and what the handler does152. **Choose** headless vs UI tool163. **Scaffold** using the appropriate template below174. **Set annotations** based on tool behavior185. **Wire** the component into the `<WebMCPProvider>` tree1920## Template: Headless tool2122For tools that do work without rendering anything visible:2324```tsx25import { useMcpTool } from "webmcp-react";26import { z } from "zod";2728export function MyTool() {29 useMcpTool({30 name: "my_tool",31 description: "One-line description of what this tool does",32 input: z.object({33 // Define each input field with .describe() for AI context34 query: z.string().describe("The search query"),35 }),36 handler: async ({ query }) => {37 // Implement tool logic here38 const result = await doSomething(query);39 return {40 content: [{ type: "text", text: JSON.stringify(result) }],41 };42 },43 });44 return null;45}46```4748## Template: Tool with execution state UI4950For tools that show loading, results, or errors:5152```tsx53import { useMcpTool } from "webmcp-react";54import { z } from "zod";5556export function MyTool() {57 const { state, execute, reset } = useMcpTool({58 name: "my_tool",59 description: "One-line description of what this tool does",60 input: z.object({61 text: z.string().describe("Input text to process"),62 }),63 handler: async ({ text }) => {64 const result = await process(text);65 return { content: [{ type: "text", text: result }] };66 },67 onSuccess: (result) => {68 // Optional: analytics, toast, etc.69 },70 onError: (error) => {71 // Optional: error reporting72 },73 });7475 return (76 <div>77 <button onClick={() => execute({ text: "hello" })} disabled={state.isExecuting}>78 {state.isExecuting ? "Processing..." : "Run"}79 </button>80 {state.lastResult && <p>{state.lastResult.content[0].text}</p>}81 {state.error && <p className="error">{state.error.message}</p>}82 <span>Executions: {state.executionCount}</span>83 </div>84 );85}86```8788## Annotations8990Set annotations to hint AI agents about tool behavior. Only include annotations that apply:9192```tsx93useMcpTool({94 // ...95 annotations: {96 title: "Human-friendly title", // Display name for the tool97 readOnlyHint: true, // Tool only reads data, no side effects98 destructiveHint: true, // Tool deletes or irreversibly modifies data99 idempotentHint: true, // Safe to call multiple times with same input100 openWorldHint: true, // Tool interacts with external systems101 },102});103```104105**Guideline for choosing annotations:**106107| Tool behavior | Annotations to set |108|---|---|109| Fetches/queries data | `readOnlyHint: true` |110| Creates a record | `idempotentHint: false` (or omit, false is default) |111| Deletes or overwrites data | `destructiveHint: true` |112| Calls an external API | `openWorldHint: true` |113| Can be retried safely | `idempotentHint: true` |114115## Return format116117Handlers must return `CallToolResult`:118119```tsx120// Text content (most common)121return {122 content: [{ type: "text", text: "result string" }],123};124125// Structured content (for machine-readable results alongside text)126return {127 content: [{ type: "text", text: "Human summary" }],128 structuredContent: { key: "value", count: 42 },129};130131// Error result (caught by the framework, but you can also return errors explicitly)132return {133 content: [{ type: "text", text: "Something went wrong" }],134 isError: true,135};136137// Image content138return {139 content: [{ type: "image", data: base64String, mimeType: "image/png" }],140};141```142143## Dynamic tools144145Tools register on mount and unregister on unmount. Use conditional rendering to dynamically control which tools are available:146147```tsx148function App({ user }) {149 return (150 <WebMCPProvider name="app" version="1.0">151 <PublicTools />152 {user.isAdmin && <AdminTools />}153 {featureFlags.betaSearch && <BetaSearchTool />}154 </WebMCPProvider>155 );156}157```158159## Wiring into the provider160161Render the tool component anywhere inside `<WebMCPProvider>`. Common patterns:162163**Dedicated tools file** (recommended for apps with many tools):164165```tsx166// components/mcp-tools.tsx167export function McpTools() {168 return (169 <>170 <SearchTool />171 <TranslateTool />172 <CheckoutTool />173 </>174 );175}176177// In app root:178<WebMCPProvider name="app" version="1.0">179 <McpTools />180 <App />181</WebMCPProvider>182```183184**Colocated with feature** (when the tool is tightly coupled to a specific component):185186```tsx187function ProductPage({ product }) {188 return (189 <div>190 <ProductDetails product={product} />191 <AddToCartTool productId={product.id} />192 </div>193 );194}195```196197## Naming conventions198199- Tool names: `snake_case` (e.g., `search_catalog`, `delete_user`, `get_order_status`)200- Component names: PascalCase ending in `Tool` (e.g., `SearchCatalogTool`, `DeleteUserTool`)201- Descriptions: start with a verb, be specific (e.g., "Search the product catalog by keyword" not "Search stuff")202203## Checklist204205Before finishing, verify:206207- [ ] Tool name is unique across the app208- [ ] Description clearly explains what the tool does (AI agents read this)209- [ ] All input fields have `.describe()` for AI context210- [ ] Handler returns `CallToolResult` with `content` array211- [ ] Appropriate annotations are set212- [ ] Component is rendered inside `<WebMCPProvider>`213- [ ] For Next.js: file has `"use client"` directive214
Full transparency — inspect the skill content before installing.