MCP Server API
Table of Contents
upsquad/mcpserver/v1/credential.proto
DeleteMCPServerCredentialRequest
DeleteMCPServerCredentialRequest identifies the credential to remove at a given scope. The tenant is taken from the auth context.
| Field | Type | Label | Description |
|---|---|---|---|
| registry_id | string | registry_id is the mcp_servers registry id the credential belongs to. | |
| credential_type | string | credential_type selects which stored credential to remove ("bearer_token" | |
| unit_id | string | unit_id scopes the delete to a team, identically to the Set request. | |
| agent_id | string | agent_id scopes the delete to an agent (mutually exclusive with unit_id; both absent ⇒ org-level _default), identically to the Set request. |
DeleteMCPServerCredentialResponse
DeleteMCPServerCredentialResponse confirms the removal WITHOUT returning any secret material.
| Field | Type | Label | Description |
|---|---|---|---|
| registry_id | string | registry_id echoes the server the credential was removed for. | |
| credential_type | string | credential_type echoes the removed credential kind. | |
| vault_ref | string | vault_ref is the scope-qualified ref that was targeted. | |
| deleted | bool | deleted is true when a row was removed, false when none existed (idempotent). | |
| deleted_at | google.protobuf.Timestamp | deleted_at is the server time the delete was processed. |
ListMCPServerCredentialsRequest
ListMCPServerCredentialsRequest enumerates a single team's stored MCP credentials. The tenant (org) is taken from the auth context; only the team is carried here.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit of unit_type 'team') whose shared credentials are listed. Required. MUST be a team the caller's tenant owns (IDOR-guarded); a cross-tenant or unknown id is rejected NotFound. |
ListMCPServerCredentialsResponse
ListMCPServerCredentialsResponse returns the team's masked credential summaries.
| Field | Type | Label | Description |
|---|---|---|---|
| credentials | MCPServerCredentialSummary | repeated | credentials is the masked summary per stored (server, credential_type), oldest-first. Empty when the team has no stored credentials. |
MCPServerCredentialSummary
MCPServerCredentialSummary is one masked credential row. It NEVER carries the plaintext secret — only a masked hint.
| Field | Type | Label | Description |
|---|---|---|---|
| registry_id | string | registry_id is the mcp_servers id the credential is bound to (parsed from the stored vault provider name mcp_<type>_<registry_id>). | |
| server_name | string | server_name is the bound server's display name, or empty when the server was deregistered (the credential row still surfaces so the UI can offer cleanup). | |
| credential_type | string | credential_type is the stored credential kind: "bearer_token" | |
| masked_hint | string | masked_hint is a UI-safe fingerprint exposing only the last 4 chars (e.g. "••••AB12"). It is derived inside Postgres and is NEVER the plaintext. | |
| created_at | google.protobuf.Timestamp | created_at is when the credential was first stored. | |
| last_rotated_at | google.protobuf.Timestamp | last_rotated_at is when the credential was last (re)written. |
SetMCPServerCredentialRequest
SetMCPServerCredentialRequest carries the target server + the raw secret. The tenant is taken from the auth context (never from the request body).
| Field | Type | Label | Description |
|---|---|---|---|
| registry_id | string | registry_id is the mcp_servers registry id the credential belongs to. It is part of the vault key so a server's credential is isolated per server. | |
| credential_type | string | credential_type selects which upstream credential is being stored: - "bearer_token" — a static Bearer token (bearer upstream-auth mode) - "oauth_client_secret" — the gateway's OAuth client_secret (oauth2 mode) Validated server-side; any other value is rejected. | |
| secret | string | secret is the raw credential. It is stored encrypted in the vault and is NEVER echoed back, logged, or persisted to any table. | |
| unit_id | string | unit_id OPTIONALLY scopes the credential to a single org_unit (team). When set it MUST be a team the caller's tenant owns (IDOR-guarded). Mutually exclusive with agent_id. Absent + agent_id absent ⇒ org-level _default. | |
| agent_id | string | agent_id OPTIONALLY scopes the credential to a single agent. When set it MUST be an agent the caller's tenant owns (IDOR-guarded). Mutually exclusive with unit_id. Agent-scoped writes are allowed at L3 when the caller manages the agent's team, otherwise L4. |
SetMCPServerCredentialResponse
SetMCPServerCredentialResponse confirms the write WITHOUT returning the secret.
| Field | Type | Label | Description |
|---|---|---|---|
| registry_id | string | registry_id echoes the server the credential was stored for. | |
| credential_type | string | credential_type echoes the stored credential kind. | |
| key_hint | string | key_hint is a masked fingerprint (last 4 chars only), safe to render in the UI to confirm which secret was stored — e.g. "••••AB12". | |
| vault_ref | string | vault_ref is the resulting vault://provider_keys/<org>/<scope>/<provider> pointer the client patches into the server's upstream_auth_config bag (as token_vault_ref for bearer, or client_secret_vault_ref for oauth2), where <scope> is _default (org), the unit_id (team), or _agent_<agent_id> (agent). Only this opaque ref — never the raw secret — is persisted. | |
| created | bool | created is true when this was a first-time set, false when it rotated an existing secret for the same (tenant, scope, server, credential_type). | |
| updated_at | google.protobuf.Timestamp | updated_at is the server time the credential was written. |
MCPServerCredentialService
MCPServerCredentialService is the thin tenant MCP-credential surface.
Write-only for secret MATERIAL: SetMCPServerCredential is the only path that accepts a raw secret, and no RPC ever returns the plaintext — a stored secret never leaves the vault. Delete removes (never reveals). ListMCPServerCredentials (core#1789) enumerates a team's stored credentials for the Credentials UI (client#566) but returns ONLY a MASKED hint per row (last 4 chars) — the mask is computed inside Postgres so the plaintext is never even materialized in the process. It is a VIEW-level read (L2+), NOT gated on the L4 write clearance, so a unit Manager can populate the UI.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| SetMCPServerCredential | SetMCPServerCredentialRequest | SetMCPServerCredentialResponse | SetMCPServerCredential stores the raw upstream credential (a bearer token or an OAuth client_secret) for an MCP server in the tenant vault and returns only a masked hint + the resulting vault ref. The credential is stored at one of three scopes, selected by the OPTIONAL unit_id / agent_id fields (mutually exclusive; both absent ⇒ org-level _default legacy slot): - org: (org, "default", mcp<type><registry_id>) — L4 - team: (org, <unit_id>, mcp<type><registry_id>) — L4 - agent: (org, "agent"+agent_id, mcp<type>_<registry_id>) — L4, or L3 when the caller is the Manager of the agent's team. Idempotent per (org, scope, provider): a second call rotates the secret. The unit_id / agent_id MUST belong to the caller's tenant (IDOR-guarded via a real existence check) — a cross-tenant or unknown id is rejected PermissionDenied. Emits one mcp_server.credential.set audit event on success carrying only the credential_type + a masked hint + the scope — never the raw secret. Tenant is taken from the auth context. |
| DeleteMCPServerCredential | DeleteMCPServerCredentialRequest | DeleteMCPServerCredentialResponse | DeleteMCPServerCredential removes a stored upstream credential at the given scope (same org/team/agent selection + IDOR + clearance rules as Set). It NEVER returns secret material — only whether a row was removed + the vault ref that was targeted. Emits one mcp_server.credential.deleted audit event. Idempotent: deleting an absent credential succeeds with deleted=false. |
| ListMCPServerCredentials | ListMCPServerCredentialsRequest | ListMCPServerCredentialsResponse | ListMCPServerCredentials enumerates every upstream MCP credential stored for a team (unit_id), one masked summary per (server, credential_type), to populate the team-page Credentials UI (client#566). SECURITY: it returns a MASKED hint only (e.g. "••••AB12", last 4 chars) — NEVER the plaintext. The mask is derived server-side inside Postgres (pgp_sym_decrypt is applied only to extract the last 4 characters) so the full secret is never materialized in the process, a log, or the response. Tenant is taken from the auth context; the requested unit_id MUST be a team the caller's tenant owns (IDOR-guarded, a cross-tenant / unknown id → NotFound). Clearance: a VIEW-level read (L2+), deliberately NOT the L4 write bar — a unit Manager can call it. Rows whose bound server was deregistered carry an empty server_name (the credential row still surfaces so the UI can offer cleanup). Read-only: emits no audit event and mutates nothing. |
upsquad/mcpserver/v1/mcpserver.proto
AddMCPGroupMemberRequest
AddMCPGroupMemberRequest attaches a server to a group.
| Field | Type | Label | Description |
|---|---|---|---|
| group_id | string | group_id is the mcp_proxy_groups.id. | |
| server_id | string | server_id is the mcp_servers.id to attach. Must be eligible and must not create a hard conflict with the existing member set. |
AddMCPGroupMemberResponse
AddMCPGroupMemberResponse carries the post-attach group.
| Field | Type | Label | Description |
|---|---|---|---|
| group | MCPProxyGroup | group is the group after the member was added. |
AgentServerCell
AgentServerCell is one agent×server verdict in the D9 grid.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id. | |
| server_name | string | server_name is the server's display name. | |
| verdict | string | verdict is the effective outcome for this agent×server pair: allowed (every gate passed) or denied (a gate denied — see reason). | |
| reason | DenialReason | reason is the machine-resolved denial cause; UNSPECIFIED when verdict is allowed. Populated most-global-first (the first denying gate wins). |
AgentServerEntitlements
AgentServerEntitlements is the per-agent row of the D9 denial-reason grid.
| Field | Type | Label | Description |
|---|---|---|---|
| agent_id | string | agent_id is the agents.id. | |
| agent_name | string | agent_name is the agent's display name. | |
| clearance | int32 | clearance is the agent's clearance level (agents.clearance, 1..5) — the value compared against each server's min_clearance floor. | |
| servers | AgentServerCell | repeated | servers is one cell per MCP server relevant to the team, server-id sorted. |
ApproveMCPServerBindingRequest
ApproveMCPServerBindingRequest approves a single pending binding.
| Field | Type | Label | Description |
|---|---|---|---|
| binding_id | string | binding_id is the mcp_server_units.id to approve. |
ApproveMCPServerBindingResponse
ApproveMCPServerBindingResponse carries the post-approve binding.
| Field | Type | Label | Description |
|---|---|---|---|
| binding | MCPServerBinding | binding is the binding after the approve transition. |
CreateMCPGroupRequest
CreateMCPGroupRequest creates a group under a team unit.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit) the group is scoped to. | |
| name | string | name is a human-readable label (2..64 chars), unique per org among active groups. | |
| member_server_ids | string | repeated | member_server_ids are the initial member mcp_servers.id values. May be empty (an empty group is created and members added later). Every id must be eligible (server + binding approved for unit, eligible transport) and the set must be conflict-free. |
CreateMCPGroupResponse
CreateMCPGroupResponse carries the freshly created group.
| Field | Type | Label | Description |
|---|---|---|---|
| group | MCPProxyGroup | group is the newly created group with its proxy URL. |
DeleteMCPGroupRequest
DeleteMCPGroupRequest soft-deletes a group.
| Field | Type | Label | Description |
|---|---|---|---|
| group_id | string | group_id is the mcp_proxy_groups.id to soft-delete. |
DeleteMCPGroupResponse
DeleteMCPGroupResponse reports whether a row was updated.
| Field | Type | Label | Description |
|---|---|---|---|
| deleted | bool | deleted is true when a row was actually soft-deleted. Idempotent — returns false when the group was already deleted or never existed. |
DenyMCPServerBindingRequest
DenyMCPServerBindingRequest denies a single pending binding.
| Field | Type | Label | Description |
|---|---|---|---|
| binding_id | string | binding_id is the mcp_server_units.id to deny. | |
| reason | string | reason is an optional free-text explanation recorded on the binding and in the audit row. |
DenyMCPServerBindingResponse
DenyMCPServerBindingResponse carries the post-deny binding.
| Field | Type | Label | Description |
|---|---|---|---|
| binding | MCPServerBinding | binding is the binding after the deny transition. |
DeregisterMCPServerRequest
DeregisterMCPServerRequest soft-deletes a server registration.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id to soft-delete. |
DeregisterMCPServerResponse
DeregisterMCPServerResponse reports whether a row was updated.
| Field | Type | Label | Description |
|---|---|---|---|
| deregistered | bool | deregistered is true when a row was actually updated. Idempotent — returns false when the server was already deleted. |
EntitlementCheck
EntitlementCheck is one gate in a tool's check chain.
| Field | Type | Label | Description |
|---|---|---|---|
| gate | string | gate names the check: server_status | |
| result | string | result is pass (gate satisfied), fail (gate denies), or conditional (an escalate policy that gates the call at run time via HITL). | |
| detail | string | detail is a short human-readable explanation of this gate's outcome. | |
| policy_id | string | policy_id is the mcp_tool_policies.id for a tool_policy / org_deny_floor gate; empty for the status / binding gates. | |
| cel_summary | string | cel_summary is the whitespace-collapsed, length-capped summary of the matched policy's CEL argument-rule expression; empty for non-policy gates or a policy with no expression. |
GetMCPGroupRequest
GetMCPGroupRequest selects a single group by id.
| Field | Type | Label | Description |
|---|---|---|---|
| group_id | string | group_id is the mcp_proxy_groups.id. |
GetMCPGroupResponse
GetMCPGroupResponse returns a group plus its aggregated tool surface.
| Field | Type | Label | Description |
|---|---|---|---|
| group | MCPProxyGroup | group is the requested group. | |
| aggregated_tools | string | repeated | aggregated_tools is the prefixed ({server}__{tool}) tool surface the proxy would expose for this group's currently-eligible members, sorted — the exact wire tool-list an MCP client sees on initialize. Empty when the group has no eligible members. |
GetMCPServerHealthRequest
GetMCPServerHealthRequest selects a single server by id.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id. |
GetMCPServerHealthResponse
GetMCPServerHealthResponse returns the last observed health snapshot.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id echoes the requested id. | |
| health_status | string | health_status is the stable string bucket. | |
| last_seen | google.protobuf.Timestamp | last_seen is the last healthy-probe timestamp; zero when never healthy. | |
| last_error | string | last_error is the message from the most recent failed probe, empty when the server is healthy. |
GetTeamEntitlementMatrixRequest
GetTeamEntitlementMatrixRequest selects the team to compute the matrix for.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit where unit_type=team) whose effective (server × tool) entitlements to compute. Validated against the caller's tenant — a team in another org resolves NotFound. |
GetTeamEntitlementMatrixResponse
GetTeamEntitlementMatrixResponse carries one ServerEntitlement per MCP server bound to the team.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id echoes the requested team. | |
| servers | ServerEntitlement | repeated | servers is one entry per MCP server bound to the team (any binding state), oldest-registration first — the same ordering ListMCPServers returns. |
| agents | AgentServerEntitlements | repeated | agents is the ADR-0024 D9 per-agent × per-server denial-reason grid: for every agent in the team and every MCP server relevant to the team, the agent's verdict (allowed |
ListMCPGroupsRequest
ListMCPGroupsRequest filters groups by team unit.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit) to list groups under. |
ListMCPGroupsResponse
ListMCPGroupsResponse returns the active groups for a team.
| Field | Type | Label | Description |
|---|---|---|---|
| groups | MCPProxyGroup | repeated | groups is the list of active (non-deleted) groups for the unit, oldest first. Unpaginated — a team is expected to define few groups in v1. |
ListMCPServersRequest
ListMCPServersRequest filters the registry by team.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit) to list servers under. |
ListMCPServersResponse
ListMCPServersResponse returns the active server registrations for a team.
| Field | Type | Label | Description |
|---|---|---|---|
| servers | MCPServer | repeated | servers is the list of active (non-deleted) registrations for the unit. Response is unpaginated — teams are expected to register fewer than ~50 MCP servers in V1. |
ListPendingMCPServerBindingsRequest
ListPendingMCPServerBindingsRequest selects the pending bindings for a team.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the team (org_unit) whose pending approval inbox to list. |
ListPendingMCPServerBindingsResponse
ListPendingMCPServerBindingsResponse returns the pending bindings for a team.
| Field | Type | Label | Description |
|---|---|---|---|
| bindings | MCPServerBinding | repeated | bindings is the list of pending (server, unit) bindings, oldest-first. |
MCPProxyGroup
MCPProxyGroup is the wire form of an mcp_proxy_groups row plus its active member server ids and the derived proxy URL. It is the AdapterMcpGroup shape the F4 client authoring UI (client#512/#519) binds to.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the stable UUID of the group. It is the {id} segment of the proxy URL {public_base}/mcp/group/{id}. | |
| org_id | string | org_id is the owning tenant organisation UUID. | |
| unit_id | string | unit_id is the team (org_unit where unit_type=team) the group is scoped to. | |
| name | string | name is the group display name — unique per org among active groups. | |
| member_server_ids | string | repeated | member_server_ids are the mcp_servers.id values currently attached to the group (active memberships only), sorted for a stable wire order. A member id present here is not necessarily eligible right now — eligibility (server + binding approved, eligible transport) is applied by the proxy resolver at request time and reflected in GetMCPGroup's aggregated_tools. |
| proxy_url | string | proxy_url is the stable client-facing endpoint {public_base}/mcp/group/{id} an MCP client connects to. | |
| created_by | string | created_by is the member id that created the group. | |
| created_at | google.protobuf.Timestamp | created_at is the row creation timestamp. | |
| updated_at | google.protobuf.Timestamp | updated_at is the last mutation timestamp (membership change or rename). |
MCPServer
MCPServer is the wire form of an mcp_servers row.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the stable UUID of the server entry. | |
| org_id | string | org_id is the owning tenant organisation UUID. | |
| unit_id | string | unit_id is the team (org_unit where unit_type=team) the server is registered under. | |
| name | string | name is the display name set by the team admin. | |
| url | string | url is the base URL the MCP client should connect to. | |
| oauth_config_json | string | oauth_config_json is the opaque OAuth configuration bag. Empty when no OAuth is required. Expected to hold only vault references, NOT raw secrets. When returning this field the service REDACTS any embedded raw_* / client_secret / token / password keys as a defence-in-depth measure. | |
| health_status | string | health_status is the latest coarse bucket as a stable string (healthy | |
| last_seen | google.protobuf.Timestamp | last_seen is the timestamp of the most recent successful probe. Zero when the server has never reported healthy. | |
| created_at | google.protobuf.Timestamp | created_at is the row creation timestamp. | |
| updated_at | google.protobuf.Timestamp | updated_at is the last mutation timestamp. | |
| approval_status | string | approval_status is the per-team binding approval gate (ADR-0021 T2): pending | |
| binding_id | string | binding_id is the mcp_server_units.id of the (server, unit) binding this row was read through. Empty on a bare server read. Used as the target of the Approve/Deny binding RPCs. | |
| status | string | status is the lifecycle bucket as a stable string (pending | |
| tool_schema | string | tool_schema is the registration-probe-captured tool surface: a JSON array of {name, description, input_schema} objects serialized as a string. Empty array ([]) until the first successful probe. | |
| transport | string | transport is the observed MCP wire transport captured at probe time (stdio | |
| version | int32 | version is the monotonic per-server schema version, bumped when the captured tool_schema changes. Starts at 1. | |
| min_clearance | int32 | min_clearance is the registry-level clearance floor (ADR-0024 D6, #1991): an agent resolves/calls this server only when its own clearance (agents.clearance, 1..5) is >= this value. 0 (the default) means NO floor. Enforced defence-in-depth at the session-snapshot freeze and the gateway edge. Set/shown by the registry UI (L4 edit floor). |
MCPServerBinding
MCPServerBinding is the wire form of a mcp_server_units row (the MCP↔team binding) joined to its server's display name.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the mcp_server_units.id — the target of Approve/Deny. | |
| org_id | string | org_id is the owning tenant organisation UUID. | |
| server_id | string | server_id is the bound mcp_servers.id. | |
| server_name | string | server_name is the bound server's display name (join convenience). | |
| unit_id | string | unit_id is the team (org_unit) the server is bound to. | |
| approval_status | string | approval_status is pending | |
| requested_by | string | requested_by is the member who registered the binding (the approval request actor). Empty when unknown. | |
| decided_by | string | decided_by is the Manager member who approved/denied. Empty while pending. | |
| decided_at | google.protobuf.Timestamp | decided_at is the decision timestamp. Zero while pending. | |
| decision_reason | string | decision_reason is the optional free-text reason (esp. for denial). | |
| created_at | google.protobuf.Timestamp | created_at is the binding creation timestamp. | |
| visibility | string | visibility is the binding's org-tree reach (ADR-0022 W2 #1739): inherit_down | |
| credential_mode | string | credential_mode is the binding's upstream-credential model (ADR-0022 W2 #1739): team_shared | |
| require_per_agent | bool | require_per_agent asserts the binding MUST be per-agent-credentialled (ADR-0022 W2 #1739). See RegisterMCPServerRequest. | |
| private_upstream_approved | bool | private_upstream_approved is the ADR-0025 (#2019) L5-gated capability that GATES whether Manager approval auto-seeds a vetted private-upstream egress row (allow_private=true, pinned_cidr) so a legitimate in-cluster upstream is reachable through the egress guard. Default false — the SSRF floor (metadata/loopback/link-local) stays denied regardless. | |
| private_pinned_cidr | string | private_pinned_cidr is the CIDR the resolved (and connect-time re-validated) upstream IP must fall inside when private_upstream_approved is set — e.g. a GKE Service CIDR or the beta-dev docker bridge 172.18.0.0/16. Empty when the capability is off. |
RegisterMCPServerRequest
RegisterMCPServerRequest registers a new MCP server under an org_unit.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the org_unit (pillar OR team — ADR-0022 W2) under which the server is registered. MUST reference a unit in the caller's tenant. | |
| name | string | name is a human-readable label (2..64 chars). | |
| url | string | url is the MCP server base URL (must be http:// or https://). | |
| oauth_config_json | string | oauth_config_json is optional; see MCPServer.oauth_config_json for format and secret-handling rules. | |
| visibility | string | visibility governs how far the binding reaches down/across the org tree (ADR-0022 W2 #1739): inherit_down | |
| credential_mode | string | credential_mode governs the upstream-credential model for the binding (ADR-0022 W2 #1739): team_shared | |
| require_per_agent | bool | require_per_agent is a policy assertion that the binding MUST be per-agent-credentialled — the resolver/gateway rejects a team-shared credential for this binding when true. Defaults false. Independent of credential_mode so the intent can be flagged before per-agent credentials are provisioned. | |
| min_clearance | int32 | min_clearance is the registry-level clearance floor (ADR-0024 D6, #1991): 0..5, where 0 (the default when unset) means NO floor. An agent may resolve or call this server only when its own clearance (agents.clearance) is >= this value. A value outside 0..5 is rejected invalid_argument. |
RegisterMCPServerResponse
RegisterMCPServerResponse carries the freshly inserted server row.
| Field | Type | Label | Description |
|---|---|---|---|
| server | MCPServer | server is the newly created registry row. |
RemoveMCPGroupMemberRequest
RemoveMCPGroupMemberRequest detaches a server from a group.
| Field | Type | Label | Description |
|---|---|---|---|
| group_id | string | group_id is the mcp_proxy_groups.id. | |
| server_id | string | server_id is the mcp_servers.id to detach. |
RemoveMCPGroupMemberResponse
RemoveMCPGroupMemberResponse carries the post-detach group.
| Field | Type | Label | Description |
|---|---|---|---|
| group | MCPProxyGroup | group is the group after the member was removed. |
ServerEntitlement
ServerEntitlement is the per-server slice of the matrix: the server's lifecycle + binding gates, its derived identity-rung, and the per-tool verdicts.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id. | |
| server_name | string | server_name is the server's display name. | |
| server_status | string | server_status is the lifecycle bucket (pending | |
| approval_status | string | approval_status is the per-team binding gate (pending | |
| identity_rung | string | identity_rung is the upstream identity mode derived SOLELY from the oauth_config_json type key — one of gateway_identity (oauth2), shared_token (bearer), or anonymous (none / unset). No secret or vault-reference material is ever read or returned. | |
| tools | ToolEntitlement | repeated | tools is one verdict entry per tool on the server's captured schema. Empty when the server has never been probed (tool_schema is []). |
SetMCPServerStatusRequest
SetMCPServerStatusRequest moves a server between approved and disabled.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id to transition. | |
| status | string | status is the target lifecycle state — MUST be 'approved' or 'disabled'. Any other value (including 'pending') is rejected invalid_argument. | |
| reason | string | reason is an optional free-text explanation recorded in the audit row. |
SetMCPServerStatusResponse
SetMCPServerStatusResponse carries the post-transition server row.
| Field | Type | Label | Description |
|---|---|---|---|
| server | MCPServer | server is the server after the status transition (or the unchanged current row on an idempotent no-op). |
SetMCPServerWithholdRequest
SetMCPServerWithholdRequest sets/clears a Manager subtree-withhold on a server for a unit.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id is the org_units.id at which the withhold applies. The caller MUST be this unit's Manager (org_units.manager_member_id). The withhold scopes to this unit and its whole subtree. | |
| server_id | string | server_id is the mcp_servers.id to withhold / un-withhold for the subtree. | |
| withheld | bool | withheld=true sets the withhold (subtracts the server from the subtree); withheld=false clears it (the subtree may again inherit / receive the server through ordinary grants). Idempotent in both directions. | |
| reason | string | reason is an optional free-text explanation recorded in the audit row. |
SetMCPServerWithholdResponse
SetMCPServerWithholdResponse reports the resulting withhold state.
| Field | Type | Label | Description |
|---|---|---|---|
| unit_id | string | unit_id echoes the unit the withhold applies at. | |
| server_id | string | server_id echoes the withheld / un-withheld server. | |
| withheld | bool | withheld is the resulting state (true = an active withhold now exists). |
ToolEntitlement
ToolEntitlement is the verdict for a single (server, tool) pair plus the ordered chain of checks that produced it.
| Field | Type | Label | Description |
|---|---|---|---|
| tool_name | string | tool_name is the tool's name from the server's captured tool_schema. | |
| verdict | string | verdict is the effective outcome: allowed (no gating policy), conditional (an escalate/HITL policy may gate the call at run time), or denied (a gate failed — disabled server, unapproved binding, or a matching deny policy / org deny floor). | |
| check_chain | EntitlementCheck | repeated | check_chain is the ordered list of gates evaluated for this tool, each recording its result and (for policy gates) the matched policy id and a summarised CEL expression. Read top-to-bottom it explains the verdict. |
UpdateMCPServerBindingRequest
UpdateMCPServerBindingRequest edits an existing binding's write-surface knobs. Each field is presence-gated by a paired *_set flag so a caller can change one knob without disturbing the others (proto3 cannot otherwise tell an unset scalar from its zero value). At least one *_set flag MUST be true.
| Field | Type | Label | Description |
|---|---|---|---|
| binding_id | string | binding_id is the mcp_server_units.id to edit. | |
| visibility | string | visibility is the new reach (inherit_down | |
| visibility_set | bool | visibility_set gates whether visibility is applied. | |
| credential_mode | string | credential_mode is the new credential model (team_shared | |
| credential_mode_set | bool | credential_mode_set gates whether credential_mode is applied. | |
| require_per_agent | bool | require_per_agent is the new policy assertion, applied only when require_per_agent_set is true. | |
| require_per_agent_set | bool | require_per_agent_set gates whether require_per_agent is applied. | |
| private_upstream_approved | bool | private_upstream_approved is the ADR-0025 (#2019) vetted private-upstream capability, applied only when private_upstream_approved_set is true. Writing it (true or false) requires L5 clearance — the RPC rejects a lower-clearance caller PermissionDenied. Setting it true REQUIRES private_pinned_cidr to be a valid CIDR (this call or already on the binding) that does NOT overlap the SSRF floor. | |
| private_upstream_approved_set | bool | private_upstream_approved_set gates whether private_upstream_approved is applied. | |
| private_pinned_cidr | string | private_pinned_cidr is the CIDR the approved private upstream must resolve inside (e.g. a GKE Service CIDR or 172.18.0.0/16), applied only when private_pinned_cidr_set is true. Also L5-gated. | |
| private_pinned_cidr_set | bool | private_pinned_cidr_set gates whether private_pinned_cidr is applied. |
UpdateMCPServerBindingResponse
UpdateMCPServerBindingResponse carries the post-edit binding.
| Field | Type | Label | Description |
|---|---|---|---|
| binding | MCPServerBinding | binding is the binding after the edit. |
UpdateMCPServerRequest
UpdateMCPServerRequest edits a registered server's name / url / oauth_config_type. Each field is presence-gated by a paired *_set flag so a caller can change one knob without disturbing the others (proto3 cannot otherwise tell an unset scalar from its zero value). At least one *_set flag MUST be true.
| Field | Type | Label | Description |
|---|---|---|---|
| server_id | string | server_id is the mcp_servers.id to edit. MUST reference a server in the caller's tenant (a server in another org resolves NotFound). | |
| name | string | name is the new human-readable label (2..64 chars), applied only when name_set is true. Rejected already_exists if it collides with another active server name in the org. | |
| name_set | bool | name_set gates whether name is applied. | |
| url | string | url is the new MCP server base URL (must be http:// or https://), applied only when url_set is true. Changing the url triggers a re-probe that refreshes tool_schema + health_status; the egress allow-list still applies (a new host may need founder allow-listing before the probe can reach it, exactly as at registration). | |
| url_set | bool | url_set gates whether url is applied. | |
| oauth_config_type | string | oauth_config_type is the new upstream auth mode — one of none | |
| oauth_config_type_set | bool | oauth_config_type_set gates whether oauth_config_type is applied. | |
| min_clearance | int32 | min_clearance is the new registry clearance floor (ADR-0024 D6, #1991), applied only when min_clearance_set is true. 0..5, where 0 means NO floor. A value outside 0..5 is rejected invalid_argument. | |
| min_clearance_set | bool | min_clearance_set gates whether min_clearance is applied. |
UpdateMCPServerResponse
UpdateMCPServerResponse carries the post-edit server row (oauth_config_json redacted on the way out, as everywhere).
| Field | Type | Label | Description |
|---|---|---|---|
| server | MCPServer | server is the server after the edit. |
DenialReason
DenialReason is the machine-resolved cause a single agent×server cell is denied (ADR-0024 D9). UNSPECIFIED means allowed (no denial). Reasons are evaluated MOST-GLOBAL-FIRST; the first that matches wins, so a server that is both disabled and below the agent's clearance reports ORG_DISABLED.
| Name | Number | Description |
|---|---|---|
| DENIAL_REASON_UNSPECIFIED | 0 | UNSPECIFIED is the proto3 sentinel; it denotes an ALLOWED cell (no gate denied). A denied cell always carries a specific reason below. |
| DENIAL_REASON_ORG_DISABLED | 1 | ORG_DISABLED — mcp_servers.status='disabled' (registry L5 kill-switch, D4a). |
| DENIAL_REASON_NOT_BOUND | 2 | NOT_BOUND — no approved grant binding resolvable to the team (D1): the server is not in the team's grant union (never bound, binding not approved, or a non-inheriting ancestor/sibling binding). |
| DENIAL_REASON_MANAGER_SUBTREE_WITHHELD | 3 | MANAGER_SUBTREE_WITHHELD — a binding_kind='withhold' on an ancestor-or-self unit removes an otherwise-granted server for the team's subtree (D4b). |
| DENIAL_REASON_CLEARANCE_FLOOR | 4 | CLEARANCE_FLOOR — the agent's clearance is below the server's registry min_clearance floor (D6). |
| DENIAL_REASON_CREATION_ALLOWLIST | 5 | CREATION_ALLOWLIST — the server is narrowed out by the agent's per-agent creation-time MCP server allowlist (agent_configurations.tools, #1744/D7). |
HealthStatus
HealthStatus enumerates the coarse bucket of the last probe outcome.
The string-typed health_status field on MCPServer carries the same
value so the message stays forward-compatible with richer status
vocabularies added server-side.
| Name | Number | Description |
|---|---|---|
| HEALTH_STATUS_UNSPECIFIED | 0 | UNSPECIFIED is the default sentinel required by proto3. |
| HEALTH_STATUS_UNKNOWN | 1 | UNKNOWN means no probe has completed yet. |
| HEALTH_STATUS_HEALTHY | 2 | HEALTHY means the last probe returned a successful result. |
| HEALTH_STATUS_DEGRADED | 3 | DEGRADED means the server is reachable but reports partial failure. |
| HEALTH_STATUS_UNREACHABLE | 4 | UNREACHABLE means the last probe failed to connect or timed out. |
MCPServerService
MCPServerService manages the team-scoped MCP server registry.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| RegisterMCPServer | RegisterMCPServerRequest | RegisterMCPServerResponse | RegisterMCPServer creates a new MCP server entry under an org_unit (pillar OR team — ADR-0022 W2 #1739). If oauth_config_json is non-empty it is opaque to the platform — the caller is responsible for ensuring any embedded secret has already been stored in the vault; only a stable reference like "vault://provider_keys/<org>/<unit>/<provider>" should appear in this JSON blob. The service itself NEVER persists raw tokens. |
The attachment's visibility + credential_mode are set on the request (see RegisterMCPServerRequest). unit_id MUST reference a unit in the caller's tenant — a cross-tenant or unknown unit is rejected invalid_argument (IDOR guard).
Requires mcp_servers.manage with ScopeOrgUnit of unit_id. |
| ListMCPServers | ListMCPServersRequest | ListMCPServersResponse | ListMCPServers returns all active MCP server entries for a unit.
Requires mcp_servers.view. |
| DeregisterMCPServer | DeregisterMCPServerRequest | DeregisterMCPServerResponse | DeregisterMCPServer soft-deletes an MCP server entry.
Requires mcp_servers.manage. |
| GetMCPServerHealth | GetMCPServerHealthRequest | GetMCPServerHealthResponse | GetMCPServerHealth returns the last observed health snapshot for a server. Health itself is produced by the MCP middleware / probe loop; this RPC is a read-only view of the stored status.
Requires mcp_servers.view. |
| ListPendingMCPServerBindings | ListPendingMCPServerBindingsRequest | ListPendingMCPServerBindingsResponse | ListPendingMCPServerBindings returns the pending MCP↔team bindings for a unit — the unit Manager's approval inbox (ADR-0021 T2 / decision 8). A binding is created 'pending' by RegisterMCPServer and is INERT (does not resolve into the team's effective config) until approved.
Requires mcp_servers.view. |
| ApproveMCPServerBinding | ApproveMCPServerBindingRequest | ApproveMCPServerBindingResponse | ApproveMCPServerBinding transitions a pending binding to approved. ONLY the unit's Manager (org_units.manager_member_id, Org v2.4 authority) may call this; a non-Manager caller is rejected permission_denied. The transition is terminal — a binding that is already approved/denied returns failed_precondition. Writes an mcp_binding_approved audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| DenyMCPServerBinding | DenyMCPServerBindingRequest | DenyMCPServerBindingResponse | DenyMCPServerBinding transitions a pending binding to denied (with an optional reason). Same Manager-only authority + terminal semantics as ApproveMCPServerBinding. Writes an mcp_binding_denied audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| UpdateMCPServerBinding | UpdateMCPServerBindingRequest | UpdateMCPServerBindingResponse | UpdateMCPServerBinding edits an EXISTING binding's write-surface knobs (ADR-0022 W2 #1739): visibility, credential_mode, require_per_agent. Only the binding unit's Manager (org_units.manager_member_id — the SAME authority as Approve/Deny, no new governance actor) may call it; a non-Manager caller is permission_denied and a unit with no Manager is failed_precondition (fail closed). Every field is optional-by-presence: a *_set companion flag gates whether the paired value is applied, so a caller can change visibility alone without disturbing credential_mode (proto3 cannot distinguish an unset string/bool from its zero value otherwise). The edit is tenant-scoped (RLS) and writes an mcp_binding_updated audit row capturing the before/after of each changed field. The binding's approval_status is untouched — this RPC does not approve/deny.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| SetMCPServerStatus | SetMCPServerStatusRequest | SetMCPServerStatusResponse | SetMCPServerStatus moves a registered server between the 'approved' and 'disabled' lifecycle states (ADR-0021 T1 — the global kill-switch, core#1715). A registered server lands 'approved' (registry-live) but serves ZERO teams until each team's per-unit binding is Manager-approved (the T2 gate is unchanged). 'disabled' drops the server from ALL resolution paths (proxy_upstreams projection + group//mcp/{id} resolvers) regardless of any approved binding — an independent, orthogonal gate — and 'approved' restores it. status ∈ {approved, disabled}; any other value is invalid_argument. Only the owning unit's Manager (org_units.manager_member_id — the SAME authority as ApproveMCPServerBinding, no new governance actor) may call it; a non-Manager caller is permission_denied and a unit with no Manager is failed_precondition (fail closed). Idempotent: setting the current status is a no-op that returns the current row. Writes an mcp_server_{approved,disabled} audit row on an actual transition.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| SetMCPServerWithhold | SetMCPServerWithholdRequest | SetMCPServerWithholdResponse | SetMCPServerWithhold sets or clears a Manager subtree-WITHHOLD on an MCP server for a unit (ADR-0024 D4b, #1992). A withhold is an mcp_server_units row with binding_kind='withhold' keyed to (unit, server); it SUBTRACTS that server from the effective MCP set of the unit AND every unit in the unit's subtree — the resolver's mcpHomeForUnits subtract-after-union pass removes any server withheld on a unit that is ancestor-or-self of the resolving team, so the reach is exactly the subtree (a sibling subtree / the org root are untouched). The withhold WINS over any inherited or subtree-direct grant. This is the Manager-scoped complement to the L5 org-wide kill-switch (SetMCPServerStatus, D4a) — a Manager may withhold within their own subtree, the root Manager reaches org-wide. withheld=true sets it (idempotent); withheld=false clears it. Only the unit's Manager (org_units.manager_member_id) may call it; a non-Manager is permission_denied and a unit with no Manager is failed_precondition (fail closed). Writes an mcp_subtree_withheld audit row on both set and clear.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| UpdateMCPServer | UpdateMCPServerRequest | UpdateMCPServerResponse | UpdateMCPServer edits a REGISTERED server's name / url / oauth_config_type (#1775) — the edit path that previously required a deregister + re-register to fix a typo'd URL, name, or auth mode. Every field is presence-gated by a paired *_set flag so a caller can change one without disturbing the others (proto3 cannot distinguish an unset scalar from its zero value otherwise); at least one *_set MUST be true. Tenant-scoped: the server must belong to the caller's org (a server in another org resolves NotFound). Requires L4 clearance (the same tenant-edit floor as setting an upstream credential).
Changing the url RE-PROBES the upstream through the SAME egress-guarded probe path registration uses, refreshing tool_schema + health_status. The egress allow-list still applies — a new host may need founder allow-listing before the probe can reach it (health lands 'unreachable' until then), exactly as at registration. Changing oauth_config_type writes ONLY the bag's type key — NEVER a raw secret; switching to a mode that needs a fresh secret leaves the vault-ref empty for SetMCPServerCredential (+ the #1774 backfill) to re-wire, while an unchanged type preserves the existing ref. Bindings are untouched. Writes an mcp_server_updated audit row capturing the before/after of each changed field (no secrets).
Requires mcp_servers.manage + L4 clearance (service-layer check). |
| CreateMCPGroup | CreateMCPGroupRequest | CreateMCPGroupResponse | CreateMCPGroup creates a group aggregating N MCP servers behind ONE /mcp/group/{id} endpoint, scoped to a team unit. Every member_server_id must be server-approved (T1) AND binding-approved (T2) for the unit and on an eligible transport (streamable_http); an ineligible member is rejected invalid_argument / failed_precondition. The requested member set is run through the SAME mcpproxy.ValidateMembers gate the proxy edge applies, so a group the RPC accepts is one the proxy will serve (no hard tool-name conflicts, no duplicate server names). Only the unit's Manager (org_units.manager_member_id) may create a group. Writes an mcp_group_created audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| ListMCPGroups | ListMCPGroupsRequest | ListMCPGroupsResponse | ListMCPGroups returns the active groups for a team unit (RLS-scoped to the caller's org, migration 131). Each group carries its member server ids + the stable proxy URL.
Requires mcp_servers.view. |
| GetMCPGroup | GetMCPGroupRequest | GetMCPGroupResponse | GetMCPGroup returns a single group with its member ids, the stable /mcp/group/{id} proxy URL, and the AGGREGATED (prefixed {server}__{tool}) tool surface the proxy would expose — computed from the currently-eligible members via the SAME mcpproxy.Aggregate the edge uses.
Requires mcp_servers.view. |
| DeleteMCPGroup | DeleteMCPGroupRequest | DeleteMCPGroupResponse | DeleteMCPGroup soft-deletes a group (idempotent). Only the unit's Manager may delete it. Writes an mcp_group_deleted audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| AddMCPGroupMember | AddMCPGroupMemberRequest | AddMCPGroupMemberResponse | AddMCPGroupMember attaches an MCP server to an existing group. The server must be eligible (as CreateMCPGroup) and the resulting member set must pass ValidateMembers. Only the unit's Manager may change membership. Writes an mcp_group_member_added audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| RemoveMCPGroupMember | RemoveMCPGroupMemberRequest | RemoveMCPGroupMemberResponse | RemoveMCPGroupMember detaches an MCP server from a group (idempotent). Only the unit's Manager may change membership. Writes an mcp_group_member_removed audit row.
Requires mcp_servers.manage + Manager identity (service-layer check). |
| GetTeamEntitlementMatrix | GetTeamEntitlementMatrixRequest | GetTeamEntitlementMatrixResponse | GetTeamEntitlementMatrix computes, live and read-only, the (team × server × tool) entitlement matrix for a team unit: for every MCP server bound to the team and every tool on that server's captured schema, a verdict (allowed | conditional | denied) plus the CHECK CHAIN that explains it — the server lifecycle-status gate (ADR-0021 T1 kill-switch), the per-team binding-approval gate (T2), the matching tool policies with their CEL expression summarised, and the org-wide deny floor. Each server also carries its identity-rung derived from the upstream auth mode (oauth2 → gateway_identity, bearer → shared_token, none → anonymous).
Sources: mcp_servers (status, tool_schema, oauth_config_json TYPE ONLY — NEVER any secret / vault-ref material), mcp_server_units (binding approval), mcp_tool_policies. Tool-policy precedence is NOT reimplemented here — it is computed by the SAME internal/guardrails resolve query used at the tool-call gate (deny outranks escalate across the whole team home-chain cascade), so the matrix and the live gate can never disagree.
Read-only (no writes). Requires L2+ clearance; the team is validated against the caller's tenant (a team in another org resolves NotFound). The response also carries the ADR-0024 D9 per-agent × per-server denial-reason grid (agents): each cell is an agent's verdict for a server plus the machine-resolved reason when denied, evaluated most-global-first, reusing the SAME resolver / clearance / allowlist predicates the runtime enforces (#1993).
Requires mcp_servers.view. |
Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | double | float | Float | |
| float | float | float | float | float32 | float | float | Float | |
| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| bool | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | |
| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |