Skip to content

ScopeBlind/protect-mcp-adk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License: MIT IETF Draft PyPI

protect-mcp-adk

Ed25519 receipt signing plugin for Google ADK agents. Every tool call produces a cryptographic receipt following the IETF Internet-Draft format.

Install

pip install protect-mcp-adk

Usage

from google.adk import Agent
from google.adk.tools import FunctionTool
from protect_mcp_adk import ReceiptPlugin, ReceiptSigner

# Generate signing keys (or load from file)
signer = ReceiptSigner.generate()

# Add the plugin to your agent
agent = Agent(
    model="gemini-2.0-flash",
    tools=[FunctionTool(my_tool)],
    plugins=[ReceiptPlugin(signer, auto_export_path="receipts.jsonl")],
)

# After execution, verify the receipt chain:
# npx @veritasacta/verify@0.2.5 receipts.jsonl --key <public-key-hex>

What It Does

Every tool call your ADK agent makes produces a signed receipt:

{
  "payload": {
    "type": "protectmcp:decision",
    "spec": "draft-farley-acta-signed-receipts-01",
    "tool_name": "search",
    "tool_input_hash": "sha256:ff7e27...",
    "decision": "allow",
    "output_hash": "sha256:a3f8c9...",
    "issued_at": "2026-04-06T21:30:00.000Z",
    "session_id": "sess_a1b2c3d4e5f6",
    "sequence": 1,
    "previousReceiptHash": null,
    "agent_name": "research_agent",
    "invocation_id": "inv_xyz"
  },
  "signature": {
    "alg": "EdDSA",
    "kid": "sb:adk:de073ae64e43",
    "sig": "3da3162da83e..."
  }
}

Privacy-preserving: Tool inputs and outputs are SHA-256 hashed, not stored raw. The receipt proves what happened without exposing what was said.

Chain-linked: Each receipt includes previousReceiptHash, creating a tamper-evident chain. If any receipt is modified or removed, the chain breaks.

Offline verifiable: Receipts verify without contacting any server:

npx @veritasacta/verify@0.2.5 receipts.jsonl --key <public-key-hex>
# Exit 0 = valid, 1 = invalid (tampered), 2 = error (malformed)

API

ReceiptSigner

# Generate a new keypair
signer = ReceiptSigner.generate()

# Load from a key file
signer = ReceiptSigner.from_key_file("keys/agent.json")

# Save keys for reuse
signer.save_key("keys/agent.json")

# Access public key (for verification)
print(signer.public_key_hex)
print(signer.kid)

ReceiptPlugin

plugin = ReceiptPlugin(
    signer,
    auto_export_path="receipts.jsonl",  # Auto-export as receipts are produced
    log_receipts=True,                   # Log each receipt to Python logger
)

# After execution
print(plugin.receipt_count)
plugin.export_receipts("audit-bundle.jsonl")
print(plugin.get_verification_command())

Plugin Callbacks

Callback When What it signs
after_tool_callback After every tool execution Tool name, input hash, output hash, decision
on_tool_error_callback On tool execution error Tool name, input hash, error reason
before_tool_callback Before tool execution Override for policy evaluation (Cedar)

Interoperability

Receipts produced by this plugin verify against the same tooling as:

Three independent implementations have been verified to produce interoperable receipts.

Algorithm Note

This plugin uses Ed25519 (RFC 8032) for signing. The receipt envelope's signature.alg field supports algorithm negotiation. For environments requiring post-quantum signatures (NIST FIPS 204), the same envelope format works with ML-DSA-65 by changing the alg field. See the IETF draft for algorithm agility details.

Related

About

Ed25519 receipt signing plugin for Google ADK agents. BasePlugin with after_tool_callback for cryptographic decision receipts.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages