Skip to content
CRP

CRP v0.2 · Proposed. Submitted to the MCP working group as an experimental capability (discussion #2246). Not yet ratified. Open spec (CC-BY-4.0); any vault can implement it.

CRP v0.2 · Proposed · MCP Working Group

Credential Resolution Protocol

The last-mile credential standard for MCP. OAuth secured the front door. CRP standardizes the hop where secrets actually live — so agents use credentials without holding them.

Protocol-first. Vendor-neutral. Sanctum is a reference implementation — not required to implement CRP.

The gap

OAuth stops at the server door

MCP OAuth 2.1 authenticates client → MCP server. It says nothing about how that server reaches GitHub, OpenAI, databases, or internal APIs. Today those keys sit in .env files — the unsecured last mile.

Today — front door secured, back door open
FRONT DOOR · SECUREDBACK DOOR · NO STANDARDClient / AgentMCP ServerDownstreamOAuth 2.1plaintext .env keysno lease · no audit · forever valid
With CRP — provider injects in transit
SECRET NEVER CROSSES THIS LINEClient / AgentMCP ServerSanctum vaultsealedDownstreamOAuth 2.1use · handleinject in transitholds lease onlysecret sealed here

How CRP works

Use-handle, never a secret

CRP standardizes the last mile OAuth doesn't cover: the hop from an MCP server to the downstream API it calls on your behalf. Instead of handing the caller a secret, a CRP provider returns a use-handle — a lease plus a proxy URL. The caller routes its request through that URL; the provider injects the real credential in transit and forwards upstream. The secret never reaches the caller — not disk, not memory, not the wire.

Request — crp/use

{
  "method": "crp/use",
  "params": {
    "service": "openai",
    "ttl": 300,
    "context": { "purpose": "chat", "traceId": "req-8f2c" }
  }
}

Response — a handle, never a secret

{
  "lease": {
    "id": "lease_a1b2c3",
    "expiresAt": "2026-07-17T18:05:00Z",
    "ttl": 300,
    "renewable": true,
    "delegationDepth": 0
  },
  "proxyBaseUrl": "https://provider.local/proxy/t/lease_a1b2c3",
  "service": "openai",
  "credentialDescriptor": {
    "type": "api_key",
    "brokered": false,
    "autoRefresh": false,
    "classification": "confidential"
  }
}

The caller points its existing HTTP SDK's base URL at proxyBaseUrl and authenticates with the session token it already holds. It never sees the credential — it just makes normal API calls through the handle.

crp/use returns a lease and proxy handle; the provider injects the credential in transit.
use-handle path — secret never leaves the provider.
crp/use path — animated
SANCTUM USE · SECRET STAYS IN THE VAULTAgent / MCPlease + proxyBaseUrlhandle onlySanctum vaultinject in transitDownstreamuse()no secretinject in transitnever holds the keysecret sealed herereal API · 200 OK

Lease lifecycle

Bounded grants, instant kill

A lease is a time- and scope-bound grant, bound to the requesting identity. It is the unit of containment: a leaked handle is worth only what the lease permits, for only as long as it lives, and only through the provider.

  • Mint

    crp/use issues a lease with a TTL, optional scopes, and a proxy handle.

  • Use

    Requests through proxyBaseUrl are checked (identity, expiry, scope) on every call; the provider injects the credential.

  • Renew

    crp/lease extends a renewable lease within provider limits.

  • Revoke

    crp/revoke kills a lease immediately; child leases cascade.

  • Expire

    Leases end at their TTL with no action required.

Because the credential only works through the provider, revocation is immediate and total — there is no long-lived secret to chase down.

Operations

CRP v0.2 surface

Normative surface for the MCP experimental capability. Full text in the specification.

  • crp/useREQUIRED

    Primary op. Returns a lease + proxy handle — never a secret. Provider injects credentials in transit.

  • crp/list · lease · revokeOPTIONAL / RECOMMENDED

    Discovery, renew/release, and emergency kill-switch with cascade on delegated leases.

  • Delegation · broker · classifyFull tier

    Bounded delegation, federated identity brokering (e.g. Entra), sensitivity classification.

  • Graduated outcomesFull tier

    require_approval and quarantine — policy results beyond flat allow/deny.

Error model

Stable codes for programmatic clients

CRP defines a stable error family (JSON-RPC codes in the -330xx range) so callers can react programmatically rather than parsing prose.

CodeNameMeaning
-33001version_mismatchProvider and caller CRP majors are incompatible
-33002service_not_foundSee note on uniform not-found/not-permitted
-33003policy_deniedSee note on uniform not-found/not-permitted
-33004lease_not_foundUnknown lease id
-33005lease_expiredLease TTL elapsed
-33006lease_not_renewableRenewal not permitted
-33007credential_unavailableProvider cannot resolve the credential
-33008rate_limitedCaller exceeded provider limits
-33009scope_unavailableRequested scope not grantable
-33010provider_errorInternal provider failure
-33011feature_not_supportedOp/feature not offered at this conformance tier
-33012revocation_failedRevoke could not complete
-33013delegation_depth_exceededDelegation would exceed the depth ceiling
-33014broker_exchange_failedFederated-token exchange failed
-33020require_approvalGraduated outcome: held pending human approval
-33021quarantineGraduated outcome: lease revoked by policy

Note on service_not_found / policy_denied: a provider MAY return a single uniform "not found or not permitted" for both cases so a caller cannot enumerate which services exist. This is the recommended default.

Delegation

Child leases without escalating secrets

A caller can derive a child lease from one it holds — for a sub-agent or a narrower task — without ever exposing the credential.

  • Same credential

    The child is bound to the same credential the parent holds — delegation never escalates to a different credential.

  • Bounded depth

    Depth is capped by a provider ceiling; a chain cannot grow without limit (delegation_depth_exceeded).

  • Cascade revoke

    Revoking a parent cascades to its entire subtree — no orphaned grants.

Brokered federated identity

Short-lived tokens, still vault-side

For services that accept short-lived federated tokens instead of static secrets, a caller can request a brokered credential via identityScope. The provider performs the token exchange server-side (e.g. an Entra Agent ID two-leg federated exchange, or OAuth2 client-credentials) and refreshes it for the life of the lease — the handle stays stable. credentialDescriptor.brokered is true; the caller still never sees a token.

Entitlement to a given identity is enforced by the identity provider's own federation configuration plus the provider's access policy — a caller cannot assume an identity it is not authorized for.

MCP integration

Experimental capability — no fork

CRP rides MCP's experimental map. Servers advertise support; clients negotiate version. No breaking change to core MCP.

{
  "experimental": {
    "crp": {
      "version": "0.2",
      "provider": "example-vault",
      "conformance": "standard",
      "features": ["use", "list", "lease", "revoke", "delegate", "broker", "classify"]
    }
  }
}

Features match the published CRP v0.2 use-centered surface (use required; no raw-secret export method in the protocol).

Conformance

Tiers built around use-not-retrieve

Providers advertise a conformance tier so callers know what to expect.

  • Basic

    crp/use + leases + audit. The minimum: agents use credentials without holding them.

  • Standard

    Basic + crp/list (discovery) + crp/revoke (kill switch) + version negotiation + data classification on descriptors.

  • Full

    Standard + delegation + brokered federated identity + graduated outcomes (require_approval, quarantine) + classification-aware policy.

Security considerations

Containment by design

  • The handle is a capability, not a secret

    proxyBaseUrl authorizes requests through the provider for the life of the lease — not a bearer credential for the upstream service, and not replayable off the provider.

  • Containment

    Compromising a caller yields leases (TTL + scope + instant revoke), not raw keys. A leaked handle is a bounded, revocable incident.

  • Use-handle is primary

    CRP defines use-not-retrieve as the conforming path. Providers that offer proprietary raw export are outside CRP for that path.

  • No enumeration oracle

    Providers SHOULD return uniform errors for not-found vs not-permitted (see error model).

  • Transport

    CRP assumes the provider terminates and re-originates the downstream request. Deployments SHOULD run the provider loopback-only or behind mutual TLS.

Implementer's on-ramp

Open protocol — any vault

CRP is an open protocol — implement it in any vault or secret manager. Start with the Basic tier (crp/use returning a lease + proxy handle), advertise the capability at MCP initialize, and add tiers as you go. Read the full normative spec at github.com/SanctumSec/crp-spec. Sanctum is a reference implementation — optional.