Inspiration

MCP is turning AI systems from assistants that answer questions into agents that can actually take actions.

An agent can now be connected to email, internal documents, repositories, databases, financial systems, and almost any other service exposed through an MCP tool.

That creates a different authorization problem.

Traditional access control can answer:

Can this application access this service?

But for autonomous agents, we increasingly need to answer:

Is this agent authorized to perform this specific action, on this specific resource, with these parameters, under the user's rules?

Giving an agent access to a tool should not mean giving it unrestricted authority over everything that tool can do.

A company might allow an agent to read documents from one assigned matter but not every client's files. An agent may be allowed to send internal email while external email requires human approval. A financial agent may be allowed to transfer money only below a private spending limit.

Existing policy engines can enforce rules like these, but they introduce another trusted system: the user still has to trust that the gateway evaluated the correct policy, and sensitive limits or internal policy rules may need to be disclosed to the verifier.

That led to zkMCP:

What if an AI agent had to cryptographically prove that an action was authorized before the MCP tool was allowed to execute it?

And what if it could do that without publishing the private policy used to make the decision?


What it does

zkMCP is a zero-knowledge authorization gateway for AI agents and MCP tools.

It sits directly between an MCP client and an existing MCP server.

When the agent sends a sensitive tools/call, zkMCP:

  1. intercepts the request before the upstream tool runs,
  2. normalizes the arbitrary MCP arguments into deterministic authorization facts,
  3. resolves trusted context such as human approval separately from agent-controlled arguments,
  4. submits the authorization statement to Midnight,
  5. evaluates the private policy inside a Compact contract,
  6. generates and verifies the zero-knowledge proof,
  7. finalizes the authorization transaction,
  8. and only then forwards an authorized request to the upstream MCP handler.

If authorization fails, the upstream tool is never invoked.

We implemented three concrete policy primitives.

Private resource membership

documents.read proves that the requested matter belongs to the agent's privately authorized resource scope.

An assigned matter succeeds.

An unrelated matter is blocked before the document tool executes.

Trusted human approval

email.send requires approval derived from trusted MCP metadata.

The AI cannot authorize itself by placing something like approved: true inside its own tool arguments.

Without trusted approval, the external email is blocked.

With trusted approval, authorization can proceed.

Private numeric limits

payments.transfer uses a private maximum amount and a private approval threshold.

In our demo policy:

  • lower-value transfers can execute directly,
  • higher-value transfers require approval,
  • and transfers above the private hard maximum are denied even when approval is present.

The verifier receives evidence that the rule passed without requiring the private limit itself to become a public ledger field.


Proof receipts

Successful MCP responses include a zkMCP authorization receipt in MCP result metadata.

The receipt contains public verification material such as:

  • policy commitment
  • execution commitment
  • nullifier
  • Midnight transaction ID
  • block height
  • contract address
  • network
  • authorization/proof duration

The private policy, raw tool arguments, policy secret, approval credentials, and other sensitive witness data are not included in the public receipt.

Denied requests return a deliberately generic authorization error rather than revealing which private policy condition failed.


How we built it

zkMCP is implemented as a TypeScript monorepo with three main infrastructure packages and a developer documentation application.

@zkmcp/core contains the typed error taxonomy, privacy-safe metadata model, and local structured logging.

@zkmcp/gateway implements the MCP authorization gateway using the official Model Context Protocol TypeScript SDK. It proxies normal MCP discovery while intercepting sensitive tools/call requests before upstream execution.

@zkmcp/midnight contains the Midnight authorization client, wallet and provider integration, private-state handling, commitment construction, transaction lifecycle, and the Compact authorization contract.

The Compact contract commits to a private policy containing the authorized agent, supported tool identifiers, allowed resource, payment maximum, approval threshold, and policy secret.

The circuit enforces common constraints such as:

committed policy matches AND agent matches AND tool is known AND nullifier is fresh

and then applies tool-specific constraints for resource membership, approval, or numeric limits.

We deliberately do not try to prove arbitrary LLM reasoning.

The AI model remains probabilistic.

The security boundary around the action is deterministic and independently verifiable.


MCP execution boundary

The key security property of zkMCP is ordering.

The flow is:

AI Agent → MCP tools/call → zkMCP → Midnight / Compact → authorization → upstream MCP tool

There is no direct route from the agent to the protected upstream handler.

Our tests explicitly track upstream execution counts and verify that denied requests never call the tool.

Successful tool results receive the proof receipt through MCP _meta.


Developer portal

We built a complete documentation-first developer portal using Fumadocs.

