Status: Draft, pre-1.0
This guide explains how to implement a server for the CCP Commerce Context Profile.
The canonical contract is JSON Schema. OpenAPI and MCP are adapter surfaces that carry the same request and response objects.
A Commerce Context server accepts:
CommerceContextRequestA Commerce Context server returns:
CommerceContextResponseauthorization_decision in every response.commerce_context for allowed or partial responses.commerce_context: null for denied responses.omissions explaining data that was not returned.Canonical schemas:
schemas/commerce-context-request.schema.jsonschemas/commerce-context-response.schema.jsonschemas/permission-grant.schema.jsonschemas/ccp-core.schema.jsonImplementations should evaluate requests in this order:
requester_actor_type matches the principal’s trust posture; reject mismatches and reject requester_actor_type: "vet" until a vet-export profile is defined.CommerceContextRequest.grant_id is supplied.authorization_decision.commerce_context, if any data may be returned.CommerceContextResponse before returning it.Scopes are necessary but not sufficient.
A server should authorize each returned field using:
requester_actor_type bound to the authenticated principal.For the Commerce Context Profile, owner_visible, caregiver_visible, and vet_shareable do not imply commerce access. Returned commerce fields must be commerce-safe and must not include staff_only or restricted_sensitive.
Use ok when:
authorization_decision.decision is allowed.commerce_context is non-null.omissions is empty.Use partial when:
authorization_decision.decision is partial.commerce_context is non-null.omissions has at least one item.Use denied when:
authorization_decision.decision is denied.commerce_context is null.omissions has at least one item.Protocol-level denials should return a CCP response envelope. Transport-level failures, such as malformed JSON or missing transport authentication, may use ordinary transport errors.
Every response must include authorization_decision.
Populate:
decision: allowed, partial, or denied.evaluated_at: timestamp of evaluation.requester_actor_id: authenticated requester.requester_actor_type: echoed from the request, MUST equal the request value.pet_id: requested pet.purpose: requested purpose.grant_id: grant used, when available.applied_scopes: scopes that were allowed and used.denied_scopes: requested or relevant scopes that were not allowed.reasons: concise human-readable explanation.Do not put private source records, raw staff notes, raw wellness timelines, diagnosis history, billing data, household data, or sensitive facility operations data in reasons.
Returned context should be minimized to the current request.
Every returned context field must use the field envelope shape:
{
"value": "large",
"visibility": ["owner_visible", "commerce_safe"],
"provenance": {
"source_type": "generated",
"source_system": "example-ccp-server",
"recorded_at": "2026-05-04T16:30:00Z",
"derived_from": ["example://pets/pet_luna_001/profile/weight"]
}
}
For generated fields or summaries, provenance must include:
source_systemderived_fromFor source facts, provenance should include source actor or system, recorded timestamp, freshness metadata when available, and source record reference when safe to expose.
Use omissions to explain data that was requested or relevant but not returned.
Common omission reasons:
not_requestedscope_missingpurpose_not_allowedvisibility_restrictedgrant_expiredgrant_revokedsource_stalenot_availablesummary_onlyOmissions belong in the response envelope. Nested context objects should not carry their own omissions array.
Omission details should be useful but not revealing. Do not disclose restricted source content in omission text. The Commerce Context response schema does not apply the SensitiveKeywordPattern overlay to reasons or omission detail strings, because legitimate Commerce Context omission text references excluded categories by name (for example, “Staff-only notes are not commerce-safe.”). This is an intentional design asymmetry, not a gap to be closed: applying the overlay uniformly would reject legitimate category-naming text. The implementer is the load-bearing control. A category label that explains why a field was withheld is fine; an actual record value smuggled alongside the label is not. Concretely:
"Staff-only notes are not commerce-safe.""Wellness timelines are outside the Commerce Context Profile.""Wellness: 2025-09-12 limp on left rear leg, suspected sprain." — this embeds restricted source content under cover of a category label.Do not return these by default in Commerce Context responses:
agent_summary_only.If a future profile explicitly allows more sensitive data, it should define separate scopes, purpose rules, visibility behavior, and conformance fixtures.
OpenAPI adapter:
openapi/commerce-context.openapi.jsonPOST /commerce-contextGET /permission-grants/{grant_id}, requiring pet.permission_grants.readMCP adapter:
mcp/commerce-context.tools.jsonccp_commerce_context_requestccp_permission_grant_get, requiring pet.permission_grants.readBoth adapters must preserve the canonical request, response, authorization decision, visibility, provenance, and omission semantics.
requester_actor_type to the authenticated principal; reject mismatches, unauthenticated requests, and requester_actor_type: "vet" until a vet-export profile lands.grantor_actor_type against the authenticated grant issuer at issuance time.npm test before claiming compatibility.Install dependencies and run:
npm install
npm test
The test suite validates positive examples, rejects negative fixtures, validates the OpenAPI adapter, and checks the MCP tool sketches.