diff --git a/evaluations/webmcp.json b/evaluations/webmcp.json
new file mode 100644
index 0000000..60aa652
--- /dev/null
+++ b/evaluations/webmcp.json
@@ -0,0 +1,70 @@
+{
+ "skill": "webmcp",
+ "description": "Evaluates agent ability to expose IC canister methods as AI-accessible tools via WebMCP",
+ "output_evals": [
+ {
+ "name": "Add WebMCP to an existing canister",
+ "prompt": "I have a Rust canister with a .did file. How do I make it discoverable by AI agents?",
+ "expected_behaviors": [
+ "Adds a webmcp section to dfx.json with name, description, expose_methods",
+ "Runs ic-webmcp-codegen to generate webmcp.json and webmcp.js",
+ "Configures CORS headers via .ic-assets.json5 for /.well-known/webmcp.json",
+ "Includes the tag or manual ICWebMCP initialization",
+ "Does NOT reference esm.sh or any external CDN for @dfinity/webmcp"
+ ]
+ },
+ {
+ "name": "Add auth-required tools",
+ "prompt": "My canister has a transfer method that should require login before AI agents can call it. How do I set this up with WebMCP?",
+ "expected_behaviors": [
+ "Adds the method to require_auth in dfx.json webmcp config",
+ "Configures onAuthRequired callback in ICWebMCP constructor",
+ "Uses @icp-sdk/auth AuthClient or Internet Identity for the login flow",
+ "Does NOT import from deprecated @dfinity/auth-client",
+ "Creates a scoped delegation via createAgentDelegation with non-empty targets",
+ "Does NOT create an unrestricted delegation with empty targets array"
+ ]
+ },
+ {
+ "name": "Use polyfill for non-Chrome environments",
+ "prompt": "I want my IC canister tools to work with Claude and OpenAI, not just Chrome. How?",
+ "expected_behaviors": [
+ "Calls installPolyfill() before ICWebMCP.registerAll()",
+ "Uses getOpenAITools() or getAnthropicTools() to export in framework-specific format",
+ "Uses dispatchToolCall() to route tool call responses back to the canister",
+ "Does NOT assume navigator.modelContext is always available natively"
+ ]
+ },
+ {
+ "name": "Generate manifest from .did file",
+ "prompt": "Generate a WebMCP manifest from my ledger.did file for the ICP ledger canister",
+ "expected_behaviors": [
+ "Uses ic-webmcp-codegen did subcommand with --did flag",
+ "Sets --canister-id to the correct ICP ledger principal",
+ "Uses --expose to select specific methods",
+ "Uses --require-auth for methods that modify state",
+ "Generates valid JSON with schema_version 1.0"
+ ]
+ }
+ ],
+ "trigger_evals": {
+ "should_trigger": [
+ "How do I expose my canister to AI agents?",
+ "WebMCP integration for Internet Computer",
+ "Make my dapp work with navigator.modelContext",
+ "Generate a tool manifest from a .did file",
+ "How do AI agents call IC canisters?",
+ "Add OpenAI function calling to my IC canister",
+ "Anthropic tool use with Internet Computer",
+ "LangChain tools for IC canisters"
+ ],
+ "should_not_trigger": [
+ "How do I deploy a canister?",
+ "Write a Motoko counter canister",
+ "How does Internet Identity work?",
+ "What is certified data?",
+ "How do I use ckBTC?",
+ "Set up a custom domain for my canister"
+ ]
+ }
+}
diff --git a/skills/webmcp/SKILL.md b/skills/webmcp/SKILL.md
new file mode 100644
index 0000000..166139e
--- /dev/null
+++ b/skills/webmcp/SKILL.md
@@ -0,0 +1,294 @@
+---
+name: webmcp
+description: "Expose Internet Computer canister methods as AI agent tools via WebMCP (Web Model Context Protocol). Covers Candid-to-JSON-Schema codegen, browser tool registration via navigator.modelContext, certified query verification, Internet Identity scoped delegation for agents, and framework adapters for OpenAI/Anthropic/LangChain. Use when building AI-accessible dapps, agent tooling, or canister discovery."
+license: Apache-2.0
+compatibility: "icp-cli >= 0.2.2"
+metadata:
+ title: WebMCP — AI Agent Tool Protocol
+ category: Integration
+---
+
+# WebMCP for the Internet Computer
+
+## What This Is
+
+WebMCP (Web Model Context Protocol) is a W3C browser standard (Chrome 146+) that lets websites register callable tools for AI agents via `navigator.modelContext`. The Internet Computer is uniquely suited for WebMCP: Candid interfaces already define structured tool schemas, certified queries provide verifiable responses, and Internet Identity enables scoped agent authentication via delegation chains.
+
+The IC WebMCP stack generates tool manifests from `.did` files, serves them from asset canisters, and bridges `navigator.modelContext` to canister calls via `@icp-sdk/core`. A polyfill extends this to non-Chrome browsers and server-side agent frameworks (Claude, OpenAI, LangChain).
+
+## Prerequisites
+
+- Rust: `ic-webmcp-codegen` crate (CLI tool) for manifest generation
+- TypeScript: `@dfinity/webmcp` package for browser/agent integration
+- `@icp-sdk/core` >= 5.0.0 (provides agent, candid, identity, principal modules)
+- `@icp-sdk/auth` for Internet Identity login flows
+- Chrome 146+ for native `navigator.modelContext` (polyfill available for other environments)
+
+## Canister IDs
+
+No fixed canister IDs. WebMCP is a protocol layer applied to YOUR canister. The manifest embeds your canister's principal ID.
+
+## Mistakes That Break Your Build
+
+1. **Not generating the manifest before deploying.** The `webmcp.json` must be generated from your `.did` file using `ic-webmcp-codegen` and placed in your asset canister's assets directory. Without it, no AI agent can discover your tools.
+
+2. **Forgetting CORS headers on the manifest.** The `/.well-known/webmcp.json` endpoint must return `Access-Control-Allow-Origin: *` and `Content-Type: application/json`. Use `ic-webmcp-asset-middleware` or add an `.ic-assets.json5` config. Without CORS, cross-origin browser requests fail silently.
+
+3. **Using `document.currentScript` in module scripts.** The generated `webmcp.js` is an ES module — `document.currentScript` is always `null` for module scripts. The codegen uses top-level `await` instead. If you write custom init code, don't rely on `document.currentScript`.
+
+4. **Passing empty `targets` to `createScopedDelegation`.** An empty targets array produces an unrestricted delegation valid for ALL IC canisters, not just yours. Always pass at least your canister ID. Use `getDelegationTargets(canisterId, manifest.authentication)` to build the correct list.
+
+5. **Omitting methods from `expose_methods` but listing them in other config sections.** If `expose_methods` is set, only those methods appear in the manifest. Methods listed in `require_auth`, `certified_queries`, or `descriptions` but NOT in `expose_methods` are silently dropped.
+
+6. **Sending parameters without an IDL factory.** Without `setIdlFactory()`, the bridge can only call zero-argument methods. For methods with parameters, the bridge throws: `"requires an idlFactory to encode parameters"`. Always provide the generated IDL factory for full tool execution.
+
+7. **Assuming `certified: true` means full BLS verification.** The `certified` flag in the manifest indicates the query supports certified responses. `@icp-sdk/core/agent` verifies node signatures automatically (`verifyQuerySignatures: true` by default), but this is node-level verification. For full BLS threshold certificate verification of canister-managed certified data, use `readCertifiedData()` with `readState()`.
+
+8. **Loading `@dfinity/webmcp` from a CDN without integrity checking.** The generated `webmcp.js` imports `@dfinity/webmcp`. In production, this must come from your own bundled copy, not a third-party CDN. CDN imports without Subresource Integrity (SRI) are a supply chain attack vector.
+
+9. **Not handling `onAuthRequired` for update methods.** Tools marked `requires_auth: true` will throw if the user is anonymous and no `onAuthRequired` callback is configured. Always provide an auth callback that triggers Internet Identity login.
+
+10. **Ignoring recursive Candid types.** Types like `type Value = variant { Array: vec Value }` (common in ICRC-3) are handled by the codegen, which emits `{ "description": "Recursive type: Value" }` at the recursion point. Agents should treat these as opaque — they cannot be fully validated by JSON Schema alone.
+
+11. **Using deprecated `@dfinity/*` packages.** The `@dfinity/agent`, `@dfinity/candid`, `@dfinity/identity`, and `@dfinity/principal` packages are deprecated. Use `@icp-sdk/core` with subpath imports instead (e.g., `@icp-sdk/core/agent`). Use `@icp-sdk/auth` instead of `@dfinity/auth-client`.
+
+## Pipeline Overview
+
+```
+icp.yaml or dfx.json (with webmcp section)
+ + backend.did
+ │
+ ▼
+ic-webmcp-codegen dfx --dfx-json dfx.json --out-dir assets/
+ │
+ ├── backend.webmcp.json → /.well-known/webmcp.json
+ └── backend.webmcp.js → /webmcp.js
+ │
+ ▼
+Asset canister serves manifest + script with CORS headers
+ │
+ ▼
+Browser loads webmcp.js → @dfinity/webmcp → navigator.modelContext
+ │
+ ▼
+AI agent discovers tools → calls execute() → @icp-sdk/core/agent → canister
+```
+
+## Step 1: Configure WebMCP
+
+The codegen currently reads a `webmcp` section from `dfx.json`. If your project uses `icp.yaml`, add a `dfx.json` alongside it for the WebMCP config (native `icp.yaml` support is planned).
+
+```json
+{
+ "canisters": {
+ "backend": {
+ "type": "rust",
+ "candid": "backend.did",
+ "webmcp": {
+ "enabled": true,
+ "name": "My DApp",
+ "description": "Description for AI agents",
+ "expose_methods": ["get_items", "add_to_cart", "checkout"],
+ "require_auth": ["add_to_cart", "checkout"],
+ "certified_queries": ["get_items"],
+ "descriptions": {
+ "get_items": "List available products with prices",
+ "add_to_cart": "Add a product to the shopping cart",
+ "checkout": "Complete purchase with current cart contents"
+ },
+ "param_descriptions": {
+ "add_to_cart.product_id": "The unique product identifier",
+ "add_to_cart.quantity": "Number of items to add (default 1)"
+ }
+ }
+ }
+ }
+}
+```
+
+Config fields:
+- `enabled` (bool, default true): whether to generate for this canister
+- `name` (string): human-readable name shown to AI agents
+- `description` (string): what the canister does, in agent-friendly terms
+- `expose_methods` (string[]): which service methods to include. Omit to include all.
+- `require_auth` (string[]): methods requiring Internet Identity authentication
+- `certified_queries` (string[]): query methods with certified responses
+- `descriptions` (object): per-method descriptions (key: method name)
+- `param_descriptions` (object): per-parameter descriptions (key: `"method.param"`)
+
+## Step 2: Generate the Manifest
+
+```bash
+# Install (from the IC repo)
+cargo install --path rs/webmcp/codegen
+
+# Generate from dfx.json (all WebMCP-enabled canisters)
+ic-webmcp-codegen dfx --dfx-json dfx.json --out-dir assets/
+
+# Or from a single .did file
+ic-webmcp-codegen did \
+ --did backend.did \
+ --canister-id ryjl3-tyaaa-aaaaa-aaaba-cai \
+ --name "My DApp" \
+ --expose get_items,add_to_cart,checkout \
+ --require-auth add_to_cart,checkout \
+ --out-manifest assets/.well-known/webmcp.json \
+ --out-js assets/webmcp.js
+```
+
+## Step 3: Configure CORS Headers
+
+Add to `assets/.ic-assets.json5`:
+
+```json5
+[
+ {
+ "match": "/.well-known/webmcp.json",
+ "headers": [
+ { "name": "Content-Type", "value": "application/json" },
+ { "name": "Access-Control-Allow-Origin", "value": "*" },
+ { "name": "Access-Control-Allow-Methods", "value": "GET, OPTIONS" },
+ { "name": "Access-Control-Allow-Headers", "value": "Content-Type" },
+ { "name": "Cache-Control", "value": "public, max-age=300" }
+ ],
+ "allow_raw_access": true
+ },
+ {
+ "match": "/webmcp.js",
+ "headers": [
+ { "name": "Content-Type", "value": "application/javascript; charset=utf-8" },
+ { "name": "Access-Control-Allow-Origin", "value": "*" },
+ { "name": "Cache-Control", "value": "public, max-age=300" }
+ ],
+ "allow_raw_access": true
+ }
+]
+```
+
+Or generate it programmatically:
+
+```rust
+use ic_webmcp_asset_middleware::ic_assets_config;
+std::fs::write("assets/.ic-assets.json5", ic_assets_config()).unwrap();
+```
+
+## Step 4: Browser Integration
+
+**Automatic (zero-code):** Include the generated script in your HTML:
+
+```html
+
+```
+
+**Manual (with auth support):**
+
+```typescript
+import { ICWebMCP } from '@dfinity/webmcp';
+import { AuthClient } from '@icp-sdk/auth';
+
+const authClient = await AuthClient.create();
+
+const webmcp = new ICWebMCP({
+ manifestUrl: '/.well-known/webmcp.json',
+ host: 'https://icp-api.io',
+ onAuthRequired: async () => {
+ await authClient.login({ identityProvider: 'https://identity.ic0.app' });
+ return authClient.getIdentity();
+ },
+});
+
+await webmcp.registerAll();
+```
+
+## Step 5: Non-Chrome / Server-Side Agent Integration
+
+Use the polyfill to expose tools to any AI framework:
+
+```typescript
+import { ICWebMCP, installPolyfill, getOpenAITools, getAnthropicTools, dispatchToolCall } from '@dfinity/webmcp';
+
+// Install shim (no-op if navigator.modelContext exists natively)
+installPolyfill();
+
+const webmcp = new ICWebMCP({ manifestUrl: '/.well-known/webmcp.json' });
+await webmcp.registerAll();
+
+// OpenAI function calling
+const tools = getOpenAITools();
+const completion = await openai.chat.completions.create({
+ model: 'gpt-4o',
+ tools,
+ messages,
+});
+
+// Anthropic tool use
+const anthropicTools = getAnthropicTools();
+const message = await anthropic.messages.create({
+ model: 'claude-sonnet-4-5-20241022',
+ tools: anthropicTools,
+ messages,
+});
+
+// Dispatch a tool call result back to the IC canister
+const result = await dispatchToolCall('get_items', {});
+```
+
+## Step 6: Scoped Delegation for Agent Auth
+
+Create short-lived, canister-scoped delegations for AI agents:
+
+```typescript
+import { ICWebMCP, createScopedDelegation, getDelegationTargets } from '@dfinity/webmcp';
+
+const webmcp = new ICWebMCP({ identity: iiIdentity });
+await webmcp.registerAll();
+
+// 1-hour delegation scoped to this canister only
+const agentIdentity = await webmcp.createAgentDelegation({
+ maxTtlSeconds: 3600,
+});
+
+webmcp.setIdentity(agentIdentity);
+```
+
+## Candid → JSON Schema Type Mapping
+
+| Candid Type | JSON Schema |
+|---|---|
+| `nat` | `{ "type": "string", "pattern": "^[0-9]+$" }` |
+| `int` | `{ "type": "string", "pattern": "^-?[0-9]+$" }` |
+| `nat8/16/32` | `{ "type": "integer", "minimum": 0, "maximum": N }` |
+| `nat64` | `{ "type": "string", "pattern": "^[0-9]+$" }` |
+| `text` | `{ "type": "string" }` |
+| `bool` | `{ "type": "boolean" }` |
+| `blob` | `{ "type": "string", "contentEncoding": "base64" }` |
+| `principal` | `{ "type": "string" }` |
+| `opt T` | `{ "oneOf": [schema(T), { "type": "null" }] }` |
+| `vec T` | `{ "type": "array", "items": schema(T) }` |
+| `record { a: T }` | `{ "type": "object", "properties": { "a": schema(T) } }` |
+| `variant { A; B: T }` | `{ "oneOf": [{ "const": "A" }, { "type": "object", ... }] }` |
+
+## Verification
+
+```bash
+# Deploy with icp-cli
+icp deploy
+
+# Check manifest is served correctly
+curl -s https://.icp0.io/.well-known/webmcp.json | jq .schema_version
+# Expected: "1.0"
+
+# Check CORS headers
+curl -sI https://.icp0.io/.well-known/webmcp.json | grep -i access-control
+# Expected: Access-Control-Allow-Origin: *
+
+# Generate and verify manifest from .did file
+ic-webmcp-codegen did --did backend.did --no-js --out-manifest /dev/stdout | jq '.tools | length'
+# Expected: number of exposed methods
+
+# Run codegen tests
+cargo test -p ic-webmcp-codegen
+
+# Run TypeScript tests
+cd packages/ic-webmcp && npm test
+```