Skip to content

One-Line AI Agent Security Middleware

The One-Line AI Agent Security Middleware Pattern

Section titled “The One-Line AI Agent Security Middleware Pattern”

In modern autonomous architectures (built on Vercel AI SDK, LangChain, CrewAI, or raw Model Context Protocol (MCP) clients), AI agents dynamically discover and invoke tools. Without pre-execution cryptographic enforcement, systems are vulnerable to:

  1. Tool Schema Drift & Rug-Pulls: An upstream MCP server silently changes a tool definition or injects an unvetted parameter after onboarding.
  2. Unauthorized Capability Invocations: An agent attempts an action (e.g. payments:refund or database:drop) that exceeds its delegated authority.
  3. Expired or Tampered Delegation Chains: A delegation receipt has expired or had its capability scope illegally expanded.

DID.is provides a high-level fail-closed middleware pattern that wraps any agent tool in a single line of code, verifying live tool drift and cryptographic authorization (didis.verifyTool) prior to execution.

FAIL-CLOSED EXECUTION BOUNDARY
DID.is Delegation v1 §4

Any missing parameter, unresolvable issuer DID, invalid cryptographic signature, or detected tool definition drift immediately aborts tool execution with a typed DidisAuthorizationError. No unverified payload reaches agent business logic.


Inspect the complete TypeScript and Python guard implementations below:

didis-guard
Run

                1
                import { DidisClient, type ToolAuthorization } from "@didis/client";
              
                2
                 
              
                3
                const defaultClient = new DidisClient();
              
                4
                 
              
                5
                export interface DidisGuardOptions {
              
                6
                /** The DID representing the executing agent (e.g. leaf did:key or did:web). */
              
                7
                agent: string;
              
                8
                /** The capability/tool string to authorize (e.g. "payments:refund"). */
              
                9
                tool: string;
              
                10
                /** Optional trusted root authority DID that the delegation must stem from. */
              
                11
                root?: string;
              
                12
                /** Optional Streamable HTTP MCP server URL. When set, asserts zero tool drift before running. */
              
                13
                mcpEndpoint?: string;
              
                14
                /** Optional custom DidisClient instance (e.g. targeting local daemon). */
              
                15
                client?: DidisClient;
              
                16
                }
              
                17
                 
              
                18
                /**
              
                19
                * Wraps any asynchronous tool handler with pre-execution DID.is authorization and drift checks.
              
                20
                * Throws immediately if authorization fails or if tool definition drift is detected.
              
                21
                */
              
                22
                export function withDidisGuard<TArgs, TResult>(
              
                23
                toolHandler: (args: TArgs) => Promise<TResult>,
              
                24
                options: DidisGuardOptions
              
                25
                ): (args: TArgs) => Promise<TResult> {
              
                26
                const client = options.client ?? defaultClient;
              
                27
                 
              
                28
                return async (args: TArgs): Promise<TResult> => {
              
                29
                  // 1. Optional MCP Drift & Rug-Pull Protection
              
                30
                  if (options.mcpEndpoint) {
              
                31
                    const inspection = await client.inspectMcp(options.mcpEndpoint);
              
                32
                    if (inspection.drift.status === "CHANGED" || inspection.drift.status === "REMOVED") {
              
                33
                      throw new Error(
              
                34
                        `[DID.is Security Block] Tool server drift detected on ${options.mcpEndpoint}: ` +
              
                35
                        `${inspection.drift.status}. Execution halted to prevent prompt injection.`
              
                36
                      );
              
                37
                    }
              
                38
                  }
              
                39
                 
              
                40
                  // 2. Cryptographic Delegation Verification
              
                41
                  const auth: ToolAuthorization = await client.verifyTool(
              
                42
                    options.agent,
              
                43
                    options.tool,
              
                44
                    options.root
              
                45
                  );
              
                46
                 
              
                47
                  if (!auth.authorized || auth.decision !== "ALLOW") {
              
                48
                    throw new Error(
              
                49
                      `[DID.is Authorization Denied] Agent '${options.agent}' is not authorized to invoke tool ` +
              
                50
                      `'${options.tool}': ${auth.reason}`
              
                51
                    );
              
                52
                  }
              
                53
                 
              
                54
                  // 3. Authorized — Proceed to Tool Execution
              
                55
                  return toolHandler(args);
              
                56
                };
              
                57
                }
              

                1
                import functools
              
                2
                from typing import Any, Callable, Optional
              
                3
                from didis import DidisClient, DidisError
              
                4
                 
              
                5
                default_client = DidisClient()
              
                6
                 
              
                7
                class DidisAuthorizationError(PermissionError):
              
                8
                  """Raised when an agent tool invocation fails cryptographic delegation checks."""
              
                9
                 
              
                10
                def didis_guard(
              
                11
                  agent: str,
              
                12
                  tool: str,
              
                13
                  root: Optional[str] = None,
              
                14
                  mcp_endpoint: Optional[str] = None,
              
                15
                  client: Optional[DidisClient] = None
              
                16
                ) -> Callable:
              
                17
                  """Universal pre-execution security decorator for CrewAI, LangChain, and MCP Python tools."""
              
                18
                  didis = client or default_client
              
                19
                 
              
                20
                  def decorator(fn: Callable) -> Callable:
              
                21
                      @functools.wraps(fn)
              
                22
                      def wrapper(*args: Any, **kwargs: Any) -> Any:
              
                23
                          # 1. Optional MCP Tool Drift Protection
              
                24
                          if mcp_endpoint:
              
                25
                              inspection = didis.inspect_mcp(mcp_endpoint)
              
                26
                              drift_status = inspection.get("drift", {}).get("status")
              
                27
                              if drift_status in ("CHANGED", "REMOVED"):
              
                28
                                  raise DidisAuthorizationError(
              
                29
                                      f"MCP tool catalog drift detected on {mcp_endpoint}: {drift_status}. Refusing execution."
              
                30
                                  )
              
                31
                 
              
                32
                          # 2. Cryptographic Delegation Verification
              
                33
                          auth = didis.verify_tool(agent=agent, tool=tool, root=root)
              
                34
                          if not auth.get("authorized") or auth.get("decision") != "ALLOW":
              
                35
                              raise DidisAuthorizationError(
              
                36
                                  f"Agent '{agent}' is unauthorized to invoke tool '{tool}': {auth.get('reason')}"
              
                37
                              )
              
                38
                 
              
                39
                          # 3. Authorized — Proceed to Tool Execution
              
                40
                          return fn(*args, **kwargs)
              
                41
                      return wrapper
              
                42
                  return decorator
              

