Skip to main content

CCP reference SDK

A Python reference runtime for the Capability Contacts Protocol (draft v0.4). It implements the protocol primitives and a runtime that validates signals, enforces local grants, mediates host functions, supports two-phase confirmation, and keeps a private audit log.

Layout

Module Implements
ccp.canonical strict canonical JSON (§25.1)
ccp.crypto Ed25519 key/signature encoding (§25.2)
ccp.schema JSON Schema subset (§25.3)
ccp.model Manifest, Grant, Signal, Decision
ccp.requester build and sign manifests and signals
ccp.consumer verify inbound documents, permission cards
ccp.agent agent tool calls and attenuation
ccp.stores grant/manifest/schema stores, replay, limits
ccp.decision the pure §10.3 validation decision
ccp.state_machine the §26.3 side-effect transitions
ccp.hosts host-function registry and privilege mediation
ccp.runtime submission, confirmation, execution, audit
ccp.adapters.fastapi provider signal/manifest routes
ccp.adapters.mcp transport-neutral MCP tool bridge

Quick start

from ccp import (Grant, GrantStore, HostRegistry, Manifest, ManifestStore,
                 Runtime, SchemaRegistry, SigningKey, sign_document)

provider = SigningKey.generate()
requester = SigningKey.generate()

manifest = Manifest.parse(sign_document({
    "ccp": "0.4-draft",
    "provider": {"id": provider.key_id, "name": "Example Service"},
    "version": 1,
    "intents": {
        "example.lookup": {
            "params_schema": "schema:example.lookup.params.v1",
            "result_schema": "schema:example.lookup.result.v1",
            "required_capabilities": ["store.read"],
            "confirmation": "grant-policy",
        }
    },
}, provider))
manifest.verify()

grant = Grant(
    id="g-1",
    requester_key=requester.key_id,
    contact_id="c-1",
    manifest_digest=manifest.digest,
    manifest_version=1,
    intents=["example.lookup"],
    limits={"calls_per_hour": 10},
    created_at="2026-10-22T10:00:00Z",
    expires_at="2026-10-22T23:00:00Z",
    confirmation={"example.lookup": "none"},
    capabilities=["store.read"],
    placement=["local"],
    egress={"network": False},
)

schemas = SchemaRegistry({
    "schema:example.lookup.params.v1": {
        "type": "object",
        "properties": {"item_id": {"type": "string"}},
        "required": ["item_id"],
        "additionalProperties": False,
    },
})
hosts = HostRegistry()
hosts.register_host("store.read", lambda item_id: {"item_id": item_id})
hosts.register_intent(
    "example.lookup",
    lambda params, access: access.call("store.read", params["item_id"]),
)

runtime = Runtime(
    grants=GrantStore([grant]),
    manifests=ManifestStore([manifest]),
    schemas=schemas,
    hosts=hosts,
    clock=lambda: "2026-10-22T12:00:30Z",
)

signal = sign_document({
    "ccp": "0.4-draft",
    "request_id": "req-1",
    "grant_digest": manifest.digest,
    "intent": "example.lookup",
    "params": {"item_id": "item-123"},
    "issued_at": "2026-10-22T12:00:00Z",
    "expires_at": "2026-10-22T12:01:00Z",
}, requester)

decision = runtime.submit(signal, requester.verifying_key())
assert decision.authorized

Confirmation

If a grant's confirmation policy for an intent is per_action, Runtime.submit returns a Decision with needs_confirmation true and a pending_id. The trusted client later calls:

runtime.confirm(decision.pending_id, approved=True)   # or approved=False

Roles

Requester, consumer, and agent mirror the TypeScript surfaces.

from ccp import Requester, SigningKey
from ccp.consumer import permission_card, verify_manifest

requester = Requester(SigningKey.generate())
manifest_wire = requester.manifest(
    {"id": provider.key_id, "name": "Example Service"}, 1, intents)

manifest = verify_manifest(manifest_wire)
card = permission_card(manifest, "example.lookup")

