> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# MCP contract

The experimental MCP gateway exposes every `x-owner` business operation in the
assembled [Go API OpenAPI specification](/reference/contracts/http/README.md). The reviewed
<a href="/source/contracts/mcp/manifest.json.txt">manifest</a> gives each operation one stable tool name, scope and
effect classification. It currently contains 54 operations, including writes,
deletions and the two operator-only Support routes.

The manifest is deliberately explicit. Generation fails when an API operation
is added or removed without a parity decision, a scope crosses its owner, a
write is classified as a read, or an input admits an authentication header.
Authentication/session endpoints, internal relays, diagnostics and realtime
connections do not become tools.

Run `pnpm generate:contracts` after editing sources or generators.
`pnpm check:http` checks the bundled specification, generated tool schemas,
Go DTOs, HTTP client and closed typed dispatcher. Generated files live in
`services/api/adaptors/{mcpcontract,mcpclient}/generated`.

## Inputs and outcomes

Inputs retain the OpenAPI names in three optional groups:

```json
{
  "path": {"id": "11111111-1111-4111-8111-111111111111"},
  "parameters": {
    "Idempotency-Key": "22222222-2222-4222-8222-222222222222",
    "If-Match": "0"
  },
  "body": {"title": "A support request", "body": "Please investigate."}
}
```

This is the input to `create_support_ticket`. Tools retain required command
headers, canonical identifiers, expected revisions, filters and pagination.
Path values are encoded by the generated client; they cannot select an arbitrary
origin or operation. Read-only annotations include SSH key validation, which the
HTTP API models as a POST.

Each result has `operation_id`, HTTP `status`, and either the generated `result`
or a bounded `error` containing `code`, `message` and optional `outcome_unknown`.
Structured and text results contain the same envelope. The HPC projection keeps
its Protobuf representation as `{media_type, base64}`.

Declared, schema-valid shared, Run and Pipeline command outcomes retain their
`aggregateId`, `version`, `status` and `rejection` in `result`, including HTTP 404,
409 and 422 where that operation declares the corresponding outcome schema.
These non-success replies set the MCP error flag while preserving the recorded
outcome for recovery. Ordinary and unrecognised error bodies keep the sanitised
`error` envelope.

Arguments are limited to 64 KiB. API replies are read with a 1 MiB bound; the
gateway also bounds the duplicated MCP envelope. Large results require narrower
queries or the ordinary API. Unknown provider error details are omitted.
Malformed successful replies fail contract validation.

The gateway makes one HTTP attempt, refuses redirects and never manufactures
idempotency keys or revisions. After an uncertain write, preserve the exact
input and command identity for a user-authorised replay or current-state query.
Tool annotations and instructions ask clients to obtain approval before writes.
The platform enforces identity, scopes and owner permissions; the client owns
per-action human approval.

## Authority

Scopes use `<owner-with-hyphens>:read`, `:write` or `:admin`. Tokens contain only
resource scopes actually granted through OAuth. Discovery is scoped to each
request. The API rechecks permissions for each invocation.

An ordinary Party can request member scopes and use the operations allowed by
the existing API and owners. The two Support administration operations also
require API-side operator enrolment, checked at each call. The broker captures
that enrolment at login and checks it on refresh; an old grant cannot silently
gain newly enrolled operator rights.

The protocol uses three distinct JWT families:

| Family                     | Signer       | Audience                  | Lifetime                                        |
| -------------------------- | ------------ | ------------------------- | ----------------------------------------------- |
| `at+jwt`                   | OAuth broker | Exact `MCP_RESOURCE_URL`  | At most 600 seconds                             |
| `pharos-mcp-login+jwt`     | OAuth broker | `pharos-api-mcp-login:v1` | At most 30 seconds                              |
| `pharos-mcp-assertion+jwt` | Gateway      | `pharos-api-mcp:v1`       | At most 30 seconds, bounded by the access token |

All carry exact `iss`, `aud`, `sub`, `iat`, `exp`, `jti`,
`identity_issuer`, `identity_subject` and `identity_voperson_id`.
Access tokens additionally require `client_id`, `party_id`, `auth_version` and
`scope`. Gateway assertions retain Party authority and scopes, replace
`client_id` with an exact `operation_id`, and receive a fresh identity and expiry.
Login assertions contain neither a Party nor an operation or scope.

The private `POST /internal/mcp/identity` endpoint accepts only a broker login
assertion. Its response contains `party_id`, `auth_version` and `operator`.
It resolves an existing exact identity through Party Governance, checks current
AUP through its established binding and rechecks the Party generation. Missing
registration, pending setup or required AUP asks the user to complete ordinary
platform login. No account is created by the MCP login query.

JWT parsing rejects duplicates, unknown fields, incorrect casing, nulls, missing
identity, wrong token families, wrong audiences and invalid lifetimes. Broker
and gateway verification keys are explicitly enrolled and must be different.
The API's ordinary browser and CLI paths retain their existing credentials.
A failed delegated bearer never falls back to a browser cookie.

See [ADR 0071](/reference/docs/decisions/0071-generated-mcp-api-edge.md) and the
[runtime guide](/reference/services/api/services/mcp/README.md).

<details class="page-source"><summary>Page source</summary>

<a href="/source/contracts/mcp/README.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
