Trust badges for MCP tool calls - RFC-006 & RFC-007 implementation
Project description
CapiscIO MCP Guard
Tool-level security for Model Context Protocol servers.
MCP Guard 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) |
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
server = CapiscioMCPServer(
name="filesystem",
did="did:web:mcp.example.com:servers:filesystem",
badge="eyJhbGc...", # From CapiscIO registry
)
@server.tool(min_trust_level=2)
async def read_file(path: str) -> str:
"""Only agents with Trust Level 2+ can read files."""
with open(path) as f:
return f.read()
@server.tool(min_trust_level=0)
async def list_files(directory: str) -> list[str]:
"""Any authenticated agent can list files."""
import os
return os.listdir(directory)
# 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)
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_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(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
License
Apache License 2.0
Contributing
See CONTRIBUTING.md for guidelines.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capiscio_mcp-2.4.0.tar.gz.
File metadata
- Download URL: capiscio_mcp-2.4.0.tar.gz
- Upload date:
- Size: 100.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ed5cf4e702b569a893294e2adeeadee637d33c12da9405df212bc4cad29ab5c
|
|
| MD5 |
3e06ec7e43dfb016f6da39c7c1e56409
|
|
| BLAKE2b-256 |
852e66de7f4c66bb8be6611c10735b021fd129a00366a0f04b6778a142bad8bf
|
Provenance
The following attestation bundles were made for capiscio_mcp-2.4.0.tar.gz:
Publisher:
publish.yml on capiscio/capiscio-mcp-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capiscio_mcp-2.4.0.tar.gz -
Subject digest:
8ed5cf4e702b569a893294e2adeeadee637d33c12da9405df212bc4cad29ab5c - Sigstore transparency entry: 912015829
- Sigstore integration time:
-
Permalink:
capiscio/capiscio-mcp-python@8c3d26d457fe446172332ebd0d1cce4aeceab2f9 -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/capiscio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c3d26d457fe446172332ebd0d1cce4aeceab2f9 -
Trigger Event:
release
-
Statement type:
File details
Details for the file capiscio_mcp-2.4.0-py3-none-any.whl.
File metadata
- Download URL: capiscio_mcp-2.4.0-py3-none-any.whl
- Upload date:
- Size: 89.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b4c7a494790284837694c7cba211d04105882edcd8cb298d26f7da158a6a439
|
|
| MD5 |
6308c5418c77de4a0334750ba230749c
|
|
| BLAKE2b-256 |
89dfec49afdf332f2a0cdd124e036b429c7ef68e96ee0108056a528f406dc7f7
|
Provenance
The following attestation bundles were made for capiscio_mcp-2.4.0-py3-none-any.whl:
Publisher:
publish.yml on capiscio/capiscio-mcp-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capiscio_mcp-2.4.0-py3-none-any.whl -
Subject digest:
8b4c7a494790284837694c7cba211d04105882edcd8cb298d26f7da158a6a439 - Sigstore transparency entry: 912015918
- Sigstore integration time:
-
Permalink:
capiscio/capiscio-mcp-python@8c3d26d457fe446172332ebd0d1cce4aeceab2f9 -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/capiscio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c3d26d457fe446172332ebd0d1cce4aeceab2f9 -
Trigger Event:
release
-
Statement type: