companion-context-protocol

Implementing A Facility Truth Server

Status: Draft, pre-1.0

This guide explains how to implement a server for the CCP Facility Truth profile.

The profile is intentionally narrow. It answers a focused question: for the requested facility, what public-fact context (profile summary, hours, services, contact methods, service area, acceptance criteria, booking methods, policy summaries) should an agent be allowed to ground itself in? It does not return staff schedules, internal capacity models, billing data, payment authority, household data, identity-document data, medical or wellness history, diagnosis history, treatment history, staff-only notes, or any pet-specific context. Higher-scrutiny facility scopes (certifications, insurance statements, capacity status, staff credentials) are deferred to a future partner-only Facility Truth slice with its own grant model.

The canonical contract is JSON Schema. OpenAPI and MCP are adapter surfaces that carry the same request and response objects.

Canonical Inputs And Outputs

A Facility Truth server accepts:

A Facility Truth server returns:

Canonical schemas:

Facility Truth v1 does not require schemas/permission-grant.schema.json. The v1 scope set covers public-fact context only and operates without a PermissionGrant.

Evaluation Order

Implementations should evaluate requests in this order:

  1. Parse the transport request.
  2. Authenticate the requester through the transport or host environment. Reject the request when the transport provides no authenticated principal. Verify that the asserted requester_actor_type matches the principal’s trust posture; reject requester_actor_type: "vet" until a vet-export profile is defined.
  3. Validate the request body against FacilityTruthRequest.
  4. Resolve the requested facility.
  5. Evaluate each requested scope independently against what the facility has published and verified within the freshness window.
  6. Apply visibility precedence and Facility Truth profile exclusions.
  7. Build the authorization_decision.
  8. Build the minimized facility_truth_context, if any sub-resource may be returned.
  9. Add machine-readable omissions for requested or relevant data that was not returned.
  10. Validate the response against FacilityTruthResponse before returning it.

A grant_id is optional on the request. v1 servers MUST silently accept and ignore a grant_id field when present — do NOT hard-reject requests that carry one. Partner-only Facility Truth scopes will make grant_id required for those scopes, and v1 servers that reject requests carrying a grant_id today will silently break compatibility when partner-only scopes land. Reserve room for a scope-conditional grant-evaluation rule rather than treating “no grant” as an invariant.

Authorization Rules

Scopes are necessary but not sufficient. The v1 profile authorizes eight public-fact scopes that must be evaluated separately:

A request may carry any non-empty subset of these. Each granted scope is evaluated independently. Each must include the corresponding visibility class (facility_public) on every returned field, and each returned field’s provenance must include verified_at within the facility’s freshness window.

A server should authorize each returned field using:

facility_public does not imply commerce safety, care-network access, contact-channel reuse, or facility-shareable status. Cross-profile reuse requires a separately authorized response in the other profile. staff_only, restricted_sensitive, commerce_safe, facility_shareable, care_network_visible, contact_shareable, action_authorization_visible, owner_visible, caregiver_visible, vet_shareable, and agent_summary_only must never appear on returned Facility Truth fields — Facility Truth fields are facility-subject, not pet- or owner-subject, so pet-centric or summary-only classes are semantically incoherent on them and are rejected by FacilityTruthVisibilitySet.

Subject Boundary

The subject boundary is load-bearing. A Facility Truth response describes only the requested facility_id. The request carries no pet_id, and the response must not include pet_id anywhere — there is no pet subject in this profile. Do not return:

Use not_available when a facility is unknown to the system. Do not return partial fields to probe whether a facility exists.

Per-Field Evaluation

Each top-level sub-resource in FacilityTruthContext (profile_summary, hours, services, service_area, contact_methods, booking_methods, acceptance_criteria, policy_summaries) is evaluated independently:

FacilityTruthContext allows a response to include any subset of the sub-resources, but at minimum must include facility_id and metadata. A response that resolves to no sub-resource at all should be denied rather than partial with an empty body.

Response Status

Use ok when:

Use partial when:

Use denied when:

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.

Building The Authorization Decision

Every response must include authorization_decision.

Populate:

Do not put private source records, identity-document data, payment authority details, household data, dispute narratives, custody details, or raw staff notes in reasons. The Facility Truth response schema applies the SensitiveKeywordPattern overlay to both reasons strings and omission detail strings, so banned-keyword leaks reject at the schema layer.

Building Returned Context

Returned context should be minimized to public, agent-grounding facts about the facility.

Every returned context field must use the field envelope shape:

{
  "value": "Mon-Fri 07:00-19:00",
  "visibility": ["facility_public"],
  "provenance": {
    "source_type": "owner_entered",
    "source_actor_id": "actor_facility_admin_001",
    "source_system": "example-ccp-server",
    "recorded_at": "2026-05-01T09:00:00Z",
    "verified_at": "2026-05-10T09:00:00Z"
  }
}

Visibility expectations: every field uses facility_public. No other class may appear on returned Facility Truth fields. The “facility-public rule” is symmetric to the commerce-safe rule.

For generated fields or summaries, provenance must include:

For all returned Facility Truth fields, provenance MUST include verified_at. The freshness window is a property of the facility’s source record and the implementer’s policy; if no verification exists within the window, the field MUST be omitted with not_verified rather than returned.

The returned bundle should not include object families that are outside this profile. Do not add commerce fields, boarding-preparation fields, pickup-verification fields, care-network fields, payment authority, identity-document data, medical history, household data, staff schedules, internal capacity models, or pet-specific context to make an integration easier. Those are profile or slice changes and need separate scopes, purpose rules, visibility behavior, examples, and conformance fixtures.

Freshness

Freshness is part of safety. A facility that has not verified a fact recently is a liability for agent grounding.

Stale or unverified data must not be returned with a guess. Agents grounding on Facility Truth depend on the contract that returned facts have been verified.

Omissions

Use omissions to explain data that was requested or relevant but not returned.

Common omission reasons:

Use purpose_not_allowed for data outside facility_truth_lookup, even if the requester might operationally want it (for example, pet-specific context, staff schedules, billing).

Use visibility_restricted for source fields marked staff_only, restricted_sensitive, or not explicitly carrying facility_public.

Use scope_missing when a sub-resource has a defined scope but the request did not include it.

Use not_verified when the scope is allowed but the facility has no current verification on file.

Use not_applicable when the sub-resource does not apply to this facility (e.g., a mobile facility with no fixed service area, or a daycare with no booking link).

Omissions belong in the response envelope. Nested context objects should not carry their own omissions array.

Omission detail strings should be useful but not revealing. The Facility Truth response applies the SensitiveKeywordPattern overlay — banned keywords (billing, payment, household, medical, diagnosis, treatment, staff-only, staff-note, relationship-dispute, custody, identity-document) are rejected by the schema. Implementers must also avoid embedding any restricted source content in detail strings.

Profile Exclusions

Do not return these in the Facility Truth profile:

Higher-scrutiny facility scopes — certifications, insurance statements, capacity status, staff credentials — are deferred to a future partner-only Facility Truth slice. That slice will define its own scopes, purpose rules, visibility class (facility_partner_visible), grant shape (likely an additive subject_facility_id on PermissionGrant), examples, and conformance fixtures. v1 implementers should not assume the no-grant semantics generalize to those future scopes.

Public Examples

Positive Facility Truth flow:

Partial flow (one scope unverified):

Valid denied response:

Schema-invalid fixtures:

Transport Adapters

OpenAPI adapter:

MCP adapter:

Both adapters preserve the canonical request, response, authorization decision, visibility, freshness, subject boundary, per-field evaluation, and omission semantics. The Facility Truth adapter sketches do not include a permission-grant lookup tool; v1 has no grant.

Implementation Checklist

Conformance

Install dependencies and run:

npm install
npm test

The test suite validates positive examples, valid denied examples, invalid fixtures, the OpenAPI adapter, and the MCP tool sketches.