> ## Documentation Index
> Fetch the complete documentation index at: /docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Servers

> Connect upstream MCP servers to Toqan Router with automatic tool discovery, and expose their tools to Agents and MCP Gateways with allow-lists.

See [HTTP URLs and authentication](/docs/reference/base-urls.md) and the API reference for complete request and response formats.

Connect upstream [Model Context Protocol](https://modelcontextprotocol.io) servers to **Toqan Router**. Once connected, tool discovery happens automatically and the tools become available to **Agents** and **Gateways**.

> [!NOTE]
>
> These are third-party **MCP Servers** that Toqan Router connects to. This is a different feature from Toqan Router's own MCP server, which coding assistants connect to for workspace administration.

**MCP Portal** is the **AI Gateway** area that manages **MCP Servers** and **MCP Gateways**.

## Use Cases

* Connecting third-party SaaS tools (Slack, Linear, Figma, etc.) to agents without writing custom integrations.
* Exposing internal APIs as callable tools for agents.
* Centralizing MCP server management instead of configuring connections per tool or per agent.
* Sharing a single MCP server across multiple agents and [MCP Gateways](/docs/ai-gateway/mcp-portal/mcp-gateways.md) with controlled tool exposure.

## How It Works

Each MCP Server registers an upstream endpoint. **Toqan Router** connects to it, discovers the available tools, and tracks the sync state. Tools can be exposed to all agents, filtered by an allow-list, or hidden entirely.

When a server is linked to an [MCP Gateway](/docs/ai-gateway/mcp-portal/mcp-gateways.md), the gateway routes tool calls and handles authentication, so clients only need the gateway URL.

### Tool schemas

Tool input schemas are validated on every sync.

**Supported dialects**: JSON Schema 2020-12 when the schema omits `$schema`; explicit draft-04, draft-06, draft-07, 2019-09 and 2020-12 declarations are accepted.

| Limit | Value |
| - | - |
| Schema size | 256 KiB |
| Nesting depth | 64 levels |
| Nodes per schema | 10,000 |

A sync fails on a missing or invalid input schema, an unsupported dialect, or a schema over a limit. The error names the tool and the schema field; fix the upstream schema and sync again.

Local `$defs` and references resolve inside the document; a reference to another file or URL is blocked. Imported OpenAPI schemas keep their constraints, and a request body that cannot become flat tool arguments stays under a `body` or `request_body` argument.

## MCP Servers List

Open **AI Gateway > MCP Portal** in the sidebar. The **MCP Servers** tab lists all registered servers, one row per server:

* **Connection**: the server key, with the provider logo when the server comes from the catalog
* **Tools**: the tool count from the last sync
* **Access**: which projects can use the server
* **Synced at**: when the server last synced
* **Created by**: the workspace member who added it

> [!NOTE]
>
> MCP connections were previously created as an **MCP Tool** under **Create Tools**. That tool type is retired, and existing MCP Tools now appear here in the **MCP Portal**.

## Set Up an MCP Server

Select **Create** in the **MCP Servers** tab. The create page offers three starting points:

* **OpenAPI**: turns a REST API that publishes an OpenAPI document into tools. See [Create from an OpenAPI Specification](#create-from-an-openapi-specification).
* **MCP URL**: connects an existing MCP server by its endpoint.
* **Catalog**: a pre-configured provider. Selecting one fills in the connection URL and the authentication it expects.

The rest of this section covers the **MCP URL** path.

1. **Fill in the general details.**
   * **Key** (required): lowercase letters, digits, `-` and `_`, up to 64 characters. Prefixes tool names in an [MCP Gateway](/docs/ai-gateway/mcp-portal/mcp-gateways.md) and sets `display_name`. The API also requires `default_tool_exposure`.
   * **Description** (optional): a note about what this server provides.

2. **Set the connection.**
   * **Type**: **HTTP** (default) or **SSE** for streaming-capable servers.
   * **URL** (required): the endpoint of the upstream MCP server.

3. **Configure authentication.**

   * **None** (default): for public servers that require no credentials.
   * **Static Headers**: send fixed headers with every request. Header values are stored encrypted and never returned by the API. A `{{variable}}` placeholder keeps a value out of storage: the server stores only the name, and the value is supplied when the server is tested, synced, or a tool is called. Placeholder values are not persisted. To expose resources from this server in an **MCP Gateway**, supply the same variable values on each gateway request in `params._meta["orq.ai/discoveryVariables"]`, keyed by MCP Server ID. This requires workspace-wide MCP Server management access.
   * **OAuth Client Credentials**: authenticate with an upstream OAuth 2.0 authorization server using the client credentials grant. Fill in **Client ID**, **Client secret**, **Token URL** and optional **Scopes**. The secret is stored encrypted and never returned by the API. See [How OAuth Client Credentials works](#how-oauth-client-credentials-works). This credential is separate from the [OAuth flow](/docs/ai-gateway/mcp-portal/mcp-gateways.md#oauth) MCP clients use to connect to an **MCP Gateway**.
   * **Per-user OAuth**: each user connects their own account through the OAuth authorization code flow, and tool calls run with that user's token. The client settings are optional and sit under **Advanced configuration**. The option is listed for HTTP and SSE connections. See [Per-user OAuth](#per-user-oauth).
   * **Enterprise managed (ID-JAG)**: call the server through an **MCP Gateway** on behalf of each SSO user. The option is listed when the workspace's plan includes SSO and the connection is not OpenAPI. Turn on Cross App Access before relying on it: without it, the form warns that tool calls fail until an admin enables it. See [Enterprise-managed authorization](#enterprise-managed-authorization).

4. **Test the connection.** Click **Test connection** to probe the server before saving. For **Enterprise managed (ID-JAG)**, this checks authorization discovery without requesting a user token or listing tools. Other authentication types validate the connection and return discovered tools.

5. Click <kbd>Create</kbd>.

### How OAuth Client Credentials works

**Toqan Router** mints an access token when it needs one, never on a timer:

* **Test connection** runs.
* The server syncs its tools.
* A tool is executed through an **Agent** or an **MCP Gateway**.

Each token is stored encrypted with the workspace key and reused until it is close to expiry.

| Situation | What happens |
| - | - |
| The authorization server returns `expires_in` | The token is renewed 30 seconds before it elapses. |
| The authorization server omits `expires_in` | The token is reused for 5 minutes. |
| Renewal | The client credentials grant has no refresh token, so **Toqan Router** exchanges the same **Client ID** and **Client secret** for a new token. Nothing needs rotating by hand. |

Tokens are not shared between **MCP Servers**. Each server holds its own token and its own upstream connection, and a change to the connection or authentication settings replaces the token.

**Scopes** are optional and space-separated. **Toqan Router** sends them as the `scope` parameter on the token request (`auth.oauth.scopes` in the API). When the token endpoint refuses the credentials, tool calls through an **MCP Gateway** fail with `UPSTREAM_CREDENTIALS_UNAVAILABLE` plus the endpoint's answer, and **Test connection** and sync report the same message.

> [!NOTE]
>
> The token endpoint must answer directly over `https`, from a host reachable on the public internet, and redirects from the **Token URL** are refused. The same rule applies to the server URL: requests to private, loopback, and link-local networks are refused. An **MCP Gateway** has no domain allow-list, so the public-internet rule is the only outbound restriction.

### Per-user OAuth

Use **Per-user OAuth** when each user must act on the upstream server with their own account. **Toqan Router** runs the OAuth authorization code flow with PKCE once per user and stores each user's tokens separately. Tool calls from **Agents** and **MCP Gateways** then use the token of the user who made the call.

Select **Per-user OAuth** under **Authentication**. Every client field is optional and sits under **Advanced configuration**:

| Field | Value |
| - | - |
| **Client ID** | A client pre-registered at the upstream authorization server. Leave blank to let **Toqan Router** register a client automatically, through a Client ID Metadata Document when the authorization server supports one, otherwise through dynamic client registration. |
| **Client secret** | The secret of a pre-registered confidential client. Requires a **Client ID**. |
| **Authorization server issuer** | Required when a **Client ID** is set. Pins discovery to this issuer. |
| **Resource URL** | The resource identifier published by the upstream server. Leave blank to use the server URL. It must use the same scheme, host, and effective port as the server URL. |
| **Scopes** | Optional scopes to request, separated by spaces. |

**Test connection** checks that the authorization server can be discovered and, when set, that its issuer matches. It does not connect an account or list tools. Use fixed URLs for both **URL** and **Resource URL**: per-user servers reject `{{variable}}` placeholders in either field.

For API requests, set `auth.type` to `MCP_AUTH_TYPE_PER_USER_OAUTH` and optionally provide `auth.oauth` with `client_id`, `client_secret`, `issuer`, `resource`, and `scopes`. `token_url` is rejected, since the token endpoint is discovered from the resource. See [Create an MCP server](/docs/reference/mcp-gateway/create-an-mcp-server.md).

**Connect an account**

* **From the server detail page**: click **Connect account** and complete the sign-in at the upstream server. The page then shows **Connected** and syncs the server's tools. When a stored connection stops working, the button reads **Reconnect account**.
* **From an Agent**: an attached per-user server without a connection shows **Connect account** in the **Agent** configuration. An **Agent** run that reaches the server without a connection fails with `Connect your account to this MCP server before using it.`
* **Through an MCP Gateway**: when the calling user has no connection, the gateway asks the MCP client to open a connection link (`Connect your account for <server>`). Clients without URL elicitation support receive the link in a tool error instead. The link opens a **Connect account** page listing the server, resource, client, and scopes. Click **Continue** to authorize or **Decline** to stop the pending operation, then retry the request from the client. Requests that carry no user identity fail with `This server requires a signed-in user connection.`

**Manage connections**

Open **AI Gateway** → **Connections** to list account connections with their **Server**, **Scopes**, and **Status** (**Connected**, **Connected via SSO**, **Disconnected**, or **Reconnect required**). Click **Disconnect** to stop using an account; it can be reconnected later. Rows from [enterprise-managed authorization](#enterprise-managed-authorization) show **Connected via SSO** while active and offer **Revoke token** instead, which drops the stored token so a new one is requested through SSO on the next call. Admins can switch between **My connections** and **All users**. The server detail menu also offers **Disconnect account** for the signed-in user.

### Enterprise-managed authorization

Use **Enterprise managed** when the organization's identity provider controls which users can access an upstream **MCP Server**. The **MCP Gateway** exchanges an identity assertion grant (ID-JAG) from that provider for a user access token at the server's authorization server. Users sign in through SSO without a separate consent flow for each upstream server.

An organization whose identity provider cannot issue ID-JAGs uses [Per-user OAuth](#per-user-oauth): each user connects their own upstream account, without an SSO connection or an identity provider change.

Configure Cross App Access on an enabled SSO connection before creating, duplicating, or switching a server to **Enterprise managed**. The identity provider must support ID-JAG issuance. The upstream server must publish OAuth protected resource metadata, and its authorization server must accept ID-JAGs through the JWT bearer grant. SSO support alone does not meet these requirements.

Select **Enterprise managed (ID-JAG)** under **Authentication**, then fill in:

| Field | Value |
| - | - |
| **Client ID** | The OAuth client registered at the upstream server's authorization server. |
| **Client secret** | That client's secret. When editing, leave blank to retain the stored secret if the client ID is unchanged. Enter a new secret when changing the client ID. |
| **Scopes** | Optional scopes to request, separated by spaces. |
| **Authorization server issuer** (under **Advanced configuration**) | Optional. Pins discovery to this issuer; **Test connection** fails when the server's authorization server is a different one. |
| **Resource URL** (under **Advanced configuration**) | The resource identifier published by the upstream server. Leave blank to use the server URL. It must use the same scheme, host, and effective port as the server URL. |

These client credentials belong to the upstream authorization server. Configure the identity provider's client separately in SSO settings. **Toqan Router** discovers the upstream token endpoint automatically and stores the client secret encrypted.

For API requests, set `auth.type` to `MCP_AUTH_TYPE_ENTERPRISE_MANAGED` and provide `auth.oauth` with `client_id`, `client_secret`, and optional `resource` and `scopes`, the same object OAuth Client Credentials uses without `token_url`. The earlier `auth.enterprise` object is superseded: requests that still send it are accepted (`auth.oauth` wins when both are present), and responses return it next to `auth.oauth` until it is removed. See [Create an MCP server](/docs/reference/mcp-gateway/create-an-mcp-server.md).

Use fixed URLs for both **URL** and **Resource URL**. Enterprise-managed servers reject `{{variable}}` placeholders in either field.

After saving, sign in through SSO and sync the server to discover its tools. Use **Run tool** to check access with the signed-in user's identity. A successful **Test connection** only confirms discovery; it does not confirm that the client credentials work or that the identity provider allows access.

### Troubleshoot enterprise-managed authorization

| Error or symptom | Action |
| - | - |
| **Enterprise managed** is unavailable in the form | Enable an SSO connection and turn on Cross App Access. |
| `ENTERPRISE_AUTH_REAUTH_REQUIRED` or `reauth_required` | Sign in through SSO again. Sign-ins from before Cross App Access was enabled do not carry the required identity assertion. Check the identity provider's Refresh Token grant if access fails again after the sign-in expires. |
| `ENTERPRISE_AUTH_REQUIRES_USER` | Connect as an SSO user through gateway OAuth or a user-owned API key. Service-account keys and federation-minted keys cannot authorize enterprise-managed tool calls. |
| `ENTERPRISE_AUTH_POLICY_DENIED` | Check the user's app assignment and access policy in the identity provider. |
| `ENTERPRISE_AUTH_IDP_CLIENT_REJECTED` | Check the client credentials in SSO settings. |
| `ENTERPRISE_AUTH_CLIENT_REJECTED` | Check the client credentials on the **MCP Server**. |
| `ENTERPRISE_AUTH_METADATA_UNAVAILABLE` or `ENTERPRISE_AUTH_UNSUPPORTED` | Check the resource URL, metadata availability, and upstream authorization server's ID-JAG support. |
| `ENTERPRISE_AUTH_TEMPORARILY_UNAVAILABLE` or `ENTERPRISE_AUTH_RATE_LIMITED` | Retry shortly. |

## Create from an OpenAPI Specification

Turn a REST API that publishes an OpenAPI document into an **MCP Server** without writing or hosting one. Each selected operation becomes a tool, and calling the tool sends the HTTP request to the API.

1. Select **Create** in the **MCP Servers** tab, then **Create** on the **OpenAPI** card.
2. Under **Specification**, pick a **Source**:
   * **URL**: the address of the document. **Sync** fetches it again to pick up changes.
   * **Paste or upload**: a JSON or YAML document. The document is not stored, so changing the tools later means loading the document again in **Settings**.
3. Click **Load operations** and select the operations to expose. Operations that cannot become tools are listed under **Skipped operations** with the reason.
4. Check **Base URL**: requests are sent there. It is filled from the first `servers` entry of the document.
5. Configure **Authentication** with **None**, **Static Headers** (for example an API key header or `Authorization: Bearer {{token}}`), or **OAuth Client Credentials**.
6. Click <kbd>Create</kbd>. The tools are available right away; there is nothing to sync.

| OpenAPI | Tool |
| - | - |
| `operationId` | Tool name; the method and path when there is none. Limited to letters, digits, `_` and `-`, at most 64 characters |
| `summary` and `description` | Tool description |
| Path, query, header and cookie parameters | Arguments |
| JSON request body | Its properties as arguments, or a single `body` argument when they collide with a parameter or the body is not a plain object |
| `GET` and `HEAD` | Read-only hint |
| `DELETE` | Destructive hint |

The response body is returned as the tool result. A response with status 400 or higher is returned as a tool error that includes the status and body.

Limits:

* OpenAPI 3.0, 3.1 and 3.2. Swagger 2.0 documents are rejected.
* JSON request bodies only.
* At most 300 tools per server and a 10 MB document.
* `$ref` to other files or URLs is not followed; operations that use one are skipped.
* **Enterprise managed** authentication is not available.
* IDE exports reach OpenAPI servers only through an **MCP Gateway**.

Through the API, set `connection.type` to `MCP_CONNECTION_TYPE_OPENAPI`, `connection.url` to the base URL, and `openapi` to either `document` or `document_url`. `openapi.operations` lists the tool names to expose; leave it empty to expose every operation.

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://router.toqan.ai/eu/v2/mcp-servers \
  -H "Authorization: Bearer $TOQAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "petstore",
    "display_name": "petstore",
    "connection": {
      "type": "MCP_CONNECTION_TYPE_OPENAPI",
      "url": "https://petstore3.swagger.io/api/v3"
    },
    "auth": { "type": "MCP_AUTH_TYPE_NONE" },
    "default_tool_exposure": { "mode": "MCP_TOOL_EXPOSURE_MODE_ALL" },
    "openapi": {
      "document_url": "https://petstore3.swagger.io/api/v3/openapi.json",
      "operations": ["getPetById", "findPetsByStatus"]
    }
  }'
```

## After Creation

Open **Explorer** to browse the server's **Tools**, **Skills**, and **Resources**. All three tabs appear on every server. **Resources** includes both individual resources and URI templates. Open **Overview** for sync status and traffic, or **Settings** for the connection, authentication, and project access.

From the server detail page:

* **View discovered tools** under **Explorer → Tools**. Select a tool on the left to inspect its description, arguments, and schema on the right. Use **Filters** to find **Read only**, **Destructive**, **Idempotent**, or **Open world** tools.
* **Inspect resource content** under **Explorer → Resources**. Select a resource to read its current content. For a template, enter the parameters and select **Read resource**. Use **Refresh** to read it again. For an MCP App, inspect its HTML under **Source** and open a related tool to see the inputs it accepts.
* **Control tool exposure** when the server is linked to an **MCP Gateway**: select the tool checkboxes on the gateway's **MCP Servers** tab (see [MCP Gateways](/docs/ai-gateway/mcp-portal/mcp-gateways.md#after-creation)). Exposure also accepts a **read-only** filter in the API. It keeps only the tools the upstream server annotates as read-only (`readOnlyHint`). The gateway trusts that annotation and does not check what the tool does.
* **Test a tool** by filling in the arguments and clicking **Run tool**. The test invokes the tool on the upstream server and shows the response.
* Select **Sync** in **Explorer** to refresh the server's catalog. Newly discovered tools appear in **Tools**, and previously discovered tools that are missing are marked `MISSING`.
* Open the row menu for **Sync**, **Edit**, or **Delete**. The detail menu also offers **Share** to copy the page link and **Duplicate** to reuse the connection and tool settings under a new key.
* **Configure project access** in the **Settings** tab. Under **Access control**, pick **All projects** to share the server with the whole workspace, or **Custom** and set each project to **Enabled** or **Disabled**. An **MCP Gateway** can only link servers whose project access covers it.

Which roles can create, edit, and delete MCP Servers is listed in [Roles and Permissions](/docs/ai-gateway/mcp-portal/mcp-gateways.md#roles-and-permissions).

## Delete a Server

Unlink the server from every **MCP Gateway** first: deleting a server that an **MCP Gateway** still links fails.

> [tab] AI Studio
>
> On the **MCP Gateway**'s **MCP Servers** tab, open the server's row menu and choose **Remove from gateway**.
>
> Then delete the server from the **Delete** item in the server list row menu, the **Delete** item in the detail page overflow menu, or the <kbd>Delete Server</kbd> button in the **Settings** form footer. Each asks for confirmation.

> [tab] API & SDK
>
> **1. Unlink the server**
>
> Read the gateway's current links with [GET /v2/mcp-gateways/{id}](/docs/reference/mcp-gateway/retrieve-an-mcp-gateway.md), then [PATCH /v2/mcp-gateways/{id}](/docs/reference/mcp-gateway/update-an-mcp-gateway.md) with `server_links` set to the links minus this server. An empty `server_links` array changes nothing, so when this server is the gateway's only link, send `clear_server_links: true` instead, which unlinks all servers on the gateway. Each entry carries `mcp_server_id`, `alias` and `tool_exposure`. See [MCP Gateways](/docs/ai-gateway/mcp-portal/mcp-gateways.md) for the `server_links` rules.
>
> ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
> curl -X PATCH https://router.toqan.ai/eu/v2/mcp-gateways/<gateway-id> \
>   -H "Authorization: Bearer $TOQAN_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "server_links": [
>       {
>         "mcp_server_id": "mcp_server_01JQ0K5R8N2ZC7X4M9T3V6HWBE",
>         "alias": "weather",
>         "tool_exposure": { "mode": "MCP_TOOL_EXPOSURE_MODE_ALL" }
>       }
>     ]
>   }'
> ```
>
> **2. Delete the server**
>
> ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
> curl -X DELETE https://router.toqan.ai/eu/v2/mcp-servers/<server-id> \
>   -H "Authorization: Bearer $TOQAN_API_KEY"
> ```
>
> > [!TIP]
> >
> > See the full [Delete an MCP server API reference](/docs/reference/mcp-gateway/delete-an-mcp-server.md).

## Monitor Usage

The **Overview** tab reports tool usage for the server, aggregated across every **MCP Gateway** that exposes it. It opens on the last 7 days, and the badge next to the heading carries the sync state, the discovered tool count, and the time of the last sync.

The cards and charts match the ones on the gateway **Overview**. See [Monitor Usage](/docs/ai-gateway/mcp-portal/mcp-gateways.md#monitor-usage) for what each one measures. The scope differs: these numbers cover the server across every gateway it is linked to, rather than a single gateway.

**Performance** adds **Tool latency**, P50 and P95 over the window, and **Calls by gateway** with each gateway's share.

## Attach to a Gateway

Link a server to an **MCP Gateway** to bundle it with other servers behind a single client-facing endpoint. See [MCP Gateway](/docs/ai-gateway/mcp-portal/mcp-gateways.md) for details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.