It includes:

  • getting started and quickstart documentation,
  • deep architecture documentation,
  • request lifecycle and trust-boundary documentation,
  • Compact circuit constraints,
  • private witness and public ledger explanations,
  • MCP integration guides,
  • an interactive authorization playground,
  • security and threat-model documentation,
  • exact receipt and error schemas,
  • implementation limitations,
  • testing and troubleshooting guides,
  • zoomable diagrams-as-code,
  • and a Scalar OpenAPI reference for the local playground HTTP bridge.

The public site runs in a self-contained recorded-proof mode using receipts captured from our final verified Midnight run.

For the demo video, the same hosted playground is connected to the local Midnight proving stack and generates fresh proof-backed authorization transactions live.


Challenges we ran into

The hardest question was deciding what should actually be proven.

Trying to prove that an LLM "reasoned correctly" would make zkMCP both impractical and conceptually confused.

Instead, we separated probabilistic reasoning from deterministic authorization.

The model proposes an action.

zkMCP proves whether that action satisfies the committed policy.

Another major challenge was translating flexible MCP JSON arguments into statements that make sense inside a zero-knowledge circuit.

MCP tools can accept arbitrary structures, while Compact needs a deterministic authorization envelope. We built tool-specific normalization that maps an MCP request into constrained facts such as agent identity, action class, resource scope, amount, approval state, and nonce.

Human approval created another important boundary. Approval cannot be trusted if the AI itself can provide it as an argument, so trusted approval context is resolved separately by the gateway before becoming a private circuit input.

Privacy-safe observability was also difficult. A useful authorization system needs logs and errors, but leaking "payment exceeded private maximum" would itself reveal policy information. zkMCP therefore collapses private policy failures into a generic denial and only logs explicitly allowlisted public receipt fields.

We also encountered several practical Midnight integration issues during development, including local wallet state surviving devnet resets, duplicate runtime package instances, and MCP transport negotiation behavior. These became documented troubleshooting cases rather than hidden implementation problems.


Accomplishments that we're proud of

The final system is not a simulated blockchain UI.

We implemented and verified a real execution path:

MCP client → zkMCP gateway → Compact authorization → Midnight proof generation and verification → authorization transaction → upstream MCP execution

Our final end-to-end verification suite passed 8/8 scenarios:

  • assigned document access authorized
  • unrelated document access denied
  • external email without approval denied
  • approved external email authorized
  • payment below private threshold authorized
  • payment requiring approval denied without approval
  • payment requiring approval authorized with trusted approval
  • payment above the private hard maximum denied even with approval

Authorized actions generated real proofs and finalized Midnight transactions.

Denied actions stopped before the upstream MCP handler.

We also independently verified that sensitive policy and action values were absent from our structured authorization logs.

Most importantly, zkMCP demonstrates a use of zero knowledge that feels native to the emerging agent ecosystem:

zero knowledge as an authorization primitive for autonomous actions.


What we learned

The biggest thing we learned is that access, intent, and authority are different concepts.

Access means an application can reach a service.

Intent means an AI model wants to perform an action.

Authority means that action is actually permitted.

Current agent infrastructure often collapses these concepts together.

As agents become more autonomous, that becomes increasingly dangerous.

We also learned that zero-knowledge proofs are especially useful around AI systems when they prove small, deterministic security statements instead of trying to prove the entire probabilistic computation.

The AI can remain flexible.

The authorization boundary does not have to be.


Current limitations

zkMCP is a hackathon prototype, and we document the current boundaries explicitly.

The fully verified end-to-end deployment currently runs on Midnight's local undeployed development environment rather than Preprod.

The current demo uses a single committed policy configuration.

The human-approval adapter uses a fixed demo credential rather than a production signed, scoped, expiring capability.

Some semantic tool fields are intentionally outside the current proof statement. For example, the payment amount is constrained by the circuit, but additional semantics such as currency or recipient class would need to be added to the authorization envelope for a production financial policy.

The workspace packages have not yet been published to npm.


What's next for zkMCP

The next step is turning zkMCP from a working prototype into a reusable authorization protocol for MCP infrastructure.

We want to add:

  • a developer-friendly policy DSL
  • reusable policy templates
  • arbitrary private resource sets
  • richer numeric and contextual constraints
  • signed and expiring human approval capabilities
  • multi-party approval
  • policy rotation and revocation
  • delegated agent capabilities
  • stronger execution commitments that bind additional tool semantics
  • Midnight Preview/Preprod deployment
  • remote proving infrastructure
  • published TypeScript packages
  • integrations with real production MCP servers

Long term, zkMCP could become a cryptographic capability layer for autonomous agents.

OAuth can establish that an application has access.

zkMCP is designed to prove that an agent had authority for the individual action.

AI agents shouldn't just claim that they followed the rules. They should be able to prove it.

Built With

Share this project:

Updates

Submission history