Enforcer

MCP server authorization

An MCP server hands an agent a set of tools. That makes every tool call an authorization question: this caller, this tool, these arguments, right now. The Model Context Protocol itself does not answer it. The spec has no mechanism for per-tool authorization, argument constraints, delegation or audit, which is why the question keeps coming up in the spec's own discussions.

Why an allowlist is not authorization

The obvious first move is to allow some tools and block others. It fails for a specific reason worth understanding: one allowed call can change the next one. An allowed read returns untrusted text, that text changes the arguments of an allowed write, and both calls were on the allowlist. Server and tool allowlists answer identity questions. They do not preserve authorization intent after an allowed read returns something that changes what happens next.

Check the call, not just the tool

Treat the tool name as the action and its target as the resource, and make the check before the handler does any work.

// in your tool handler, before doing the work
const d = await fetch(`${BASE}/authz/check`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${callerToken}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    action: toolName,
    resource: { type: 'document', id: args.id },
    contexts: args
  })
}).then(r => r.json());

if (!d.allow) return { isError: true, content: [{ type: 'text', text: d.reason }] };

Passing the arguments in contexts is the part people skip. Allowing a tool is not the same as allowing every call to it, and a rule usually needs to care about the amount, the recipient or the destination rather than just the name.

Human approval, and the replay problem

If a sensitive action needs a person to approve it, bind the approval to the exact request: the actor, the resource, the amount, and an expiry. Otherwise the agent can resubmit the same action with a fresh timestamp and reuse an approval that was never given for it. Approval-per-call also stops scaling once you have more than a handful of tools, so reserve it for actions a human would have to unwind.

Connecting Enforcer over MCP

Enforcer is itself an MCP server, listed in the official registry as dev.instruxi.enforcer/v3.

url        https://api.instruxi.dev/mcp
transport  Streamable HTTP
header     X-API-Key: <an Enforcer API key>

Any MCP client connects from those three lines. Claude Code has a shortcut (claude mcp add --transport http enforcer https://api.instruxi.dev/mcp --header "X-API-Key: $ENFORCER_API_KEY"), and a client with no MCP support can skip it entirely: the REST API does everything the tools do.

Last updated 2026-09-11. Enforcer is identity and authorization in one system, for applications and AI agents. Docs at docs.instruxi.dev. Set it up from an AI agent: docs.instruxi.dev/setup.