> ## Documentation Index
> Fetch the complete documentation index at: https://tyk-tt17611-iam-auth.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage MCP Servers with Tyk Operator

> Use the TykMcpProxyDefinition CRD in Tyk Operator to proxy, secure, and apply rate limits to Model Context Protocol (MCP) servers in Kubernetes using a GitOps workflow

## Overview

From Tyk Operator v1.4.0, you can manage [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers declaratively using the `TykMcpProxyDefinition` custom resource. The Operator reconciles the resource into a Tyk-managed MCP proxy, so the same GitOps workflow you use for `ApiDefinition` and `TykOasApiDefinition` resources also applies to MCP servers.

## Prerequisites

* Tyk Operator v1.4.0 or later installed in the cluster.
* A Tyk Gateway / Dashboard with MCP proxy support enabled.
* A `ConfigMap` containing the MCP proxy's OAS document under a known key (for example `mcp.json`).

## Custom Resource Reference

**Kind:** `TykMcpProxyDefinition`

**Group / Version:** `tyk.tyk.io/v1alpha1`

**Scope:** Namespaced

**Short name:** `tykmcp` (usable as `kubectl get tykmcp`)

### Spec

| Field                           | Type              | Description                                                                                                                                                                               |
| :------------------------------ | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contextRef`                    | object (optional) | Reference (`name`, `namespace`) to an [OperatorContext](/api-management/automations/operator#multi-tenancy-in-tyk) used when reconciling this resource. Use this for multi-tenant setups. |
| `tykMCP.configmapRef.name`      | string            | Name of the `ConfigMap` that holds the MCP proxy's OAS document.                                                                                                                          |
| `tykMCP.configmapRef.namespace` | string (optional) | Namespace of the ConfigMap. Defaults to the resource's namespace.                                                                                                                         |
| `tykMCP.configmapRef.keyName`   | string            | Key inside the ConfigMap's `data` map that contains the OAS document.                                                                                                                     |

### Status

| Field                      | Description                                       |
| :------------------------- | :------------------------------------------------ |
| `id`                       | Tyk-side API identifier.                          |
| `name`                     | API name as registered in Tyk.                    |
| `listenPath`               | Base path on Tyk where the MCP proxy is served.   |
| `domain`                   | Custom domain, if configured in the OAS document. |
| `enabled`                  | Whether the API is active.                        |
| `latestTransaction.status` | Result of the last reconciliation.                |
| `linkedByPolicies`         | Security policies that reference this MCP proxy.  |

`kubectl get tykmcp -o wide` surfaces `Domain`, `ListenPath`, `Enabled`, and `SyncStatus` as printed columns.

## Quick Start

The example below proxies a mock MCP server reachable at `http://mock-mcp-server.default.svc.cluster.local:7878`.

### 1. Store the MCP proxy's OAS document in a `ConfigMap`

```yaml expandable theme={null}
apiVersion: v1
kind: ConfigMap
metadata:
  name: mock-mcp-config
  namespace: default
data:
  mcp.json: |
    {
      "info": {
        "title": "Mock MCP Proxy",
        "version": "1.0.0"
      },
      "openapi": "3.0.3",
      "components": {},
      "paths": {},
      "x-tyk-api-gateway": {
        "info": {
          "id": "mock-mcp-api-id-001",
          "name": "Mock MCP Proxy",
          "state": {
            "active": true
          }
        },
        "upstream": {
          "url": "http://mock-mcp-server.default.svc.cluster.local:7878"
        },
        "server": {
          "listenPath": {
            "value": "/mock-mcp/",
            "strip": true
          }
        }
      }
    }
```

### 2. Create the `TykMcpProxyDefinition`

```yaml theme={null}
apiVersion: tyk.tyk.io/v1alpha1
kind: TykMcpProxyDefinition
metadata:
  name: mock-mcp
  namespace: default
spec:
  tykMCP:
    configmapRef:
      name: mock-mcp-config
      namespace: default
      keyName: mcp.json
```

Apply both manifests and verify with:

```bash theme={null}
kubectl apply -f mock-mcp-config.yaml -f mock-mcp.yaml
kubectl get tykmcp -o wide
```

If you update the `ConfigMap`, the Operator detects the change (via `latestConfigMapHash`) and pushes the new OAS document to Tyk on the next reconciliation.

## Managing a proxy generated from a Tyk-managed REST API

The example above proxies a remote MCP server. The same `TykMcpProxyDefinition` and `ConfigMap` pattern also manages a proxy [generated directly from a Tyk-managed REST API](/ai-management/mcps/api-to-mcp), which the Operator reconciles identically. Two things differ in the OAS document itself:

* `upstream.url` is an adapter target (for example `tyk://a1b2c3d4e5f647a8b9c0d1e2f3a4b5c6/mcp`) instead of a remote `https://` URL. See [The upstream adapter target](/ai-management/mcp-gateway/api-to-mcp-definitions#the-upstream-adapter-target) for what this value means.
* The document carries an `x-tyk-mcp-server` extension alongside `x-tyk-api-gateway`, holding the tool catalog: which of the source API's operations are exposed as tools, and any name, description, or parameter overrides.

<Note>
  Authoring this via the Operator means hand-writing the tool catalog in the `x-tyk-mcp-server` extension yourself. The [Dashboard's guided wizard](/ai-management/mcp-gateway/managing-proxies#tyk-api) has no Operator equivalent. See [REST API to MCP x-tyk-mcp-server extension](/ai-management/mcp-gateway/api-to-mcp-definitions) for the full field reference.
</Note>

### Example

This assumes a Tyk-managed REST API with ID `a1b2c3d4e5f647a8b9c0d1e2f3a4b5c6` already exists, managed via the Dashboard, the Gateway API, or its own `TykOasApiDefinition` resource.

```yaml expandable theme={null}
apiVersion: v1
kind: ConfigMap
metadata:
  name: orders-mcp-config
  namespace: default
data:
  mcp.json: |
    {
      "info": {
        "title": "Orders MCP Proxy",
        "version": "1.0.0"
      },
      "openapi": "3.0.3",
      "components": {},
      "paths": {},
      "x-tyk-api-gateway": {
        "info": {
          "id": "orders-mcp-api-id-001",
          "name": "Orders MCP Proxy",
          "state": {
            "active": true
          }
        },
        "upstream": {
          "url": "tyk://a1b2c3d4e5f647a8b9c0d1e2f3a4b5c6/mcp"
        },
        "server": {
          "listenPath": {
            "value": "/orders-mcp/",
            "strip": true
          },
          "authentication": {
            "enabled": true,
            "securitySchemes": {
              "bearerAuth": { "enabled": true }
            }
          }
        }
      },
      "x-tyk-mcp-server": {
        "primitives": [
          {
            "source": { "operationId": "getOrder" },
            "name": "get_order",
            "description": "Look up a single order by ID.",
            "allow": true
          },
          {
            "source": { "operationId": "listOrders" },
            "name": "list_orders",
            "allow": true
          }
        ]
      }
    }
```

```yaml theme={null}
apiVersion: tyk.tyk.io/v1alpha1
kind: TykMcpProxyDefinition
metadata:
  name: orders-mcp
  namespace: default
spec:
  tykMCP:
    configmapRef:
      name: orders-mcp-config
      namespace: default
      keyName: mcp.json
```

Apply both manifests the same way as the Quick Start example above. `SecurityPolicy` resources reference this proxy identically to one fronting a remote MCP server. Nothing about policy configuration differs based on the proxy's upstream type.

## Securing an MCP Proxy with a `SecurityPolicy`

The `SecurityPolicy` resource is extended with two MCP-aware blocks inside each entry of `access_rights_array`:

* `mcp_access_rights` — allow/block lists for MCP tools, resources, and prompts.
* `json_rpc_methods_access_rights` — allow/deny lists keyed by JSON-RPC method.
* `mcp_primitives` — per-primitive rate limits.
* `json_rpc_methods` — rate limits keyed by JSON-RPC method (`tools/call`, `tools/list`, `resources/read`, `prompts/get`, ...).

For background on the underlying gateway feature, see the [MCP proxy policies documentation](/ai-management/mcp-gateway/policies).

Reference an MCP proxy by setting `kind: TykMcpProxyDefinition` on the access entry.

### Allow / block lists

```yaml expandable theme={null}
apiVersion: tyk.tyk.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: mcp-acl-policy
  namespace: default
spec:
  name: MCP ACL Policy
  state: active
  active: true
  access_rights_array:
    - name: mock-mcp
      namespace: default
      kind: TykMcpProxyDefinition
      mcp_access_rights:
        tools:
          allowed: ["get_users", "get_products", "generate_uuid"]
          blocked: ["delete_user", "slow_response"]
        resources:
          allowed: ["users://list", "products://catalog"]
        prompts:
          allowed: ["user_management", "ecommerce_assistant"]
```

### Per-primitive and JSON-RPC rate limits

```yaml expandable theme={null}
apiVersion: tyk.tyk.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: mcp-ratelimit-policy
  namespace: default
spec:
  name: MCP Rate Limit Policy
  state: active
  active: true
  access_rights_array:
    - name: mock-mcp
      namespace: default
      kind: TykMcpProxyDefinition
      mcp_primitives:
        - type: tool
          name: get_users
          limit:
            rate: "10"
            per: "60"
        - type: resource
          name: "users://list"
          limit:
            rate: "20"
            per: "60"
        - type: prompt
          name: "user_management"
          limit:
            rate: "3"
            per: "60"
      json_rpc_methods:
        - name: tools/call
          limit:
            rate: "100"
            per: "60"
        - name: resources/read
          limit:
            rate: "200"
            per: "60"
```

<Note>
  `rate` and `per` are declared as `Float64` strings in the CRD schema — quote the values in YAML or `kubectl apply` will reject the manifest.
</Note>

`type` in `mcp_primitives` accepts `tool`, `resource`, or `prompt`.

## Verifying

```bash theme={null}
kubectl get tykmcp -o wide
kubectl describe tykmcp mock-mcp
```

The `Status` block reports the reconciled `listenPath`, `domain`, `enabled` flag, and `latestTransaction.status`. Failed reconciliations also surface as Kubernetes events on the resource.
