Add WebMCP Tools with webmcp-kit
When to Use This Skill
Use this skill when the request is about WebMCP tools in a website codebase:
- Add a new tool using
- Edit existing tool behavior, schema, or annotations
- Debug tools that do not appear or fail at runtime
- Test tools with webmcp-kit dev panel or direct execution
Do not use this skill for unrelated frontend/backend feature work that does not involve WebMCP tool definitions.
Preflight
Run these checks before changing code:
- Confirm dependencies and framework
- Check for and
- If missing, install with
npm install webmcp-kit zod
- Find likely tool files
- Search for existing tools and registration:
rg -n "defineTool\(|\.register\(" src
rg -n "enableDevMode\(" src
- Prioritize common locations:
- Locate app entrypoint
- Identify where browser app bootstraps and where tool calls belong
- Ensure is enabled for local debugging when requested
Playbooks
1) Add New Tool
Entry condition: No existing tool satisfies the requested capability.
Steps:
- Create or update the tool module.
- Define tool with specific camelCase name and clear description.
- Add strict with for every field and for optional defaults.
- Implement and return either string or response helper (, , ).
- Register the tool in the app flow with .
- Verify with dev panel and direct execution.
Reference template:
typescript
import { defineTool, jsonContent } from 'webmcp-kit';
import { z } from 'zod';
export const searchProducts = defineTool({
name: 'searchProducts',
description: 'Search products by query',
inputSchema: z.object({
query: z.string().describe('Search text entered by user'),
limit: z.number().min(1).max(50).default(10).describe('Maximum results'),
}),
execute: async ({ query, limit }) => {
const results = await db.products.search(query, limit);
return jsonContent(results);
},
});
searchProducts.register();
Completion checks:
- Tool appears in dev panel list
- Valid inputs execute successfully
- Invalid inputs return schema validation errors
2) Edit Existing Tool
Entry condition: Tool exists and user requests schema/behavior changes.
Steps:
- Update only the target tool definition (, , , ).
- Keep input changes backward-compatible unless user explicitly requests breaking changes.
- Re-run dev panel execution with old and new input shapes when relevant.
- Confirm path still executes at app startup.
Common edits:
- Schema constraints (, , , )
- Output formatting ( vs string)
- Optional params ( / )
- Confirmation hints for sensitive actions
Completion checks:
- Existing intended flow still works
- Updated behavior matches user request
3) Debug Missing or Broken Tool
Entry condition: Tool does not show up or throws errors.
Steps:
- If tool is missing in panel:
- Verify executes
- Verify runs in browser entry
- Check browser console for diagnostics
- If validation fails:
- Match failing field to
- Add/adjust bounds, optionals, defaults, descriptions
- If execution fails:
- Isolate failing logic in
- Add guarded error handling and clearer error messages
- Confirm environment mode:
- Native mode: real available
- Mock mode: fallback message is expected and dev panel still works
Expected fallback log (normal in unsupported browsers):
[webmcp-kit] Using mock modelContext for tool "toolName". Native WebMCP not available.
Completion checks:
- Root cause identified and fixed
- Tool now appears/executes in expected mode
4) Test Tool (Dev Panel + Direct Execution)
Entry condition: New or changed tool needs verification.
Steps:
- Dev panel test:
- Enable
- Open panel, select tool, run with valid and invalid payloads
- Direct execution test:
typescript
const result = await searchProducts.execute({ query: 'pizza', limit: 5 });
- Schema inspection when needed:
typescript
console.log(searchProducts.inputSchema); // JSON Schema
console.log(searchProducts.schema); // original Zod schema
Completion checks:
- Dev panel path works
- Direct invocation works
- Validation errors are understandable
Destructive or Sensitive Actions
For delete/checkout/payment/account changes:
- Add
annotations: { destructiveHint: true }
(or )
- Request user confirmation in before mutation
- Return cancellation result when confirmation is denied
Pattern:
typescript
const deleteItem = defineTool({
// ...
annotations: { destructiveHint: true },
execute: async (input, agent) => {
const { confirmed } = await agent.requestUserInteraction({
prompt: 'Are you sure?',
type: 'confirmation',
});
if (!confirmed) return 'Cancelled';
// perform mutation
},
});
Validation Checklist
- Tool name is specific, camelCase, and action-oriented
- explains when an agent should call the tool
- Every schema field has
- Optional inputs use or
- Tool is registered with in startup path
- is enabled when local testing is requested
- Native vs mock behavior is explicitly validated
- Sensitive actions require confirmation flow
Expected Output
When using this skill, report results in this structure:
- Summary
- What was added/edited/debugged
- Files changed
- Exact paths touched
- What changed in each file
- Verification
- Commands/tests/manual checks run
- Native or mock mode used
- Remaining risks
- Open issues, follow-up tests, or assumptions