import { tool } from "ai";
import { z } from "zod";
import { withDidisGuard } from "./didisGuard";
export const agentTools = {
// One-line wrapped secure payment tool
refundPayment: withDidisGuard(
tool({
description: "Process a refund for an e-commerce customer transaction",
parameters: z.object({ paymentId: z.string(), amountCents: z.number().int() }),
execute: async ({ paymentId, amountCents }) => {
// Business logic runs ONLY if cryptographic authorization passes
return { success: true, paymentId, amountCents };
},
}),
{
agent: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
tool: "payments:refund",
root: "did:web:enterprise.example"
}
)
};
from crewai.tools import tool
from security_guard import didis_guard
# One-line decorator guarding agent execution
@tool("Refund Transaction")
@didis_guard(
agent="did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
tool="payments:refund",
root="did:web:enterprise.example"
)
def process_refund(payment_id: str, amount: float) -> str:
"""Executes a financial refund against the ledger."""
return f"Refunded {amount} for {payment_id}"
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
import { withDidisGuard } from "./didisGuard";
const rawRefundTool = new DynamicStructuredTool({
name: "refund_payment",
description: "Issues a refund to a specified order ID",
schema: z.object({ orderId: z.string() }),
func: async ({ orderId }) => `Order ${orderId} refunded successfully.`
});
// Single-line execution guard interceptor
rawRefundTool.invoke = withDidisGuard(
rawRefundTool.invoke.bind(rawRefundTool),
{
agent: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
tool: "payments:refund",
root: "did:web:enterprise.example"
}
);

Audit Model Context Protocol (MCP) tool servers:

Terminal window
curl -s "https://did.is/api/v1/mcp/inspect?endpoint=https://mcp.example.com/mcp" | jq '{
status: .status,
mode: .mode,
toolsCount: (.tools | length),
inventoryHash: .inventoryHash,
drift: .drift.status
}'
import { DidisClient } from "@didis/client";
const didis = new DidisClient();
const inspection = await didis.inspectMcp("https://mcp.example.com/mcp");
console.log(`Server Protocol: ${inspection.mode} (${inspection.negotiatedVersion})`);
console.log(`Inventory Hash: ${inspection.inventoryHash}`);
console.log(`Catalog Drift: ${inspection.drift.status}`);
for (const tool of inspection.tools) {
console.log(`Tool: ${tool.name} (Declared: ${tool.declaredClass}, Heuristic: ${tool.heuristicClass})`);
if (tool.riskSignals.length > 0) {
console.warn(` ⚠️ Risk signals: ${tool.riskSignals.join(", ")}`);
}
}

  • definitionSha256: SHA-256 of RFC 8785 canonical tool JSON.
  • schemaSha256: SHA-256 of tool parameter schema.
  • inventoryHash: Deterministic composite hash over all sorted tool fingerprints.
  • Drift States: FIRST_OBSERVATION, UNCHANGED, ADDED, REMOVED, CHANGED, PROFILE_CHANGED.

Audit Agent-to-Agent protocol discovery cards:

const card = await didis.inspectA2a("https://agent.example");
console.log(`Card URL: ${card.cardUrl}`);
console.log(`Provider: ${card.provider?.organization}`);
console.log("Signatures:");
for (const sig of card.signatures) {
console.log(` [${sig.status}] ${sig.alg} via ${sig.kid ?? sig.jku}`);
}

Verify multi-hop capability delegation chains without centralized servers:

// Chain array ordered root first
const chain = [
"eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZGlzLWRlbGVnYXRpb24rand0Ii...", // Root: human -> org
"eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZGlzLWRlbGVnYXRpb24rand0Ii...", // Hop 1: org -> orchestrator
"eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZGlzLWRlbGVnYXRpb24rand0Ii..." // Hop 2: orchestrator -> worker
];
const { result } = await didis.verifyDelegation(chain, {
trustedRoots: ["did:web:enterprise.example"],
register: true // Persists valid chain in core SQLite for fast lookup
});
console.log(`Chain Status: ${result.status}`);
console.log(`Effective Capabilities: ${result.effectiveCapabilities.join(", ")}`);

Verify if an agent is authorized to invoke a tool:

// Stateless check against a supplied chain
const auth = await didis.authorize(chain, "payments:refund");
console.log(`Decision: ${auth.authorization.decision}`); // ALLOW or DENY
// Fast check against registered, unexpired chains stored in core SQLite
const registeredAuth = await didis.verifyTool(
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"payments:refund",
"did:web:enterprise.example"
);
console.log(`Registered Decision: ${registeredAuth.decision}`); // ALLOW