MCP Guard
Tool-level trust enforcement for Model Context Protocol servers.
MCP Guard (pip install capiscio-mcp) provides trust badges and identity verification for Model Context Protocol (MCP) tool calls. It implements:
- RFC-006: MCP Tool Authority and Evidence
- RFC-007: MCP Server Identity Disclosure and Verification
Installation
pip install capiscio-mcp
For MCP SDK integration (FastMCP wrapper):
pip install capiscio-mcp[mcp]
Why MCP Guard?
MCP servers expose powerful tools to autonomous agents—file systems, databases, APIs. But MCP itself doesn't define how to:
- Authenticate which agent is calling a tool
- Authorize whether that agent should have access
- Audit what happened for post-incident review
MCP Guard solves this with:
| Feature | Description |
|---|---|
| @guard decorator | Protect tools with trust-level requirements |
| Evidence logging | Cryptographic audit trail for every invocation |
| Server identity | Verify MCP servers before connecting |
| Server registration | Generate keypairs and register server DIDs |
| Trust levels | 0 (self-signed) → 4 (extended validation) |
Quickstarts
Building an MCP server? Start with Quickstart 1. Connecting to MCP servers? Start with Quickstart 2. Registering a server identity? Start with Quickstart 3.
Quickstart 1: Server-Side (Tool Guarding)
Protect your MCP tools with trust-level requirements:
from capiscio_mcp import guard
@guard(min_trust_level=2)
async def read_database(query: str) -> list[dict]:
"""Only agents with Trust Level 2+ can execute this tool."""
# ... database query logic
pass
# Sync version available
from capiscio_mcp import guard_sync
@guard_sync(min_trust_level=2)
def read_database_sync(query: str) -> list[dict]:
pass
With Full Configuration
from capiscio_mcp import guard, GuardConfig
config = GuardConfig(
min_trust_level=2,
trusted_issuers=["did:web:registry.capisc.io"],
allowed_tools=["read_*", "list_*"],
require_badge=True, # Deny anonymous access
)
@guard(config=config)
async def execute_query(sql: str) -> list[dict]:
pass
Quickstart 2: Client-Side (Server Verification)
Verify the identity of MCP servers you connect to:
from capiscio_mcp import verify_server, ServerState
result = await verify_server(
server_did="did:web:mcp.example.com",
server_badge="eyJhbGc...",
transport_origin="https://mcp.example.com",
)
if result.state == ServerState.VERIFIED_PRINCIPAL:
print(f"Trusted server at Level {result.trust_level}")
elif result.state == ServerState.DECLARED_PRINCIPAL:
print("Server identity declared but not verified")
elif result.state == ServerState.UNVERIFIED_ORIGIN:
print("Warning: Server did not disclose identity")
Quickstart 3: Server Registration
Register your MCP server's identity with the CapiscIO registry:
from capiscio_mcp import setup_server_identity
# One-step setup: generate keys + register with registry
result = await setup_server_identity(
server_id="550e8400-e29b-41d4-a716-446655440000", # From dashboard
api_key="sk_live_...", # Registry API key
ca_url="https://registry.capisc.io", # Optional, defaults to production
output_dir="./keys",
)
print(f"Server DID: {result['did']}")
# did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
print(f"Private key saved to: {result['private_key_path']}")
Step-by-Step Registration
from capiscio_mcp import generate_server_keypair, register_server_identity
# Step 1: Generate keypair
keys = await generate_server_keypair(output_dir="./keys")
# Step 2: Register with registry
await register_server_identity(
server_id="550e8400-e29b-41d4-a716-446655440000",
api_key="sk_live_...",
did=keys["did_key"],
public_key=keys["public_key_pem"],
ca_url="https://registry.capisc.io", # Optional, defaults to production
)
MCP SDK Integration
For seamless integration with the official MCP Python SDK, install with the mcp extra:
pip install capiscio-mcp[mcp]
Server with FastMCP Wrapper
Create an MCP server with built-in trust enforcement:
from capiscio_mcp.integrations.mcp import CapiscioMCPServer
# db is your application's database connection (asyncpg, databases, etc.)
db = ... # e.g. databases.Database("postgresql://...")
server = CapiscioMCPServer.connect()
@server.tool(min_trust_level=2)
async def get_user(user_id: int) -> dict:
"""Only agents with Trust Level 2+ can read user data."""
return await db.fetch_one("SELECT * FROM users WHERE id = $1", user_id)
@server.tool(min_trust_level=1)
async def list_tables() -> list[str]:
"""Agents with a valid badge (Trust Level 1+) can list tables."""
return await db.get_table_names()
# Run the server (stdio transport)
server.run()
Client with Trust Verification
Connect to MCP servers via stdio transport:
from capiscio_mcp.integrations.mcp import CapiscioMCPClient
async with CapiscioMCPClient(
command="python",
args=["my_mcp_server.py"],
min_trust_level=1,
badge="eyJhbGc...", # Your client badge
) as client:
# List available tools
tools = await client.list_tools()
print(f"Available tools: {[t['name'] for t in tools]}")
# Call a tool
result = await client.call_tool("read_file", {"path": "/data/config.json"})
print(result)
CapiscioMCPServer.connect() — “Let's Encrypt” Style Setup
Register your MCP server and get a badge with a single call:
from capiscio_mcp.integrations.mcp import CapiscioMCPServer
server = CapiscioMCPServer.connect()
print(server.did) # did:web:registry.capisc.io:servers:550e8400-...
print(server.badge) # Current badge JWS (auto-issued)
Using Environment Variables
server = CapiscioMCPServer.connect()
| Variable | Required | Description |
|---|---|---|
CAPISCIO_SERVER_ID |
Yes | Server UUID from dashboard |
CAPISCIO_API_KEY |
Yes | Registry API key |
CAPISCIO_SERVER_URL |
No | Registry URL (default: production) |
CAPISCIO_SERVER_DOMAIN |
No | Domain for badge issuance |
CAPISCIO_SERVER_PRIVATE_KEY_PEM |
No | PEM-encoded Ed25519 private key for ephemeral environments |
Deploying to Containers / Serverless
In ephemeral environments (Docker, Lambda, Cloud Run) the local ~/.capiscio/ directory
doesn't survive restarts. On first run the SDK generates a keypair and logs a capture hint:
╔══════════════════════════════════════════════════════════╗
║ New server identity generated — save key for persistence ║
╚══════════════════════════════════════════════════════════╝
Add to your secrets manager / .env:
CAPISCIO_SERVER_PRIVATE_KEY_PEM='-----BEGIN PRIVATE KEY-----\nMC4C...\n-----END PRIVATE KEY-----\n'
Copy that value into your secrets manager and set it as an environment variable. On subsequent starts the SDK will recover the same DID without generating a new identity.
Key resolution priority: env var → local file → generate new.
# docker-compose.yml
services:
mcp-server:
environment:
CAPISCIO_SERVER_ID: "550e8400-..."
CAPISCIO_API_KEY: "sk_live_..."
CAPISCIO_SERVER_PRIVATE_KEY_PEM: "${MCP_SERVER_KEY}" # from secrets
See the Deployment Guide for full examples.
Core Connection Modes
MCP Guard connects to capiscio-core for cryptographic operations:
Embedded Mode (Default)
SDK automatically downloads and manages the core binary:
pip install capiscio-mcp
# Just works! Binary downloaded on first use.
External Mode
Connect to a separately managed core service:
# Start core in another terminal
capiscio mcp serve --listen localhost:50051
# SDK connects to external core
export CAPISCIO_CORE_ADDR="localhost:50051"
Trust Levels
Per RFC-002 v1.4:
| Level | Name | Validation | Use Case |
|---|---|---|---|
| 0 | Self-Signed (SS) | None, did:key issuer |
Local dev, testing, demos |
| 1 | Registered (REG) | Account registration | Development, internal agents |
| 2 | Domain Validated (DV) | DNS/HTTP challenge | Production, B2B agents |
| 3 | Organization Validated (OV) | DUNS/legal entity | High-trust production |
| 4 | Extended Validated (EV) | Manual review + legal | Regulated industries |
Evidence Logging
Every tool invocation—allowed or denied—produces an evidence record:
from capiscio_mcp import guard, GuardError
@guard(min_trust_level=2)
async def sensitive_operation(data: dict) -> dict:
pass
try:
result = await sensitive_operation(data={"key": "value"})
except GuardError as e:
# Evidence logged even on denial
print(f"Denied: {e.reason}")
print(f"Evidence ID: {e.evidence_id}") # For audit trail
Evidence includes:
- Tool name and parameters hash (not raw params—PII safe)
- Caller identity (agent DID, badge JTI, auth level)
- Decision and reason
- Timestamp and unique evidence ID
Configuration Reference
GuardConfig
from capiscio_mcp import GuardConfig
config = GuardConfig(
min_trust_level=2, # Minimum trust level (0-4)
accept_level_zero=False, # Accept self-signed badges?
trusted_issuers=[ # List of trusted issuer DIDs
"did:web:registry.capisc.io",
],
allowed_tools=[ # Glob patterns for allowed tools
"read_*",
"list_*",
],
require_badge=True, # Deny anonymous/API key access
policy_version="v1.0", # Policy version for tracking
)
VerifyConfig
from capiscio_mcp import VerifyConfig
config = VerifyConfig(
trusted_issuers=[...], # Trusted issuer DIDs
min_trust_level=2, # Minimum required level
accept_level_zero=False, # Accept self-signed servers?
offline_mode=False, # Skip revocation checks?
skip_origin_binding=False, # Skip host/path binding?
)
Environment Variables
| Variable | Description | Default |
|---|---|---|
CAPISCIO_SERVER_ID |
Server UUID (for MCPServerIdentity) |
— |
CAPISCIO_API_KEY |
Registry API key (for MCPServerIdentity) |
— |
CAPISCIO_SERVER_URL |
Registry server URL | https://registry.capisc.io |
CAPISCIO_SERVER_DOMAIN |
Domain for badge issuance | (derived from server URL) |
CAPISCIO_SERVER_PRIVATE_KEY_PEM |
PEM-encoded Ed25519 private key (ephemeral envs) | — |
CAPISCIO_CORE_ADDR |
External core address | (embedded mode) |
CAPISCIO_SERVER_ORIGIN |
Server origin for guard | (auto-detect) |
CAPISCIO_LOG_LEVEL |
Logging verbosity | info |
API Reference
Guard (RFC-006)
guard(config=None, min_trust_level=None, tool_name=None)— Async decoratorguard_sync(...)— Sync decoratorevaluate_tool_access(tool_name, params, credential, config)— Low-level APIcompute_params_hash(params)— Deterministic parameter hashingGuardConfig— Configuration dataclassGuardResult— Evaluation result dataclassGuardError— Exception for denied access
Server (RFC-007)
verify_server(server_did, server_badge, transport_origin, endpoint_path, config)— Async verificationverify_server_sync(...)— Sync verificationverify_server_strict(...)— Raises ServerVerifyError on any verification failureparse_http_headers(headers)— Extract identity from HTTP headersparse_jsonrpc_meta(meta)— Extract identity from MCP _metaVerifyConfig— Configuration dataclassVerifyResult— Verification result dataclassServerVerifyError— Exception for verification failures
Registration (Server Identity)
generate_server_keypair(key_id, output_dir)— Generate Ed25519 keypairgenerate_server_keypair_sync(...)— Sync versionregister_server_identity(server_id, api_key, did, public_key, ca_url)— Register DID with registryregister_server_identity_sync(...)— Sync versionsetup_server_identity(server_id, api_key, ca_url, output_dir, key_id)— Combined setupsetup_server_identity_sync(...)— Sync versionRegistrationError— Exception for registration failuresKeyGenerationError— Exception for key generation failures
Types
Decision— ALLOW / DENYAuthLevel— ANONYMOUS / API_KEY / BADGEDenyReason— Enumeration of denial reasonsTrustLevel— Trust levels 0-4 per RFC-002ServerState— VERIFIED_PRINCIPAL / DECLARED_PRINCIPAL / UNVERIFIED_ORIGINServerErrorCode— Enumeration of verification error codes
MCP SDK Integration (optional)
Requires pip install capiscio-mcp[mcp]:
CapiscioMCPServer.connect()— One-liner: load identity from env and create serverCapiscioMCPServer(name, did, badge, ...)— FastMCP wrapper with trust enforcementCapiscioMCPServer.tool(min_trust_level=...)— Decorator for guarded toolsCapiscioMCPServer.run(transport="stdio")— Run the serverCapiscioMCPClient(command, args, ...)— Client for stdio transport*CapiscioMCPClient.call_tool(name, args)— Call a tool on the serverCapiscioMCPClient.list_tools()— List available tools
*Note: Server identity verification in CapiscioMCPClient requires MCP SDK support for _meta passthrough in initialize responses. This is not yet available, so min_trust_level and fail_on_unverified parameters are currently not enforced. Server-side trust enforcement via @server.tool(min_trust_level=...) works fully.
Documentation
- RFC-006: MCP Tool Authority and Evidence
- RFC-007: MCP Server Identity Disclosure
- Server Registration Guide
- Server-Side Guide
- Client-Side Guide
- Evidence Logging Guide
Development
# Clone repository
git clone https://github.com/capiscio/capiscio-mcp-python.git
cd capiscio-mcp-python
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest -v
# Run tests with coverage
pytest --cov=capiscio_mcp --cov-report=html
# Type checking
mypy capiscio_mcp
# Linting
ruff check capiscio_mcp
Related Packages
| Package | What it does | Install |
|---|---|---|
| Agent Guard | Runtime trust verification for A2A agents | pip install capiscio-sdk |
| CapiscIO CLI | Agent validation for CI/CD pipelines | pip install capiscio |
| capiscio-core | Go library, CLI binary, and gateway | Install guide |
Documentation · Website · Platform
License
Apache License 2.0
Contributing
See CONTRIBUTING.md for guidelines.
Metadata
Release files for capiscio-mcp 2.7.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| capiscio_mcp-2.7.1.tar.gz | 144.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| capiscio_mcp-2.7.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 263.6 kB
Release files / capiscio_mcp-2.7.1.tar.gz
| Download URL | capiscio_mcp-2.7.1.tar.gz |
|---|---|
| Size | 144.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
073fd511b0b64c9387a9a9389cc5da607ed9f93adb6c15a8cfd47e37a872bcae
|
|
BLAKE2b-256 checksum How to use checksums |
e4778750e62e9f3897c6d4c0c45c55efa00af98bbf86013abab0e6688a631760
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency logRelease files / capiscio_mcp-2.7.1-py3-none-any.whl
| Download URL | capiscio_mcp-2.7.1-py3-none-any.whl |
|---|---|
| Size | 119.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dd1e7620568f62f07334338aa914680006da0b750505fee4f3b28b697f10f7cd
|
|
BLAKE2b-256 checksum How to use checksums |
8396561ad470297f0e96f7b5c1325d42eb10cb1ad4763b020f9757c3fb23a417
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency log