Skip to content

2026-03-15

MCP + Intended: Governing Every Tool Call in 5 Lines of Code

Intended Team · Founding Team

MCP Is the Hot Protocol

The Model Context Protocol has rapidly become the standard for how AI agents interact with external tools and services. MCP provides a clean, typed interface for tool discovery, parameter passing, and result handling. It works with LangChain, PydanticAI, OpenAI Agents, and a growing ecosystem of frameworks.

But MCP has a governance gap. The protocol defines how tools are called. It does not define whether tools should be called. There is no built-in mechanism for authorization, risk evaluation, or audit logging. Every MCP tool call executes with whatever permissions the underlying service account has -- no policy check, no risk score, no proof.

For prototypes and development environments, that is fine. For production deployments where AI agents are making real decisions with real consequences, it is a problem.

The Governance Gap

Consider what happens when an AI agent makes an MCP tool call in a typical setup:

python
# A normal MCP tool — no governance: it just runs.
def create_pull_request(repo: str, title: str, base: str, head: str):
    return github.create_pull_request(repo, title, base, head)

create_pull_request("acme/production-api", "Refactor payment processing", "main", "ai/payment-refactor")

This call goes directly to GitHub. The AI agent has whatever permissions the GitHub token provides. There is no check on whether this particular agent should be creating pull requests against this particular repository. There is no risk evaluation. There is no audit trail beyond what GitHub itself logs. If the AI agent decides to create 50 pull requests in a minute, nothing stops it.

Now wrap that tool with Intended. On an MCP server, the published Python middleware guards each tool function so it runs only on an ALLOW decision:

python
from intended.mcp import IntendedMCPMiddleware

middleware = IntendedMCPMiddleware(
    api_key="intended_live_your_api_key_here",
    tenant_id="your-tenant-id",
)

@middleware.protect
def create_pull_request(repo: str, title: str, base: str, head: str):
    # Runs only if Intended returns ALLOW; raises PermissionError on escalate/deny.
    return github.create_pull_request(repo, title, base, head)

Same tool, same parameters. But now the call passes through Intended's authority engine first. The action (mcp.tool.create_pull_request) is classified against the Open Intent taxonomy (OI-100, software development), risk-scored, and policy-evaluated — production-repo PRs can require human approval — and every decision is recorded in the tamper-evident audit chain. The developer experience is nearly identical; the governance difference is enormous.

Setup in a Few Lines

The runnable path today is the published Python middleware (pip install intended). Create it once, then decorate each tool:

python
from intended.mcp import IntendedMCPMiddleware

middleware = IntendedMCPMiddleware(
    api_key="intended_live_your_api_key_here",
    tenant_id="your-tenant-id",
)
# then put @middleware.protect on each MCP tool function (see above)

Each guarded call is submitted as an intent, evaluated against your policies, and either approved, denied, or escalated — before the tool's side effect runs. Fail-closed is the default: any engine error denies.

Zero-config gateway proxy — private preview. A drop-in gateway proxy that governs every MCP call without per-tool decorators (the @intended-inc/mcp-gateway package) is in private preview and is not yet published to npm. Talk to us for access; until then, use the Python middleware above.

What Happens Under the Hood

When the middleware intercepts an MCP tool call, it executes a four-step pipeline:

Step 1: Intent Classification

The gateway maps the MCP tool name and parameters to a Open Intent category. "github.createPullRequest" maps to OI-100 (Software Development), category "pull-request-create." The classification includes the tool name, the target resource, and the action parameters.

The middleware names each action mcp.tool.<tool_name> and the engine classifies it against the Open Intent taxonomy from that text — there is no mapping file to maintain. (The private-preview gateway proxy adds configurable per-tool mappings for built-in connectors.)

Step 2: Policy Evaluation

The classified intent is evaluated against your organization's policies. Policies can be as simple as "allow all read operations" or as granular as "deny production deployments by AI agents between 9 AM and 5 PM unless the agent has Tier 3 authorization and the deployment was pre-approved in a change management ticket."

Policies are defined in the Intended console or via the API. They reference Open Intent categories, risk thresholds, time windows, agent identities, and resource patterns. The policy engine evaluates all applicable policies and returns a decision: ALLOW, DENY, or ESCALATE.

Step 3: Token Issuance

If the decision is ALLOW, the authority engine issues a signed token. The token contains the decision metadata: what was authorized, which policies were evaluated, the risk score, the timestamp, and the cryptographic signature. The token is attached to the tool call as evidence of authorization.

If the decision is ESCALATE, the tool call is held in the queue. A notification is sent to the designated reviewers. When a human approves or denies the escalation, the tool call either proceeds with a token or is rejected.

If the decision is DENY, the tool call is blocked. The gateway returns a structured error to the AI agent explaining why the action was denied and what policy triggered the denial.

Step 4: Audit Recording

Regardless of the decision, the event is recorded in the audit chain. The entry includes the intent, the classification, the policies evaluated, the decision, the risk score, and the evidence bundle. The entry is hash-linked to the previous entry, creating a tamper-proof chain.

Fail-closed by default

The middleware is fail-closed: if Intended is unreachable or the engine errors, the decision is DENY and the tool's side effect never runs. There is no fail-open mode in the published middleware — an explicit ALLOW is the only thing that lets an action through.

Framework helpers

Beyond the MCP middleware, the published intended SDK ships first-class guards for the major Python agent frameworks — each wraps a tool so it runs only on ALLOW:

  • LangChainfrom intended.langchain import IntendedToolGuardguard.wrap(tool)
  • PydanticAIfrom intended.pydantic_ai import IntendedPydanticGuard@guard.protect
  • OpenAI Agentsfrom intended.openai_agents import IntendedOpenAIGuard@guard.protect

For TypeScript agents, use createIntendedSdk(...).guard(action, onAllow) directly inside each tool. In every case your existing tool definitions and agent logic are unchanged.

What It Catches

In production deployments, the MCP Gateway catches patterns that raw MCP tool calls miss entirely:

  • Velocity anomalies: an agent making 10x its normal rate of tool calls
  • Scope creep: an agent calling tools outside its designated domain
  • Risk escalation: routine operations that shift to high-risk when combined (e.g., modifying a security group and then deploying code)
  • Policy violations: actions that violate time-based, resource-based, or role-based policies
  • Audit gaps: tool calls that would otherwise execute without any record

Every denied or escalated action is an incident that was prevented, not investigated after the fact.

Getting Started

pip install intended, set your API key and tenant id, and put @middleware.protect on your MCP tools. Every guarded call is classified, evaluated, and audited — no changes to your agent logic. For zero-decorator coverage across an entire MCP server, the gateway proxy is in private preview (talk to us).

MCP + Intended: Governing Every Tool Call in 5 Lines of Code | Intended