Metadata-Version: 2.4
Name: capiscio-mcp
Version: 2.7.0
Summary: Trust badges for MCP tool calls - RFC-006 & RFC-007 implementation
Project-URL: Homepage, https://capisc.io
Project-URL: Documentation, https://docs.capisc.io/mcp
Project-URL: Repository, https://github.com/capiscio/capiscio-mcp-python
Project-URL: Issues, https://github.com/capiscio/capiscio-mcp-python/issues
Author-email: CapiscIO Team <engineering@capisc.io>
License: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,badges,mcp,model-context-protocol,security,trust
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: base58>=2.1.0
Requires-Dist: cryptography>=42.0.0
Requires-Dist: grpcio>=1.60.0
Requires-Dist: protobuf>=4.25.0
Requires-Dist: requests>=2.31.0
Provides-Extra: dev
Requires-Dist: black>=24.0.0; extra == 'dev'
Requires-Dist: grpcio-tools>=1.60.0; extra == 'dev'
Requires-Dist: mypy>=1.9.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.3.0; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# MCP Guard

Tool-level trust enforcement for Model Context Protocol servers.

[![PyPI version](https://badge.fury.io/py/capiscio-mcp.svg)](https://badge.fury.io/py/capiscio-mcp)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

**MCP Guard** (`pip install capiscio-mcp`) provides trust badges and identity verification for [Model Context Protocol (MCP)](https://modelcontextprotocol.io) tool calls. It implements:

- **RFC-006**: MCP Tool Authority and Evidence
- **RFC-007**: MCP Server Identity Disclosure and Verification

## Installation

```bash
pip install capiscio-mcp
```

For MCP SDK integration (FastMCP wrapper):

```bash
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](#quickstart-1-server-side-tool-guarding).
> **Connecting to MCP servers?** Start with [Quickstart 2](#quickstart-2-client-side-server-verification).
> **Registering a server identity?** Start with [Quickstart 3](#quickstart-3-server-registration).

## Quickstart 1: Server-Side (Tool Guarding)

Protect your MCP tools with trust-level requirements:

```python
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

```python
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:

```python
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:

```python
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

```python
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](https://github.com/modelcontextprotocol/python-sdk), install with the `mcp` extra:

```bash
pip install capiscio-mcp[mcp]
```

### Server with FastMCP Wrapper

Create an MCP server with built-in trust enforcement:

```python
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:

```python
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:

```python
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

```python
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.

```yaml
# 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](https://docs.capisc.io/mcp-guard/guides/deployment/) 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:

```bash
pip install capiscio-mcp
# Just works! Binary downloaded on first use.
```

### External Mode

Connect to a separately managed core service:

```bash
# 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:

```python
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

```python
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

```python
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 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.connect()` — One-liner: load identity from env and create server
- `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

- [RFC-006: MCP Tool Authority and Evidence](https://docs.capisc.io/rfcs/006)
- [RFC-007: MCP Server Identity Disclosure](https://docs.capisc.io/rfcs/007)
- [Server Registration Guide](https://docs.capisc.io/mcp-guard/guides/server-registration)
- [Server-Side Guide](https://docs.capisc.io/mcp-guard/guides/server-side)
- [Client-Side Guide](https://docs.capisc.io/mcp-guard/guides/client-side)
- [Evidence Logging Guide](https://docs.capisc.io/mcp-guard/guides/evidence)

## Development

```bash
# 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](https://github.com/capiscio/capiscio-sdk-python) | Runtime trust verification for A2A agents | `pip install capiscio-sdk` |
| [CapiscIO CLI](https://github.com/capiscio/capiscio-python) | Agent validation for CI/CD pipelines | `pip install capiscio` |
| [capiscio-core](https://github.com/capiscio/capiscio-core) | Go library, CLI binary, and gateway | [Install guide](https://github.com/capiscio/capiscio-core#install) |

[Documentation](https://docs.capisc.io) · [Website](https://capisc.io) · [Platform](https://app.capisc.io)

## License

Apache License 2.0

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
