# Authentication & access Source: https://docs.mcpmanager.ai/admin-api/authentication The MCP Manager Admin API access model: Personal Access Tokens (mcpm_pat_, SHA-256 hashed at rest, 1–90 day TTL) and OAuth 2.1 with RFC 8707 audience binding, the ff-mcpm-admin entitlement, per-operation capability gating, and the HTTP status codes the API returns on denied, malformed, and failed requests. Every Admin API request carries a bearer credential and passes three checks in order: the workspace **entitlement**, then the credential's **authenticity**, then the operation's **capability**. This page is the reference for each credential type, the entitlement, capability gating, and the status codes the API returns. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## Credential types The Admin API accepts a bearer token in the `Authorization` header on both the MCP and REST surfaces. The token is either a **Personal Access Token** or an **OAuth 2.1** access token; the server tells them apart by prefix — a credential beginning with `mcpm_pat_` is verified as a Personal Access Token, and anything else is verified as an OAuth JWT. ```http theme={null} Authorization: Bearer mcpm_pat_ ``` ### Personal Access Tokens A Personal Access Token (PAT) is a long-lived credential you mint for a headless agent or pipeline. Create, list, and revoke them at [MCP & API → Tokens](https://app.mcpmanager.ai/settings/tokens/tokens) or with the `create_access_token` / `list_access_tokens` / `revoke_access_token` operations. | Property | Value | | --------------------- | ------------------------------------------------------------------------------------------------------------ | | Format | `mcpm_pat_` followed by 64 hexadecimal characters (32 random bytes) | | Storage at rest | Only the token's **SHA-256 hash** is persisted; the secret is shown once at creation and cannot be recovered | | Default lifetime | **90 days** | | Configurable lifetime | **1–90 days**, or **0** to never expire | | Displayed identifier | A non-secret prefix (`mcpm_pat_` plus the first 4 characters) and your label | | Scope | Acts as **you** — carries your user, role, and capabilities | The plaintext secret is returned **once**, at creation. Because only its SHA-256 hash is stored, MCP Manager cannot show it again — losing it means minting a new token. Each token's **last-used** time is tracked (best-effort, updated at most once every 5 minutes) so you can spot dormant tokens. **Creating a token is idempotent by label.** If you already hold a valid token with the same label, `create_access_token` returns that token's metadata with `alreadyExisted: true` and `token: null` — the existing secret is never re-shown — rather than minting a duplicate. Choose a new label when you genuinely need a fresh secret. This lets a provisioning pipeline re-run safely without accumulating duplicate tokens. **Revocation is immediate and scoped to you.** Revoking a token stops it working on the next call and removes it from your list. You can only revoke your **own** tokens; a request to revoke a token you don't own returns `404`. ### OAuth 2.1 access tokens Interactive MCP clients can authenticate with **OAuth 2.1** instead of a Personal Access Token. The admin MCP server is an OAuth protected resource: a client that lacks a credential receives a `401` whose `WWW-Authenticate` header advertises the protected-resource metadata document per **[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)** (OAuth 2.0 Protected Resource Metadata), and the client then runs the authorization flow to obtain a bearer. Access tokens are **audience-bound** to the admin MCP resource following **[RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)** (Resource Indicators for OAuth 2.0): the token's `aud` claim must include the resource `${ROOT_URL}/mcpm-admin/mcp`. A token minted for a different resource is rejected, so an OAuth token issued for one endpoint cannot be replayed against the Admin API. The token's `sub` claim carries the resolved MCP Manager identity, which supplies the user, role, and capabilities for the request. ## The MCP Manager Admin API entitlement Access to the entire Admin API — every tool and endpoint, and the **Settings → MCP & API** area in the app — is gated by the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`) on your workspace. This is a plan-level feature, not a role capability, so it is checked before anything else on every request. * A workspace **without** the entitlement receives `403 Forbidden` — *"This organization is not entitled to the MCP & API configuration layer."* * If the entitlement check itself can't be completed (an upstream lookup fails), the API returns `503 Service Unavailable` — *"Entitlement check failed"* — rather than silently allowing or denying the call. The check fails loud. If you can reach the app but the **MCP & API** settings area is missing, your workspace isn't in the beta. Access depends on the entitlement, not on any role — ask your MCP Manager contact to enable it. ## Capability gating Once a request is entitled and authenticated, each operation enforces the **same capability** as the equivalent action in the app. The Admin API never widens what your role permits: if you couldn't do something by hand in the UI, the corresponding tool or endpoint refuses it. * Most operations require a specific capability — for example, creating a server requires **Basic server management**, querying logs requires **View and export logs**, and editing a role requires **Manage roles**. * A few operations require **no** capability and are available to any authenticated caller: `whoami`, `list_capabilities`, and managing your **own** access tokens (`create_access_token`, `list_access_tokens`, `revoke_access_token`). * A call you lack the capability for returns `403 Forbidden` — *"Capability '\' is required."* Call `list_capabilities` to retrieve every capability key, grouped as it appears in the product, with a label and description — useful before building a role's grants with `create_role` or `modify_role`. Each entry in the [tool & endpoint reference](/admin-api/reference/overview) names the capability it requires. For what each capability allows, see the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## Response status codes The REST surface returns standard HTTP status codes; the MCP surface surfaces the same failures as tool errors. Every response carries a correlation id you can quote when asking support to trace a request. | Status | Meaning | Typical cause | | ------ | --------------------- | ------------------------------------------------------------------------------------------------------- | | `200` | Success | Read or update completed (some creates return `201`) | | `201` | Created | A new resource was created (for example, an access token) | | `400` | Bad Request | Input failed schema validation — the message names the offending field | | `401` | Unauthorized | Missing or invalid bearer credential, or an OAuth token whose audience doesn't match the admin resource | | `403` | Forbidden | The workspace lacks the entitlement, or your role lacks the required capability | | `404` | Not Found | The target resource doesn't exist, or isn't yours (for example, another user's token) | | `500` | Internal Server Error | An unexpected failure; the detail is logged under the correlation id, not returned | | `503` | Service Unavailable | The entitlement check couldn't be completed | ## What is and isn't exposed The Admin API is deliberate about secrets. These properties hold across every operation: * **Token secrets are write-only.** A Personal Access Token's secret is shown once and stored only as a SHA-256 hash. * **Credential and integration header values are write-only.** Identity header tokens and OpenTelemetry collector headers are stored encrypted; read operations return header **names only**, never their values. * **Alert debug context is scrubbed at write time.** Sensitive keys — `authorization`, `cookie`, `set-cookie`, `access_token`, `refresh_token`, `client_secret`, `password`, and similar — are replaced with `[REDACTED]` before an alert is stored, so `get_alert` never surfaces a live secret. ## Further reading Every operation with the capability it enforces and its MCP tool name and REST route. Create a token and register the admin MCP server in your client. What each capability allows, grouped exactly as the Admin API groups them. How MCP Manager stores and brokers the credentials behind every identity. ## External sources The audience-binding standard the admin server enforces on OAuth access tokens. The metadata document the `401` challenge advertises so clients can discover the authorization server. # Connect an agent to the Admin MCP server Source: https://docs.mcpmanager.ai/admin-api/connect How to create an MCP Manager admin access token and connect an agent or REST client to the admin MCP server: generate a Personal Access Token in Settings → MCP & API, copy the Connect URL, add the server to Claude or Cursor (or call the REST twin with curl), and confirm access with whoami. This page connects an agent (or a REST client) to the MCP Manager Admin API. By the end you will have an admin access token, the admin MCP server registered in your client, and a successful `whoami` call confirming which capabilities you can exercise. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## Prerequisites * Your workspace has the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`). If you don't see **Settings → MCP & API**, it isn't enabled yet — ask your MCP Manager contact to join the beta. * An MCP-capable client (for example Claude Code or Cursor), or any HTTP client for the REST twin. ## Create an admin access token An **admin Personal Access Token** authenticates every call to the Admin API. Create one from the app, then copy it into your client. The token secret is shown **once**. Go to [MCP & API → Tokens](https://app.mcpmanager.ai/settings/tokens/tokens) and select **Create token**. Give the token a **Label** you'll recognise later (for example "Claude Desktop" or "CI pipeline"). Leave **Expires in (days)** blank for the 90-day default, enter a value from **1 to 90**, or enter **0** for a token that never expires. Creating a token is **idempotent by label**: if a valid token with the same label already exists, MCP Manager returns that token's details instead of minting a duplicate — but it cannot re-show the secret. Use a new label when you need a fresh secret. The result panel shows two values. Copy the **Connect URL** (your workspace's admin MCP endpoint) and the **Access token** (it starts with `mcpm_pat_`). Store the token in a secret manager now — it will not be viewable again after you close the page. Treat the access token like a password. It carries your full identity and capabilities against the Admin API. If it leaks, revoke it immediately from the same page (**Revoke**). ## Register the admin MCP server in your client Add the server to your MCP client using the **Connect URL** as the endpoint and the token as a bearer credential. Replace `` with the Connect URL you copied and `mcpm_pat_...` with your token. ```bash terminal theme={null} claude mcp add --transport http mcpm-admin \ --header "Authorization: Bearer mcpm_pat_..." ``` Add the server to your `mcp.json`: ```json mcp.json theme={null} { "mcpServers": { "mcpm-admin": { "url": "", "headers": { "Authorization": "Bearer mcpm_pat_..." } } } } ``` The REST twin lives under `/api/v1/mcpm-admin` on the same host as your Connect URL. Send the token as a bearer credential: ```bash terminal theme={null} curl -H "Authorization: Bearer mcpm_pat_..." \ https:///api/v1/mcpm-admin/whoami ``` The admin MCP server registers under the name **MCP Manager Admin**. Its `initialize` response carries instructions telling the agent to route any "connect X" request **through your gateway** rather than wiring the client straight to the provider, and points the agent at the docs MCP server and `llms.txt` for reference. ## Confirm access with `whoami` Call the `whoami` tool (or `GET /api/v1/mcpm-admin/whoami`) as your first request. It needs no parameters and no special capability, and it returns your resolved user, organization, team, and role — plus the **capability keys your role grants**, which are exactly the operations you're allowed to perform. A successful `whoami` response lists your capability keys. Use it to discover what you can do before attempting a gated write — a call you lack the capability for is refused with a `403`. Ask your agent to call the `whoami` tool, or invoke it directly: ```json theme={null} {} ``` ```bash terminal theme={null} curl -H "Authorization: Bearer mcpm_pat_..." \ https:///api/v1/mcpm-admin/whoami ``` ## Prefer OAuth over a long-lived token? Interactive MCP clients that support OAuth can connect without a Personal Access Token: the admin server is an OAuth 2.1 protected resource, and a client that discovers it will run the authorization flow and obtain a short-lived bearer automatically. A token minted this way is **audience-bound** to the admin MCP resource, so it can't be replayed against another endpoint. Personal Access Tokens remain the right choice for headless agents and pipelines. See [Authentication & access](/admin-api/authentication) for both paths. ## Further reading Token format and lifetime, OAuth audience binding, the entitlement, and capability gating. Every tool and endpoint, grouped by domain, with parameters and required capabilities. What the control-plane surface is and how MCP tools and the REST twin relate. What each capability `whoami` reports actually allows. # Admin API & MCP Server Source: https://docs.mcpmanager.ai/admin-api/overview What the MCP Manager Admin API and MCP server are: a control-plane surface that lets an agent or script manage your MCP Manager configuration — servers, gateways, identities, hosts, teams, roles, logs, and integrations — with the same actions as the app, scoped by your role capabilities, over MCP tools and a REST twin. The **MCP Manager Admin API and MCP server** let an agent or a script administer your MCP Manager workspace programmatically — create and configure servers, gateways, identities, hosts, teams, roles, access tokens, and log/alert queries — instead of clicking through the app. Every action runs **as you**, enforcing the **same role capabilities** as the product UI: a caller can do exactly what its user could do by hand, and nothing more. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## What the Admin API lets you do The Admin API is the **control plane** for MCP Manager: it manages the configuration itself, as opposed to the **data plane** where AI clients call tools through a gateway. With it you can, from code or an agent: * Create, rename, enable/disable, and delete **inbound MCP servers**, and manage their **identities** (credentials). * Create and configure **gateways**, assign servers to them with an identity scheme, provision them to **teams**, and issue gateway tokens. * Manage **hosts** and their **connections** — including inspecting a connection's last successful call and recent errors. * Manage **people**: invite and deactivate users, create and edit **roles** and their capabilities, and manage **teams**. * Query the **MCP call logs** and **alerts**, and configure **OpenTelemetry** log/trace forwarding. * Mint, list, and revoke your own **admin access tokens**. Every operation is **capability-gated**. A call you don't have the capability for is refused — the Admin API never widens what your role permits. This is the same model the app uses, so the boundary an administrator already configured holds for agents automatically. See [Authentication & access](/admin-api/authentication) for how gating and errors work. ## One manifest, two surfaces: MCP tools and a REST twin Every operation is defined once and exposed two ways, so both surfaces stay identical: * **MCP tools (primary).** Connect an MCP client — Claude, Cursor, or your own agent — to the admin MCP endpoint and call tools like `create_inbound_server`, `assign_server_to_gateway`, or `query_logs`. The server advertises instructions and per-tool annotations (read-only, destructive, idempotent hints) so an agent selects the right tool. * **REST API (secondary).** The same operations are available as HTTP endpoints under `/api/v1/mcpm-admin` for pipelines and infrastructure code that don't speak MCP. The MCP endpoint path is `/mcpm-admin/mcp`; the exact **Connect URL** for your workspace is shown when you create an access token (see [Connect an agent](/admin-api/connect)). Because both surfaces are generated from a single definition, the [tool & endpoint reference](/admin-api/reference/overview) documents each operation once, with its MCP tool name and REST route side by side. ## Admin access tokens are not gateway API tokens MCP Manager has two kinds of access token, and they are not interchangeable — using the wrong one is the most common setup mistake. | Token | Prefix | Grants access to | Gated by | | ------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | **Admin Personal Access Token** | `mcpm_pat_` | The Admin API / MCP server (this section) — the control plane | The **MCP Manager Admin API** entitlement (`ff-mcpm-admin`) | | **Gateway API access token** | — | A single gateway connection — the data plane, for [headless agents](/features/api-tokens-and-headless-agents) | The **Create and manage API tokens** capability | An **admin Personal Access Token** (`mcpm_pat_…`) authenticates to the Admin API to *manage* your MCP Manager setup. A **gateway API access token** lets a headless agent *use* a gateway to reach downstream MCP servers. If you want an agent to call tools through a gateway, you need a gateway token, not an admin token — see [API Tokens & Headless Agents](/features/api-tokens-and-headless-agents). ## Where to go next Create an admin access token and wire the admin MCP server into Claude, Cursor, or a REST client. Access tokens, OAuth audience binding, the entitlement, capability gating, and error responses. Every operation, with its MCP tool name, REST route, parameters, and required capability. Reporting, gateway rules, feature provisioning, an audit log, and a downloadable CLI — available soon. ## Further reading The step-by-step path from a fresh token to your first `whoami` call. How the Admin API fits alongside the token-based agent connection that ships today. The full list of capabilities the Admin API enforces on every call. The docs MCP server and `llms.txt` the admin server points agents to. # Account & Access Tokens Source: https://docs.mcpmanager.ai/admin-api/reference/account Admin API operations for confirming who you are and what your role permits, and for creating, listing, and revoking your own MCP Manager admin Personal Access Tokens. These operations confirm your identity and capabilities and manage your own admin access tokens. They are available to any authenticated caller — no extra capability is required. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## Who Am I Read-only Return the authenticated identity and granted capabilities. Returns the resolved user, organization, team, role, and the capability keys the caller's role grants — i.e. exactly which configuration actions are permitted. Useful as a first call to confirm authentication and discover available actions. **MCP tool:** `whoami` · **REST:** `GET /api/v1/mcpm-admin/whoami` · **Capability:** none — available to any authenticated caller **Parameters:** none. **Example** ```json theme={null} {} ``` ## Create Access Token Create a Personal Access Token for the authenticated user. Mints a new Personal Access Token. The plaintext token is returned **once** — store it securely; only its SHA-256 hash is persisted. Use it as `Authorization: Bearer `. Choose an expiry up to 90 days (the default), or 0 to never expire; revoke anytime. Idempotent by label: if you already have a token with this label, the existing one is returned with `alreadyExisted: true` and `token: null` (its secret cannot be re-shown) rather than minting a duplicate — pick a new label if you need a fresh secret. **MCP tool:** `create_access_token` · **REST:** `POST /api/v1/mcpm-admin/tokens` · **Capability:** none — available to any authenticated caller **Parameters** A human-readable name to identify this token later (e.g. "Claude Desktop", "CI pipeline"). Days until the token expires (1–90). Omit for the 90-day default; use 0 for a token that never expires. **Example** ```json theme={null} {"displayLabel":"CI pipeline"} ``` ## List Access Tokens Read-only List the authenticated user's access tokens. Returns token metadata (label, a recognisable non-secret prefix, last-used time). Secrets are never returned. **MCP tool:** `list_access_tokens` · **REST:** `GET /api/v1/mcpm-admin/tokens` · **Capability:** none — available to any authenticated caller **Parameters:** none. **Example** ```json theme={null} {} ``` ## Revoke Access Token Destructive Revoke one of the authenticated user's access tokens. Soft-deletes the token by id. Scoped to the caller — you cannot revoke another user's token. **MCP tool:** `revoke_access_token` · **REST:** `DELETE /api/v1/mcpm-admin/tokens/:id` · **Capability:** none — available to any authenticated caller **Parameters** The id of the token to revoke (from list\_access\_tokens). **Example** ```json theme={null} {"id":"CAT-..."} ``` ## List Capabilities Read-only List the full set of MCP Manager capabilities, grouped as in the product. Returns every capability key, grouped as it appears in the product (Identities, Servers, Gateways, Hosts, People, Workspace settings, Logging, Alerting, Reporting, Integrations, and Partner Portal), each with a label and description. Use this to discover the valid capability keys — and what each grants — before building a role's `grants` map with create\_role or modify\_role. Partner Portal capabilities only apply to partner-type workspaces. **MCP tool:** `list_capabilities` · **REST:** `GET /api/v1/mcpm-admin/capabilities` · **Capability:** none — available to any authenticated caller **Parameters:** none. **Example** ```json theme={null} {} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Gateways Source: https://docs.mcpmanager.ai/admin-api/reference/gateways Admin API operations for gateways: create, rename, enable, disable, archive, and issue tokens; assign servers with an identity scheme and manage those assignments; and provision gateways to teams. These operations manage gateways, the server assignments on them, and team provisioning. Because the same server can be assigned to a gateway more than once with different schemes, assignment operations target a specific assignment, not just the server. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## List Gateways Read-only List the caller's outbound gateways. Returns each accessible gateway as `{ guid, name, enabled, url }`. The `url` is the public, user-facing connect URL for the gateway (derived, not stored). The set is scoped to the gateways the caller's role and team membership permit, exactly like the gateways overview page. **MCP tool:** `list_gateways` · **REST:** `GET /api/v1/mcpm-admin/gateways` · **Capability:** View and use all gateways (`viewAllGateways`) **Parameters:** none. **Example** ```json theme={null} {} ``` ## Create Gateway Create a new outbound gateway. Creates a gateway with the given name, enabled by default, owned by the caller (a `creator` edge to the calling user). Returns the created gateway as `{ guid, name, enabled, url }`. Assigning the gateway to teams (provisioning) is a separate capability and is not performed here. **MCP tool:** `create_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** A human-readable name for the new gateway (e.g. "Engineering Gateway"). **Example** ```json theme={null} {"name":"Engineering Gateway"} ``` ## Get Gateway Read-only Fetch a single gateway, including its connect URL. Returns the gateway as `{ guid, name, enabled, url }`. The `url` is the public, user-facing connect URL (derived from the gateway guid, not stored on the object). **MCP tool:** `get_gateway` · **REST:** `GET /api/v1/mcpm-admin/gateways/:gatewayGuid` · **Capability:** View and use all gateways (`viewAllGateways`) **Parameters** The guid of the gateway to fetch (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Rename Gateway Change a gateway's name. Renames the gateway to `newName`. The gateway is only saved when the name actually changes. Returns the updated gateway as `{ guid, name, enabled, url }`. **MCP tool:** `rename_gateway` · **REST:** `PATCH /api/v1/mcpm-admin/gateways/:gatewayGuid/name` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the gateway to rename (from list\_gateways). The new name for the gateway. **Example** ```json theme={null} {"gatewayGuid":"MOG-...","newName":"Renamed Gateway"} ``` ## Enable Gateway Enable a gateway. Sets the gateway to enabled. Only saves when the state actually changes. Returns the updated gateway as `{ guid, name, enabled, url }`. **MCP tool:** `enable_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/enable` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the gateway to enable (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Disable Gateway Disable a gateway. Sets the gateway to disabled. Only saves when the state actually changes. Returns the updated gateway as `{ guid, name, enabled, url }`. **MCP tool:** `disable_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/disable` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the gateway to disable (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Issue Gateway Token Issue an API token for the caller on a specific gateway. Provisions a per-user connection to the gateway and mints an API token the caller can use to call it. Returns `{ token, tokenId, gatewayGuid, connectionGuid, url }`. The `token` is the plaintext credential — send it to the gateway `url` as the access token; it is returned **once**, so store it securely. The token is always issued for the authenticated caller. Revoke it later by deleting the underlying token (tokenId). If the gateway has any server whose assignment uses the per-user identity scheme ('userIdentity'), pass `serverIdentities` mapping each such inbound server guid to the identity guid to connect with; issuing fails (listing the servers) until each is supplied. **MCP tool:** `issue_gateway_token` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/tokens` · **Capability:** Create and manage API tokens (`createAndManageApiTokens`) **Parameters** The guid of the gateway to issue a token for (from list\_gateways). Map of inbound server guid -> credential identity guid. REQUIRED for every server on this gateway whose assignment uses identityScheme:'userIdentity' (each user brings their own credential). Find which servers need one (and their guids) via list\_gateway\_assignments, and the identity guids via list\_identities (or create one with add\_identity). Omit when the gateway has no userIdentity servers — issuing without a required selection fails fast rather than minting a token that errors at request time. *(a string → string map)* **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## List Gateway Assignments Read-only List the servers assigned to a gateway. Returns each assignment as `{ guid, gatewayGuid, serverGuids, enabled }`. An assignment is the join object that makes a server reachable through the gateway; `serverGuids` are the inbound servers it exposes. Use the `guid` to remove an assignment. **MCP tool:** `list_gateway_assignments` · **REST:** `GET /api/v1/mcpm-admin/gateways/:gatewayGuid/assignments` · **Capability:** View and use all gateways (`viewAllGateways`) **Parameters** The guid of the gateway whose server assignments to list (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Assign Server to Gateway Assign an inbound server to a gateway, making it reachable through it. Creates an `McpGatewayAssignment` (enabled, all features allowed) wiring the server to the gateway — the link that exposes the server through the gateway. Selects the per-assignment identity: `userIdentity` (each user brings their own) or `sharedIdentity` (one shared credential, via `sharedIdentityGuid`). Returns `{ guid, gatewayGuid, serverGuids, enabled, configured, configurationHint? }`. **Check `configured`**: when false the assignment exists but is not yet usable (the server requires auth and has no identity) — `configurationHint` says exactly how to finish; relay it to the user and offer to complete the setup once an identity is available. A just-created OAuth server has no identity until its browser authorization is approved, so prefer `userIdentity` there (or finish the OAuth flow, then re-assign). **MCP tool:** `assign_server_to_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/assignments` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the gateway to assign the server to (from list\_gateways). The guid of the inbound server to assign (from list\_inbound\_servers). Which credential identity the gateway uses for this server. For a server that requires authentication, SET this so the assignment is usable: 'userIdentity' (each connecting user supplies their own credential — recommended for per-user services like Atlassian/GitHub) makes the assignment complete on its own; 'sharedIdentity' (one shared credential for everyone) also requires `sharedIdentityGuid` (call list\_identities first). Omitting it leaves the assignment in a "Needs configuration" state — usable only for `open` servers that need no auth. One of: `sharedIdentity`, `userIdentity`. The guid of the shared identity to use (from list\_identities). Required when `identityScheme` is 'sharedIdentity'. **Example** ```json theme={null} {"gatewayGuid":"MOG-...","inboundServerGuid":"MIS-..."} ``` ## Remove Gateway Assignment Destructive Remove a server→gateway assignment. Deletes the assignment, severing the server’s reachability through that gateway. Returns `{ removed: true, assignmentGuid }`. **MCP tool:** `remove_gateway_assignment` · **REST:** `DELETE /api/v1/mcpm-admin/assignments/:assignmentGuid` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the assignment to remove (from list\_gateway\_assignments). **Example** ```json theme={null} {"assignmentGuid":"MGA-..."} ``` ## Modify Gateway Assignment Change an existing assignment's identity scheme. Reconfigures an existing server→gateway assignment (targeted by its guid, since a server can be assigned more than once): switch its identity scheme between 'userIdentity' and 'sharedIdentity', re-pointing or clearing the shared-identity selection to match. Returns the updated assignment summary. To enable/disable the assignment use enable\_gateway\_assignment / disable\_gateway\_assignment. (The manual token/endpoint scheme is not yet supported.) **MCP tool:** `modify_gateway_assignment` · **REST:** `PATCH /api/v1/mcpm-admin/assignments/:assignmentGuid` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the assignment to modify (from list\_gateway\_assignments). The identity scheme to set: 'userIdentity' (each user brings their own credential) or 'sharedIdentity' (one shared credential, requires sharedIdentityGuid). Applies to the server(s) in this assignment. One of: `sharedIdentity`, `userIdentity`. The shared credential identity to use (from list\_identities). Required when identityScheme is 'sharedIdentity'; supplying it alone implies that scheme. Ignored for 'userIdentity'. **Example** ```json theme={null} {"assignmentGuid":"MGA-...","identityScheme":"sharedIdentity","sharedIdentityGuid":"MISI-..."} ``` ## Enable Gateway Assignment Enable a server→gateway assignment. Enables the assignment so its server is reachable through the gateway. Saved only when the state changes. Returns the assignment summary. **MCP tool:** `enable_gateway_assignment` · **REST:** `POST /api/v1/mcpm-admin/assignments/:assignmentGuid/enable` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the assignment to enable (from list\_gateway\_assignments). **Example** ```json theme={null} {"assignmentGuid":"MGA-..."} ``` ## Disable Gateway Assignment Disable a server→gateway assignment. Disables the assignment so its server is no longer reachable through the gateway (the assignment stays, the proxy skips it). Saved only when the state changes. Returns the assignment summary. **MCP tool:** `disable_gateway_assignment` · **REST:** `POST /api/v1/mcpm-admin/assignments/:assignmentGuid/disable` · **Capability:** Basic gateway management (`basicGatewayManagement`) **Parameters** The guid of the assignment to disable (from list\_gateway\_assignments). **Example** ```json theme={null} {"assignmentGuid":"MGA-..."} ``` ## Archive Gateway Archive a gateway (and disable it). Archives the gateway and disables it (an archived gateway is never left enabled), hiding it from the active gateways list. There is no hard delete for gateways — archiving is the delete equivalent, and it is reversible with unarchive\_gateway. Returns `{ guid, name, enabled, url, archived }`. **MCP tool:** `archive_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/archive` · **Capability:** Archive and view archived gateways (`archiveGateway`) **Parameters** The guid of the gateway to archive (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Unarchive Gateway Unarchive a previously archived gateway. Clears the archived flag. The gateway stays disabled (unarchiving does not re-enable it) — call enable\_gateway to bring it back online. Returns `{ guid, name, enabled, url, archived }`. **MCP tool:** `unarchive_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/unarchive` · **Capability:** Archive and view archived gateways (`archiveGateway`) **Parameters** The guid of the gateway to unarchive (from list\_gateways). **Example** ```json theme={null} {"gatewayGuid":"MOG-..."} ``` ## Provision Gateway to Team Provision a gateway to a team (grant the team’s members access). Wires a `provisionedGateway` edge from the team to the gateway, so the team’s members can use it. Idempotent. Returns `{ assigned: true, gatewayGuid, teamGuid }`. **MCP tool:** `assign_team_to_gateway` · **REST:** `POST /api/v1/mcpm-admin/gateways/:gatewayGuid/teams/:teamGuid` · **Capability:** Manage team-gateway provisioning (`manageTeamGatewayProvisioning`) **Parameters** The guid of the gateway to provision (from list\_gateways). The guid of the team to grant access (from list\_teams). **Example** ```json theme={null} {"gatewayGuid":"MOG-...","teamGuid":"WST-..."} ``` ## Deprovision Gateway from Team Destructive Remove a gateway’s provisioning from a team. Deletes the team→gateway `provisionedGateway` edge. A no-op (`removed: false`) when the team was not provisioned to the gateway. **MCP tool:** `remove_team_from_gateway` · **REST:** `DELETE /api/v1/mcpm-admin/gateways/:gatewayGuid/teams/:teamGuid` · **Capability:** Manage team-gateway provisioning (`manageTeamGatewayProvisioning`) **Parameters** The guid of the gateway to deprovision (from list\_gateways). The guid of the team to remove access from (from list\_teams). **Example** ```json theme={null} {"gatewayGuid":"MOG-...","teamGuid":"WST-..."} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Hosts Source: https://docs.mcpmanager.ai/admin-api/reference/hosts Admin API operations for hosts and their gateway connections: create, rename, enable, disable, and delete hosts, and list, inspect, enable, and disable the connections between a host and a gateway. These operations manage hosts (the apps and agents that connect to gateways) and their connections. Inspecting a single connection returns its last successful tool call and recent errors, so you can debug a failing connection without correlating separate log and alert queries. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## List Hosts Read-only List the workspace’s hosts. Returns each host as `{ guid, name, enabled, authenticationType }`, scoped to what the caller may see, like the hosts page. **MCP tool:** `list_hosts` · **REST:** `GET /api/v1/mcpm-admin/hosts` · **Capability:** Create and manage API tokens (`createAndManageApiTokens`) **Parameters:** none. **Example** ```json theme={null} {} ``` ## Create Host Create an API-token host. Creates an enabled API-token host (the kind a token connects through). Returns `{ guid, name, enabled, authenticationType }`. To mint a usable token for a gateway, use issue\_gateway\_token. **MCP tool:** `create_host` · **REST:** `POST /api/v1/mcpm-admin/hosts` · **Capability:** Create and manage API tokens (`createAndManageApiTokens`) **Parameters** A human-readable name for the host (e.g. "CI runner"). **Example** ```json theme={null} {"name":"CI runner"} ``` ## Rename Host Rename a host. Renames the host. Saved only when the name actually changes. Returns `{ guid, name, enabled, authenticationType }`. **MCP tool:** `rename_host` · **REST:** `PATCH /api/v1/mcpm-admin/hosts/:hostGuid/name` · **Capability:** Create and manage API tokens (`createAndManageApiTokens`) **Parameters** The guid of the host to rename (from list\_hosts). The new name for the host. **Example** ```json theme={null} {"hostGuid":"MPH-...","newName":"Release runner"} ``` ## Enable Host Enable a host. Sets the host to enabled. Saved only when the state changes. Returns `{ guid, name, enabled, authenticationType }`. **MCP tool:** `enable_host` · **REST:** `POST /api/v1/mcpm-admin/hosts/:hostGuid/enable` · **Capability:** Disable and enable hosts (`enableDisableHosts`) **Parameters** The guid of the host to enable (from list\_hosts). **Example** ```json theme={null} {"hostGuid":"MPH-..."} ``` ## Disable Host Disable a host. Sets the host to disabled. Saved only when the state changes. Returns `{ guid, name, enabled, authenticationType }`. **MCP tool:** `disable_host` · **REST:** `POST /api/v1/mcpm-admin/hosts/:hostGuid/disable` · **Capability:** Disable and enable hosts (`enableDisableHosts`) **Parameters** The guid of the host to disable (from list\_hosts). **Example** ```json theme={null} {"hostGuid":"MPH-..."} ``` ## Delete Host Destructive Delete a host. Hard-deletes the host. Returns `{ deleted: true, hostGuid }`. **MCP tool:** `delete_host` · **REST:** `DELETE /api/v1/mcpm-admin/hosts/:hostGuid` · **Capability:** Delete hosts (`deleteHosts`) **Parameters** The guid of the host to delete (from list\_hosts). **Example** ```json theme={null} {"hostGuid":"MPH-..."} ``` ## List Connections Read-only List gateway connections, optionally filtered by host, gateway, or user. Lists the gateway connections in the workspace — each is a host's live connection to a gateway, established when a token was issued. Filter by hostGuid, gatewayGuid, and/or userGuid (the establishing user). Returns `{ guid, name, enabled, gatewayGuid, hostGuid, creatorGuid }` per connection; use get\_connection for detail and last-activity. **MCP tool:** `list_connections` · **REST:** `GET /api/v1/mcpm-admin/connections` · **Capability:** Disable and enable connections (`enableDisableConnections`) **Parameters** Only return connections for this host (from list\_hosts). Only return connections for this gateway (from list\_gateways). Only return connections established by this user (from list\_users). **Example** ```json theme={null} {"hostGuid":"MHO-..."} ``` ## Get Connection Read-only Inspect one connection, including its last successful call and recent errors. Returns full detail for a connection: the summary fields plus the resolved gateway/host names and the establishing user's email, how many servers it carries, and — to debug a failing connection — the date of its last successful call (`lastSuccessfulCallAt`) and its `recentErrors` (from the connection's MCP call logs). No separate log/alert query is needed. **MCP tool:** `get_connection` · **REST:** `GET /api/v1/mcpm-admin/connections/:connectionGuid` · **Capability:** Disable and enable connections (`enableDisableConnections`) **Parameters** The guid of the connection to inspect (from list\_connections). **Example** ```json theme={null} {"connectionGuid":"MGC-..."} ``` ## Disable Connection Disable a connection. Cuts off the connection without deleting it (sets enabled=false). Saved only when the state changes. Returns the connection summary. **MCP tool:** `disable_connection` · **REST:** `POST /api/v1/mcpm-admin/connections/:connectionGuid/disable` · **Capability:** Disable and enable connections (`enableDisableConnections`) **Parameters** The guid of the connection to disable (from list\_connections). **Example** ```json theme={null} {"connectionGuid":"MGC-..."} ``` ## Enable Connection Enable a connection. Re-enables a previously disabled connection (sets enabled=true). Saved only when the state changes. Returns the connection summary. **MCP tool:** `enable_connection` · **REST:** `POST /api/v1/mcpm-admin/connections/:connectionGuid/enable` · **Capability:** Disable and enable connections (`enableDisableConnections`) **Parameters** The guid of the connection to enable (from list\_connections). **Example** ```json theme={null} {"connectionGuid":"MGC-..."} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Identities Source: https://docs.mcpmanager.ai/admin-api/reference/identities Admin API operations for managing the credential identities a server authenticates with: add, list, delete, enable, disable, set personal-versus-shared availability, and set an identity’s header tokens. These operations manage identities — the credentials MCP Manager uses to authenticate to a downstream server. A private identity is usable and editable only by its owner; its header token values are write-only and never returned. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## List Identities Read-only List the credential identities attached to an inbound server. Returns each identity as `{ guid, name, enabled, accessControl, inboundServerGuid }`. `accessControl` is 'private' (only the creator may use it) or 'global' (anyone in the org). The set is scoped to the server's identities the caller is permitted to see. **MCP tool:** `list_identities` · **REST:** `GET /api/v1/mcpm-admin/servers/:inboundServerGuid/identities` · **Capability:** View all identities (`accessAllIdentities`) **Parameters** The guid of the inbound server whose identities to list (from list\_inbound\_servers). **Example** ```json theme={null} {"inboundServerGuid":"MIS-..."} ``` ## Add Identity Add a credential identity to an existing inbound server. The server's `authenticationType` selects the path: * **Headers server** — provide `headers`; a new credential identity is created and the headers stored **encrypted**. Returns the identity as `{ guid, name, enabled, accessControl, inboundServerGuid }`. * **OAuth server** — supply the `oauth*` token fields for headless token injection (returns the server summary, `authenticated` after feature-learn validates the token), or omit them for the interactive flow (returns the server summary with an `authorizationUrl` to relay to the user for approval; the redirect callback then creates the identity). Do not pass `headers` to an OAuth server. On the interactive path the identity exists only after approval — once it does, you can wire it as the shared identity on a gateway assignment or select it when issuing a gateway token. **MCP tool:** `add_identity` · **REST:** `POST /api/v1/mcpm-admin/servers/:inboundServerGuid/identities` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the inbound server to add an identity to (from list\_inbound\_servers). Static request headers, as a name → value map. Required when the server uses `headers` auth. Header values are secrets — stored encrypted and never logged. `mcpm-` prefixed headers are reserved and dropped. *(a string → string map)* OAuth access token for headless token injection on an `oauth` server. Secret — never logged. OAuth refresh token (optional) for headless token injection. Secret — never logged. Unix epoch milliseconds at which the access token expires (optional) for headless token injection. The OAuth token endpoint URL, used to refresh the access token. Required for headless token injection. The OAuth client id. Required for headless token injection. The OAuth client secret (optional — confidential clients only). Secret — never logged. Space-separated OAuth scopes granted to the token (optional) for headless token injection. A name for the new credential identity. Defaults to "\ Identity". Who may use the credential: 'private' (only the creator) or 'global' (anyone in the org). Defaults to 'private'. One of: `private`, `global`. **Example** ```json theme={null} {"inboundServerGuid":"MIS-...","headers":{"Authorization":"Bearer "}} ``` ## Enable Identity Enable a credential identity. Enables the identity. Saved only when the state changes. Returns the identity summary. A private identity you did not create is not visible. **MCP tool:** `enable_identity` · **REST:** `POST /api/v1/mcpm-admin/identities/:identityGuid/enable` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the identity to enable (from list\_identities). **Example** ```json theme={null} {"identityGuid":"MISI-..."} ``` ## Disable Identity Disable a credential identity. Disables the identity so it can no longer authenticate. Saved only when the state changes. Returns the identity summary. **MCP tool:** `disable_identity` · **REST:** `POST /api/v1/mcpm-admin/identities/:identityGuid/disable` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the identity to disable (from list\_identities). **Example** ```json theme={null} {"identityGuid":"MISI-..."} ``` ## Delete Identity Destructive Delete a credential identity. Permanently deletes the identity. Its encrypted header credential is not separately deleted (it lingers, org-scoped, and is never exposed). Returns `{ deleted: true, identityGuid }`. **MCP tool:** `delete_identity` · **REST:** `DELETE /api/v1/mcpm-admin/identities/:identityGuid` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the identity to delete (from list\_identities). **Example** ```json theme={null} {"identityGuid":"MISI-..."} ``` ## Set Identity Availability Set a credential identity's availability (private or global). Changes whether the identity is private (creator-only) or global (usable/selectable by anyone in the workspace). CREATOR-ONLY: only the identity's creator may change this, even with the view-all-identities capability. Returns the identity summary. **MCP tool:** `set_identity_availability` · **REST:** `PATCH /api/v1/mcpm-admin/identities/:identityGuid/availability` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the identity (from list\_identities). 'private' (only the creator may use it) or 'global' (anyone in the workspace may use/select it). One of: `private`, `global`. **Example** ```json theme={null} {"identityGuid":"MISI-...","availability":"global"} ``` ## Set Identity Headers Replace a credential identity's header secrets. Sets (replaces) the encrypted header credentials for a header-authenticated identity. CREATOR-ONLY: only the identity's creator may edit its secrets. Values are write-only — they are never returned by any tool. Returns the identity summary (header names/values are not included). **MCP tool:** `set_identity_headers` · **REST:** `PUT /api/v1/mcpm-admin/identities/:identityGuid/headers` · **Capability:** Identity management (`identityManagement`) **Parameters** The guid of the identity whose header credentials to replace (from list\_identities). The full set of request headers (e.g. an Authorization bearer) this identity sends upstream. REPLACES the existing set. Reserved 'mcpm-' and empty entries are dropped. Stored encrypted and never returned. *(a string → string map)* **Example** ```json theme={null} {"identityGuid":"MISI-...","headers":{"Authorization":"Bearer "}} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Logging Source: https://docs.mcpmanager.ai/admin-api/reference/logging Admin API operations for observability: query the MCP call logs with filters and pagination, list and inspect alerts, and configure OpenTelemetry log and trace forwarding. These operations read the MCP call logs and alerts and configure OpenTelemetry forwarding. Responses are thin by default — request the heavy detail only for the specific rows you need. The call log here is the AI-usage log, distinct from the forthcoming admin audit log. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## Query Logs Retrieve MCP call logs, paginated and filterable. Queries the workspace MCP call logs (the same data behind the Logging view), scoped to the caller's org. Filter by connection, user, gateway, server, host, container, team, log type, or trace/span id, and by a created\_at time range (fromMs/toMs). Returns a page of log rows ordered newest-first by default. Responses are thin by default (no request/response body or headers); pass includeDetail:true to include them (truncated). This is the AI-usage/call log — distinct from the admin audit log. **MCP tool:** `query_logs` · **REST:** `POST /api/v1/mcpm-admin/logs/query` · **Capability:** View and export logs (`exportLogs`) **Parameters** Flat column -> value equality filters. Allowed columns: gateway\_connection\_guid (a connection), user\_guid, outbound\_gateway\_guid (a gateway), inbound\_server\_guid (a server), host\_guid, server\_container\_guid, team\_guid, type (log type), trace\_id, span\_id. Combine with the time range and pagination below. org scope is always applied automatically. *(a string → string map)* Time-range start as epoch milliseconds (inclusive lower bound on created\_at). Omit for no lower bound. Time-range end as epoch milliseconds (inclusive upper bound on created\_at). Omit for no upper bound. Column to sort by (must be a returned log column). Defaults to created\_at. Sort direction. Defaults to desc (newest first). One of: `asc`, `desc`. Number of rows to skip (offset) for pagination. Defaults to 0. Max rows to return this page. Defaults to 100, capped at 1000. When true, include the heavy request/response `body` and `headers` fields (truncated). Omitted by default to keep responses small — request detail only for the specific rows you need. **Example** ```json theme={null} {"filters":{"gateway_connection_guid":"MGC-..."},"take":50} ``` ## List Alerts List workspace alerts, paginated and filterable (thin summaries). Lists alerts for the workspace, newest-first, filterable by code, messageType, related resource, and a created-at time range. Returns a thin summary per alert (`guid, code, messageType, subject, message, createdAt, relatedResourceIds`) — the heavy debug/close context is omitted; use get\_alert for the full record of a single alert. **MCP tool:** `list_alerts` · **REST:** `POST /api/v1/mcpm-admin/alerts/query` · **Capability:** See all alerts (`seeAllAlerts`) **Parameters** Optional filters: code (exact alert code), messageType (info | warning | error), and/or relatedResourceGuid (matches any related resource on the alert — server, gateway, assignment, identity, policy, or connection). Combine with the time range below. *(a string → string map)* Time-range start as epoch milliseconds (inclusive lower bound on when the alert was created). Time-range end as epoch milliseconds (inclusive upper bound on when the alert was created). Number of alerts to skip (offset) for pagination. Defaults to 0. Max alerts to return this page. Defaults to 50, capped at 250. **Example** ```json theme={null} {"filters":{"messageType":"error"},"take":25} ``` ## Get Alert Read-only Get the full record for one alert. Returns everything about a single alert: the summary fields plus the unresolved dynamicContent (subject/message with placeholders), the debugContext (response status/headers/body and engine details), relatedContext, and closeDetails. Use for debugging a specific alert surfaced by list\_alerts. **MCP tool:** `get_alert` · **REST:** `GET /api/v1/mcpm-admin/alerts/:alertGuid` · **Capability:** See all alerts (`seeAllAlerts`) **Parameters** The guid of the alert to inspect (from list\_alerts). **Example** ```json theme={null} {"alertGuid":"MAM-..."} ``` ## Get OpenTelemetry Configuration Read-only Get the workspace OpenTelemetry (OTLP/HTTP) log-forwarding configuration. Returns the workspace OTLP/HTTP configuration used to forward logs (and optionally traces) to a SIEM: `{ configured, logsCollectorUrl, tracesCollectorUrl, headerNames }`. For security, only the header **names** are returned, never their values (headers often carry a bearer/API token). `configured` is false when no collector is set up yet. **MCP tool:** `get_otel_configuration` · **REST:** `GET /api/v1/mcpm-admin/otel-configuration` · **Capability:** Manage OpenTelemetry collector (`manageOtelCollector`) **Parameters:** none. **Example** ```json theme={null} {} ``` ## Set OpenTelemetry Configuration Configure the workspace OpenTelemetry (OTLP/HTTP) collector. Creates or updates the workspace OTLP/HTTP configuration for log (and optional trace) forwarding, mirroring the Logging > Integrations panel. Provide at least one collector URL. URLs are stored exactly as entered (no signal path is appended); each URL is updated independently, so omitting one keeps the currently-stored value rather than clearing it. `headers` is optional: omit to keep existing headers, pass a map to replace them, or an empty map to clear them. Returns the same shape as get\_otel\_configuration (header names only, never values). **MCP tool:** `set_otel_configuration` · **REST:** `POST /api/v1/mcpm-admin/otel-configuration` · **Capability:** Manage OpenTelemetry collector (`manageOtelCollector`) **Parameters** OTLP/HTTP endpoint for log export (e.g. [https://collector.example.com:4318/v1/logs](https://collector.example.com:4318/v1/logs)). Sent exactly as given — include the signal path yourself; it is never appended. Omit to keep the currently-stored logs URL unchanged (updating only the traces URL never disables log forwarding). At least one of logsCollectorUrl or tracesCollectorUrl is required. OTLP/HTTP endpoint for trace export (e.g. [https://collector.example.com:4318/v1/traces](https://collector.example.com:4318/v1/traces)). Sent exactly as given — include the signal path yourself; it is never appended. Omit to keep the currently-stored traces URL unchanged (updating only the logs URL never disables trace forwarding). At least one of logsCollectorUrl or tracesCollectorUrl is required. HTTP request headers sent with both signals (e.g. an Authorization bearer for the SIEM). OMIT this to keep the existing headers unchanged — important because their values are write-only and cannot be read back to re-supply. Pass a map to REPLACE the full header set, or an empty map to clear all headers. Values are stored encrypted and are never returned by get\_otel\_configuration. *(a string → string map)* **Example** ```json theme={null} {"logsCollectorUrl":"https://collector.example.com:4318/v1/logs","tracesCollectorUrl":"https://collector.example.com:4318/v1/traces","headers":{"Authorization":"Bearer "}} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Tool & endpoint reference Source: https://docs.mcpmanager.ai/admin-api/reference/overview Index of every MCP Manager Admin API operation, grouped by domain. Each operation is exposed as an MCP tool and a REST endpoint from a single definition, so the reference documents both surfaces together, with parameters and the capability each one enforces. The Admin API exposes **64 operations** across seven domains. Each operation is defined once and generated into both an **MCP tool** and a **REST endpoint**, so every reference entry lists the MCP tool name, the REST method and path, and the capability the operation enforces — the two surfaces never diverge. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## How to read the reference Each operation entry shows four things: * **MCP tool** — the tool name to call from an MCP client (for example `create_inbound_server`). * **REST** — the HTTP method and path under `/api/v1/mcpm-admin` (for example `POST /api/v1/mcpm-admin/servers`). * **Capability** — the capability your role must hold, shown as its product label and its exact key (for example *Basic server management* (`basicServerManagement`)). Operations marked *none* are available to any authenticated caller. * **Parameters** — every input, with its type, whether it's required, its default, and where the REST surface reads it from (path, query, or body). Read-only operations carry a **Read-only** badge and destructive ones a **Destructive** badge, mirroring the annotations the MCP server sends to clients. List and query operations return **thin results by default** — opt into heavy detail (request/response bodies, full alert context) only for the specific records you need, so responses stay small. ## Operations by domain | Domain | Operations | What it covers | | ------------------------------------------------------- | ---------: | ------------------------------------------------------------------------------------ | | [Account & access tokens](/admin-api/reference/account) | 5 | Confirm identity and capabilities; create, list, and revoke your admin access tokens | | [Servers](/admin-api/reference/servers) | 7 | Create and manage inbound MCP servers and their authentication | | [Identities](/admin-api/reference/identities) | 7 | Manage the credentials a server authenticates with, and their availability | | [Gateways](/admin-api/reference/gateways) | 17 | Gateways, server assignments and identity schemes, and team provisioning | | [People, teams & roles](/admin-api/reference/people) | 13 | Invite and deactivate users; manage roles, capabilities, and teams | | [Hosts](/admin-api/reference/hosts) | 10 | Hosts and their gateway connections, including connection diagnostics | | [Logging](/admin-api/reference/logging) | 5 | Query call logs and alerts; configure OpenTelemetry forwarding | Every operation enforces the same capability as the equivalent action in the app, so the Admin API can never do more than your role already allows. See [Authentication & access](/admin-api/authentication) for the full access model and the status codes returned when a call is denied, malformed, or fails. ## Browse the domains Identity, capabilities, and admin Personal Access Token management. Create, rename, enable, disable, and delete inbound MCP servers. Add, manage, and set the availability of server credentials. Gateways, server assignments with identity schemes, and team provisioning. Users, roles and capabilities, and teams. Hosts and their gateway connections, with connection diagnostics. Call-log and alert queries and OpenTelemetry configuration. ## Further reading The first domain in the reference — start here. Tokens, the entitlement, capability gating, and error codes. Get a token and register the admin MCP server in your client. Operations that are on the way but not in the beta yet. # People, Teams & Roles Source: https://docs.mcpmanager.ai/admin-api/reference/people Admin API operations for people: invite and deactivate users, assign roles and teams, and create, modify, list, and delete roles and teams. These operations manage the people in your workspace and the roles and teams that scope what they can do. User and role operations each require their own management capability. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## List Teams Read-only List the workspace’s teams. Returns each accessible team as `{ guid, name, enabled }`, scoped to what the caller may see, like the people → teams page. **MCP tool:** `list_teams` · **REST:** `GET /api/v1/mcpm-admin/teams` · **Capability:** none — available to any authenticated caller **Parameters:** none. **Example** ```json theme={null} {} ``` ## Create Team Create a workspace team. Creates an enabled team owned by the caller (a `creator` edge), wired to the organization. Returns `{ guid, name, enabled }`. Add members with set\_user\_teams and provision gateways with assign\_team\_to\_gateway. **MCP tool:** `create_team` · **REST:** `POST /api/v1/mcpm-admin/teams` · **Capability:** Manage teams (`manageTeams`) **Parameters** A human-readable name for the team (e.g. "Engineering"). **Example** ```json theme={null} {"name":"Engineering"} ``` ## Rename Team Rename a team. Renames the team. Saved only when the name actually changes. Returns `{ guid, name, enabled }`. **MCP tool:** `rename_team` · **REST:** `PATCH /api/v1/mcpm-admin/teams/:teamGuid/name` · **Capability:** Manage teams (`manageTeams`) **Parameters** The guid of the team to rename (from list\_teams). The new name for the team. **Example** ```json theme={null} {"teamGuid":"WST-...","newName":"Platform"} ``` ## Delete Team Destructive Delete a team. Hard-deletes the team; its membership and gateway-provisioning edges are removed. Returns `{ deleted: true, teamGuid }`. **MCP tool:** `delete_team` · **REST:** `DELETE /api/v1/mcpm-admin/teams/:teamGuid` · **Capability:** Manage teams (`manageTeams`) **Parameters** The guid of the team to delete (from list\_teams). **Example** ```json theme={null} {"teamGuid":"WST-..."} ``` ## List Roles Read-only List the workspace’s roles. Returns each role as `{ guid, name, grantedCapabilities }`, where `grantedCapabilities` are the capability keys the role grants (value true) — the same keys whoami reports for a user. **MCP tool:** `list_roles` · **REST:** `GET /api/v1/mcpm-admin/roles` · **Capability:** Manage roles (`manageRoles`) **Parameters:** none. **Example** ```json theme={null} {} ``` ## Create Role Create a workspace role. Creates a role owned by the caller, wired to the organization, with the given capability grants (empty by default). Returns `{ guid, name, grantedCapabilities }`. **MCP tool:** `create_role` · **REST:** `POST /api/v1/mcpm-admin/roles` · **Capability:** Manage roles (`manageRoles`) **Parameters** A human-readable name for the role (e.g. "Gateway Operator"). Initial capability grants as a map of capability key → "true"/"false" (e.g. `{ "basicGatewayManagement": "true" }`). Omit to create a role with no capabilities (set them later with modify\_role). Any value other than "true" is treated as false. *(a string → string map)* **Example** ```json theme={null} {"name":"Viewer"} ``` ## Modify Role Rename a role and/or change its capability grants. Updates the role’s name and/or merges the supplied capability grants into its existing grants (provided keys are set; others unchanged). Saved only when something actually changes. Returns `{ guid, name, grantedCapabilities }`. **MCP tool:** `modify_role` · **REST:** `PATCH /api/v1/mcpm-admin/roles/:roleGuid` · **Capability:** Manage roles (`manageRoles`) **Parameters** The guid of the role to modify (from list\_roles). A new name for the role. Capability grants to set, as a map of capability key → "true"/"false". MERGED into the existing grants — only the provided keys change; others are left as-is. Any value other than "true" is treated as false. *(a string → string map)* **Example** ```json theme={null} {"roleGuid":"WSR-...","grants":{"deleteServers":"true"}} ``` ## Delete Role Destructive Delete a role, reassigning its users to a replacement role. Before deleting, every user currently on the role is moved to `reassignToRoleGuid` (so no user is ever left without a role), then the role is deleted. Returns `{ deleted: true, reassignedUsers }`. **MCP tool:** `delete_role` · **REST:** `DELETE /api/v1/mcpm-admin/roles/:roleGuid` · **Capability:** Manage roles (`manageRoles`) **Parameters** The guid of the role to delete (from list\_roles). The guid of the role to move every affected user onto. Required — a user must always have exactly one role. **Example** ```json theme={null} {"roleGuid":"WSR-old","reassignToRoleGuid":"WSR-new"} ``` ## List Users Read-only List the workspace’s users with their role and teams. Returns each user as `{ guid, emailAddress, displayName, isActive, roleGuid, roleName, teamGuids }`, scoped to what the caller may see, like the people → users page. **MCP tool:** `list_users` · **REST:** `GET /api/v1/mcpm-admin/users` · **Capability:** none — available to any authenticated caller **Parameters:** none. **Example** ```json theme={null} {} ``` ## Set User Role Set a user’s role. Assigns the role to the user, replacing their current one (a user has exactly one role). Returns the updated user `{ guid, emailAddress, displayName, isActive, roleGuid, roleName, teamGuids }`. **MCP tool:** `set_user_role` · **REST:** `PUT /api/v1/mcpm-admin/users/:userGuid/role` · **Capability:** Manage user role assignments (`manageUserRoles`) **Parameters** The guid of the user (from list\_users). The guid of the role to assign (from list\_roles). **Example** ```json theme={null} {"userGuid":"USR-...","roleGuid":"WSR-..."} ``` ## Set User Teams Replace a user’s team memberships. Sets the user’s teams to exactly the provided list: missing memberships are added, ones not listed are removed. Returns the updated user `{ guid, emailAddress, displayName, isActive, roleGuid, roleName, teamGuids }`. **MCP tool:** `set_user_teams` · **REST:** `PUT /api/v1/mcpm-admin/users/:userGuid/teams` · **Capability:** Manage user team assignments (`manageUserTeams`) **Parameters** The guid of the user (from list\_users). The full set of team guids the user should belong to, comma-separated (e.g. "WST-a,WST-b"). This REPLACES the user’s team memberships — teams not listed are removed. Pass an empty string to clear all. **Example** ```json theme={null} {"userGuid":"USR-...","teamGuids":"WST-a,WST-b"} ``` ## Invite User Invite a new user to the workspace. Sends a workspace invitation, optionally assigning a role and teams. The invite is processed through the BFF so role/team edges and notification wiring are created correctly. Returns `{ email, userGuid }` (`userGuid` is the created user once available). The caller is recorded as the referrer. **MCP tool:** `invite_user` · **REST:** `POST /api/v1/mcpm-admin/users/invite` · **Capability:** Invite users (`inviteUsers`) **Parameters** The email address to invite. The invitee’s first name (optional). The invitee’s last name (optional). The guid of the role to assign the invitee (from list\_roles). Comma-separated team guids to add the invitee to (from list\_teams), e.g. "WST-a,WST-b". An optional custom message included in the invitation email. **Example** ```json theme={null} {"email":"new@example.com","roleGuid":"WSR-...","teamGuids":"WST-..."} ``` ## Deactivate User Destructive Deactivate a workspace user. Deactivates the user (removes their workspace membership and license) via the BFF. Authorization is enforced by the `removeUsers` capability. Returns `{ deactivated: }`. **MCP tool:** `deactivate_user` · **REST:** `POST /api/v1/mcpm-admin/users/:userGuid/deactivate` · **Capability:** Remove users (`removeUsers`) **Parameters** The guid of the user to deactivate (from list\_users). **Example** ```json theme={null} {"userGuid":"USR-..."} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # Servers Source: https://docs.mcpmanager.ai/admin-api/reference/servers Admin API operations for managing inbound MCP servers: create remote servers with open, header, or OAuth authentication, and rename, enable, disable, and delete them. These operations manage inbound MCP servers — the upstream servers your gateways expose. Creating and editing servers requires the Basic server management capability; enabling, disabling, and deleting have their own capabilities. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## List Servers Read-only List the caller's inbound MCP servers. Returns each accessible inbound server as `{ guid, name, url, enabled, authenticationType, authenticationStatus }`. The set is scoped to the servers the caller's role and team membership permit, exactly like the servers overview page. **MCP tool:** `list_inbound_servers` · **REST:** `GET /api/v1/mcpm-admin/servers` · **Capability:** View all servers (`viewAllServers`) **Parameters:** none. **Example** ```json theme={null} {} ``` ## Get Server Read-only Fetch a single inbound server, including its authentication status. Returns the server as `{ guid, name, url, enabled, authenticationType, authenticationStatus }`. Poll `authenticationStatus` after `create_inbound_server`: feature-learn runs asynchronously, so a newly created token-auth server stays `needs-authentication` until it is validated, then flips to `authenticated` (or `unsupported` on failure). Open servers report `open`. **MCP tool:** `get_inbound_server` · **REST:** `GET /api/v1/mcpm-admin/servers/:inboundServerGuid` · **Capability:** View all servers (`viewAllServers`) **Parameters** The guid of the inbound server to fetch (from list\_inbound\_servers). **Example** ```json theme={null} {"inboundServerGuid":"MIS-..."} ``` ## Create Server Create a remote inbound MCP server (open, static-header, or OAuth auth). Creates an unmanaged remote inbound server owned by the caller (a `creator` edge to the calling user), enabled by default, then kicks off feature-learning to validate it and discover its tools. * `authType: "none"` — an open server needing no credential. No identity or secret is created; the server reports `authenticationStatus: "open"`. * `authType: "headers"` — static-header / API-key auth. Provide `headers` as a name → value map; a credential identity is created and the headers are stored **encrypted**. Header values are never logged. * `authType: "oauth"` — OAuth 2.1. Two completion paths: * **Headless token injection**: supply `oauthAccessToken`, `oauthClientId`, and `oauthTokenEndpoint` (plus optional `oauthRefreshToken`, `oauthExpiresAt`, `oauthClientSecret`, `oauthScope`). The server is authenticated with no browser step; secrets are stored **encrypted** and never logged. * **Interactive**: omit the `oauth*` token fields. The response includes `authorizationUrl` — relay it to the user to open in a browser and approve at the provider. The server-side redirect callback then completes the exchange and creates the identity automatically. Until that approval the server has NO credential identity, so if you assign it to a gateway now, prefer identityScheme:'userIdentity' (each user authenticates themselves) or wait until approval and then select the shared identity. Feature-learning runs asynchronously, so a `headers` / token-injected server is returned as `needs-authentication`; poll `get_inbound_server` until `authenticationStatus` becomes `authenticated` (or `unsupported` on failure). For the interactive OAuth path, poll until the human finishes approving. **MCP tool:** `create_inbound_server` · **REST:** `POST /api/v1/mcpm-admin/servers` · **Capability:** Basic server management (`basicServerManagement`) **Parameters** A human-readable name for the server (e.g. "Microsoft Learn MCP"). The remote MCP server URL (e.g. "[https://learn.microsoft.com/api/mcp](https://learn.microsoft.com/api/mcp)"). How to authenticate to the server: 'none' for an open server that needs no credential, 'headers' for static-header / API-key auth (provide `headers`), or 'oauth' for OAuth 2.1 (supply the `oauth*` token fields for a fully headless setup, or omit them to receive an authorization URL for a human to approve). One of: `none`, `headers`, `oauth`. Static request headers to authenticate with, as a name → value map (e.g. `{ "Authorization": "Bearer ..." }`). Required when `authType` is `headers`. Header values are secrets — they are stored encrypted and never logged. `mcpm-` prefixed headers are reserved and dropped. *(a string → string map)* OAuth access token for headless token injection (`authType: "oauth"`). When provided together with `oauthClientId` and `oauthTokenEndpoint`, the server is authenticated without any browser step. Secret — never logged. OAuth refresh token (optional) for headless token injection. Secret — never logged. Unix epoch milliseconds at which the access token expires (optional) for headless token injection. The OAuth token endpoint URL, used to refresh the access token. Required for headless token injection. The OAuth client id. Required for headless token injection. The OAuth client secret (optional — confidential clients only) for headless token injection. Secret — never logged. Space-separated OAuth scopes granted to the token (optional) for headless token injection. A name for the credential identity created on the `headers` / `oauth` paths. Defaults to "\ Identity". Who may use the created credential: 'private' (only the creator) or 'global' (anyone in the org). Defaults to 'private'. Ignored on the open path. One of: `private`, `global`. **Example** ```json theme={null} {"name":"Microsoft Learn MCP","url":"https://learn.microsoft.com/api/mcp","authType":"none"} ``` ## Rename Server Change a server's name. Renames the inbound server to `newName` (the only editable field — a remote server's URL and auth are fixed at creation). Saved only when the name actually changes. Returns the updated server as `{ guid, name, url, enabled, authenticationType, authenticationStatus }`. **MCP tool:** `rename_inbound_server` · **REST:** `PATCH /api/v1/mcpm-admin/servers/:inboundServerGuid/name` · **Capability:** Basic server management (`basicServerManagement`) **Parameters** The guid of the server to rename (from list\_inbound\_servers). The new display name for the server. **Example** ```json theme={null} {"inboundServerGuid":"MIS-...","newName":"Atlassian (prod)"} ``` ## Enable Server Enable a server. Enables the inbound server so it can be reached through its gateways. Saved only when the state actually changes. Returns the updated server summary. **MCP tool:** `enable_inbound_server` · **REST:** `POST /api/v1/mcpm-admin/servers/:inboundServerGuid/enable` · **Capability:** Disable and enable servers (`enableDisableServers`) **Parameters** The guid of the server to enable (from list\_inbound\_servers). **Example** ```json theme={null} {"inboundServerGuid":"MIS-..."} ``` ## Disable Server Disable a server. Disables the inbound server so it is no longer reachable through any gateway (assignments stay but go inert). Saved only when the state actually changes. Returns the updated server summary. **MCP tool:** `disable_inbound_server` · **REST:** `POST /api/v1/mcpm-admin/servers/:inboundServerGuid/disable` · **Capability:** Disable and enable servers (`enableDisableServers`) **Parameters** The guid of the server to disable (from list\_inbound\_servers). **Example** ```json theme={null} {"inboundServerGuid":"MIS-..."} ``` ## Delete Server Destructive Delete a server. Permanently deletes the inbound server. Its gateway assignments and identities are not deleted — the assignments go inert (the server they point at is gone) and any identities linger until removed explicitly. Returns `{ deleted: true, inboundServerGuid }`. **MCP tool:** `delete_inbound_server` · **REST:** `DELETE /api/v1/mcpm-admin/servers/:inboundServerGuid` · **Capability:** Delete servers (`deleteServers`) **Parameters** The guid of the server to delete (from list\_inbound\_servers). **Example** ```json theme={null} {"inboundServerGuid":"MIS-..."} ``` ## Further reading Where to go next in this section. Tokens, the entitlement, capability gating, and error codes for every operation here. The full reference index across every domain. What each capability named on this page actually allows. # What's coming Source: https://docs.mcpmanager.ai/admin-api/roadmap The MCP Manager Admin API capabilities that are planned but not yet in the beta: reporting and a richer whoami, gateway rules and custom rule engines, per-assignment feature provisioning, an admin audit log, entitlement queries, multi-workspace targeting, a downloadable CLI, and proxying the docs MCP server through the admin gateway. The beta covers configuration, identities, gateways, people, hosts, and log and alert queries. Several capabilities that round out full parity with the app are planned but **not in the beta yet**. This page describes what's on the way so you can plan for it. We don't commit to dates here; if one of these is important to you, tell your MCP Manager contact — real demand shapes what ships first. **Closed beta.** The MCP Manager Admin API and MCP server are available now to a limited set of workspaces through the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. If you don't see **Settings → MCP & API** in your workspace, it isn't enabled for you yet — ask your MCP Manager contact to join the beta. ## Reporting and a richer identity call **Available soon.** Read-only reporting tools and an enriched `whoami` are planned. Today `whoami` returns your user, organization, team, role, and capabilities. A richer version will add more context about your workspace in a single call. Alongside it, reporting operations will expose the same usage summaries you see in the app's reports — a read-only, low-risk addition on top of the [log queries](/admin-api/reference/logging) that already ship. ## Gateway rules and custom rule engines **Available soon.** Creating, editing, reordering, and configuring gateway rules — and registering custom rule engines — over the Admin API is planned. Gateway rules govern what traffic a gateway allows and how it's transformed. Managing them programmatically is security-sensitive, so it's being designed carefully before it opens up. One deliberate design point to plan around: **MCP Manager rules are allow-lists.** There is no "deny list" of blocked items — you express a restriction as "allow everything except X." When these tools arrive, a request to "block tool Y" will be shaped into an allow-rule that excludes Y, not a separate deny primitive. See [Gateway Rules](/features/gateway-rules/overview) for how rules work in the app today. ## Per-assignment feature provisioning **Available soon.** Configuring which features (tools, resources, prompts) a server assignment exposes on a gateway is planned. A server can be assigned to a gateway more than once, each assignment exposing a different subset of the server's features. Provisioning that subset — choosing exactly which tools an assignment offers, and previewing the result before it goes live — is planned for the Admin API. Like rules, exposure is **allow-list only**: you select what to expose. See [Feature Provisioning](/features/feature-provisioning) for the concept. ## Admin audit log **Available soon.** A dedicated log of administrative changes made through the Admin API and the app is planned. The Admin API's design is to **enable every action your role permits and make it accountable**, rather than to block sensitive writes. The accountability half of that is a dedicated **admin audit log** — a record of who changed what configuration, when — kept separate from the AI-usage call log. Two different logs, two different tools. The [`query_logs`](/admin-api/reference/logging) operation that ships today returns the **AI-usage call log** (the MCP tool calls flowing through your gateways). The forthcoming **admin audit log** records **configuration changes** made through the Admin API. When the audit tools arrive they will be named distinctly so an agent never confuses the two. ## Multi-workspace targeting **Available soon.** Targeting a specific workspace on each call — for partners and others who manage more than one — is planned. Every operation currently acts on the workspace your token belongs to. Planned multi-workspace support will let `whoami` report every workspace you belong to and let each operation take an optional workspace target, defaulting to your token's workspace when omitted. This is aimed at partners administering sub-workspaces on behalf of their customers. ## Entitlement queries **Available soon.** Reading your workspace's plan entitlements through the Admin API is planned. Tools to query which features and entitlements your workspace holds are planned, so an agent can check what's available before attempting to use it. ## A downloadable CLI **Available soon.** A standalone command-line client for the same operations is planned. Every operation is already defined with a command-line form. A downloadable CLI will let you script the same operations and wire them into pipelines and infrastructure-as-code workflows, without writing an MCP or HTTP client yourself. ## Docs lookups through the admin server **Available soon.** Reaching the documentation MCP server through your admin connection is planned. The admin server already points agents at the [docs MCP server and `llms.txt`](/get-started/use-docs-with-ai). A planned enhancement will let an agent look up documentation **through** the admin connection, so a single connected server covers both operating your workspace and answering questions about how it works. ## Further reading Everything that is in the beta today, grouped by domain. What the control-plane surface is and how it's built. How the Admin API relates to the agent connection that ships today. How allow-list rules work in the app while the rule tools are in development. # Agents that Pass Identities to MCP Manager Source: https://docs.mcpmanager.ai/advanced/agents-passing-identities How to whitelist a headless agent for use with Claude and proxy credentials at the calling-user level through MCP Manager: create one token-based host for the agent, let each end user enroll and bring their own identity to mint a per-user access token, and have the agent map each user to their token so downstream MCP servers act as the real user — fully governed and logged. This is an advanced pattern for teams **building their own agent** that calls an **MCP Manager** gateway on behalf of many end users. A typical case: an agent running in your cloud (say, GCP Cloud Run) that has extra skills and business context on top of a downstream MCP server — for example a BigQuery server — and is called from Claude. You want to **whitelist that agent for use with Claude** and have every downstream call use **the calling user's own credentials**, with their own permissions, and no impersonation. MCP Manager supports exactly this. The key idea is that identity is carried by a **per-user access token**, not asserted in a side header. Each end user enrolls once, brings their own identity to the downstream system, and MCP Manager mints them a token. Your agent then presents the right user's token on each call, and MCP Manager brokers that user's own downstream credential. Creating the token-based host and managing its tokens is gated by the **Create and manage API tokens** capability; controlling which agents are allowed uses **Disable and enable hosts**. If you can't create a host or token, your role doesn't have the capability — ask a workspace administrator. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## The shape of the solution: two maps Two lookup tables make per-user proxying work, one on each side of the gateway: * **Map 1 — in your agent: end user → MCP Manager token.** Your agent already knows who the end user is (it authenticates them, e.g. from their Claude connection). It keeps a table mapping each known user to the MCP Manager access token that user generated. * **Map 2 — in MCP Manager: token → that user's downstream identity.** When the agent calls the gateway with a user's token, MCP Manager resolves the user and uses the downstream identity that user brought (their BigQuery credential, their Atlassian login, and so on). The result: each user's downstream actions run with **their own permissions** — reads and writes are attributed to them, never to a shared agent account. Building Map 1 is the work on your side; Map 2 is what MCP Manager handles for you. ## Step 1 — Create one token-based host for the agent In **Apps & Agents**, create a single **token-based host** that represents the agent — the catch-all for any headless agent (one with no human signing in to it directly). Name it for the agent, for example "BigQuery via GCP agent," and save. You only do this once, as an administrator, at [Apps & Agents](https://app.mcpmanager.ai/settings/hosts). ## Step 2 — Each user enrolls and brings their identity Each end user then enrolls themselves so MCP Manager can mint **their own** access token. Share the connection link (over Slack or email); each user opens it, adds a connection, picks the gateway that contains the target server, and brings their identity to that server — either through a standard OAuth handshake or by selecting an identity they've already added. MCP Manager stores that identity encrypted and mints the user a token scoped to the gateway. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram autonumber actor Admin actor U as End user participant M as 🛡️ MCP Manager participant S as 🖥️ Downstream MCP server Admin->>M: Create one token-based host for the agent Note over Admin,U: Admin shares a connection link (Slack / email) U->>M: Open the link and add a connection U->>M: Choose the gateway with the target server U->>S: Authorize via OAuth (or pick an existing identity) S-->>M: Identity returned, stored encrypted M-->>U: Mint this user's MCP Manager access token Note over U: Token goes into the agent's "user → token" map ``` Distribute the connection link to the people who will use the agent. The user opens the link, clicks **Add a connection**, and selects the gateway that contains the target server. The user authorizes the downstream server through OAuth, or picks an identity they already added. MCP Manager stores it encrypted in its key vault. On approval, MCP Manager mints an access token for that user, valid for this gateway. This token represents that user. ## Step 3 — Your agent maps each user to their token Your agent maintains **Map 1**: as each user enrolls, record their MCP Manager token against the identity the agent knows them by. When a request arrives from Claude, the agent identifies the user, looks up their token, and uses it for the call. This mapping table is the piece your team builds and maintains. ## How a call is made at runtime At request time, the agent presents the calling user's token to the gateway. MCP Manager resolves the user (Map 2), applies policy, calls the downstream server **as that user's own credential**, logs the exchange, and returns the result. Everything between the agent and the downstream server happens inside MCP Manager's governed zone. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram autonumber actor U as End user participant C as 🤖 AI client (e.g. Claude) participant A as 🤖 Your agent (MCP server) participant M as 🛡️ MCP Manager gateway participant S as 🖥️ Downstream MCP server Note over A: Map 1 — end user → MCP Manager token
alice → mcpm_tok_a
bob → mcpm_tok_b Note over M: Map 2 — token → that user's identity
mcpm_tok_a → alice's own credential
mcpm_tok_b → bob's own credential U->>C: "Run a query on…" C->>A: MCP call — auth identifies the end user (alice) activate A Note right of A: Lookup 1 — alice → mcpm_tok_a rect rgb(232, 244, 253) Note over A,S: Governed zone — MCP Manager applies policy,
guardrails, and audit logging A->>M: MCP call — Authorization: Bearer mcpm_tok_a activate M Note right of M: Lookup 2 — mcpm_tok_a → alice's credential
+ policy check + log entry M->>S: MCP call using alice's own credential activate S S-->>M: Result deactivate S M-->>A: Result (governed and logged) deactivate M end A-->>C: Result deactivate A C-->>U: Formatted answer ``` ## Why this preserves identity and stays governed Because each user brought their own identity and got their own token, the downstream server sees the **real user** and enforces **their** permissions — a data analyst reads and writes only what they're allowed to, with no shared-account impersonation. And because every call passes through the gateway, MCP Manager applies your rules and writes an audit log entry for each one, attributed to that user. You get the full benefit of a gateway around an otherwise opaque, headless agent. ## Controlling which agents are allowed The token-based host is also the control point for **whitelisting**: an administrator can disable the host to block the agent entirely, or re-enable it, without deleting any tokens. This is how you allow a vetted agent for use with Claude and keep others out. Disabling and enabling hosts is gated by the **Disable and enable hosts** capability. ## Identities behind the tokens Each token resolves to an **identity** — a set of credentials scoped to one server, created by bearer token or OAuth, and stored encrypted with AES-256-GCM. For per-user attribution like the flow above, pair the agent's servers with the gateway's **per-user identity scheme**; for a single shared service account, use a **shared identity**. See [the identities model](/mcp-gateway-concepts/mcp-servers/overview#how-identities-control-access-across-all-three-types) and the gateway's [identity scheme](/mcp-gateway-concepts/mcp-gateways#two-separate-authentications). **Coming improvement.** Today, each user's token is surfaced for the user (or your provisioning) to place into the agent's map. A more seamless flow — where MCP Manager delivers the token to your agent automatically via a webhook, so there's no copy-and-paste — is in active design exploration with partners and not yet generally available. Reach out if a tighter integration would help your rollout. ## Further reading How apps and agents appear in your workspace and connect to gateways. The single governed URL an agent calls, and how it brokers identity to each upstream. Private and Global identities, and per-user versus shared identity schemes. The host and token capabilities that gate creating and disabling apps and agents. # Building a Custom Rule Engine Source: https://docs.mcpmanager.ai/advanced/building-a-custom-rule-engine The developer reference for building a custom rule-engine webhook for MCP Manager: the request envelope, the pass/block/modify/error response shapes, modifiedPayload.body validation, the 30-second timeout, retries, the 16 MiB cap, and a complete Express example. A **custom rule engine** is a webhook on your own server that MCP Manager calls when a [gateway rule](/features/gateway-rules/overview) using the **Custom** provider fires. Your server inspects the message and tells the gateway one of four things: **pass** it through, **modify** it, **block** it, or signal that it **couldn't decide**. This page is the developer reference for that webhook — the full request/response contract, so you can stand up an engine in any stack. For a guided build that takes you from an empty project to a working engine, see [Build and connect your first custom rule engine](/tutorials/custom-rule-engine). This is the build-it-yourself path. To register an engine in the UI and understand the surrounding settings — URL, HTTP method, headers, header forwarding, IP allowlisting, testing, and deletion — see [Custom Rule Engines](/features/gateway-rules/custom-rules-engines). For managed alternatives that need no code, see [Amazon Bedrock](/features/amazon-bedrock) and [Lakera Guard](/features/lakera-guard). ## How it works 1. Register a rule engine under **Rule Engines** with **Custom** as the provider, point the URL at your webhook, and optionally add request headers (for example, a bearer token for your own auth). 2. Attach that engine to a [gateway rule](/features/gateway-rules/overview#add-a-new-rule) by selecting it as the rule's **Detection method**. 3. When a tool message reaches the rule's [detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires), the gateway POSTs the message (wrapped in a small metadata envelope) to your webhook. Your webhook returns one of four shapes. The gateway acts on that shape before forwarding the message. The gateway calls your engine over **HTTPS only**, and URLs that resolve to private or loopback IP ranges are rejected up front — your engine must be reachable on a public network. See [Only HTTPS, public endpoints](/features/gateway-rules/custom-rules-engines#only-https-public-endpoints). ## The contract, as TypeScript types Paste this block into your project verbatim. Every field below is exactly what the gateway sends and expects in return. If a coding agent is generating your webhook, this is the source of truth — feed it these types. ```ts webhook-types.ts theme={null} // What the gateway POSTs to your webhook. export interface WebhookRequest { metadata: WebhookMetadata; body: JsonRpcResponse; } export interface WebhookMetadata { /** Engine's ID in MCP Manager (CSO guid). */ ruleEngineId: string; /** User whose tool call triggered this message. May be null for service-to-service traffic. */ userGuid: string | null; /** Email of the user whose tool call triggered this message. May be null for service-to-service traffic. */ userEmail: string | null; /** Gateway that fired the rule. May be null in edge cases. */ gatewayGuid: string | null; /** MCP server involved in the message. May be null in edge cases. */ serverGuid: string | null; /** Correlation ID — matches the value shown in your logs and alerts. */ sessionId: string; /** ISO 8601. */ timestamp: string; /** * Which leg of the tool call this is. * - 'request' → body is the upstream tool call (body.params.arguments carries the tool args) * - 'response' → body is the tool result (body.result carries the returned content) */ direction: 'request' | 'response'; /** Tool that was called, when known. */ toolName: string | null; /** MCP JSON-RPC method — 'tools/call' (gateway rules apply to tools only). */ method: string; /** JSON-RPC id of the in-flight request — echo it back unchanged in any modify response. */ requestId: string | number; } // The MCP message your engine inspects. JSON-RPC 2.0. export interface JsonRpcResponse { jsonrpc: '2.0'; id: string | number | null; /** Present on a result. A plain string, an OpenAI-shaped content envelope, or arbitrary JSON. */ result?: unknown; /** Present on error responses from the upstream MCP server. */ error?: { code: number; message: string; data?: unknown }; } // What your webhook MUST return. Pick exactly one shape. export type WebhookResponse = PassResponse | BlockResponse | ModifyResponse | ErrorResponse; export interface PassResponse { type: 'pass'; comment?: string; } export interface BlockResponse { type: 'block'; comment?: string; } export interface ModifyResponse { type: 'modify'; comment?: string; modifiedPayload: { /** A COMPLETE JSON-RPC response, not a partial. Same id as the inbound request. */ body: JsonRpcResponse; }; } export interface ErrorResponse { type: 'error'; comment?: string; } ``` ## What the gateway sends A single JSON object, posted with `Content-Type: application/json` plus any headers you configured. The HTTP method is whatever you set on the rule engine (defaults to POST). The shape is stable across all custom engines: ```json theme={null} { "metadata": { "ruleEngineId": "MRE-3a2f1d7b-8c4e-49ee-b1a5-2f9c0d6e80a1", "userGuid": "USR-…", "userEmail": "alice@example.com", "gatewayGuid": "GWY-…", "serverGuid": "MIS-…", "sessionId": "ckyxxxxxxxxxxxxxxxxx", "timestamp": "2026-05-08T15:00:00.000Z", "direction": "response", "toolName": "lookup_customer", "method": "tools/call", "requestId": 7 }, "body": { "jsonrpc": "2.0", "id": 7, "result": { "content": [{ "type": "text", "text": "Customer email: alice@example.com" }] } } } ``` On a **request**-direction rule, `body` is the tool call and the arguments live under `body.params.arguments`. On a **response**-direction rule, `body` is the tool result, shown above. ### Variations you'll see in `body.result` MCP servers don't all produce the same result shape. A response-direction engine should handle these: ```json theme={null} // 1. OpenAI-shaped content envelope (most common) { "result": { "content": [{ "type": "text", "text": "Customer email: alice@example.com" }] } } // 2. Multiple content items (rare but legal) { "result": { "content": [ { "type": "text", "text": "Customer email: alice@example.com" }, { "type": "text", "text": "Address: 123 Main St" } ] } } // 3. Plain string result (older MCP servers) { "result": "Customer email: alice@example.com" } // 4. Tool-call error from the upstream MCP server { "error": { "code": -32603, "message": "Internal error" } } ``` When `error` is present and `result` is missing, there's usually nothing to inspect — the right move is to return `pass`, since blocking an error response just compounds the failure. ## The four responses Return one of these JSON shapes with HTTP `200 OK`. ### pass — let the message through unchanged ```json theme={null} { "type": "pass", "comment": "no PII detected" } ``` The gateway forwards the original message with no modification. `comment` lands in the `rule_engine_comment` column in your [logs](/features/viewing-logs). ### block — reject the message ```json theme={null} { "type": "block", "comment": "credit card detected: 4111-XXXX-XXXX-XXXX" } ``` The gateway replaces the upstream message with a JSON-RPC error so the client knows the call was blocked. If the rule has [alerts](/features/alerts) enabled, an alert appears with your comment attached. Keep `comment` short and human-readable — it's what the alert renderer displays. ### modify — rewrite the message The `modifiedPayload.body` you return is what the gateway forwards in place of the original. It must be a **complete, valid JSON-RPC response**: `jsonrpc: "2.0"`, the **same `id`** as the inbound request (echo `metadata.requestId`), and either a `result` or an `error` field. ```json theme={null} { "type": "modify", "comment": "redacted email", "modifiedPayload": { "body": { "jsonrpc": "2.0", "id": 7, "result": { "content": [{ "type": "text", "text": "Customer email: [REDACTED]" }] } } } } ``` To replace a plain-string result, return `"result": "Customer email: [REDACTED]"` instead of the content envelope. To surface a tailored error to the client rather than the generic block message, return an `error` object in place of `result`: ```json theme={null} { "type": "modify", "comment": "PII present, returning sanitized error", "modifiedPayload": { "body": { "jsonrpc": "2.0", "id": 7, "error": { "code": -32603, "message": "Sensitive data can't be returned through this channel." } } } } ``` If `modifiedPayload.body` isn't a valid JSON-RPC envelope — missing `jsonrpc`, missing `id`, or neither `result` nor `error` — the gateway treats it as malformed and falls through to the rule's [failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails). Don't include extra top-level fields beyond `jsonrpc` / `id` / `result` / `error`. ### error — you can't decide ```json theme={null} { "type": "error", "comment": "upstream classifier timed out" } ``` Use this when your engine ran but couldn't reach a verdict. The gateway falls through to the rule's **failure mode**: with failure mode **Allow** the original message passes through unchanged; with failure mode **Block** the message is blocked. Failure mode is set on the gateway rule, not the engine, so the same engine can be wired to different rules with different failure-mode policies. The **default failure mode for custom engines is Block.** ## What gets rejected as malformed The gateway treats any of these as an `error` outcome and routes through the rule's failure mode: * Response is not valid JSON * HTTP status not in the 200–299 range (after retries are exhausted) * Body missing the `type` field * `type` not one of `pass` / `block` / `modify` / `error` * `type: "modify"` without a `modifiedPayload.body` that is a complete JSON-RPC response * Response body larger than **16 MiB** When this happens the rule-engine row in your [logs](/features/viewing-logs) records the specific reason (for example `invalid_json`, `http_error`, or `connection_error`) so you can debug from the dashboard. MCP Manager does **not** attempt to repair malformed JSON — quietly fixing a response that should have failed is treated as a security risk, so a broken response always falls through to the failure mode. ## Operational notes A few things worth knowing before you write your webhook: * **Timeout.** The gateway times out at **30 seconds per attempt**. Your engine sits inline on tool traffic, so aim for sub-second latency in practice; the generous ceiling exists for engines that make their own downstream calls. * **Retries.** Transient failures (timeouts, 5xx) are retried with exponential backoff, up to **3 attempts** (1 initial + 2 retries). 4xx responses are deterministic and aren't retried. Make your webhook **idempotent** — receiving the same envelope twice must produce the same result. Use `metadata.sessionId` as a dedupe key if you have side effects. * **Concurrency.** Multiple tool calls can be in flight at once; each fires an independent POST. Don't assume one call at a time. * **TLS.** All calls go over HTTPS. There's no way to disable it, and self-signed certs aren't supported — use a public CA. * **No streaming.** The webhook is a single request/response — no SSE, no chunked streaming. The envelope arrives fully buffered. * **HTTP status codes.** Return `200 OK` for every shape, including `block` and `error` — those describe an outcome, not an HTTP failure. Reserve non-2xx for actual webhook failures (your service is down, the request was malformed). ## Auth and headers Anything you add in the **Headers** section of the rule engine is sent on every request, encrypted at rest until call time. Common patterns: a bearer token (`Authorization: Bearer `), a custom API key (`X-Api-Key: `), or a static signing secret you verify on your end. For defense in depth, you can also [allowlist MCP Manager's static IP](/features/gateway-rules/custom-rules-engines#defense-in-depth-allowlist-mcp-managers-ip). See [Authenticating your engine](/features/gateway-rules/custom-rules-engines#authenticating-your-engine). ## Helpers — TypeScript These helpers handle the body-shape variations above, so your route handler stays a clean four-branch switch. ```ts helpers.ts theme={null} import type { JsonRpcResponse } from './webhook-types'; /** * Pulls the user-visible text out of a tool response. Handles all three body shapes: * - OpenAI-style { content: [{ type: 'text', text }] } → joins all text items with newlines * - Plain string result → returns it * - Anything else → JSON-stringifies it so a regex / classifier still has something to scan * Returns null when the response has no result at all (e.g. a JSON-RPC error response). */ export function extractResponseText(body: JsonRpcResponse): string | null { if (body.result == null) return null; if (typeof body.result === 'string') return body.result; if (typeof body.result === 'object') { const envelope = body.result as { content?: Array<{ type?: string; text?: string }> }; if (Array.isArray(envelope.content)) { const texts = envelope.content .filter((item) => item?.type === 'text' && typeof item.text === 'string') .map((item) => item.text as string); if (texts.length > 0) return texts.join('\n'); } return JSON.stringify(body.result); } return String(body.result); } /** * Builds a new JsonRpcResponse with the given text substituted back into the same slot the * original came from. Echoes the inbound id so the modify response is JSON-RPC-valid. */ export function buildResponseWithReplacedText(original: JsonRpcResponse, replacement: string): JsonRpcResponse { if (typeof original.result === 'string') { return { jsonrpc: '2.0', id: original.id, result: replacement }; } if (original.result && typeof original.result === 'object') { const envelope = original.result as { content?: Array<{ type?: string; text?: string }> }; if (Array.isArray(envelope.content)) { const rewritten = envelope.content.map((item) => (item?.type === 'text' ? { ...item, text: replacement } : item)); return { jsonrpc: '2.0', id: original.id, result: { ...envelope, content: rewritten } }; } } return { jsonrpc: '2.0', id: original.id, result: replacement }; } ``` ## End-to-end example: Express + TypeScript A complete webhook covering all four response shapes. Drop into a Node 20+ project with `express` and `@types/express` installed. ```ts server.ts theme={null} import type { Request, Response } from 'express'; import express from 'express'; import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const app = express(); app.use(express.json({ limit: '16mb' })); // matches the gateway's body cap const SHARED_SECRET = process.env.MCP_RULE_ENGINE_SECRET ?? ''; const CREDIT_CARD_PATTERN = /\b(?:\d[ -]*?){13,19}\b/; const EMAIL_PATTERN = /\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b/g; app.post('/inspect', (request: Request, response: Response) => { // Verify the shared secret you configured in the rule engine's Headers section. if (request.headers['x-api-key'] !== SHARED_SECRET) { response.status(401).json({ error: 'Unauthorized' }); return; } const envelope = request.body as WebhookRequest; const text = extractResponseText(envelope.body); // No usable text (e.g. an upstream JSON-RPC error) — let it through. if (text == null) { response.json({ type: 'pass', comment: 'no text to inspect' } satisfies WebhookResponse); return; } // Hard fail: credit card detected → block. if (CREDIT_CARD_PATTERN.test(text)) { response.json({ type: 'block', comment: 'credit card number detected' } satisfies WebhookResponse); return; } // Soft fail: emails → redact in place and forward. if (EMAIL_PATTERN.test(text)) { const redacted = text.replace(EMAIL_PATTERN, '[REDACTED EMAIL]'); const modifiedBody = buildResponseWithReplacedText(envelope.body, redacted); response.json({ type: 'modify', comment: 'redacted email address(es)', modifiedPayload: { body: modifiedBody }, } satisfies WebhookResponse); return; } response.json({ type: 'pass' } satisfies WebhookResponse); }); const port = Number(process.env.PORT ?? 3000); app.listen(port, () => console.log(`Rule engine listening on :${port}`)); ``` Two things this demonstrates that you'll want in your own version: **authenticate before parsing** (verify the secret before running your classifier), and **stay idempotent** (read the body once and return deterministically, so a retry of the same envelope produces the same result). ## Common pitfalls `block` and `error` are outcomes, not HTTP failures. Return `200 OK` and put the outcome in the JSON `type`. A non-2xx status is treated as `http_error` and routed through the rule's failure mode instead. MCP clients correlate requests to responses by `id`. A mismatch makes the client wait forever and then time out. Always set `modifiedPayload.body.id` to the inbound `metadata.requestId`. Only `jsonrpc`, `id`, `result`, and `error` are accepted. Anything else gets the response rejected as malformed and falls through to the failure mode. Retries mean the same envelope can arrive more than once. If you log to an immutable audit store, insert a billing row, or fire an alert from inside the handler, key it on `metadata.sessionId` so a retry doesn't double-count. ## When to use a custom engine vs a built-in provider Use **Custom** when you want full control: your own classifier, your own retraining pipeline, your own audit trail. If you'd rather drop in a managed service, MCP Manager has built-in providers — [Amazon Bedrock Guardrails](/features/amazon-bedrock) and [Lakera Guard](/features/lakera-guard) — that translate to and from a specific vendor's API for you. You pick those from the same provider dropdown and only configure auth (and, for Bedrock, a couple of identifying fields); MCP Manager handles the request/response translation. ## Further reading Registering, testing, and managing the engine you build here. Detection methods, hooks, failure modes, actions, and rule ordering. A managed alternative to building your own engine. A security-first managed alternative to a custom engine. # Building vs. Buying an MCP Gateway Source: https://docs.mcpmanager.ai/advanced/building-vs-buying An honest look at building an MCP gateway in-house versus adopting one: why the proxy is the easy 5% and identity brokering, per-upstream OAuth, inline inspection, audit, and a constantly moving spec are where the real cost lives — the same reason teams don't build their own identity provider — plus a rough sense of the bill, what adopting gets you instead, the hybrid middle path, and the narrow cases where building yourself is still the right call. A capable engineer can stand up a working MCP gateway in an afternoon. Wrap a couple of tools, wire up the JSON-RPC handshake, route a call from Claude through to a server and back — it works, the demo lands, and the team gets a green light. Then it goes to staging, and the real shape of the problem appears. We build and operate [**MCP Manager**](/mcp-gateway-concepts/mcp-gateways), so this page is not a sales pitch that building is impossible. It plainly isn't. It's an honest account of what you are actually signing up for when you own a gateway in production — written from the inside, by the people maintaining one. If you're genuinely on the fence, read this first. ## The proxy is the part you can see The routing layer — the reverse proxy that negotiates capabilities between a client and a set of tool servers — is real work, but it's a couple of weeks of it, and coding agents make it faster still. Call it five percent of an enterprise deployment. The other ninety-five percent is everything that makes the gateway *safe to put sensitive traffic through*: brokering identity, holding credentials, inspecting traffic in flight, attributing every action, and keeping all of it current as the protocol moves underneath you. None of that shows up in the afternoon demo. All of it is blocking before the first real user. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#aed8ff','primaryTextColor':'#062b4c','primaryBorderColor':'#0b4880','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TB subgraph above["Above the surface — what the demo shows (~5%)"] P["MCP proxy / JSON-RPC router"] end subgraph below["Below the surface — what production needs (~95%)"] direction LR I1["Identity brokering
& per-user OAuth"] I2["Inline traffic
inspection"] I3["Audit trail
& retention"] I4["Multi-tenant
isolation"] I5["Compliance
(SOC 2, ISO 27001…)"] I6["Tracking a
moving spec"] end above --- below classDef visible fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; classDef warn fill:#ffd863,color:#12141d,stroke:#ffa535,stroke-width:1.5px; class P visible; class I1,I2,I3,I4,I5,I6 warn; style above fill:transparent,stroke:#9ca1ab,stroke-dasharray:4 3,color:#6a6b76; style below fill:transparent,stroke:#9ca1ab,stroke-dasharray:4 3,color:#6a6b76; ``` It's the same reason a team that can validate a password doesn't conclude they've built an identity provider. The hard part was never the part you could see. ## It looks like an API gateway. It behaves nothing like one. The instinct is to reach for the familiar pattern: a reverse proxy, some OAuth, structured logs, a rate limiter. Engineers have built each of those before. But an API gateway guards a fixed internal estate whose routes change slowly and only by a developer's hand. An MCP gateway sits between AI agents and a sprawling, fast-moving ecosystem of third-party servers, and the traffic is a different animal: * **It's volatile.** An agent stuck in a loop can fire hundreds of calls a minute. Load arrives in bursts you didn't schedule. * **It's natural language wrapped in tool definitions.** A payload that reads as ordinary developer text to a generic filter can carry an instruction to exfiltrate data. The structural context — which tool, which server, which identity, what came before — *is* the signal, and content-only inspection throws it away. * **The threat model is new and still moving.** The catalog of MCP-specific attacks is growing and getting more sophisticated faster than generic guardrails keep up. (The [Security Overview](/security/overview) walks through the current set.) ## What surfaces after the demo This is the part teams don't price in, because you can't see it from the prototype. A few of the walls you hit — not a to-do list, just an honest picture of the terrain: * **Authentication is two-sided, and nobody implements it the same way.** Your gateway has to be an OAuth *authorization server* to your agents and an OAuth *client* to every upstream server at once. OAuth is a standard; the implementations are not. One provider rotates refresh tokens on every use and another never does; one omits expiry entirely; one answers an expired token with a clean `401` and another returns `200 OK` with the failure buried in the body. Each divergence is a quiet week of work and a new way for a user's agent to silently lose access on a Thursday afternoon. * **The ecosystem is young and rough at the edges.** Sessions drop and have to be recovered without spiralling into a retry loop. Some servers only signal a dead session through a prose error string, not a status code. Transports differ in their framing and their failure modes. None of this is in the spec's happy path; all of it shows up in production. * **You can inspect traffic, or you can stream it — not both for free.** The moment you want to catch a leaked secret or a poisoned tool result, you have to hold the response to look at it instead of passing bytes straight through. Do that without care and you've traded away either latency or memory. This is a genuine architectural tension, not a setting. * **Governance cannot live in the prompt.** Telling a model it has read-only access is not enforcement — given a tool that can write, a model may use it if its reasoning concludes that would help. That isn't a jailbreak; it's the model doing its job. Real enforcement has to be deterministic and sit *below* the model, which is another system to design, build, and defend. (See [Runtime Protections](/security/runtime-protections) and [Feature Governance](/security/feature-governance).) * **Identity is a graph, not a column.** Who may call which tool, under whose credentials, attributed to whom — across users, teams, the whole organization, and every connection — is a multi-tenant isolation problem from day one, not a field you add later. The authentication problem alone — just the first bullet — already looks like this, because your one gateway is both sides of OAuth at once and no two upstreams agree: ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart LR A["🤖
Your agents & clients
(Claude, Cursor, …)"] GW["🛡️
Your gateway
OAuth server ⟷ OAuth client"] A -->|"you are their
authorization server"| GW GW -->|"rotates refresh
token every call"| U1["🖥️
Upstream A"] GW -->|"never rotates"| U2["🖥️
Upstream B"] GW -->|"omits token expiry"| U3["🖥️
Upstream C"] GW -->|"401 → expired"| U4["🖥️
Upstream D"] GW -->|"200 OK — error
buried in body"| U5["🖥️
Upstream E"] classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef client fill:#80cbc4,color:#062b4c,stroke:#00796b,stroke-width:1.5px; classDef server fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; class GW gateway; class A client; class U1,U2,U3,U4,U5 server; ``` We're deliberately not handing you the blueprint here. The point isn't *how* each of these is solved; it's that there are far more of them than the whiteboard suggests, and they keep arriving. ## The spec moves, and you inherit its roadmap A one-time build is a fiction. The Model Context Protocol is actively evolving — transports have shifted, auth patterns have changed, and new capabilities land regularly. Own a gateway and you inherit that roadmap whether or not it's on yours. On top of the spec itself, every upstream vendor changes its own OAuth behavior, deprecates a scope, or bumps an API version on its own schedule, and each change is a potential silent breakage you have to chase down. That's not project work that ends; it's an operating cost that doesn't. A modest integration surface is a permanent half-person at the very least, and it grows with every server you add. ## You'd be maintaining a security product, not shipping a feature This is the heart of it. Companies federate to Okta or Entra instead of writing their own authorization server, and buy an endpoint-protection product instead of writing their own — not because they couldn't, but because security and governance infrastructure is a discipline of its own, best left to a team that does nothing else and learns from every customer at once. An MCP gateway is exactly that kind of platform. Two costs in particular tend to be discovered late: * **Bus factor.** A gateway one motivated senior engineer built in a quarter becomes load-bearing infrastructure two years later that nobody else fully understands. * **Compliance creep.** Audit logging starts as a nice-to-have and becomes mandatory the moment you sell into a regulated industry. A SOC 2 Type II report alone requires a months-long observation window, and the controls — immutable audit trails, retention policies, access reviews — have to be designed in from the start. Retrofitting them into a gateway that wasn't built for them can cost as much as building it did. ## Detection gets sharper the more it sees There's a subtler reason this is hard to do well alone: the quality of threat detection is largely a function of how much malicious traffic you've already seen. A team defending MCP traffic across many organizations turns a single attack on one customer into a defense for all of them — the dataset, the detection models, and the response playbooks all compound. An in-house gateway only ever sees its own traffic, so it starts near zero against each new technique and stays a step behind a threat landscape that's moving quickly. You'd be asking one platform team to keep pace, alone, with the whole ecosystem's worth of adversaries. ## A rough sense of the bill Exact figures depend entirely on your environment, so treat these as illustrative order-of-magnitude ranges, not a quote: | Layer | Rough effort | | ---------------------------------------- | ---------------------------------------------- | | The MCP proxy itself | 2–4 weeks | | SSO federation (integration + licensing) | weeks, plus annual license cost | | [SCIM](/enterprise/scim) provisioning | 4–8 weeks | | Per-upstream OAuth | \~1 week each to build — then permanent upkeep | | Admin console, audit logging, retention | weeks to months | | Compliance (e.g. SOC 2 Type II) | 6+ months of calendar time | Stacked up, it's the layer you *can't* see finishing that dominates — the visible proxy is the cheap base, and the perpetual cost lives in the per-upstream auth above it: ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TB L7["Compliance — SOC 2 Type II, ISO 27001
high · 6+ months calendar"] L6["Admin console + audit logging + retention
medium · ongoing"] L5["Policy / governance enforcement
medium-high · ongoing"] L4["⚠️
Per-upstream OAuth, per user
high · RECURRING, forever"] L3["SCIM provisioning
medium-high · 4–8 weeks"] L2["SSO / identity federation
medium · + licensing"] L1["MCP proxy / router
low · 2–4 weeks, once"] L1 --> L2 --> L3 --> L4 --> L5 --> L6 --> L7 classDef visible fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; classDef warn fill:#ffd863,color:#12141d,stroke:#ffa535,stroke-width:1.5px; class L1 visible; class L4 warn; ``` Added up, in-house first-year builds commonly land in the low-to-mid six figures and **six to nine months before the first user is governed** — and then the maintenance line never goes away. The proxy you can see is rarely more than a rounding error against that total. And the maintenance line is the one that quietly hurts most: every engineer-week spent chasing a vendor's changed OAuth scope is a week not spent on the product only your company can build. We've watched capable teams start down the build road with a strong platform group and a clear plan, and arrive at the same realization a few months in: the routing layer shipped on schedule, and everything *around* it — the per-upstream auth, the audit trail an auditor would accept, the identity model that survives a reorg — quietly became a standing program no one had staffed. The build estimate was for the five percent. ## What adopting a gateway gets you instead The flip side of every cost above is what you *don't* carry when you adopt a gateway rather than build one: | | Build in-house | Adopt a gateway | | -------------------------------------- | --------------------- | ------------------------------ | | Time to first governed user | months | weeks | | Per-upstream OAuth upkeep | yours, forever | absorbed for you | | Spec & vendor-change tracking | yours to chase | handled upstream | | Compliance evidence (SOC 2, ISO 27001) | a build line item | a vendor checkbox | | Threat detection | only your own traffic | sharpens across every customer | | Where your engineers spend their time | maintaining a gateway | your own product | None of this makes building wrong everywhere — it just moves the burden off your roadmap and onto a team whose whole job is to carry it. ## When building it yourself is the right call We'd rather you make this decision clear-eyed than regret it, so here's the honest version. Building in-house is the right answer when **all** of these hold: * **Your integration surface is narrow and stable** — a handful of internal services you control, with no plan to add a long tail of third-party SaaS. The per-upstream OAuth burden, which is where the cost compounds, shrinks dramatically. * **You have platform capacity available now** — not theoretical headroom, but engineers who can own the gateway indefinitely and aren't on your product's critical path. * **You have a hard requirement no vendor can meet** — a fully air-gapped environment, an exotic compliance regime, or integration with a proprietary internal identity system. * **You're not on a clock** — your security team can credibly hold the line on AI adoption for the better part of a year while you build. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TD Q1{"Integration surface
narrow & stable?"} Q2{"Platform capacity
available now?"} Q3{"Hard requirement
no vendor meets?"} Q4{"No near-term
adoption clock?"} Build["Build in-house"] Buy["🛡️
Adopt a gateway"] Hybrid["✅
Hybrid — own internal layers,
adopt the governance layer"] Q1 -->|yes| Q2 Q1 -->|no| Buy Q2 -->|yes| Q3 Q2 -->|no| Buy Q3 -->|yes| Q4 Q3 -->|no| Hybrid Q4 -->|yes| Build Q4 -->|no| Buy classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef trust fill:#2fedb4,color:#062b4c,stroke:#059669,stroke-width:1.5px; class Buy gateway; class Hybrid trust; ``` If even one of those isn't true, the math gets ugly faster than it looked at the whiteboard — and the bypass risk grows, because every month without a governed path is a month teams wire up ungoverned MCP servers on their own. ## The middle path: own some layers, adopt others The most considered teams we talk to don't treat this as all-or-nothing. They split the stack by where the build economics are worst. Proprietary internal tooling — homegrown services that will never appear in a public catalog — is theirs to own, and a thin proxy in front of it is a reasonable build. The integration-and-governance layer — per-user OAuth across a long tail of third-party SaaS, identity, audit, compliance — is the one with the ugliest economics, so it's the first they hand to a vendor. The useful question is rarely "build or buy" but "which layers do we own, and which do we let someone else carry?" ## A few questions worth answering honestly Before committing to build, the questions that tend to predict regret aren't about the proxy — they're about everything after it: * How many third-party integrations will you need in twelve months? In twenty-four? * Who owns the fix when an upstream changes its OAuth flow next quarter? * Have you budgeted SSO licensing and [SCIM](/enterprise/scim), or assumed manual provisioning at a hundred-plus users? * Do you need SOC 2 Type II evidence for this system, and has the observation window been scoped? * If the build stalls, what's the exit — and does it mean buying anyway, on top of the sunk cost? If two or more of those are uncomfortable, the build case is weaker than it looked on the whiteboard. For most organizations the honest question isn't whether the team *could* build a gateway. It's whether the next six to nine months of platform engineering are better spent building one, or building the thing only your company can. ## Further reading How the gateway is hardened as the control point in the path of every call. The MCP-specific threats a gateway has to address, and how each is met. Where a gateway fits in an enterprise AI control stack, and how to lock it down. Where MCP Manager runs, what stays in your environment, and the self-hosting question. # Custom Rule Engine Examples Source: https://docs.mcpmanager.ai/advanced/custom-rule-engine-examples Six recipes for what a custom rule-engine webhook can do with the modify and block verdicts: slim verbose responses to cut tokens, strip or redact fields by name, summarize long text, scope data to the caller's identity, enforce a channel or project allowlist, and block on policy violations. Webhooks can rewrite tool responses in flight, offering powerful capabilities — from PII redaction to cost savings to general-purpose transforms on the data your agents see. This page is a cookbook of six recipes showing what the [**modify** and **block** verdicts](/advanced/building-a-custom-rule-engine#the-four-responses) make possible, each with a runnable handler and before/after responses. This is the applied companion to [Building a Custom Rule Engine](/advanced/building-a-custom-rule-engine), which is the authoritative reference for the request envelope, the four response shapes, `modifiedPayload.body` validation, timeouts, and retries. Read that first — everything here builds on its contract and reuses its [shared types](/advanced/building-a-custom-rule-engine#the-contract-as-typescript-types) (`WebhookRequest`, `WebhookResponse`) and [helpers](/advanced/building-a-custom-rule-engine#helpers-—-typescript) (`extractResponseText`, `buildResponseWithReplacedText`). These recipes are **instructional**. They illustrate the contract and the shape of each transform — not production-hardened code. Before relying on one, add the input validation, error handling, authentication, observability, and tests your environment requires. Treat them as starting points, not drop-in implementations. Every recipe below is a function you'd call from the `/inspect` route handler in that page's [Express example](/advanced/building-a-custom-rule-engine#end-to-end-example-express-+-typescript). They sort into three motivations: | Motivation | Recipes | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Cost** — fewer tokens for the model to read | [Slim verbose responses](#slim-verbose-responses-to-cut-token-cost), [Summarize long fields](#summarize-long-fields-in-place) | | **Data governance** — control what data can ever reach the model | [Strip or redact fields by name](#strip-or-redact-fields-the-model-should-never-see), [Channel / project / space allowlist](#restrict-a-server-to-an-allowlist-of-channels-projects-or-spaces), [Block on policy violation](#block-and-audit-on-a-policy-violation) | | **Identity** — scope data to who's actually asking | [Identity-scoped filtering](#scope-results-to-the-calling-identity) | ## How the transform fits in Five of the six recipes below fire on a **response**-direction rule, inspect `body.result`, and hand the gateway a rewritten (or rejected) result. The [channel / project / space allowlist](#restrict-a-server-to-an-allowlist-of-channels-projects-or-spaces) recipe uses **both** directions — a request-leg rule blocks writes to off-limits targets, and a response-leg rule filters reads. The agent only ever sees what your webhook returns. ```mermaid theme={null} flowchart LR A[MCP server
tool result] --> B[MCP Manager gateway] B -->|POST envelope| C[Your webhook] C --> D{Transform
body.result} D -->|modifiedPayload.body| B B -->|slimmed / redacted / scoped result| E[Agent / LLM] ``` These examples assume the tool returns its structured data as a JSON string inside `body.result.content[0].text` — the most common shape, and the one `extractResponseText` and `buildResponseWithReplacedText` are built for. If your server populates `result.structuredContent` instead, apply the same parse → transform → re-serialize logic to that object and set it back on `result`. See [the result variations](/advanced/building-a-custom-rule-engine#variations-you’ll-see-in-body-result) for the shapes you might encounter. **A rule engine runs on every `tools/call` flowing through the gateway.** There is no per-server or per-tool selector in the rule UI, so your webhook sees every tool from every server on that gateway — including tools that legitimately return non-JSON. Each recipe therefore checks `metadata.serverGuid` and `metadata.toolName` first and **returns `pass` immediately** for anything it wasn't written for, so unrelated traffic is waved through untouched and cheaply. Only after confirming it's the target server and tool do we parse the result; because that tool is contracted to return JSON, a result that *isn't* parseable JSON is a real anomaly and gets **blocked** rather than passed. ## Slim verbose responses to cut token cost *Goal: cut token cost.* MCP servers tend to return everything they know about an object — twenty-plus fields when your agent needs three. Every unused field is input tokens the model pays to read on every call. An **allowlist** keeps only the fields a given agent or gateway actually uses and drops the rest, including expensive rich-text fields like `description`. ```ts slim-fields.ts theme={null} import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; // Scope: only transform responses from this one tool on this one server. const TARGET_SERVER = 'MIS-7c2a4e91'; // the McpInboundServer guid this rule is scoped to const TARGET_TOOL = 'get_account'; // The only fields the agent needs from this tool's records. const ALLOWED_FIELDS = ['id', 'name', 'status'] as const; export function slimResponse(envelope: WebhookRequest): WebhookResponse { // The engine sees every tool on the gateway. Act only on our server + tool; // pass everything else straight through. if (envelope.metadata.serverGuid !== TARGET_SERVER || envelope.metadata.toolName !== TARGET_TOOL) { return { type: 'pass' }; } const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result present to transform' }; let record: Record; try { record = JSON.parse(text); } catch { // Our tool is supposed to return JSON. If it didn't, something is wrong — fail loudly. return { type: 'block', comment: `expected JSON from '${TARGET_TOOL}' but the response was not parseable` }; } const slimmed: Record = {}; for (const field of ALLOWED_FIELDS) { if (field in record) slimmed[field] = record[field]; } const modifiedBody = buildResponseWithReplacedText(envelope.body, JSON.stringify(slimmed)); return { type: 'modify', comment: `slimmed ${Object.keys(record).length} fields to ${Object.keys(slimmed).length}`, modifiedPayload: { body: modifiedBody }, }; } ``` ```json Raw Tool Response theme={null} { "id": "003ABC", "name": "Acme Corp", "status": "active", "description": "A 600-word company profile the agent never reads…", "annualRevenue": 4200000, "billingAddress": { "street": "1 Market St", "city": "SF" }, "lastModifiedBy": "ops@example.com", "createdDate": "2021-04-02T10:00:00Z" } ``` ```json Modified Tool Response theme={null} { "id": "003ABC", "name": "Acme Corp", "status": "active" } ``` An allowlist (keep these) is safer than a denylist (drop these) for cost trimming: when the upstream server adds a new field next quarter, an allowlist silently ignores it instead of leaking it into every prompt. **Value — roughly a 90% token cut on every read of this object.** The raw record above is about 600 input tokens; the slimmed version is under 40. For a tool an agent calls hundreds of times a day, that difference recurs on every prompt that reads the result — a large, compounding saving with no change to the agent or the upstream server. ## Strip or redact fields the model should never see *Goal: data governance.* Some fields must never reach the model, and you know them by name — an `ssn`, an internal `creditScore`, a `compensation` figure. Because the field name is a stable, unchanging identifier, a **denylist by key** is exact and predictable. You have two strategies: * **Delete** the key entirely — the model never knows it existed. * **Redact** — keep the key but replace its value with a placeholder like `{{REDACTED}}`. The model can see that the field was present but withheld, which stops it from assuming the data is simply missing and retrying the call a different way. ```ts strip-fields.ts theme={null} import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const TARGET_SERVER = 'MIS-7c2a4e91'; // the McpInboundServer guid this rule is scoped to const TARGET_TOOL = 'get_contact'; // Fields that must never reach the model, by name. Stable identifiers, so a denylist is exact. const BLOCKED_FIELDS = new Set(['ssn', 'creditScore', 'compensation', 'internalNotes']); const STRATEGY: 'delete' | 'redact' = 'redact'; const REDACTED = '{{REDACTED}}'; export function stripFields(envelope: WebhookRequest): WebhookResponse { // The engine sees every tool on the gateway. Act only on our server + tool; // pass everything else straight through. if (envelope.metadata.serverGuid !== TARGET_SERVER || envelope.metadata.toolName !== TARGET_TOOL) { return { type: 'pass' }; } const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result present to inspect' }; let record: Record; try { record = JSON.parse(text); } catch { return { type: 'block', comment: `expected JSON from '${TARGET_TOOL}' but the response was not parseable` }; } let affected = 0; for (const field of Object.keys(record)) { if (!BLOCKED_FIELDS.has(field)) continue; affected++; if (STRATEGY === 'delete') delete record[field]; else record[field] = REDACTED; } if (affected === 0) return { type: 'pass', comment: 'no blocked fields present' }; const modifiedBody = buildResponseWithReplacedText(envelope.body, JSON.stringify(record)); return { type: 'modify', comment: `${STRATEGY === 'delete' ? 'removed' : 'redacted'} ${affected} field(s)`, modifiedPayload: { body: modifiedBody }, }; } ``` ```json Raw Tool Response theme={null} { "id": "003ABC", "name": "Jane Doe", "ssn": "123-45-6789", "creditScore": 740, "email": "jane@example.com" } ``` ```json Modified Tool Response — Redact theme={null} { "id": "003ABC", "name": "Jane Doe", "ssn": "{{REDACTED}}", "creditScore": "{{REDACTED}}", "email": "jane@example.com" } ``` ```json Modified Tool Response — Delete theme={null} { "id": "003ABC", "name": "Jane Doe", "email": "jane@example.com" } ``` This differs from a regex or Presidio rule, which matches on the *value* (anything that looks like an SSN). Matching on the *key* is the right tool when sensitivity is a property of the field, not its contents — a salary is just a number until you know which column it came from. **Value — a hard guarantee, not a best-effort filter.** Named sensitive fields never enter a prompt, never reach the model provider, and never land in your model-side logs. The `comment` on each `modify` gives you an auditable record of exactly which fields were withheld and how often — the kind of evidence a data-governance or compliance review asks for. ## Summarize long fields in place *Goal: cut token cost while keeping the gist.* Sometimes the model doesn't need a field gone — it needs it shorter. A 4,000-token `description`, `body`, or `notes` field where the agent only needs the gist is pure waste. Call your own model to summarize the value and splice the summary back into the response. ```ts summarize-field.ts theme={null} import { createHash } from 'node:crypto'; import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const TARGET_SERVER = 'MIS-2b81f0d4'; // the McpInboundServer guid this rule is scoped to const TARGET_TOOL = 'get_ticket'; const FIELD_TO_SUMMARIZE = 'description'; const MIN_CHARS_TO_SUMMARIZE = 1_000; // leave short values untouched const summaryCache = new Map(); // swap for a shared store (Redis, etc.) in production async function summarize(text: string): Promise { // Call your own model however you like. Stay well inside the 30s tool-call budget. // return await myModel.summarize(text); return text; // placeholder } export async function summarizeLongField(envelope: WebhookRequest): Promise { // The engine sees every tool on the gateway. Act only on our server + tool; // pass everything else straight through. if (envelope.metadata.serverGuid !== TARGET_SERVER || envelope.metadata.toolName !== TARGET_TOOL) { return { type: 'pass' }; } const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result present to summarize' }; let record: Record; try { record = JSON.parse(text); } catch { return { type: 'block', comment: `expected JSON from '${TARGET_TOOL}' but the response was not parseable` }; } const value = record[FIELD_TO_SUMMARIZE]; if (typeof value !== 'string' || value.length < MIN_CHARS_TO_SUMMARIZE) { return { type: 'pass', comment: 'nothing long enough to summarize' }; } // Cache by content hash so a retried envelope returns the same summary (idempotency). const cacheKey = createHash('sha256').update(value).digest('hex'); let summary = summaryCache.get(cacheKey); if (summary == null) { summary = await summarize(value); summaryCache.set(cacheKey, summary); } record[FIELD_TO_SUMMARIZE] = summary; const modifiedBody = buildResponseWithReplacedText(envelope.body, JSON.stringify(record)); return { type: 'modify', comment: `summarized '${FIELD_TO_SUMMARIZE}' (${value.length} → ${summary.length} chars)`, modifiedPayload: { body: modifiedBody }, }; } ``` ```json Raw Tool Response theme={null} { "id": "TICK-91", "subject": "Login fails after SSO migration", "description": "…1,800 words of back-and-forth troubleshooting, stack traces, and reply chains…" } ``` ```json Modified Tool Response theme={null} { "id": "TICK-91", "subject": "Login fails after SSO migration", "description": "User can't log in after the SSO migration; SAML assertion is rejected as expired. Unresolved." } ``` This recipe is the one that does real work per call, so mind the [operational limits](/advanced/building-a-custom-rule-engine#operational-notes): you're inline on the tool path with a 30-second ceiling, and the gateway can retry the same envelope. Caching by content hash keeps retries cheap and deterministic, and means two calls returning the same long text only pay for one summarization. **Value — roughly 99% off the cost of one bloated field, gist intact.** An 1,800-word `description` is about 2,400 input tokens; a one-line summary is around 25. You trade a single summarization call (cached, so retries are free) for a permanent per-read saving on a field the agent only ever skims. ## Scope results to the calling identity *Goal: identity-aware data governance.* A shared tool — `list_opportunities`, `search_documents` — often returns everything, regardless of who asked. The envelope's `metadata.userGuid` tells you which user triggered the call, so you can filter the result down to the records that user is allowed to see. The envelope also carries `metadata.userEmail` — the same caller's email address — which is often a more convenient join key than the GUID when your access model is keyed on email (an identity provider, a CRM `owner` field, a directory lookup). This recipe resolves on `userGuid`; swap in `userEmail` wherever you'd resolve the owner if that maps more cleanly to your data. Combine either with [runtime header forwarding](/features/gateway-rules/custom-rules-engines) to receive the inbound connection's identity headers and map them to your own access model. ```ts identity-filter.ts theme={null} import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const TARGET_SERVER = 'MIS-9a3e5c20'; // the McpInboundServer guid this rule is scoped to const TARGET_TOOL = 'list_opportunities'; // Map MCP Manager's userGuid to the owner key your data uses. In practice you'd look this up, // or read a forwarded identity header off the request. If your directory is keyed on email, // resolve on envelope.metadata.userEmail instead — the same null-handling applies. async function resolveOwnerId(userGuid: string | null): Promise { if (userGuid == null) return null; // return await myDirectory.ownerIdFor(userGuid); return userGuid; } export async function filterToCaller(envelope: WebhookRequest): Promise { // The engine sees every tool on the gateway. Act only on our server + tool; // pass everything else straight through. if (envelope.metadata.serverGuid !== TARGET_SERVER || envelope.metadata.toolName !== TARGET_TOOL) { return { type: 'pass' }; } const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result present to scope' }; let records: unknown; try { records = JSON.parse(text); } catch { return { type: 'block', comment: `expected JSON from '${TARGET_TOOL}' but the response was not parseable` }; } if (!Array.isArray(records)) { return { type: 'block', comment: `expected a list from '${TARGET_TOOL}' but got something else` }; } const ownerId = await resolveOwnerId(envelope.metadata.userGuid); if (ownerId == null) { // Can't establish who's asking — fail closed rather than over-share. return { type: 'block', comment: 'no caller identity to scope results to' }; } const visible = records.filter((row) => (row as { ownerId?: string }).ownerId === ownerId); const modifiedBody = buildResponseWithReplacedText(envelope.body, JSON.stringify(visible)); return { type: 'modify', comment: `scoped ${records.length} → ${visible.length} record(s) for caller`, modifiedPayload: { body: modifiedBody }, }; } ``` ```json Raw Tool Response theme={null} [ { "id": "OPP-1", "name": "Acme renewal", "ownerId": "USR-jane" }, { "id": "OPP-2", "name": "Globex expansion", "ownerId": "USR-raj" }, { "id": "OPP-3", "name": "Initech pilot", "ownerId": "USR-jane" } ] ``` ```json Modified Tool Response — Caller USR-jane theme={null} [ { "id": "OPP-1", "name": "Acme renewal", "ownerId": "USR-jane" }, { "id": "OPP-3", "name": "Initech pilot", "ownerId": "USR-jane" } ] ``` Note the **fail-closed** choices: when the engine can't establish who's calling, it blocks rather than returning the full, unfiltered list. Returning an empty array is the gentler alternative when an unidentified caller should simply see nothing. **Value — one rule enforces per-user scoping everywhere the tool is used.** Instead of teaching every agent prompt and every upstream MCP server about row-level access, a single webhook trims each response to the caller's own records. That closes the over-exposure gap on shared, broadly-scoped tools and gives you one auditable place where the access rule lives. ## Restrict a server to an allowlist of channels, projects, or spaces *Goal: data governance.* Allowing an agent to reach a server doesn't mean it should reach every channel, project, or space on that server. Two rules cover the two ways data can move: a **request**-leg rule blocks writes to a disallowed target before the call goes upstream; a **response**-leg rule strips disallowed items from results before they reach the agent. A rule on one leg only leaves the other open — blocking writes still leaves reads and searches open, and filtering search results doesn't stop a write. This pattern pairs with, not replaces, source-side credential scoping. Connecting the server with a credential that already has only the intended access is the primary control; these rules are the gateway-side backstop and audit layer. ```ts channel-allowlist.ts theme={null} import { buildResponseWithReplacedText, extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const TARGET_SERVER = 'MIS-3f9a2b14'; // McpInboundServer guid for your Slack server // Tools that write to a specific channel — inspect the request arguments. const WRITE_TOOLS = new Set(['chat_postMessage', 'conversations_invite']); // Tools that return messages from multiple channels — filter the response. const SEARCH_TOOLS = new Set(['search_messages']); // Allowed channel IDs. IDs are stable across renames; resolve names → IDs at deploy time // or call the Slack API at runtime via the forwarded auth header (see tip below). const ALLOWED_CHANNEL_IDS = new Set(['C01ABCDEF', 'C02GHIJKL']); /** * Request-leg handler — blocks writes to channels outside the allowlist. * Wire to a rule with direction: Request. * Arguments arrive in body.params.arguments for tools/call requests. */ export function enforceWriteAllowlist(envelope: WebhookRequest): WebhookResponse { if (envelope.metadata.serverGuid !== TARGET_SERVER) return { type: 'pass' }; if (!WRITE_TOOLS.has(envelope.metadata.toolName ?? '')) return { type: 'pass' }; const args = envelope.body.params?.arguments as Record | undefined; const channelId = typeof args?.channel === 'string' ? args.channel : null; if (channelId == null) { return { type: 'block', comment: 'channel argument missing — cannot verify allowlist' }; } if (!ALLOWED_CHANNEL_IDS.has(channelId)) { return { type: 'block', comment: `blocked write to channel ${channelId} — not in allowlist (tool: ${envelope.metadata.toolName})`, }; } return { type: 'pass', comment: 'channel in allowlist' }; } /** * Response-leg handler — strips search results from channels outside the allowlist. * Wire to a rule with direction: Response. */ export function enforceSearchAllowlist(envelope: WebhookRequest): WebhookResponse { if (envelope.metadata.serverGuid !== TARGET_SERVER) return { type: 'pass' }; if (!SEARCH_TOOLS.has(envelope.metadata.toolName ?? '')) return { type: 'pass' }; const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result to filter' }; let result: { messages?: { matches?: Array<{ channel?: { id?: string } }> } }; try { result = JSON.parse(text); } catch { return { type: 'block', comment: `expected JSON from '${envelope.metadata.toolName}' but the response was not parseable`, }; } const matches = result.messages?.matches ?? []; const before = matches.length; const filtered = matches.filter((m) => m.channel?.id != null && ALLOWED_CHANNEL_IDS.has(m.channel.id)); if (filtered.length === before) return { type: 'pass', comment: 'all results in allowlist' }; result.messages = { ...result.messages, matches: filtered }; const modifiedBody = buildResponseWithReplacedText(envelope.body, JSON.stringify(result)); return { type: 'modify', comment: `stripped ${before - filtered.length} result(s) from channels outside the allowlist`, modifiedPayload: { body: modifiedBody }, }; } ``` **Request leg** — a write to a disallowed channel is blocked before it reaches Slack: ```json Raw Tool Call Arguments theme={null} { "channel": "C09RESTRICTED", "text": "Here is the weekly report…" } ``` ```json Engine Verdict — sent back to the gateway theme={null} { "type": "block", "comment": "blocked write to channel C09RESTRICTED — not in allowlist (tool: chat_postMessage)" } ``` **Response leg** — a search that would have returned messages from an off-limits channel returns only permitted matches instead: ```json Raw Tool Response theme={null} { "messages": { "matches": [ { "text": "Budget discussion", "channel": { "id": "C01ABCDEF", "name": "finance-team" } }, { "text": "Restricted post", "channel": { "id": "C09RESTRICTED", "name": "restricted-hr" } }, { "text": "Product update", "channel": { "id": "C02GHIJKL", "name": "product" } } ] } } ``` ```json Modified Tool Response theme={null} { "messages": { "matches": [ { "text": "Budget discussion", "channel": { "id": "C01ABCDEF", "name": "finance-team" } }, { "text": "Product update", "channel": { "id": "C02GHIJKL", "name": "product" } } ] } } ``` Using `modify` on the response leg means the search returns permitted matches rather than failing outright — a better experience than an opaque block when some results are legitimate. The `comment` on every verdict records the channel ID and tool name so each enforcement action lands in the logs with an auditable reason. **Resolving identifiers at runtime.** Allowlists keyed on IDs are stable — a renamed channel keeps the same ID. To resolve names to IDs at runtime rather than hardcoding them at deploy time, enable **Forward headers** on the rule; your webhook then receives the caller's upstream auth header and can call the Slack (or Atlassian) API as that user to confirm membership or look up an ID, rather than maintaining a static list. See [Custom Rule Engines](/features/gateway-rules/custom-rules-engines) for how to enable header forwarding. **Value — covers both data directions with two rules and a full audit trail.** Writes to off-limits channels are stopped before they reach Slack; searches return only permitted matches instead of failing outright. Every verdict writes the channel ID and tool name to the logs — the evidence a security review needs to confirm the control is working, not just configured. The same logic applies to Jira projects and Confluence spaces — the allowlist check is identical; only the tool names and the JSON path to the project or space identifier differ. ## Block and audit on a policy violation *Goal: data governance and compliance.* Some responses must never pass at all — a document classified above the caller's clearance, or one carrying a restricted data class. Detect the condition and return [**block**](/advanced/building-a-custom-rule-engine#block-—-reject-the-message): the gateway replaces the result with a JSON-RPC error, and if the rule has [alerts](/features/alerts) enabled, your `comment` is what the alert renders. The same comment lands in the `rule_engine_comment` column in your [logs](/features/viewing-logs), giving you an audit trail of every blocked call. ```ts block-on-classification.ts theme={null} import { extractResponseText } from './helpers'; import type { WebhookRequest, WebhookResponse } from './webhook-types'; const TARGET_SERVER = 'MIS-4d7b1f88'; // the McpInboundServer guid this rule is scoped to const TARGET_TOOL = 'get_document'; // Upstream stamps documents with a classification marker. Anything restricted or above // must never reach the model through this gateway. const BLOCKED_CLASSIFICATIONS = new Set(['restricted', 'secret']); export function blockOnClassification(envelope: WebhookRequest): WebhookResponse { // The engine sees every tool on the gateway. Act only on our server + tool; // pass everything else straight through. if (envelope.metadata.serverGuid !== TARGET_SERVER || envelope.metadata.toolName !== TARGET_TOOL) { return { type: 'pass' }; } const text = extractResponseText(envelope.body); if (text == null) return { type: 'pass', comment: 'no result present to classify' }; let record: { classification?: string }; try { record = JSON.parse(text); } catch { // For a governance gate, an unreadable response is exactly when to fail closed. return { type: 'block', comment: `expected JSON from '${TARGET_TOOL}' but the response was not parseable` }; } const classification = record.classification?.toLowerCase(); if (classification && BLOCKED_CLASSIFICATIONS.has(classification)) { return { type: 'block', comment: `blocked ${classification} document from tool '${envelope.metadata.toolName}'`, }; } return { type: 'pass', comment: 'classification within policy' }; } ``` ```json Raw Tool Response theme={null} { "id": "DOC-44", "title": "FY27 acquisition shortlist", "classification": "restricted", "body": "…" } ``` ```json Engine Verdict — sent back to the gateway theme={null} { "type": "block", "comment": "blocked restricted document from tool 'get_document'" } ``` The gateway then replaces the tool result with a JSON-RPC error, so the client sees a clean failure rather than the protected content. If you'd rather hand the agent a graceful, on-brand message than the generic block error, return a [`modify` with an `error` body](/advanced/building-a-custom-rule-engine#modify-—-rewrite-the-message) instead of `block` — for example, an error whose message reads "This document can't be accessed through this assistant." Either way the data never leaves the gateway. The **custom** provider's response contract is `type` + `comment` + (for modify) `modifiedPayload`. There's no structured `detections` field to return — that's reserved for the built-in Presidio and Lakera providers. Put what a human needs to know into `comment`. **Value — a hard compliance stop with a built-in audit trail.** Restricted data never leaves the gateway, regardless of how the agent phrased the request. Every block writes its `comment` to the alert and the log, so you get a per-incident record of what was withheld and from which tool — the difference between a control you can attest to and a filter you hope is working. ## Combining recipes These aren't mutually exclusive. A single webhook can run several in sequence — block on classification first, then strip fields by name, then slim and summarize what's left — returning a single `modify` with the cumulative result. You can also split them across [several gateway rules](/features/gateway-rules/overview) on the same gateway, each pointed at the same engine or different ones; rules fire in order, and the first `block` short-circuits the rest. Keep each transform small and idempotent, and let the rule ordering compose them. ## Further reading The full webhook contract these recipes build on: envelope, response shapes, validation, and limits. Registering, testing, header forwarding, and managing the engine in the UI. Detection methods, hooks, failure modes, and how rules compose in order. Where rule-engine comments and outcomes land for auditing. # Frequently Asked Questions Source: https://docs.mcpmanager.ai/advanced/faq Answers to common MCP Manager questions: restricting a data source to specific tables, projects, channels, or folders; scoping who can use a single server such as Salesforce; whether a user can hold multiple roles and teams; applying different rules or tools to different groups by using separate gateways; disabling individual tools on a server even when the vendor offers no tool-level controls; whether there is a REST API to pull logs by session ID; whether MCP Manager supports OpenTelemetry traces as well as logs; where MCP Manager is hosted; whether there is a limit on tool response size; where to check status, uptime, or an outage or incident; and provisioning programmatically. Short answers to questions that come up often about **MCP Manager**. Each answer links to the page with the full detail. ## Can I restrict a data source to specific tables, projects, channels, or folders? Scope it **at the source**. MCP Manager governs which servers and tools are reachable and can inspect the traffic, but it does not re-implement each data source's own permissions for tables, projects, channels, or folders. The supported way to narrow a source is to connect it with a **credential or service account that already has only the access you intend to expose** — a database role limited to certain schemas, a Slack token limited to certain channels, an Asana service account added only to specific projects, or a Google service account scoped to certain folders. The gateway [rules engine](/features/gateway-rules/overview) is a **backstop** for content — blocking or redacting sensitive data in results — not a replacement for source-side scoping. Pair narrowly-scoped credentials with [identity controls](/features/identity-controls) so each user reaches the source as themselves, with their own permissions. For content- or path-level enforcement at the gateway — for example, blocking results that reference a particular Google Drive folder — you can [build a custom rule engine](/advanced/building-a-custom-rule-engine) that inspects tool calls and their results and blocks or redacts them. Gateway rules act on tool traffic, so this **complements** source-side scoping rather than replacing it. For a worked example covering Slack channels, Jira projects, and Confluence spaces, see the [channel / project / space allowlist recipe](/advanced/custom-rule-engine-examples#restrict-a-server-to-an-allowlist-of-channels-projects-or-spaces). ## Can I make someone an admin of just one server, like Salesforce? You can control **who can use** a single server, but you cannot grant management powers over only that one server. Capabilities in MCP Manager are **workspace-wide** — there is no per-gateway or per-server admin role. To restrict use of one server, put it in its own [gateway](/deployment/gateway-deployment-strategies) and [provision that gateway only to the team](/deployment/teams) that should have it, so only those users can connect to it. Management capabilities themselves — creating gateways, exporting logs, managing integrations — are granted by [capabilities](/deployment/rbac-and-roles/capabilities) at the workspace level. So "only the Salesforce team can use the Salesforce server" is fully supported; "an administrator of only the Salesforce server" is not. ## Can a user have multiple roles and multiple teams? A user has **exactly one role** and can belong to **many teams**. The split is deliberate: a role is the single, workspace-wide answer to "what is this person allowed to do," so keeping it to one role means there's never a conflict to resolve between two overlapping permission sets. Teams answer a different question — "which gateways can this person reach" — and they're additive, so adding someone to more teams simply **unions** the gateways available to them. To change what someone can *do*, change their role; to change what they can *reach*, adjust their team membership. See [Access Control](/deployment/access-control). ## Can I apply different rules or expose different tools to different groups of people? Yes — by giving them different gateways. The **gateway is the smallest unit of governance** in MCP Manager: gateway rules, the tools and resources each server exposes, and the per-server identity scheme are all configured on the gateway. There is no setting below the gateway level that applies a different rule set or tool set to some users but not others on the same gateway. So when one group needs a different policy — a stricter rule, a narrower tool set, a different identity scheme — you create another [gateway](/deployment/gateway-deployment-strategies) with that configuration and [provision it to the right team](/deployment/teams). Standing up another gateway is inexpensive, and doing so is the intended way to vary governance rather than looking for a finer-grained control inside a single gateway. ## The vendor's MCP server has no tool enable/disable settings — can I still disable individual tools? Yes — regardless of whether the upstream server supports it. Which tools a gateway exposes from each server is controlled by [feature provisioning](/features/feature-provisioning), and MCP Manager itself enforces it: a hidden tool is invisible to clients and uncallable, and a direct call to it is blocked and logged. The upstream server's own settings play no part, so a vendor that offers no tool-level enable/disable controls is governed exactly like one that does. See [Feature Governance](/security/feature-governance) for the security model. ## Is there a REST API to pull logs, for example by session ID? No. MCP Manager does not expose a public API to query or pull logs, including by session ID. MCP Manager records every request and response as a log, which you can view and export in the app and — the path for programmatic access — **forward to your own observability or SIEM platform over OpenTelemetry**. To query logs by session ID, correlation ID, user, or any other field from code, send them to your own tool (Datadog, Grafana, Splunk, Honeycomb, and others) and query them there. See [Export to SIEM](/enterprise/export-to-siem). ## Does MCP Manager support OpenTelemetry traces, not just logs? Yes. MCP Manager emits both the OpenTelemetry **logs** and **traces** signals over OTLP/HTTP (metrics are not exported). For each proxied MCP request the gateway creates a **span** carrying the method, organization, gateway, tool, and response status, and exports it to a traces collector URL you configure — giving you a real request waterfall, not just log lines. Trace context propagates **into downstream MCP servers** over W3C `traceparent` (both the HTTP header and the JSON-RPC `params._meta`), so the gateway → upstream hop joins one trace. Every log record is also stamped with the request's `traceId`/`spanId`, so logs correlate to the trace even if you forward logs only. Two layers of correlation are available: the older `correlation_id` (also sent upstream as the `x-correlation-id` header) ties the four legs of one request together within MCP Manager's records, and the OTLP trace context ties them across services. Configure a logs URL, a traces URL, or both. See [Audit & Observability](/security/audit-and-observability) and [Export to SIEM](/enterprise/export-to-siem). ## Where is MCP Manager hosted, and do you offer EU data residency or on-premise? MCP Manager is a hosted service running on Google Cloud Platform in the United States. There is no self-hosted or on-premise version, and EU data residency is not available today. See [Hosting & Data Residency](/deployment/hosting-and-data-residency) for the full picture, including what you can run in your own environment. ## Is MCP Manager down? Where can I check status or report an outage? Check the live status page at **[status.mcpmanager.ai](https://status.mcpmanager.ai)** for current uptime, any ongoing incident, and historical reliability. If you're seeing connection failures or degraded performance, the status page is the fastest way to tell whether it's a platform-wide incident or something specific to your setup — and you can subscribe there to be notified when an incident is opened or resolved. ## Can I provision gateways and connections with an API, CLI, or Terraform? Not yet. A control-plane API, CLI, and MCP-based provisioning are in active development and not generally available; gateways, connections, and identities are created in the app today. What ships now is token-based agent connection and per-user identity passing. See [Programmatic Access](/enterprise/programmatic-access). ## Can one agent act as many different users? Yes. A single token-based host can serve many end users while using each user's own downstream credential, so every action runs as the real person and is logged as them. See [Agents that Pass Identities to MCP Manager](/advanced/agents-passing-identities). ## Is there a limit on how large a tool response can be? There is, though you're unlikely to ever reach it. The gateway accepts a single response of **up to 16 MB** — comfortably larger than what tools return in everyday use, so normal traffic flows straight through. If a response does go over, you'll get a clear MCP error rather than a half-delivered result. The limit is there for a good reason: the gateway reads each response in full to check it for leaked secrets and injected content, and capping the size keeps one unusually large response from crowding out everyone else sharing the platform. See [Architecture & Trust](/mcp-gateway-concepts/architecture-and-trust#response-size-limits). ## Further reading How each user reaches a downstream server as themselves, with their own permissions. The workspace-wide capabilities that decide what a role can do. Isolating a single server in its own gateway, and other topologies. Forward logs over OpenTelemetry and query them in your own tool. # Fix a Broken or Stale MCP Connection Source: https://docs.mcpmanager.ai/advanced/fixing-broken-connections How to fix a remote MCP server connection that worked before and then broke because of a stale authorization held on the provider's side. Recognize the symptoms — an identity flagged Needs authentication or Disconnected, tool calls returning a re-authentication error, and no provider consent screen when you retry — then re-authenticate from Settings, and when that isn't enough, deauthorize MCP Manager's app on the provider and use Authenticate again to force a fresh authorization (which moves your gateways and connections onto the repaired identity automatically). Applies to any OAuth-based server, shown with Asana as the worked example. A remote MCP server that connected fine and later stopped working is almost always an **authentication** problem, not an outage. The most stubborn version is a **stale authorization on the provider's side**: the provider — Asana, Atlassian, Slack, and so on — is holding an old authorization for **MCP Manager** that no longer works, and it keeps handing that same dead authorization back instead of prompting you to authorize again. Reconnecting inside MCP Manager alone doesn't clear it. This page shows how to recognize that case and fix it. This page is for a connection that **worked before and then broke**. If instead a server is temporarily **down or unreachable**, that's an outage — see [Gateway Resiliency](/advanced/gateway-resiliency). If you're running **your own** server and the OAuth step fails because the server lost the client MCP Manager registered (often a "client not registered" error), that's a different problem — see [Debugging self-hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth) and the [stale dynamic-client registration edge behavior](/security/authentication-and-identity#edge-and-failure-behavior). ## Recognize a stale connection You'll see some combination of the following: * **The identity shows a broken-status badge.** On the server's [Identities](https://app.mcpmanager.ai/settings/servers) tab, the identity for the affected server is flagged — **Needs authentication** while its credential might still recover, or the terminal **Disconnected** once a definitive rejection has archived the stored credential (a transient blip shows a shorter-lived **Not connected**). See [what each status means](/security/authentication-and-identity#identity-authentication-statuses). MCP Manager also stops offering a broken identity as an automatic choice, and makes it non-selectable in the connection flow — it never silently picks a credential it knows is broken. * **Tool calls fail with a re-authentication error.** In your AI client, a call to that server comes back with: > MCP Manager needs 'Asana' to be re-authenticated before this request can complete. Visit your gateway settings to reconnect. * **Nothing about your setup changed.** The failure persists even though your Client ID and Client Secret are unchanged and still correct. Because you set this connection up successfully before, a message pointing you at your credentials is usually a red herring for this failure — what changed is the authorization on the provider's side, not your app registration. * **The tell: no consent screen on retry.** When you try to reconnect (below), the provider's **consent screen doesn't appear** — the authorization tab flashes and closes in about a second — and the connection still fails. That near-instant, no-consent round trip is the fingerprint of a stale authorization: the provider auto-approves an authorization it already has, then rejects the token it just issued. If the reconnect in Step 1 below **does** show a real consent screen and then works, you had an ordinary expired token, not a stale authorization — you're done. The rest of this page is for when the consent screen never appears. ## Why it happens MCP Manager refreshes OAuth tokens automatically, so a healthy connection keeps working without you re-authenticating. A connection goes stale when the **authorization on the provider's side** is gone or blocked while the provider still treats its app authorization as active. Common triggers: * You changed your password or your security settings with the provider. * An administrator at the provider revoked or blocked the app. * You removed the account, workspace, or app that the original authorization was tied to. * For providers that rotate refresh tokens on a fixed schedule, the authorization simply reached the end of its life — roughly every 90 days for some (Atlassian, for example), which makes this a routine re-authentication rather than a one-off failure. Re-adding your identity inside MCP Manager often isn't enough on its own. MCP Manager reuses **one registered OAuth application per server** and, for most providers, does not force the provider to show its consent screen again. It also has no way to revoke its own authorization on the provider for you. So if the provider still holds a stale authorization, a fresh attempt is silently auto-approved against that same dead authorization. The one action that reliably clears it is to **deauthorize the app on the provider's side**, which forces the provider to prompt for consent the next time — and that is something only you, or a provider administrator, can do. **Google-backed servers are the exception.** MCP Manager always forces a fresh consent screen when you re-authorize a Google server, so the provider-side deauthorize step below is usually unnecessary for them — a straight reconnect (Step 1) is normally enough. ## Fix it In MCP Manager, go to the [Servers](https://app.mcpmanager.ai/settings/servers) page, open the affected server, and on its **Identities** tab open the broken identity and click **Authenticate again** (or, for a brand-new identity, **Add a new identity**). For an OAuth server this launches the provider's authorization in a new browser tab. Watch that tab: * If the provider shows its **consent screen**, you approve it, and the connection starts working — you're done. This was an ordinary expired token, and **Authenticate again** has already moved your gateways and connections onto the repaired identity. * If the tab **flashes and closes with no consent screen**, and the connection still fails, the provider is holding a stale authorization. Continue to the next step. This is the load-bearing step — it forces the provider to prompt for consent again next time. The exact location differs per provider (see [the table below](#where-to-deauthorize-by-provider)); the pattern is always to remove MCP Manager's authorization in the provider's connected-apps settings. In **Asana**, for example: click your avatar to open **Settings**, choose **Apps** in the left column, find the MCP Manager app under **Authorized Apps** (named something like **"Asana MCP company-wide"**), and click **Deauthorize**. If several apps look similar, match the one whose **last activity** date lines up with roughly when your connection stopped working. Back in MCP Manager, on the same **Identities** tab, open the affected identity and click **Authenticate again**, then confirm **Yes, re-authenticate**. This time, because you deauthorized on the provider's side, the provider shows a real **consent screen** — approve it. Because identities are write-once, MCP Manager mints a **new** identity with brand-new credentials that no longer reference the stale ones, then **moves your gateway assignments and connections onto it automatically** and retires the old one — so you don't have to re-select the identity everywhere it was used. **Authenticate again** repairs your **own OAuth** identities, and is the preferred fix because it carries your existing uses over for you. If the identity is a **shared (Global) service account** or authenticates with a **header token** rather than OAuth, re-establish it instead by updating its credential or adding a new identity — in this release the automatic move-your-uses-over step applies to your own OAuth identities. The identity's broken-status badge (**Needs authentication** or **Disconnected**) should clear, and a tool call to the server should now succeed from your AI client. ## Where to deauthorize, by provider The step that actually clears a stale authorization is removing MCP Manager's authorization in the **provider's** own settings — look for **connected apps**, **authorized apps**, or **third-party access**. The location varies by provider, and the provider's own documentation is authoritative for the exact path. | Provider | Where to deauthorize MCP Manager's app | | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Asana** (worked example) | Settings → **Apps** → **Authorized Apps** → the MCP Manager app (e.g. "Asana MCP company-wide") → **Deauthorize** | | **Atlassian, Slack, and other OAuth providers** | In the provider's account or security settings, find connected / authorized / third-party apps and remove the MCP Manager entry | | **Google** | Usually unnecessary — MCP Manager forces a fresh consent screen for Google servers on every re-authorization (see the note above) | Some providers expire authorizations on a fixed schedule — roughly every 90 days for Atlassian, for example — so this kind of re-authentication can be a routine, recurring event rather than a sign that something is broken. ## Still not connecting? If a fresh consent screen still doesn't restore the connection, the cause is likely outside your control as an individual user: * **Ask a provider administrator to check the app's status.** The app may be **blocked** or pending approval at the organization level in the provider's app-management settings — something a single user can't clear. * **Contact the provider's support** with the timestamps of the failed attempts and, if you have it, the exact error the provider returned (for example, an `invalid_grant` response from its token endpoint). A connection problem rooted in how the provider works is fastest to resolve at the source. * **Contact MCP Manager support** if you believe the problem is on the MCP Manager side — include the **correlation ID** from the error so the request can be traced. This flow clears the most common cause of a broken connection — a stale authorization on the provider's side. It does not address a provider [outage](/advanced/gateway-resiliency) or a self-hosted server that lost the client MCP Manager registered ([debugging self-hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth)). ## Further reading When an identity's credential stops working versus a plain server outage, and how each is surfaced. How MCP Manager stores credentials, refreshes tokens automatically, and what happens when a refresh fails. The guided, server-by-server connection flow and how saved identities are reused. Confirm a server and its credentials work in isolation with an open inspector before diagnosing further. How MCP Manager detects a server's authentication type, and the per-server connection guides. The Asana setup guide — registering the app, Client ID and Secret, and the OAuth approval. # Gateway Resiliency Source: https://docs.mcpmanager.ai/advanced/gateway-resiliency What happens when an MCP server in your MCP Manager gateway is down, offline, or unreachable: the gateway keeps every other server working, the offline server's tools drop off the list, and calls to it return a clear error and raise an admin alert. One server's outage is never a gateway-wide outage, and a recovered server rejoins on its own — no new gateway needed. A **gateway** in **MCP Manager** usually fronts several MCP servers at once. When one of them goes down — maintenance, a deploy, or a network blip — the gateway keeps serving every other server and contains the problem to the one that's down. One server's outage is never a gateway-wide outage, so you don't need to take anything else offline or rebuild your setup to ride it out. ## What happens when one MCP server is down The gateway reaches each MCP server **independently**, so a failure stays contained to the server that failed: its tools become temporarily unavailable while every healthy server in the gateway keeps working. A down server shows up in exactly three ways: * **Its tools drop off the list.** When your client asks the gateway for available tools, the gateway returns the tools from every server it can reach and omits the one it can't. * **A call to it returns a clear error.** A tool call to the offline server gets back a plain-language error naming the server, not a hang and not a failure that spreads to your other tools. * **An admin alert is raised.** The gateway records an alert naming the failed server and the reason, so an administrator sees the outage on the [Alerts](/features/alerts) page instead of inferring it from a vague client-side error. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart LR C["🤖
AI client"] --> GW["🛡️
MCP Manager gateway"] GW -->|"healthy"| S1["🖥️
GitHub server"] GW -->|"healthy"| S2["🖥️
Jira server"] GW -.->|"unreachable"| S3["🚫
Offline server"] S3 -.-> E["⚠️
Clear error to the client +
alert to admins"] classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef client fill:#80cbc4,color:#062b4c,stroke:#00796b,stroke-width:1.5px; classDef server fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; classDef blocked fill:#ec9c9d,color:#12141d,stroke:#eb5757,stroke-width:2px; classDef warn fill:#ffd863,color:#12141d,stroke:#ffa535,stroke-width:1.5px; class GW gateway; class C client; class S1,S2 server; class S3 blocked; class E warn; ``` Here, GitHub and Jira keep answering through the gateway while the offline server is contained, producing a client error and an admin alert. The **Alerts** feed is in-app and visible to roles with the **See all alerts** capability. End users don't need it to understand an outage — the error they get back already names the affected server. ## A server that's unreachable when you connect When you connect an MCP client, like Claude, to a gateway, MCP Manager walks you through its servers and gathers what each one needs (see [Connection Experience](/features/connection-experience)). A server that's **down** doesn't stop you connecting: the authorization flow never contacts the server itself, so you can finish connecting. However, when the MCP client connects to the gateway, any offline servers simply contribute no tools, resources, or prompts when your client loads them. Reconnect once it's healthy and its features join the list. OAuth is the one case where a provider's availability can matter, and only when you're **adding a new identity**. Authorizing a new OAuth identity completes that server's OAuth authorization with its provider, so if the provider is unreachable right then, you can't add that identity until it's back. A **saved identity** you've connected before, or a **shared identity** an administrator attached, needs no live authorization: MCP Manager uses the stored credential, so you connect even if the server or its OAuth provider is unreachable, and the credential is exercised only on your first call. ### Connecting records your credentials but doesn't test them The authorization flow — where your client sends you to MCP Manager to authorize and bring an identity for each server — **records** the identities and credentials you provide. It does not connect to the downstream servers or verify that those credentials work at that time. A wrong or expired credential, or a server that's quietly down, therefore surfaces on the **first call** that uses it rather than during the connection flow. Completing the flow confirms every server has an identity attached, which is not the same as proving each identity works. ## A server that goes down during a session If a server is healthy when you connect but goes down later, the tools you already loaded stay listed and calls to the **healthy** servers keep working. A call to the **offline** server comes back with a clear gateway error: > MCP Manager couldn't reach 'GitHub'. If the issue persists, provide the correlation ID below for further investigation. Your client receives this as an ordinary tool error and can report it or move on, rather than stalling or losing the connection. When the server recovers, the next call to it simply succeeds again. ## Why some tools are missing from your gateway If a gateway shows fewer tools than you expect, a server was most likely **unreachable when your client requested the tool list**. The gateway builds that list from the servers it can reach at that moment, so a server that was down during connection contributes nothing to it. **Reconnect or refresh the connection** once the server is healthy and the gateway gathers the list again from all reachable servers, restoring the missing tools. This is also why two people who connect at different times can briefly see different tool counts from the same gateway. If a gateway is missing tools you know it should have, check the [Alerts](/features/alerts) page for a server-discovery failure before changing any configuration — the alert names the server and the reason faster than you can infer it from the client side. ## When an identity's credential stops working A separate case from a plain outage: the server is reachable, but the **identity** you connected is rejected because its credential no longer works. Two things cause this: * **An OAuth token that can't be refreshed.** MCP Manager refreshes a saved identity's OAuth tokens automatically, so this is uncommon — but if a token expires and genuinely can't be refreshed, the next call fails authentication. * **A revoked or expired token credential.** A header credential such as a GitHub personal access token or an API key doesn't refresh; once it's revoked or expires, the server rejects it and it has to be replaced. Either way the call fails authentication rather than returning an unreachable error, and MCP Manager marks the identity so the problem is visible instead of silent. On the server's **Identities** tab the identity carries a [status badge](/security/authentication-and-identity#identity-authentication-statuses) — **Needs authentication** when the credential was rejected but might still recover, or the terminal **Disconnected** once a definitive rejection has archived the stored credential (a shorter-lived **Not connected** marks a transient blip that may clear on its own). For an expired OAuth token the caller also sees the failure directly: > MCP Manager needs 'GitHub' to be re-authenticated before this request can complete. Visit your gateway settings to reconnect. How you fix it depends on the credential type, and in both cases you do it **from MCP Manager — with no disconnect-and-reconnect dance in your AI client**: * **An OAuth identity** — open it on the server's **Identities** tab and click **Authenticate again**. That re-runs the provider's authorization and, because identities are write-once, mints a **new** identity, then **moves your gateway assignments and connections onto it automatically** and retires the broken one — so you don't lose where the identity was in use. The [full walkthrough](/advanced/fixing-broken-connections) covers the stubborn case where the provider keeps handing back a stale authorization. * **A header or token credential** — update the token on the identity, or add a new identity, since these credentials don't refresh and aren't re-authorized through a provider. The rest of the gateway keeps working throughout. The "couldn't reach" message points to a server that's down; this one points to a credential you repair in Settings — telling you whether to wait out an outage or re-authenticate. ## Recovery is automatic — no rebuild required The gateway doesn't lock a server out after it fails; it retries on the next request. A downed server therefore recovers **on its own** — calls succeed again and its tools return the next time a client loads them — with no restart, no remove-and-re-add, and no configuration change on your part. The answer to the most common worry during an outage is simply to **wait it out**: the rest of the gateway keeps serving, and the server rejoins automatically once it's healthy. ## Do you need a separate gateway for an unreliable server? During an outage, the instinct is often to build a new gateway that leaves the troublesome server out. For a **transient** outage that's wasted effort: the gateway already isolates a down server and lets it rejoin on recovery, so a parallel gateway only adds something to maintain and later unwind. Two situations do warrant a deliberate change: * **Permanently retiring a server.** Remove it from the gateway, or switch off its per-layer `enabled` toggle ([Runtime Protections](/security/runtime-protections#break-glass-instant-kill-switches)) — a clean kill-switch, checked on every request, that stops the server being offered immediately with nothing deleted. * **Isolating a chronically flaky server.** If one server is unreliable often enough that its alerts or missing tools disrupt a gateway you depend on, move it to its own gateway. Reach for this when a server is *persistently* unreliable, not for a one-off the gateway is built to absorb. ## At a glance: server-outage scenarios | Scenario | What you experience | What to do | | -------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | A server is down when you connect | You connect to the rest of the gateway; the offline server's tools are absent | Reconnect once the server is healthy to pick its tools back up | | A server goes down mid-session | Calls to it return a "couldn't reach" error; other servers keep working | Wait it out — the next call succeeds once it recovers | | A gateway is missing tools | A server was unreachable when the tool list was built | Check [Alerts](/features/alerts), then reconnect once it's back | | An identity's credential stops working | Calls fail auth; the identity shows **Needs authentication** or **Disconnected** | Open it in Settings → **Authenticate again** (OAuth), or replace its token | | A server is permanently gone | — | Remove it or switch off its `enabled` toggle | In every row, the gateway's healthy servers keep working, and recovering from an outage needs nothing beyond reconnecting when prompted. ## Further reading The guided, server-by-server connection flow, and how saved identities are reused. The admin feed where a server-discovery or connection failure is surfaced. Why the gateway sits in the path of every call, and the latency it adds. The per-layer enabled toggles that switch a server, host, or gateway off instantly. Re-authenticate an identity whose credential died, including the stale provider-side authorization case. What a gateway is and how it bundles several MCP servers behind one connection. # Getting fresh data from Claude Source: https://docs.mcpmanager.ai/advanced/getting-fresh-data-from-claude Why Claude (Claude.ai, Claude Desktop, Claude Code) sometimes answers from an earlier MCP response instead of re-querying, how to recognize it, how to confirm it in your gateway logs, and how to prompt around it so the data you act on is current. MCP Manager always returns live data from the source; the staleness happens inside the client's handling of large responses. When you connect Claude to an **MCP Manager** gateway, every tool call returns live data from the source. The gateway does not cache results. But Claude's own handling of large responses can sometimes surface older data later in a long conversation, so the answer you see may not match what is in the source right now. This page explains why that happens, how to confirm it, and how to prompt around it, so the data you act on is current. This is a client-side behavior, not something MCP Manager does. The gateway reads each response in full to check it for leaked secrets and injected content, then passes it straight through to the client. It returns fresh data on every call and keeps no copy. The staleness lives inside how the client stores and reuses that response, so the fix is in how you prompt the client rather than anything you configure on the gateway. ## Why Claude sometimes shows stale data When Claude calls a tool, the result becomes part of the conversation, and Claude may answer a later question from that earlier result instead of calling the tool again. When it does, you see the data as it was at the first call, not as it is now. Large responses make this more likely. In **Claude Code** specifically, a large tool result is written to a temporary file and Claude reads from that file rather than keeping the whole result inline, so a follow-up question can be answered from that snapshot. The exact mechanism differs by client — Claude.ai, Claude Desktop, and Claude Code each manage long conversations differently, and it is an internal behavior that can change without notice — but the effect is the same everywhere: an earlier result gets reused in place of a fresh call. The trigger is response size. A broad query early in a long conversation is the most likely to be reused, because it returns the most data. A small, targeted response is more likely to be re-fetched the next time you ask. ## How to spot it A few signs that Claude may be answering from an earlier result: * You updated a record after asking Claude about it, and Claude's answer does not reflect the change. * Claude's answer contradicts what you see directly in the tool, for example in Jira, Notion, or Salesforce. * Claude refers back to data from earlier in the conversation without making a fresh tool call. If you are unsure, treat the data as potentially stale and confirm it before you rely on it. ## Confirm it with your gateway logs The reliable way to tell whether Claude made a live call is to check the gateway's audit log — not to ask Claude. Every tool call through your gateway is logged. Open [Viewing Logs](/features/viewing-logs) and look for a request at the moment Claude claims it queried. If there is no matching log entry, Claude answered from an earlier result rather than a live call. Asking Claude directly — "was that a live call or an earlier result?" — is at best a weak hint. Models cannot reliably report their own tool use and may answer confidently either way. Trust the logs, not Claude's self-report. ## How to prompt around it ### Fetch specific records by ID Ask Claude to fetch a specific record directly instead of running a broad query. A direct fetch by ID or key returns a small response that is much less likely to be reused from an earlier snapshot. Instead of: > What are my open Jira tasks? Try: > Fetch ME-1110 directly and tell me its current status. ### Tell Claude the data may have changed Say plainly that the data might be out of date and ask Claude to query again. Narrowing the scope at the same time keeps the response small, so the fresh result is more likely to stay inline. > That data might be stale. Can you re-query Jira for just the issues in Implementing status? ### Start a new conversation Reused results only live within a single conversation. Starting a new chat clears them, so Claude has no earlier result to fall back on and has to query again. ## Query patterns that lower the risk | Higher risk | Lower risk | | ------------------------------------------ | ------------------------------------------------ | | "Show me all my tasks" | "Show me my tasks in Implementing status" | | A broad query early in a long conversation | A targeted query by ID or key | | Following up on data from much earlier | Re-querying explicitly before acting on the data | | Trusting a status field from a bulk result | Confirming the status with a direct record fetch | ## The golden rule If you are about to take an action based on what Claude told you, such as reassigning a ticket, closing an issue, or making a decision, fetch that specific record directly first. Bulk query results are good for exploration and overview. A direct fetch by ID is the source of truth. ## Further reading Add a gateway to Claude, Cursor, or VS Code and start calling tools. Confirm what Claude actually called, and when, in your gateway's audit log. How the gateway reads each response and passes live data through to the client. Short answers to common questions about how MCP Manager behaves. # Validate MCP Servers with Open Tools Source: https://docs.mcpmanager.ai/advanced/validate-mcp-servers Confirm an MCP server connects and behaves correctly using free, open-source inspectors that run on your own machine — the official MCP Inspector and MCPJam. Both speak the raw protocol and need no LLM to validate connectivity and list a server's tools, resources, and prompts. Validate connectivity and correctness here first, so your time in MCP Manager goes to governance rather than diagnosing connections. Most MCP servers connect in just a few minutes. The ones that take longer usually aren't hard so much as particular — as the [server guides](/mcp-server-guides/overview) show, one server wants a scoped token, another a region-specific URL or an admin-allowlisted callback, and a few, like the [AWS MCP Server](/mcp-server-guides/aws), run through a local signing proxy. Whichever you're connecting, it helps to confirm it works on its own first, and that's exactly what these tools are for. Two free, open-source **inspectors** are purpose-built to help teams building or using MCP servers validate **connectivity and correctness**: the **official MCP Inspector** and **MCPJam**. Both run on your own machine, speak the raw MCP protocol, and need **no LLM** to connect to a server and list what it offers. Use one to confirm a server is sound *before* you bring it into MCP Manager — so the time you spend in MCP Manager goes to what it's for, **governance** (rules, identity, logging, and access), instead of diagnosing connections. Both tools are independent open-source projects, not part of MCP Manager. Their own documentation is authoritative and may be more current than this page. We point to them because they're the fastest, most neutral way to verify a server in isolation. ## Which tool should I use? Either works for the core job — connect to a server and inspect its tools, resources, and prompts. They differ in focus: | | **Official MCP Inspector** | **MCPJam** | | :------------------- | :----------------------------------------------------------- | :----------------------------------------------------------------------------------- | | Maintainer · license | The MCP project (Anthropic) · MIT | MCPJam · Apache 2.0 | | How to run | `npx @modelcontextprotocol/inspector` (local only) | Hosted at `app.mcpjam.com`, `npx @mcpjam/inspector@latest`, or a desktop app | | Strongest at | The neutral reference tool; a scriptable `--cli` mode for CI | Guided **OAuth debugging**; sharing a live server with teammates | | OAuth | Bearer token / custom headers, plus an OAuth flow | A step-by-step **OAuth Debugger** across spec versions (DCR, pre-registration, CIMD) | | Shareable / hosted | No — runs locally | Yes — a hosted web app and shareable links | | Needs an LLM? | Never | Only for its optional chat/eval features | * **Choose the official MCP Inspector** when you want the canonical reference implementation, everything strictly local, or a `--cli` mode you can wire into CI. * **Choose MCPJam** when OAuth is the hard part (its OAuth Debugger is the standout), when you want to share a running server with teammates, or when you may later go beyond connectivity into model-driven chat and evals. You don't have to pick one forever — both install in seconds, so it's fine to reach for whichever fits the problem in front of you. ## The official MCP Inspector The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is the reference testing tool maintained by the Model Context Protocol project (MIT-licensed). It has two parts: a **React UI** (the *MCP Inspector Client*, default port **6274**) and a **proxy** (the *MCP Proxy*, default port **6277**) that bridges the browser to the server's transport. Both bind to `localhost` only. ### Run it ```bash theme={null} # UI mode — opens the inspector in your browser npx @modelcontextprotocol/inspector ``` It prints a URL that includes a **proxy session token**; open that exact link. (The token protects the proxy from other processes on your machine — see [Security](#security-notes).) You can also point it straight at a local/STDIO server: ```bash npm package server theme={null} npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /path/to/dir ``` ```bash PyPI package server theme={null} npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/repo ``` ```bash local build theme={null} npx @modelcontextprotocol/inspector node build/index.js arg1 arg2 ``` Pass environment variables with `-e key=value`, and change ports with `CLIENT_PORT=8080 SERVER_PORT=9000`. ### Connect to a remote server Run `npx @modelcontextprotocol/inspector` with no command, then in the **Server connection** pane choose the transport — **Streamable HTTP** for most current servers (or **SSE** for older ones) — and paste the server's URL. For an authenticated server, enter a **Bearer token** (sent in the `Authorization` header; you can override the header name) or add custom headers. The Inspector runs the same `initialize` handshake and capability negotiation any MCP client does. ### Inspect what the server offers The UI has panes for **Resources**, **Prompts**, **Tools**, and **Notifications**. List each, read the schemas, call a tool with custom inputs, and watch every JSON-RPC message and server log in the Notifications pane. ### CLI mode (for scripting and CI) `--cli` runs the same checks headlessly — ideal for a CI gate: ```bash theme={null} # List a remote server's tools over Streamable HTTP npx @modelcontextprotocol/inspector --cli https://mcp.example.com --transport http --method tools/list # Call a tool npx @modelcontextprotocol/inspector --cli https://mcp.example.com --transport http \ --method tools/call --tool-name search --tool-arg query=hello # Send a custom auth header npx @modelcontextprotocol/inspector --cli https://mcp.example.com --header "Authorization: Bearer " --method tools/list ``` ### Security notes The Inspector hardened its defaults after [CVE-2025-49596](https://github.com/modelcontextprotocol/inspector/security/advisories) (a remote-code-execution risk): * The proxy requires a **session token** (auto-generated, printed on startup, or set via `MCP_PROXY_AUTH_TOKEN`). Use the printed link so the token is included. * Both the UI and proxy **bind to `localhost`** by default, with `Origin`-header (DNS-rebinding) validation. * Don't set `DANGEROUSLY_OMIT_AUTH` — disabling the token can let a malicious web page reach the proxy and run commands on your machine. ## MCPJam [MCPJam](https://www.mcpjam.com/) is an open-source (Apache 2.0) MCP development platform. For validation its standout is a guided **OAuth Debugger**, which is exactly what you want for the auth-heavy servers in the [server guides](/mcp-server-guides/overview). ### Run it | Option | How | Notes | | :-------------------------------------------- | :---------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- | | **Hosted** (fastest) | Open [app.mcpjam.com](https://app.mcpjam.com/) | No install. **HTTPS server URLs only**; no local/STDIO. | | **Terminal** (best for sensitive credentials) | `npx @mcpjam/inspector@latest` | Node.js 20+. Opens at `http://localhost:6274`. HTTP/S **and** local STDIO. Everything stays on your machine. | | **Desktop** (Mac/Windows) | [Download the installer](https://github.com/MCPJam/inspector/releases/latest) | No Node.js required. HTTP/S and local STDIO. | ### Connect and inspect Click **Add server**, enter the server's URL, and choose its authentication — **None**, **Bearer Token**, or **OAuth 2.0**. Once connected, browse **Tools**, **Resources**, and **Prompts**, and run a tool by hand with full JSON-RPC visibility. ### Debug OAuth For an OAuth server, MCPJam's **OAuth Debugger** walks each stage of the handshake — discovery, Dynamic Client Registration, client pre-registration, the authorize redirect, and token exchange — and shows where it breaks. This is the fastest way to pin down problems like a callback domain an admin hasn't allowlisted, or a [self-hosted server's DCR](/build-your-own-mcp-server/debugging-self-hosted-oauth) failing. For a real server with **sensitive credentials**, prefer MCPJam's **terminal** (`npx`) or **desktop** app — the connection and any tokens stay on your machine. The hosted app is great for a quick check of a public HTTPS endpoint; note it stores any OAuth tokens you connect in its own vault, and its optional AI chat (the only LLM-using feature) is gated behind usage credits. ## Validate, then govern Whichever tool you use, the validation loop is the same — and none of it touches an LLM: Start the official Inspector (`npx @modelcontextprotocol/inspector`) or MCPJam (`npx @mcpjam/inspector@latest`) and open the printed link. Use the identical endpoint and credentials. You're testing the exact thing you intend to connect, so the result transfers directly. A clean `initialize` and capability negotiation means the server and your credentials are sound. A failure here is a server- or credential-side issue to fix at the source. List tools, resources, and prompts, and call a tool to confirm it actually responds. If it's an OAuth server, step through the handshake (MCPJam's OAuth Debugger is ideal). Once the server checks out, add it in MCP Manager using the matching [server guide](/mcp-server-guides/overview) — and spend your time there on what MCP Manager is for: rules, identity, logging, and access. Connectivity is already settled. ### Audit what a gateway exposes The same inspectors work in reverse. Point one at an **MCP Manager gateway URL** (with its credential) and list its tools to confirm exactly what that gateway serves to clients — a quick way to verify a gateway's surface after you've assigned servers and applied rules. ## Gotchas & things to keep in mind * **Both UIs default to port `6274`.** To run them at the same time, move one — `CLIENT_PORT`/`SERVER_PORT` for the official Inspector, `--port` for MCPJam. * **Use the official Inspector's printed link.** It carries the proxy session token; opening a bare `localhost:6274` without it won't authenticate. Never set `DANGEROUSLY_OMIT_AUTH`. * **MCPJam's hosted app is HTTPS-only and has no STDIO.** For an HTTP endpoint, a local/STDIO server, or sensitive credentials, use a locally-run inspector instead. * **These tools validate connectivity, not governance.** They confirm a server works and show its capabilities; the inspection, identity, logging, and rules that make traffic *safe* are what MCP Manager adds on top. * **They're independent projects.** Their UIs and flags change; if a step here has drifted, their own docs are authoritative. ## Further reading How MCP Manager detects a server's authentication type, and the per-server connection guides. A good example of a connection worth validating first — IAM SigV4 via a locally-run proxy. An OAuth server where an OAuth debugger helps — including the admin callback-domain allowlist. When you're building the server yourself and the OAuth handshake is the problem. ## External sources The official, MIT-licensed reference inspector — source, releases, and the security model. The Model Context Protocol project's guide to the Inspector's panes and workflow. Open MCPJam in your browser; no install required (HTTPS servers only). The open-source (Apache 2.0) MCPJam project — source, installation, and releases. # Build on Cloudflare Workers Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/cloudflare How to build a remote MCP server on Cloudflare Workers that runs behind MCP Manager: serving Streamable HTTP from an McpAgent, using workers-oauth-provider as a full OAuth 2.1 authorization server with dynamic client registration, why its KV-backed client storage avoids the ephemeral-client problem other frameworks hit, the KV eventual-consistency gotcha that causes transient post-registration 401s, and a compatibility checklist. Cloudflare Workers is one of the most popular ways to host a remote MCP server, and it's the closest thing to a **turnkey dynamic-client-registration server**: the [Agents SDK](https://developers.cloudflare.com/agents/) gives you Streamable HTTP from an `McpAgent`, and [`workers-oauth-provider`](https://github.com/cloudflare/workers-oauth-provider) makes the Worker a full OAuth 2.1 authorization server. Crucially, its registered-client storage is **Workers KV** — durable across instances — so the ephemeral-client failure that bites memory-backed frameworks doesn't happen here. This page covers what to choose for **MCP Manager** and the one gotcha KV introduces; Cloudflare's docs are authoritative. Start with [Building Your Own MCP Server](/build-your-own-mcp-server/overview) for the requirements and the auth-mode decision tree. This page is the Cloudflare layer on top. ## Serve Streamable HTTP from McpAgent An `McpAgent` (a Durable Object, bound as `MCP_OBJECT`) exposes two factories: `MyMCP.serve('/mcp')` for **Streamable HTTP** and `MyMCP.serveSSE('/sse')` for the legacy SSE transport. Point MCP Manager at the **`/mcp`** endpoint. See [Build a Remote MCP server](https://developers.cloudflare.com/agents/guides/remote-mcp-server/) and the [transport page](https://developers.cloudflare.com/agents/model-context-protocol/transport/). Mount `/mcp` (Streamable HTTP). You can keep `/sse` for legacy clients, but a server exposing **only** `/sse` won't connect to MCP Manager, which speaks Streamable HTTP only. ## Auth with workers-oauth-provider `workers-oauth-provider` makes the Worker a full OAuth 2.1 authorization server (with PKCE) and **auto-publishes** the RFC 8414 (`/.well-known/oauth-authorization-server`) and RFC 9728 (`/.well-known/oauth-protected-resource`) metadata. Dynamic client registration (RFC 7591) turns on when you set the optional **`clientRegistrationEndpoint`** — set it so MCP Manager can self-register. You wire an upstream IdP handler (Google, GitHub, Auth0, Stytch, WorkOS) as the `defaultHandler`, and the Worker mints its own MCP tokens after the upstream login. ```ts Illustrative — see github.com/cloudflare/workers-oauth-provider theme={null} export default new OAuthProvider({ apiRoute: "/mcp", apiHandler: MyMCP.serve("/mcp"), defaultHandler: YourUpstreamIdpHandler, // Google / GitHub / Auth0 / ... authorizeEndpoint: "/authorize", tokenEndpoint: "/token", clientRegistrationEndpoint: "/register", // enables DCR — set this }); ``` This is the **standard OAuth + DCR** mode in MCP Manager — the most seamless one. You bring nothing but your approval at connect time. ## Storage: durable by default, but eventually consistent Registered clients, grants, and tokens live in **Workers KV** (the `OAUTH_KV` binding), a global namespace. Because it's KV and not process memory, a client MCP Manager registers is visible to every Worker isolate — so the [ephemeral-client failure](/build-your-own-mcp-server/debugging-self-hosted-oauth) that plagues memory-backed frameworks **does not happen here**. Dynamically-registered clients expire per `clientRegistrationTTL` (default 90 days). The trade-off is KV's **eventual consistency**. A client or token written on one edge may not be visible on another for a short window, so a token or authorize call made *immediately* after registration can briefly return `invalid_client` or `401` and then clear on its own. Build a little tolerance (retry/backoff) into anything that registers and immediately uses a client. Secrets (client secrets, tokens) are stored only by hash, and the grant's `props` are encrypted with the access token as key material — so you can't read them out of KV without a valid token. That's good security hygiene; just don't expect to inspect grant context directly in KV. ## MCP Manager compatibility checklist Route `/mcp` to `MyMCP.serve('/mcp')`; don't expose only `/sse`. Provide `clientRegistrationEndpoint` so MCP Manager can self-register via DCR. Without it, registration is off. The Durable Object (`MCP_OBJECT`) and the KV namespace (`OAUTH_KV`) bindings must exist in `wrangler` — a missing binding is a common setup failure. MCP Manager registers as a confidential client. If you set `disallowPublicClientRegistration: true`, that's fine — just don't expect public (no-secret) client registration to work. Expect a brief window after registration where a call can 401; retry rather than treating it as a hard failure. ## Cloudflare gotchas KV is eventually consistent, so a token or authorize call made immediately after DCR can briefly fail and then succeed. This is the KV analogue of the ephemeral-store problem — self-clearing, not structural. Add retry/backoff. Map each path to the right factory: `serve('/mcp')` for Streamable HTTP, `serveSSE('/sse')` for legacy. When fronting both under OAuth, use the `apiHandlers` map form rather than a single `apiHandler`. The `scopesSupported` field only *advertises* scopes in metadata — it doesn't restrict what a client may request. Enforce real authorization in your authorize handler, not by relying on that field. ## Further reading Cloudflare's authoritative guide to the McpAgent and the OAuth provider. The OAuth 2.1 + DCR library, including KV storage and configuration. Why most frameworks hit the ephemeral-client failure that Cloudflare's KV storage avoids. The cross-framework requirements, decision tree, and troubleshooting catalog. # Add a Connect Button to Your Site Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/connect-button How to put a "Connect via MCP Manager" button on your own site or README so a visitor reaches MCP Manager with your server URL and name already filled in and the connection already discovered — the deep-link URL format and its parameters, ready-to-paste HTML and Markdown snippets, what the visitor sees whether they are signed in, signed out, or have no account yet, the usage rules for the button, and how to debug a link that lands wrong. If you publish an MCP server, you can put a **Connect via MCP Manager** button on your docs, your pricing page, or your README: Connect via MCP Manager, light variant Connect via MCP Manager, dark variant Someone who clicks it arrives with your server's URL and name already filled in and the connection already worked out, so the only thing left for them to do is authorize it. Instead of copying your endpoint out of your docs and pasting it somewhere, they press one button. MCP Manager is where teams connect MCP servers to their AI apps and put governance in front of them — identity, permissions, and audit. Your users may already run it; if they don't, the link still works, and they can sign up on the way through. You need no relationship with us to publish the button, and there is nothing to register. The button is a plain link. No SDK, no script tag, no embed, and nothing that phones home, so it works on any site — including one with a strict content security policy. ## Build your link The link points at MCP Manager's servers page and carries your endpoint as a query parameter: ```text Link format theme={null} https://app.mcpmanager.ai/settings/servers?addRemoteServerUrl=&serverName= ``` | Parameter | Required | What it does | | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `addRemoteServerUrl` | Yes | Your MCP endpoint — the same Streamable HTTP URL a user would paste by hand. Must be `https://` to connect automatically; anything else is pre-filled but waits for a click. | | `serverName` | No | The name your server is listed under once they've added it. Leave it out and MCP Manager falls back to the name your server advertises in its `initialize` response, then its hostname. | | `source` | No | Reserved for attribution — a registry name or your own slug. MCP Manager accepts it and ignores it today. Including it now is safe and future-proof. | Two things worth knowing before you test your own link: * **The parameters disappear from the address bar** once the dialog opens. That is deliberate — it stops a refresh or a back-navigation re-opening the dialog on top of the visitor. Your link is not being rewritten or broken. * **If a parameter appears twice, the first value wins.** `?serverName=Acme&serverName=Acme%20Docs` uses `Acme`. The values are never joined together. **URL-encode both values.** `addRemoteServerUrl` is a URL nested inside a URL, so its `://` and `/` characters must be escaped or the link breaks at the first slash. `serverName` needs encoding for spaces and `&`. In JavaScript that's `encodeURIComponent(value)`; most languages have the equivalent. A worked example, for a server named **Acme Docs** at `https://mcp.acme.com/mcp`: ```text Encoded link theme={null} https://app.mcpmanager.ai/settings/servers?addRemoteServerUrl=https%3A%2F%2Fmcp.acme.com%2Fmcp&serverName=Acme%20Docs ``` ## Copy a snippet Both colour variants are here as complete, self-contained HTML. Pick the one that suits your page, or use the auto-switching form below if your site has both themes. ```html HTML (light) theme={null} Connect via MCP Manager ``` ```html HTML (dark) theme={null} Connect via MCP Manager ``` ```html HTML (follows the visitor's theme) theme={null} Connect via MCP Manager ``` ```markdown Markdown / README theme={null} [![Connect via MCP Manager](https://docs.mcpmanager.ai/images/connect-via-mcp-manager.svg)](https://app.mcpmanager.ai/settings/servers?addRemoteServerUrl=https%3A%2F%2Fmcp.acme.com%2Fmcp&serverName=Acme%20Docs) ``` ```markdown README (follows the reader's theme) theme={null} Connect via MCP Manager ``` ```text Plain link theme={null} https://app.mcpmanager.ai/settings/servers?addRemoteServerUrl=https%3A%2F%2Fmcp.acme.com%2Fmcp&serverName=Acme%20Docs ``` Replace the endpoint and name in every snippet with your own, encoded as above. The first two HTML snippets carry their styling inline, so they inherit nothing from your stylesheet and nothing in it can break them. The third uses a class and a `prefers-color-scheme` media query instead, because an inline `style` attribute can't hold a media query — use it when your page serves both themes, and rename the class if `mcp-manager-connect` collides with anything of yours. In all three the mark is inline SVG, so there is no image request to us and nothing to break if you are offline or behind a proxy. For a README, the plain Markdown badge is the simplest thing that works everywhere. The `` form switches with the reader's theme and is supported on GitHub. Both hotlink the SVG from our docs; you are equally welcome to download either file and serve it from your own domain. | File | Use on | | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | | [`connect-via-mcp-manager.svg`](https://docs.mcpmanager.ai/images/connect-via-mcp-manager.svg) | light backgrounds — gradient mark, white ground | | [`connect-via-mcp-manager-dark.svg`](https://docs.mcpmanager.ai/images/connect-via-mcp-manager-dark.svg) | dark backgrounds — white mark, near-black ground | ## What the visitor sees The add-a-server dialog opens for them, with your endpoint in the URL field and your name in the name field. Nothing has been saved yet. It runs the same discovery it would run if they had pasted your URL themselves: fetching your [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) authorization-server metadata and [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) protected-resource metadata, and sending an MCP `initialize` request to see what your server asks for. They press one button. Depending on what your server supports, that runs the OAuth flow or takes them to enter a token. **MCP Manager always stops here for a click** — it never saves a server or opens an authorization window on someone's behalf, however the link was built. ### If they don't have an account The link works for someone signed out, and for someone who has never heard of MCP Manager. They're taken to sign in or sign up first, and your server is carried through the whole journey — including single sign-on and accepting terms. They come out the other side at exactly the state above, with your endpoint and name still filled in. This is the part that makes the button worth putting on a public page: you can link to it from anywhere, and a first-time visitor still lands on your server rather than an empty dashboard, with nothing to find their way back to. ## Using the button * Use the wording **Connect via MCP Manager**. Don't reword it to imply a partnership, endorsement, or certification that isn't in place. * Don't recolor, rotate, stretch, or otherwise redraw the mark. Use the light or dark SVG as shipped, or the inline HTML snippet. * Point the link at `https://app.mcpmanager.ai`. A button pointed somewhere else isn't this button. * You don't need our permission, an account, or a partnership to use it. If you operate a registry or a directory and want to generate these links in bulk, that works the same way — the format is stable and documented here. * Link to **your own** server. Don't publish a button that connects visitors to somebody else's endpoint. ## Troubleshooting The endpoint wasn't URL-encoded, so everything after its first `/` was read as part of the outer link. Encode it — see the warning above. Your endpoint isn't `https://`. Discovery runs on our servers rather than in the visitor's browser, so we don't fire a plaintext address supplied by a third-party page without a click — and that includes `localhost`, which from our side means our own infrastructure rather than the visitor's machine. Nothing is blocked: the visitor can still press **Connect**. Publish over HTTPS and the button works end to end. The link is fine — discovery reached your server and didn't find what it needed. That's the same failure a hand-typed URL would hit, so debug it as a server problem. Start with [Debugging self-hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth) and the troubleshooting catalog in the [overview](/build-your-own-mcp-server/overview). Names are trimmed and capped at 200 characters, and invisible control characters are stripped. If `serverName` is empty after that, MCP Manager falls back to the name your server advertises in its `initialize` response, then to its hostname. ## Further reading The two things any server must do to work behind MCP Manager, and the troubleshooting catalog for when one doesn't. The dynamic-client-registration failure that trips up multi-instance deployments. What your users are actually adding when they press your button, and the three ways it can authenticate. Which authentication method to support, from the perspective of the server you operate. # Debugging Your Self-Hosted Server's OAuth Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/debugging-self-hosted-oauth How to debug a self-hosted remote MCP server whose OAuth identity step fails even though the connection works: why a 'Client Not Registered' / client-ID-not-found error during dynamic client registration is almost always an ephemeral, per-instance client store on autoscaling hosts like Cloud Run, the shared-storage-plus-stable-key fix (with the FastMCP OIDCProxy example), the other common culprits — missing well-known metadata, redirect-URI allowlists, and well-known-client allowlists — a diagnostic decision flow, and the pre-registration and token fallbacks when you can't change the server. This page is for teams running **their own remote MCP server** who hit a wall at the **identity step**: the server connects fine, but creating an identity — the OAuth handshake — fails. Almost every self-hosted OAuth problem lives on the server, not in **MCP Manager**, and they cluster into a handful of recognizable patterns. MCP Manager implements the standard OAuth flow by the book; this page helps you find what your server is doing differently and fix it. If you're connecting a **third-party** server (Atlassian, Slack, HubSpot) rather than one you built, start with [Find & Connect MCP Servers](/mcp-server-guides/overview) and the [per-server guides](/mcp-server-guides/overview#connection-guides) instead. This page is specifically about debugging a server **you control** — see also [Building Your Own MCP Server](/build-your-own-mcp-server/overview). Before changing your server, reproduce the failure in an [isolated client](/advanced/validate-mcp-servers). MCPJam's **OAuth Debugger** walks each stage of the handshake — discovery, dynamic client registration, redirect, and token exchange — and shows exactly where it breaks, often faster than reading server logs. ## The symptom that brings most people here The classic report sounds like this: **connecting to the server works, but creating the identity fails** — often with a message rendered by your own server that reads something like: ```text theme={null} Client Not Registered The client ID a1b2c3d4-… was not found in the server's client registry. ``` Two details make this diagnosable: * **It's the *identity* step that fails, not the connection.** A plain reachability test passes; the OAuth authorize hop is what breaks. * **It often looks intermittent.** A fresh setup works for a little while, then the same error returns — and everyone on the team hits it. That intermittency is the tell. The error page is served by **your server**, not by MCP Manager, and it almost always means the client MCP Manager registered a moment earlier is no longer in the place your server looks for it. The rest of this page explains why, and the much shorter list of other things it can be. ## What MCP Manager actually does during the identity step Knowing the exact sequence tells you where to look on your server. When you add a server by URL and create an identity, MCP Manager runs the standard [OAuth 2.1](https://oauth.net/2.1/) flow the [MCP authorization spec](https://modelcontextprotocol.io/specification/draft/basic/authorization) defines: 1. **Discovery.** MCP Manager fetches your server's `/.well-known/oauth-authorization-server` ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)) and, where present, the protected-resource metadata ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)) to learn your `registration_endpoint`, `authorization_endpoint`, and `token_endpoint`. 2. **Dynamic client registration (DCR).** If a `registration_endpoint` is advertised, MCP Manager POSTs to it ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)) and your server returns a fresh `client_id`. This is a **server-to-server** call from MCP Manager's backend. 3. **Authorization.** MCP Manager redirects **your browser** to the `authorization_endpoint` with that `client_id` and a [PKCE](https://oauth.net/2/pkce/) challenge. You approve, and your server redirects back to MCP Manager's fixed callback URL. 4. **Token exchange + refresh.** MCP Manager exchanges the code at the `token_endpoint`, stores the tokens encrypted, and refreshes them automatically from then on. The callback your server must allow is always: ```text Callback URL theme={null} https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback ``` The crucial structural fact is that **steps 2 and 3 are two separate requests from two different origins** — a backend registration call, then a browser authorize redirect: ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram autonumber participant M as 🛡️ MCP Manager (backend) actor B as 🌐 Your browser participant S as 🖥️ Your server (one of N instances) M->>S: POST registration_endpoint (DCR) Note right of S: Instance A stores client_id → memory S-->>M: client_id = a1b2c3d4-… M->>B: Redirect to authorize with client_id B->>S: GET authorize?client_id=a1b2c3d4-… Note right of S: Lands on instance B —
never saw a1b2c3d4-… S-->>B: ❌ Client Not Registered ``` If those two requests reach **different instances of your server**, and the registration was only stored in the first instance's memory, the second instance has no record of the `client_id` — and renders the error you're seeing. ## Cause #1 — ephemeral client storage on a multi-instance host This is the overwhelmingly common case, and it matches the intermittent symptom exactly. Many MCP server frameworks **default to an in-memory store** for dynamically registered clients. On a single long-lived process that's fine. But on an autoscaling or serverless host — **Google Cloud Run**, AWS Lambda/Fargate, Kubernetes with more than one replica, anything behind a load balancer — that store is **per-instance and ephemeral**: it isn't shared between instances and doesn't survive a scale event or restart. The DCR call writes the client into one instance's memory; the browser authorize request is load-balanced to another instance that never saw it. **Why it looks intermittent.** A fresh attempt can succeed when both hops happen to land on the same warm instance, then fail minutes later once traffic is balanced elsewhere or the instance recycles. "It works for a bit after a fresh login, then breaks for everyone" is the signature of ephemeral per-instance storage — not a flaky network. ### The fix: shared storage + a stable key The fix is on the server, and it has **two parts** — getting only the first is a common near-miss: 1. **Give the OAuth layer a network-accessible, shared client store** so a registration written by one instance is visible to every instance. Redis (e.g. Cloud Memorystore) is the usual choice; a database, Firestore, or object storage also work. 2. **Use a stable encryption key shared across instances.** If the client store is encrypted with a per-instance ephemeral key, a second instance still can't *decrypt* what the first wrote — so you get the same failure even with shared storage. Derive the key from a secret manager so every instance loads the same one. **`min-instances` and session affinity won't reliably fix this.** Pinning to one warm instance or enabling sticky sessions only narrows the window — the registration and authorize requests come from **different origins** (a backend call and a browser), so they can't be guaranteed to land together. Shared storage plus a stable key is the real fix; instance pinning just hides the bug until the next scale event. ### FastMCP's OIDCProxy, specifically [FastMCP](https://gofastmcp.com)'s built-in `OIDCProxy` is a frequent source of this exact failure on Cloud Run, because its client store defaults to in-memory and its default encryption key is ephemeral. The fix maps directly onto the two parts above: * Set the proxy's **`client_storage`** to a shared backend so registrations are durable and visible to every instance — see FastMCP's [`client_storage` parameter reference](https://gofastmcp.com/servers/auth/oidc-proxy#param-client-storage). * Wrap that store so it's encrypted with a **stable key** (for example, a Fernet key derived from a Secret Manager secret), rather than the default ephemeral key. ```python Illustrative example theme={null} # Shared, durable client store + a stable encryption key, # so every Cloud Run instance reads the same registrations. oidc_proxy = OIDCProxy( # ... your existing upstream OIDC config ... client_storage=shared_backed_store, # Redis / Firestore / GCS, not in-memory ) ``` FastMCP's [OIDC Proxy documentation](https://gofastmcp.com/servers/auth/oidc-proxy) covers the cloud and multi-instance deployment guidance in full and is authoritative for the exact current API. See also the [FastMCP cookbook](/build-your-own-mcp-server/fastmcp). This isn't unique to FastMCP. **Any** server that keeps DCR clients in process memory — other frameworks, or a hand-rolled OAuth layer — fails the same way once it runs more than one instance. The two-part fix (shared store + stable key) is identical regardless of framework, and the [TypeScript SDK](/build-your-own-mcp-server/typescript) and [Spring AI](/build-your-own-mcp-server/java-spring) cookbooks call out the equivalent for those stacks. [Cloudflare Workers](/build-your-own-mcp-server/cloudflare) avoids it by storing clients in KV. ## After you fix the server, force MCP Manager to re-register MCP Manager persists the `client_id` **and `client_secret`** it received during dynamic client registration and reuses that stored pair on every connection — it never re-registers on its own. So after you repair the server's storage, MCP Manager keeps presenting the `client_id` it registered against the broken deployment, your repaired server doesn't recognize it, and the same "Client Not Registered" error continues. Fixing the server is necessary but not sufficient: you also have to make MCP Manager register again. Delete the server and re-add it to force a fresh registration: Remove it on the [MCP Servers](https://app.mcpmanager.ai/settings/servers) page. MCP Manager stops using the stale `client_id` and `client_secret` it stored against the old deployment. MCP Manager treats this as a new server, re-runs discovery and DCR, receives a fresh `client_id` and `client_secret` your repaired server recognizes, and stores those instead. Retrying in place won't clear this. **Authenticate now**, **Add Identity**, and **Reconnect server** all reuse the `client_id` and `client_secret` already stored against the old deployment, so the registration error repeats. Only deleting and re-adding the server forces MCP Manager to register from scratch. Re-add the server any time its stored registration changes underneath MCP Manager — a new client store, a wiped database, or a fresh OAuth layer. ## The other usual suspects If the failure is **consistent** rather than intermittent, or the storage fix doesn't resolve it, work down this list. Each is a distinct server-side cause. If your server doesn't publish `/.well-known/oauth-authorization-server` ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)), or it omits `registration_endpoint`, MCP Manager can't discover where to register and can't run automatic DCR — so it falls back to asking you for a Client ID and Secret. Verify what your server actually advertises by fetching that well-known path and reading the JSON. Confirm `registration_endpoint`, `authorization_endpoint`, and `token_endpoint` are present and point at reachable URLs. MCP Manager handles a server that legitimately has no metadata by guiding you to [pre-registration](/security/authentication-and-identity#oauth-with-client-pre-registration) — but if you *intended* DCR to work, missing metadata is why it didn't. Some servers ignore the `redirect_uris` supplied during DCR and enforce their own allowlist at the authorize step. If MCP Manager's callback isn't on it, the authorize hop is rejected. Add the exact callback URL to your server's allowed redirect URIs: ```text theme={null} https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback ``` A server may advertise a `registration_endpoint` but in practice only accept a fixed allowlist of well-known clients (Claude, Cursor, ChatGPT, and the like), rejecting any dynamically registered client. That's a limitation in the server's DCR implementation — open DCR means accepting clients you didn't pre-approve. Either widen the server to accept dynamically registered clients, or expose a pre-registration path so MCP Manager can connect with a Client ID and Secret you issue it. MCP Manager negotiates a supported `token_endpoint_auth_method` (`client_secret_post`, `client_secret_basic`, or `none`) and requests the scopes your metadata advertises. If your server requires a method it doesn't advertise, or a scope it doesn't list in `scopes_supported`, registration can succeed while the token exchange fails. Make sure the methods and scopes your server *enforces* match the ones it *advertises* in its metadata. ## A quick diagnostic path ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TD A["Identity step fails"] --> B{"Does discovery find
a registration_endpoint?"} B -->|"no"| M["Fix well-known metadata,
or use pre-registration"] B -->|"yes"| C{"'Client Not Registered'
and intermittent?"} C -->|"yes"| D["⚠️
Ephemeral per-instance store —
shared storage + stable key"] C -->|"no, fails consistently"| E{"Rejected at the
authorize redirect?"} E -->|"yes"| F["Allow the callback URI,
or the well-known-client allowlist"] E -->|"token exchange fails"| G["Reconcile advertised vs enforced
auth method and scopes"] classDef warn fill:#ffd863,color:#12141d,stroke:#ffa535,stroke-width:1.5px; classDef step fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; class D warn; class M,F,G step; ``` ## What MCP Manager does on its side When the OAuth callback fails, MCP Manager doesn't fail silently. It records an **alert** (`error.inbound_server.oauth_callback_failed`) capturing the provider's error code and description and the redirect URI involved, and deep-links you to it so you can read exactly what the upstream returned. The server's identity then shows a **Not connected** state. Because MCP Manager reuses the `client_id` and `client_secret` it already stored, retrying in place won't pick up a server-side fix that changed the client registration — delete and re-add the server to force a fresh registration, as in the section above. Retrying in place resolves the failure only when the stored registration is still valid, and it also lets you switch the server to pre-registration or token auth if you decide not to rely on DCR. See [Alerts](/features/alerts) and [Authentication & Identity](/security/authentication-and-identity#edge-and-failure-behavior). If your server is private, remember MCP Manager's discovery and registration calls come from a **single static IP**, shown at [Security → IP addresses](https://app.mcpmanager.ai/settings/security/ip-addresses). Allowlist it so those backend hops aren't silently dropped by a firewall. See [Remote MCP Servers](/mcp-gateway-concepts/mcp-servers/remote#why-use-a-remote-server). # Build with FastMCP (Python) Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/fastmcp How to build a FastMCP (Python) MCP server that runs behind MCP Manager: serving Streamable HTTP rather than the unsupported legacy SSE transport, choosing among FastMCP's TokenVerifier, RemoteAuthProvider, OAuthProxy, OIDCProxy and OAuthProvider to match one of MCP Manager's three auth modes, the Cloud Run client-storage gotcha (the in-memory default loses dynamically-registered clients), and a compatibility checklist with the framework-specific gotchas. [FastMCP](https://gofastmcp.com) is the most widely used Python framework for building MCP servers, and its high-level `FastMCP` class also ships inside the official [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk). It can be a full OAuth authorization server *and* serve Streamable HTTP, so it can satisfy any of **MCP Manager**'s three authentication modes. This page covers the decisions that matter for running it behind MCP Manager and the gotchas that bite on cloud hosts — it is not a substitute for [FastMCP's own documentation](https://gofastmcp.com), which is authoritative and moves quickly. New to the requirements? Read [Building Your Own MCP Server](/build-your-own-mcp-server/overview) first — it covers the two things every server must do and how to choose an auth mode. This page is the FastMCP-specific layer on top. ## Serve Streamable HTTP — never SSE-only Run FastMCP over its HTTP transport, which serves Streamable HTTP. The default path is **`/mcp/`** with a **trailing slash** — point MCP Manager at exactly that. For any multi-instance host (Cloud Run, Kubernetes), run the ASGI app in stateless mode so a request isn't tied to one process's memory. See [FastMCP — Running the server over HTTP](https://gofastmcp.com/deployment/http). ```python Illustrative — see gofastmcp.com/deployment/http theme={null} from fastmcp import FastMCP mcp = FastMCP('My Server') # Streamable HTTP at /mcp/ (note the trailing slash). # stateless_http=True is what you want behind a load balancer. app = mcp.http_app(stateless_http=True) ``` Do **not** start FastMCP with `transport='sse'`. That serves the legacy two-endpoint HTTP+SSE transport, which **MCP Manager does not connect to** — it speaks Streamable HTTP only and will mark an SSE-only server unsupported. (Streamable HTTP may still *answer* with a `text/event-stream` body; that's fine and supported.) ## Match FastMCP's auth to an MCP Manager mode FastMCP offers a ladder of auth providers (all passed as `auth=` on `FastMCP(...)`). The decision is which one matches the [MCP Manager mode](/build-your-own-mcp-server/overview#choosing-an-authentication-mode) you want. | You want | Use in FastMCP | Notes | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | **Standard OAuth + DCR** (most seamless) | `OAuthProvider` (full authorization server), or `OAuthProxy` / `OIDCProxy` to front an upstream IdP while still exposing DCR to MCP Manager | Publishes the well-known metadata and a `/register` endpoint, so MCP Manager self-registers and users just approve | | **OAuth pre-registration** | `OAuthProxy` / `OIDCProxy` configured with your fixed upstream app credentials | You give MCP Manager a Client ID + Secret instead of relying on DCR | | **Token in a header** | `TokenVerifier` (e.g. `JWTVerifier`), or a custom token check | Simplest; no metadata or DCR. Maps to MCP Manager's header-token mode | A bare `TokenVerifier` is **not** enough for the DCR mode — it validates tokens but exposes no `/register` endpoint and no authorization-server metadata, so MCP Manager can't auto-register against it. Choose a provider that publishes metadata if you want the seamless path. See [FastMCP — Authentication](https://gofastmcp.com/servers/auth/authentication), [OAuth Proxy](https://gofastmcp.com/servers/auth/oauth-proxy), and [OIDC Proxy](https://gofastmcp.com/servers/auth/oidc-proxy). FastMCP's `OAuthProxy` / `OIDCProxy` are the sweet spot for most teams: you keep an upstream IdP (Google Workspace, Auth0, WorkOS) that doesn't itself do DCR, and FastMCP presents a clean DCR-capable face to MCP Manager. That's exactly the shape behind the most common deployment. ## The Cloud Run gotcha you must get right If you use a FastMCP proxy provider (`OAuthProxy` / `OIDCProxy`) and deploy on a multi-instance host, this is the failure that bites: FastMCP's registered-client store defaults to an **in-memory store on Linux**, and its default encryption key is **ephemeral**. MCP Manager registers a client against one instance; the browser authorize request hits another instance that has no record of it, and your server renders a "client not registered" error. It looks intermittent but is structural. The fix has **two parts** — getting only the first is a common near-miss: 1. Point `client_storage` at a **shared, network-accessible** backend (Redis on a managed cache, or another durable store) so registrations are visible to every instance. See the [`client_storage` parameter reference](https://gofastmcp.com/servers/auth/oidc-proxy#param-client-storage). 2. Use a **stable encryption key** shared across instances (for example a Fernet key from your secret manager) and a stable `jwt_signing_key`, so a second instance can decrypt what the first one wrote. ```python Illustrative — see gofastmcp.com/servers/auth/oidc-proxy theme={null} auth = OIDCProxy( # ... your upstream OIDC config + public base_url ... client_storage=shared_backed_store, # Redis/etc. — NOT the in-memory default ) ``` `min-instances` or session affinity won't reliably fix this — the registration and authorize requests come from different origins and can't be pinned together. Shared storage plus a stable key is the real fix. The full diagnosis is in [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). Deploying the fix doesn't reconnect a server you already added. MCP Manager reuses the `client_id` and `client_secret` it first registered, so once shared storage is live, **delete and re-add the server** to force a fresh registration against the repaired deployment. ## MCP Manager compatibility checklist Serve over the HTTP transport (`mcp.http_app(...)`), point MCP Manager at the real path including the trailing slash (`/mcp/`), and never ship `transport='sse'` only. Use `stateless_http=True` so a session isn't bound to one process's memory behind a load balancer. For the seamless OAuth path, use `OAuthProvider`, `OAuthProxy`, or `OIDCProxy` — not a bare `TokenVerifier` — so the well-known metadata and `/register` exist. Set `client_storage` to a shared backend and use stable encryption / `jwt_signing_key` values across instances. Set the proxy's `base_url` to your public HTTPS URL (not `localhost`), and allow MCP Manager's callback `https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback` (FastMCP's `allowed_client_redirect_uris`). ## FastMCP gotchas FastMCP's HTTP transport serves at `/mcp/` by default. If MCP Manager (or any client) calls `/mcp` without the slash and your stack 301-redirects, the redirect can drop the `Authorization` header. Point MCP Manager at the exact path, or configure the path explicitly. The proxy providers default to an in-memory client store on Linux and an ephemeral key — fine on one laptop, broken across instances. Configure `client_storage` and a stable key before deploying. See [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). If `base_url` is left as `http://localhost:...`, the published well-known metadata advertises endpoints MCP Manager can't reach. Set `base_url` to your public HTTPS URL so `issuer`, `authorization_endpoint`, `token_endpoint`, and `registration_endpoint` are all reachable. ## Further reading The deep dive on the dynamic-client-registration failure FastMCP hits on Cloud Run. The authoritative reference for FastMCP's auth providers. Fronting an upstream OIDC provider while exposing DCR, including client storage. The cross-framework requirements, decision tree, and troubleshooting catalog. # Build with Go Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/go How to build a Go MCP server (official Go SDK or mcp-go) to run behind MCP Manager: serving Streamable HTTP, and why these frameworks are resource servers that verify bearer tokens rather than authorization servers with dynamic client registration — so you connect via MCP Manager's token-in-header or pre-registration modes, advertising an external authorization server through RFC 9728 protected-resource metadata. Go has two main MCP frameworks: the official [Go SDK](https://github.com/modelcontextprotocol/go-sdk) (maintained with Google) and the popular community [`mcp-go`](https://github.com/mark3labs/mcp-go). Both serve Streamable HTTP, and both are **resource servers** — they verify a bearer token but do **not** implement an OAuth authorization server or dynamic client registration. That's the defining fact for **MCP Manager**: with Go you connect via the **token-in-header** or **pre-registration** modes, with any DCR handled by a *separate* authorization server. This page covers that path; the frameworks' own docs are authoritative. Start with [Building Your Own MCP Server](/build-your-own-mcp-server/overview) for the requirements and the auth-mode decision tree. This page is the Go layer on top. ## Serve Streamable HTTP Both frameworks expose Streamable HTTP as an `http.Handler`: * **Official Go SDK:** `mcp.NewStreamableHTTPHandler(getServer, opts)` returns a handler serving a single MCP endpoint. See the [repo](https://github.com/modelcontextprotocol/go-sdk) and the [`auth` package](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth). * **mcp-go:** `server.NewStreamableHTTPServer(mcpServer, server.WithEndpointPath("/mcp"))`, with options like `WithStateful` and `WithStreamableHTTPCORS`. See the [repo](https://github.com/mark3labs/mcp-go). ```go Illustrative — see the framework docs theme={null} // Official Go SDK — a single Streamable HTTP endpoint. handler := mcp.NewStreamableHTTPHandler(getServer, nil) http.ListenAndServe(":8080", handler) ``` Serve Streamable HTTP, not an SSE-only endpoint — MCP Manager won't connect to a legacy HTTP+SSE-only server. On multi-instance hosts, prefer stateless handling (or sticky sessions); both frameworks keep stateful sessions in process memory by default. ## Authenticate as a resource server Neither framework issues tokens or registers clients — they validate tokens an external authorization server issued, and advertise that authorization server through RFC 9728 metadata. * **Official Go SDK:** wrap your handler with `auth.RequireBearerToken(verifier, opts)` (it returns `401` with a `WWW-Authenticate` pointing at your metadata) and serve `auth.ProtectedResourceMetadataHandler(...)`. * **mcp-go:** serve `server.NewProtectedResourceMetadataHandler(...)` at `/.well-known/oauth-protected-resource`; do token validation in your own middleware. This maps to MCP Manager as follows: | You want | How, in Go | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Token in a header** | Validate an API key / bearer token in middleware. Simplest; one shared identity unless you scope tokens per user | | **OAuth pre-registration** | Run an external authorization server (your IdP), advertise it via RFC 9728, and give MCP Manager a Client ID + Secret for it | Standard OAuth + **DCR** isn't available from the Go server itself — there's no `/register` endpoint to expose. If you need DCR, put a DCR-capable authorization server in front and let the Go server validate its tokens. ## MCP Manager compatibility checklist Serve `NewStreamableHTTPHandler` (Go SDK) or `NewStreamableHTTPServer` (mcp-go) at a stable path; not an SSE-only endpoint. Stateful sessions live in process memory — run stateless or enable sticky sessions behind a load balancer. Validate a header token (simplest), or stand up an external authorization server and connect MCP Manager with pre-registration. Serve protected-resource metadata so MCP Manager can discover the external authorization server, with the `resource` set to your public URL. ## Go gotchas Neither framework can be an authorization server, so DCR must live elsewhere. Don't expect MCP Manager's automatic OAuth path to find a `registration_endpoint` on the Go server itself — use token or pre-registration. See [the auth modes](/build-your-own-mcp-server/overview#choosing-an-authentication-mode). Stateful Streamable HTTP sessions are per-process. Behind a load balancer without sticky routing, follow-up requests can miss their session — run stateless or pin sessions. Your token verifier should validate the audience against your canonical public URL (no trailing slash), or valid tokens get rejected with `401`. ## Further reading The Streamable HTTP handler and the `auth` package for bearer verification. The community framework, its Streamable HTTP server, and protected-resource metadata. MCP Manager's token-in-header and pre-registration modes in depth. The cross-framework requirements, decision tree, and troubleshooting catalog. # Build with Spring AI (Java) Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/java-spring How to build a Java MCP server with Spring AI to run behind MCP Manager: serving the STREAMABLE protocol, choosing between the mcp-authorization-server module (the only JVM path to dynamic client registration, built on Spring Authorization Server) and the resource-server or API-key modules, and the critical gotcha that the default InMemoryRegisteredClientRepository is ephemeral and must be swapped for JdbcRegisteredClientRepository in production. [Spring AI](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-security.html) is the JVM path to an MCP server, and its `mcp-security` modules are the **only Java option that can be a full OAuth authorization server with dynamic client registration** — via the `mcp-authorization-server` module, built on Spring Authorization Server. The official [Java SDK](https://github.com/modelcontextprotocol/java-sdk) deliberately delegates authorization to Spring, so this page is really about the Spring security modules. They're authoritative; this page covers the choices and the one gotcha that matters most for **MCP Manager**. Start with [Building Your Own MCP Server](/build-your-own-mcp-server/overview) for the requirements and the auth-mode decision tree. This page is the Spring AI layer on top. ## Serve the STREAMABLE protocol Use the WebMVC server starter (`spring-ai-starter-mcp-server-webmvc`) and set `spring.ai.mcp.server.protocol=STREAMABLE` (or `STATELESS` for a stateless deployment). That gives you the Streamable HTTP transport MCP Manager requires. `mcp-security` supports **WebMVC**, not WebFlux — a WebFlux MCP server isn't covered by the security modules today. And as with every framework here, don't ship a server that only speaks the legacy HTTP+SSE transport; MCP Manager won't connect to it. ## Choose a security module per MCP Manager mode `mcp-security` has two distinct modules. Pick the one matching your [chosen MCP Manager mode](/build-your-own-mcp-server/overview#choosing-an-authentication-mode). | You want | Use in Spring | Notes | | ----------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | **Standard OAuth + DCR** | `mcp-authorization-server` (`McpAuthorizationServerConfigurer.mcpAuthorizationServer()`) | Built on Spring Authorization Server; **DCR is on by default**. Also serves RFC 8414 + RFC 9728 metadata | | **Pre-registration / bearer token** | `mcp-server-security` (`McpServerOAuth2Configurer.mcpServerOAuth2()`) | Resource server: validates inbound JWTs against an issuer, serves RFC 9728 at `/.well-known/oauth-protected-resource/mcp` | | **Token in a header** | `McpApiKeyConfigurer.mcpServerApiKey()` | API-key auth, maps to MCP Manager's header-token mode | See [Spring AI — MCP Security](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-security.html) and the [Securing MCP Servers](https://spring.io/blog/2025/09/30/spring-ai-mcp-server-security/) blog post. ## The gotcha: the default client repository is in-memory If you run `mcp-authorization-server`, registered clients are stored in Spring Authorization Server's `RegisteredClientRepository`, which **defaults to `InMemoryRegisteredClientRepository`** — dev/test only, and ephemeral. On a multi-instance deployment that's the [classic DCR failure](/build-your-own-mcp-server/debugging-self-hosted-oauth): MCP Manager registers against one instance, the authorize hop lands on another, and the client isn't found. The fix is to wire a **`JdbcRegisteredClientRepository`** backed by a shared database (applying the `oauth2-registered-client-schema.sql` tables). This is an explicit configuration step — it does not happen automatically. Switching to `JdbcRegisteredClientRepository` is required for any production or multi-instance deployment. Leaving the default in-memory repository in place is the single most common reason a Spring-based DCR server works in testing and fails once it scales. Switching to the JDBC repository doesn't reconnect a server you already added. MCP Manager reuses the `client_id` and `client_secret` it first registered, so once the persistent repository is live, **delete and re-add the server** to force a fresh registration. ## MCP Manager compatibility checklist Set `spring.ai.mcp.server.protocol=STREAMABLE` with the WebMVC starter; mcp-security doesn't cover WebFlux. `mcp-authorization-server` for DCR; `mcp-server-security` for bearer-token resource-server; `McpApiKeyConfigurer` for header tokens. Replace the default in-memory client repository with the JDBC one so registrations survive restarts and are shared across instances. Ensure the authorization server's issuer and the resource server's audience are your public URL, so discovery and token validation line up. ## Spring AI gotchas The default loses clients on restart and isn't shared across instances. Wire `JdbcRegisteredClientRepository` for production. See [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). `mcp-security` targets WebMVC. If you're on WebFlux, the security modules don't apply — plan for WebMVC or handle auth yourself. A documented limitation of the current modules is that every client supports all `resource` identifiers. If you rely on per-resource audience separation, validate that behavior against your version. ## Further reading The authoritative reference for the resource-server and authorization-server modules. A walkthrough of the Spring AI MCP server security model. The dynamic-client-registration failure and why in-memory storage causes it. The cross-framework requirements, decision tree, and troubleshooting catalog. # Building Your Own MCP Server Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/overview A one-stop guide for developers building their own remote MCP server to run behind MCP Manager: the two things any server must do (expose Streamable HTTP and authenticate one of three supported ways), a decision tree for choosing an auth mode, a framework comparison across FastMCP, the TypeScript SDK, Cloudflare Workers, Spring AI, and Go, the universal gotchas that bite multi-instance deployments, and a troubleshooting catalog that maps each symptom you see in MCP Manager back to the server-side misconfiguration that actually causes it. This section is for developers **building their own MCP server** to run behind **MCP Manager** — whether you're at the start, choosing a framework and deciding how to wire up authentication, or you already have a server and it won't connect cleanly. MCP Manager connects to any standards-compliant remote MCP server; this guide is about making *your* server one of them, and fixing it when it isn't. Connecting a **third-party** server (Atlassian, Slack, HubSpot)? That's a different job — see [Find & Connect MCP Servers](/mcp-server-guides/overview) and the [per-server guides](/mcp-server-guides/overview#connection-guides). This section is about a server **you build and operate**. ## Two requirements, and everything else is detail Strip away the framework choices and a server that works behind MCP Manager does exactly two things: 1. **It's reachable over HTTPS as a Streamable HTTP MCP server.** [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports) is the current MCP remote transport — a single endpoint (commonly `/mcp`) that accepts a JSON-RPC `POST` and answers with either a JSON body or a `text/event-stream` body. **MCP Manager connects over Streamable HTTP only.** It does *not* fall back to the older, separate HTTP+SSE transport (the 2024-11-05 design with distinct `/sse` and `/messages` endpoints); a server that exposes only that legacy transport is rejected as **unsupported**. Streaming your *responses* as `text/event-stream` from the single Streamable HTTP endpoint is fine — what's unsupported is the two-endpoint legacy transport. Build on Streamable HTTP. 2. **It authenticates in one of the three ways MCP Manager supports** — standard OAuth with dynamic client registration, OAuth with client pre-registration, or a token in a custom header. When you add a server by URL, MCP Manager runs a discovery call and connects automatically if it can. Every framework decision below maps onto these two axes. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart LR Dev["🧑‍💻
Your MCP server"] --> R1{"Reachable over HTTPS
as Streamable HTTP?"} R1 -->|"no"| F1["Expose a remote endpoint
(or run it as a workstation server)"] R1 -->|"yes"| R2{"Authenticates a
supported way?"} R2 -->|"OAuth + DCR"| A1["Fully automatic —
paste URL, approve"] R2 -->|"OAuth, no DCR"| A2["Pre-registration —
you issue a Client ID + Secret"] R2 -->|"API key / token"| A3["Token in a header"] A1 --> OK["✅ Governed by MCP Manager"] A2 --> OK A3 --> OK classDef trust fill:#2fedb4,color:#062b4c,stroke:#059669,stroke-width:1.5px; classDef step fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; class OK trust; class A1,A2,A3,F1 step; ``` ## Choosing an authentication mode The biggest decision is how your server authenticates, because it determines how much you build and how seamless the connect experience is. MCP Manager supports three modes (covered in depth under [Authentication & Identity](/security/authentication-and-identity#how-mcp-manager-authenticates-to-a-server)); here's how to choose. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TD Q1{"Do you need per-user identity
(each user acts as themselves)?"} Q1 -->|"no — one shared service credential is fine"| T["Token in a custom header
Simplest. An API key your framework validates."] Q1 -->|"yes"| Q2{"Can your server be an OAuth
authorization server with
dynamic registration?"} Q2 -->|"yes"| DCR["Standard OAuth + DCR
Most seamless. MCP Manager self-registers; users just approve."] Q2 -->|"no, or you proxy an upstream IdP"| PRE["OAuth pre-registration
You issue MCP Manager a Client ID + Secret once."] classDef step fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; classDef best fill:#2fedb4,color:#062b4c,stroke:#059669,stroke-width:1.5px; class DCR best; class T,PRE step; ``` The most seamless. Your server is an OAuth 2.1 authorization server that supports [dynamic client registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) and publishes [well-known metadata](https://datatracker.ietf.org/doc/html/rfc8414). MCP Manager registers itself and users just approve a consent screen. **Most build effort, best UX.** Your server speaks OAuth but doesn't register clients on the fly (or proxies an upstream IdP that doesn't). You issue MCP Manager a **Client ID and Client Secret** once and allow its callback URL. **Less to build, still per-user.** No OAuth at all — your server checks an API key or bearer token. You hand MCP Manager the header name and value. **Least to build; one shared identity unless you scope tokens per user.** If you only need a single shared service identity and want the least to build, a **token in a header** is the fastest path to a governed server. Reach for **OAuth + DCR** when you want every user to authenticate as themselves with no setup on their part — that's where MCP Manager's per-user identity model shines. See [per-user versus shared identity](/security/authentication-and-identity#per-user-identity-versus-shared-identity). ## Pick your framework The framework you choose mostly decides **how much of the OAuth work is done for you**. The key fact: only some frameworks can be an OAuth authorization server with dynamic client registration out of the box. The rest are resource servers — they verify a token but expect a separate authorization server — which maps to the pre-registration or token modes above. All of them can serve Streamable HTTP; just don't ship a server that speaks *only* the legacy HTTP+SSE transport, which MCP Manager won't connect to. | Framework | Language | OAuth + DCR built in? | Registered-client storage | Read the cookbook | | ----------------------------- | ---------- | ------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------- | | **FastMCP** | Python | Yes — `OAuthProvider`, `OAuthProxy`, `OIDCProxy` | In-memory on Linux by default — **must** configure a shared store | [FastMCP →](/build-your-own-mcp-server/fastmcp) | | **MCP TypeScript SDK** | TypeScript | Yes — `mcpAuthRouter`, `ProxyOAuthServerProvider` | Only an in-memory demo store ships — **bring your own** | [TypeScript →](/build-your-own-mcp-server/typescript) | | **Cloudflare Workers** | TypeScript | Yes — `workers-oauth-provider` | Workers KV — durable across instances (but eventually consistent) | [Cloudflare →](/build-your-own-mcp-server/cloudflare) | | **Spring AI** | Java | Yes — `mcp-authorization-server` | In-memory by default — switch to JDBC for production | [Spring AI →](/build-your-own-mcp-server/java-spring) | | **Go** (official SDK, mcp-go) | Go | No — resource-server / token only | N/A — DCR lives in an external authorization server | [Go →](/build-your-own-mcp-server/go) | The most popular Python framework, with full OAuth and DCR. The shared-storage gotcha and the proxy patterns, explained. The official SDK's full authorization server, plus the Vercel `mcp-handler` resource-server pattern for Next.js. The closest thing to a turnkey DCR server — KV-backed storage means none of the ephemeral-client pain. The one JVM path to a DCR-capable server, and why you must move off the in-memory client repository. Resource-server frameworks: how to connect with pre-registration or a token when the server can't do DCR itself. The deep dive on the single most common failure — the OAuth identity step that works, then doesn't. ## Five rules that prevent most problems These cut across every framework. Get them right and the troubleshooting section below stays unread. Expose the current Streamable HTTP transport at a fixed `https://` path and point MCP Manager at exactly that path. Do **not** ship a server that speaks only the older HTTP+SSE transport (separate `/sse` and `/messages` endpoints) — MCP Manager won't connect to it and marks it unsupported. Watch the **trailing slash**, too: `/mcp` and `/mcp/` are different URLs, and a framework that 301-redirects between them can drop the `Authorization` header on the way. Serve [`/.well-known/oauth-authorization-server`](https://datatracker.ietf.org/doc/html/rfc8414) and [`/.well-known/oauth-protected-resource`](https://datatracker.ietf.org/doc/html/rfc9728), and make every URL inside them — `issuer`, `authorization_endpoint`, `token_endpoint`, `registration_endpoint`, `resource` — your **public** address, not an internal host or `localhost`. The `issuer` must exactly match the URL the metadata is served from. On any host that runs more than one instance — Cloud Run, Lambda, Kubernetes replicas, anything load-balanced — a per-instance in-memory store loses the client MCP Manager just registered. Use a shared, network-accessible store and a stable encryption/signing key shared across instances. This is the number-one cause of "it worked, then it didn't." See [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). If your OAuth layer enforces a redirect-URI allowlist, add MCP Manager's fixed callback: ```text Callback URL theme={null} https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback ``` Make the `resource`/audience your server validates equal to its canonical public URL (no trailing slash). A token minted for `https://mcp.example.com/` won't validate against `https://mcp.example.com` — a single slash is a real, common failure. If your server is private, allow MCP Manager's **single static IP** (shown at [Security → IP addresses](https://app.mcpmanager.ai/settings/security/ip-addresses)) through your firewall, so the backend discovery and registration calls aren't silently dropped. ## Troubleshooting: it works in a quick test, but fails in MCP Manager If you've landed here from a search, you're probably seeing an error while connecting your own server and you suspect MCP Manager. Usually the symptom surfaces *in* MCP Manager but the cause lives *in your server's* OAuth or transport configuration — MCP Manager implements the standard flow by the book. Each entry below maps what you see to the most likely server-side cause and fix. Not every one of these is your server's fault. A few are bugs in MCP **clients** (the AI apps) that present as server problems — we flag those so you don't "fix" a server that's already correct. **You see:** an error page (often rendered by your own server) saying the client ID wasn't found in its registry, during the identity/authorize step. It's intermittent — a fresh setup works, then everyone hits it. **Most likely cause:** your OAuth layer stores dynamically-registered clients **in process memory**, and on a multi-instance host the authorize request lands on an instance that never saw the registration. This is structural, not flaky networking. **Fix:** a shared, network-accessible client store plus a stable encryption key across instances. The full diagnosis and per-framework fix is in [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). **You see:** discovery doesn't find OAuth, so MCP Manager falls back to asking for a Client ID and Secret (pre-registration) when you expected automatic OAuth. **Most likely cause:** your server doesn't publish complete OAuth metadata — `/.well-known/oauth-authorization-server` is missing or omits `registration_endpoint`, `/.well-known/oauth-protected-resource` (RFC 9728) is absent, or your `401` doesn't carry the challenge header that points at the metadata. A wrong `issuer` (not byte-matching the URL it's served from), or metadata advertising internal or `localhost` URLs, causes the same outcome. **Fix:** serve both well-known documents at your public URL, include `registration_endpoint` if you support DCR, and make `issuer` match exactly. Verify by fetching the well-known path and reading the JSON. **You see:** the connection fails before any identity step — a transport error, a `404`/`405`, a timeout, or the server is marked **unsupported**. **Most likely cause:** your server speaks only the **legacy HTTP+SSE transport** (separate `/sse` and `/messages` endpoints). MCP Manager connects over **Streamable HTTP only** and does not fall back, so an SSE-only server is rejected on a content-type mismatch. Other causes: a **trailing-slash redirect** (`/mcp` ↔ `/mcp/`) that strips the request, or **Origin/CORS** validation rejecting the call. (Streaming *responses* as `text/event-stream` from your single Streamable HTTP endpoint is fully supported — only the two-endpoint legacy transport is not.) **Fix:** serve Streamable HTTP at the exact path with no redirect; if you validate `Origin`, allow MCP Manager rather than disabling the check. **You see:** the browser hop to authorize is rejected — a `redirect_uri` mismatch or "not registered for client." **Most likely cause:** your auth server enforces its own redirect-URI allowlist and MCP Manager's callback isn't on it. Some servers ignore the `redirect_uris` sent during DCR and enforce a separate list. **Fix:** add `https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback` to your server's allowed redirect URIs. **You see:** the identity is created, but every tool call comes back `401`. **Most likely cause:** an **audience/resource mismatch**. Your server validates the token against an audience or issuer that doesn't match what was minted — frequently a trailing-slash difference between the `resource` your metadata advertises and your server's canonical URL ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html)). **Fix:** make the advertised `resource`/audience the canonical no-trailing-slash public URL, and confirm your authorization server issues tokens with that exact `aud`. **You see:** connections or identities succeed sometimes and fail other times with no clear pattern; "session not found," or repeated re-auth prompts. **Most likely cause:** per-instance in-memory state on a multi-instance deployment — either DCR clients (see the first entry) or **Streamable HTTP sessions** held in one instance's memory without sticky routing. Eventually-consistent stores (for example Cloudflare KV) cause a related, self-clearing version: a token or client written on one node isn't visible on another for a short window. **Fix:** run the server **stateless**, enable sticky sessions, or externalize session and client state to a shared store. For eventually-consistent stores, tolerate a brief propagation delay with retries. **You see:** the server shows connected, but the tool list is empty. **Most likely cause:** the token's **scopes are too narrow** to expose the scope-gated tools, the server returns an empty list instead of a `401` when auth quietly failed, or the `tools` capability wasn't advertised at `initialize`. (Client-side: some clients cache the tool list at startup — a restart can be needed.) **Fix:** grant the scopes the tools require, return `401` on auth failure so re-auth is triggered, and confirm your server advertises the `tools` capability. **You see:** identities stop working after a while and users must reconnect. **Most likely cause:** your authorization server never issues a **refresh token**, so MCP Manager can't refresh silently when the access token expires. MCP Manager refreshes automatically when a refresh token exists. **Fix:** enable refresh tokens on your authorization server (include `refresh_token` in the client's `grant_types`; advertise `offline_access` if your stack uses it). When MCP Manager hits an OAuth failure it records an **alert** with the provider's exact error and a deep link, and marks the server **Not connected** so you can fix the server and use the **reconnect** flow. See [Alerts](/features/alerts) and the failure-behavior notes in [Authentication & Identity](/security/authentication-and-identity#edge-and-failure-behavior). ## Further reading The deep dive on the dynamic-client-registration failure that trips up multi-instance deployments. Put a "Connect via MCP Manager" button on your site so users reach your server pre-filled and one click from authenticating. The three authentication methods, per-user versus shared identity, and how credentials are stored. How MCP Manager reaches a remote server and how to choose an authentication method. How MCP Manager detects a server's authentication type when you add it by URL. ## External sources The Streamable HTTP transport every remote server should implement. The OAuth 2.1-based authorization model for MCP, including discovery and DCR. # Build with the TypeScript SDK Source: https://docs.mcpmanager.ai/build-your-own-mcp-server/typescript How to build an MCP server on the official TypeScript SDK to run behind MCP Manager: serving Streamable HTTP with StreamableHTTPServerTransport, choosing between the SDK's full authorization server (mcpAuthRouter, with dynamic client registration) and resource-server mode (mcpAuthMetadataRouter + requireBearerAuth), why you must replace the in-memory demo client store with a persistent one, the protected-resource-metadata path suffix, and the Vercel mcp-handler resource-server pattern for Next.js. The official [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) (`@modelcontextprotocol/sdk`) is the most capable TypeScript option: it can be a full OAuth authorization server with dynamic client registration, *or* a plain resource server that verifies bearer tokens. Either way it serves Streamable HTTP, so it fits any of **MCP Manager**'s three authentication modes. This page covers the decisions and gotchas for running it behind MCP Manager — the SDK's own docs and source are authoritative. It also covers Vercel's [`mcp-handler`](https://github.com/vercel/mcp-handler) for Next.js at the end. Start with [Building Your Own MCP Server](/build-your-own-mcp-server/overview) for the cross-framework requirements and the auth-mode decision tree. This page is the TypeScript layer on top. ## Serve Streamable HTTP — never SSE-only Use `StreamableHTTPServerTransport` (from `@modelcontextprotocol/sdk/server/streamableHttp.js`). For a stateless deployment behind a load balancer, construct it with `sessionIdGenerator: undefined`; for stateful sessions, supply a generator (the transport then issues an `Mcp-Session-Id`). The SDK also ships a legacy `SSEServerTransport` for the old two-endpoint transport — don't ship that as your only transport. See the [server guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/server.md). ```ts Illustrative — see the SDK server guide theme={null} import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; // Stateless: no session bound to one instance's memory. const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); ``` A server that only mounts `SSEServerTransport` (the 2024-11-05 HTTP+SSE transport) **will not connect** to MCP Manager, which speaks Streamable HTTP only. Streamable HTTP responses themselves may be `text/event-stream` — that's supported. ## Match the SDK's auth to an MCP Manager mode The SDK splits cleanly into authorization-server mode and resource-server mode. Pick the one that matches your [chosen MCP Manager mode](/build-your-own-mcp-server/overview#choosing-an-authentication-mode). | You want | Use in the SDK | Notes | | ------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | | **Standard OAuth + DCR** | `mcpAuthRouter` (mounts the authorization-server endpoints, including `/register`) | `/register` only appears if your `clientsStore.registerClient` is implemented | | **OAuth + DCR proxying an IdP** | `ProxyOAuthServerProvider` with `endpoints.registrationUrl` set | Forwards dynamic registration to your upstream IdP | | **Pre-registration / token** | `mcpAuthMetadataRouter` + `requireBearerAuth({ verifier, requiredScopes, resourceMetadataUrl })` | Resource-server only: you verify tokens an external authorization server issued | For the DCR path, the registration endpoint is mounted **only** when `clientsStore.registerClient` exists — so dynamic registration is opt-in by implementing it. See the SDK's `src/server/auth/` (router, providers, middleware). The only client store that ships is the **in-memory `DemoInMemoryClientsStore`** — a `Map`, for examples only. On any multi-instance or serverless deployment you **must** implement a persistent `OAuthRegisteredClientsStore` (backed by a database or Redis), and persist authorization codes and tokens too. An in-memory store loses MCP Manager's registration between the register and authorize hops — the [DCR failure](/build-your-own-mcp-server/debugging-self-hosted-oauth) in a nutshell. Fixing the store doesn't reconnect a server you already added. MCP Manager reuses the `client_id` and `client_secret` it first registered, so after you deploy a persistent client store, **delete and re-add the server** to force a fresh registration. ## Get the metadata path right The SDK serves protected-resource metadata at a **path-suffixed** well-known URL when your MCP endpoint isn't at the root — for example `/.well-known/oauth-protected-resource/mcp` for an endpoint at `/mcp`. Use the SDK's `getOAuthProtectedResourceMetadataUrl(serverUrl)` helper to build it rather than hand-writing the path, and make sure the advertised `issuer` and `resource` are your public HTTPS URL, not an internal host. A mismatch here is what makes MCP Manager fall back to manual entry or reject tokens. ## Vercel `mcp-handler` (Next.js) If you're shipping an MCP server as Next.js route handlers on Vercel, [`mcp-handler`](https://github.com/vercel/mcp-handler) wraps the SDK with `createMcpHandler(...)`. It is a **resource server only** — it verifies tokens with `withMcpAuth(handler, verifyToken, options)` and serves RFC 9728 protected-resource metadata, but it does **not** implement an authorization server or dynamic client registration. That maps to MCP Manager's **pre-registration** or **token-in-header** modes: an external authorization server (or a static token) handles credentials, and `mcp-handler` validates them. See [`mcp-handler` authorization docs](https://github.com/vercel/mcp-handler/blob/main/docs/AUTHORIZATION.md). On Vercel/serverless, the SSE response path needs a **Redis** URL for its pub/sub backing — pure request/response Streamable HTTP works statelessly, but don't rely on in-process state across invocations. ## MCP Manager compatibility checklist Mount `StreamableHTTPServerTransport` at a stable path (`/mcp`); don't ship only `SSEServerTransport`. Use `sessionIdGenerator: undefined` for stateless, or externalize session state — don't keep it in one instance's memory. Mount `mcpAuthRouter`, implement `clientsStore.registerClient`, and back it (plus codes and tokens) with a database or Redis — not the demo in-memory `Map`. Serve `oauth-protected-resource` (and, for an authorization server, `oauth-authorization-server`) at your public URL with the right path suffix; advertise the canonical `resource`. If you enforce a redirect allowlist, include `https://app.mcpmanager.ai/api/v1/mcpm/inbound/oauth/callback`. ## TypeScript gotchas `DemoInMemoryClientsStore` is a `Map` for examples. On more than one instance it loses MCP Manager's registration between the register and authorize hops. Implement a persistent `OAuthRegisteredClientsStore`. See [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth). `mcpAuthRouter` mounts the registration endpoint **only** when `clientsStore.registerClient` is defined. If MCP Manager can't find a `registration_endpoint`, that's usually why — implement it or use pre-registration. For an endpoint at `/mcp`, the metadata lives at `/.well-known/oauth-protected-resource/mcp`. Build it with `getOAuthProtectedResourceMetadataUrl()` rather than hand-rolling, or discovery fails. ## Further reading The dynamic-client-registration failure and the persistent-store fix. The authoritative repo, including the server guide and auth source. The Next.js resource-server adapter and its authorization docs. The cross-framework requirements, decision tree, and troubleshooting catalog. # Access Control Source: https://docs.mcpmanager.ai/deployment/access-control How access control works in MCP Manager: roles grant capabilities (what you can do), teams grant gateways (which you can reach), and a user's access is the intersection of the two — why each user has exactly one role but can belong to many teams, plus the per-server identity and feature-provisioning layers that scope it further. In MCP Manager, **access control** decides two things for every person in your workspace: *what* they are allowed to do, and *which* gateways they can reach. Those two questions are answered by two independent building blocks — **roles** and **teams** — and a user's effective access is the **intersection** of the two. Two finer, per-server layers narrow it further. This section is the reference for all of them. Access control is administered under **People** at [People](https://app.mcpmanager.ai/settings/people), gated by a set of fine-grained People capabilities — **Manage roles**, **Manage teams**, **Manage user role assignments**, **Manage user team assignments**, **Remove users**, and **View all teams**. Holding any one of them reveals the **People** section; each then unlocks its own actions. If the **People** link is missing from your left-hand navigation, your role has none of them. Access is governed by capabilities, not by any fixed role name. ## Roles and teams are the two halves of access MCP Manager keeps *what you can do* and *which gateways you can reach* as two separate concepts and combines them with an **AND**: * A **role** is a named bundle of [capabilities](/deployment/rbac-and-roles/capabilities) — granular permissions such as "Basic gateway management" or "View and export logs." Every user holds **exactly one** role — which keeps the answer to "what actions is this person allowed to take?" unambiguous, with no overlapping roles to reconcile. * A **team** grants access to specific [gateways](/mcp-gateway-concepts/mcp-gateways). A user can belong to **zero, one, or many** teams, and it answers "which gateways can this person reach?" A user can act on a gateway only when **both** halves agree: their role grants the capability for the action, and one of their teams provisions the gateway. Holding a capability does not widen which gateways it applies to, and joining a team does not, by itself, grant any administrative action. A small family of "view all" capabilities — **View and use all gateways**, **View all servers**, **View all identities**, **View all teams**, and **See all alerts** — deliberately overrides team scoping for administrators who need a workspace-wide view. ## The finer, per-server layers Roles and teams set the broad boundaries; two further controls scope access **within** a gateway, one server at a time: * **Identity scheme** — whether each server is reached with the user's own identity or a shared service account. See [Identity Controls](/features/identity-controls). * **Feature provisioning** — which tools, resources, and prompts each server exposes on a gateway. See [Feature Provisioning](/features/feature-provisioning). There is no per-person or per-resource access list, so you express fine-grained access by separating servers onto different gateways and provisioning each gateway to the right team. See [Gateway Deployment Strategies](/deployment/gateway-deployment-strategies). ## The pieces of access control The capabilities a user holds — what they're allowed to do. Every user has exactly one role. The gateways a user can reach. A user can belong to zero, one, or many teams. The complete reference of every permission a role can grant, grouped as they appear in the product. ## Further reading Start here — the three built-in roles, custom roles, and how a role is assigned. Per-server identity schemes that scope what a user reaches within a gateway. Per-server tool, resource, and prompt exposure — the finest layer of access. How to package gateways so each team gets exactly the access it should. # Enterprise Strategy & Lockdown Source: https://docs.mcpmanager.ai/deployment/enterprise-strategy-and-lockdown Where MCP Manager fits in an enterprise AI control stack and the levers admins use to lock it down: funneling all MCP usage through the gateway with client-side connector allowlists, MDM/EDR, and static egress IPs; what MCP Manager governs versus what the endpoint layer controls on the device; the in-platform controls; and a recommended rollout sequence. **MCP Manager** is the control point for MCP in your organization — but it is one layer of a defense-in-depth strategy, not the whole thing. A [gateway](/mcp-gateway-concepts/mcp-gateways) governs everything that flows *through* it: identity, which tools are exposed, what data may pass, and a complete audit trail. The job of an enterprise rollout is to make sure MCP traffic actually goes *through* the gateway — and then to use the controls inside MCP Manager to lock down what happens there. This page covers both halves: where MCP Manager fits in your stack, and the levers admins have. For the hands-on version, the [Lockdown Checklist](/deployment/lockdown-checklist) turns all of this into a role-by-role list of the exact settings that lock down who can connect to which MCP servers, and how. The in-platform controls below are gated by capabilities (for example **Disable and enable hosts**, **Basic gateway management**, **Manage feature provisioning settings**, **Manage integrations**). Access depends on the capability granted to your role, not on any fixed role name. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## Where MCP Manager fits: defense in depth Think of MCP governance as a stack. MCP Manager is the enforcement and visibility layer in the middle; the layers around it exist to ensure clients can only reach servers *by going through it*. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TB subgraph Device["Managed device — MDM / EDR"] direction TB Client["🤖
AI client (Claude, ChatGPT, Cursor)
admin connector allowlist"] end Client -->|"only the MCP Manager gateway URL is allowed"| GW["🛡️
MCP Manager gateway
identity · tool provisioning · rules · logging"] GW -->|"brokered identity, over TLS"| Servers["🖥️
Your MCP servers"] classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef client fill:#80cbc4,color:#062b4c,stroke:#00796b,stroke-width:1.5px; classDef server fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; class GW gateway; class Client client; class Servers server; style Device fill:transparent,stroke:#9ca1ab,stroke-dasharray:4 3,color:#6a6b76; ``` * **Endpoint layer (MDM / EDR).** Your device-management and endpoint-protection tooling controls which apps run on managed machines and can allowlist MCP Manager's network traffic. * **AI client layer (connector allowlists).** Enterprise and team tiers of the major AI clients let an admin restrict which connectors users may add. * **MCP Manager (the gateway).** The single governed path where identity, tool exposure, content rules, and logging are enforced. * **Your servers.** Reachable only through the gateway when the layers above are configured to funnel traffic to it. ## Funneling all MCP usage through the gateway The most common enterprise question is: *what stops a user from just adding their own MCP server directly in their AI client and bypassing the gateway entirely?* MCP Manager governs what passes through it, so the answer is to ensure clients can only reach the gateway. This is done at the layers around MCP Manager: * **Client-side connector allowlists.** The team and enterprise tiers of clients like Claude, ChatGPT, and Cursor let administrators control which connectors users can add. Allow **only** your MCP Manager gateway URL, and a user can no longer wire up an arbitrary MCP server in that client. (This is how Usercentrics runs internally — MCP Manager is the only permitted Claude connector.) * **Endpoint management (MDM / EDR).** Manage which AI clients and configurations are present on company devices, and use EDR allowlisting so only MCP Manager's egress traffic is permitted. * **Network controls and static egress IPs.** MCP Manager can present **static egress IP addresses**, so you can allowlist its traffic at the firewall and have sensitive upstreams accept connections only from the gateway. [Managed servers](/mcp-gateway-concepts/mcp-servers/managed) can be locked to that static IP directly. MCP Manager cannot, on its own, stop someone from running an MCP client on an **unmanaged personal device** outside your control — no gateway product can. That is precisely why the surrounding endpoint and client-allowlist layers matter: together they ensure that on the devices and clients you *do* manage, the gateway is the only path. Treat MCP Manager as the enforcement point, and the client/endpoint controls as what funnels traffic to it. ## What MCP Manager governs on the device — and what it doesn't MCP Manager governs the **MCP connection**: which servers and tools an app or agent can reach through the gateway, as whose identity, with what data allowed to pass, and a log of every call. It does **not** control what an AI client does locally on the machine it runs on. When the client is a desktop app — Claude Desktop, for example — local behavior such as reading files on disk, accessing the clipboard, or taking screenshots happens on the device, outside any MCP server, so it never traverses the gateway and is not something MCP Manager can see or stop. Those local behaviors belong to the **endpoint layer** — your MDM/EDR and OS-level policy, which decide what an app may do on a managed device. The two are complementary: MCP Manager governs everything that flows through MCP, and your device tooling governs what runs on the machine. (Local MCP servers are the exception that proves the rule — when a tool *is* an MCP server running on the workstation, routing it through a [workstation server](/mcp-gateway-concepts/mcp-servers/workstation) brings it back under the gateway's governance.) ## Lockdown levers inside MCP Manager Once traffic flows through the gateway, these are the controls admins own directly: | Lever | What it locks down | Where | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | **Allowed apps & agents** | Standardize on some clients and block others — e.g. allow Claude org-wide, block ChatGPT — and cut off any single agent. | [Apps & Agents](/mcp-gateway-concepts/apps-and-agents) | | **Teams & roles** | Who can reach which gateway, and what each person is allowed to *do* with it. | [Roles](/deployment/rbac-and-roles/overview) · [Teams](/deployment/teams) | | **Feature provisioning** | Which tools, resources, and prompts a gateway exposes at all — fail-closed, so only allowlisted capabilities pass. | [Feature Provisioning](/features/feature-provisioning) | | **Identity scheme per server** | Whether each server uses each user's own identity or a shared service account. | [Identity Controls](/features/identity-controls) | | **Gateway rules** | What data may flow — PII redaction, secret blocking, prompt-injection defense. | [Gateway Rules](/features/gateway-rules/overview) | | **SSO & SCIM** | Centralized sign-in and automatic provisioning/deprovisioning from your IdP. | [SSO](/enterprise/sso) · [SCIM](/enterprise/scim) | | **Break-glass toggles** | Instantly disable an identity, connection, host, server, or gateway during an incident or offboarding. | [Runtime Protections](/security/runtime-protections#break-glass-instant-kill-switches) | | **Log export to SIEM** | Stream every call to your own observability/SIEM for retention and monitoring. | [Export to SIEM](/enterprise/export-to-siem) | Together these answer the governance questions an enterprise security review asks: *who* can use *which* tools, as *whose* identity, with *what* data allowed to pass, *provably* logged, and *instantly* revocable. ## A recommended rollout sequence Stand up one [gateway](/mcp-gateway-concepts/mcp-gateways) with a small set of servers and a pilot team. Start with the picker URL and a simple topology — see [Gateway Deployment Strategies](/deployment/gateway-deployment-strategies). In your AI clients' admin settings, restrict connectors to the MCP Manager gateway URL. Reinforce with MDM/EDR on managed devices so the gateway is the only reachable path. Connect [SSO](/enterprise/sso) so everyone signs in through your IdP, and turn on [SCIM](/enterprise/scim) so team membership — and deprovisioning — sync automatically. Use [feature provisioning](/features/feature-provisioning) to expose only the tools each gateway needs, and add [gateway rules](/features/gateway-rules/overview) for PII and injection on the gateways that handle sensitive data. Forward logs to your [SIEM](/enterprise/export-to-siem), confirm the audit trail meets your requirements, then expand to more teams — splitting into per-team or per-use-case gateways as needs diverge. ## Further reading The hands-on, role-by-role checklist of exact settings that lock down who can connect to which MCP servers. Choosing a gateway topology — organization-wide, per team, per server, or per use case. Allowing some clients and blocking others, and disabling a specific app or agent. Delegating sign-in to your organization's identity provider. Automatic user provisioning and deprovisioning from your IdP. How the gateway path is secured — encryption, isolation, and static egress IPs. The permissions behind every lockdown lever on this page. # Gateway Deployment Strategies Source: https://docs.mcpmanager.ai/deployment/gateway-deployment-strategies How to choose a gateway topology in MCP Manager: the building blocks (servers, gateways, teams, roles), the picker versus locked connection URLs, and four strategies — one organization-wide gateway, one per team, one per server, and one per use case — with when each fits and the trade-offs. There is no single right way to deploy [gateways](/mcp-gateway-concepts/mcp-gateways) in **MCP Manager**. The best setup depends on how your teams work, how many MCP servers you plan to connect, and how much control you want at each layer. This guide walks through the common strategies, when each makes sense, and the trade-offs to watch for. Whichever you choose, the security guarantees are the same — per-user identity (or a shared service account when you choose it), logs that always tie back to a real person, and the ability to enable or disable anything in real time. **The strategy is about packaging and experience, not the level of security you get.** Creating gateways and assigning servers is part of the **Basic gateway management** capability; making a gateway available to a team uses **Manage team-gateway provisioning**. If you can't create or provision gateways, your role lacks the relevant capability — access depends on the capability, not on any fixed role name. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## First, the building blocks A quick refresher so the strategies make sense: * [**MCP servers**](/mcp-gateway-concepts/mcp-servers/overview) are your back of house. Adding a server to MCP Manager does **not** expose it to anyone — it just registers it so you can decide what to do with it. * [**Gateways**](/mcp-gateway-concepts/mcp-gateways) are your front of house. A gateway packages one or more servers and is the only way users actually connect. This is where you provision which tools are available (see [Feature Governance](/security/feature-governance)) and which [rules](/features/gateway-rules/overview) apply. * [**Teams**](/deployment/teams) are how you grant access. Assigning a gateway to a team makes it available to everyone on that team, and teams can sync from your identity provider through [SCIM](/enterprise/scim). * [**Roles**](/deployment/rbac-and-roles/overview) are what a person can *do* with the gateways they have. Roles are independent of teams, so you can give the same gateway to two teams while controlling capabilities separately. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart LR S1["🖥️
Server A"] --> GW["🛡️
Gateway
(servers + provisioned tools + rules)"] S2["🖥️
Server B"] --> GW GW -->|"provisioned to"| T["👥
Team"] T -->|"membership"| U["👤
Users"] R["Role"] -.->|"capabilities"| U classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef server fill:#aed8ff,color:#062b4c,stroke:#0b4880,stroke-width:1.5px; classDef user fill:#c3c9d4,color:#12141d,stroke:#2c2c37,stroke-width:1.5px; class GW gateway; class S1,S2 server; class T,U user; ``` Most policy in MCP Manager is scoped **per gateway**: each gateway has its own [rules and rule engines](/features/gateway-rules/overview), [tool allowlists](/security/feature-governance), redaction behavior, and [identity schemes](/security/authentication-and-identity), so two gateways can govern the same server differently — which is what lets a topology scope policy per client, team, or tenant. A few settings are instead **organization-wide** regardless of topology, most notably the [log retention period](/features/viewing-logs#how-long-logs-and-exports-stay-available). ## How users connect: two URL modes Before choosing a topology, know that you control whether a user **picks** a gateway or **lands in** a specific one. The picker URL is the base path; the locked URL appends the gateway ID to it. **Picker URL (no gateway specified).** The user sees every gateway their team membership grants and chooses one. This pairs naturally with a single company-wide connector: distribute one URL to everyone and let team assignments route people to the right place. If a user's teams map to only one gateway, that is effectively the only option they see. ```text Picker URL theme={null} https://app.mcpmanager.ai/gateway/v1/mcp ``` **Locked URL (gateway specified).** The connector loads with that specific gateway already selected — no picking step. Hand this to a specific team or workflow so the experience is one click and unambiguous. You can copy a gateway's URL from the **Connect via URL** section of its **Connect this gateway** menu — open the gateway from [Gateways](https://app.mcpmanager.ai/settings/gateways) to find it. ```text Locked URL theme={null} https://app.mcpmanager.ai/gateway/v1/mcp/ ``` You can mix the two: distribute the picker URL broadly for general use, and hand specific teams a locked URL for their dedicated gateway. Locked URLs you handed out previously in the `?gateway=` query form continue to work, so there's nothing to migrate. ## Strategy 1: One gateway for the whole organization **What it is.** A single gateway holds every server you want broadly available, and you distribute one connector to the entire company. **Why pick it.** The simplest possible rollout — one link to communicate, one connector to manage, and users never think about which gateway to use. Modern AI clients trim and discover tools well at runtime, so a large catalog behind one connector is far less of a cost or performance burden than it used to be. A strong default when most of your team needs broadly the same tools, or when you're early in your rollout and want minimal overhead. **Watch out for.** Everyone behind the gateway shares the same provisioned tool set and the same gateway-level rules. Per-user identity and [roles](/deployment/rbac-and-roles/overview) still scope what each individual can actually do, so it isn't a free-for-all — but if different groups need genuinely different tool subsets or guardrails, you'll eventually want to split them out. Very large tool catalogs can also add context overhead for clients that don't trim well. ## Strategy 2: One gateway per team **What it is.** A gateway for each department or function (Sales, Legal, Engineering, IT), each containing the servers and tool subsets that group needs, with policies tuned to them. **Why pick it.** A clean mental model that mirrors your org chart. Each team gets exactly the tools relevant to them and nothing else, reducing context bloat and narrowing the security surface. You can apply different guardrails per team — for example, stricter PII redaction for a group that touches customer data. Hand each team a locked URL so they drop straight into their gateway, and let team membership (synced from your IdP) do the routing as you grow. **Watch out for.** More gateways to maintain. Cross-team work is a little awkward: someone on two teams either sees two connectors or relies on the picker URL. And if your teams need mostly the same tools anyway, the per-team split is overhead without much payoff. ## Strategy 3: One gateway per MCP server **What it is.** A one-to-one mapping — each gateway exposes a single MCP server (one for Atlassian, one for Google Workspace, one for Slack, and so on). **Why pick it.** Maximum isolation and the most granular control. Switch any single integration on or off instantly without touching the others, govern each with its own rules, and reason about each independently. Especially useful during evaluation: stand a new server up in its own gateway, put it through its paces, and decide later. Also a good fit for a small number of high-sensitivity servers you want walled off, and it makes auditing one integration at a time straightforward. **Watch out for.** The most connectors for end users to manage. If someone needs five servers, that's five connectors and five connections to establish. At scale that's heavier for everyone — so this works best as a **staging and preview** pattern, or for a handful of sensitive servers, rather than the whole-company default. ## Strategy 4: One gateway per use case or work stream **What it is.** A hybrid. You build a gateway around a workflow or project that cuts across teams, bundling only the servers and tools that workflow needs, regardless of which department people sit in. **Why pick it.** Some work doesn't follow the org chart. A cross-functional project or a specific agentic workflow may need a curated set of tools drawn from several servers. A use-case gateway delivers exactly that set to exactly the people on that work stream, keeping the toolset tight and purpose-built — which helps both security and agent performance, since the agent only ever sees the tools it actually needs. **Watch out for.** These can proliferate if every short-lived project spins one up. Reserve the pattern for durable, important workflows rather than every passing effort. ## Quick comparison | If you want | Use this | | ------------------------------------------------------------------- | ----------------------------- | | The simplest rollout, with one link to manage | One organization-wide gateway | | Tools and policies tailored to each department | One gateway per team | | Maximum isolation, plus easy evaluation of new or sensitive servers | One gateway per server | | A curated toolset for a workflow that spans teams | One gateway per use case | ## You are not locked in Most organizations don't pick one strategy and stop. A very common path is to start simple with a single gateway, then split out per-team or per-use-case gateways as needs diverge. Because access is always governed by the combination of **team, role, and per-user identity**, and because every change takes effect in real time, you can evolve your topology without disrupting people who are already connected. If you're not sure where to begin, start with **one gateway and the picker URL.** It's the lowest-effort setup, and it's easy to grow from there. ## Further reading What a gateway is and how it aggregates servers, brokers identity, and applies rules. How provisioning a gateway to a team grants access, and how access resolves across teams. How roles control what a person can do with the gateways they can reach. Provisioning which tools each gateway exposes, per server. Put a strategy into practice: create a team, build its gateway, and invite someone. # Hosting & Data Residency Source: https://docs.mcpmanager.ai/deployment/hosting-and-data-residency Where MCP Manager is hosted — Google Cloud Platform in the United States — whether a self-hosted or on-premise version exists and why the hosted model usually fits, what you keep in your own environment (your servers, an EU copy of your logs, redaction before logging, static egress IPs), and the status of EU data residency. **MCP Manager** is a hosted cloud service operated by Usercentrics. This page covers where it runs, whether you can self-host, how it reaches servers inside your network, and EU data residency. ## Where MCP Manager runs MCP Manager runs inside **Usercentrics' own cloud platform** — **Google Cloud Platform**, US region `us-east1` — under the same security and compliance program Usercentrics runs as a data-privacy company. The services that process your traffic and the database holding your configuration and logs are all there. There is no region selection today, so the data MCP Manager processes and stores resides in the United States. That operator matters for a governance product. Usercentrics is a global leader in consent management and data privacy — based in Munich, active in 100+ countries, processing billions of user consents a month across millions of websites and apps. Handling personal data and meeting regulatory obligations at scale is its core business. See [Architecture & Trust](/mcp-gateway-concepts/architecture-and-trust) for how the path is secured. ## Self-hosted and on-premise Wanting a self-hosted control plane is a common, reasonable security-review ask. **MCP Manager ships only as the Usercentrics-operated cloud service** described above. There is no on-premise, customer-deployed, or air-gapped build today, and none is planned. For most teams, the hosted model fits the real goal better than self-hosting would: * **The traffic is mostly cloud-to-cloud already.** The servers a gateway fronts are usually SaaS (Atlassian, GitHub, HubSpot) and the AI clients (Claude, ChatGPT, Cursor) are cloud too, so that traffic already leaves your network — an on-premise gateway wouldn't contain it. The gateway's value is identity, governance, and audit on top of that flow, delivered without you running more infrastructure. * **No infrastructure to run.** Self-hosting means operating and securing the whole stack yourself, from Kubernetes to scaling, patching, and uptime. On the hosted model that work falls to a high-trust provider whose global team monitors the systems around the clock, so you can meet your compliance obligations without taking on the cost and complexity of running it yourself. You can review Usercentrics' security posture and certifications at its [trust center](https://trust.usercentrics.com/). * **You still control what stays in your environment.** See [what you keep](#what-you-keep-in-your-own-environment) for the levers that decide what ever leaves your network. If a self-hosted control plane is a hard requirement for your organization, tell your MCP Manager contact, since customer demand shapes what we build next. ### Reaching a server inside your network A common version of the on-premise question is *"how does MCP Manager connect to a server that's only reachable inside my network?"* It needs nothing self-hosted: * **Workstation server (best fit).** A small agent inside your network opens an **outbound, encrypted WireGuard tunnel** to the gateway. The server stays behind your firewall, opens **no inbound ports**, and is never exposed to the internet — the gateway reaches it only through that tunnel. See [Workstation servers](/mcp-gateway-concepts/mcp-servers/workstation). * **Managed or self-hosted remote server.** Whether the server already runs at a URL in your network (a [remote server](/mcp-gateway-concepts/mcp-servers/remote)) or you launch it there as a [managed server](/mcp-gateway-concepts/mcp-servers/managed), the gateway reaches it over HTTPS from a single **static IP address**. Allowlist that one IP on your firewall so the server accepts connections only from MCP Manager. Find your static IPs at [enterprise/ip-ranges](https://app.mcpmanager.ai/enterprise/ip-ranges). Either way an internal-only server is fully governed without being published to the internet. ## What you keep in your own environment You decide what leaves your environment and what the hosted service ever holds: * **Your servers keep their data.** [Workstation](/mcp-gateway-concepts/mcp-servers/workstation) and [managed](/mcp-gateway-concepts/mcp-servers/managed) servers — and the systems behind them — stay in your infrastructure; MCP Manager brokers access from in front. * **You control what's logged.** [Gateway rules](/features/gateway-rules/overview) redact, mask, or block sensitive values **before** anything is logged, and you can forward logs to your own [collector](/enterprise/export-to-siem/self-hosted-collector) in any region while keeping in-platform retention short. * **You can lock the path.** Allowlist the gateway's [static egress IPs](/mcp-gateway-concepts/architecture-and-trust) so a sensitive upstream accepts connections only from MCP Manager. ## EU data residency For European organizations this is often a real compliance concern. **A dedicated EU-hosted deployment is not available today.** There is no region selection, and the gateway processes traffic and stores configuration and logs in the United States, as described above. What usually shapes a GDPR position is *which* personal data is processed and where it comes to rest, rather than the region of any single component in the path. Two levers help here, and both are in your hands: * **Keep personal data out of the hosted store.** [Gateway rules](/features/gateway-rules/overview) redact or mask PII before it is logged — often little or none reaches the US store — and a [self-hosted collector](/enterprise/export-to-siem/self-hosted-collector) in an EU region holds the audit copy you keep. * **Keep source data in the EU.** The systems behind [your own servers](#what-you-keep-in-your-own-environment) never leave your EU environment. Given Usercentrics' European roots, EU residency is under active consideration, and we are actively working through how to deliver it. If it matters to you, tell your MCP Manager contact about your needs — and it helps us most if you can answer the two questions that shape the design: * Does your requirement cover **data at rest only**, or **data in transit** as well, including the servers that process it? * Is it acceptable for **US-based operational staff to access the systems for maintenance**, or must the **entire solution stay EU-only**? Customer answers to these questions directly guide what we build. ## Further reading Reach a server inside your network through an outbound encrypted tunnel. Run your own MCP servers and broker access through a gateway. Keep a copy of your logs in an OpenTelemetry collector you run. How the gateway path is secured — encryption, isolation, egress IPs. # MCP Enterprise Lockdown Checklist Source: https://docs.mcpmanager.ai/deployment/lockdown-checklist How to ensure your organization's MCP traffic routes through governed MCP gateways you set up in MCP Manager. How to ensure your organization's MCP traffic routes through governed MCP gateways you set up in **MCP Manager**. This version covers Claude Enterprise and Cursor. For the strategy behind these steps, see [Enterprise Strategy & Lockdown](/deployment/enterprise-strategy-and-lockdown). ## Step 0: Line up access You need three kinds of admin. Hand each section to the right person. * An **admin of each AI client** you're connecting MCP Manager gateways to (e.g., a Claude org Owner, a Cursor team admin) * An **IT/endpoint admin** who controls MDM/GPO and the firewall * An **MCP Manager admin** ## Claude org admin **Owner:** Claude org Owner
**Works in:** claude.ai admin settings
1. Claude admin must go to **Organization settings → Connectors** and remove all direct connectors, so MCP Manager is the only connection.

Available on Claude Team and Enterprise plans. Members can only use connectors an Owner has enabled.

2. In the same settings, click **Add custom connector** and enter the MCP Manager gateway URL(s).

Each member then connects to the gateway from their own Connectors settings. Get the URL from the gateway's **Connect this gateway** menu in MCP Manager.

3. Turn off public desktop extensions for the org.

Extensions bundle local MCP servers. This setting follows the account to any device, even personal ones.

4. If devs use Claude Code: push `allowedMcpServers` (gateway URL only) with `allowManagedMcpServersOnly: true` via server-managed settings.

Match by URL, not server name. Names are just labels users pick. These settings follow the account, so they work on unmanaged devices too.

## Cursor team admin **Owner:** Cursor team admin
**Works in:** the Cursor dashboard
1. Cursor admin must go to **MCP Configuration** in the dashboard and create an MCP allowlist.

Requires Cursor Enterprise. With no allowlist, people can add any MCP server.

2. Add the MCP Manager gateway URL(s) as the only allowlist entries.

Once the allowlist exists, Cursor blocks every server that isn't on it. Nothing else to disable.

3. Require SSO for the team.

Team rules only cover people signed into the team account. SSO keeps everyone on it.

## IT / endpoint admin **Owner:** IT / endpoint admin
**Works in:** MDM / GPO / firewall — not in Claude or Cursor's admin areas
These are settings IT pushes to company devices with your device-management tool (Jamf, Intune, Group Policy), plus firewall rules. 1. Claude Desktop: set `isLocalDevMcpEnabled: false` and `isDesktopExtensionEnabled: false`.

macOS: configuration profile (`com.anthropic.claudefordesktop`). Windows: Group Policy or Intune registry.

2. Claude Desktop: set `forceLoginOrgUUID` so work machines can't sign into personal Claude accounts. 3. Cursor: deploy `~/.cursor/permissions.json` so the allowlist holds even outside the team account. 4. Claude Code, stricter option: deploy `managed-mcp.json` with a fixed server set. An empty set turns MCP off entirely.

**Careful:** this file also blocks claude.ai connectors, including your gateway, unless you set `allowAllClaudeAiMcps` or put the gateway URL in the file itself.

5. Block unapproved AI clients from installing or running. 6. Add an EDR alert for AI clients spawning long-running `npx`, `uvx`, `node`, or `python` processes.

That's what a rogue local MCP server looks like. This catches what app control misses.

7. Allow these MCP Manager domains through your firewall and proxy: all TCP 443, with TLS-inspection exemptions.

Allowlist by hostname, not IP. The underlying addresses change. Corporate proxies (Zscaler, Netskope, Cisco Umbrella) commonly break the workstation tunnel without the TLS exemption.

| Domain | What it is | | ------------------------- | ------------------------------------------------------------------------ | | `app.mcpmanager.ai` | Main server: the app, MCP gateway, admin MCP server, custom rule engines | | `gateway.mcpmanager.ai` | Supporting back-office server | | `headscale.mcpmanager.ai` | Workstation tunnel coordination. Without it the tunnel never starts | | `derp-a.mcpmanager.ai` | Relay server. Carries workstation tunnel traffic | | `derp-b.mcpmanager.ai` | Relay server, second region, for failover and latency | 8. Lock sensitive upstream MCP servers to MCP Manager's static egress IPs.

Those servers then only accept traffic that came through the gateway. Bypassing it stops working instead of just being against policy. Your static IPs are listed at [enterprise/ip-ranges](https://app.mcpmanager.ai/enterprise/ip-ranges).

## MCP Manager admin **Owner:** MCP Manager admin
**Works in:** MCP Manager
1. Connect [SSO](/enterprise/sso). 2. Turn on [SCIM](/enterprise/scim) so access is granted and revoked automatically as people join and leave. 3. Set up [Teams & Roles](/deployment/teams) so each gateway only reaches the people who need it. 4. Provision tools allowlist-by-default: expose only the tools each team needs. See [Feature Provisioning](/features/feature-provisioning). 5. Review connected [apps and agents](/mcp-gateway-concepts/apps-and-agents) regularly and disable any you don't recognize.

Apps appear as users connect, so this is ongoing upkeep, not one-time setup.

6. Find the kill switches now: MCP Manager can instantly disable a user identity, a connection, or a server. Know where those toggles are before an incident, not during one. ## Rollout **Owner:** Whoever owns the project 1. Inventory the MCP servers people already use, before blocking anything.

That inventory is your migration list for the gateway.

2. Pilot with one gateway, a small server set, and one team before going org-wide. See [Safe Rollout Sequence](/deployment/safe-rollout-sequence). 3. Tell people what's changing before enforcement lands.

Blocked servers just vanish from their client with no explanation. Say what's blocked and how to connect to the gateway instead.

# Product Support Source: https://docs.mcpmanager.ai/deployment/product-support How to get help with MCP Manager: the in-product chat and ticket system available on every plan via the Get help tab, and the dedicated Slack channel, relationship, and executive access available on Enterprise and higher-tier plans. Every MCP Manager plan comes with responsive, human support — what changes by plan is the channel and how close you sit to the team building the product. ## In-product chat and tickets — available on every plan Click **Get help** in the bottom-left corner of MCP Manager to reach the team directly, either through live chat or by submitting a ticket. Use whichever fits the moment — chat for a quick question, a ticket when you want a record to reference later. Response times are generally **same-day**, and typically land within **minutes to a few hours** during NYC business hours. For customers in Europe, support is generally available as early as **7am NYC time**, and for customers in APAC, support is often available as late as **11pm ET**, extending meaningfully into the region's business day. As our customer base continues to grow globally, we're expanding support hours to cover more time zones — so coverage outside NYC business hours will keep improving as we scale. ## Enterprise and higher: a direct line to the team Organizations on **Enterprise plans and above** get a step up in immediacy and continuity, on top of everything above: * **A dedicated Slack channel**, shared between your team and ours, for realtime access to MCP Manager's highest tier of support. This channel is monitored by our **VP of AI / Chief AI Officer (CAIO)** — the same executive accountable for the product's direction has direct visibility into how it's running for you. * **A dedicated relationship** with a named member of the MCP Manager team, so you're not re-explaining your setup, your rollout, or your constraints to a new person every time you reach out. ## Our highest Enterprise tiers: direct executive access Organizations on our highest tiers of Enterprise plans also maintain **direct contact with our VP of AI / CAIO** — not routed through a queue — for the strategic conversations that come with running MCP Manager at the center of an AI governance program. This commonly includes the option to schedule **semi-annual business reviews**: a standing session to walk through how the platform is being used, what's changed, and what's coming next, so your rollout keeps pace with both your needs and the product's roadmap. ## Further reading How MCP Manager handles deployments, planned maintenance, and status communication. Where MCP Manager fits in an enterprise AI control stack and how to lock it down. # Capabilities Source: https://docs.mcpmanager.ai/deployment/rbac-and-roles/capabilities The complete reference of MCP Manager capabilities — every permission you can grant to a role, grouped by area (Identities, Servers, Gateways, Hosts, People, Workspace settings, Logging, Alerting, Reporting, Integrations) — and exactly what each one allows. A **capability** in MCP Manager is a single, granular permission — the smallest unit of "what a user is allowed to do." Capabilities are never assigned to users directly; instead they are bundled into [roles](/deployment/rbac-and-roles/overview), and each user holds exactly one role. This page is the complete reference of every capability, grouped exactly as they appear in the product. Granting and revoking capabilities — and editing a role at all — is itself governed by the **Manage roles** capability. You edit a role's capabilities under the **Capabilities** tab when managing a role in [People](https://app.mcpmanager.ai/settings/people). If you can't reach role management, your role doesn't have **Manage roles** — access is governed by capabilities, not by any fixed role name. ## How capabilities work Capabilities are granted **per role**. Under [People](https://app.mcpmanager.ai/settings/people), open a role and use its **Capabilities** tab to toggle each permission on or off; every user assigned that role inherits the result. The built-in **Super admin** role always holds every capability and its toggles are locked on; the **Administrator** and **Member** roles, and any custom role, have fully editable capabilities. See [Roles](/deployment/rbac-and-roles/overview) for how roles are assigned and edited. **Capabilities define *what* you can do; [teams](/deployment/teams) define *which* gateways you can do it to.** Most capabilities act only on the resources your team membership already grants you. A separate family of "view all" capabilities — **View and use all gateways**, **View all servers**, **View all identities**, **View all teams**, **See all alerts** — overrides that team scoping for its resource type. See [How team scoping interacts with capabilities](#how-team-scoping-interacts-with-capabilities). ## Identities Identity capabilities govern the credentials users connect to downstream MCP servers. Personal identity management — managing your *own* identities — is always available to every user and is not gated by a capability. | Capability | What it allows | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Identity management** | This includes updating identity availability, disabling and enabling identities, and deleting identities created by others. Personal identity management is always enabled. | | **View all identities** | Access all identities in this workspace regardless of who created them. | ### View all identities is a governance-preview capability, not an impersonation grant **View all identities** lets an administrator see every identity in the workspace, including the private identities created by other users, regardless of who created them. Critically, it does **not** let the holder *use* another person's private identity, nor view or edit its **header tokens** (its secret credentials). A private identity is never selectable for assignment by anyone other than its owner, even with this capability, and its header tokens can only be viewed or edited by its creator — an identity can only be used by others if its owner has made it **shared** (globally available) rather than personal. What the capability provides is a **read-only preview**: an administrator can see which identities exist and preview what tools and access a given user's identity would expose. This is an instrumental tool for building governance policies — understanding what different users and their identities can reach — without granting the ability to act as those users. For the concepts behind identities and shared-versus-personal availability, see [Authentication & Identity](/security/authentication-and-identity). ## Servers Server capabilities govern MCP servers and server instances — adding them, editing them, enabling or disabling them, deleting them, and creating managed and workstation server instances. | Capability | What it allows | | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Basic server management** | This includes adding remote servers, editing remote server names, and creating remote server identities via authentication. | | **Disable and enable servers** | Disable and enable servers in this workspace. | | **Delete servers** | Delete servers from this workspace. | | **Manage feature provisioning settings** | Manage the provisioning settings for features on servers in this workspace. | | **View all servers** | View all servers and server instances in this workspace regardless of access. | | **Create managed server instances** | This includes deploying new server instances, and editing server instance names. | | **Create and configure managed and workstation servers** | This includes creating new managed and workstation servers, editing their names, editing default template configurations, updating server instance permissions, and setting and updating tunnel schemes. | | **Create workstation instances** | This includes deploying new workstation instances, and editing workstation instance names. This includes deploying new workstation instances, and editing workstation instance names. | Like gateways, servers are scoped by access: **View all servers** overrides that scoping so the holder sees every server and server instance in the workspace regardless of access. **Create workstation instances** is reserved for an upcoming feature and is not yet active. ## Gateways Gateway capabilities govern creating and configuring [gateways](/mcp-gateway-concepts/mcp-gateways), provisioning them to teams, and archiving them. | Capability | What it allows | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Basic gateway management** | This includes creating gateways, editing gateway names, disabling and enabling gateways, assigning servers to gateways, changing identity scheme ("shared" or "personal"), disabling and enabling assigned servers, and revoking assigned servers from gateways. | | **View and use all gateways** | View and use all gateways on any team in this workspace. | | **Manage team-gateway provisioning** | This includes creating and revoking team gateway provisions. | | **Archive and view archived gateways** | Archive gateways to hide them from default views without deleting. This also controls the ability to unarchive them. Archived gateways are automatically disabled. | ### Basic gateway management acts only on gateways you can reach **Basic gateway management** grants the *actions* — creating gateways, renaming them, enabling and disabling them, assigning and revoking servers. It does not, by itself, widen *which* gateways those actions apply to. A user with this capability can manage only the gateways their [team membership](/deployment/teams) provisions to them. To act on gateways across the whole workspace regardless of team, a role also needs **View and use all gateways** (below). **Manage team-gateway provisioning** is the separate capability for granting and revoking a team's access to a gateway. ## Hosts Host capabilities govern the apps and agents that connect to your gateways — the API tokens and OAuth connections that link a host to a gateway, and enabling, disabling, or deleting hosts. | Capability | What it allows | | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Create and manage API tokens (including copy & download)** | This includes generating/copying/downloading API access tokens, editing token-based host names, disabling and enabling hosts, and deleting hosts. | | **Authenticate via OAuth** | Establish connections between hosts and gateways via OAuth. | | **Disable and enable connections** | Disable and enable connections between hosts and gateways. | | **Disable and enable hosts** | Disable and enable hosts in this workspace. | | **Delete hosts** | Delete hosts from this workspace. | ## People People capabilities govern user, role, and team administration and SSO/SCIM mapping. | Capability | What it allows | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Invite users** | This includes inviting users to the role and any team that the inviter has access to. | | **Manage roles** | Create and duplicate roles, edit role names and icons, edit role capabilities, and delete roles that have no assigned users. | | **Manage teams** | Create teams, edit team names, enable and disable teams, and delete teams. | | **Manage user role assignments** | Update and modify which role a user holds. | | **Manage user team assignments** | Create and revoke users' team memberships. | | **Remove users** | Deactivate users to remove their access to the workspace. | | **View all teams** | See every team in the workspace, regardless of which teams you belong to. | | **Manage SSO/SCIM mapping** | Configure how IDP groups (e.g. Okta) map to MCP Manager teams and edit workspace-level SSO settings, including the default team for SCIM-provisioned users. | ### People administration is split into fine-grained capabilities User, role, and team administration is divided into **six independent capabilities**, so you can grant exactly the administrative scope a role needs rather than all of it at once: * **Manage roles** — create and duplicate roles, edit role names, icons, and capabilities, and delete roles that have no assigned users. * **Manage teams** — create teams, edit team names, enable and disable teams, and delete teams. * **Manage user role assignments** — change which role a user holds. * **Manage user team assignments** — add users to and remove them from teams. * **Remove users** — deactivate a user to revoke their access to the workspace. * **View all teams** — see every team in the workspace, not only the teams you belong to (the team-scoping override for People; see [How team scoping interacts with capabilities](#how-team-scoping-interacts-with-capabilities)). Holding **any one** of these makes the **People** section visible; each capability then unlocks only its own actions. Grant them individually — for example, a help-desk role might get **Manage user team assignments** and **Remove users** without the ability to edit roles or their capabilities. These six capabilities replace the earlier single **Manage people** capability, which bundled all of the above together. If you previously relied on **Manage people**, grant the specific capabilities a role now needs. The built-in **Super admin** role holds all of them automatically. ### Manage SSO/SCIM mapping also gates a page **Manage SSO/SCIM mapping** controls more than buttons: the SSO/SCIM settings page itself is gated by this capability. A user whose role lacks it cannot open that page even by direct link — they are redirected away. See [SSO](/enterprise/sso) and [SCIM](/enterprise/scim). ## Workspace settings | Capability | What it allows | | ----------------------------- | ----------------------------------------------------------------------------------------------------- | | **Manage workspace settings** | Manage basic workspace settings like workspace name and date/time formats throughout the application. | | **Manage plans** | Manage billing plans and subscriptions for the workspace. | ## Logging Logging capabilities govern viewing and exporting [logs](/features/viewing-logs) and configuring the OpenTelemetry collector that forwards them. | Capability | What it allows | | ---------------------------------- | ------------------------------------------------------------------------------ | | **View and export logs** | View and export logs for hosts, gateways, and servers that you have access to. | | **Manage OpenTelemetry collector** | Configure, edit, and remove the OpenTelemetry collector used to forward logs. | **View and export logs** is scoped to the resources you can already reach: it lets you view and export logs only for the hosts, gateways, and servers your team membership grants you access to — it is not a workspace-wide "view every log" grant. **Manage OpenTelemetry collector** governs the collector used to forward logs to an external destination; see [Export to SIEM](/enterprise/export-to-siem). ## Alerting | Capability | What it allows | | ------------------ | ---------------------------------- | | **See all alerts** | View all alerts in your workspace. | **See all alerts** overrides the default team-based scoping of [alerts](/features/alerts) so the holder sees every alert in the workspace rather than only those tied to resources they can reach. ## Reporting | Capability | What it allows | | ---------------- | ---------------------------------------------------- | | **View reports** | View reports for hosts, gateways, servers, and more… | **View reports** controls the [Reporting](/features/reporting) page end to end: with it, the **Reporting** link appears in the left-hand navigation and every chart is available; without it, the link is hidden and the page is unavailable. ## Integrations | Capability | What it allows | | ----------------------- | --------------------------------------------------------------------------------------------------------------- | | **Manage integrations** | Configure, edit, and remove integrations such as rule engines, including custom providers and built-in engines. | **Manage integrations** governs the [rule engines](/features/gateway-rules/custom-rules-engines) and other integrations attached to your gateways — configuring them, editing them, and removing them, for both built-in engines and custom providers. ## How team scoping interacts with capabilities Most capabilities are bounded by [team membership](/deployment/teams): they let you act only on the gateways (and the servers, hosts, logs, and alerts behind them) that your teams provision to you. A handful of capabilities are deliberately designed to **override** that scoping for administrators who need a workspace-wide view: * **View and use all gateways** — see and use every gateway on any team, bypassing team provisioning entirely. * **View all servers** — see every server and server instance regardless of access. * **View all identities** — see every identity regardless of creator (read-only preview; it does not let you *use* another user's private identity, or view or edit its header tokens). * **View all teams** — see every team in the workspace, not only the teams you belong to. * **See all alerts** — see every alert in the workspace. If you grant one of these "view all" capabilities, remember that you are removing the team boundary for that resource type. For most users, leave them off and rely on team membership to scope access; reserve them for administrative roles. For how team access itself is granted, disabled, and resolved, see [Teams](/deployment/teams). ## Further reading How capabilities are bundled into roles and assigned to users. The team scoping that bounds most capabilities. # Roles Source: https://docs.mcpmanager.ai/deployment/rbac-and-roles/overview How roles and capabilities govern what users can do in MCP Manager: the three built-in roles, creating and duplicating custom roles, how a role is assigned at invite and through SSO, and how roles combine with teams to control access. In MCP Manager, a **role** decides *what a user can do*. A role is a named bundle of [capabilities](/deployment/rbac-and-roles/capabilities) — granular permissions such as "Basic gateway management" or "View and export logs" — and every user is assigned exactly one role. Roles answer the question "what actions is this person allowed to take?"; [teams](/deployment/teams) answer the separate question "which gateways can this person reach?". Together they form MCP Manager's access model. Creating and editing roles requires the **Manage roles** capability. If you can't open role management under [People](https://app.mcpmanager.ai/settings/people) — or the **People** link is missing from your left-hand navigation entirely — your role doesn't have that capability. Access is governed by capabilities, not by any fixed role name. See [Who can manage roles](#who-can-manage-roles). ## Roles and teams are the two halves of access MCP Manager separates access into two independent concepts, and a user's effective access is the **intersection** of the two: * A **role** grants [capabilities](/deployment/rbac-and-roles/capabilities) — the *kinds* of actions a user may perform (create gateways, delete servers, export logs, manage roles, and so on). * A **team** grants access to specific gateways — the *which*. See [Teams](/deployment/teams). For example, the **Basic gateway management** capability lets a user create gateways and edit gateway names, but only on the gateways they can actually reach through their team membership. Holding the capability does not, on its own, let a user manage a gateway that none of their teams provision — unless their role also has the **View and use all gateways** capability, which overrides team scoping. Capabilities define what you can do; teams define what you can do it to. ## How fine-grained can access get? MCP Manager has **no per-person or per-resource access list**. You cannot grant one named user access to one specific server (and nothing else) through an individual permission attached to that server. Capabilities are **workspace-wide**: a capability such as **View and export logs** applies across the whole workspace, not to a single server or gateway. Fine-grained access is instead built from four coarser controls that combine: * **Role** — the [capabilities](/deployment/rbac-and-roles/capabilities) a user holds, workspace-wide. * **Team membership** — which gateways the user can reach. See [Teams](/deployment/teams). * **Per-server identity scheme** — whether each server on a gateway is used with the user's own identity or a shared service account. See [Identity Controls](/features/identity-controls). * **Per-server feature provisioning** — which tools, resources, and prompts each server exposes on a gateway. See [Feature Provisioning](/features/feature-provisioning). To give different groups of people different access to different servers, you separate those servers onto different **gateways** and provision each gateway to the appropriate team. The gateway is the smallest unit at which you apply a distinct rule set, tool set, or identity scheme — so a different governance posture means a different gateway, not a per-server permission. See [Gateway Deployment Strategies](/deployment/gateway-deployment-strategies) and the [FAQ](/advanced/faq). ## Every user has exactly one role Each user in a workspace is assigned **exactly one role** — never more than one, and never none. To change what a user can do, you change their role assignment (or edit the capabilities of the role they hold). This is deliberately different from [team membership](/deployment/teams), where a user can belong to **zero, one, or many** teams at the same time. ## The three built-in roles Every MCP Manager workspace ships with three built-in (system) roles. They exist in every workspace, cannot be deleted, and serve as the starting points most teams build on. | Built-in role | Purpose | Capabilities | | ----------------- | -------------------------------------------- | ----------------------------------------------------------- | | **Super admin** | Unrestricted administration of the workspace | Holds **every** capability; the set cannot be reduced | | **Administrator** | Day-to-day administration | A broad, pre-selected set of capabilities that you can edit | | **Member** | The default role for everyday users | A minimal set of capabilities that you can edit | ### Super admin The **Super admin** role holds every capability MCP Manager offers, and that set **cannot be reduced** — its capabilities are locked on and the capability toggles are disabled when editing it. Super admin is **not** limited to a single person: you can assign it to as many users as you need. Because it grants unrestricted control of the workspace, assign it only to the people who genuinely need full administrative power. ### Administrator The **Administrator** role comes pre-selected with a broad set of capabilities suitable for day-to-day administration. Unlike Super admin, its capabilities are fully editable. We strongly recommend each workspace **review the Administrator role's capabilities carefully** so you know exactly what it grants in your environment before assigning it — and tailor it to your governance needs. ### Member The **Member** role is the workspace **default role**: it is the role new users receive automatically. It starts with a minimal set of capabilities and, like Administrator, is fully editable. Anyone invited to the workspace, and anyone provisioned through [SSO](/enterprise/sso), is assigned the Member role unless a different role is chosen for them. See [How a role is assigned](#how-a-role-is-assigned). The built-in roles are named starting points, but what a role actually permits is determined by the **capabilities** currently granted to it — and Administrator and Member are editable. When you need to know precisely what someone can do, look at their role's capabilities, not its name. See the full [Capabilities reference](/deployment/rbac-and-roles/capabilities). ## How a role is assigned A user receives their role in one of two ways. ### At invite time When you invite users from [People](https://app.mcpmanager.ai/settings/people) (**Add new users**), you select a single role that every user in that invitation will receive, along with any teams to add them to. Inviting users requires the **Invite users** capability, and you can only invite people to the role and teams you yourself have access to. ### Automatically through SSO In a workspace that uses [SSO](/enterprise/sso), anyone who signs in through the identity provider associated with your domain and gains access to the workspace is automatically assigned the workspace **default role** — the **Member** role. SSO and SCIM map your identity-provider groups to MCP Manager **teams, not to roles**. They determine which teams a provisioned user joins, but every provisioned user still receives the default role; there is no mapping from an IdP group or attribute to a role today. Roles are assigned and changed manually in MCP Manager. (If you would like roles to be driven from your IdP, it is not available today, but we are open to it — talk to your MCP Manager contact.) Mapping groups to teams, and choosing the default team for provisioned users, is controlled by the **Manage SSO/SCIM mapping** capability. See [SSO](/enterprise/sso) and [SCIM](/enterprise/scim#scim-syncs-teams-not-roles). ## Editing a role With the **Manage roles** capability you can edit roles from [People](https://app.mcpmanager.ai/settings/people): * **Rename a role** and **change its icon** — including for the built-in roles — so it's easy to recognize in your workspace. * **Manage its capabilities** — grant or revoke individual capabilities — for any role **except Super admin**, whose capabilities are locked on. System (built-in) roles **cannot be deleted**. A custom role can be deleted, but only once it has **no users assigned to it** — reassign its members to another role first. ## Custom roles Beyond the three built-in roles, you can create your own roles to match how your organization governs access. * **Create a custom role** and grant it exactly the capabilities you want. * **Duplicate an existing role** — including a built-in one — to start from its capability set and then add or remove capabilities. Duplicating is the fastest way to make a small variation on a role that already works. * **The number of custom roles you can create depends on your plan.** Creating teams is unlimited, but custom roles are a plan-gated resource — if you need more, upgrading your plan raises the limit. ## Who can manage roles Role administration is split across two capabilities. **Manage roles** governs the roles themselves — creating, duplicating, renaming, re-iconing, editing capabilities, and deleting unused roles. **Manage user role assignments** is the separate capability for changing which role a given user holds. Team administration is governed by its own People capabilities (**Manage teams**, **Manage user team assignments**, **View all teams**) — see [Teams](/deployment/teams) and the full [Capabilities reference](/deployment/rbac-and-roles/capabilities#people). Because access is governed by capabilities rather than by role name, whether a given person can manage roles depends on the capabilities granted to their role — which is fully configurable, including on any custom role you create. If the **People** section or its role controls are missing for someone, their role does not have **Manage roles** (nor any other People capability). For the complete list of what every capability unlocks, see the [Capabilities reference](/deployment/rbac-and-roles/capabilities). ## Further reading The other half of access — which gateways a user can reach. The complete reference of every capability a role can grant. Per-server identity schemes that further scope what a user reaches. Per-server tool exposure, the finest layer of access control. # Safe Rollout Sequence Source: https://docs.mcpmanager.ai/deployment/safe-rollout-sequence The recommended order for configuring a gateway in MCP Manager before any user connects — so governance is in place before data can move. Security reviewers ask a reasonable question before approving a gateway deployment: at what point does an AI client actually get access to our data, and is there a window where it has more access than we intended? The answer is yes — if you skip ahead — and the sequence below is what closes that window. Configure governance first, then open access. ## What each step exposes Not every setup action opens a data path. This table shows exactly what becomes reachable at each point. | Setup action | What becomes reachable | | ------------------------------------------- | ----------------------------------------------------------- | | Add an MCP server | Nothing — registration only; no connection is made | | Add an identity | Credentials are stored encrypted; no data moves | | Create a gateway | Nothing — until a server is assigned and a client connects | | Assign a server to the gateway | Nothing — until rules are applied and a client connects | | Apply gateway rules | Governance is now in the path of any future connection | | Provision the gateway to a team | Team members *may* now connect — but only after authorizing | | A user connects their client and authorizes | First point at which data can move | ## The recommended order Configure rules **before** you provision the gateway to anyone, including yourself. 1. Add your MCP server(s). 2. Add identities. 3. Create the gateway and assign servers to it. 4. Apply rules to the gateway. 5. Provision the gateway to a small pilot team (see below). 6. Test rule behavior against real tool calls. 7. Widen access when you're satisfied. ## Two clarifications that come up every time **Creating an identity does not move data.** Authorizing a server stores a credential in MCP Manager — nothing is read from the source system until a tool call is made through a gateway. The authorization step is credential storage, not data access. **A gateway appearing in an AI client's connector list does not mean a user can use it.** If a user has not been provisioned — they're not on a team the gateway is assigned to — they'll see a message telling them they don't have access when they try to connect. Appearing in the list and having access are not the same thing. See [Connection experience](/features/connection-experience) for what that flow looks like. ## Pilot before you publish Before sharing the gateway URL broadly: 1. Create a team with two or three admins. See [Teams](/deployment/teams) for how provisioning works. 2. Provision the gateway only to that team. 3. Connect and send real tool calls — ones that should pass and ones that should be blocked. 4. Confirm rule verdicts appear in [Logs](/features/viewing-logs) with the expected outcome and comment. 5. Adjust rules if anything behaves unexpectedly. 6. Add the wider team once the behavior is confirmed. The [Build a team gateway](/tutorials/team-gateway) tutorial walks through this end to end. ## Pre-flight checklist Before sharing the gateway URL with anyone: * [ ] Rules are attached to this gateway — not just created in the rules engine * [ ] Tool allowlist has been reviewed; write-capable tools are intentional * [ ] Identity scheme is chosen per server (per-user or shared) * [ ] At least one real tool call has been made through the gateway and logged * [ ] Logs show rule activity (a verdict was applied, not bypassed) ## For security and procurement reviewers If you need to share a compliance and security reference with InfoSec, Legal, or procurement, the [Security and compliance](/enterprise/security-and-compliance) page is a public resource that requires no login and answers the questions those teams typically ask. ## Further reading Topology decisions that precede sequencing — one gateway versus per-team, per-server, or per-use-case. How rules govern what passes through a gateway and how to configure them. How provisioning a gateway to a team grants access, and how access resolves across teams. What users see when they connect — including the message shown to unprovisioned users. # Teams Source: https://docs.mcpmanager.ai/deployment/teams How teams in MCP Manager grant users access to gateways: provisioning a gateway to a team, team membership, disabling and deleting teams, how access resolves across multiple teams, and how teams combine with roles. In MCP Manager, a **team** is how access to gateways is distributed to users. Adding a user to a team grants them the gateways that team is provisioned, and a [gateway](/mcp-gateway-concepts/mcp-gateways) reaches its users by being provisioned to one or more teams. Teams answer the question "which gateways can this person reach?"; [roles](/deployment/rbac-and-roles/overview) answer the separate question "what is this person allowed to do?". Together they form MCP Manager's access model. Creating and managing teams requires the **Manage teams** capability; seeing every team in the workspace requires **View all teams**; and changing users' team memberships requires **Manage user team assignments**. If you can't create or edit teams under [People](https://app.mcpmanager.ai/settings/people), your role doesn't have the relevant capability. Access is governed by capabilities, not by any fixed role name. See [Who can manage teams](#who-can-manage-teams). ## Teams and roles are the two halves of access A user's effective access is the **intersection** of their role and their teams: * A **team** grants access to specific gateways — the *which*. * A **role** grants [capabilities](/deployment/rbac-and-roles/capabilities) — the *what*, the kinds of action a user may take. A user can only act on a gateway that one of their teams provisions (unless their role holds the **View and use all gateways** capability, which overrides team scoping — see [below](#the-view-and-use-all-gateways-override)). And being on a team that provisions a gateway does not, by itself, grant administrative actions on it — those come from the user's role capabilities. See [Roles](/deployment/rbac-and-roles/overview). ## Team membership: zero, one, or many A user can belong to **zero, one, or many** teams at the same time — there is no minimum. A user with no team memberships simply has no gateway access through teams (and reaches gateways only if their role holds **View and use all gateways**). This is deliberately different from [roles](/deployment/rbac-and-roles/overview), where every user holds **exactly one** role. ## Provisioning a gateway to a team A gateway becomes available to users by being provisioned to teams. You can do this at two points. On the **Add** flow under [Gateways](https://app.mcpmanager.ai/settings/gateways), when you give the new gateway a name you also select the teams to distribute it to. At least one team must be selected to create the gateway. Open the gateway and go to its **Team access** tab. From there you can **provision to a team** — checking the box next to each team that should receive access — or **remove** a team's access. The tab lists every team that currently has access, along with when it was provisioned. Managing which teams a gateway is provisioned to is governed by the **Manage team-gateway provisioning** capability. A useful pattern: create a dedicated team for administrators who want to **preview** MCP servers and gateways. Provision a new gateway only to that team while you evaluate it, then provision it to your broader teams once you're confident it's ready to roll out. ## Access resolves as the union of your teams A user's gateway access is the **union** across all the teams they belong to: if **any** of their teams provisions a gateway, they can reach it. The same gateway can be provisioned to several teams at once with no conflict — a user who reaches a gateway through more than one team simply has access, and losing it from one team does not remove it as long as another team still grants it. ## Disabling a team A team can be **disabled** rather than deleted. Disabling a team renders the gateway access it provides **inactive**: the access is enforced at the MCP proxy at connection time, so a user who reaches a gateway only through a disabled team can no longer communicate with that gateway through MCP Manager. When such a user next connects, MCP Manager surfaces a message telling them the team has been disabled. Because [access is the union of a user's teams](#access-resolves-as-the-union-of-your-teams), disabling a team only cuts off the access that team was the sole provider of. If a user reaches the same gateway through another team that is still active, they keep that access — only the access that depended on the disabled team goes inactive. Re-enabling the team restores the access it provided. Disabling and enabling teams is governed by the **Manage teams** capability. ## Deleting a team Any team can be deleted, with one guardrail: a workspace must always have **at least one team**, so you cannot delete the last remaining team. Deleting a team is permanent and removes the gateway access it provided; reassign or re-provision affected gateways to other teams first if users still need them. Deleting teams is governed by the **Manage teams** capability. ## The View and use all gateways override The **View and use all gateways** capability overrides team-based scoping entirely. A user whose role holds it can see and use **every** gateway in the workspace regardless of team membership — both in the dashboard listing and at the MCP proxy when connecting. This is the capability that lets administrators work across all gateways without being added to every team. For everyone else, team membership remains the boundary on which gateways they can reach. See the [Capabilities reference](/deployment/rbac-and-roles/capabilities#how-team-scoping-interacts-with-capabilities). ## Creating teams You can create **as many teams as you want** — team creation is never limited by your plan. (Custom [roles](/deployment/rbac-and-roles/overview), by contrast, are plan-gated.) Create teams to model how gateway access should be grouped in your organization — by department, by project, by environment, or however your governance requires. ## Who can manage teams Team administration is split across three People capabilities, so you can grant just the slice a role needs: * **Manage teams** — create teams, edit team names, enable and disable teams, and delete teams. * **Manage user team assignments** — add users to and remove them from teams. * **View all teams** — see every team in the workspace, not only the teams the holder belongs to. [Role](/deployment/rbac-and-roles/overview) administration is governed by its own separate capabilities (**Manage roles** and **Manage user role assignments**). Because access is governed by capabilities rather than by role name, whether a given person can manage teams depends on the capabilities granted to their role — which is fully configurable, including on any custom role you create. For the full list of what every capability unlocks, see the [Capabilities reference](/deployment/rbac-and-roles/capabilities#people). ## Further reading The complete reference of every capability a role can grant. The other half of access — what a user is allowed to do. How to package gateways so each team gets the right access. A hands-on lesson: create a team, build its gateway, and invite a teammate. # Uptime & Maintenance Plan Source: https://docs.mcpmanager.ai/deployment/uptime-and-maintenance-plan How MCP Manager handles deployments and planned maintenance: no regularly scheduled downtime, a minimum 7-day notice window when maintenance is required, and how to subscribe to status.mcpmanager.ai for incident and maintenance announcements by email, Slack, MS Teams, or RSS. MCP Manager sits in the path of every governed MCP call your organization makes, so its own availability is part of what you're evaluating. This page covers how we deploy, how we handle the rare planned maintenance window, and how to stay informed. ## No regularly scheduled maintenance windows **MCP Manager does not have any regularly scheduled maintenance windows or downtime.** Every deployment is planned so that customers experience no service interruption. ## When a planned maintenance window is required On the rare occasion a planned maintenance window is genuinely required, MCP Manager is committed to: * **Giving as much notice as possible** — a minimum of **7 days**, and often more. * **Scheduling for minimal impact** — choosing the window least likely to affect our global customer base, rather than defaulting to any single region's off-hours. ## How we communicate status and maintenance We're proud of MCP Manager's uptime, and we publish it transparently: current status, historical uptime, and every incident are all visible on our public status page, [**status.mcpmanager.ai**](https://status.mcpmanager.ai). Every incident and planned maintenance window is announced there too. You can subscribe to be notified by: * Email * Slack * Microsoft Teams * RSS Subscribe your team's on-call or platform channel once, and everyone who needs to know about an MCP Manager incident or maintenance window is notified automatically — no need to check the page manually. MCP Manager also reflects current status **inside the product**: if a service is experiencing a partial or major outage, or a maintenance window is active or imminent, an in-app banner surfaces it directly, with a link back to the full status page for details. ## Further reading The support channels available on every plan, and what Enterprise and higher-tier plans add. Where MCP Manager runs and what that means for availability and compliance. # Export to SIEM Source: https://docs.mcpmanager.ai/enterprise/export-to-siem How MCP Manager forwards structured MCP request logs and traces to any OpenTelemetry (OTLP/HTTP) collector or SIEM — what gets sent, how to control how much of each record is exported, how to set the logs and traces collector URLs and request headers, how trace context propagates downstream, how to verify delivery, and how to troubleshoot export failures. MCP Manager records every request and response that flows through your MCP gateways, and can forward that telemetry — **logs** and request **traces** — to your own observability or **SIEM (Security Information and Event Management)** platform over **OpenTelemetry (OTEL)**. Once forwarding is configured, MCP Manager streams each log record, and a span for each proxied request, to the OTLP/HTTP endpoints you supply — so you can correlate MCP tool usage with the rest of your operational data, build dashboards, see a request waterfall, and keep a single audit trail across your stack. A **SIEM (Security Information and Event Management)** system — such as Splunk, Microsoft Sentinel, or Elastic Security — centralizes security logs for monitoring, alerting, and compliance. The term is sometimes misheard or mistranscribed as **"SIM"**; if you came here looking for a "SIM" integration, this is the page you want. MCP Manager sends to any backend that speaks **OTLP/HTTP** with JSON-encoded records: Grafana Cloud, New Relic, Honeycomb, Datadog, your own OpenTelemetry Collector, or anywhere else with an OTLP intake. Each MCP Manager organization forwards to **one collector**, and you configure a **logs endpoint, a traces endpoint, or both** — at least one is required. The two signals use separate OTLP paths (typically `/v1/logs` and `/v1/traces`), which is why each has its own URL field. Export to SIEM is an **Enterprise** capability, and configuring it is gated by the **Manage OpenTelemetry collector** capability. If you do not see a **Logging → Integrations** tab at [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations), or you see only a promotional panel instead of the configuration form, then either your plan does not include the OpenTelemetry integration or your role lacks the capability. See [Who can set up log export](#who-can-set-up-log-export). ## What MCP Manager sends to your collector For every MCP request that flows through an MCP Manager gateway, MCP Manager emits structured OTLP log records describing that request. By default each record carries the same data you see on the [Viewing Logs](/features/viewing-logs) page; you can narrow this to metadata, or turn export off, with the **Export content** setting (see [Control what MCP Manager exports](#control-what-mcp-manager-exports)). A full record includes: * **Timing** — durations for the client-facing leg and each upstream MCP server leg. * **Who** — the acting user's email and name, plus the organization and team. * **What** — the JSON-RPC method, the MCP feature type and feature name, the tool or resource involved, and the inbound server name and URL. * **How much** — estimated token counts, HTTP response codes, and durations. * **Correlation** — a `correlation_id` shared across the (typically four) records that make up one message, so you can reconstruct a single interaction end-to-end. See [Log types and the correlation model](/features/viewing-logs#log-types-and-the-correlation-model). * **Trace context** — every record carries a `trace_id` and a `span_id`, and (on the legs that have one) a `traceparent`, so each log can pivot to its distributed trace. These are stamped onto every record **whether or not** you configure a traces endpoint. See [Traces and trace-correlated logs](#traces-and-trace-correlated-logs). Records are sent as **OTLP/HTTP, JSON-encoded** log records to the endpoint you configure. Every record also carries two OpenTelemetry **resource attributes** that identify the source: `service.name` (in production, `mcp-manager`) and `service.version` (`1.0.0`). Use these to filter MCP Manager's logs in your backend — they are also shown to you in the configuration panel after you save (see [Verify that logs are flowing](#verify-that-logs-are-flowing)). ### Traces and trace-correlated logs Trace correlation is **always on** — it does not depend on a traces endpoint. For every proxied MCP request, the gateway mints one OpenTelemetry **trace** and the spans within it, and stamps the trace identifiers onto **every log record** that request produces: * **`trace_id`** — the W3C trace ID (32 hex characters) for the whole request, identical on every log leg of that request. It is the key you filter on to pull back all the logs of one interaction. * **`span_id`** — the span that log leg represents (16 hex characters): the gateway span on the client- and server-facing legs, and a distinct child span for each rule-engine evaluation. * **`traceparent`** — the W3C `traceparent` in play on that leg (see the determinism rule below). Because the gateway mints in-process, the log legs of a request group under one trace ID in your backend **even when no traces endpoint is set**. Minting and stamping are not configurable and cannot be disabled. **Configuring a traces endpoint adds export, nothing more.** When a traces URL is set, MCP Manager also sends the spans themselves over OTLP/HTTP, so you get a request **waterfall** in your tracing backend — the gateway span as the root, with each rule-engine evaluation and the upstream MCP server hop as children. Each span records the JSON-RPC method, the organization, the outbound gateway, the tool name where applicable, and the final HTTP status (a 4xx/5xx response marks the span as an error). Without a traces URL you still get the IDs on every log; you just don't get the exported span waterfall. The gateway **continues an incoming trace** when the client sends W3C `traceparent` context, and otherwise starts a fresh root. A client can supply it two ways: the HTTP `traceparent` header, or the JSON-RPC body at `params._meta.traceparent`. **When both are present, the body value (`params._meta.traceparent`) wins.** MCP Manager then **propagates the context downstream** to the upstream MCP server on both the HTTP `traceparent` header and `params._meta.traceparent`, so the gateway → upstream hop stitches into the same trace. The body value carries the gateway span — the same trace ID as the inbound request, with MCP Manager's own span ID — so MCP-native and multi-hop servers can link their own records to the trace. **Received vs. minted — how to tell which happened.** The `traceparent` column makes the origin of a trace deterministic from the logs alone. On the client-facing leg (`proxy_request_success`), a **present** `traceparent` means the client sent it and MCP Manager adopted that trace; an **absent** `traceparent` means MCP Manager minted the trace itself — and in that case only the gateway ↔ upstream (`mcp_*`) legs carry a `traceparent`. The server-facing `mcp_*` legs always carry the outbound `traceparent` MCP Manager forwarded, because MCP Manager always mints downstream context. MCP Manager does **not** filter or mask forwarded records based on your gateway rules: if a request or response contains PII or other sensitive data, that data is forwarded to your collector even when a rule blocks or masks it elsewhere. What you *can* limit is how much of each record leaves MCP Manager: set **Export content** to **Metadata only** to exclude message bodies and headers, or **Do not export** to send nothing (see [Control what MCP Manager exports](#control-what-mcp-manager-exports)). Whatever you forward, confirm that your collector is a secure, compliant destination before connecting it. ## Set up the OpenTelemetry collector You configure forwarding from the **Integrations** tab of the Logging section. You need the **endpoint URL(s)** from your collector or backend — a **logs collector URL**, a **traces collector URL**, or both — and any **request headers** the backend requires for authentication. The headers you configure are sent with both signals. MCP Manager sends to each collector URL **exactly as you enter it — nothing is appended.** Paste the full signal path: for most backends the logs URL ends in `/v1/logs` and the traces URL ends in `/v1/traces`. Pasting a base URL and expecting MCP Manager to add the path is the single most common setup mistake, and it results in a `404` on every export. Confirm the exact, complete URLs with your provider — the logs and traces paths are different. Go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. If the tab or panel is not visible, see [Who can set up log export](#who-can-set-up-log-export). In **Logs collector URL**, paste the full HTTPS endpoint for your OTLP logs intake; in **Traces collector URL**, paste the endpoint for your OTLP traces intake. Each must include the complete signal path. For example: ```text theme={null} https:///v1/logs https:///v1/traces ``` Replace `` with the host (and port, if required) from your provider. MCP Manager sends to each URL verbatim — if your provider's documented OTLP URLs end in `/v1/logs` and `/v1/traces` (or vendor-specific paths such as Grafana Cloud's `/otlp/v1/logs`), include those paths here. **You must fill in at least one of the two URLs** — the form rejects a save with both blank. Set only the logs URL to forward logs (still trace-correlated, see [Traces and trace-correlated logs](#traces-and-trace-correlated-logs)); set only the traces URL to forward spans; set both to forward everything. Under **Request headers**, add the header name/value pairs your collector expects for authentication. Each row has a header **field name** and a **field value** — for example an `Authorization` header with the value `Bearer `, or a vendor-specific header such as New Relic's `api-key`. Because the headers are an open key/value list, MCP Manager supports both standard `Authorization` schemes and custom headers. Add as many pairs as your backend requires; leave the section empty only if your collector accepts unauthenticated traffic. Each pair must have both a name and a value, or both blank. Select **Save**. MCP Manager stores the collector URL and headers (the header values are encrypted at rest and are not shown back to you in plaintext) and confirms that the configuration was saved. A confirmation appears and the panel switches to a read-only view showing your collector URL and the header names you configured. Saving only **stores** the configuration — it does **not** verify that logs reach your collector. A saved configuration is not proof that logs are flowing. Continue to [Verify that logs are flowing](#verify-that-logs-are-flowing). ## Control what MCP Manager exports By default MCP Manager forwards to your collector the same content it keeps in its own logs. When you need to send your collector less than you store, or more, the Integrations tab has a second card, **OpenTelemetry export content**, that sets this independently of the workspace's [log storage policy](/features/viewing-logs#data-retention-log-storage-policy). It is a workspace-wide choice with four settings: | Export content | What MCP Manager sends your collector | | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Mirror log storage policy** *(default)* | The same content MCP Manager stores, following whatever the workspace's [log storage policy](/features/viewing-logs#data-retention-log-storage-policy) is. Your collector never receives more than MCP Manager itself keeps. | | **Full data** | Complete request and response **contents, headers, and metadata**, regardless of the storage policy. | | **Metadata only** | Metadata only. Timestamps, methods, servers, users, status codes, durations, and token counts, with no message contents or headers. | | **Do not export** | **Nothing** is sent to your collector. | The default, **Mirror log storage policy**, ties the two together, so a stricter storage policy automatically tightens what you export: with storage set to **Metadata only** the mirror sends metadata only, and with **Zero data retention** it sends nothing. The explicit settings let the two diverge. For example, keep **Full data** flowing to your SIEM while MCP Manager stores **Metadata only** internally, or the reverse. Unlike the log storage policy, **Export content** is never constrained by a compliance designation; it is your choice of what leaves MCP Manager. **Export content** and the **collector URLs** are different settings on the same Integrations tab. The URLs decide *where* telemetry goes, and whether traces are exported at all (see [Traces and trace-correlated logs](#traces-and-trace-correlated-logs)); **Export content** decides *how much* of each record is included. Both require the **Manage OpenTelemetry collector** capability. ## Verify that logs are flowing Saving the configuration confirms only that MCP Manager stored it, not that records are successfully reaching your collector. To verify actual delivery: From any connected MCP client (Claude, Claude Code, Cursor, ChatGPT, and so on), make any call through one of your gateways. A `tools/list` call is a safe choice. A single MCP message produces several log records — at minimum a `proxy_request_success` and a `proxy_response_success` — that should now export to your collector. After saving, the OpenTelemetry collector panel shows a **Filter your logs by** section listing the exact `service.name` and `service.version` MCP Manager is sending (in production, `mcp-manager` and `1.0.0`). Use those values to filter in your logs backend. The OTLP `service.name` resource attribute typically surfaces as a `service_name` field or label in your backend. If no records appear, open [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager. Export failures surface there as a warning-level alert before anything else — see [Where export failures surface](#where-export-failures-surface). If there is no alert, work through [Troubleshooting](#troubleshooting). ## Per-vendor setup guides The exact endpoint URL, the authentication header, and where to find your logs differ by backend. Use the vendor guide for your platform: A License (ingest) key and the `api-key` header; query with NRQL. The `/otlp/v1/logs` gateway and an instance-ID/token Basic header; logs land in Loki. The per-site OTLP endpoint and the `dd-api-key` header; verify JSON acceptance. The US or EU endpoint and the `x-honeycomb-team` ingest key. No OTLP logs intake — requires a Collector to translate to Splunk HEC. The universal fallback: receive OTLP logs and forward to any backend. Not every observability platform accepts OTLP logs directly. **Splunk Observability Cloud**, for example, has no OTLP logs intake at all — it accepts only OTLP traces and metrics — so forwarding MCP Manager logs to it requires an OpenTelemetry Collector in front to translate OTLP logs into Splunk's HEC format (see the [Splunk Observability Cloud](/enterprise/export-to-siem/splunk-observability-cloud) guide). When a backend cannot accept OTLP/HTTP logs natively, point MCP Manager at your own [self-hosted Collector](/enterprise/export-to-siem/self-hosted-collector) and forward onward from there. ## Troubleshooting Before diving into vendor-specific details, rule out the most common causes. Export failures almost always come down to the URL path or the authentication header. Because MCP Manager appends nothing to the collector URL, a `404` almost always means the path is incomplete. A stock OTLP/HTTP receiver accepts logs at `/v1/logs`, but many hosted backends mount the OTLP endpoint under a prefix — for example Grafana Cloud uses `/otlp/v1/logs`. Confirm the exact, complete URL with your provider and paste it in full. Whatever they tell you to put in an OTLP exporter's `url` field is what belongs in **Collector URL**. A `401` or `403` means the URL is reaching the backend but the credentials are rejected. Common causes: * **A doubled scheme prefix.** If your provider gives you a value that already starts with `Bearer ` or `Basic `, paste it as-is — do not add another prefix. A value of `Basic Basic ` fails. * **A missing scheme prefix.** If the header is `Authorization`, most backends expect `Bearer ` or `Basic `, not a bare token. * **Raw credentials used for Basic auth.** HTTP Basic expects the base64 encoding of `username:password`; a raw `username:password` value fails. * **A rotated or revoked token.** If forwarding worked before and now fails, check whether the token was rotated or revoked in your provider's dashboard, and update the header value. Some backends use a custom header instead of `Authorization` (for example New Relic's `api-key`). Make sure the header **name** matches exactly what your provider documents. If you run an OpenTelemetry Collector between MCP Manager and your backend, receiving records is not the same as forwarding them. Make sure your exporter is wired into the **logs** pipeline: ```yaml collector-config.yaml theme={null} service: pipelines: logs: receivers: [otlp] exporters: [otlphttp] # the exporter must be listed here ``` If an exporter is defined but not listed under `service.pipelines.logs.exporters`, the collector accepts records from MCP Manager and silently drops them. In a unified observability platform, logs, traces, and metrics usually live in separate backends. MCP Manager sends **logs and traces** (not metrics). Make sure you are querying the right datasource — the logs view (for example Loki in Grafana) for log records, and the traces/APM view for spans — not the metrics store. Querying the wrong datasource returns nothing and looks identical to data not arriving. From any machine with outbound HTTPS access, POST an empty body to your collector URL with your authentication header: ```bash terminal theme={null} curl -v -X POST "" \ -H ": " \ -H "Content-Type: application/json" \ -d '{}' ``` Interpret the response: * **`400 Bad Request`** mentioning an invalid or malformed payload — the URL and authentication are both correct; the backend just rejected the empty test body. This is the result you want. * **`404 Not Found`** — the URL path is wrong. You are reaching the host but not the OTLP logs endpoint (often a missing path prefix). * **`401` / `403`** — the URL is right but the authentication header is wrong. * **Could not resolve host** — a DNS or hostname typo. * **Connection refused or timeout** — the host is unreachable (a self-hosted collector not exposed to MCP Manager, or a wrong port). ## Where export failures surface When an export fails, MCP Manager records it in two places so you can diagnose it without watching the gateway in real time: * **The Alerts tab.** MCP Manager raises a **warning**-level alert the first time an export fails for a given collector URL and error — titled **"Failed to export telemetry logs to OTEL collector"** for the logs signal, or **"Failed to export telemetry traces to OTEL collector"** for the traces signal. The alert message names the collector URL, the number of records or spans, and the HTTP status or error code. To avoid flooding the workspace, the alert is **deduplicated for one hour** per unique combination of collector, signal, and error, so a persistently broken configuration produces one alert per hour rather than one per failed request. Open [Alerts](https://app.mcpmanager.ai/settings/alerts) to see it. * **The gateway logs.** Each failed export is logged on the gateway — by the `OtlpLoggerExporter` for logs and the `OtlpTracerExporter` for traces — including the target URL, the HTTP status, and the error message. A green or saved state on the configuration panel only reflects that the configuration is stored — delivery problems surface as alerts, not as errors on the configuration form. ## Who can set up log export Two conditions govern access to log forwarding, and they are separate from the ability to view logs. * **Plan.** The OpenTelemetry integration is an Enterprise capability. On plans that do not include it, the Integrations tab shows a promotional panel with a contact prompt instead of the configuration form. * **Capability.** Configuring, editing, and removing the collector is controlled by the **Manage OpenTelemetry collector** capability, found in the **Logging** group under the **Capabilities** tab when managing a role (in [People](https://app.mcpmanager.ai/settings/people)). When a user's role has this capability, the OpenTelemetry collector panel is available to them; when it does not, the panel is hidden. Capabilities are assigned per role and are fully configurable — including on any custom roles you create — so access depends on the capabilities granted to a person's role, not on any fixed role name. The separate **View and export logs** capability controls who can read and download logs inside MCP Manager, as described in [Viewing Logs](/features/viewing-logs#who-can-view-and-export-logs). A person can have one capability without the other. ## Frequently asked questions No. MCP Manager sends logs directly to whatever OTLP/HTTP endpoint you configure. If your platform exposes a native OTLP logs intake (Grafana Cloud, New Relic, Honeycomb, and others), point MCP Manager straight at it. Run your own collector only if you need to filter, enrich, or fan out before logs reach their final destination — or if your backend does not accept OTLP logs natively. Not directly. MCP Manager supports a single collector URL per organization. To deliver to multiple destinations, point MCP Manager at your own OpenTelemetry Collector and use its pipeline to fan out to each backend. Not per event. MCP Manager forwards every log record the gateway produces, so you cannot pick individual events. You *can* control how much of each record is sent with the **Export content** setting: full data, metadata only, or nothing (see [Control what MCP Manager exports](#control-what-mcp-manager-exports)). To filter by event or field, terminate at your own collector and filter in the collector's pipeline before exporting onward. No. Each record is exported as it is created; there is no buffering and no replay. If the collector is unreachable when a record is emitted, that record is not delivered. This keeps the gateway latency-neutral. If durable buffering matters to you, run an OpenTelemetry Collector close to MCP Manager with a persistent queue and point MCP Manager at that collector. No. Log export is fire-and-forget: the gateway completes the request to the client and emits the log record asynchronously, so a slow or failing collector does not add latency to MCP calls. Repeated export failures are surfaced as alerts rather than affecting request handling. A saved configuration only confirms storage, not delivery. Check [Alerts](https://app.mcpmanager.ai/settings/alerts) first for a **"Failed to export telemetry logs to OTEL collector"** alert — its message includes the HTTP status code. If there is no alert, confirm you triggered an MCP call after saving, that you are querying the logs datasource in your backend, and work through [Troubleshooting](#troubleshooting). ## Further reading The first per-vendor guide — License key, regional endpoint, and NRQL. The universal fallback for any backend that can't take OTLP logs directly. The log model and the data each forwarded record carries. Why forwarding logs to your SIEM completes the audit story. # Datadog Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/datadog How to forward MCP Manager logs to Datadog over OpenTelemetry: the per-site OTLP logs endpoint, the dd-api-key header, and the JSON-vs-protobuf caveat to verify before relying on it. Datadog offers an agentless OTLP intake, so MCP Manager can send logs directly to Datadog's per-site OTLP/HTTP endpoint over HTTPS without running the Datadog Agent or an OpenTelemetry Collector. This guide covers the Datadog-specific details. For what MCP Manager sends, how forwarding behaves, who can configure it, and general troubleshooting, see [Export to SIEM](/enterprise/export-to-siem). Configuring log forwarding requires the **Manage OpenTelemetry collector** capability and an Enterprise plan that includes the OpenTelemetry integration. If you do not see the **Logging → Integrations** panel, see [Who can set up log export](/enterprise/export-to-siem#who-can-set-up-log-export). MCP Manager sends OTLP/HTTP log records **JSON-encoded**. Datadog's OTLP **logs** intake documentation and examples use the OTLP HTTP **Protobuf** exporter, and JSON acceptance for the logs endpoint is not guaranteed. **Test delivery before relying on it** (Step 4). If records are rejected with a `400`, your backend likely requires protobuf — in that case, route MCP Manager through a [self-hosted OpenTelemetry Collector](/enterprise/export-to-siem/self-hosted-collector) that re-encodes to Datadog. ## What you'll need * A **Datadog account**, and the **site** your organization is on (US1, US3, US5, EU, AP1, AP2, and so on). * A Datadog **API key** (an ingest key), **not** an Application key. * Access to MCP Manager with the **Manage OpenTelemetry collector** capability. ## Step 1: Determine your collector URL Datadog's OTLP intake host is **per-site**, and the logs path is `/v1/logs` on port `443`. For the US1 site the endpoint is: ```text theme={null} https://otlp.datadoghq.com/v1/logs ``` Datadog renders the correct host for your site (US1, US3, US5, EU, AP1, AP2, and the FedRAMP sites) dynamically in its documentation. Use the site selector on Datadog's OTLP intake docs to get your exact host rather than guessing — a host that belongs to the wrong site returns `403 Forbidden`. Append `/v1/logs` to that host; MCP Manager appends nothing itself. ## Step 2: Create an API key In Datadog, go to **Organization Settings → API Keys** and create or copy an **API key**. This is an ingest key, distinct from an **Application key** (which is used for the Datadog API and will not work for OTLP ingestion). ## Step 3: Connect MCP Manager to Datadog Datadog authenticates with a custom **`dd-api-key`** header rather than the standard `Authorization` header. Because MCP Manager's **Request headers** field is an open key/value list, you add `dd-api-key` directly. In MCP Manager, go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. In **Logs collector URL**, paste your site's OTLP logs endpoint, for example `https://otlp.datadoghq.com/v1/logs` for US1. (To also export traces, set **Traces collector URL** to the matching `/v1/traces` endpoint; this guide covers logs.) Under **Request headers**, add one header: * **Field name:** `dd-api-key` * **Field value:** your Datadog API key Select **Save**. MCP Manager stores the configuration and encrypts the header value. Saving confirms storage only — it does not confirm delivery. ## Step 4: Verify logs are flowing Trigger an MCP call through a gateway (a `tools/list` call is enough), then check [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager for a **"Failed to export telemetry logs to OTEL collector"** alert. For Datadog: * **`403 Forbidden`** — the endpoint host is wrong for your organization's site, or the API key is invalid. * **`404 Not Found`** — the URL path is wrong (missing `/v1/logs`). * **`400 Bad Request`** — the payload was rejected, which can mean the logs intake did not accept the JSON encoding (see the caveat above). When logs arrive, find them in Datadog's **Logs Explorer**. The OTLP `service.name` resource attribute maps to Datadog's `service` facet, so filter on the `service.name` shown in MCP Manager's **Filter your logs by** panel. ## Troubleshooting A `403` on the Datadog intake means the endpoint URL is wrong for your organization's site, or the key is bad. Confirm your site with the Datadog site selector and use that exact host, and confirm you used an **API key** (not an Application key) that belongs to the right account. The URL path is wrong. MCP Manager appends nothing, so the URL must end in `/v1/logs` on your site's OTLP host. Datadog's OTLP logs intake documents the HTTP/Protobuf exporter, and MCP Manager sends OTLP/HTTP **JSON**. A `400` rejecting the payload can mean the logs intake did not accept JSON. Route MCP Manager through a [self-hosted OpenTelemetry Collector](/enterprise/export-to-siem/self-hosted-collector) and let the collector export to Datadog, or confirm JSON support with Datadog. From any machine with outbound HTTPS access: ```bash terminal theme={null} curl -v -X POST "https://otlp.datadoghq.com/v1/logs" \ -H "dd-api-key: " \ -H "Content-Type: application/json" \ -d '{}' ``` A `2xx` or a `400` rejecting only the empty body means the site host and key are valid; `403` means the site host is wrong or the key is bad; `404` means the path is wrong. ## Further reading The next per-vendor guide — US/EU endpoints and the x-honeycomb-team key. What MCP Manager sends, how forwarding behaves, and general troubleshooting. Re-encode JSON to protobuf and forward to Datadog from your own collector. ## External sources The agentless intake and the per-site host selector for your exact endpoint. The `/v1/logs` path, the `dd-api-key` header, and the protobuf-exporter examples. Where keys live and the API-key-vs-Application-key distinction. The protocol spec, including the success and partial-success response contract. # Grafana Cloud Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/grafana-cloud How to forward MCP Manager logs to Grafana Cloud over OpenTelemetry: the /otlp/v1/logs gateway URL, building the instanceID:token Basic auth header, the logs:write access-policy token, and querying logs in Loki with LogQL. Grafana Cloud exposes a managed OTLP gateway that accepts OpenTelemetry logs natively over OTLP/HTTP, so MCP Manager can send logs directly with no intermediate collector. Grafana Cloud converts incoming OTLP logs into **Loki**, where you query them with LogQL. This guide covers the Grafana Cloud-specific details. For what MCP Manager sends, how forwarding behaves, who can configure it, and general troubleshooting, see [Export to SIEM](/enterprise/export-to-siem). Configuring log forwarding requires the **Manage OpenTelemetry collector** capability and an Enterprise plan that includes the OpenTelemetry integration. If you do not see the **Logging → Integrations** panel, see [Who can set up log export](/enterprise/export-to-siem#who-can-set-up-log-export). ## What you'll need * A **Grafana Cloud** stack. * Your stack's numeric **instance ID** and OTLP gateway URL (both shown in the Grafana Cloud portal). * A **Cloud Access Policy token** scoped with `logs:write` — **not** a Grafana API or service-account token. * Access to MCP Manager with the **Manage OpenTelemetry collector** capability. ## Step 1: Find your OTLP gateway URL and instance ID In the Grafana Cloud portal, select your stack and open the **OpenTelemetry** ("Configure") panel. It shows your OTLP gateway URL and your numeric instance ID. The gateway URL has the shape: ```text theme={null} https://otlp-gateway-.grafana.net/otlp ``` Your `` is region-specific, for example `prod-us-east-0` or `prod-eu-west-0`. The gateway is rooted at `/otlp`, so the **logs** endpoint you paste into MCP Manager is that base plus `/v1/logs`, on port `443`: ```text theme={null} https://otlp-gateway-.grafana.net/otlp/v1/logs ``` Include the `/otlp` segment. Grafana Cloud's logs path is `/otlp/v1/logs`, **not** `/v1/logs` — and because MCP Manager appends nothing to the URL, pasting `.../v1/logs` without the `/otlp` prefix returns a `404` on every export. This is by far the most common Grafana Cloud setup mistake. ## Step 2: Create an access-policy token with logs:write In the Grafana Cloud portal, go to **Access Policies**, create (or reuse) a policy that includes the `logs:write` scope, and generate a **token** under it. Use this Cloud Access Policy token — a Grafana API key or service-account token will not authenticate against the OTLP gateway. ## Step 3: Build the Basic auth header value Grafana Cloud's OTLP gateway uses HTTP **Basic** authentication, where the credentials are your **instance ID** and **access-policy token** joined with a colon and base64-encoded. Build the value yourself: ```bash terminal theme={null} printf '%s' ':' | base64 ``` The header value you give MCP Manager is the word `Basic`, a space, and that encoded string: ```text theme={null} Basic ``` ## Step 4: Connect MCP Manager to Grafana Cloud In MCP Manager, go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. In **Logs collector URL**, paste your full logs endpoint including the `/otlp` prefix, for example `https://otlp-gateway-prod-us-east-0.grafana.net/otlp/v1/logs`. (To also export traces, set **Traces collector URL** to the matching `/otlp/v1/traces` endpoint; this guide covers logs.) Under **Request headers**, add one header: * **Field name:** `Authorization` * **Field value:** `Basic ` from Step 3 Select **Save**. MCP Manager stores the configuration and encrypts the header value. Saving confirms storage only — it does not confirm delivery. ## Step 5: Find your logs in Loki Grafana Cloud routes OTLP logs into **Loki**, so query them with **LogQL** in Grafana Explore (or the Logs Drilldown app). The OTLP `service.name` resource attribute becomes the `service_name` label, so filter MCP Manager's logs with: ```logql theme={null} {service_name="mcp-manager"} ``` Use the exact `service.name` shown in MCP Manager's **Filter your logs by** panel. To discover available labels, open the label browser in Grafana Explore. ## Troubleshooting The `/otlp` prefix is almost certainly missing. The logs endpoint is `https://otlp-gateway-.grafana.net/otlp/v1/logs` — MCP Manager appends nothing, so the URL must include `/otlp/v1/logs` in full. The `instance-id:access-policy-token` pair is wrong or was not base64-encoded. Rebuild the value with `printf '%s' ':' | base64` and confirm the header is `Basic ` (a single `Basic` prefix). Also confirm you used a **Cloud Access Policy token with `logs:write`**, not a Grafana API or service-account token. Make sure you are querying **Loki**, not Prometheus. MCP Manager sends logs, which land in Loki; querying the Prometheus (metrics) datasource returns nothing and looks identical to logs not arriving. Switch the datasource selector in Explore to your Loki datasource and query `{service_name="mcp-manager"}`. From any machine with outbound HTTPS access: ```bash terminal theme={null} curl -v -X POST "https://otlp-gateway-.grafana.net/otlp/v1/logs" \ -H "Authorization: Basic " \ -H "Content-Type: application/json" \ -d '{}' ``` A `2xx` confirms the URL and credentials are valid; `401` means the `instance-id:token` pair is wrong or not base64-encoded; `404` almost always means the `/otlp` prefix was omitted; `400` means a malformed body (expected for this empty probe once the URL and auth are correct). ## Further reading The next per-vendor guide — per-site endpoint and the dd-api-key header. What MCP Manager sends, how forwarding behaves, and general troubleshooting. Route through your own collector to filter, enrich, or fan out. ## External sources Grafana's reference for native OTLP ingest into Loki, Mimir, and Tempo. The gateway URL shape and the instance-ID/token Basic auth construction. Generate the Cloud Access Policy token with the `logs:write` scope. The protocol spec, including the success and partial-success response contract. # Honeycomb Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/honeycomb How to forward MCP Manager logs to Honeycomb over OpenTelemetry: the US and EU endpoints, the x-honeycomb-team ingest key, and how dataset routing works via x-honeycomb-dataset or service.name. Honeycomb accepts OpenTelemetry logs natively over OTLP/HTTP, including JSON encoding, so MCP Manager can send logs directly with no intermediate collector. This is one of the most straightforward backends to connect. This guide covers the Honeycomb-specific details. For what MCP Manager sends, how forwarding behaves, who can configure it, and general troubleshooting, see [Export to SIEM](/enterprise/export-to-siem). Configuring log forwarding requires the **Manage OpenTelemetry collector** capability and an Enterprise plan that includes the OpenTelemetry integration. If you do not see the **Logging → Integrations** panel, see [Who can set up log export](/enterprise/export-to-siem#who-can-set-up-log-export). ## What you'll need * A **Honeycomb account** (US or EU instance). * A Honeycomb **ingest API key** with permission to send events and create datasets. * Access to MCP Manager with the **Manage OpenTelemetry collector** capability. ## Step 1: Choose your collector URL Honeycomb's OTLP host depends on your instance, and the logs path is `/v1/logs` on port `443`: | Instance | Logs collector URL | | -------- | -------------------------------------- | | US | `https://api.honeycomb.io/v1/logs` | | EU | `https://api.eu1.honeycomb.io/v1/logs` | The **US** endpoint is `https://api.honeycomb.io/v1/logs` and the **EU** endpoint is `https://api.eu1.honeycomb.io/v1/logs`. Include the `/v1/logs` path — MCP Manager appends nothing to the URL. ## Step 2: Create an ingest API key In Honeycomb, go to **Environment settings → API Keys** and create an **ingest key** with permission to send events and create datasets. This key goes in the `x-honeycomb-team` header. ## Step 3: Connect MCP Manager to Honeycomb Honeycomb authenticates with the **`x-honeycomb-team`** header. Routing to a dataset is controlled by an optional **`x-honeycomb-dataset`** header. In MCP Manager, go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. In **Logs collector URL**, paste your instance's endpoint, for example `https://api.honeycomb.io/v1/logs` for the US instance. (To also export traces, set **Traces collector URL** to the matching `/v1/traces` endpoint; this guide covers logs.) Under **Request headers**, add the API key header, and optionally a dataset header: * **`x-honeycomb-team`** = your ingest API key (required) * **`x-honeycomb-dataset`** = the destination dataset name (optional) Select **Save**. MCP Manager stores the configuration and encrypts the header values. Saving confirms storage only — it does not confirm delivery. On Environments-based Honeycomb accounts, if you do **not** set `x-honeycomb-dataset`, logs are routed to a dataset named after the OTLP `service.name` resource attribute (in production, `mcp-manager`), creating that dataset if it does not exist. On Honeycomb Classic, the `x-honeycomb-dataset` header is required. Set the dataset header explicitly if you want MCP Manager's logs in a specific, named dataset. ## Step 4: Find your logs in Honeycomb Trigger an MCP call through a gateway (a `tools/list` call is enough), then open the destination dataset in Honeycomb's **Query Builder**. The dataset is the one named by your `x-honeycomb-dataset` header, or — if you did not set it — the one matching MCP Manager's `service.name` (`mcp-manager`). If nothing appears, check [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager for an export-failure alert. ## Troubleshooting The `x-honeycomb-team` API key is missing or invalid. Confirm the header name is exactly `x-honeycomb-team` and that the value is an **ingest** key with permission to send events. The URL path is wrong. MCP Manager appends nothing, so the URL must end in `/v1/logs`. Check the host for your instance: `api.honeycomb.io` for US, `api.eu1.honeycomb.io` for EU. Without an `x-honeycomb-dataset` header, Environments-based accounts route logs to a dataset named after `service.name` (`mcp-manager`) and create it if needed. To control the destination, set `x-honeycomb-dataset` explicitly to your chosen dataset name. From any machine with outbound HTTPS access: ```bash terminal theme={null} curl -v -X POST "https://api.honeycomb.io/v1/logs" \ -H "x-honeycomb-team: " \ -H "Content-Type: application/json" \ -d '{}' ``` A `2xx` or a `400` rejecting only the empty body means the host and key are valid; `401` means the team key is bad; `404` means the path is wrong. ## Further reading The next per-vendor guide — why it needs a Collector to take OTLP logs. What MCP Manager sends, how forwarding behaves, and general troubleshooting. Route through your own collector to filter, enrich, or fan out. ## External sources The US and EU endpoints and how Honeycomb handles OTLP signal paths. Create the ingest key used in the `x-honeycomb-team` header. The protocol spec, including the success and partial-success response contract. Default ports and paths, and the protobuf-JSON encoding constraint for OTLP/HTTP JSON. # New Relic Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/new-relic How to forward MCP Manager logs to New Relic over OpenTelemetry: generating a License (ingest) key, choosing the regional OTLP endpoint, configuring the api-key request header, and verifying logs with NRQL. New Relic supports native OTLP (OpenTelemetry Protocol) ingestion, so MCP Manager can send logs straight to New Relic's OTLP/HTTP endpoint over HTTPS — no intermediate collector or proxy required. Once your MCP Manager logs are in New Relic, you can query them with NRQL, build dashboards alongside your application telemetry, and alert on unusual MCP tool-call patterns. This guide covers the New Relic-specific details. For what MCP Manager sends, how forwarding behaves, who can configure it, and general troubleshooting, see [Export to SIEM](/enterprise/export-to-siem). Configuring log forwarding requires the **Manage OpenTelemetry collector** capability and an Enterprise plan that includes the OpenTelemetry integration. If you do not see the **Logging → Integrations** panel, see [Who can set up log export](/enterprise/export-to-siem#who-can-set-up-log-export). ## What you'll need * A **New Relic account**. * A New Relic **License (ingest) key** — the key type used to send data into New Relic. This is **not** a User key. * Access to MCP Manager with the **Manage OpenTelemetry collector** capability. ## Step 1: Generate a License (ingest) key in New Relic MCP Manager authenticates to New Relic with a **License key** (also called an ingest key). License keys are specifically for sending data into New Relic; User keys are for NerdGraph API access and will **not** work for OTLP ingestion. Log in at [one.newrelic.com](https://one.newrelic.com) and go to the **API keys** page (via the user menu in the lower-left corner, or directly at [one.newrelic.com/api-keys](https://one.newrelic.com/api-keys)). Select **Create a key**. For **Key type**, choose **Ingest - License**. Give it a descriptive name (for example, `MCP Manager OTEL Logs`) and select the account the key belongs to. Select **Create a key**, then copy the full key right away. New Relic shows the complete key only once at creation; afterward only the first characters are visible. If you lose it, create a new one. License (ingest) keys are distinct from User keys. A User key returns `403 Forbidden` on the OTLP ingest endpoint. If you are unsure which you have, create a fresh **Ingest - License** key. ## Step 2: Choose your collector URL New Relic exposes regional OTLP endpoints. Pick the one matching your account's data region, and include the full `/v1/logs` path — MCP Manager sends the URL exactly as entered and appends nothing. | Region | Logs collector URL | | ------------- | -------------------------------------------- | | United States | `https://otlp.nr-data.net:4318/v1/logs` | | Europe | `https://otlp.eu01.nr-data.net:4318/v1/logs` | | US FedRAMP | `https://gov-otlp.nr-data.net:4318/v1/logs` | The **US** endpoint is `https://otlp.nr-data.net:4318/v1/logs` and the **EU** endpoint is `https://otlp.eu01.nr-data.net:4318/v1/logs`. A few details: * **Port `4318`** is the standard OTLP/HTTP port. New Relic also accepts traffic on `443` and `4317`, but `4318` is the recommended HTTP port. * **The `/v1/logs` path is required.** MCP Manager does not append path segments, so the URL must end in `/v1/logs`. * If you are unsure of your region, check your New Relic URL: `one.newrelic.com` is US, and `one.eu.newrelic.com` is EU. ## Step 3: Connect MCP Manager to New Relic New Relic authenticates with a custom **`api-key`** header rather than the standard `Authorization` header. Because MCP Manager's **Request headers** field is an open key/value list, you add the `api-key` header directly — no `Bearer` or `Basic` prefix. In MCP Manager, go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. In **Logs collector URL**, paste your regional endpoint from Step 2, for example `https://otlp.nr-data.net:4318/v1/logs` for a US account. (To also export traces, set **Traces collector URL** to the matching `/v1/traces` endpoint; this guide covers logs.) Under **Request headers**, add one header: * **Field name:** `api-key` * **Field value:** your License (ingest) key from Step 1 Paste the key value directly — New Relic expects the raw key, with no `Bearer` or `Basic` prefix. Select **Save**. MCP Manager stores the configuration and encrypts the header value. Saving confirms storage only — it does not confirm delivery. Continue to verification. ## Step 4: Verify logs are flowing From a connected MCP client (Claude, Claude Code, Cursor, ChatGPT, and so on), make any call through a gateway — a `tools/list` call is enough. Open [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager. A delivery failure appears as a **"Failed to export telemetry logs to OTEL collector"** alert with the HTTP status code. For New Relic, the common codes are: * **`401 Unauthorized`** — the License key is incorrect or expired. * **`403 Forbidden`** — the key is likely a User key rather than a License (ingest) key. * **`404 Not Found`** — the URL path is wrong (missing `/v1/logs`). The OpenTelemetry collector panel's **Filter your logs by** section shows the `service.name` and `service.version` MCP Manager is sending. Use those values to find your logs in New Relic. ## Step 5: Find your logs in New Relic Once logs are flowing, open **Logs** in the New Relic left sidebar (under **All Capabilities** if it is not pinned), or query with NRQL: ```sql theme={null} SELECT * FROM Log SINCE 30 minutes ago ``` You should see records for each MCP request, including `proxy_request_success` and `proxy_response_success` entries with detailed metadata. There may be a short delay (up to a few minutes) before logs appear in New Relic's query interface. From here you can build dashboards and alert conditions alongside the rest of your New Relic telemetry. ## Troubleshooting Check [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager first — a failing export shows the HTTP status code there. Confirm you triggered an MCP call **after** saving (records are only produced when requests flow through a gateway), and that you are looking at **Logs** in New Relic, not APM or Infrastructure. Allow a few minutes for records to appear. Verify you are using a **License (ingest) key**, not a User key — a User key returns `403`. Confirm the key belongs to the correct New Relic account, and check in New Relic's **API keys** page that the key has not been revoked. The most common cause is a missing `/v1/logs` path — MCP Manager appends nothing, so the URL must end in `/v1/logs`. Double-check the host for your region: `otlp.nr-data.net` for US, `otlp.eu01.nr-data.net` for EU. From any machine with outbound HTTPS access: ```bash terminal theme={null} curl -v -X POST "https://otlp.nr-data.net:4318/v1/logs" \ -H "api-key: " \ -H "Content-Type: application/json" \ -d '{}' ``` A `400 Bad Request` with a JSON error about an invalid payload means the URL and `api-key` are both correct — New Relic just rejected the empty test body. That is the response you want; it confirms MCP Manager would reach the endpoint on a real request. ## Further reading The next per-vendor guide — the /otlp/v1/logs gateway and Basic auth. What MCP Manager sends, how forwarding behaves, and general troubleshooting. The universal fallback if you'd rather route through your own collector. # Self-hosted OpenTelemetry Collector Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/self-hosted-collector How to forward MCP Manager logs to a self-hosted OpenTelemetry Collector: the OTLP/HTTP receiver on port 4318, the /v1/logs path, adding authentication with collector auth extensions, and using the collector as a universal fallback to any backend. A self-hosted **OpenTelemetry Collector** is the universal fallback for MCP Manager log forwarding. The Collector's OTLP receiver is the reference OTLP implementation — it accepts OTLP/HTTP in both JSON and protobuf natively — so MCP Manager can send to it directly, and you use the Collector's pipeline to filter, enrich, fan out to multiple backends, or translate to a protocol a backend requires (for example translating OTLP logs to Splunk HEC for [Splunk Observability Cloud](/enterprise/export-to-siem/splunk-observability-cloud)). This guide covers the Collector-specific details. For what MCP Manager sends, how forwarding behaves, who can configure it, and general troubleshooting, see [Export to SIEM](/enterprise/export-to-siem). Configuring log forwarding requires the **Manage OpenTelemetry collector** capability and an Enterprise plan that includes the OpenTelemetry integration. If you do not see the **Logging → Integrations** panel, see [Who can set up log export](/enterprise/export-to-siem#who-can-set-up-log-export). ## What you'll need * A running OpenTelemetry Collector **reachable from MCP Manager over HTTPS** (MCP Manager is a hosted service, so the receiver must be exposed to the public internet or to MCP Manager's egress). * The Collector's **OTLP receiver** enabled with a **logs** pipeline. * Optionally, an **authentication extension** if you want the receiver to require credentials. * Access to MCP Manager with the **Manage OpenTelemetry collector** capability. ## Step 1: Determine your collector URL The Collector's OTLP receiver listens on `0.0.0.0:4317` for **gRPC** and `0.0.0.0:4318` for **HTTP**. MCP Manager sends OTLP/HTTP, so use the HTTP port and the default logs path: ```text theme={null} https://:4318/v1/logs ``` The HTTP paths default to `/v1/traces`, `/v1/metrics`, and `/v1/logs`, and the logs path is overridable via the receiver's `logs_url_path` setting. Point MCP Manager at the **HTTP** port `4318`, not the gRPC port `4317`. MCP Manager sends OTLP/HTTP, so a URL on `4317` will not work. And because MCP Manager appends nothing to the URL, include the full `/v1/logs` path (or your overridden `logs_url_path`). ## Step 2: Enable the OTLP receiver and a logs pipeline In your Collector configuration, enable the OTLP receiver and wire it into a **logs** pipeline alongside your chosen exporter: ```yaml collector-config.yaml theme={null} receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 exporters: otlphttp: endpoint: https://your-backend.example.com/otlp service: pipelines: logs: receivers: [otlp] exporters: [otlphttp] # the exporter must be listed here ``` Receiving records is not the same as forwarding them: an exporter that is defined but not listed under `service.pipelines.logs.exporters` causes the Collector to accept records from MCP Manager and silently drop them. ## Step 3: Add authentication (optional) The OTLP receiver has **no authentication by default**. Add an auth extension and reference it from the receiver. Common choices live in the Collector-contrib `extension/` directory: `bearertokenauthextension` (an `Authorization: Bearer ` scheme), `basicauthextension` (HTTP Basic), and `oidcauthextension`. Because MCP Manager sends arbitrary request headers, whatever scheme you configure is supported — you set the matching header in MCP Manager. ```yaml collector-config.yaml theme={null} extensions: bearertokenauth: token: receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 auth: authenticator: bearertokenauth service: extensions: [bearertokenauth] pipelines: logs: receivers: [otlp] exporters: [otlphttp] ``` ## Step 4: Connect MCP Manager to your Collector In MCP Manager, go to [Logs → Integrations](https://app.mcpmanager.ai/settings/logging/integrations) and find the **OpenTelemetry collector** panel. In **Logs collector URL**, paste your Collector's HTTP logs endpoint, for example `https://collector.example.com:4318/v1/logs`. (To also export traces, set **Traces collector URL** to the Collector's `/v1/traces` endpoint and wire a `traces` pipeline; this guide covers logs.) Under **Request headers**, add whatever header your auth extension expects — for example an `Authorization` header with the value `Bearer ` to match `bearertokenauthextension`. Leave the section empty if your receiver accepts unauthenticated traffic. Select **Save**. MCP Manager stores the configuration and encrypts the header values. Saving confirms storage only — it does not confirm delivery. ## Step 5: Verify and find your logs Trigger an MCP call through a gateway (a `tools/list` call is enough). A correctly configured OTLP/HTTP receiver returns `2xx` with a body of `{"partialSuccess":{}}` for an accepted batch. Where the records end up depends on your downstream exporter; the OTLP `service.name` resource attribute (in production, `mcp-manager`) is preserved through the pipeline, so filter on it in your final backend. If nothing arrives, check [Alerts](https://app.mcpmanager.ai/settings/alerts) in MCP Manager. Encoding constraint: OTLP/HTTP with JSON must use protobuf-JSON serialization, with `bytes` fields base64-encoded. The reference OTLP receiver accepts this natively, so MCP Manager's JSON encoding works against a standard Collector. If you place a non-standard receiver in front, confirm it accepts protobuf-JSON. ## Long-term retention to object storage A self-hosted Collector is also how you keep MCP logs for **any duration** — well beyond your MCP Manager plan's retention period, including indefinitely for compliance. The pattern is **MCP Manager → your Collector → your object store**: point MCP Manager at your Collector as above, then add an exporter that writes to object storage such as Amazon S3, and control how long the data lives with your bucket's own lifecycle rules. Because the data sits in your storage, the retention period is entirely yours to set. The Collector-contrib `awss3exporter` writes the log records it receives to an S3 bucket. Add it to the **logs** pipeline alongside (or instead of) your other exporters: ```yaml collector-config.yaml theme={null} receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 exporters: awss3: s3uploader: region: us-east-1 s3_bucket: your-mcp-log-archive s3_prefix: mcp-manager s3_partition_format: '%Y/%m/%d/%H' service: pipelines: logs: receivers: [otlp] exporters: [awss3] # add other exporters here to fan out as well ``` The exporter writes objects under the `s3_prefix`, partitioned by time, so logs land as `your-mcp-log-archive/mcp-manager/2026/05/29/14/...`. Set the retention you need with an **S3 lifecycle policy** on the bucket — keep objects for a fixed number of years, transition them to colder storage classes, or never expire them. Equivalent exporters exist for other clouds (for example `googlecloudstorageexporter` for Google Cloud Storage and `azureblobexporter` for Azure Blob Storage); the pattern is the same. The `awss3exporter` archives raw log records to object storage; it is not a query engine. To search archived logs, run a query layer over the bucket (for example Amazon Athena over the S3 objects) or keep a parallel exporter to your SIEM for live querying while S3 holds the long-term archive. ## Troubleshooting Confirm MCP Manager is pointed at the **HTTP** port `4318` and the `/v1/logs` path, not the gRPC port `4317`. Confirm the Collector host is reachable from the public internet (a Collector bound only to a private network is not reachable by the hosted MCP Manager service). The exporter is not wired into the logs pipeline. Ensure your exporter is listed under `service.pipelines.logs.exporters`; otherwise the Collector receives records and drops them. The OTLP/HTTP receiver does not always return a clean status when an authenticator rejects a request — the client may report an unparseable response rather than a clear `401`. If exports fail right after you enable an auth extension, suspect the credentials or header name before chasing a transport bug, and confirm the header MCP Manager sends matches what the extension expects. From any machine with outbound HTTPS access: ```bash terminal theme={null} curl -v -X POST "https://:4318/v1/logs" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' ``` A `2xx` with `{"partialSuccess":{}}` confirms the receiver is reachable and authenticated; a `404` means a wrong or overridden path; a `400` means a malformed body. ## Further reading What MCP Manager sends, how forwarding behaves, and general troubleshooting. The backend that needs this collector to translate OTLP logs to Splunk HEC. Why forwarding logs to your own store completes the audit and retention story. ## External sources Default ports and paths, `logs_url_path`, and the protobuf-JSON encoding constraint. The `bearertokenauth`, `basicauth`, and `oidcauth` extensions, each with its own README. The protocol spec, including the success and partial-success response contract. Archive log records to Amazon S3 for long-term retention; see also the GCS and Azure Blob exporters. # Splunk Observability Cloud Source: https://docs.mcpmanager.ai/enterprise/export-to-siem/splunk-observability-cloud Why MCP Manager cannot forward logs directly to Splunk Observability Cloud — it has no OTLP logs intake — and how to deliver logs instead using a self-hosted OpenTelemetry Collector that translates OTLP to Splunk HEC. Splunk Observability Cloud is the one backend on this list that MCP Manager **cannot** forward logs to directly. This page explains why, and what to do instead. **Splunk Observability Cloud has no OTLP logs intake.** Its OTLP/HTTP endpoint accepts only **traces** (at `/v2/trace/otlp`) and **metrics** (at `/v2/datapoint/otlp`) — there is no `/v1/logs` equivalent. Because MCP Manager sends logs over OTLP/HTTP, it cannot deliver them to Splunk Observability Cloud directly. Use a [self-hosted OpenTelemetry Collector](/enterprise/export-to-siem/self-hosted-collector) in front to translate OTLP logs into Splunk's HEC format. This page covers the Splunk-specific situation. For what MCP Manager sends and how forwarding behaves in general, see [Export to SIEM](/enterprise/export-to-siem). ## Why MCP Manager can't send logs directly Splunk Observability Cloud's documented OTLP/HTTP exporter exposes only a traces endpoint and a metrics endpoint — there is no logs endpoint. In the Splunk product split, **logs** live in **Splunk Cloud Platform** (or Splunk Enterprise), not in Observability Cloud; Observability Cloud's **Log Observer Connect** feature *queries* those logs where they already reside rather than ingesting them over OTLP. Splunk's own logs ingestion path is the **HTTP Event Collector (HEC)**, for example `https://:8088/services/collector`, which is a different protocol from OTLP. The practical consequence: MCP Manager's single OTLP/HTTP logs forwarder has no Splunk Observability Cloud endpoint to target for logs. ## What to do instead: translate OTLP logs to HEC with a Collector Run a [self-hosted OpenTelemetry Collector](/enterprise/export-to-siem/self-hosted-collector) that receives OTLP logs from MCP Manager and exports them to Splunk via the `splunk_hec` exporter. MCP Manager points at your Collector; your Collector translates and forwards to HEC. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart LR A["🛡️
MCP Manager"] -->|OTLP/HTTP logs| B["🔌
Your OpenTelemetry Collector"] B -->|splunk_hec exporter| C["🔌
Splunk Cloud Platform HEC"] classDef gateway fill:#0086ff,color:#ffffff,stroke:#062b4c,stroke-width:2px; classDef external fill:#e0e2e8,color:#2c2c37,stroke:#9ca1ab,stroke-width:1px,stroke-dasharray:4 3; class A gateway; class B,C external; ``` In this setup: * **MCP Manager → Collector** uses the OTLP/HTTP logs endpoint described in the [self-hosted Collector guide](/enterprise/export-to-siem/self-hosted-collector) (`https://:4318/v1/logs`). * **Collector → Splunk** uses the `splunk_hec` exporter pointed at your HEC endpoint (`https://:8088/services/collector`) with a HEC token. This logs limitation does **not** apply to traces. MCP Manager now also exports request **traces** over OTLP/HTTP, and Splunk Observability Cloud *does* accept OTLP traces — so you can point the **Traces collector URL** at Splunk's trace endpoint (`https://ingest..observability.splunkcloud.com/v2/trace/otlp`) with an `X-SF-Token` access-token header, no Collector required. Because MCP Manager sends OTLP/HTTP **JSON** and Splunk's trace intake documents protobuf, verify delivery before relying on it (and fall back to a self-hosted Collector if JSON is rejected). For **logs**, the Collector-to-HEC path above remains the only route. ## Find your realm Every Splunk Observability Cloud host is realm-specific (for example `us0`, `us1`, `eu0`, `eu1`, `ap0`). Your realm determines the ingest host `https://ingest..observability.splunkcloud.com`. Find your realm in the Splunk Observability Cloud UI before configuring any endpoint. ## Further reading Stand up the Collector that receives OTLP logs and exports them to Splunk HEC. What MCP Manager sends, how forwarding behaves, and general troubleshooting. ## External sources Shows the `/v2/trace/otlp` and `/v2/datapoint/otlp` paths with `X-SF-Token` auth — and no logs endpoint. How to find your realm, which determines every ingest host. How Splunk queries logs that live in Splunk Cloud Platform rather than Observability Cloud. # Add an MCP Manager tile to your IdP dashboard Source: https://docs.mcpmanager.ai/enterprise/idp-dashboard-tile Configure your IdP so users launch MCP Manager with one click from the IdP dashboard: allow IdP-initiated login, set the tenant-scoped Initiate login URI, and assign the application to users. By default, signing in to MCP Manager is **service-provider-initiated**: the user starts at MCP Manager, enters their work email, and MCP Manager routes them to your IdP. You can also add an MCP Manager tile to your IdP dashboard that signs users in **directly** — one click takes them straight into MCP Manager through your IdP, with no email to type. The tile points at a sign-in URL that already names your workspace's connection, so MCP Manager knows which IdP to use without asking. The tile requires a working [SSO connection](/enterprise/sso). Set up SSO first; the tile is an optional addition for IdP-initiated launch. ## Configure the tile In the OIDC application's general settings, set **Login initiated by** to allow both the IdP and the app (in Okta, *Either Okta or App*), and enable **Display application icon to users**. Set the application's **Initiate login URI** to the workspace-specific MCP Manager sign-in URL we provide during onboarding. It has the form: ```text theme={null} https://gateway.mcpmanager.ai/auth/login?tenant=your-workspace-tenant ``` The `tenant` value identifies your workspace's enterprise connection and is **specific to your organization** — MCP Manager gives you the exact value; do not guess it. Because the URL carries the tenant, clicking the dashboard tile sends the user straight to MCP Manager and through your IdP, signed in without entering their email. Click [this link](/images/mcp-manager-logo-vertical.png) to download the MCP Manager icon seen below. The image below will be rendered through our CDN as an AVIF format, but the link above will bypass the CDN and serve you the raw PNG. Mcp Manager Logo Vertical 1 Mcp Manager Logo Vertical 1 Assign the OIDC application to the users or groups who should see and use the tile. The tile only appears for users who are **assigned to the OIDC application** in your IdP. This is separate from SCIM provisioning assignment — a user assigned for provisioning but not assigned to the OIDC application will not see the tile and cannot launch from it. See [SCIM provisioning](/enterprise/scim). A user assigned to the application sees the MCP Manager tile on their IdP dashboard, and clicking it lands them in MCP Manager signed in — no email entry. ## Troubleshooting The tile requires the OIDC application to allow IdP-initiated launch (in Okta, *Either Okta or App*), to display its icon to users, and to have its **Initiate login URI** set to the tenant-scoped sign-in URL we provide (`https://gateway.mcpmanager.ai/auth/login?tenant=your-workspace-tenant`). Confirm those settings, that the `tenant` value matches the one MCP Manager gave you, and that the user is assigned to the OIDC application. ## Further reading How MCP Manager brokers enterprise sign-in and what you provide to connect your IdP. Automatically create users and sync IdP groups to MCP Manager teams. # Programmatic Access Source: https://docs.mcpmanager.ai/enterprise/programmatic-access How to manage MCP Manager from code: the Admin API and MCP server (in closed beta) provision and configure gateways, servers, identities, roles, and more over MCP tools and REST, scoped by your role capabilities; a downloadable CLI and infrastructure-as-code support are still to come; and the token-based agent connection and per-user identity passing that ship today. Many teams want to manage their MCP Manager setup **programmatically** — provisioning gateways, servers, identities, and roles from code or a pipeline instead of clicking through the app. This page explains what you can automate today and where the programmatic surfaces stand. **The Admin API and MCP server are now in closed beta.** You can provision and manage most of your workspace over MCP tools and a REST API, scoped by your role capabilities. See the [Admin API & MCP](/admin-api/overview) section for the full documentation. Access is gated by the **MCP Manager Admin API** entitlement — ask your MCP Manager contact to join. ## Manage your workspace with the Admin API The [Admin API and MCP server](/admin-api/overview) are the **control plane** for MCP Manager. From an agent or a script you can create and configure inbound servers, gateways and their assignments, identities, hosts and connections, teams, roles, and access tokens, and query the call logs and alerts — the same actions available in the app, enforced by the **same role capabilities**. Every operation is exposed both as an MCP tool and as a REST endpoint under `/api/v1/mcpm-admin`. This is in **closed beta**: available now to workspaces with the **MCP Manager Admin API** entitlement (`ff-mcpm-admin`), ahead of general availability. To get started, see [Connect an agent](/admin-api/connect) and the [tool & endpoint reference](/admin-api/reference/overview). ### Still to come A few programmatic surfaces are planned but not in the beta yet — the [Admin API roadmap](/admin-api/roadmap) tracks them: * **A downloadable CLI** to script the same operations and wire them into pipelines and infrastructure-as-code workflows. * **Gateway rules, custom rule engines, feature provisioning, reporting, and an admin audit log** over the Admin API. **A dedicated Terraform provider is not available.** Teams that manage infrastructure declaratively can drive the REST API from their own automation today; a first-class Terraform provider isn't something we offer yet. If declarative infrastructure-as-code is a requirement for you, tell your MCP Manager contact — it helps us prioritise. ## What you can automate today without the beta Two related capabilities ship to every workspace and don't require the Admin API entitlement. Both are about **connecting agents** to gateways, not about **provisioning** the gateways themselves. * **Token-based agent connection.** A headless agent connects to a gateway with a gateway API access token rather than an interactive sign-in. You create a token-based host and issue it a token scoped to a single gateway connection. See [API Tokens & Headless Agents](/features/api-tokens-and-headless-agents). * **Per-user identity passing.** A single agent can carry each end user's own identity through to downstream servers, so actions run as the real person and stay fully logged. See [Agents that Pass Identities to MCP Manager](/advanced/agents-passing-identities). These let an agent **use** a gateway programmatically. The **Admin API** above is what lets you **build and manage** your gateways, servers, and roles programmatically. **Two kinds of access token, for two different jobs.** A **gateway API access token** connects a headless agent to a gateway (the data plane). An **admin Personal Access Token** (`mcpm_pat_…`) authenticates to the Admin API to manage your configuration (the control plane). They are not interchangeable — see [Admin API & MCP](/admin-api/overview#admin-access-tokens-are-not-gateway-api-tokens). ## Working with log data programmatically Log data leaves MCP Manager in three ways. In the Admin API beta, the [`query_logs`](/admin-api/reference/logging) operation reads the **AI-usage call logs** with filters and pagination — useful for ad-hoc inspection from an agent. For durable pipelines into your own tooling, log data is also available as a **file export** (CSV or ND-JSON) and **pushed to your own collector over OpenTelemetry**. For a continuous feed into a SIEM, prefer OpenTelemetry over polling `query_logs`. See [Accessing log data programmatically](/features/viewing-logs#accessing-log-data-programmatically) and [Export to SIEM](/enterprise/export-to-siem). ## Further reading The control-plane API and MCP server for managing your workspace from code. How a headless agent connects to a gateway with a scoped API access token. One agent, many users, each acting as themselves through per-user tokens. The authorization flow every host uses to connect to a gateway. # SCIM Provisioning Source: https://docs.mcpmanager.ai/enterprise/scim How MCP Manager acts as a SCIM 2.0 service provider so your IdP automatically creates, updates, and deactivates users and syncs IdP groups to MCP Manager teams: connecting Okta or Entra ID with the base URL and bearer token, mapping groups to teams, the user lifecycle, supported SCIM operations, and troubleshooting plus FAQs on roles and settings-page visibility. SCIM provisioning lets your identity provider (IdP) — Okta, Microsoft Entra ID, or another SCIM 2.0-capable IdP (see [Supported identity providers](/enterprise/supported-identity-providers)) — automatically create, update, and deactivate users in MCP Manager, and keep IdP group membership in sync with MCP Manager teams. With SCIM configured, your directory is the source of truth: when someone joins, changes teams, or leaves in your IdP, MCP Manager reflects it without anyone editing users by hand. SCIM provisioning is enabled as part of guided onboarding — you do **not** turn it on from a settings screen. The MCP Manager team enables it for your workspace and gives you the connection details to paste into your IdP. To request SCIM, use the **Contact us** prompt on the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) or talk to your MCP Manager contact. ## How provisioning works in MCP Manager MCP Manager is the **SCIM service provider** (the target): your IdP pushes changes to a SCIM endpoint that MCP Manager hosts for your workspace, authenticating every request with a **bearer token** issued specifically for your workspace. We provide both the endpoint and the token during onboarding. SCIM provisioning is independent of, but complementary to, [single sign-on](/enterprise/sso). SSO controls how people **sign in**; SCIM controls how their **accounts and team membership** are created and kept current. Most enterprise customers enable both: SCIM provisions users and their teams ahead of time, and SSO signs them in. They are configured as **two separate applications** in your IdP — the OIDC application for sign-in and the SCIM application for provisioning — each with its own assignment list. ### How users and teams stay in sync After the initial setup, MCP Manager keeps users and team membership aligned with your IdP. The behaviors below define what happens through the user lifecycle. * **Your IdP is the source of truth.** Fields and team memberships that SCIM manages are owned by your IdP. In MCP Manager, those controls are locked, with the message: *"This value is managed by an external identity provider (SCIM). To change it, update the user in your IdP — the change will sync back into MCP Manager on the next push."* To change a managed value, change it in your IdP. * **SCIM takes precedence.** An administrator can still add a user to a SCIM-managed team manually — useful for granting immediate access — but the next push from your IdP reconciles membership to what the IdP says. Manual additions that the IdP does not reflect are overwritten on sync; team memberships an administrator created outside of SCIM (on teams not driven by a mapped group) are left untouched. * **Deactivation is a soft deactivation.** When your IdP deactivates a user (or sends a delete), MCP Manager marks the account inactive and ends its workspace membership — but retains the user record so your **audit history stays intact**: every logged action keeps pointing at the real person who performed it, even after they leave. Re-activating the user in your IdP restores the account and re-applies their group-derived team membership. * **Deprovisioning is scoped to your managed domain.** SCIM only manages the users your IdP provisions. Accounts that were added by other means — for example, a user on a different email domain, or a manually created account — are not deactivated by SCIM. Remove those manually if you no longer want them. * **SSO without SCIM still grants entry, not access.** A user on a registered SSO domain who signs in before SCIM provisions them is created just-in-time and can sign in, but receives no team-based access until SCIM (or an administrator) places them on a team. See [First sign-in and access](/enterprise/sso#first-sign-in-and-access). ## Connect your IdP Enabling SCIM is an assisted step. We turn it on for your workspace and issue the credentials; you connect your IdP. In your IdP, add a SCIM 2.0 application that authenticates with an OAuth bearer token. In Okta, the App Catalog entry **SCIM 2.0 Test App (OAuth Bearer Token)** works well; name it something recognizable such as `MCP Manager SCIM`. You can leave the sign-in defaults as they are — this application is used only for provisioning, not for login. The MCP Manager team enables SCIM for your workspace and provides your **SCIM base URL** and a **bearer token**. The token is delivered through a secure link that expires after a short window — with your SCIM application already created, you can paste it in as soon as it arrives. The bearer token grants full provisioning access to your workspace's users and groups. Treat it like a password: paste it directly into your IdP's provisioning settings, never store it in plaintext, and ask us to rotate it if it is ever exposed. In the application's **Provisioning** settings, configure the API integration with the **SCIM base URL** and **bearer token** we provided. While you are on the API integration settings, **uncheck Import Groups** (Okta enables it by default) — provisioning flows one direction, from your IdP into MCP Manager, and importing would pull MCP Manager's groups back into your IdP as app groups. Then run your IdP's **Test Connector Configuration**. A successful test confirms your IdP can reach MCP Manager and authenticate. Some IdPs (for example, JumpCloud) create a temporary **test user** — and sometimes a test group — during activation to verify the integration. When asked for a test user email, use a placeholder address such as `scim-test@yourcompany.com`, never a real employee's. After activation, a deactivated test user may remain visible in MCP Manager; it is safe to ignore or remove. Under the provisioning **To App** settings, enable **Create Users**, **Update User Attributes**, and **Deactivate Users**. These are the lifecycle actions MCP Manager honors. Save. With the connection tested and these actions enabled, your IdP can now create, update, and deactivate MCP Manager users. Set the SCIM application **not** to display to users (in Okta, *Do not display application icon to users*). It is a provisioning connector, not a sign-in tile — your users launch MCP Manager through the OIDC application or the [login page](https://app.mcpmanager.ai/login), as described in [SSO](/enterprise/sso). ## Assign users and map groups to teams Once the connector is live, you choose who gets provisioned and which groups become teams. Assigning and pushing happen in your IdP; mapping happens in MCP Manager. On the SCIM application's **Assignments** tab, assign the people who should exist in MCP Manager — usually by assigning whole groups rather than individuals. Each assigned user is created as an MCP Manager user. On the **Push Groups** tab, push the IdP groups you want represented in MCP Manager. Pushing a group sends its definition and membership to MCP Manager. Pushing groups is separate from importing groups — MCP Manager receives groups from your IdP; you do not import MCP Manager groups back into the IdP. Go to the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso). Groups your IdP has pushed appear in the mapping table. A notice highlights any groups that are not yet mapped to a team — pushing a group makes it visible, but it does not yet grant access. Select **Create matching teams in MCP Manager** to create one team per unmapped group automatically, matching on the group's name. Alternatively, map a group to a team that already exists to consolidate membership onto it. After mapping, members of that IdP group are placed on the corresponding team. Mapping is a manual step you repeat when you add new groups you want represented as teams — newly pushed groups are not turned into teams automatically. Provisioned **users** still arrive automatically; it is the group-to-team mapping that you confirm here. ## Supported SCIM operations MCP Manager implements the SCIM 2.0 protocol for users and groups, with the standard discovery endpoints (`ServiceProviderConfig`, `ResourceTypes`, and `Schemas`) so your IdP can negotiate capabilities automatically. The behaviors and limits below define what your IdP can rely on. * **Users.** Create, fetch, list, update (`PUT` and `PATCH`), and deactivate. Deactivating a user (`active: false` or a delete) is a soft deactivation, as described in [How users and teams stay in sync](#how-users-and-teams-stay-in-sync). Creating a user that already exists returns a conflict that points your IdP at the existing record, so retries are safe. * **Groups.** Create, fetch, list, update, and member add/remove. Group membership changes are translated into MCP Manager team membership through the mappings you configure. * **Filtering and paging.** List requests support a simple `attribute eq "value"` filter (for example, on `userName`). Results are paginated, and a single page returns at most **500** records; larger requests are clamped to that ceiling. * **Not supported.** Bulk operations, sorting, ETag concurrency control, password change, and complex filter expressions are not supported. Requests that depend on them are rejected. * **Isolation and security.** Each workspace's SCIM endpoint is authenticated by its own bearer token, the token is stored encrypted, and every request is scoped to that workspace — one workspace's token cannot read or change another workspace's users or groups. SCIM activity is logged for auditing. ## Troubleshooting Confirm the SCIM base URL and bearer token were pasted exactly as provided, with no trailing spaces, and that the token has not expired before you copied it. Because the token is delivered through a time-limited link, an expired or partially copied token is the most common cause. If the token is no longer available, ask us to rotate and reissue it. Provisioning creates users; teams grant access. Confirm you pushed the relevant groups and mapped them to teams on the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso). Until a user's group is mapped to a team, the user exists but has no gateway access. Newly pushed groups are not converted to teams automatically. Open the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) and select **Create matching teams in MCP Manager**, or map the group to an existing team. You repeat this when you push additional groups. Fields owned by SCIM are intentionally read-only in MCP Manager. Change the value in your IdP; it syncs back on the next push. The locked control shows a tooltip explaining this. Deactivation is a soft deactivation: the account is marked inactive and its workspace membership ends, but the user record is retained so audit history referencing that person stays intact (and a future re-hire restores cleanly). SCIM also only deprovisions users it manages — accounts on other domains or created manually must be removed by an administrator. ## Frequently asked questions No — SCIM provisions users and keeps their **team membership** in sync with your IdP groups, but it does not assign or sync a user's **role**. A SCIM-provisioned user is created with your workspace's default role; from there, [roles](/deployment/rbac-and-roles/overview) are assigned and changed by hand inside MCP Manager. There is no mapping today from an IdP group or attribute to an MCP Manager role. If you would like roles driven from your IdP as well, that is not available today, but we are open to it — talk to your MCP Manager contact about your requirements. Your role does not have the **Manage SSO/SCIM mapping** capability, which controls visibility and access to the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) — including the mapping table and the **Create matching teams in MCP Manager** action. Ask a workspace administrator to grant the capability to your role from [People](https://app.mcpmanager.ai/settings/people). Capabilities are assigned per role and are fully configurable, including on custom roles, so access depends on the capability rather than on any fixed role name. ## Further reading The searchable list of IdPs MCP Manager works with for SSO and SCIM. Sign your team in through your corporate identity provider. How team membership grants users access to gateways. The full reference of role capabilities, including SSO/SCIM mapping. ## External sources Okta's guide to configuring SCIM provisioning for an app. # Security & Compliance Source: https://docs.mcpmanager.ai/enterprise/security-and-compliance MCP Manager's public enterprise resource center — security certifications, compliance documents, IP ranges, DPA, NDA, and BAA. Everything your legal, security, and procurement teams need, without going through a sales process. Everything your legal, security, and procurement teams need is at [app.mcpmanager.ai/enterprise](https://app.mcpmanager.ai/enterprise) — no sales process required. MCP Manager is built by **Usercentrics**, the compliance and consent infrastructure company behind billions of data interactions every month. Over 100,000 B2B customers worldwide trust Usercentrics with their most sensitive data obligations. The company crossed \$120M ARR in October 2025 and is profitable — purpose-built for the long term and not dependent on the next funding round. That institutional foundation is what backs MCP Manager's security program. ## Security documentation The [security index](https://app.mcpmanager.ai/enterprise/security) is the access point for MCP Manager's compliance reports, live controls status, and related resources: | Document or resource | Section | | ----------------------------------------------------------------------- | ---------------------------- | | SOC 2 Type 2 + HIPAA report (2025) | Trust Center → Resources | | ISO 27001:2022 & ISO 27701:2019 certificates | Trust Center → Resources | | TISAX Level 3 assessment result | Trust Center → Resources | | Penetration test report | Trust Center → Resources | | Security questionnaires (CAIQ-Lite 4.0.3, VSA-CORE) | Trust Center → Resources | | ISMS policies (information security, BCM, risk, incident, cryptography) | Trust Center → Resources | | Live security controls status | Trust Center → Controls | | Subprocessor list (with change notifications) | Trust Center → Subprocessors | | Security update announcements (subscribable) | Trust Center → Updates | The gated documents above — including the SOC 2 Type II + HIPAA report — are obtained by submitting the request form in the [Usercentrics Trust Center](https://trust.usercentrics.com/) **Resources** section. ## IP ranges MCP Manager's static IP addresses are published at [app.mcpmanager.ai/enterprise/ip-ranges](https://app.mcpmanager.ai/enterprise/ip-ranges). Allowlist these at your firewall so a sensitive upstream accepts connections only from MCP Manager. A machine-readable version is available at [app.mcpmanager.ai/enterprise/ip-ranges.json](https://app.mcpmanager.ai/enterprise/ip-ranges.json) for automated firewall provisioning. See [Architecture & Trust](/mcp-gateway-concepts/architecture-and-trust) and [Hosting & Data Residency](/deployment/hosting-and-data-residency) for how egress IPs fit into the network-isolation model. ## Data Processing Agreement A pre-signed DPA is available at [app.mcpmanager.ai/enterprise/dpa](https://app.mcpmanager.ai/enterprise/dpa). If you require one, download, countersign, and return it to your MCP Manager contact. ## Non-Disclosure Agreement A pre-signed NDA is available at [app.mcpmanager.ai/enterprise/nda](https://app.mcpmanager.ai/enterprise/nda) for enterprise customers who require one before proceeding with evaluation or procurement. ## Business Associate Agreement (BAA) As a HIPAA-compliant platform, MCP Manager signs Business Associate Agreements (BAAs) with covered entities and business associates. We can provide our own or countersign yours. BAAs are available on select enterprise plans; contact your MCP Manager representative to set it up. ## Company information Legal entity names, DUNS numbers, and contact information are at [app.mcpmanager.ai/enterprise/company](https://app.mcpmanager.ai/enterprise/company). ## Further reading How the gateway path is encrypted, isolated, and hardened — including egress IPs. Where MCP Manager runs, what stays in your environment, and EU data residency. # Single Sign-On (SSO) Source: https://docs.mcpmanager.ai/enterprise/sso How MCP Manager brokers enterprise single sign-on through Auth0: how IdP federation and email-domain routing work, what you provide to connect Okta or Entra ID, just-in-time provisioning, and troubleshooting plus FAQs on sharing credentials securely, secret rotation, break-glass accounts, and social sign-in versus enterprise SSO. Single sign-on (SSO) lets your team sign in to MCP Manager with your existing corporate identity provider (IdP) — Okta, Microsoft Entra ID, or any OIDC-compatible IdP (see [Supported identity providers](/enterprise/supported-identity-providers)) — instead of an MCP Manager-specific credential. MCP Manager brokers enterprise SSO through **Auth0**: your IdP federates to MCP Manager's Auth0 tenant, and MCP Manager routes each sign-in based on the user's verified email domain. The result is one-click access for your people, central control in your IdP, and no separate password for anyone to manage. Setting up SSO is a guided, assisted process — you do **not** self-serve it from a settings screen. You provide a few values from your IdP, and the MCP Manager team completes the connection on our side. To request SSO for your workspace, use the **Contact us** prompt on the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) or talk to your MCP Manager contact. ## How single sign-on works in MCP Manager MCP Manager does not implement SAML or OIDC endpoints directly. Instead, it uses **Auth0** as the identity broker between your corporate IdP and the MCP Manager application. Your IdP is added to MCP Manager's Auth0 tenant as an enterprise connection, and sign-in is matched to your organization by **email domain**. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram participant U as 👤 User participant M as 🛡️ MCP Manager participant A as 🔌 Auth0 (MCP Manager) participant I as 🔌 Your IdP (Okta / Entra ID) U->>M: Enter work email at app.mcpmanager.ai/login M->>A: Recognize email domain, start OIDC flow A->>I: Federate to your enterprise connection I->>A: Authenticate user, return verified identity A->>M: Return ID token (email, name, subject) M->>U: Provision/sign in, land in the workspace ``` Three properties of this flow matter for planning: * **Domain-based routing.** MCP Manager decides whether to send a sign-in through your IdP by matching the part of the email address after the `@` against the domains you register for SSO. You tell us which domains route through your IdP (for example, `yourcompany.com`). * **Verified email required.** MCP Manager accepts a federated identity only when your IdP asserts that the email address is verified. A sign-in carrying an unverified email is rejected and returned to the login screen. This prevents anyone from claiming an account on your domain that they do not actually control. * **No forced SSO by default.** Enabling SSO for a domain does not, on its own, disable other sign-in methods for addresses outside that domain. Email addresses on domains you have **not** registered for SSO continue to use the standard email-code login. Use this deliberately to keep a break-glass account (see the [FAQ](#frequently-asked-questions)). ### How your team signs in End users never create or manage an MCP Manager password. They sign in one of two ways: by clicking the MCP Manager tile on your IdP dashboard (if you configure one — see [Add an MCP Manager tile to your IdP dashboard](/enterprise/idp-dashboard-tile)), or by entering their work email at [the login page](https://app.mcpmanager.ai/login), where MCP Manager recognizes the registered domain and routes them to your IdP to authenticate. ### First sign-in and access MCP Manager provisions an account the first time a user signs in through SSO — you do not have to pre-create accounts. * **Just-in-time account creation.** When a user on a registered domain signs in through your IdP and no MCP Manager account exists yet, MCP Manager creates one from the verified identity (email, first and last name) — even if the user was never provisioned through SCIM. * **Access still depends on teams.** A just-in-time account, on its own, grants no access to any gateway — access in MCP Manager comes from **team** membership. Until a user is placed on a team, they can sign in but will not see any gateways. * **SCIM is the durable path to team access.** Pair SSO with [SCIM provisioning](/enterprise/scim) so group membership flows from your IdP into MCP Manager teams automatically. ## What you provide Connecting your IdP is a short exchange: you create one application in your IdP and send us the connection details; we register your enterprise connection and confirm when it is live. 1. Configure your IdP for a new **OpenID Connect** connection (in Okta, an **OIDC — Web Application** app integration) and provide us with all of the following: 1. **Issuer URL** (ends with `/.well-known/openid-configuration`) 2. **Client ID** 3. **Client Secret** (the secret *value*, not its ID) 4. The **email domain(s)** that should sign in through your IdP (for example, all users with an email address `@yourcompany.com`) 2. Allow the following **sign-in redirect (callback) URLs** in your IdP application — paste them verbatim; the redirect URI must match exactly for the connection to succeed: 1. `https://login.usercentrics-sandbox.eu/login/callback` (for our test system) 2. `https://login.usercentrics.eu/login/callback` (for production) MCP Manager is a Usercentrics product — enterprise sign-in is brokered through Usercentrics. Copy the block below into a secure form of communication, fill it in, and send it to your MCP Manager contact: ```text SSO connection details theme={null} Issuer URL: Client ID: Client Secret: Email domain(s): ``` The Client Secret is a sensitive credential, and the choice of secure channel is yours. Some customers create a secure note in their password management system and share a time-limited access link just for this process. If a secret is ever exposed, rotate it in your IdP and send us the new value. Once the connection is live, a user on a registered domain who enters their work email at [the login page](https://app.mcpmanager.ai/login) is routed to your IdP to authenticate. ## Troubleshooting MCP Manager routes to your IdP only for email domains registered for SSO. If a user enters an address on an unregistered domain, they get the standard email-code flow. Confirm the user's email domain is one you registered with us, and that they entered the corporate address rather than a personal one. The most common cause is that the user is not assigned to the OIDC application in your IdP. Assign the user (or their group) to the OIDC application and have them try again. Remember that assignment to the OIDC application is separate from SCIM provisioning assignment — a user can be provisioned yet still be unable to sign in if they are not assigned to the OIDC application. MCP Manager only accepts a federated identity whose email your IdP marks as verified. Confirm the user's email is verified in your IdP. This guard is intentional and protects against accounts being claimed on your domain by someone who does not control the address. A just-in-time account has no team membership, and gateway access in MCP Manager comes from teams. Add the user to a team — automatically through SCIM group-to-team mapping or manually on the team — so they gain access. See [SCIM provisioning](/enterprise/scim) and [Teams](/deployment/teams). ## Frequently asked questions **Okta:** create an **OIDC — Web Application** app integration. The **Client ID** and **Client Secret** are on the application's **General** tab under *Client Credentials*. The **Issuer URL** is your Okta org domain followed by the discovery path: `https://yourcompany.okta.com/.well-known/openid-configuration`. **Microsoft Entra ID:** register a new app following [Microsoft's quickstart](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). The **Issuer URL** is listed under the **Endpoints** button in your app's overview page. Create a **Client Secret** under *Certificates & secrets* — and send us the secret **Value**, not the Secret ID. That choice is yours. If your security policy permits sending credentials by email, you can do that. We recommend a **one-time link** instead — a self-destructing secret link from your password manager or a one-time-secret service — so the value cannot be read again after we retrieve it. If a secret is ever exposed in transit, rotate it in your IdP and send us the new value. Create a new secret in your IdP and send the new value to us through a secure channel; we update your enterprise connection and confirm when it is in place. Sign-ins through your IdP fail from the moment the old secret expires until the new one is active, so if your IdP enforces secret expiry (Microsoft Entra ID does by default), plan the rotation with us ahead of the expiry date. Yes — we can route your email domain to a standard **Google sign-in** without any of the setup on this page. The difference from enterprise SSO is control: with Google sign-in, authentication happens against the user's Google account rather than your IdP, so it **cannot be enforced or centrally managed** by your IT team, and it does not pair with [SCIM provisioning](/enterprise/scim) for automatic team membership. Connecting your IdP as an enterprise connection gives you both. No. A social **“Sign in with Microsoft”** button authenticates personal Microsoft accounts and is **not** compatible with your organization's Microsoft Entra ID. If your organization uses Entra ID, connect it as an enterprise connection following the steps on this page — that is what routes your users through your directory, with your policies enforced. We request the default OpenID scopes: `openid profile email`. No custom scopes or extension claims are required. SSO handles **authentication** (who the user is), not **authorization** (what they can do). Access to gateways comes from [team membership](/deployment/teams), and permissions come from [roles](/deployment/rbac-and-roles/overview) — both managed in MCP Manager, or driven automatically from your IdP groups via [SCIM provisioning](/enterprise/scim). Blocking or removing a user in your IdP does stop them from signing in to MCP Manager. No — keep at least one administrator account outside SSO as a break-glass account. Because MCP Manager routes sign-in by email domain, any domain you do **not** register keeps the standard email-code login. If your IdP ever has an outage or a misconfiguration, an administrator account on an unregistered domain can still sign in and restore access; registering every domain removes this safety net. Your role does not have the **Manage SSO/SCIM mapping** capability, which controls visibility and access to the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) — without it, the page is hidden and navigating to it returns you to the People area. Ask a workspace administrator to grant the capability to your role from [People](https://app.mcpmanager.ai/settings/people). Only the settings page is gated; the end-user sign-in flow works for anyone on a registered domain. ## Further reading Let users launch MCP Manager with one click from your IdP dashboard. The searchable list of IdPs MCP Manager works with for SSO and SCIM. Automatically create users and sync IdP groups to MCP Manager teams. How team membership grants users access to gateways. How roles and capabilities govern what users can do. ## External sources Okta's guide to creating an OIDC Web Application integration. Microsoft's quickstart for registering an application and creating a client secret. # Supported Identity Providers Source: https://docs.mcpmanager.ai/enterprise/supported-identity-providers The identity providers MCP Manager works with for enterprise SSO and SCIM provisioning, in one searchable table. Because sign-in is standards-based OpenID Connect (OIDC) federated through Auth0 and provisioning is SCIM 2.0 (RFC 7643 and RFC 7644), any conformant IdP works — Okta, Microsoft Entra ID, Ping Identity, OneLogin, JumpCloud, Google Workspace, and many more. Each provider is marked for SSO (OIDC) and outbound SCIM 2.0 support. MCP Manager's enterprise identity features are **standards-based**, so compatibility is broad rather than a fixed list of certified vendors. Sign-in is [**OpenID Connect (OIDC)**](/enterprise/sso) federated through Auth0, and user provisioning is [**SCIM 2.0**](/enterprise/scim) — both open IETF/OpenID standards that virtually every modern identity provider (IdP) implements. The table below names the providers customers most often ask about and marks, for each, what MCP Manager supports today. **Short answer: if your IdP speaks OIDC and SCIM 2.0, MCP Manager works with it.** There is no per-vendor integration code on our side — we federate any OIDC provider and accept SCIM 2.0 from any conformant client. If you don't see your provider below, that almost certainly means we haven't listed it yet, **not** that it is unsupported. [Talk to us](https://app.mcpmanager.ai/settings/people/sso) and we'll confirm. ## How compatibility is determined Two independent standards decide whether a provider appears with a check mark, and they are worth separating because a provider can support one without the other. ### SSO — OpenID Connect (OIDC) MCP Manager brokers single sign-on through **Auth0**, which adds your IdP as an OIDC **enterprise connection** and routes each sign-in by verified email domain. Your IdP only has to do what any OpenID Connect identity provider does: * Expose a standard **OIDC** provider built on **OAuth 2.0 / OAuth 2.1**, ideally with an OpenID Provider Metadata document at `/.well-known/openid-configuration` for discovery. * Support the **Authorization Code flow with PKCE** ([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)) and return an **ID token** (a signed JWT) carrying the `email` and `email_verified` claims. That surface is near-universal, so the **SSO (OIDC)** column is a check mark for essentially every real identity provider. MCP Manager does not implement SAML endpoints — connections are OIDC. See [Single Sign-On (SSO)](/enterprise/sso) for the full setup. ### SCIM provisioning — SCIM 2.0 (outbound) MCP Manager is a **SCIM 2.0 service provider (the target)**, implementing the System for Cross-domain Identity Management protocol per [**RFC 7643**](https://datatracker.ietf.org/doc/html/rfc7643) (core schema) and [**RFC 7644**](https://datatracker.ietf.org/doc/html/rfc7644) (protocol), authenticated with an **OAuth 2.0 Bearer token** ([RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)). It exposes the standard `/ServiceProviderConfig`, `/ResourceTypes`, and `/Schemas` discovery endpoints, and reads group membership from both the `groups` attribute and the Enterprise User extension schema (`urn:ietf:params:scim:schemas:extension:enterprise:2.0:User`). The distinction that decides the **SCIM 2.0 provisioning** column is direction: * **Outbound SCIM (what MCP Manager needs).** Your IdP acts as a SCIM *client/source*, pushing create, update, and deactivate operations to MCP Manager's endpoint. Only providers that can do this earn a check mark. * **Inbound SCIM (not sufficient on its own).** Many platforms — especially developer-focused CIAM products — implement SCIM only as a *service provider* that receives provisioning from an upstream IdP. Being a SCIM target does not let a provider push to MCP Manager, so those providers are not marked for SCIM here. See [SCIM Provisioning](/enterprise/scim) for the supported operations, filtering, and paging limits. ## How to read the table **`✓`** means MCP Manager supports that capability with this provider. **`—`** means it is **not a documented path today** — not a claim that the provider is incompatible. Many `—` cells are simply combinations we have not yet validated or that depend on a provider edition; sign-in may still work even where provisioning is not listed. When in doubt, [contact us](https://app.mcpmanager.ai/settings/people/sso). ## Supported identity providers | Identity provider | SSO (OIDC) | SCIM 2.0 provisioning | Notes | | ---------------------------------------------- | :--------: | :-------------------: | ------------------------------------------------------------------------------------------------------------------------------------------- | | **Okta** (Workforce Identity) | ✓ | ✓ | Outbound SCIM requires the **Okta Lifecycle Management** add-on. Okta ships a *SCIM 2.0 Test App (OAuth Bearer Token)* reference connector. | | **Microsoft Entra ID** (Azure AD) | ✓ | ✓ | Provisioning to a non-gallery SCIM app requires **Entra ID P1** or higher. | | **Microsoft Entra External ID** | ✓ | ✓ | Uses the same Entra provisioning service as workforce Entra ID. | | **Google Workspace / Cloud Identity** | ✓ | — | OIDC sign-in is supported. Google's auto-provisioning is catalog-gated, so a generic custom SCIM endpoint is not a documented path. | | **Ping Identity — PingOne** | ✓ | ✓ | Configure a *SCIM Outbound* connection with OAuth 2 Bearer Token auth. | | **Ping Identity — PingFederate** | ✓ | ✓ | Enable outbound provisioning (the SCIM provisioner). | | **OneLogin** | ✓ | ✓ | SCIM provisioning is a paid capability on your OneLogin plan. | | **JumpCloud** | ✓ | ✓ | Use a *Custom SCIM* integration (base URL + token). | | **AWS IAM Identity Center** (formerly AWS SSO) | ✓ | — | Federates via OIDC/SAML, but it is a SCIM *target*, not an outbound source. | | **Amazon Cognito** | ✓ | — | Acts as an OIDC provider; no native outbound SCIM. | | **Auth0** (by Okta) | ✓ | — | OIDC provider; Auth0 supports **inbound** SCIM only. | | **IBM Security Verify** | ✓ | ✓ | Generic SCIM 2.0 custom-application connector with bearer auth. | | **Oracle Cloud Infrastructure IAM / IDCS** | ✓ | ✓ | Use the *Generic SCIM App Template*. | | **SailPoint Identity Security Cloud** | ✓ | ✓ | SCIM 2.0 outbound connector; SSO is typically via a paired IdP. | | **CyberArk Identity** | ✓ | ✓ | Outbound SCIM provisioning with a SCIM URL and access token. | | **ForgeRock / Ping (PingIDM)** | ✓ | ✓ | PingIDM SCIM connector, configured over REST. | | **SAP Cloud Identity Services (IAS + IPS)** | ✓ | ✓ | OIDC via IAS; outbound SCIM 2.0 via the Identity Provisioning Service. | | **Salesforce Identity** | ✓ | — | OIDC provider; Salesforce is a SCIM *target*. | | **Cisco Duo** | ✓ | ✓ | Generic OIDC relying party plus a generic SCIM 2.0 target. | | **Rippling** | ✓ | ✓ | Custom SAML + SCIM app integration. | | **Workday** | — | ✓ | SCIM requires **Workday Enterprise** with SSO enabled. Sign-in is typically SAML-based; confirm OIDC with us. | | **WSO2 Identity Server** | ✓ | ✓ | Outbound provisioning connector (SCIM 2.0). | | **Keycloak** | ✓ | — | OIDC provider; no built-in outbound SCIM client (community extensions only). | | **authentik** | ✓ | ✓ | Native SCIM provider (base URL + bearer token). | | **Zitadel** | ✓ | — | OIDC provider; an outbound SCIM client is in development. | | **Authelia** | ✓ | — | OIDC provider; no SCIM provisioning. | | **Gluu** | ✓ | — | Primarily a SCIM *server* (inbound). | | **Cloudflare Access (Zero Trust)** | ✓ | — | OIDC for apps; outbound SCIM to apps is in limited beta. | | **WorkOS** | ✓ | — | Directory Sync *receives* SCIM (target), and provides OIDC SSO. | | **Frontegg** | ✓ | — | OIDC provider; SCIM is inbound only. | | **FusionAuth** | ✓ | — | Implements SCIM as a server (inbound) only. | | **Stytch** | ✓ | — | B2B OIDC; SCIM target for upstream IdPs. | | **Clerk** | ✓ | — | OIDC provider; SCIM is inbound only. | | **Descope** | ✓ | — | OIDC provider; SCIM is inbound only. | | **miniOrange** | ✓ | ✓ | *SCIM server* app for outbound provisioning (base URL + bearer token). | | **LoginRadius** | ✓ | ✓ | Directory Sync supports outbound SCIM 2.0. | | **Beyond Identity** | ✓ | ✓ | Generic SCIM 2.0 registration for outbound provisioning. | | **Transmit Security (Mosaic)** | ✓ | ✓ | SCIM-based user lifecycle to downstream apps. | | **RSA Governance & Lifecycle** | ✓ | ✓ | SCIM connector for outbound provisioning. | | **Broadcom / Symantec VIP** | ✓ | ✓ | VIP Authentication Hub exposes SCIM 2.0 management APIs. | | **OpenText / NetIQ Identity Manager** | ✓ | ✓ | SCIM driver (Integration Module) for outbound provisioning. | | **Optimal IdM (OptimalCloud)** | ✓ | ✓ | SCIM 2.0 inbound and outbound. | | **HelloID (Tools4ever)** | ✓ | ✓ | Outbound SCIM availability varies by target connector. | | **Azure AD B2C** | ✓ | — | OIDC sign-in; no outbound app provisioning service. | | **Shibboleth IdP** | ✓ | — | OIDC via the OP plugin; no SCIM. | | **SimpleSAMLphp** | ✓ | — | OIDC OP module; no SCIM. | ## Edition and licensing notes A few providers gate **outbound SCIM** behind a specific edition or add-on. Where a row above is marked `✓` for SCIM, confirm your license covers provisioning before you plan a rollout: * **Okta** — outbound SCIM to a custom app requires the **Lifecycle Management** add-on. * **Microsoft Entra ID** — provisioning a non-gallery SCIM application requires **Entra ID P1** (or P2 / a bundle that includes it). * **OneLogin** — provisioning is a paid capability on the IdP plan. * **Workday** — SCIM is part of the **Enterprise** tier and requires SSO to be enabled first. SSO via OIDC generally carries no such gating — it is part of the base offering for nearly every provider listed. ## Don't see your provider? The table is a convenience, not a boundary. Because MCP Manager federates **any** OIDC provider and accepts SCIM 2.0 from **any** conformant client, a provider's absence here is not a statement that it won't work. If your IdP issues OIDC ID tokens with a verified email claim, you can use it for **SSO**. If it can push outbound **SCIM 2.0** with a bearer token, you can use it for **provisioning**. To confirm your specific provider and edition, use the **Contact us** prompt on the [SSO / SCIM settings page](https://app.mcpmanager.ai/settings/people/sso) or talk to your MCP Manager contact. ## Further reading How MCP Manager federates your OIDC identity provider through Auth0. Automatically create users and sync IdP groups to MCP Manager teams. The two-authentications model and how identity is brokered to servers. How team membership grants users access to gateways. ## External sources The OIDC specification behind MCP Manager's SSO federation. The SCIM protocol MCP Manager implements as a service provider. The SCIM resource schema for users and groups. The bearer-token scheme that authenticates SCIM requests. # Alerts Source: https://docs.mcpmanager.ai/features/alerts What the MCP Manager Alerts page surfaces — the error, warning, and info events it raises, what triggers each one, what an alert record contains, and how alerts differ from logs and reporting. The **Alerts** page in MCP Manager is the workspace-wide feed of notable events that an administrator should know about — authentication failures on a connected server, and the outcomes of policy rule engines configured on your gateways. Open it from the **Alerts** link in the left-hand navigation at [Alerts](https://app.mcpmanager.ai/settings/alerts). Each alert is a stored record you can open, read, and use to jump straight to the server, gateway, policy, or connection it concerns. If you don't see an **Alerts** link in your left-hand navigation, your role doesn't have the **See all alerts** capability. Access to the Alerts page — and whether the link appears at all — is controlled by that capability. Ask whoever manages roles in your workspace to grant it. See [Who can see alerts](#who-can-see-alerts). Alerts are **in-app only** today. MCP Manager does not yet send alerts by email, Slack, or webhook — there is no proactive notification. To learn about a new alert, open the Alerts page. Treat it as a feed you check, not a channel that pages you. ## How alerts differ from logs and reporting The Alerts page, [Viewing Logs](/features/viewing-logs), and [Reporting](/features/reporting) draw on the same gateway activity but answer different questions: * **Logs** capture *every* individual request and response passing through a gateway — the complete per-message record. * **Reporting** aggregates that activity into trend charts — what was called, by whom, how fast. * **Alerts** call out a small set of *notable events* — things that failed or that a policy flagged — that warrant an administrator's attention. An alert is the exception worth surfacing; a log is the full transcript. When an alert fires, the logs are where you find the surrounding request detail. ## What an alert record contains Every alert is a stored record with the same shape, designed so you can start diagnosing the problem from the alert alone: * **A subject and message** — a plain-language summary of what happened (for example, "Failed to refresh features for …"). * **A code** — a machine-readable identifier in the form `type.category.subcategory`, where `type` is one of `error`, `warning`, or `info`. The code groups the alert by severity and source. See [What triggers an alert](#what-triggers-an-alert). * **A debug context** — the technical detail an engineer would need to diagnose the cause. For a failed HTTP call this includes the response status code, the response headers, and the response body. Long values (headers and bodies) are truncated in the panel and offer a copy button for the full value. * **Related-resource links** — navigation shortcuts to the server, identity, gateway, gateway-server assignment, policy, rule engine, and connection involved (see [Navigating from an alert](#navigating-from-an-alert)). Alerts do **not** have a resolved state. An alert is a record that an event occurred; MCP Manager does not track whether you have acted on it, and there is no "mark as resolved" action. Old alerts remain in the list until removed. ## What triggers an alert MCP Manager raises an alert for the following events. Each row shows the alert's code and what causes it. **Warnings** come from gateway policy enforcement; **errors** come from connected-server failures. | Code | Severity | What triggers it | | ------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------- | | `warning.gateway.policy_triggered.tool_response` | Warning | A policy rule on a gateway flagged a tool response while enforcing your configured policy. | | `warning.gateway.rule_engine_misconfigured` | Warning | A policy references a rule engine whose URL is missing or invalid, so the engine cannot be reached. | | `warning.gateway.rule_engine_error_verdict` | Warning | A configured rule engine failed at request time (for example, returned an error or timed out). | | `error.inbound_server.oauth_callback_failed` | Error | The OAuth token exchange with a connected server failed during the authentication callback. | A few behaviors are worth calling out explicitly: * **A rule-engine error does not block the request.** When a rule engine fails at runtime (`warning.gateway.rule_engine_error_verdict`), MCP Manager records the alert but does **not** automatically block the call — the policy's own failure mode decides whether the request passes or is blocked. * **Engine alerts are deduplicated for 30 minutes.** To prevent a misconfigured or failing engine from flooding the feed, MCP Manager raises at most one `rule_engine_misconfigured` and one `rule_engine_error_verdict` alert per engine per **30-minute** window. Policy-trigger and OAuth-callback alerts are not deduplicated and are recorded each time they occur. * **Alerts are workspace-scoped.** The Alerts page shows alerts for the whole workspace (organization), not per-user. There are no on-page filters to scope by gateway, server, or user. ### Understanding rule-engine alert codes The two rule-engine warning codes represent fundamentally different states — one means the engine **failed to respond**, the other means it **responded and fired a rule**. They can look similar in the alert panel, especially when a `failure mode block` label appears alongside a block action. | Code | What it means | Was the engine reached? | | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | `warning.gateway.rule_engine_error_verdict` | The engine was **unreachable or errored**. No verdict was returned. The block or allow you see came from the rule's [failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails) configuration, not from the engine's decision. | **No** | | `warning.gateway.policy_triggered.tool_response` | The engine **was reached and returned a verdict** (pass, modify, or block). The outcome is the engine's actual decision. | **Yes** | The `failure mode block` label in the alert detail panel is a confirmation that the rule's failure mode is **configured to block** — it is not a signal that the engine evaluated the content and decided to block it. When you see this label on a `rule_engine_error_verdict` alert, it means the engine was down or errored and the failure mode closed the call. When you see it on a `policy_triggered` alert, the engine was healthy and the block was its verdict. **Practical test:** if you're investigating a block and aren't sure whether your rule engine actually fired or was simply unreachable, check the alert code first. `rule_engine_error_verdict` → diagnose connectivity or the engine's health. `policy_triggered.tool_response` → review the engine's policy configuration. ## Viewing and navigating alerts The Alerts page lists alerts in a paginated table, ordered by when they were created, with the most recent at the top. Each row shows the alert's subject and message and its creation date. Selecting a row opens a detail panel with the full message, the debug context, and the related-resource links. The table loads **50** alerts per page by default; you can switch the page size to **100** or **250**. Pagination preferences are remembered for this page. ### Navigating from an alert The alert detail panel links to the resources the alert concerns, so you can go straight from the event to the thing that caused it. Depending on the alert, the panel offers links to: * the **MCP server** involved, * the connected-server **identity** that failed, * the **gateway** that processed or blocked the call, * the **gateway-to-server assignment**, * the **policy** whose rule triggered (opens the gateway's rules), * the **rule engine** referenced (opens the [Rule Engines](https://app.mcpmanager.ai/settings/integrations) list, where you can find the engine by its ID — there is no per-engine deep link yet), and * the **connection** involved, along with the **user** who established it (shown by name and email). A link appears only when that resource is attached to the alert. Each alert also has its own URL, so you can share or bookmark a specific alert. ## Who can see alerts Access to the Alerts page is controlled by a single capability. Under the **Capabilities** tab when managing a role (in [People](https://app.mcpmanager.ai/settings/people)), the **Alerting** group contains one capability: | Capability | What it allows | | ------------------ | ---------------------------------------------------------- | | **See all alerts** | View all alerts in the workspace and open the Alerts page. | When a user's role has **See all alerts**, the **Alerts** link appears in their left-hand navigation and they can open every alert; when their role does not, the link is hidden and the page is unavailable to them. Capabilities are assigned per role and are fully configurable — including on any custom roles you create — so whether a given person has access depends on the capabilities granted to their role, not on any fixed role name. Because alert debug context can include request and response data, grant **See all alerts** only to the roles that should see that detail. ## Further reading The full per-request record behind an alert, with scoped views and export. Workspace-wide trend charts for activity, performance, and error rates. # Amazon Bedrock Source: https://docs.mcpmanager.ai/features/amazon-bedrock What AWS Bedrock Guardrails is and how to connect one to MCP Manager as a custom rule engine: the guardrail policy types, the model-agnostic ApplyGuardrail API integration, the ARN/version/Authorization setup, and the tier, pricing, and logging details to plan for. The **AWS Bedrock Guardrails** template connects an Amazon Bedrock guardrail as a [custom rule engine](/features/gateway-rules/custom-rules-engines) in MCP Manager. You create and tune the guardrail in AWS; MCP Manager calls Bedrock's `ApplyGuardrail` API on your behalf and translates the result into a pass / modify / block verdict on the tool message. Add it from **Rule Engines** → **Add** → **AWS Bedrock Guardrails**. This page summarizes Bedrock Guardrails to help you decide how to configure one for MCP Manager. AWS owns the feature and changes it often — treat the [AWS Bedrock Guardrails documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) and the [AWS Bedrock pricing page](https://aws.amazon.com/bedrock/pricing/) as the authoritative source for the current policy types, limits, tiers, and prices. ## What Bedrock Guardrails is Amazon Bedrock Guardrails is a **managed safety layer** that evaluates content against policies you define — on both the way in (prompts) and the way out (model responses). Its defining property for governance is that it enforces **deterministic controls that don't depend on a model cooperating**: unlike instructions embedded in a prompt, a guardrail's decision doesn't rely on the model's reasoning quality. (AWS publishes its own efficacy figures for how much harmful content guardrails catch; see the AWS product page for the current numbers and methodology.) ### Why MCP Manager uses the ApplyGuardrail API Bedrock's **`ApplyGuardrail` API evaluates content against a guardrail without invoking any foundation model** — standalone content moderation, decoupled from inference. That decoupling is what makes a guardrail a fit for a gateway: MCP Manager sends the **tool message text** from your MCP traffic to `ApplyGuardrail`, the guardrail applies its configured policies, and MCP Manager acts on the verdict. Two consequences are worth knowing: * **It's model-agnostic.** Because the guardrail evaluates text rather than running a model, the same guardrail you use elsewhere in Bedrock works here against MCP tool traffic — independent of which model your client ultimately talks to. * **It complements model-side guardrails rather than replacing them.** A guardrail attached to a model call protects that call; applying a guardrail at the MCP gateway protects the data flowing through your [connections](/features/viewing-logs). You can run both. ## The policies a guardrail can enforce AWS groups guardrail safeguards into several configurable policy types. You enable only the ones you want, and a guardrail must contain at least one policy plus the blocked-prompt and blocked-response messaging. The current set, per AWS: Detect and filter harmful text (and image) content across predefined categories such as Hate, Insults, Sexual, Violence, Misconduct, and Prompt Attack, with adjustable strength per category. Define topics that are off-limits for your application; content is blocked when one of those topics appears in a query or a response. Block specific words, phrases, and profanity on exact match — useful for competitor names, brand terms, or other disallowed vocabulary. Detect PII from a predefined list or from your own custom types defined with regular expressions, then redact or block it. Evaluate whether a response is grounded in the provided source material and relevant to the question, to reduce hallucinations. Mathematically verify natural-language content against policies you define, using formal logic. Per AWS, Automated Reasoning checks do **not** protect against prompt injection — they validate content as-is. AWS recommends pairing them with content filters. For the exact, current list of policy types and how to configure each, see [how Bedrock guardrails work](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-how.html) in the AWS docs. ## What you need from AWS Configure your guardrail in the Amazon Bedrock console first. Then you only need three things to connect it to MCP Manager: From the AWS console: **Bedrock → Guardrails → your guardrail → ARN**. It looks like `arn:aws:bedrock:us-east-1:000000000000:guardrail/abc123`. MCP Manager parses the **region** and **guardrail ID** out of it to build the endpoint URL automatically. The rule-engine form links to the [Bedrock guardrails console](https://console.aws.amazon.com/bedrock/home#/guardrails) if you still need to create one. A **numeric guardrail version** (for example `1`), or the literal `DRAFT` for the unpublished working copy. Pinning a number lets you publish new guardrail versions in AWS while controlling exactly which one MCP Manager uses. Under **Headers**, add a header named exactly `Authorization` with the value `Bearer ` — the word `Bearer`, a space, then a Bedrock API key. The form links directly into the AWS console to generate a long-term Bedrock API key for this credential. The **endpoint URL is built for you** from the ARN and version — you don't enter it. MCP Manager constructs `https://bedrock-runtime..amazonaws.com/guardrail//version//apply`, taking the region and guardrail ID from the ARN. The HTTP method is fixed to POST. ## How it behaves as a rule Once saved, the Bedrock engine appears in the **Detection method** dropdown on any gateway rule. On the rule's [detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires), MCP Manager forwards the tool message to your guardrail through `ApplyGuardrail` and acts on the result. As with every [custom engine](/features/gateway-rules/custom-rules-engines), there is **no action picker** — the guardrail's decision drives whether the message passes, is modified, or is blocked — and the rule's [failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails) defaults to **Block** if Bedrock is unreachable or errors. You can [test](/features/gateway-rules/custom-rules-engines#testing-an-engine) the engine with sample text before attaching it to a gateway. ## Cost, tiers, and logging to plan for A few AWS-side operational details affect how you configure and budget a guardrail. **All of these are AWS behaviors and can change — confirm the specifics against AWS before relying on them.** ### Safeguard tiers Content filters and denied topics can each run in a **Classic** or **Standard** tier, and you can mix tiers within one guardrail. The Standard tier adds stronger contextual understanding (including robustness to typos and variations), better prompt-attack defense, and support for many more languages. Using the Standard tier requires opting in to **cross-region inference** for Bedrock Guardrails. See the [AWS safeguard-tiers announcement](https://aws.amazon.com/blogs/machine-learning/tailor-responsible-ai-with-new-safeguard-tiers-in-amazon-bedrock-guardrails/). ### Pricing Bedrock Guardrails is billed **per policy, per text unit**, and you pay only for the policies you enable. A **text unit holds up to 1,000 characters**, so a longer message counts as several text units. Two billing nuances matter for a gateway that evaluates traffic inline: * Evaluation is charged **even when content is blocked** — a block doesn't make the check free. * In MCP Manager's standalone `ApplyGuardrail` use there is no model inference to pay for, so a blocked input costs only the guardrail evaluation. Rates differ by policy and change over time, so use the [AWS Bedrock pricing page](https://aws.amazon.com/bedrock/pricing/) — which includes worked examples — as the source of truth before estimating cost. These charges are billed by **AWS**, not by MCP Manager. As long as your plan includes custom rule engines, MCP Manager does **not** meter or charge per call for routing MCP traffic to a Bedrock guardrail — you pay AWS directly, under Bedrock's own pricing, for the calls your guardrail evaluates. ### Logging and encryption If you enable Bedrock **model invocation logs**, blocked content can be stored **in plain text** in those logs. If that's a concern for your compliance or regulatory posture, disable invocation logging or scope it carefully. This is an AWS-side setting, separate from MCP Manager's own [logs](/features/viewing-logs). Guardrails are encrypted with an AWS-managed key by default, and you can supply your own customer-managed KMS key instead. See AWS for the details. ## Further reading A security-first custom rule engine for prompt injection, jailbreaks, and PII. How custom engines are added, tested, and applied to gateway rules. Detection methods, hooks, failure modes, actions, and rule ordering. The built-in PII detection method, complementary to a Bedrock guardrail. ## External sources # API Tokens & Headless Agents Source: https://docs.mcpmanager.ai/features/api-tokens-and-headless-agents How headless agents connect to MCP Manager with API access tokens: how token-based hosts differ from headed OAuth apps, creating a token-based host and generating, copying, and revoking an API access token scoped to a gateway connection, managing connections, and the break-glass toggles that disable a host, connection, or identity instantly. Interactive apps like Claude connect to a gateway through OAuth and appear automatically. **Headless agents** — code with no human at a browser — connect with an **API access token** instead. This page covers how to create a token-based host for an agent, generate and revoke its tokens, manage its connections, and cut access instantly with break-glass controls. For a hands-on, end-to-end walkthrough — create a host, issue a token, and call a tool over HTTP — see [Run a headless agent with an API token](/tutorials/headless-agent). For the advanced pattern where one agent carries each end user's *own* identity through to downstream servers, see [Agents that Pass Identities to MCP Manager](/advanced/agents-passing-identities). Creating token-based hosts and generating tokens is gated by the **Create and manage API tokens** capability; cutting access uses **Disable and enable connections** and **Disable and enable hosts**. If you don't see these controls, your role doesn't have the capability — access depends on the capability, not on any fixed role name. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). The **gateway API access token** on this page connects an agent to a gateway to *use* its servers (the data plane). It is different from an **admin Personal Access Token** (`mcpm_pat_…`), which authenticates to the [Admin API](/admin-api/overview) to *manage* your MCP Manager configuration (the control plane). Use a gateway token to call tools; use an admin token to provision servers, gateways, and roles. ## Headed apps versus headless agents A client is tracked in MCP Manager as a [host](/mcp-gateway-concepts/apps-and-agents), and how it connects determines how you set it up: * **Headed apps (OAuth).** Interactive clients connect through an OAuth flow and **appear automatically** the first time someone connects one — nothing to register in advance, and the connecting user's identity rides in their OAuth token. * **Headless agents (token-based).** An agent with no interactive sign-in connects with an **API access token**. You create a **token-based host** to represent the agent and generate a token for it to present on each call. ## Create a token-based host and generate a token In [Apps & Agents](https://app.mcpmanager.ai/settings/hosts), create a **token-based host** and name it for the agent (for example, "Feedback bot"). You do this once, as an administrator. Generate an API access token for the host and select the gateway it should reach. You're taken through the **same authorization flow** as any connection — confirming the gateway and bringing an identity for each per-user server (see [Connection Experience](/features/connection-experience)). On completion MCP Manager issues the **API access token**. Copy or download it **now** — it is shown once — and place it in the agent's secret store. The token is scoped to that **host and its connection to the chosen gateway**, so it only reaches the servers that gateway exposes. ## Revoking and rotating tokens Revocation is immediate. Revoke a token by deleting it or disabling its host; to **rotate**, generate a new token and update the agent, then remove the old one. Because the token is bound to a specific host and gateway connection, revoking it stops only that agent's access — nothing else is affected. ## Managing connections Each **connection** is the intersection of a specific host, a specific gateway, and a specific user. From a gateway's **Connections** tab you can see every connection and **disable or enable** any one of them, cutting or restoring that single link without touching the host's other connections. ## Break-glass: cut access instantly Every layer of a connection carries an `enabled` toggle that is checked on **every request, with no caching**, so disabling one takes effect at once and nothing is deleted in the meantime: * Disable a **host** to block an entire app or agent. * Disable a **connection** to sever one host-to-gateway link. * Disable an **identity**, a **server**, or a whole **gateway** to stop traffic at that scope. Re-enabling restores access immediately. This is the control you reach for during an incident, an offboarding, or when a vetted agent starts misbehaving — one toggle, effective on the next call. ## Carrying each user's identity through an agent A single token-based host can serve **many** end users while still using **each user's own downstream credential**: every user enrolls once and brings their identity, MCP Manager mints them a per-user token, and the agent presents the right user's token on each call so the downstream server acts as the real person — fully governed and logged. This advanced pattern, including the runtime sequence, is documented in [Agents that Pass Identities to MCP Manager](/advanced/agents-passing-identities). ## Further reading How clients are tracked as hosts and how administrators allow or disable them. One agent, many users, each acting as themselves through per-user tokens. The shared authorization flow that token-based hosts use to connect. How credentials are stored, refreshed, and revoked behind every token. # Connection Experience Source: https://docs.mcpmanager.ai/features/connection-experience What an end user experiences when connecting a gateway in MCP Manager: adding the gateway URL in a client like Claude, the tab that opens back to MCP Manager to authorize, how only apps administrators allow can connect, how the flow guides you server by server so you end fully connected, how identities for SaaS apps and custom MCP servers are connected once and reused to cut clicks, and how token-based hosts follow the same flow. Connecting to a gateway is the first thing an end user does in **MCP Manager**, and it's designed to be a short, guided round trip: you paste one URL into your AI client, the client sends you to MCP Manager to authorize, you bring an identity for each server that needs one, and you land back in your client with the tools ready. This page describes that experience from the user's chair. You can only connect gateways your [team](/deployment/teams) membership grants you, and establishing an OAuth connection is gated by the **Authenticate via OAuth** capability. If a gateway you expect isn't offered, your team doesn't have it or your role lacks the capability — access depends on the capability and team, not on any fixed role name. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## The tab that opens back to MCP Manager Say you're connecting in Claude. You add the gateway's URL as a custom connector; because the gateway requires authorization, Claude can't use it immediately and instead **opens a browser tab to MCP Manager** so you can authorize. (Every client does the equivalent — the gateway responds that authorization is needed, and the client hands you off to MCP Manager.) You land on the MCP Manager authorization screen, signing in through your organization's [SSO](/enterprise/sso) if you aren't already. From here, MCP Manager walks you through everything needed to connect, and at the end it sends you straight back to your client. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram actor U as You participant C as 🤖 AI client (e.g. Claude) participant M as 🛡️ MCP Manager U->>C: Add the gateway URL as a connector C->>M: Connect — gateway needs authorization C->>U: Opens a tab to MCP Manager Note over M: Confirms the gateway ·
validates the app is allowed loop For each server that needs your identity M->>U: Bring an identity (authorize once, or pick a saved one) end U->>M: Allow M-->>C: Connected — the tab closes C->>U: Tools are ready ``` ## MCP Manager confirms which gateway you're connecting The authorization screen already knows which gateway you're connecting — it's carried in the URL your client used — so it selects that gateway for you. If you connected with a general "picker" URL instead of a gateway-specific one, you choose from the gateways your team membership grants. (The two URL modes are covered in [Gateway Deployment Strategies](/deployment/gateway-deployment-strategies#how-users-connect-two-url-modes).) ## Only the apps your administrators allow can connect MCP Manager validates the **app or agent** you're connecting from — what it calls a [host](/mcp-gateway-concepts/apps-and-agents). Administrators decide which apps are allowed, and if an app has been **disabled**, any attempt to connect or call through it is rejected before it reaches a server. This is how an organization standardizes on some clients and not others — for example, allowing Claude while blocking ChatGPT. The check isn't a one-time gate at sign-up: a host's allowed status is enforced on **every request**, so an administrator can cut off an app at any moment and the change takes effect immediately, without deleting anything. The practical result for you is simple — you can connect through the apps your administrators permit, and only those. ## It guides you through each server A gateway usually bundles several MCP servers, and MCP Manager steps you through exactly what each one needs — no more, no less: * **Servers set to a shared identity** need nothing from you. The administrator already attached a service-account credential, so they're ready the moment you connect and you're never prompted. * **Servers set to per-user identity** ask you to bring your own identity for that server (see [Identity Controls](/features/identity-controls)). You're prompted once per such server, with a progress indicator showing where you are. * **A bundled server *container*** also asks you to pick which server instance you're connecting to before you choose an identity. You can't finish the flow until every server that requires something from you has it, so by the time the screen lets you complete, you're **fully connected** — there's no ambiguous half-connected state where some tools silently fail later. ## Connect once, reuse everywhere The clicks happen mostly the **first** time. When you bring an identity for a SaaS app (Notion, GitHub, Jira) or a custom MCP server, you authorize it once — through that provider's OAuth consent screen, or by providing a token — and MCP Manager saves the resulting **identity** for you. After that, the identity is offered as a ready-to-pick option whenever you connect a gateway that includes the same server, so you select it instead of authorizing again. MCP Manager refreshes the underlying OAuth tokens automatically, so a saved identity keeps working without you re-authenticating each session. In practice, your second and later connections to the same servers are nearly click-free — the flow only stops to ask about servers you haven't connected before. Occasionally a provider expires an authorization on a fixed schedule (roughly every 90 days for some, such as Atlassian); when that happens the saved identity needs a one-time re-authentication, covered in [Fix a broken connection](/advanced/fixing-broken-connections). ## When a saved identity is broken Reuse assumes the saved identity still works. If one has broken — its credential was rejected (its status is [Needs authentication or Disconnected](/security/authentication-and-identity#identity-authentication-statuses)), or an administrator disabled it — MCP Manager **won't let you pick it** for a new connection: in the identity list for that server it appears **greyed out and non-selectable**, with a tooltip explaining why and what to do. It never silently connects you with a credential it knows is dead, and it won't auto-select one either. You have two ways forward, without the flow getting stuck: * **Bring a new identity** for that server, the same way you did the first time; or * **Authenticate again** on the broken identity from its page in [Settings](https://app.mcpmanager.ai/settings/servers) — which re-runs the provider authorization and moves your existing gateways and connections onto the repaired identity automatically. See [Fix a broken connection](/advanced/fixing-broken-connections). This non-selectable treatment is specific to the connection flow — elsewhere in Settings a disabled identity stays visible and selectable so an administrator can manage it. ## When you're done Once every required server has an identity, you complete the authorization, MCP Manager hands you back to your client, and **the tab closes on its own**. Your client finishes connecting and the gateway's tools appear, ready to use — each call from then on running under the identity you brought and recorded in your [logs](/features/viewing-logs). ## The same flow for token-based hosts Headless agents that connect with an API token instead of OAuth — see [Agents that Pass Identities to MCP Manager](/advanced/agents-passing-identities) — enroll through the **same authorization screen** described here. A user still opens the connection, confirms the gateway, and brings an identity for each per-user server in the same guided, reusable way. The only difference comes at the end: instead of an automatic OAuth handoff back to an app, MCP Manager issues an **API access token** for the agent to use. Everything about confirming the gateway, validating the host, and stepping through identities is identical. For a hands-on walkthrough of issuing and using such a token, see [Run a headless agent with an API token](/tutorials/headless-agent). ## Further reading Choose exactly which tools, prompts, and resources each gateway exposes. The per-server choice between your own identity and a shared service account. How clients are tracked as hosts and how administrators allow or disable them. The token-based host flow for headless agents, end to end. Picker versus locked connection URLs, and how to package gateways for your teams. The hands-on version of this flow: paste the URL, authorize, and use the tools. # Feature Provisioning Source: https://docs.mcpmanager.ai/features/feature-provisioning How to provision which MCP features a gateway exposes in MCP Manager: for each server, choose to allow all, allow only those that match conditions, or block all tools (and the same for resources and prompts), preview a server's live tools using an identity, pin a tool by its name, title, or description so unreviewed changes stop passing the gateway, and filter or gate tools by their MCP annotations — read-only, destructive, idempotent, and open world. When you add a server to a [gateway](/mcp-gateway-concepts/mcp-gateways), you decide exactly which of its tools, prompts, and resources the gateway exposes to clients. That decision is **feature provisioning**, and it's how you apply least privilege in practice — exposing the handful of tools a job actually needs instead of a server's entire surface. The filtering is enforced entirely by the gateway, so it works for **every** server — including one whose vendor offers no tool-level enable/disable controls of its own. What you can block is never limited by the upstream server's settings. This page is the how-to; for why it matters and the security model behind it (including the defense against tool poisoning and rug pulls), see [Feature Governance](/security/feature-governance). For a guided walkthrough that provisions a trimmed toolset on a new gateway, see [Build a team gateway](/tutorials/team-gateway). Provisioning features on a server is gated by the **Manage feature provisioning settings** capability. If you don't see provisioning controls on a server within a gateway, your role doesn't have it — capabilities are assigned per role and fully configurable, so access depends on the capability, not on any fixed role name. See the [capabilities reference](/deployment/rbac-and-roles/capabilities). ## The three provisioning modes For each server on a gateway, and **independently for tools, prompts, and resources**, you pick one of three schemes from a dropdown: * **Allowing all** (shown as *Allowing all tools*, *Allowing all resources*, or *Allowing all prompts*) — every capability of that type passes through. This is the scheme a server gets when you first assign it to a gateway. * **Allow if conditions are met** — only capabilities matching an explicit allowlist pass; everything else is hidden and uncallable. This is least privilege in practice. * **Blocking all** (shown as *Blocking all tools*, and so on) — no capability of that type is exposed. A gateway starts out with no servers, so it exposes nothing. When you **assign** a server, each of its feature types defaults to **Allowing all** — for a simple setup, every tool, prompt, and resource the server offers passes straight through, so you aren't forced to curate before the gateway is useful. Narrowing a type is a deliberate switch, and that switch is **fail-closed by design**: change a type to **Allow if conditions are met** and it exposes **zero** capabilities until you add allowlist entries. Anything you haven't explicitly allowed — including a tool the server adds or renames later — stays hidden and uncallable. MCP Manager filters by allowlist only: there is no "allow everything except these" denylist mode, and that's deliberate — see [Why an allowlist, not a denylist](/security/feature-governance#why-an-allowlist-not-a-denylist) for the reasoning. ## Preview a server's tools with an identity To choose which tools to allow, MCP Manager shows you the server's **live feature list**. Because a server can return a different set of tools to different identities, you first pick an **identity to preview against** — your own, or a shared one. MCP Manager fetches the tools that identity can see, so you select from the real, current list rather than guessing. (Picking the identity to preview is also where you set the server's [identity scheme](/features/identity-controls) — per-user or shared.) ## Provision the tools you want From [Gateways](https://app.mcpmanager.ai/settings/gateways), open a gateway and select the assigned server you want to provision. Choose the identity scheme and select an identity to preview the server's live tools, so the allowlist is built from the actual current capabilities. Set the tools scheme to **Allow if conditions are met**. The moment you switch away from **Allowing all**, the list drops to zero exposed tools — nothing passes until you add an entry. Browse the previewed tool list and add the specific tools to expose. Each one you add becomes an allowlist entry. For each added tool, choose which of its fields the gateway must match to let it through — its **name**, **title**, and/or **description**. See [Pinning a tool by its metadata](#pinning-a-tool-by-its-metadata) below. Set the prompts and resources schemes to **Blocking all** (or **Allow if conditions are met**) depending on what this gateway needs, then save. ## Pinning a tool by its metadata Each allowed tool is admitted only if it matches the fields you chose, exactly. How tightly you pin is a deliberate trade-off: * **Match on name only** to tolerate the vendor improving a tool's description over time. * **Match on name and description** to freeze exactly the wording you reviewed — so if the description later changes, the tool **no longer matches and is dropped** rather than reaching the model with new, unreviewed text. That second option is the control that neutralizes **rug pulls** and **tool poisoning**: you approve a specific version of a tool's metadata, and anything that doesn't match what you approved stops passing the gateway. The full reasoning is in [Feature Governance](/security/feature-governance#a-defense-against-tool-poisoning-and-rug-pulls). ## Filter and gate by tool type MCP tools can carry **annotations** — behavioral hints the upstream server attaches to a tool, such as whether it only reads data or might delete it. MCP Manager surfaces those hints as a tool's **tool type** and lets you use them in two ways: to **filter** the lists you provision from, and to **gate** an allowlist condition. Every tool's annotations appear in a **Tool type** column on the Available, Provisioned, and Conditions tabs. Each reported annotation shows as an icon — its **-off** variant when the value is `false` — and annotations the server didn't report are left out. Hover any icon for a tooltip stating that annotation's value, such as *Read-only is true*. The four hints are independent — a tool can be read-only and open-world at once — so each renders on its own. Tool type comes from the MCP server and is **not validated by MCP Manager** — the filter dropdown says exactly that. Treat it as a way to triage and organize tools, **not** as a security control. Read [What each annotation means, and how far to trust it](#what-each-annotation-means-and-how-far-to-trust-it) before you depend on it. ### Filter the list by tool type On the **Available**, **Provisioned**, and **Conditions** tabs, open **Filter by tool type** to narrow which rows are shown. The dropdown has one section per annotation — **Read-only**, **Destructive**, **Idempotent**, and **Open world** — and within each you can check **is true**, **is false**, and/or **is unknown**. **Select all** and **Deselect all** toggle every box at once. * The checks are combined with **OR**, across every section: a row is shown if it matches **any** box you've checked. With nothing checked, everything is shown — the trigger reads "Filter by tool type"; check one box and it reads "Filtering by 1 annotation", more than one and it reads "Filtering by *N* annotations". * **is unknown** matches tools whose server didn't report that annotation — a state distinct from `true` or `false`. * The same filter sits on all three tabs and shares one selection: it narrows the **Available** and **Provisioned** lists of tools, and on the **Conditions** tab it narrows the allowlist rules to those that gate on the annotations you've checked. * The filter composes with the search box, so you can stack a *Destructive is true* filter on top of a name search to scan, say, every delete-style tool on a large server. * Filtering applies to **tools only**. Prompts and resources don't carry these hints, so the control doesn't appear for them. Filtering only changes what you see while provisioning; on its own it doesn't change what the gateway exposes. To make a tool-type rule actually gate traffic, add it to the allowlist as a condition. ### Gate an allowlist on tool type A **condition** is an allowlist entry that admits tools by a rule rather than by naming a single tool — it lives on the **Conditions** tab next to the tools you've pinned by name. When you add one, you can constrain it on annotations alongside name, title, and description. Each annotation offers **is true**, **is false**, **is unknown**, or **No value** — and **No value** is the default, meaning "don't constrain on this annotation." A single condition combines its constraints with **AND**: a tool matches only if it satisfies *every* field you set. So *Read-only is true* on its own admits every tool the server marks read-only, without your naming each one; add *Open world is false* to the same condition and it narrows to tools that are both read-only **and** closed-world. The allowlist as a whole combines its entries with **OR**: a tool is exposed if it matches **any** entry — any tool pinned by name, or any condition. That lets you pair a broad rule ("all read-only tools") with specific pins ("plus these three write tools I reviewed") on the same server. The filter and a condition use opposite logic by design: the filter casts a wide net with **OR** so you can eyeball several types at once, while a condition tightens with **AND** so one rule can be precise. **is unknown** in a condition gates to tools whose server didn't report that hint — a way to single out or quarantine unannotated tools, distinct from `true` or `false`. Identical conditions are de-duplicated, so adding the same rule twice is a no-op. However you build it, a condition is enforced like any other allowlist entry: non-matching tools are filtered out of what clients see, and a direct call to one is blocked — see [What clients see, and how it's logged](#what-clients-see-and-how-its-logged). A tool-type condition gates on what the server **says about itself**, so it is only as trustworthy as that server. Never let *Read-only is true* be your sole defense against a destructive tool from a server you don't control — pair it with name and description [pinning](#pinning-a-tool-by-its-metadata). ### What each annotation means, and how far to trust it The four annotations come straight from the [Model Context Protocol tool specification](https://modelcontextprotocol.io/specification/2025-06-18/server/tools). When a server sets one to `true`, it means: | Annotation | What `true` asserts | MCP field | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | **Read-only** | The tool does not modify its environment. | `readOnlyHint` | | **Destructive** | The tool may perform destructive updates to its environment; `false` means it performs only additive updates. Meaningful only when the tool is *not* read-only. | `destructiveHint` | | **Idempotent** | Calling the tool repeatedly with the same arguments has no additional effect beyond the first call. | `idempotentHint` | | **Open world** | The tool may interact with an "open world" of external entities — a web-search tool, say; `false` means its domain is closed, like a memory tool. | `openWorldHint` | A tool shows **unknown** for any annotation its server didn't report. MCP Manager surfaces that gap honestly rather than guessing — which is worth knowing, because it differs from the MCP spec's own defaults: the spec assumes an *unannotated* tool is the riskier case (not read-only, potentially destructive, not idempotent, and open-world). An "unknown" in MCP Manager is therefore an absence of information, not a claim of safety. **Annotations are hints, not guarantees — and not a security boundary.** The MCP specification is explicit that these properties "are not guaranteed to provide a faithful description of tool behavior," and that a client "should never make tool use decisions based on annotations received from untrusted servers." A malicious or buggy server can advertise `readOnlyHint: true` on a tool that quietly deletes data; MCP Manager passes the values through unvalidated and labels them as such. So rely on tool type to **triage and organize** — surfacing likely-destructive tools for review, or trimming a long list to the read-only ones. For an *enforceable* control, rely on [name and description pinning](#pinning-a-tool-by-its-metadata) and the [feature-governance model](/security/feature-governance), which gate on the metadata you actually reviewed rather than on what the server asserts about itself. ## Turn off prompts and resources you don't need Tools aren't the only feature type a server can expose. If a gateway doesn't need a server's prompts or resources, set those types to **Block all** to remove them entirely — fewer features means less context, lower cost, and a smaller surface. ## What clients see, and how it's logged Provisioning changes take effect **immediately**. A change to a server's scheme or allowlist is enforced on the next request from any connected client — there's no separate publish or deploy step, and no staged draft that batches changes for later release. Remove a capability and it stops passing at once; add one and it becomes reachable right away. Clients connecting to the gateway see **one unified, filtered toolset**, each tool namespaced by its server — only the capabilities you provisioned. Provisioning decisions are recorded in your [logs](/features/viewing-logs): a capability removed from a list because it didn't match the allowlist is logged as `gateway_feature_filtered`, and a direct call to a disallowed capability is logged as `gateway_feature_blocked`. That lets you confirm what a gateway exposes and catch the moment a previously-passing tool stops matching — for example, when an upstream server renames or rewrites it. ## Further reading The security model behind provisioning — least privilege, and the defense against tool poisoning and rug pulls. How a gateway presents one filtered, namespaced toolset across many servers. Choosing the per-server identity you preview and provision against. The `gateway_feature_filtered` and `gateway_feature_blocked` log types. # Custom Rule Engines Source: https://docs.mcpmanager.ai/features/gateway-rules/custom-rules-engines How to add and manage custom rule engines in MCP Manager: the Rule Engines section, provider choices, endpoint/method/header configuration, HTTPS-only and private-IP rejection, header forwarding, IP allowlisting, testing, and deletion rules. A **custom rule engine** lets you use an external service as a [gateway rule](/features/gateway-rules/overview) detection method in MCP Manager. Instead of matching patterns or running Presidio in-process, the gateway calls out to a webhook — your own service, an [AWS Bedrock guardrail](/features/amazon-bedrock), or [Lakera Guard](/features/lakera-guard) — and that service decides whether to pass, modify, or block the message. You register engines once in the **Rule Engines** section, then select them as a detection method on any gateway rule. MCP Manager does **not** meter or charge per call for custom rule engines. As long as your plan includes custom rule engines, the calls the gateway makes to an engine are not metered or billed per call by MCP Manager. Any usage cost is billed by the engine's own provider — for example [AWS](/features/amazon-bedrock) or [Lakera](/features/lakera-guard) — under their pricing; an engine you run yourself carries no such per-call charge. The **Rule Engines** section, and the ability to add, edit, or remove engines, are controlled by the **Manage integrations** capability ("Configure, edit, and remove integrations such as rule engines, including custom providers and built-in engines"). If you don't see **Rule Engines** in your left-hand navigation, your role doesn't have it. Capabilities are assigned per role and are fully configurable, including on custom roles, so access depends on the capability granted to your role, not on any fixed role name. ## Adding a rule engine Open **Rule Engines** in the left-hand navigation and click **Add**. You first choose a **provider**: | Provider | Use it when | Setup guide | | -------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | | **Custom** | You run your own webhook that speaks MCP Manager's rule-engine contract. | [Building a Custom Rule Engine](/advanced/building-a-custom-rule-engine) | | **AWS Bedrock Guardrails** | You want to apply an Amazon Bedrock guardrail; MCP Manager builds the request for you. | [Amazon Bedrock](/features/amazon-bedrock) | | **Lakera Guard** | You want to use Lakera's hosted guardrail. | [Lakera Guard](/features/lakera-guard) | All three providers share a common set of fields; the provider you pick determines which fields are editable and which MCP Manager fills in for you. Running the **Custom** provider? See the full webhook contract — the request envelope, the four response verdicts, TypeScript types, and a working Express example. ## Configuration fields * **Name** — a label for the engine (up to 120 characters). It appears in the detection-method dropdown and alongside the rule wherever the engine is applied. * **Description** — a longer description, shown next to the rule when the engine is applied to a gateway. * **Endpoint URL** — the webhook MCP Manager calls (up to 256 characters). For the **Custom** provider you type this directly; for **AWS Bedrock** it is built automatically from the guardrail ARN and version; for **Lakera Guard** it is fixed at `https://api.lakera.ai/v2/guard`. * **HTTP method** — the verb MCP Manager uses: `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. Defaults to **POST**; the Bedrock and Lakera templates lock it to POST. * **Default direction** — the [detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires) (request, response, or both) this engine pre-selects when you add a rule that uses it. It is a recommendation, not a lock — you can override it per rule. * **Forward inbound server headers** — whether to pass the inbound runtime headers to your engine (see [Forwarding runtime headers](#forwarding-runtime-headers)). Off by default. * **Headers** — static custom headers sent on every call (see [Authenticating your engine](#authenticating-your-engine)). ## Only HTTPS, public endpoints For security, MCP Manager requires every rule-engine URL to use **HTTPS**, and it rejects URLs that resolve to **private, loopback, link-local, or carrier-grade-NAT IP ranges**. Your engine must be reachable on the public internet. A non-HTTPS URL is rejected with *"Webhook URL must use https\://"*, and a private address with *"Webhook URL resolves to a non-public IP …, which is not permitted. Customer-supplied rule engine webhooks must be reachable on a public network."* This guard prevents the gateway from being used to reach or scan internal infrastructure. ## Authenticating your engine Don't expose a rule engine on the public internet without locking it down. Anything you add under **Headers** is sent on every request and stored **encrypted at rest** until call time. Common patterns: * **Bearer token** — `Authorization: Bearer ` * **API key** — `X-Api-Key: `, or whatever header your service expects ## Forwarding runtime headers Switching on **Forward inbound server headers** copies the headers from the inbound connection — including the calling user's identity and any OAuth tokens — through to your engine. This is what makes identity-aware guardrails possible. For example, to forbid access to files inside a particular Google Drive folder, your engine can read the file IDs out of the message and then call the Google Drive API *as the requesting user* to resolve each file's parent folder — a lookup that's only possible with the user's forwarded identity. MCP Manager ships this exact guardrail for you as the [Google Drive Folder Blocker](/features/gateway-rules/google-drive-folder-blocker) — a managed engine that protects Drive folders by walking the folder hierarchy under the requesting user's identity. Enable it without building anything yourself. Forwarding runtime headers hands your engine custody of the inbound connection's **secure headers**, including identity and access tokens. Only forward headers to an engine you trust and control, and make sure it is [authenticated](#authenticating-your-engine) and ideally [IP-allowlisted](#defense-in-depth-allowlist-mcp-managers-ip). ## Defense in depth: allowlist MCP Manager's IP Beyond header-based authentication, a sophisticated engine can accept connections **only from MCP Manager** by allowlisting MCP Manager's static outbound IP address. Find it at [Security → IP addresses](https://app.mcpmanager.ai/settings/security/ip-addresses) — a single static IP you can add to your firewall or service allowlist. ## Testing an engine Every rule engine has a **Test** action. It opens a modal where you enter sample text (up to 500 characters) and previews how the engine would respond — its verdict and how long the call took — without attaching the engine to a gateway. The test runs **outside a live gateway session**, so it can't supply [forwarded runtime headers](#forwarding-runtime-headers). An engine that depends on forwarded identity headers won't behave the same way under test as it does in production — there's no inbound connection to forward from. ## Using an engine on a gateway rule Once an engine is registered, it appears in the **Detection method** dropdown of the [rule editor](/features/gateway-rules/overview#add-a-new-rule) on every gateway. A rule that uses a custom engine differs from a built-in rule in two ways: * **No action picker.** The engine's response — `pass`, `modify`, `block`, or `error` — determines what happens, so there is no action to choose. See the [webhook contract](/advanced/building-a-custom-rule-engine). * **Failure mode defaults to Block.** If the engine is unreachable, too slow, returns invalid data, or signals an error, the message is blocked unless you set the rule's [failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails) to **Allow**. ## Deleting a rule engine You can't delete a rule engine while it's still used by one or more gateway rules. To remove it, first delete every rule that references it — on each gateway's **Rules** tab — then return to **Rule Engines** and delete the engine. ## Further reading The full webhook contract for the Custom provider — envelope, verdicts, and a working example. Detection methods, hooks, failure modes, actions, and rule ordering. The AWS Bedrock template for a managed guardrail engine. The Lakera Guard template for security-first detection. A hands-on lesson: write a webhook, register it, and watch its verdict in the logs. # Google Drive Folder Blocker Source: https://docs.mcpmanager.ai/features/gateway-rules/google-drive-folder-blocker How the Google Drive Folder Blocker protects sensitive Drive folders from being read or referenced through the Google Workspace MCP: what it inspects, how the folder-hierarchy walk works across shortcuts and shared drives, how to find folder IDs and configure the rule, the accepted ID format and supported maximum, the deleted-ID behavior and the opt-in per-request check that alerts when a protected folder has been deleted, and its fail-closed validation and failure characteristics. The **Google Drive Folder Blocker** is a managed [gateway rule](/features/gateway-rules/overview) that stops an AI client from reading — or even referencing — files inside Google Drive folders you mark as protected. It is the gateway-side guardrail for the **Google Workspace MCP**: once enabled, it inspects every Workspace request **and** response that touches Drive and blocks anything that resolves into a protected folder, no matter how deeply the file is nested, whether it is reached through a shortcut, or whether it lives on a shared drive. Like every managed engine, you add it under **Rule Engines** and then apply it as a detection method on a gateway rule. It is configured by pointing a [custom rule engine](/features/gateway-rules/custom-rules-engines) at an MCP Manager-hosted endpoint and listing the folder IDs to protect. The Folder Blocker is purpose-built for the **Google Workspace MCP**. Like all [gateway rules](/features/gateway-rules/overview) it inspects only `tools/call` arguments and results (not prompts or resources). It resolves Drive folders using the calling user's Google identity, which MCP Manager forwards to the engine **automatically** when you configure it — see [Set up the Folder Blocker](#set-up-the-folder-blocker) below. ## What it protects against The Folder Blocker enforces a folder boundary **at the gateway**, independent of each user's own Drive permissions. The problem it solves: a user (or an agent acting on their behalf) often has legitimate Drive access to a sensitive shared drive — HR, legal, finance, executive — but you don't want that content reachable through an AI client. Drive's own sharing model can't express "this person may open the folder, but their AI assistant may not." The Folder Blocker can. Once active, protection applies automatically to **everyone whose traffic flows through that gateway** — you configure it once, not per user. An administrator sets it up inside MCP Manager; end users simply continue connecting their AI client to the **gateway URL**, and protected folders are unreachable through it. ## How it works On each Drive-touching tool call, the engine extracts every Drive file and folder ID it can find in the message — from IDs in tool arguments, URLs, and the structured fields a Workspace tool returns — and then determines whether any of them sit inside a protected folder. Because the protected set is checked against each item's **ancestor chain**, a file is caught even when it is nested many levels below the folder you listed. ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','lineColor':'#6a6b76','primaryColor':'#e0e2e8','primaryTextColor':'#12141d','primaryBorderColor':'#6a6b76','edgeLabelBackground':'#ffffff','textColor':'#12141d'}}}%% flowchart TD A["🗂️ Tool call or result references Drive"] --> B["Extract every Drive file / folder ID"] B --> C{"Is any ID — or one of its
ancestor folders — protected?"} C -->|"Yes"| BLOCK["🚫 Block"] C -->|"No — fully verified clean"| PASS["✅ Allow"] C -->|"Couldn't fully verify"| FM{"Rule failure mode"} FM -->|"Block (default)"| BLOCK FM -->|"Allow"| PASS classDef ok fill:#80cbc4,color:#062b4c,stroke:#00796b,stroke-width:1.5px; classDef bad fill:#ec9c9d,color:#12141d,stroke:#eb5757,stroke-width:2px; class PASS ok; class BLOCK bad; ``` The walk is built to leave no quiet gaps: * **Full hierarchy.** Each referenced item's parent chain is walked upward, so a file deep inside `Protected/2026/Q3/board-deck` is blocked by listing `Protected`. * **Shortcuts are resolved.** A Drive shortcut that lives in an allowed folder but points into a protected one is followed to its target, so it can't be used as a side door. * **Shared drives included.** Items on shared drives (formerly Team Drives) are resolved correctly, so a protected shared-drive folder is enforced just like a My Drive folder. * **Both directions.** The rule fires on the **request** leg (catching content-extraction tools such as `get_drive_file_content` and `export_doc_to_pdf`, where the file ID is in the arguments and no ID comes back in the result) and on the **response** leg (catching metadata-returning tools such as `search_drive_files` and `list_drive_items`, where the ID is only in the result). The engine pre-selects both directions, so adding it materializes a paired request + response rule (see [Detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires)). * **Fail-closed by design.** If the engine can't fully verify a request — a Drive lookup fails, the hierarchy is implausibly deep, or the payload references more items than it can check — it returns an error rather than guessing, and the rule's [failure mode](#validation-and-failure-behavior) (Block by default) decides the outcome. It never lets an unverified reference through silently. ## Before you start The Folder Blocker depends on the Google Workspace MCP already being governed by MCP Manager: 1. The **Google Workspace MCP** is added as a [remote MCP server](/mcp-gateway-concepts/mcp-servers/remote) and provisioned to the gateway you want to protect. MCP Manager handles the Google OAuth and proxies Workspace requests on each user's behalf — no one manages raw tokens. 2. End users connect their AI client to the **gateway URL** (not the Workspace server URL). The gateway is where the Folder Blocker and every other rule are applied. ## Find a folder or shared drive ID A protected entry is a Drive **folder ID** or **shared drive ID**. To get one, open the folder in Google Drive and copy the identifier from the URL — it is the segment after `/folders/` (for a folder) or `/drive/folders/` (inside a shared drive): ``` https://drive.google.com/drive/folders/1aBc-Defg_Hij1234567890KLMNOPQRSTu └──────────────┬──────────────────┘ the folder ID to protect ``` You can list as many as you need. Protecting a top-level folder automatically protects everything nested beneath it, so you usually list the few high-level folders that bound the sensitive content rather than enumerating every subfolder. ## Set up the Folder Blocker Setup is deliberately short: once MCP Manager recognizes the managed endpoint URL, it configures the engine for you, so you only name it, set the URL, and fill in the folder list. Open **Rule Engines** in the left-hand navigation, click **Add**, and choose the **Custom** provider. Give it a clear name such as `Google Drive Folder Blocker`. Set the **Endpoint URL** to: ``` https://app.mcpmanager.ai/api/v1/mcpm/rule-engines/gdrive-folder-blocker ``` As soon as MCP Manager recognizes this as the managed Folder Blocker endpoint, it configures the rest for you: the **HTTP method** is set to **POST**, the internal access token is injected (you never see or set it), **header forwarding is enabled** so the calling user's Google identity reaches the engine and it can resolve each file's ancestor folders as the requesting user, and the **`mcpm-gdrive-blocked-folders` header is added** ready for its value. A second header, **`mcpm-gdrive-warn-deleted-folders`**, is added at the same time and pre-set to `false` — leave it as-is unless you want the opt-in [deleted-folder alert](#warn-on-deleted-folders). In the pre-added **`mcpm-gdrive-blocked-folders`** header, paste the folder IDs from the previous section as the value. Separate them with commas, whitespace, or one per line — all three are accepted (a JSON array works too). Save the engine. On the gateway's **Rules** tab, add a rule and choose the Folder Blocker from the **Detection method** dropdown. It pre-selects **both** directions, so saving creates a paired request + response rule. There is no action picker — the engine's verdict drives the outcome — and the rule's [failure mode](#validation-and-failure-behavior) defaults to **Block**. ## Accepted folder ID format Each entry must be a syntactically valid Google Drive identifier: * **Character set** — the URL-safe characters Drive uses: `A–Z`, `a–z`, `0–9`, `-`, and `_`. No spaces or other punctuation inside an ID. * **Length** — roughly **19 to 44 characters**. Shared drive IDs are around 19 characters; file and folder IDs are longer. The blocker validates against this range rather than a single fixed length, so both shapes are accepted. Surrounding whitespace around each entry and a single trailing comma are tolerated and cleaned up for you. The blocker will **never** merge or split your tokens to "fix" them — see the next section for why that matters. ## Supported maximum A single Folder Blocker rule can protect well beyond any realistic list — the enforced maximum is **500 folder IDs**, comfortably above the few hundred that the largest real-world deployments use. The limit is checked when you save, so an oversized list is rejected up front with a clear message rather than being silently truncated at runtime. You rarely need a large list. Because protection is inherited down the hierarchy, listing a handful of top-level folders (one per sensitive shared drive, say) usually covers thousands of nested files. ## Validation and failure behavior The Folder Blocker is designed to **never weaken protection silently**. A misconfigured list fails loudly; a list that can't be fully evaluated fails closed. The behavior splits cleanly between save time and request time. **At save time**, the list is validated before it is stored: * A token that isn't a valid Drive ID — wrong characters, too short, too long, or **two IDs merged by a dropped comma** — is rejected with an error that **names the offending token**, so you can find and fix it immediately. * A list that exceeds the [supported maximum](#supported-maximum) is rejected the same way. * Whitespace and a single trailing comma are normalized away, but tokens are never auto-merged or auto-split — a malformed entry is reported, not "repaired" into something you didn't intend. **At request time**, the engine errs toward protecting what you asked it to: * If a stored list somehow can't be parsed safely, the engine **fails closed** — it blocks that gateway's Drive traffic rather than under-protecting, and it never silently ignores a token it can't understand. * If the engine can't fully verify a particular call — a Drive API error, a hierarchy deeper than it will walk, or a payload referencing more items than it can check — it returns an **error**. The rule's **failure mode** then decides: **Block** (the default for custom engines, and the recommended setting here) fails closed; **Allow** fails open. See [Failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails). **Deleted or inaccessible folders are safe to leave in the list.** A well-formed ID that points to a folder which has since been deleted — or that the requesting user can't see — is treated as a harmless **no-op**: it simply matches nothing. Every other entry in the list keeps protecting normally, and the request is **not** errored on account of the stale entry, so it never breaks the rest of the list. By default the blocker stays quiet about such entries; if you want to be told when a protected folder has been **deleted**, opt in with [Warn on deleted folders](#warn-on-deleted-folders). ## Warn on deleted folders A protected entry whose folder has since been **deleted** keeps behaving as a safe [no-op](#validation-and-failure-behavior) — it matches nothing and never errors a request. Confirming that a listed folder no longer exists, though, means issuing a Drive **`get`** for the folder on **every** request the rule inspects, which adds latency to all of the gateway's Drive traffic. Because most lists are stable, that check is **off by default** and must be explicitly opted into. To enable it, set the **`mcpm-gdrive-warn-deleted-folders`** header to `true`. Like the `mcpm-gdrive-blocked-folders` header, MCP Manager adds this header for you — pre-set to `false` — when it recognizes the managed endpoint, so you only change the value: * **`false` (default)** — no extra lookup. Deleted entries stay silent no-ops, and Drive traffic keeps the lowest latency. * **`true`** — each protected folder is resolved on every inspected request, and when one is found to have been deleted the blocker raises an **alert** (an `McpAlertMessage`) so you can prune the stale entry. Turn on the rule's [**Alerts** toggle](#how-it-appears-in-logs-and-alerts) as well to actually be notified. Enabling this trades latency for visibility: it performs an extra Drive `get` on every inspected request to confirm the listed folders still exist, which slows all of the gateway's Drive traffic. Switch it on when you actively want to catch removed folders — for example while cleaning up a large list — and consider turning it back off once the list has settled. ## Validating it works The standard rule-engine **Test** action can't meaningfully exercise this engine: a test runs outside a live gateway session, so there is no forwarded Google identity for it to query Drive with (and a real test would forward live Google credentials on every check). Validate it live from an AI client instead: 1. Ask the assistant to open or summarize a file you know is **inside** a protected folder — it should be blocked. 2. Ask it to open a file **outside** every protected folder — it should work normally. Confirm both before relying on the rule. Each result is also visible in your [logs](/features/viewing-logs). ## Security characteristics and limitations * **Identity-aware, gateway-enforced.** The boundary is enforced at the gateway using the user's own forwarded Google identity, so it holds regardless of how the file is reached — and applies uniformly to every user on the gateway without changing anyone's Drive permissions. * **Defense against indirect access paths.** Nesting, shortcuts, and shared-drive placement are all resolved during the ancestor walk, closing the obvious ways a protected file could be reached without naming its folder directly. The engine's threat model is maintained as a catalog of closed circumvention paths. * **Fail-closed posture.** Combined with the default **Block** failure mode, an unverifiable or misconfigured state errs toward blocking rather than leaking — consistent with the rest of MCP Manager's [runtime protections](/security/runtime-protections). **Residual exposure: Drive search can leak existence.** The Folder Blocker stops protected files from being **read or referenced** — their contents and metadata never leave Drive. It does not, however, hide the *signal* that something matching a query exists: Drive **search** (`search_drive_files`) can still return a non-empty result indicating that a file matching the query lives somewhere the user can see, including inside a protected folder, even though the matched content stays blocked. This is inherent to allowing search at all. If that inference is in scope for your threat model, close it completely by **disabling the Drive search tool** (`search_drive_files`) on your gateway, so it is never exposed to clients in the first place. You control exactly which of a server's tools a gateway offers through [Feature Provisioning](/features/feature-provisioning). ## How it appears in logs and alerts The Folder Blocker behaves like any [custom rule engine](/features/gateway-rules/custom-rules-engines) in your [logs](/features/viewing-logs): each run records the engine, its verdict (`pass` / `block` / `error`), and a comment explaining the decision — for example, which protected folder a blocked file resolved into, or which Drive lookup failed. A block is also recorded as a `policy_enforced_abort` entry. Turn on the rule's **Alerts** toggle to be notified whenever it acts, including fail-closed warnings and — when [Warn on deleted folders](#warn-on-deleted-folders) is enabled — alerts that a protected folder has been deleted. See [Audit & Observability](/security/audit-and-observability) for what each leg stores. ## Further reading Detection methods, hooks, failure modes, actions, and rule ordering. How managed and custom engines are added, configured, and applied to rules. Where in-path enforcement fits in MCP Manager's security model. Control which Workspace tools — including Drive search — a gateway exposes. # Gateway Rules Overview Source: https://docs.mcpmanager.ai/features/gateway-rules/overview How gateway rules work in MCP Manager: the per-gateway Rules tab, the detection methods (regex, Microsoft Presidio, custom engines), detection hooks, failure modes, actions, alerts, rule ordering, and how rule activity appears in logs. Every tool call that passes through an MCP Manager gateway can be inspected and acted on before it continues. **Gateway rules** are the content-level policies that do this. Each rule is attached to one gateway and runs only on the tool traffic flowing through that gateway, so different gateways can enforce different policy — one way to scope rules per client, team, or tenant. When a rule matches it can **block** the message, **modify** its contents, or let it through while raising an **alert**. You create and manage rules on a gateway's **Rules** tab: open a gateway from [Gateways](https://app.mcpmanager.ai/settings/gateways) in the left-hand navigation and select **Rules**. A rule is built from two halves: a **detection method** (what to look for) and what to do about it (an **action** — or, for custom engines, the engine's own verdict). MCP Manager ships two built-in detection methods, [regular expressions](/features/gateway-rules/regex) and [Microsoft Presidio](/features/gateway-rules/presidio), and lets you plug in your own [custom rule engines](/features/gateway-rules/custom-rules-engines) — including the [AWS Bedrock Guardrails](/features/amazon-bedrock) and [Lakera Guard](/features/lakera-guard) templates, or [build your own](/advanced/building-a-custom-rule-engine). Gateway rules currently apply to **tools only** — tool calls and tool results (the MCP `tools/call` method). They do **not** run on prompts (`prompts/get`) or resources (`resources/read`). A rule you create will never see, block, or modify a prompt or resource message. If you don't see a **Rules** tab on a gateway, your role doesn't have the capability to manage that gateway. Building [custom rule engines](/features/gateway-rules/custom-rules-engines) is separately controlled by the **Manage integrations** capability — if the **Rule Engines** section is missing from your left-hand navigation, your role lacks it. Capabilities are assigned per role and are fully configurable, including on custom roles, so access depends on the capabilities granted to your role, not on any fixed role name. ## Where gateway rules run in a tool call A gateway rule fires at one of two points in a tool call's round trip — on the **request** leg (the client's `tools/call` heading to the MCP server) or the **response** leg (the server's result heading back to the client). The diagram shows both hook points: ```mermaid theme={null} %%{init: {'theme':'base','themeVariables':{'fontFamily':'Lato, sans-serif','actorBkg':'#aed8ff','actorBorder':'#0b4880','actorTextColor':'#062b4c','signalColor':'#6a6b76','signalTextColor':'#12141d','noteBkgColor':'#fff8e4','noteBorderColor':'#ffa535','noteTextColor':'#12141d'}}}%% sequenceDiagram participant Client as 🤖 MCP Client participant Gateway as 🛡️ MCP Gateway participant Server as 🖥️ MCP Server Client->>Gateway: tools/call request Note over Gateway: Request-hook rules run Gateway->>Server: request (unless blocked) Server->>Gateway: tool result Note over Gateway: Response-hook rules run Gateway->>Client: response (unless blocked) ``` On the request leg, a rule scans the tool's arguments (`params.arguments`) and can stop the call before it ever reaches the server. On the response leg, a rule scans the tool's result and can stop or rewrite it before it reaches the client. A rule that **blocks** a request means the call never reaches the server; a rule that **blocks** a response means the result never reaches the client. ## The detection methods The **Detection method** dropdown in the rule editor is populated with both built-in engines and every custom rule engine your workspace has registered: | Detection method | What it does | Where to read more | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | **Regular expression** | Matches one or more JavaScript regex patterns against the message text. Built in. | [Regex](/features/gateway-rules/regex) | | **Microsoft Presidio** | Detects PII (credit cards, SSNs, emails, names, and more) using Microsoft's open-source engine. Built in; available as an add-on. | [Microsoft Presidio](/features/gateway-rules/presidio) | | **A custom rule engine** | Calls an external webhook you configure under **Rule Engines** — your own service, an AWS Bedrock guardrail, or Lakera Guard. | [Custom rule engines](/features/gateway-rules/custom-rules-engines) | The two built-in methods are always offered. Below them, the dropdown lists every engine registered in the **Rule Engines** section, so anything you add there becomes selectable as a detection method on any gateway rule. ## Choosing a detection method The three methods aren't ranked — they're suited to different kinds of data, and most mature setups use more than one. Match the method to what you're trying to catch: | Choose | When you're matching | Because | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Regular expression** | **Structured** values with a predictable shape — credit-card and Social Security numbers, national IDs, `AKIA…`-style keys, known prompt-injection strings. | Runs in-process, so it's the fastest option, has **no failure mode** to reason about, and supports all five actions (block, redact, replace, mask, hash). | | **Microsoft Presidio** | **Unstructured, contextual** PII — people's names, emails, phone numbers, locations — where a fixed pattern can't capture the variation. | Model-driven detection reads surrounding context. It's a managed add-on you don't host, tuned with entity types and a confidence threshold. | | **A custom rule engine** | **Nuanced or domain-specific policy** — jailbreak and prompt-injection intent, toxicity, field-level JSON redaction, or anything you want a model or your own service to judge. | Delegates the decision to [AWS Bedrock](/features/amazon-bedrock), [Lakera Guard](/features/lakera-guard), or [your own webhook](/advanced/building-a-custom-rule-engine) — at the cost of an external round-trip per call. | A few rules of thumb: * **Start with regex for anything that has a fixed shape.** It's free in latency terms and deterministic, so structured secrets and IDs are best caught here. * **Reach for Presidio when the value is a human artifact** — a name or address won't yield to a regex. Tune it against your own traffic; see [Microsoft Presidio](/features/gateway-rules/presidio). * **Bring a custom engine when the judgment is hard** — intent, context, or policy a pattern can't express. These add the latency of an external call and are bounded by a 30-second timeout and a [failure mode](#failure-mode-what-happens-when-a-detection-method-fails). * **Layer them.** The methods compose: a regex rule that **blocks** prompt injection placed *first*, then a Presidio rule that **replaces** PII as a safety net, then a custom engine for nuanced policy. Because [rule order](#rule-order-and-the-enable-toggle) matters, put your most decisive rules at the top. For PII specifically, [PII Filtering](/features/pii-filtering) walks through this decision in the context of keeping customer data out of the model. ## Add a new rule New to rules? [Add your first gateway rule](/tutorials/first-gateway-rule) walks through this in a safe, non-blocking configuration. From [Gateways](https://app.mcpmanager.ai/settings/gateways), select the gateway you want to protect and open its **Rules** tab. Click **Add new rule** to open the rule editor. Enter a **Rule name** — a short, descriptive label such as `Block prompt injection` or `Redact SSNs`. The name identifies the rule in the rules list, in your [logs](/features/viewing-logs), and in any [alerts](/features/alerts) it raises. Pick **Regular expression**, **Microsoft Presidio**, or one of your custom rule engines from the **Detection method** dropdown. The fields below the dropdown change to match the method you chose. Set whether the rule fires on the **request**, the **response**, or **both** directions. See [Detection hook](#detection-hook-when-a-rule-fires). Fill in the method-specific settings — patterns for regex; entity types, confidence, and failure mode for Presidio; failure mode for a custom engine — and, for the built-in methods, choose an **Action**. Custom engines have no action picker; the engine's response carries the action. Toggle **Alerts** on if you want to be notified whenever the rule acts, then save. The rule appears in the rules list, enabled and ready. ## Detection hook: when a rule fires The **Detection hook** controls which leg of the tool call a rule runs on: * **Request** — the rule fires before the `tools/call` reaches the MCP server, scanning the tool's arguments. Use this to stop a call from ever being made. * **Response** — the rule fires after the server returns its result, scanning the result before it reaches the client. This is the default and the most common choice. * **Both** — a convenience that, on save, **materializes a paired rule per direction**: one request rule and one response rule, each independently sortable, editable, and toggleable. There is no single "both" rule at runtime — you end up with two rules. When editing an existing rule you can only set a single direction; to cover both, add a second rule. A custom rule engine can advertise a preferred direction, which pre-selects this picker when you choose that engine — but you can always override it. ## Failure mode: what happens when a detection method fails Some detection methods call an external system that can fail — Microsoft Presidio, an AWS Bedrock guardrail, Lakera Guard, or your own endpoint. "Fail" means the service is **unreachable**, **too slow**, returns an **invalid response**, or otherwise signals an **error**. For these methods the rule has a **Failure mode** that decides what happens in that case: * **Block** — a failure blocks the message. A failed request never reaches the server; a failed response never reaches the client. This is the **default for custom rule engines**, so a misconfigured or unavailable engine fails closed rather than silently leaking data. * **Allow** — a failure lets the original message through unchanged. This is the **default for Microsoft Presidio rules**. Regular-expression rules run in-process and synchronously, so they have **no failure mode** — there is no external call to fail. ## Actions: what a matching rule does For the built-in detection methods you choose an **Action** that applies when the rule matches. Custom rule engines do not show an action picker — their webhook response (`pass`, `modify`, `block`, or `error`) determines the outcome. | Action | Effect | Available for | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | | **Block** | Blocks the message entirely. A blocked tool call never reaches the server; a blocked response never reaches the client. | Regex, Microsoft Presidio | | **Redact** | Removes the matched text entirely, leaving nothing in its place. | Regex | | **Replace** | Substitutes the matched text with a placeholder. Regex rules use the constant ``; Microsoft Presidio tags each detected entity by type, such as `` or ``. | Regex, Microsoft Presidio | | **Mask** | Replaces each character of the match with an asterisk, preserving length (a 16-character value becomes `****************`). | Regex | | **Hash** | Replaces the match with a truncated SHA-256 hash of the form `` (16 hex characters), letting you correlate repeats without exposing the value. | Regex | Regular-expression rules support all five actions; Microsoft Presidio rules support **Block** and **Replace** only. For the modification actions (redact, replace, mask, hash), the message continues to the next enabled rule after being rewritten; a **Block** action stops rule processing immediately. ## Alerts Every rule has an independent **Alerts** toggle. When it's on, MCP Manager raises a real-time alert each time the rule **acts** — whether the action was block, a modification, or a custom engine's verdict — and the alert appears in the [Alerts](/features/alerts) section in the left-hand navigation. You can leave a rule on a non-destructive action (say, **Replace**) and still be alerted every time it fires, which is a good way to roll a rule out safely before switching it to **Block**. ## Rule order and the enable toggle Rules are listed in the order they run, with a number in the leftmost column. **Hover the number, grab the gripper, and drag** to reorder. Ordering is compared **within a detection hook**: all request-hook rules run in their relative order, and all response-hook rules run in their relative order, independently of each other. Order matters because a **Block** action stops processing immediately — no later rule runs on that message — while modification actions **chain**, each operating on the text the previous rule already modified. Put your most decisive rules (such as prompt-injection blocking) first. Each rule also has an **Enabled** toggle. Flipping it takes effect almost immediately, so you can turn a rule off to investigate a false positive and back on without deleting and re-creating it. ## How rule activity appears in your logs Every time a rule runs it is recorded in your [logs](/features/viewing-logs) — not only when it blocks. Rule-engine activity populates three log columns: * **`rule_engine_id`** — which engine acted. * **`rule_engine_type`** — how it classified the message: `pass`, `modify`, or `block`. * **`rule_engine_comment`** — any comment the engine returned (for a custom engine, the `comment` field of its response). Alongside these, a blocked message is logged with the type `policy_enforced_abort` and a modified message with `policy_enforced_mutation`, so you can tell from the log type alone whether a rule **blocked**, **modified**, or **allowed** a given message. Block entries also record the specific detection that triggered them — for example the Presidio entity or regex pattern that matched — not just the rule name. See [Viewing Logs](/features/viewing-logs) for the full column reference, and [Alerts](/features/alerts) for the higher-level event feed. A rule changes what the MCP **client** receives — it is not a control over what gets written to the audit log. The original client request is logged on the inbound leg **before** any request-side rule runs, so a redaction or masking rule on the request never keeps the original out of your logs. On the response leg, a modifying or blocking rule is recorded as the modified or blocked content. Either way, the rule's activity is logged rather than hidden: by default a redaction rule produces an audit record, it does not scrub one. If you need sensitive payloads kept out of the stored log entirely, that can be configured for your workspace on request — discuss it with your MCP Manager contact. To see exactly what each leg stores, see [Audit & Observability](/security/audit-and-observability#what-every-call-records). ## Further reading Pattern-matching rules for structured secrets and IDs, with all five actions. Context-aware PII detection, run as a managed add-on. Plug in AWS Bedrock, Lakera Guard, or your own webhook. Using rules to keep customer PII out of the model. # Microsoft Presidio Source: https://docs.mcpmanager.ai/features/gateway-rules/presidio How the Microsoft Presidio detection method finds and anonymizes PII in MCP Manager: the analyzer/anonymizer two-pass engine MCP Manager runs for you as a managed add-on, the selectable entity types, the 0.2 default confidence threshold, why it is less reliable for names in free-form text, the Allow/Block failure mode, and the Block and Replace actions. **Microsoft Presidio** is a [gateway rule](/features/gateway-rules/overview) detection method in MCP Manager that finds and anonymizes **personally identifiable information (PII)** in tool messages — credit cards, Social Security numbers, emails, names, locations, and more. Presidio is Microsoft's open-source PII framework; in MCP Manager it runs as a **managed add-on**, so you get its detection and anonymization without deploying, scaling, or securing it yourself. Select **Microsoft Presidio** as the **Detection method** in the rule editor. For a step-by-step walkthrough, see [Redact PII from tool responses with Presidio](/tutorials/pii-filtering). Microsoft Presidio is available as an **add-on**. If your workspace doesn't have it enabled, choosing this detection method shows a **Schedule consultation** option instead of the configuration fields. The built-in [regex](/features/gateway-rules/regex) method is always available. For the framework itself, see the [Microsoft Presidio documentation](https://microsoft.github.io/presidio/). ## How Presidio detection works Presidio cleanly separates **detection** from **anonymization**, and MCP Manager runs both stages for you on each tool message: 1. **Analyzer** — identifies candidate PII and assigns each finding a **confidence score**, combining named-entity-recognition (NER) models, regular-expression recognizers, checksums, and surrounding-context cues. 2. **Anonymizer** — takes the analyzer's findings and transforms the matched spans according to the rule's action. Because detection is model-driven rather than a fixed pattern list, accuracy depends on configuration — chiefly the entity types you select and the confidence threshold (below). A Presidio rule scans on whichever [detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires) you chose (request or response). ## Entity types In the rule editor you choose which **entity types** to detect. **If you select none, all supported entity types are detected.** The entity types MCP Manager exposes are Presidio's standard global recognizers: * **Financial** — `CREDIT_CARD`, `IBAN_CODE`, `US_BANK_NUMBER`, `CRYPTO` (wallet addresses) * **Government and national IDs** — `US_SSN`, `US_ITIN`, `US_PASSPORT`, `US_DRIVER_LICENSE`, `UK_NHS`, `MEDICAL_LICENSE` * **Contact and identity** — `PERSON`, `EMAIL_ADDRESS`, `PHONE_NUMBER`, `LOCATION`, `NRP` (nationality, religious, or political group) * **Technical** — `IP_ADDRESS`, `URL`, `DATE_TIME` Selecting specific entities narrows detection to just those categories, which reduces noise when you only care about, say, credit cards and SSNs. ## Confidence threshold The **Confidence threshold** is the minimum score — from **0.0 to 1.0** — an entity must reach to count as a detection. The default is **0.2**. Lower values catch more entities but produce more false positives; higher values are more conservative. Because Presidio's accuracy is tuning-dependent, treat the threshold as a dial: start at the default, watch what fires in your [logs](/features/viewing-logs) and [alerts](/features/alerts), and raise it if benign text is being flagged. ## Reliability with names and free-form text Presidio's reliability is not uniform across entity types, and the difference decides what you can safely trust it for. **Structured identifiers** — `CREDIT_CARD`, `US_SSN`, `EMAIL_ADDRESS`, `IBAN_CODE`, `IP_ADDRESS`, and the like — are found with patterns, checksums, and validation, so Presidio catches them dependably wherever they appear. **Names and other context-dependent entities** — `PERSON`, `LOCATION`, and `NRP` — are found by NLP models, which are markedly less reliable on short, terse, or loosely structured text. A person's name in a task title, a column header, or a single-word field can be missed even when the same name in a full sentence would be caught. Do not rely on Presidio alone for high-confidence redaction of **names or other sensitive content buried in free-form text**. Presidio is a strong, fast layer for structured PII, and a missed name in unstructured text is an expected limitation of model-based detection rather than a defect. When names in free text must not slip through, add a classifier-based detection method alongside it: * [Amazon Bedrock Guardrails](/features/amazon-bedrock) — managed PII and content policies that can detect and de-identify free-form text. * [Lakera Guard](/features/lakera-guard) — classifier-based detection that blocks messages carrying the flagged content. * A [custom rule engine](/features/gateway-rules/custom-rules-engines) — your own classifier or data-loss-prevention service. A practical pattern is to keep a Presidio rule for structured PII and place a classifier-based rule above it for names and other free-form sensitive content. ## Failure mode Presidio runs as a service the gateway calls, so a Presidio rule has a **Failure mode** for when it's unreachable, too slow, or errors: * **Allow** — let the message through unchanged. This is the **default**. * **Block** — block the message as a precaution. Choose **Block** when it matters more that PII never slips through than that the tool keeps working during a Presidio outage. See [Failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails). ## Actions Presidio rules support two actions: * **Block** — block the whole message when any selected entity is detected; the anonymizer is not invoked. * **Replace** — run the anonymizer to replace each detected entity **with a tag naming its type**. For example, `Customer email: alice@example.com` becomes `Customer email: `, and a card number becomes ``. The surrounding text is left intact. Presidio's Replace tags each entity by **type** (``, ``, …), which is more informative than the single `` placeholder a [regex](/features/gateway-rules/regex) Replace rule uses. The other regex actions — redact, mask, and hash — are not available for Presidio; use a regex rule if you need those. ## Example: broad PII protection Detection method: **Microsoft Presidio** · Entity types: *none (detect all)* · Confidence threshold: **0.5** · Failure mode: **Block** · Action: **Replace** This catch-all rule tags every detected entity by type and fails closed if the service is down. Combine it with more targeted [regex](/features/gateway-rules/regex) rules placed *above* it — for example, blocking prompt injection first, then replacing PII as a safety net. See [Rule order](/features/gateway-rules/overview#rule-order-and-the-enable-toggle). Presidio sits at a different layer than [Amazon Bedrock](/features/amazon-bedrock) and [Lakera Guard](/features/lakera-guard). Those are policy and safety services that return an allow/block decision on a whole interaction; Presidio is a focused **PII detection-and-transformation** engine that tags or removes sensitive spans in place. They complement each other — you might screen for prompt injection with one and tag PII with Presidio. ## Good to know Microsoft is candid about Presidio's limits, and they apply here too: * **No detection is guaranteed.** Presidio uses automated detection, so it won't catch *every* piece of sensitive data. Treat it as one layer, not a complete guarantee, and pair it with other controls for high-stakes data. * **Accuracy is tuning-dependent.** Out of the box it's strong on common PII, but the confidence threshold and entity selection materially change recall and the false-positive rate. Tune against your own traffic. * **It's a building block, not a full DLP product.** Presidio complements enterprise data-loss-prevention tooling rather than replacing it. * **No per-call charges.** Once the Presidio add-on is enabled on your plan, MCP Manager does **not** meter or charge per scan or per detection. Run Presidio rules on as much traffic as you need — there is no per-call or per-message fee for using it. Because MCP Manager runs Presidio as a managed service, you don't take on the open-source project's deployment concerns yourself — hosting the NER models, securing the API, and scaling the containers — but the detection-quality caveats above still apply. ## Further reading A managed guardrail for free-form text, added as a custom rule engine. Classifier-based detection for names and free-form sensitive content. Detection methods, hooks, failure modes, actions, and rule ordering. Where Presidio fits in keeping customer PII out of the model. ## External sources # Regex Source: https://docs.mcpmanager.ai/features/gateway-rules/regex How regular-expression gateway rules work in MCP Manager: JavaScript regex syntax with case-insensitive global matching, multiple OR patterns, the five actions (block, redact, replace, mask, hash), and ready-to-use patterns for prompt injection, SSNs, credit cards, and secrets. A **regular expression** rule is the most flexible [gateway rule](/features/gateway-rules/overview) detection method in MCP Manager: you supply one or more patterns, and the rule matches them against the text of a tool message. Select **Regular expression** as the **Detection method** in the rule editor on a gateway's **Rules** tab. ## How regex matching works MCP Manager compiles each pattern as a **JavaScript regular expression** and evaluates it with the **case-insensitive (`i`)** and **global (`g`)** flags. Matching therefore ignores letter case and finds *every* occurrence in the message, not just the first. Enter patterns in JavaScript syntax (the same syntax the `RegExp` constructor accepts); surrounding slashes are optional. A regex rule scans the text of the tool message on whichever [detection hook](/features/gateway-rules/overview#detection-hook-when-a-rule-fires) you chose — the tool's arguments on the request leg, or the tool's result on the response leg. Because regex runs in-process and synchronously, it never "fails," so a regex rule has no [failure mode](/features/gateway-rules/overview#failure-mode-what-happens-when-a-detection-method-fails). ## Multiple patterns A single rule can hold **more than one pattern**. Use **Add matching pattern** in the rule editor to add another. Patterns are evaluated as an **OR**: if *any* pattern matches, the rule's action fires. Each pattern is compiled and tested independently. ## Pattern validation and the Regex101 helper If a pattern has invalid syntax, the rule editor shows an inline error with a **"Click here to test and fix your pattern on Regex101"** link, pre-filled with your pattern so you can debug it on [regex101.com](https://regex101.com) and paste the corrected version back. ## Actions Regular-expression rules support **all five** rule actions. The action applies to the text each pattern matched: | Action | What it does to the matched text | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Block** | Blocks the whole message. A blocked request never reaches the server; a blocked response never reaches the client. | | **Redact** | Removes the match entirely, leaving nothing in its place. | | **Replace** | Substitutes the match with the constant ``. | | **Mask** | Replaces each character of the match with an asterisk, preserving the original length. | | **Hash** | Replaces the match with a truncated SHA-256 hash, `` (16 hex characters), so you can correlate repeated values without exposing them. | For the modification actions (redact, replace, mask, hash), every occurrence of every matched pattern is transformed and the message then continues to the next enabled rule. A **Block** action stops rule processing immediately. See [Actions](/features/gateway-rules/overview#actions-what-a-matching-rule-does) for how actions and [rule order](/features/gateway-rules/overview#rule-order-and-the-enable-toggle) interact. ## Examples Detection method: **Regular expression** · Action: **Block** · Alerts: **on** ```text Patterns theme={null} ignore\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions|prompts|directives) you\s+are\s+now\s+(in\s+)?(developer|admin|debug|unrestricted)\s+mode disregard\s+(all\s+)?(your|the)\s+(previous|prior|safety|system)\s+(instructions|rules|guidelines|prompt) system\s*:\s*(you\s+are|from\s+now|new\s+instructions|override) ``` If a tool response carries text like "ignore your previous instructions," the response is blocked before the model sees it. Enable alerts so you're notified on every attempt. Detection method: **Regular expression** · Action: **Replace** ```text Pattern theme={null} \b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b ``` Matches `123-45-6789`, `123 45 6789`, and `123456789`. With **Replace** each match becomes ``; with **Redact** it disappears. Detection method: **Regular expression** · Action: **Mask** ```text Pattern theme={null} \b(?:\d[ -]*?){13,19}\b ``` Catches most card formats and replaces the digits with asterisks, preserving length. For checksum-validated detection with fewer false positives, use a [Presidio](/features/gateway-rules/presidio) rule with the `CREDIT_CARD` entity instead. Detection method: **Regular expression** · Action: **Replace** ```text Patterns theme={null} (?:api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token)\s*[:=]\s*['"]?[A-Za-z0-9_\-\.]{20,}['"]? sk[-_]live[-_][A-Za-z0-9]{20,} ghp_[A-Za-z0-9]{36,} AKIA[0-9A-Z]{16} ``` Targets generic key/secret assignments plus Stripe secret keys, GitHub personal access tokens, and AWS access key IDs. Roll a new pattern out on a non-destructive action first. Set the action to **Replace** with **Alerts** on, watch the [Alerts](/features/alerts) and [logs](/features/viewing-logs) to see what it catches, tune the pattern to remove false positives, and only then switch high-severity rules to **Block**. ## Further reading Context-aware detection for unstructured PII like names and addresses. Detection methods, hooks, failure modes, actions, and rule ordering. Delegate nuanced policy to AWS Bedrock, Lakera Guard, or your own webhook. # Google Model Armor Source: https://docs.mcpmanager.ai/features/google-model-armor What Google Cloud Model Armor is and how to connect a template to MCP Manager as a custom rule engine: the filters it enforces, the sanitizeModelResponse integration, the project/location/template plus service-account-key setup, server-side authentication, and the pricing and behavior to plan for. The **Google Model Armor** template connects a Model Armor template as a [custom rule engine](/features/gateway-rules/custom-rules-engines) in MCP Manager. You create and tune the template in Google Cloud; MCP Manager calls Model Armor's `sanitizeModelResponse` API on your behalf and translates the result into a pass / modify / block verdict on the tool message. Add it from **Rule Engines** → **Add** → **Google Model Armor**. This page summarizes Model Armor to help you decide how to configure a template for MCP Manager. Google owns the feature and changes it often — treat the [Model Armor documentation](https://docs.cloud.google.com/model-armor/overview) and the Model Armor pricing section of that page as the authoritative source for the current filters, limits, and prices. ## What Model Armor is Google Cloud Model Armor is a **managed AI-safety service** that screens content against filters you configure in a template — both prompts on the way in and model (or tool) responses on the way out. Its defining property for governance is that it enforces **controls that don't depend on a model cooperating**: unlike instructions embedded in a prompt, a Model Armor verdict doesn't rely on the model's reasoning quality. It is model-independent and cloud-agnostic — it evaluates text, so it works regardless of which model your client ultimately talks to. ### How MCP Manager integrates it Model Armor exposes a **`sanitizeModelResponse` endpoint that evaluates content against a template without invoking any foundation model** — standalone content screening, decoupled from inference. MCP Manager sends the **tool message text** from your MCP traffic to that endpoint (as `modelResponseData`), Model Armor applies the template's filters, and MCP Manager acts on the verdict. Two consequences are worth knowing: * **It's response-direction today.** MCP Manager fires this engine on tool **responses** — the result your agent is about to receive — and screens that text. (Model Armor also has a `sanitizeUserPrompt` endpoint for the request direction; MCP Manager uses the response endpoint.) * **It complements model-side safety rather than replacing it.** Safety attached to a model call protects that call; screening at the MCP gateway protects the data flowing through your [connections](/features/viewing-logs). You can run both. ## What a template can detect You configure Model Armor's filters in the template, and MCP Manager surfaces which filter fired in the rule's alert and [logs](/features/viewing-logs). The current set, per Google: Detect and filter harmful content across categories such as hate speech, harassment, sexually explicit, and dangerous content, with configurable confidence thresholds. Detect attempts to subvert the model's instructions or safety such as prompt-injection and jailbreak patterns are reported with a confidence level. Detect sensitive data using Google's Sensitive Data Protection (SDP). A **basic** configuration inspects and flags PII; an **advanced** SDP template can return a **de-identified** version of the text. When Model Armor returns de-identified text that differs from the original, MCP Manager applies it as a **modify** (see below) rather than blocking outright. Flag URLs in the content that are known to be malicious. Screen for child sexual abuse material. This protection is always evaluated. For the exact, current list of filters and how to configure each, see [Model Armor templates](https://docs.cloud.google.com/security-command-center/docs/manage-model-armor-templates) in the Google Cloud docs. ## Detecting PII with Sensitive Data Protection Model Armor's PII detection runs on **Sensitive Data Protection (SDP)**, Google's data-inspection service. You turn it on inside your Model Armor template in one of two modes: **Basic** for a quick start, or **Advanced** when you want to choose exactly what to detect — or to redact matches in place instead of blocking. The Sensitive Data Protection filter uses Google's SDP service, so the **Sensitive Data Protection API** must be enabled in your project once: `gcloud services enable dlp.googleapis.com`, or accept the enable prompt the first time you open the SDP console. ### Default detection (Basic) The fastest way to start, with nothing to configure beyond a toggle. In the [Model Armor console](https://console.cloud.google.com/security/model-armor), open **your template → Edit**, and turn on the **Sensitive Data Protection** filter. Select **Basic**. Model Armor uses Google's built-in set of common PII detectors — a US-focused default list covering things like Social Security numbers and credit-card numbers. There's nothing else to configure in the template. Basic mode **inspects and flags** PII but never rewrites it. In MCP Manager that means a tool response containing PII is **blocked** (the result is replaced with an error) — the right choice when such data should never reach the agent. ### Custom detection (Advanced) Use Advanced when you want to pick specific data types, add your own, or **redact PII in place instead of blocking**. Advanced mode points your Model Armor template at one or two templates you create in **Sensitive Data Protection**. In the [Sensitive Data Protection console](https://console.cloud.google.com/security/sensitive-data-protection), go to **Configuration → Templates → Create template**, and create an **Inspect** template. Choose the data types (infoTypes) you care about from Google's 150+ built-in detectors — PII, credentials, and more — and/or add a **custom infoType** with a regular expression or a word/phrase dictionary for organization-specific data (for example, your employee-ID format). Create it in the **same region** as your Model Armor template. To have matches **redacted in place** rather than blocked, also create a **De-identify** template. It defines the transformation — replace a value with a placeholder like `[REDACTED]`, or mask it (for example, showing only the last four digits of a card number). So Model Armor can read your templates at runtime, grant its **service agent** the **DLP User** (`roles/dlp.user`) and **DLP Reader** (`roles/dlp.reader`) roles on the project that holds the SDP templates. The service agent is `service-@gcp-sa-modelarmor.iam.gserviceaccount.com` (your project number is on the Cloud console dashboard). Skip this and Advanced mode fails with a permission error. Back in your template in the [Model Armor console](https://console.cloud.google.com/security/model-armor), set **Sensitive Data Protection** to **Advanced** and select your Inspect template (and the De-identify template, if you created one). If the form asks for a path rather than a picker, it's `projects//locations//inspectTemplates/`. How the two Advanced setups behave in MCP Manager: * **Inspect template only** (no de-identification) → a match **blocks** the tool response. * **With a de-identification template** → Model Armor returns the **de-identified** text and MCP Manager applies it as a **modify**: the agent receives the redacted or masked version instead of the original, and the message still flows through. ### Turn it into a high-value PII engine A few choices take a basic Advanced setup and make it genuinely protective for MCP traffic — most of the value comes from the first two: * **Mask instead of block.** Pair your Inspect template with a De-identify template so matches are redacted *in place* (a **modify**) rather than blocking the whole response. The agent keeps working — an email becomes `[EMAIL_ADDRESS]`, a card is masked to its last four digits — and the raw value never reaches it. Reserve blocking for data that must never appear at all; masking is usually the better default for tool output. * **Catch credentials, not just classic PII.** Tool responses leak API keys, OAuth/JWT tokens, and passwords far more often than Social Security numbers, and an agent that ingests a live credential is a real risk. Add Sensitive Data Protection's **credentials-and-secrets** detectors — it's the single highest-value addition for an MCP gateway. (See the [infoType reference](https://docs.cloud.google.com/sensitive-data-protection/docs/infotypes-reference) for exact names such as `AUTH_TOKEN`, `GCP_API_KEY`, and `PASSWORD`.) * **Add one custom infoType for your own identifiers.** A single regex or word-list detector teaches Model Armor your organization's data — employee IDs (e.g. `EMP-\d{6}`), internal project codenames, customer account numbers, internal hostnames. This is what makes the engine feel tailor-made rather than generic. * **Keep the set tight and raise the threshold to cut noise.** Start with a focused list (emails, phone numbers, payment cards, your region's national ID, credentials) instead of all 150+ detectors, and set the inspection **minimum likelihood** to `LIKELY` so borderline guesses don't flood your [logs](/features/viewing-logs) with false positives. You can always widen it later. * **Define it once, reuse it everywhere.** An SDP Inspect (and De-identify) template is a reusable resource — point multiple Model Armor templates at the same one so every gateway enforces the same definition of "sensitive." SDP templates are **regional** and must be created in the same location as the Model Armor template that references them. This is a getting-started overview — for the full list of detectors, custom-infoType options, and de-identification transforms, see [Sensitive Data Protection](https://docs.cloud.google.com/sensitive-data-protection/docs). ## What you need from Google Cloud Set up the template and a service account in Google Cloud first. Enable Model Armor (`gcloud services enable modelarmor.googleapis.com`), then create a template that defines your filters. Creating templates requires the **Model Armor Admin** (`roles/modelarmor.admin`) role. Note the **project ID**, the **location**, and the **template ID** you choose. Choose a **regional** location (for example `us-east1`, `us-central1`, `europe-west4`) — not `global`. MCP Manager builds the regional endpoint `modelarmor..rep.googleapis.com`, and a `global` template uses a different host that MCP Manager will reject. Create a service account (IAM & Admin → Service Accounts) and grant it the **Model Armor User** (`roles/modelarmor.user`) role on the project that holds your template — this is the role that lets it **call** the API (Admin is only needed to create templates). In the IAM role picker, searching "model" surfaces the Viewer, Admin, and Editor roles first — **"Model Armor User" is below them under "Show more" / additional results.** It's easy to miss. On the service account's **Keys** tab, choose **Add key → Create new key → JSON**. The downloaded file (it contains `client_email` and `private_key`) is what you paste into MCP Manager. ## Connecting it in MCP Manager In the **Google Model Armor** rule-engine form, provide: Enter the **GCP project ID**, the template's **location**, and the **template ID** from the setup above. The form links to the Google Cloud console if you still need to create a service account. Paste the full contents of the downloaded service-account key JSON. MCP Manager stores it **encrypted at rest** and uses it to authenticate to Model Armor. The **endpoint URL is built for you** from the project, location, and template — you don't enter it. MCP Manager constructs `https://modelarmor..rep.googleapis.com/v1/projects//locations//templates/