signal_wire = requester.signal(
    "req-1", manifest.digest, "example.lookup", {"item_id": "item-123"},
    "2026-10-22T12:00:00Z", "2026-10-22T12:01:00Z")

from ccp import Agent, attenuates

agent = Agent(agent_key)
tool_signal = agent.signal_for(
    intent="example.lookup", params={"item_id": "item-123"},
    grant=grant, request_id="req-2",
    issued_at="2026-10-22T12:00:00Z", expires_at="2026-10-22T12:01:00Z")
assert attenuates(parent_grant, child_grant)

FastAPI provider

from fastapi import FastAPI
from ccp.adapters.fastapi import manifest_router, provider_router

app = FastAPI()
app.include_router(manifest_router(manifest.document))
app.include_router(provider_router(runtime, key_resolver))

key_resolver(request) returns the sender's VerifyingKey, or None for an unknown sender; it may be sync or async. Statuses match the TypeScript adapter: 200 authorized/confirmation_required, 400 invalid_request, 401 unknown sender, 403 unauthenticated/no_matching_grant, 409 other refusals.

The adapter only parses {"signal": "<canonical json>"}, resolves the key, and calls Runtime.submit. Install with pip install 'ccp-protocol[fastapi]'.

MCP agent bridge

from ccp.adapters.mcp import McpServer

bridge = McpServer(
    runtime=runtime,
    agent=agent,
    grant=grant,
    # Trusted host decision. Omit to surface confirmation_required instead.
    confirm=lambda request: ask_user(request.card, request.params),
)

# Adapt bridge.tools and `await bridge.call_tool(name, args)` in your MCP
# server. The bridge has no MCP dependency and never auto-approves.

One bridge serves one grant; tool names are the granted intent names. Provider descriptions are labelled untrusted, results are returned as JSON data, and confirmation is the host's decision. Install with pip install 'ccp-protocol[mcp]'. See examples/agent_mcp/.

Durable stores

Runtime accepts any object implementing the store contracts in ccp/stores.py. ccp.stores_sqlite.SqliteStores provides SQLite-backed implementations of all six, using only the standard library:

from ccp import Runtime
from ccp.stores_sqlite import SqliteStores

stores = SqliteStores("ccp.db")  # or ":memory:"
runtime = Runtime(
    grants=stores.grants,
    manifests=stores.manifests,
    replay=stores.replay,
    limits=stores.limits,
    pending=stores.pending,
    audit=stores.audit,
    schemas=schemas,
    hosts=hosts,
)

The atomic operations (try_reserve, mark_if_new, take) use BEGIN IMMEDIATE transactions, so they are safe across processes sharing the database file. See ../../docs/guides/persistence.md for the contract.

Examples

  • examples/provider_fastapi/ — HTTP provider (python examples/provider_fastapi/server.py)
  • examples/agent_mcp/ — stdio MCP server and in-memory client demo (python examples/agent_mcp/demo.py)

Tests

/usr/bin/python3 -m unittest discover -s tests

The suite includes conformance tests that run the frozen vectors in ../../test-vectors/ through the SDK, plus end-to-end runtime, role, and adapter tests. The FastAPI and MCP tests need the dev extra:

pip install '.[dev]'
/usr/bin/python3 -m unittest discover -s tests

Requires Python 3.10+ and cryptography for Ed25519. The MCP extra requires Python 3.10+.

Metadata

Release files for ccp-protocol 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for ccp-protocol 0.4.0
File Interpreter ABI Platform Actions
ccp_protocol-0.4.0-py3-none-any.whl Python 3 none any Details for ccp_protocol-0.4.0-py3-none-any.whl

Release files / ccp_protocol-0.4.0-py3-none-any.whl

Download URL ccp_protocol-0.4.0-py3-none-any.whl
Size 29.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
94dd53b7f7011da8bcdf8fe6e8862a3ba4adee4d096758ea44dffa32bee7a684
BLAKE2b-256 checksum
How to use checksums
3952cdbe221034ece9ab84e36a503d2b2547c88c35594c8c17535a15047ed20b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for ccp-protocol 0.4.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.0 This release

1 release file No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page