Skip to content

Client-Side: Verifying MCP Servers

This guide covers verifying MCP server identity per RFC-007.

Basic Usage

from capiscio_mcp import verify_server, ServerState

result = await verify_server(
    server_did="did:web:mcp.example.com:servers:filesystem",
    server_badge="eyJhbGc...",
    transport_origin="https://mcp.example.com",
)

if result.state == ServerState.VERIFIED_PRINCIPAL:
    print(f"✅ Server verified at trust level {result.trust_level}")
elif result.state == ServerState.DECLARED_PRINCIPAL:
    print("⚠️ Identity declared but verification failed")
    print(f"   Error: {result.error_detail}")
elif result.state == ServerState.UNVERIFIED_ORIGIN:
    print("❌ Server did not disclose any identity")

VerifyConfig Options

from capiscio_mcp import verify_server, VerifyConfig

config = VerifyConfig(
    # List of trusted issuer DIDs
    trusted_issuers=[
        "did:web:registry.capisc.io",
    ],

    # Minimum trust level required (0-4)
    min_trust_level=2,

    # Accept self-signed (did:key) servers?
    accept_level_zero=False,

    # Skip revocation checks (offline mode)?
    offline_mode=False,

    # Skip host/path binding checks (for trusted gateways)
    skip_origin_binding=False,
)

result = await verify_server(
    server_did="did:web:mcp.example.com",
    server_badge="eyJhbGc...",
    transport_origin="https://mcp.example.com",
    config=config,
)

Extracting Identity from Transport

From HTTP Headers

from capiscio_mcp import parse_http_headers, verify_server

# Extract from HTTP response
headers = {
    "Capiscio-Server-DID": "did:web:mcp.example.com",
    "Capiscio-Server-Badge": "eyJhbGc...",
}

identity = parse_http_headers(headers)

if identity.has_identity:
    result = await verify_server(
        server_did=identity.server_did,
        server_badge=identity.server_badge,
        transport_origin="https://mcp.example.com",
    )

From JSON-RPC _meta

from capiscio_mcp import parse_jsonrpc_meta, verify_server

# Extract from MCP JSON-RPC response
response = {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {...},
    "_meta": {
        "serverDid": "did:web:mcp.example.com",
        "serverBadge": "eyJhbGc...",
    }
}

identity = parse_jsonrpc_meta(response.get("_meta", {}))

if identity.has_identity:
    result = await verify_server(
        server_did=identity.server_did,
        server_badge=identity.server_badge,
    )

Server States Explained

State RFC-007 Definition Recommended Action
VERIFIED_PRINCIPAL DID verified via badge chain ✅ Safe to proceed
DECLARED_PRINCIPAL DID provided but verification failed ⚠️ Warn user, consider blocking
UNVERIFIED_ORIGIN No identity material provided ❌ Treat as untrusted

Error Handling

from capiscio_mcp import verify_server, ServerVerifyError

try:
    result = await verify_server(
        server_did="did:web:mcp.example.com",
        server_badge="eyJhbGc...",
        transport_origin="https://mcp.example.com",
    )
except ServerVerifyError as e:
    print(f"Verification error: {e.error_code}")
    print(f"Detail: {e.message}")

Error Codes

Code Meaning
DID_INVALID Server DID is malformed
BADGE_INVALID Badge signature invalid
BADGE_EXPIRED Badge has expired
BADGE_REVOKED Badge has been revoked
TRUST_INSUFFICIENT Trust level below minimum
ORIGIN_MISMATCH Transport origin doesn't match DID domain
PATH_MISMATCH Endpoint path doesn't match DID path
ISSUER_UNTRUSTED Badge issuer not in trusted list

Synchronous Version

from capiscio_mcp import verify_server_sync

result = verify_server_sync(
    server_did="did:web:mcp.example.com",
    server_badge="eyJhbGc...",
    transport_origin="https://mcp.example.com",
)

Strict Mode

Raises an exception if verification fails:

from capiscio_mcp.server import verify_server_strict

# Raises ServerVerifyError if not VERIFIED_PRINCIPAL
result = await verify_server_strict(
    server_did="did:web:mcp.example.com",
    server_badge="eyJhbGc...",
    transport_origin="https://mcp.example.com",
)
# If we get here, server is verified
print(f"Server trust level: {result.trust_level}")