MCP Proxy Design Principles

July 27, 2026 ยท View on GitHub

AgentVeil MCP Proxy applies foundational security engineering principles from Saltzer and Schroeder's 1975 paper The Protection of Information in Computer Systems to the agent action control domain. This document maps the eight principles to specific architecture decisions in the proxy and AVP control plane.

The principles have remained useful for more than 50 years because they provide a compact vocabulary for reviewing protection mechanisms: simple mechanisms, deny-by-default decisions, complete mediation, open design, separation of privilege, least privilege, least common mechanism, and psychological acceptability. This mapping makes AVP's MCP Proxy design reviewable against a framework that security and compliance teams already use for operating systems, containers, infrastructure, and cloud control planes.

This is not a proof that every possible policy is safe. Harrison, Ruzzo, and Ullman's 1976 paper Protection in Operating Systems shows that the general access-control safety problem is undecidable under weak assumptions. AVP's claim is narrower and operational: constrain agent actions to an explicit policy vocabulary, mediate them before downstream execution, bind approvals to signed evidence, and make decisions auditable and reversible inside that bounded subset.

1. Economy Of Mechanism

Saltzer and Schroeder's economy of mechanism principle asks security designs to stay small enough to inspect and reason about.

AgentVeil MCP Proxy keeps the trusted primitive set intentionally narrow:

  • Ed25519 did:key identity for proxy identity and backend signer identity.
  • JCS canonical JSON for signed DecisionReceipts and exported proof material.
  • SHA-256 hashes for payload binding, evidence record hashes, and chain links.
  • Local SQLite evidence with one hash-chain construction.
  • JSON policy rules with explicit decision ranks.

The proxy avoids stacking multiple unrelated crypto formats or authorization protocols into the same path. For example, offline verification does not need a JWT parser, OAuth introspection, or a proprietary binary format. It validates a JCS string, an Ed25519 signature, trusted signer DIDs, and record-to-receipt field bindings.

This makes review narrower: if a DecisionReceipt verifies, the auditor can inspect the same canonical body that the signer signed. If an evidence chain verifies, the auditor can trace record order and mutation history through one hash construction.

2. Fail-Safe Defaults

Fail-safe defaults mean access starts denied and becomes allowed only through an explicit rule, signed decision, or approval.

The MCP Proxy applies this in several places:

  • Built-in policy defaults block or require approval for higher-risk actions.
  • Destructive filesystem-style operations are denied or routed to explicit approval by default.
  • Runtime Gate fallback behavior blocks destructive, production, and financial risk classes when the backend cannot provide a trusted decision.
  • Approval timeout behavior defaults to deny; the timeout path does not become an implicit allow.
  • New local proxy identities are encrypted by default. Plaintext identity storage requires the explicit --plaintext opt-out.
  • Headless mode denies by default unless a bounded policy grants a specific pre-approval.

The practical result is that missing policy, missing approval, missing trusted signature, missing receipt binding, and unavailable Runtime Gate responses move toward denial or explicit operator review, not silent forwarding.

3. Complete Mediation

Complete mediation requires every protected access to be checked.

For tool calls routed through the MCP Proxy, the downstream server is not called directly. Each client JSON-RPC request passes through the same mediation path:

  1. Parse and bound the client message.
  2. Classify the tool call by action and risk.
  3. Evaluate local policy rules and built-in policy packs.
  4. Call AVP Runtime Gate when policy requires an authoritative decision.
  5. Route to the local approval surface when a human decision is required.
  6. Write durable evidence before forwarding or terminal denial.
  7. Forward only allowed or approved calls to the downstream MCP server.

This mediation boundary is precise: calls that bypass the proxy cannot be mediated by the proxy. That is why Lurkr remains a separate pre-runtime scanner: it finds bypass paths, exposed credentials, and uncontrolled integrations before deployment.

4. Open Design

Open design says protection should not depend on hiding the algorithm or protocol.

The source-available MCP Proxy package exposes the proxy source, policy DSL, receipt schema handling, JCS canonicalization choices, evidence-chain format, and offline verifier under its own license. Auditors can inspect the implementation instead of trusting a black-box statement that an action was controlled.

The protected secrets are the correct trust anchors and private keys:

  • The customer's encrypted proxy identity key.
  • The local control grant scoped to that proxy identity.
  • The pinned AVP backend signer DID set used for DecisionReceipt verification.

If an exported bundle includes an attacker-controlled signer DID, strict offline verification rejects it: verify trusts only the operator-supplied --trusted-signer-did set and never the bundle's embedded signer list, so a receipt-bearing bundle fails closed when no external pin is supplied instead of falling back to a warning.

5. Separation Of Privilege

Separation of privilege reduces reliance on a single authority.

The MCP Proxy path separates three key roles:

  • Proxy identity key: the local Ed25519 key identifying one proxy instance. It is encrypted at rest by default.
  • Control grant: the tenant-scoped delegation receipt authorizing that proxy identity to ask Runtime Gate for decisions in allowed categories.
  • Backend signer DID: the AVP signing identity that issues authoritative DecisionReceipts.

