---
title: "Finish an OAuth connect (hop 2)"
method: POST
path: "/v1/mcp-servers/oauth/finalize"
tags: ["mcp-servers"]
---

# Finish an OAuth connect (hop 2)

`POST /v1/mcp-servers/oauth/finalize`

## Request

- Base URL: `https://api.patricia.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.patricia.app/v1/mcp-servers/oauth/finalize`
- Auth: HTTP bearer

Redeems the one-time capability from the callback, exchanges the authorization code, and installs the server. Requires the same person who started the ceremony.

## Headers

- `X-Tenant-Id` string, nullable

## Request body

- OAuthFinalizeBody: The capability the dashboard came back with, plus a compatibility marker. ``extra="forbid"`` is what makes the marker work at all: a replica that predates this field rejects a request carrying it with a 422 during BODY VALIDATION — before the claim runs, so nothing is claimed and no capability is consumed. That is the whole mechanism, and it is why the console sends the field only when the capability announces a personal ceremony. Sent unconditionally it would 422 every TEAM finalize against an old replica, breaking the property the namespace was designed to preserve. It is compatibility metadata and NOTHING else. It must never select visibility: the attempt row is the only thing that says what scope the human consented to, and a marker in a request body could be changed by whoever sends it.
  - `capability` string, required
  - `protocol_version` 2, nullable

## Response `200`

Successful Response

- McpServerResponse: What the dashboard renders on the card.
  - `integration_account_id` string, required
  - `external_account_id` string, required
  - `base_url` string, required
  - `auth_mode` string, required
  - `header_name` string, nullable
  - `negotiated_protocol_version` string, nullable
  - `connection_generation` integer, required
  - `refreshing` boolean, required
  - `last_error_class` string, nullable
  - `needs_reauth` boolean
  - `connected_by` string, nullable
  - `catalog` McpCatalogOutcomeResponse: The last catalog reading recorded for a server — what it offered and how much Patricia took. This exists because a refusal was previously invisible: a tool whose schema Patricia declines yields no spec, so it is simply ABSENT from the listing, indistinguishable from one the server withdrew. The card could show a server as connected and healthy while half its catalog had been refused, and no surface said so. Every field describes THE READING, never the account's current rows. On a refused reduction the enabled rows are the PREVIOUS catalog, so the two answer different questions and must not be mixed in one sentence.
    - `complete` boolean, required
    - `accepted_unique` integer, required
    - `offered_unique` integer, required
    - `rejected` integer, required
    - `dropped_pre_schema` integer, required
    - `reasons` object[], required
    - `observed_at` string, date-time, required
  - `visibility` string, required
  - `is_mine` boolean
  - `can_manage` boolean
  - `can_disconnect` boolean
  - `can_reconsent` boolean
  - `can_share_to_team` boolean
  - `can_make_private` boolean
  - `share_to_team_unavailable_reason` 'already_shared' | 'managed_account' | 'scope_sealed_at_connect' | 'missing_edit_permission' | 'not_your_account' | 'mcp_share_needs_reconnect': Why ``can_share_to_team`` is False for this caller. Null when it is True. The value names the first term of that answer that fails, in the order ``account_capabilities`` states: - ``already_shared``: the connection is shared with the whole workspace already. - ``managed_account``: Patricia manages the connection. The visibility write refuses a managed connection for every caller. - ``scope_sealed_at_connect``: the connection's credential is sealed to the sharing it was connected with (``core.scope_sealed_credentials``), and both visibility writes refuse to move it in either direction. Its refusal message carries the remedy, ``core.scope_sealed_credentials.SCOPE_SEALED_REMEDY``. - ``missing_edit_permission``: the caller does not hold ``integrations:edit``. - ``not_your_account``: the caller is not the person who connected this private connection. - ``mcp_share_needs_reconnect``: the MCP server's saved sign-in would stop its tools working once the server is shared, so the write refuses with the ``MCP_SHARE_NEEDS_RECONNECT`` error code. Reconnecting the server is the remedy.
  - `make_private_unavailable_reason` 'already_private' | 'managed_account' | 'scope_sealed_at_connect' | 'missing_edit_permission' | 'owner_unknown' | 'oauth_owner_mismatch': Why ``can_make_private`` is False for this caller. Null when it is True. The value names the first term of that answer that fails, in the order ``account_capabilities`` states: - ``already_private``: the connection is private already. - ``managed_account``: Patricia manages the connection. The visibility write refuses a managed connection for every caller. - ``scope_sealed_at_connect``: the connection's credential is sealed to the sharing it was connected with (``core.scope_sealed_credentials``), and both visibility writes refuse to move it in either direction. Its refusal message carries the remedy, ``core.scope_sealed_credentials.SCOPE_SEALED_REMEDY``. - ``missing_edit_permission``: the caller does not hold ``integrations:edit``. - ``owner_unknown``: the connection records nobody it could become private to. An MCP server needs a ``connected_by_user_id``; another connection needs an email or actor. - ``oauth_owner_mismatch``: the MCP server signs in with OAuth, and its saved sign-in is not the recorded connector's: there is none, it names another user or no membership, or its membership is not the one the connector's owner contract resolves. The narrowing's grant check refuses it with the ``MCP_OAUTH_OWNER_MISMATCH`` error code. Disconnecting the server and having that person connect it again as private is the remedy.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/patricia/apis/patricia-api.md) · [All operations](https://skmtc.dev/patricia/apis/patricia-api/llms.txt) · [OpenAPI document](https://skmtc.dev/patricia/apis/patricia-api/revisions/0dfb44495ea1?raw)
