Skip to main content

Trust badges for MCP tool calls - RFC-006 & RFC-007 implementation

Project description

CapiscIO MCP Guard

Tool-level security for Model Context Protocol servers.

PyPI version Python 3.10+ License

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 decorator
  • guard_sync(...) — Sync decorator
  • evaluate_tool_access(tool_name, params, credential, config) — Low-level API
  • compute_params_hash(params) — Deterministic parameter hashing
  • GuardConfig — Configuration dataclass
  • GuardResult — Evaluation result dataclass
  • GuardError — Exception for denied access

Server (RFC-007)

  • verify_server(server_did, server_badge, transport_origin, endpoint_path, config) — Async verification
  • verify_server_sync(...) — Sync verification
  • verify_server_strict(...) — Raises ServerVerifyError on any verification failure
  • parse_http_headers(headers) — Extract identity from HTTP headers
  • parse_jsonrpc_meta(meta) — Extract identity from MCP _meta
  • VerifyConfig — Configuration dataclass
  • VerifyResult — Verification result dataclass
  • ServerVerifyError — Exception for verification failures

Registration (Server Identity)

  • generate_server_keypair(key_id, output_dir) — Generate Ed25519 keypair
  • generate_server_keypair_sync(...) — Sync version
  • register_server_identity(server_id, api_key, did, public_key, ca_url) — Register DID with registry
  • register_server_identity_sync(...) — Sync version
  • setup_server_identity(server_id, api_key, ca_url, output_dir, key_id) — Combined setup
  • setup_server_identity_sync(...) — Sync version
  • RegistrationError — Exception for registration failures
  • KeyGenerationError — Exception for key generation failures

Types

  • Decision — ALLOW / DENY
  • AuthLevel — ANONYMOUS / API_KEY / BADGE
  • DenyReason — Enumeration of denial reasons
  • TrustLevel — Trust levels 0-4 per RFC-002
  • ServerState — VERIFIED_PRINCIPAL / DECLARED_PRINCIPAL / UNVERIFIED_ORIGIN
  • ServerErrorCode — Enumeration of verification error codes

MCP SDK Integration (optional)

Requires pip install capiscio-mcp[mcp]:

  • CapiscioMCPServer(name, did, badge, ...) — FastMCP wrapper with trust enforcement
  • CapiscioMCPServer.tool(min_trust_level=...) — Decorator for guarded tools
  • CapiscioMCPServer.run(transport="stdio") — Run the server
  • CapiscioMCPClient(command, args, ...) — Client for stdio transport*
  • CapiscioMCPClient.call_tool(name, args) — Call a tool on the server
  • CapiscioMCPClient.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

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

capiscio_mcp-2.4.0.tar.gz (100.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

capiscio_mcp-2.4.0-py3-none-any.whl (89.5 kB view details)

Uploaded Python 3

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

Hashes for capiscio_mcp-2.4.0.tar.gz
Algorithm Hash digest
SHA256 8ed5cf4e702b569a893294e2adeeadee637d33c12da9405df212bc4cad29ab5c
MD5 3e06ec7e43dfb016f6da39c7c1e56409
BLAKE2b-256 852e66de7f4c66bb8be6611c10735b021fd129a00366a0f04b6778a142bad8bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for capiscio_mcp-2.4.0.tar.gz:

Publisher: publish.yml on capiscio/capiscio-mcp-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for capiscio_mcp-2.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b4c7a494790284837694c7cba211d04105882edcd8cb298d26f7da158a6a439
MD5 6308c5418c77de4a0334750ba230749c
BLAKE2b-256 89dfec49afdf332f2a0cdd124e036b429c7ef68e96ee0108056a528f406dc7f7

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page