A leaked proxy identity cannot forge backend decisions. A backend signing key cannot impersonate a customer's local proxy identity without the customer's control grant and local state. A stale or missing control grant cannot silently turn into permission to execute downstream calls.

This separation also appears in the local approval path: browser approval requires a path token, an HMAC cookie, and a per-request CSRF token. Evidence records are written separately from transient browser state.

6. Least Privilege

Least privilege means each component and authorization should carry only the authority needed for its task.

The proxy's approval model treats approvals as capability tokens, not flat permissions:

  • Signed: authoritative decisions are signed by the AVP backend signer DID.
  • Scoped: receipts bind risk class, policy context hash, payload hash, and request metadata.
  • Time-bounded: follow-on grants such as similar_5m expire quickly.
  • Replay-resistant: the local replay cache rejects repeated signed receipts within the proxy's retention window, and offline verification rejects duplicate receipt references in a bundle.
  • Attenuatable: cache-hit records reference granted_by_request_id; they inherit a narrower follow-on scope rather than broadening authority.

High-risk headless pre-approvals also require exact payload binding unless the operator explicitly opts into a narrow-match exception. Destructive, production, and financial approvals are therefore tied to concrete action evidence, not broad standing rights.

7. Least Common Mechanism

Least common mechanism asks designs to minimize shared mutable machinery that many users depend on.

The documented deployment pattern is one proxy process per IDE client:

  • Each proxy process has its own local approval server port.
  • Each proxy home has its own identity, control grant, and evidence database.
  • Each proxy process has independent circuit breaker state.
  • Each process starts and supervises one downstream MCP server.

This avoids turning a single local proxy into shared mutable control state for multiple IDEs. Cross-IDE isolation is not absolute because all processes still share the host operating system, but the AVP component itself does not add a shared approval queue or shared downstream subprocess across IDE clients.

For centralized policy across multiple developers, enforce policy in the AVP backend or deployment environment. Do not multiplex unrelated developer IDEs through one local proxy as a shortcut.

8. Psychological Acceptability

Psychological acceptability means people should be able to use the protection mechanism correctly without fighting the interface.

The proxy keeps the first-use path familiar:

  • agentveil-mcp-proxy init creates identity, config, and control grant.
  • agentveil-mcp-proxy doctor validates local state.
  • agentveil-mcp-proxy run starts the stdio proxy used by MCP clients.

Built-in policy defaults let operators start from reviewable behavior before writing custom policy rules. The browser approval surface uses conventional form semantics: review a privacy-filtered action, approve, deny, or let it expire. The operations guide documents the trade-offs between TTY passphrases, passphrase files, command-line arguments, and environment variables instead of presenting them as equivalent.

The intended customer experience is constrained but not obscure: users can see what is being requested, why a policy decision happened, where evidence is stored, and how to export proof for offline verification.

Tool Catalog Growth Boundary

The product-route tool catalog and the policy/provider system are separate architecture layers:

  • A tool represents one executable action that an MCP client can discover and call.
  • Policy evaluates whether that action is relevant, allowed, approval-required, redirected, or rejected in the current context.
  • Optional paid providers may enrich the bounded decision, Approval Center projection, and evidence metadata. They do not publish plan-specific copies of existing MCP tools.

Adding Builder, Team, Enterprise, partner, role, risk, or approval variants must therefore not create variants such as builder_write_file, enterprise_write_file, or write_file_with_approval. Those variants use the existing write_file action and differ only in server-side evaluation and bounded review context.

PRODUCT_ROUTE_TOOL_CATALOG may grow only when AgentVeil supports a genuinely new executable action that cannot be represented by an existing tool. Such a change requires a dedicated catalog-change review covering the tool schema, pack ownership, policy, approval behavior, evidence, visibility, and catalog footprint. Policy/provider-only changes must leave the advertised catalog unchanged.

The committed product-route catalog contract is enforced by the MCP Proxy test suite in CI. Updating that snapshot is an explicit architecture change, not routine cleanup in a policy or provider slice. Schema compaction may proceed without changing the snapshot as long as tool names, order, and pack ownership remain stable.

This boundary preserves zero-config provider activation: installed providers are discovered from trusted local activation state and the generic provider contract. Customers do not select plan-specific MCP catalogs or configure provider-specific tool names.

Capability References

The proxy's capability-token framing aligns with Mark Miller's Robust Composition work on object-capability security and with Macaroons' contextual caveat model from Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud.

AVP does not implement macaroons directly. The comparison is architectural: authority is expressed as bounded, signed, context-specific evidence rather than as a broad ambient permission. The practical enforcement fields are AVP DecisionReceipt signatures, policy context hashes, payload hashes, risk classes, request IDs, expiry timestamps, local replay cache state, and evidence-chain records.

For operational details, see MCP_PROXY_OPERATIONS.md. For security context and known limitations, see the v0.1 release notes and the security sections in the operations guide.