HomeArticles › WebMCP Security

WebMCP Security — How AI Agent Permissions Work

By Andy · March 14, 2026 · 11 min read

Giving AI agents direct access to your website's functionality sounds risky. It should — any new browser API that lets external code call your functions deserves scrutiny. But WebMCP was designed with security as a primary constraint, not an afterthought.

This guide covers how the permission model works, what protections exist at each layer, and what you should (and should never) expose through WebMCP tools.

1. The Permission Model

WebMCP operates within the browser's existing security sandbox. Tools registered via navigator.modelContext.registerTool() run in the page's JavaScript context — they have the same access as any other script on the page, nothing more.

This means:

The security boundary is identical to any JavaScript running on your page. WebMCP doesn't escalate privileges — it just provides a structured calling convention.

When an AI agent wants to call a WebMCP tool, the browser shows the user what tool is being called, with what parameters, and asks for confirmation before executing. This is similar to how browsers handle other permission prompts (location, camera, notifications).

The user sees:

This consent layer is crucial — it keeps the user in control even when an AI agent is making decisions. As a site owner, write clear tool descriptions so users understand what they're approving.

3. Input Validation

WebMCP validates tool inputs against the JSON Schema you define in inputSchema before calling your handler. If an agent passes a string where you expected a number, or omits a required field, the call fails before your code runs.

inputSchema: {
  type: "object",
  properties: {
    email: {
      type: "string",
      format: "email",
      description: "User's email address"
    },
    count: {
      type: "integer",
      minimum: 1,
      maximum: 100
    }
  },
  required: ["email"]
}

But schema validation is your first line of defense, not your only one. Always validate inputs again inside your execute function — sanitize strings, check bounds, and escape anything that touches a database or HTML output.

4. Output Sanitization

Your tool's return value gets serialized to JSON and sent to the AI agent. Before returning data, strip out anything that shouldn't be exposed:

A common mistake: returning your entire database record including internal metadata. Only return the fields that are relevant and safe for the agent to see.

5. Rate Limiting

AI agents can call tools much faster than humans click buttons. Without rate limiting, a misbehaving agent could hammer your backend with hundreds of calls per second.

Implement rate limiting in your tool handlers:

const callCounts = new Map();
const RATE_LIMIT = 10; // calls per minute

function checkRateLimit(toolName) {
  const now = Date.now();
  const key = toolName;
  const history = callCounts.get(key) || [];
  const recent = history.filter(t => now - t < 60000);

  if (recent.length >= RATE_LIMIT) {
    throw new Error(`Rate limit exceeded. Max ${RATE_LIMIT} calls per minute.`);
  }

  recent.push(now);
  callCounts.set(key, recent);
}

Also rate limit on your backend — don't rely solely on client-side enforcement.

6. Authentication Patterns

WebMCP tools run in the authenticated user's browser session, so they naturally inherit whatever auth state exists. This is both a feature and a responsibility:

7. What Sites Should Never Expose via WebMCP

These operations should never be accessible through WebMCP tools, regardless of authentication state:

Stick to read-heavy, low-risk operations for your first WebMCP tools. Search, lookup, status checks, and data formatting are all good starting points.

8. Trust Indicators

When evaluating whether to trust a site's WebMCP tools, look for these signals:

9. How webmcplist.com Vets Submissions

Every site submitted to webmcplist.com goes through a review process before being listed. We check:

Sites that pass review get listed with a verified badge. We re-check periodically and remove sites that break or change their behavior.

Secure Tool Template

Here's a template that follows all the security patterns discussed above:

if (navigator.modelContext) {
  const rateLimiter = new Map();

  navigator.modelContext.registerTool({
    name: "safe-search",
    description: "Search our catalog by keyword. Returns up to 20 results " +
                 "with title, price, and availability.",
    inputSchema: {
      type: "object",
      properties: {
        query: {
          type: "string",
          minLength: 1,
          maxLength: 200,
          description: "Search keyword"
        },
        limit: {
          type: "integer",
          minimum: 1,
          maximum: 20,
          default: 10
        }
      },
      required: ["query"]
    },
    execute: async ({ query, limit = 10 }) => {
      // Rate limit check
      const now = Date.now();
      const calls = (rateLimiter.get("search") || [])
        .filter(t => now - t < 60000);
      if (calls.length >= 15) {
        return { error: "Rate limit exceeded. Try again in a minute." };
      }
      calls.push(now);
      rateLimiter.set("search", calls);

      // Sanitize input
      const cleanQuery = query.trim().slice(0, 200);

      // Call backend
      const res = await fetch(`/api/search?q=${encodeURIComponent(cleanQuery)}&limit=${limit}`);
      if (!res.ok) return { error: "Search temporarily unavailable" };
      const data = await res.json();

      // Sanitize output — only return safe fields
      return {
        results: data.items.map(item => ({
          title: item.title,
          price: item.price,
          inStock: item.inventory > 0,
          url: item.publicUrl
        })),
        total: data.totalCount
      };
    }
  });
}
Previous How to Add WebMCP to Your Website Next article The Future of WebMCP
Built by Andy · Build a microsite · Free VIN Decoder · QR Code Generator