# Enkrypt AI Secure MCP Gateway

![enkrypt-secure-mcp-gateway-hld](./docs/images/enkrypt-secure-mcp-gateway-hld.png)

> **📖 Featured Blog Post:** Learn how the Secure MCP Gateway prevents top attacks and vulnerabilities in our latest blog:
>
> **[How Enkrypt's Secure MCP Gateway and MCP Scanner Prevent Top Attacks](https://www.enkryptai.com/blog/how-enkrypts-secure-mcp-gateway-and-mcp-scanner-prevent-top-attacks)**
>
> Discover real-world attack scenarios, security best practices, and how our gateway protects your AI applications.

## Overview

This Secure MCP Gateway is built with authentication, automatic tool discovery, caching, and guardrail enforcement.

It sits between your MCP client and MCP servers. So, by it's nature it itself also acts as an MCP server as well as an MCP client :)

When your MCP client connects to the Gateway, it acts as an MCP server. When the Gateway connects to the actual MCP server, it acts as an MCP client.

- [Pypi Package](https://pypi.org/project/secure-mcp-gateway/)

- [Docker Image](https://hub.docker.com/r/enkryptai/secure-mcp-gateway)

- Also see:
  - [CLI-Commands-Reference.md](./CLI-Commands-Reference.md) for the list of commands and their usage
  - [API-Reference.md](./API-Reference.md) for the list of API endpoints and their usage
  - [MCP Gateway Setup Notebook](./mcp_gateway_setup.ipynb) for a complete walkthrough of all the essential commands

## Table of Contents

- [1. Features 🚀](#1-features)
- [2. High level steps of how the MCP Gateway works 🪜](#2-high-level-steps-of-how-the-mcp-gateway-works)
- [3. Prerequisites 🧩](#3-prerequisites)
- [4. Gateway Setup 👨‍💻](#4-gateway-setup)
- [5. (Optional) OpenTelemetry Setup 📊](#5-optional-opentelemetry-setup)
- [6. Verify Installation and check the files generated ✅](#6-verify-installation-and-check-the-files-generated)
- [7. Edit the Gateway config as needed ✏️](#7-edit-the-gateway-config-as-needed)
- [8. CLI Quick Start Guide 🖥️](#8-cli-quick-start-guide)
- [9. (Optional) Add GitHub MCP Server to the Gateway 🤖](#9-optional-add-github-mcp-server-to-the-gateway)
- [9.1 (Optional) Connect to MCP Servers with OAuth 🔐](#91-optional-connect-to-mcp-servers-with-oauth)
- [10. (Optional) Protect GitHub MCP Server and Test Echo Server 🔒](#10-optional-protect-github-mcp-server-and-test-echo-server)
- [11. Recommendations for using Guardrails 💡](#11-recommendations-for-using-guardrails)
- [12. Other tools available 🔧](#12-other-tools-available)
- [13. Deployment Patterns 🪂](#13-deployment-patterns)
- [14. Uninstall the Gateway 🗑️](#14-uninstall-the-gateway)
- [15. Troubleshooting 🕵](#15-troubleshooting)
- [16. Known Issues being worked on 🏗️](#16-known-issues-being-worked-on)
- [17. Known Limitations ⚠️](#17-known-limitations)
- [18. Contribute 🤝](#18-contribute)
- [19. Testing 🧪](#19-testing)
- [20. License](#20-license)

## 1. Features

![enkrypt-secure-mcp-gateway-features](./docs/images/enkrypt-secure-mcp-gateway-features.png)

Below are the list of features Enkrypt AI Secure MCP Gateway provides:

1. **Authentication**: We use Unique Key to authenticate with the Gateway. We also use Enkrypt API Key if you want to protect your MCPs with Enkrypt Guardrails. Additionally, a secure `admin_apikey` (256-character random string) is automatically generated for administrative REST API operations.

2. **Ease of use**: You can configure all your MCP servers locally in the config file or better yet in Enkrypt *(Coming soon)* and use them in the Gateway by using their name

3. **Dynamic Tool Discovery**: The Gateway discovers tools from the MCP servers dynamically and makes them available to the MCP client

4. **Restrict Tool Invocation**: If you don't want all tools to be accessible of a an MCP server, you can restrict them by explicitly mentioning the tools in the Gateway config so that only the allowed tools are accessible to the MCP client

5. **Caching**: We cache the user gateway config and tools discovered from various MCP servers locally or in an external cache server like KeyDB if configured to improve performance

6. **Guardrails**: You can configure guardrails for each MCP server in Enkrypt both on input side (before sending the request to the MCP server) and output side (after receiving the response from the MCP server)

7. **Logging**: We log every request and response from the Gateway locally in your MCP logs and also forward them to Enkrypt *(Coming soon)* for monitoring. This enables you to see all the calls made in your account, servers used, tools invoked, requests blocked, etc.

### 1.1 Guardrails

![enkrypt-secure-mcp-gateway-guardrails](./docs/images/enkrypt-secure-mcp-gateway-guardrails.png)

**Input Protection:** Topic detection, NSFW filtering, toxicity detection, injection attack prevention, keyword detection, policy violation detection, bias detection, and PII redaction (More coming soon like system prompt protection, copyright protection, etc.)

**Output Protection:** All input protections plus adherence checking and relevancy validation (More coming soon like hallucination detection, etc.) We also auto unredact the response if it was redacted on input.

### 1.2 Concepts

- MCP Config is an array of MCP servers like `mcp_server_1`, `mcp_server_2`, `mcp_server_3` etc.
  - Each config has a unique ID

- User is a user of the gateway with unique email and ID

- A project is a collection of users that share an MCP Config
  - Project has a name and unique ID
  - The MCP Config can be updated or can be pointed to a different config by the Admin
  - Users can be added to multiple projects

- An API Key is created for a user and project combination
  - A user can have different API Keys for different projects
  - This API Key is used to authenticate the user and identify the right project and MCP Config

- *See [6.5 Example config file generated](#65-example-config-file-generated) and [7. Edit the Gateway config as needed](#7-edit-the-gateway-config-as-needed) for schema reference*

## 2. High level steps of how the MCP Gateway works

![Local Gateway with Remote Guardrails Flow](./docs/images/enkryptai-apiaas-MCP%20Gateway%20Local.drawio.png)

<br>
<details>
<summary><strong>🪜 Steps </strong></summary>
<br>

1. Your MCP client connects to the Secure MCP Gateway server with API Key (handled by `src/secure_mcp_gateway/gateway.py`).

2. Gateway server fetches gateway config from local `enkrypt_mcp_config.json` file or remote Enkrypt Auth server *(Coming soon)*.

    - It caches the config locally or in an external cache server like KeyDB if configured to improve performance.

3. If input guardrails are enabled, request is validated before the tool call (handled by `src/secure_mcp_gateway/guardrail.py`).
   - Request is blocked if it violates any of the configured guardrails and the specific detector is configured to block.

4. Requests are forwarded to the Gateway Client (handled by `src/secure_mcp_gateway/client.py`).

5. The Gateway client forwards the request to the appropriate MCP server (handled by `src/secure_mcp_gateway/client.py`).

6. The MCP server processes the request and returns the response to the Gateway client.

7. If it was a discover tools call, the Gateway client caches the tools locally or in an external cache server like KeyDB if configured. It then forwards the response to the Gateway server.

8. The Gateway server receives the response from the Gateway client and if output guardrails are enabled, it validates the response against the configured guardrails (handled by `src/secure_mcp_gateway/guardrail.py`).

    - Response is blocked if it violates any of the configured guardrails and the specific detector is configured to block.

9. The Gateway server forwards the response back to the MCP client if everything is fine.

</details>

## 3. Prerequisites

<details>
<summary><strong>🔗 Dependencies </strong></summary>

- `Git 2.43` or higher

- `Python 3.11` or higher installed on your system and is accessible from the command line using either `python` or `python3` command

- `pip 25.0.1` or higher is installed on your system and is accessible from the command line using either `pip` or `python -m pip` command

- `uv 0.7.9` or higher is installed on your system and is accessible from the command line using either `uv` or `python -m uv` command

<br>
<details>
<summary><strong>🔍 Check versions </strong></summary>

- Check if Python, pip and uv are installed

- If any of the below commands fail, please refer the respective documentation to install them properly

```bash

# ------------------

# Python

# ------------------

python --version

# Example output
Python 3.13.3

# If not, install python from their website and run the version check again

# ------------------

# pip

# ------------------
pip --version

# Example output
pip 25.0.1 from C:\Users\PC\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pip (python 3.13)

# If not, try the following and run the version check again
python -m ensurepip

# ------------------

# uv

# ------------------

uv --version

# Or run with "python -m" if uv is not found directly

# If this works, use "python -m" before all uv commands from now on
python -m uv --version

# Example output
uv 0.7.9 (13a86a23b 2025-05-30)

# If not, try the following and run the version check again
python -m pip install uv

```

</details>
</details>

<!-- - Set `PYTHONPATH` in your system environment variables

  - For reference, see [How to Add Python to PATH on Windows, Linux, and macOS](https://phoenixnap.com/kb/add-python-to-path)

    - In Windows, if you can't find python in the folder mentioned in the article, try `%USERPROFILE%\AppData\Local\Microsoft\WindowsApps` -->

- Install **Claude Desktop** as the MCP Client from [their website](https://claude.ai/download) if you haven't already and login to it

  - *If you are using Linux and cannot run any [unofficial version](https://www.greghilston.com/post/claude-desktop-on-linux/) of Claude Desktop, you can use [any supported MCP Client](https://modelcontextprotocol.io/quickstart/server#testing-your-server-with-claude-for-desktop) to test the Gateway. If it does not support mcp cli `mcp install` command, then go through the scripts code and run the commands supported manually.*

- Any other dependencies required for the MCP servers we want to proxy requests to

  - Follow the instructions of the respective MCP server to install its dependencies

  - Like `Node.js`, `npx`, `docker`, etc.

- (Optional) A cache server like KeyDB installed and running (If you want to cache externally and not locally)

<br>
<details>
<summary><strong>🔒 Optional Protection with Enkrypt Guardrails </strong></summary>
<br>

If you want to protect your MCPs with Enkrypt Guardrails, you need to do the following:

- Create a new account if you don't have one. It's free! 🆓 No credit card required 💳🚫

- An `ENKRYPT_API_KEY` which you can get from [Enkrypt Dashboard Settings](https://app.enkryptai.com/settings)

- To protect your MCPs with Guardrails, you can use the default sample Guardrail `Sample Airline Guardrail` to get started or you can create your own custom Guardrail

- To configure custom Guardrails, you need to either login to Enkrypt AI App or use the APIs/SDK

  - [Create Guardrails in Enkrypt AI App Dashboard ✅](https://app.enkryptai.com/guardrails)

  - [Create Guardrails using APIs](https://docs.enkryptai.com/guardrails-api-reference/endpoint/add-policy)

  - [Create Guardrails using SDK](https://docs.enkryptai.com/libraries/python/introduction#guardrails-policy-management)

  - [You can also use Enkrypt MCP Server 🤯 to create Guardrails and use them in the Gateway](https://github.com/enkryptai/enkryptai-mcp-server)

</details>

## 4. Gateway Setup

### 4.1 Local Installation with pip

<details>
<summary><strong>📦 Pip Installation Steps </strong></summary>

#### 4.1.1 Download and Install the Package

- Activate a virtual environment

  ```bash
  python -m venv .secure-mcp-gateway-venv

  # Activate the virtual environment
  # On Windows
  .secure-mcp-gateway-venv\Scripts\activate

  # On Linux/macOS
  source .secure-mcp-gateway-venv/bin/activate

  # Run the below to exit the virtual environment later if needed
  deactivate
  ```

- Install the package. For more info see [https://pypi.org/project/secure-mcp-gateway/](https://pypi.org/project/secure-mcp-gateway/)

  ```bash
  pip install secure-mcp-gateway
  ```

#### 4.1.2 Run the Generate Command

- **This generates the config file at `~/.enkrypt/enkrypt_mcp_config.json` on macOS and `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json` on Windows**

  ```bash
  secure-mcp-gateway generate-config
  ```

<details>
<summary><strong>🖨️ Example output</strong></summary>
<br>

```bash
Initializing Enkrypt Secure MCP Gateway
Initializing Enkrypt Secure MCP Gateway Common Utilities Module
Initializing Enkrypt Secure MCP Gateway Module
--------------------------------
SYSTEM INFO:
Using Python interpreter: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Scripts\python.exe
Python version: 3.13.3 (tags/v3.13.3:6280bb5, Apr  8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
Current working directory: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway
PYTHONPATH: Not set
--------------------------------
Installing dependencies...
All dependencies installed successfully.
Initializing Enkrypt Secure MCP Gateway Client Module
Initializing Enkrypt Secure MCP Gateway Guardrail Module
Error: Gateway key is required. Please update your mcp client config and try again.
Getting Enkrypt Common Configuration
config_path: C:\Users\PC\.enkrypt\enkrypt_mcp_config.json
example_config_path: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Lib\site-packages\secure_mcp_gateway\example_enkrypt_mcp_config.json
No enkrypt_mcp_config.json file found. Defaulting to example_enkrypt_mcp_config.json
--------------------------------
ENKRYPT_GATEWAY_KEY: ****NULL
enkrypt_log_level: info
is_debug_log_level: False
enkrypt_base_url: https://api.enkryptai.com
enkrypt_use_remote_mcp_config: False
enkrypt_api_key: ****_KEY
enkrypt_tool_cache_expiration: 4
enkrypt_gateway_cache_expiration: 24
enkrypt_mcp_use_external_cache: False
enkrypt_async_input_guardrails_enabled: False
--------------------------------
External Cache is not enabled. Using local cache only.
Initializing Enkrypt Secure MCP Gateway CLI Module
Generated default config at C:\Users\PC\.enkrypt\enkrypt_mcp_config.json

```

</details>

#### 4.1.3 Example of the generated config file

<details>
<summary><strong>🍎 Example file in macOS</strong></summary>
<br>

- This is an example of the default configuration file generated by the CLI on macOS:

```json
{
  "common_mcp_gateway_config": {
    "enkrypt_log_level": "INFO",
    "enkrypt_base_url": "https://api.enkryptai.com",
    "enkrypt_api_key": "YOUR_ENKRYPT_API_KEY",
    "enkrypt_use_remote_mcp_config": false,
    "enkrypt_remote_mcp_gateway_name": "enkrypt-secure-mcp-gateway-1",
    "enkrypt_remote_mcp_gateway_version": "v1",
    "enkrypt_mcp_use_external_cache": false,
    "enkrypt_cache_host": "localhost",
    "enkrypt_cache_port": 6379,
    "enkrypt_cache_db": 0,
    "enkrypt_cache_password": null,
    "enkrypt_tool_cache_expiration": 4,
    "enkrypt_gateway_cache_expiration": 24,
    "enkrypt_async_input_guardrails_enabled": false,
    "enkrypt_async_output_guardrails_enabled": false,
    "enkrypt_telemetry": {
      "enabled": true,
      "insecure": true,
      "endpoint": "http://localhost:4317"
    }
  },
  "mcp_configs": {
    "fcbd4508-1432-4f13-abb9-c495c946f638": {
      "mcp_config_name": "default_config",
      "mcp_config": [
        {
          "server_name": "echo_server",
          "description": "Simple Echo Server",
          "config": {
            "command": "python",
            "args": [
              "/Users/user/enkryptai/secure-mcp-gateway/venv/lib/python3.13/site-packages/secure_mcp_gateway/bad_mcps/echo_mcp.py"
            ]
          },
          "tools": {},
          "input_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "pii_redaction": false
            },
            "block": [
              "policy_violation"
            ]
          },
          "output_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "relevancy": false,
              "hallucination": false,
              "adherence": false
            },
            "block": [
              "policy_violation"
            ]
          }
        }
      ]
    }
  },
  "projects": {
    "3c09f06c-1f0d-4153-9ac5-366397937641": {
      "project_name": "default_project",
      "mcp_config_id": "fcbd4508-1432-4f13-abb9-c495c946f638",
      "users": [
        "6469a670-1d64-4da5-b2b3-790de21ac726"
      ],
      "created_at": "2025-07-16T17:02:00.406877"
    }
  },
  "users": {
    "6469a670-1d64-4da5-b2b3-790de21ac726": {
      "email": "default@example.com",
      "created_at": "2025-07-16T17:02:00.406902"
    }
  },
  "apikeys": {
    "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat": {
      "project_id": "3c09f06c-1f0d-4153-9ac5-366397937641",
      "user_id": "6469a670-1d64-4da5-b2b3-790de21ac726",
      "created_at": "2025-07-16T17:02:00.406905"
    }
  }
}

```

</details>
<details>
<summary><strong>🪟 Example file in Windows</strong></summary>
<br>

- This is an example of the default configuration file generated by the CLI on Windows:

```json
{
  "common_mcp_gateway_config": {
    "enkrypt_log_level": "INFO",
    "enkrypt_base_url": "https://api.enkryptai.com",
    "enkrypt_api_key": "YOUR_ENKRYPT_API_KEY",
    "enkrypt_use_remote_mcp_config": false,
    "enkrypt_remote_mcp_gateway_name": "enkrypt-secure-mcp-gateway-1",
    "enkrypt_remote_mcp_gateway_version": "v1",
    "enkrypt_mcp_use_external_cache": false,
    "enkrypt_cache_host": "localhost",
    "enkrypt_cache_port": 6379,
    "enkrypt_cache_db": 0,
    "enkrypt_cache_password": null,
    "enkrypt_tool_cache_expiration": 4,
    "enkrypt_gateway_cache_expiration": 24,
    "enkrypt_async_input_guardrails_enabled": false,
    "enkrypt_async_output_guardrails_enabled": false,
    "enkrypt_telemetry": {
      "enabled": true,
      "insecure": true,
      "endpoint": "http://localhost:4317"
    }
  },
  "mcp_configs": {
    "fcbd4508-1432-4f13-abb9-c495c946f638": {
      "mcp_config_name": "default_config",
      "mcp_config": [
        {
          "server_name": "echo_server",
          "description": "Simple Echo Server",
          "config": {
            "command": "python",
            "args": [
              "C:\\Users\\<User>\\Documents\\GitHub\\EnkryptAI\\secure-mcp-gateway\\.secure-mcp-gateway-venv\\Lib\\site-packages\\secure_mcp_gateway\\bad_mcps\\echo_mcp.py"
            ]
          },
          "tools": {},
          "input_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "pii_redaction": false
            },
            "block": [
              "policy_violation"
            ]
          },
          "output_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "relevancy": false,
              "hallucination": false,
              "adherence": false
            },
            "block": [
              "policy_violation"
            ]
          }
        }
      ]
    }
  },
  "projects": {
    "3c09f06c-1f0d-4153-9ac5-366397937641": {
      "project_name": "default_project",
      "mcp_config_id": "fcbd4508-1432-4f13-abb9-c495c946f638",
      "users": [
        "6469a670-1d64-4da5-b2b3-790de21ac726"
      ],
      "created_at": "2025-07-16T17:02:00.406877"
    }
  },
  "users": {
    "6469a670-1d64-4da5-b2b3-790de21ac726": {
      "email": "default@example.com",
      "created_at": "2025-07-16T17:02:00.406902"
    }
  },
  "apikeys": {
    "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat": {
      "project_id": "3c09f06c-1f0d-4153-9ac5-366397937641",
      "user_id": "6469a670-1d64-4da5-b2b3-790de21ac726",
      "created_at": "2025-07-16T17:02:00.406905"
    }
  }
}

```

</details>

#### 4.1.4 Install the Gateway for Claude Desktop

- Run the following command to install the gateway for Claude:

  ```bash
  secure-mcp-gateway install --client claude-desktop
  ```

- This will register Enkrypt Secure MCP Gateway with Claude Desktop.

- **NOTE: Please restart Claude Desktop after installation**

<details>
<summary><strong>🖨️ Example output</strong></summary>
<br>

```bash
Initializing Enkrypt Secure MCP Gateway
Initializing Enkrypt Secure MCP Gateway Common Utilities Module
Initializing Enkrypt Secure MCP Gateway Module
--------------------------------
SYSTEM INFO:
Using Python interpreter: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Scripts\python.exe
Python version: 3.13.3 (tags/v3.13.3:6280bb5, Apr  8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
Current working directory: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway
PYTHONPATH: Not set
--------------------------------
Installing dependencies...
All dependencies installed successfully.
Initializing Enkrypt Secure MCP Gateway Client Module
Initializing Enkrypt Secure MCP Gateway Guardrail Module
Error: Gateway key is required. Please update your mcp client config and try again.
Getting Enkrypt Common Configuration
config_path: C:\Users\PC\.enkrypt\enkrypt_mcp_config.json
example_config_path: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Lib\site-packages\secure_mcp_gateway\example_enkrypt_mcp_config.json
Loading enkrypt_mcp_config.json file...
--------------------------------
ENKRYPT_GATEWAY_KEY: ****NULL
enkrypt_log_level: info
is_debug_log_level: False
enkrypt_base_url: https://api.enkryptai.com
enkrypt_use_remote_mcp_config: False
enkrypt_api_key: ****_KEY
enkrypt_tool_cache_expiration: 4
enkrypt_gateway_cache_expiration: 24
enkrypt_mcp_use_external_cache: False
enkrypt_async_input_guardrails_enabled: False
--------------------------------
External Cache is not enabled. Using local cache only.
Initializing Enkrypt Secure MCP Gateway CLI Module
CONFIG_PATH:  C:\Users\PC\.enkrypt\enkrypt_mcp_config.json
GATEWAY_PY_PATH:  C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Lib\site-packages\secure_mcp_gateway\gateway.py
client name from args:  claude-desktop
Successfully installed gateway for claude-desktop
Path to gateway is incorrect. Modifying the path to gateway in claude_desktop_config.json file...
Path to gateway modified in claude_desktop_config.json file
Please restart Claude Desktop to use the gateway.

```

</details>

#### 4.1.5 Example of the Claude Desktop Config after installation

<details>
<summary><strong>🍎 Example file in macOS</strong></summary>
<br>

- `~/Library/Application Support/Claude/claude_desktop_config.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "mcp",
        "args": [
          "run",
          "/Users/user/enkryptai/secure-mcp-gateway/venv/lib/python3.13/site-packages/secure_mcp_gateway/gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>
<details>
<summary><strong>🪟 Example file in Windows</strong></summary>
<br>

- `%USERPROFILE%\AppData\Roaming\Claude\claude_desktop_config.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "mcp",
        "args": [
          "run",
          "C:\\Users\\<User>\\Documents\\GitHub\\EnkryptAI\\secure-mcp-gateway\\.secure-mcp-gateway-venv\\Lib\\site-packages\\secure_mcp_gateway\\gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>

#### 4.1.6 Install the Gateway for Cursor

- Run the CLI Install Command for Cursor

  ```bash
  secure-mcp-gateway install --client cursor
  ```

- This automatically updates your ~/.cursor/mcp.json (on Windows it is at: %USERPROFILE%\.cursor\mcp.json) with the correct entry.

- *Although it is not usually required to restart, if you see it in loading state for a long time, please restart Cursor*

<details>
<summary><strong>🍎 Example file in macOS</strong></summary>
<br>

- `~/.cursor/mcp.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "mcp",
        "args": [
          "run",
          "/Users/user/enkryptai/secure-mcp-gateway/venv/lib/python3.13/site-packages/secure_mcp_gateway/gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>
<details>
<summary><strong>🪟 Example file in Windows</strong></summary>
<br>

- `%USERPROFILE%\.cursor\mcp.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "uv",
        "args": [
          "run",
          "--with",
          "mcp[cli]",
          "mcp",
          "run",
          "C:\\Users\\<User>\\Documents\\GitHub\\EnkryptAI\\secure-mcp-gateway\\.secure-mcp-gateway-venv\\Lib\\site-packages\\secure_mcp_gateway\\gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>

#### 4.1.7 Install the Gateway for Claude Code

[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's CLI-based coding agent. It uses `claude mcp add` commands to configure MCP servers. Unlike Claude Desktop and Cursor which use JSON config files, Claude Code manages MCP servers through its own CLI.

> **Prerequisite:** The `claude` CLI must be installed on your system. See [Claude Code docs](https://docs.anthropic.com/en/docs/claude-code) for installation.

**Step 1: Install the gateway**

```bash
secure-mcp-gateway install --client claude-code
```

This automatically:
- Reads your gateway key, project ID, and user ID from the generated config
- Runs `claude mcp add` with the correct credentials and gateway path
- Registers the server with `--scope user` (available across all Claude Code projects)

**Step 2: Verify the server was added**

```bash
claude mcp list
```

You should see `Enkrypt-Secure-MCP-Gateway` in the list.

**Step 3: Use the gateway in Claude Code**

Launch Claude Code and try:

```
list all servers, get all tools available
```

<details>
<summary><strong>Manual alternative (if you prefer to run claude mcp add directly)</strong></summary>
<br>

Get your credentials from the generated `enkrypt_mcp_config.json` and the gateway path:

```bash
python -c "import secure_mcp_gateway.gateway; print(secure_mcp_gateway.gateway.__file__)"
```

Then add the gateway manually:

```bash
claude mcp add --transport stdio --env ENKRYPT_GATEWAY_KEY=YOUR_GATEWAY_KEY --env ENKRYPT_PROJECT_ID=YOUR_PROJECT_ID --env ENKRYPT_USER_ID=YOUR_USER_ID --scope user Enkrypt-Secure-MCP-Gateway -- mcp run /path/to/secure_mcp_gateway/gateway.py
```

> **Note:** The server name must use hyphens or underscores — Claude Code does not allow spaces in names.

</details>

</details>

### 4.2 Local Installation with git clone

<details>
<summary><strong>🗂️ Git Clone Installation Steps </strong></summary>

#### 4.2.1 Clone the repo, setup virtual environment and install dependencies

- Clone the repository:

```bash
git clone https://github.com/enkryptai/secure-mcp-gateway

cd secure-mcp-gateway

```

<br>
<details>
<summary><strong>⚡ Activate a virtual environment </strong></summary>
<br>

```bash

# ------------------

# Create a virtual environment

# ------------------

uv venv

# Example output
Using CPython 3.13.3 interpreter at: C:\Users\PC\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\python.exe
Creating virtual environment at: .venv
Activate with: .venv\Scripts\activate

# ------------------

# Activate the virtual environment

# ------------------

# For 🍎 Linux/macOS, run the following
source ./.venv/Scripts/activate

# For 🪟 Windows, run the following
.\.venv\Scripts\activate

# After activating, you should see (enkrypt-secure-mcp-gateway) before the file path in the terminal

# Example:

# (enkrypt-secure-mcp-gateway) %USERPROFILE%\Documents\GitHub\EnkryptAI\secure-mcp-gateway>

# ------------------

# Install pip in the virtual environment

# ------------------

python -m ensurepip

# ------------------

# Install uv in the virtual environment

# ------------------

python -m pip install uv

```

- Install Python dependencies:

```bash
uv pip install -r requirements.txt

```

- Verify mcp cli got installed successfully:

```bash
mcp version

# Example output
MCP version 1.9.2

```

</details>

#### 4.2.2 Run the setup script

<!-- - The `setup` script checks versions of Python, pip, uv and makes sure they are installed and accessible -->

<!-- - It then installs the dependencies -->

- This script creates the config file at `~/.enkrypt/enkrypt_mcp_config.json` on macOS and `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json` on Windows based on `src/secure_mcp_gateway/example_enkrypt_mcp_config.json` file

- It replaces `UNIQUE_GATEWAY_KEY` and other `UUIDs` with auto generated values and also replaces `DUMMY_MCP_FILE_PATH` with the actual path to the test MCP file `bad_mcps/echo_mcp.py`

- It also installs the MCP client in Claude Desktop

- *NOTE: Please restart Claude Desktop after running the setup script to see the Gateway running in Claude Desktop*

```bash

# On 🍎 Linux/macOS run the below
cd scripts
chmod +x *.sh
./setup.sh

# On 🪟 Windows run the below
cd scripts
setup.bat

# Now restart Claude Desktop to see the Gateway running

```

<br>
<details>
<summary><strong>🖨️ Example output</strong></summary>
<br>

```bash
-------------------------------
Setting up Enkrypt Secure MCP Gateway enkrypt_mcp_config.json config file
-------------------------------
        1 file(s) copied.
Generated unique gateway key: WTZOpoU1mXJz8b_ZJQ42DuSXlQCSCtWOn3FX0jG8sO_FKYNJetjYEgSluvhtBN8_
Generated unique uuid: 7920749a-228e-47fe-a6a9-cd2d64a2283b
DUMMY_MCP_FILE_PATH: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\src\secure_mcp_gateway\bad_mcps\echo_mcp.py
-------------------------------
Setup complete. Please check the enkrypt_mcp_config.json file in the ~\.enkrypt directory and update with your MCP server configs as needed.
-------------------------------
-------------------------------
Installing Enkrypt Secure MCP Gateway with gateway key and dependencies
-------------------------------
mcp is installed. Proceeding with installation...
ENKRYPT_GATEWAY_KEY: WTZOpoU1mXJz8b_ZJQ42DuSXlQCSCtWOn3FX0jG8sO_FKYNJetjYEgSluvhtBN8_
The system cannot find the path specified.
Package names only:
Dependencies string for the cli install command:
Running the cli install command: mcp install gateway.py --env-var ENKRYPT_GATEWAY_KEY=WTZOpoU1mXJz8b_ZJQ42DuSXlQCSCtWOn3FX0jG8sO_FKYNJetjYEgSluvhtBN8_
Initializing Enkrypt Secure MCP Gateway
Initializing Enkrypt Secure MCP Gateway Common Utilities Module
Initializing Enkrypt Secure MCP Gateway Module
--------------------------------
SYSTEM INFO:
Using Python interpreter: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Scripts\python.exe
Python version: 3.13.3 (tags/v3.13.3:6280bb5, Apr  8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
Current working directory: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\src\secure_mcp_gateway
PYTHONPATH: Not set
--------------------------------
Installing dependencies...
All dependencies installed successfully.
Initializing Enkrypt Secure MCP Gateway Client Module
Initializing Enkrypt Secure MCP Gateway Guardrail Module
Getting Enkrypt Common Configuration
config_path: C:\Users\PC\.enkrypt\enkrypt_mcp_config.json
example_config_path: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Lib\site-packages\secure_mcp_gateway\example_enkrypt_mcp_config.json
Loading enkrypt_mcp_config.json file...
--------------------------------
ENKRYPT_GATEWAY_KEY: ****BN8_
enkrypt_log_level: info
is_debug_log_level: False
enkrypt_base_url: https://api.enkryptai.com
enkrypt_use_remote_mcp_config: False
enkrypt_api_key: ****_KEY
enkrypt_tool_cache_expiration: 4
enkrypt_gateway_cache_expiration: 24
enkrypt_mcp_use_external_cache: False
enkrypt_async_input_guardrails_enabled: False
--------------------------------
External Cache is not enabled. Using local cache only.
Initializing Enkrypt Secure MCP Gateway Module
--------------------------------
SYSTEM INFO:
Using Python interpreter: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Scripts\python.exe
Python version: 3.13.3 (tags/v3.13.3:6280bb5, Apr  8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
Current working directory: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\src\secure_mcp_gateway
PYTHONPATH: Not set
--------------------------------
Installing dependencies...
All dependencies installed successfully.
Getting Enkrypt Common Configuration
config_path: C:\Users\PC\.enkrypt\enkrypt_mcp_config.json
example_config_path: C:\Users\PC\Documents\GitHub\EnkryptAI\secure-mcp-gateway\.secure-mcp-gateway-venv\Lib\site-packages\secure_mcp_gateway\example_enkrypt_mcp_config.json
Loading enkrypt_mcp_config.json file...
--------------------------------
ENKRYPT_GATEWAY_KEY: ****BN8_
enkrypt_log_level: info
is_debug_log_level: False
enkrypt_base_url: https://api.enkryptai.com
enkrypt_use_remote_mcp_config: False
enkrypt_api_key: ****_KEY
enkrypt_tool_cache_expiration: 4
enkrypt_gateway_cache_expiration: 24
enkrypt_mcp_use_external_cache: False
enkrypt_async_input_guardrails_enabled: False
--------------------------------
External Cache is not enabled. Using local cache only.
[06/15/25 13:14:10] INFO     Added server 'Enkrypt Secure MCP Gateway' to Claude config                                                                                                             claude.py:137
                    INFO     Successfully installed Enkrypt Secure MCP Gateway in Claude app                                                                                                           cli.py:486
-------------------------------
Installation complete. Check the claude_desktop_config.json file as per the readme instructions and restart Claude Desktop.
-------------------------------

```

</details>

#### 4.2.3 Setup Other MCP Clients

<details>
<summary><strong>⬡ Cursor </strong></summary>
<br>

- You can navigate to cursor's **Global MCP** file at `~/.cursor/mcp.json` on Linux/macOS or `%USERPROFILE%\.cursor\mcp.json` on Windows

  - If you would like to use at a **Project level** place it inside your project. For details see [Cursor's docs](https://docs.cursor.com/context/model-context-protocol#configuration-locations)

- You can also navigate to the file Via cursor's UI by clicking on `settings` gear icon on the top right

  ![cursor-settings-icon](./docs/images/cursor-settings-icon.png)

- Click on `MCP` and then click on `Add new global MCP server` which takes you to the `mcp.json` file

  ![cursor-settings-mcp](./docs/images/cursor-settings-mcp.png)

- Example `mcp.json` file opened in the editor

  ![cursor-mcp-file](./docs/images/cursor-mcp-file.png)

- Once the file is opened at Global or Project level, you can copy paste the same config we used in `Claude Desktop`. For reference, you can refer to [Installation - 6.2 Example MCP config file generated 📄](#62-example-mcp-config-file-generated)

  - *Be sure to use your own file that was generated by the `setup` script in [Installation - 4.2.2 Run the setup script 📥](#422-run-the-setup-script). Please do not copy paste the example config file in this repo.*

- See [Verify Cursor](#66-verify-cursor) section to verify the MCP server is running in Cursor

</details>
<details>
<summary><strong>⬡ Claude Code </strong></summary>
<br>

- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) uses its own CLI to manage MCP servers instead of JSON config files

- Get your credentials from the generated `enkrypt_mcp_config.json` (gateway key, project ID, user ID)

- Find the gateway.py path:

  ```bash
  python -c "import secure_mcp_gateway.gateway; print(secure_mcp_gateway.gateway.__file__)"
  ```

- Add the gateway to Claude Code:

  ```bash
  claude mcp add --transport stdio --env ENKRYPT_GATEWAY_KEY=YOUR_GATEWAY_KEY --env ENKRYPT_PROJECT_ID=YOUR_PROJECT_ID --env ENKRYPT_USER_ID=YOUR_USER_ID --scope user Enkrypt-Secure-MCP-Gateway -- mcp run /path/to/secure_mcp_gateway/gateway.py
  ```

- Verify: `claude mcp list`

- For detailed setup, see [4.1.7 Install the Gateway for Claude Code](#417-install-the-gateway-for-claude-code)

</details>
</details>

### 4.3 Docker Installation

<details>
<summary><strong>🐳 Docker Installation Steps </strong></summary>

#### 4.3.1 Build the Docker Image

```bash
docker build -t secure-mcp-gateway .

```

<details>
<summary><strong>🖨️ Example output</strong></summary>
<br>

```bash
[+] Building 72.9s (20/20) FINISHED                                                                                                                                          docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                   0.1s
 => => transferring dockerfile: 724B                                                                                                                                                   0.1s
 => [internal] load metadata for docker.io/library/python:3.11-alpine                                                                                                                  1.0s
 => [internal] load .dockerignore                                                                                                                                                      0.1s
 => => transferring context: 456B                                                                                                                                                      0.1s
 => [ 1/15] FROM docker.io/library/python:3.11-alpine@sha256:8068890a42d68ece5b62455ef327253249b5f094dcdee57f492635a40217f6a3                                                          0.0s
 => => resolve docker.io/library/python:3.11-alpine@sha256:8068890a42d68ece5b62455ef327253249b5f094dcdee57f492635a40217f6a3                                                            0.0s
 => [internal] load build context                                                                                                                                                      1.5s
 => => transferring context: 82.25kB                                                                                                                                                   1.4s
 => CACHED [ 2/15] WORKDIR /app                                                                                                                                                        0.0s
 => CACHED [ 3/15] COPY requirements.txt .                                                                                                                                             0.0s
 => [ 4/15] COPY requirements-dev.txt .                                                                                                                                                0.0s
 => [ 5/15] RUN pip install --upgrade pip && pip install -r requirements.txt && pip install -r requirements-dev.txt                                                                   38.7s
 => [ 6/15] COPY src src                                                                                                                                                               0.2s
 => [ 7/15] COPY setup.py setup.py                                                                                                                                                     0.1s
 => [ 8/15] COPY MANIFEST.in MANIFEST.in                                                                                                                                               0.1s
 => [ 9/15] COPY pyproject.toml pyproject.toml                                                                                                                                         0.1s
 => [10/15] COPY CHANGELOG.md CHANGELOG.md                                                                                                                                             0.1s
 => [11/15] COPY LICENSE.txt LICENSE.txt                                                                                                                                               0.1s
 => [12/15] COPY README.md README.md                                                                                                                                                   0.1s
 => [13/15] COPY README_PYPI.md README_PYPI.md                                                                                                                                         0.1s
 => [14/15] RUN python -m build                                                                                                                                                        8.5s
 => [15/15] RUN pip install .                                                                                                                                                          5.5s
 => exporting to image                                                                                                                                                                16.6s
 => => exporting layers                                                                                                                                                               11.8s
 => => exporting manifest sha256:47bd860c903fdefeda59364f577c487f96e1482b0e8eadef8292df86922641dc                                                                                      0.0s
 => => exporting config sha256:9d211386091dfc08fcfe80f1efb399d4a1ab80484f850476c328614ecaaefbae                                                                                        0.1s
 => => exporting attestation manifest sha256:bc85b5aaf4035e6f449d9b94567135a28a61c594fa2a507ca7fea889efbf2952                                                                          0.0s
 => => exporting manifest list sha256:7cd30cbf456ba3105d4bef7c28ea8402ec5476e4da3cd8c16b752f3214f8b3b1                                                                                 0.0s
 => => naming to docker.io/library/secure-mcp-gateway:latest                                                                                                                           0.0s
 => => unpacking to docker.io/library/secure-mcp-gateway:latest

```

</details>

#### 4.3.2 Generate the config file

- This creates a config file in the `~/.enkrypt/docker/enkrypt_mcp_config.json` file on macOS/Linux and `%USERPROFILE%\.enkrypt\docker\enkrypt_mcp_config.json` file on Windows.

> **Quick shorthand** — If you have the CLI installed locally via pip, you can use the `--docker` flag on any command and skip the verbose Docker syntax:
> ```bash
> secure-mcp-gateway --docker generate-config
> ```

<details>
<summary><strong>Verbose Docker run commands (if CLI is not installed locally)</strong></summary>

```bash

# On 🍎 Linux/macOS run the below
docker run --rm -e HOST_OS=macos -e HOST_ENKRYPT_HOME=$HOME/.enkrypt -v ~/.enkrypt/docker:/app/.enkrypt/docker --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli generate-config

# On 🪟 Windows (CMD) run the below
docker run --rm -e HOST_OS=windows -e HOST_ENKRYPT_HOME=%USERPROFILE%\.enkrypt -v %USERPROFILE%\.enkrypt\docker:/app/.enkrypt/docker --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli generate-config

# On 🪟 Windows (📟 PowerShell) run the below
docker run --rm -e HOST_OS=windows -e "HOST_ENKRYPT_HOME=$env:USERPROFILE\.enkrypt" -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli generate-config

```

</details>

<details>
<summary><strong>🐳 Example Docker config file</strong></summary>
<br>

```json
{
  "common_mcp_gateway_config": {
    "enkrypt_log_level": "INFO",
    "enkrypt_base_url": "https://api.enkryptai.com",
    "enkrypt_api_key": "YOUR_ENKRYPT_API_KEY",
    "enkrypt_use_remote_mcp_config": false,
    "enkrypt_remote_mcp_gateway_name": "enkrypt-secure-mcp-gateway-1",
    "enkrypt_remote_mcp_gateway_version": "v1",
    "enkrypt_mcp_use_external_cache": false,
    "enkrypt_cache_host": "localhost",
    "enkrypt_cache_port": 6379,
    "enkrypt_cache_db": 0,
    "enkrypt_cache_password": null,
    "enkrypt_tool_cache_expiration": 4,
    "enkrypt_gateway_cache_expiration": 24,
    "enkrypt_async_input_guardrails_enabled": false,
    "enkrypt_async_output_guardrails_enabled": false,
    "enkrypt_telemetry": {
      "enabled": true,
      "insecure": true,
      "endpoint": "http://localhost:4317"
    }
  },
  "mcp_configs": {
    "fcbd4508-1432-4f13-abb9-c495c946f638": {
      "mcp_config_name": "default_config",
      "mcp_config": [
        {
          "server_name": "echo_server",
          "description": "Simple Echo Server",
          "config": {
            "command": "python",
            "args": [
              "/usr/local/lib/python3.11/site-packages/secure_mcp_gateway/bad_mcps/echo_mcp.py"
            ]
          },
          "tools": {},
          "input_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "pii_redaction": false
            },
            "block": [
              "policy_violation"
            ]
          },
          "output_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "relevancy": false,
              "hallucination": false,
              "adherence": false
            },
            "block": [
              "policy_violation"
            ]
          }
        }
      ]
    }
  },
  "projects": {
    "3c09f06c-1f0d-4153-9ac5-366397937641": {
      "project_name": "default_project",
      "mcp_config_id": "fcbd4508-1432-4f13-abb9-c495c946f638",
      "users": [
        "6469a670-1d64-4da5-b2b3-790de21ac726"
      ],
      "created_at": "2025-07-16T17:02:00.406877"
    }
  },
  "users": {
    "6469a670-1d64-4da5-b2b3-790de21ac726": {
      "email": "default@example.com",
      "created_at": "2025-07-16T17:02:00.406902"
    }
  },
  "apikeys": {
    "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat": {
      "project_id": "3c09f06c-1f0d-4153-9ac5-366397937641",
      "user_id": "6469a670-1d64-4da5-b2b3-790de21ac726",
      "created_at": "2025-07-16T17:02:00.406905"
    }
  }
}

```

</details>

#### 4.3.3 Install the Gateway in Claude Desktop

- You can find the Claude config location at the below locations in your system. [For reference see Claude docs.](https://modelcontextprotocol.io/quickstart/user#:~:text=This%20will%20create%20a%20configuration%20file%20at%3A)
  - macOS: `~/Library/Application Support/Claude`
  - Windows: `%APPDATA%\Claude`

> **Note:** The generated config includes `MCP_TRANSPORT=stdio` for stdio mode communication with Claude Desktop.

```bash

# On 🍎 Linux/macOS run the below
docker run --rm -i -e HOST_OS=macos -e HOST_ENKRYPT_HOME=$HOME/.enkrypt -v ~/.enkrypt/docker:/app/.enkrypt/docker -v ~/Library/Application\ Support/Claude:/app/.claude --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client claude-desktop

# On 🪟 Windows (CMD) run the below
docker run --rm -i -e HOST_OS=windows -e HOST_ENKRYPT_HOME=%USERPROFILE%\.enkrypt -v %USERPROFILE%\.enkrypt\docker:/app/.enkrypt/docker -v %APPDATA%\Claude:/app/.claude --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client claude-desktop

# On 🪟 Windows (📟 PowerShell) run the below
docker run --rm -i -e HOST_OS=windows -e "HOST_ENKRYPT_HOME=$env:USERPROFILE\.enkrypt" -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" -v "$env:APPDATA\Claude:/app/.claude" --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client claude-desktop

```

#### 4.3.4 Example Claude Desktop config file

<details>
<summary><strong>🪟 Example Windows claude_desktop_config.json</strong></summary>
<br>

```json
{
  "mcpServers": {
    "Enkrypt Secure MCP Gateway": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "MCP_TRANSPORT=stdio",
        "-v",
        "C:\\Users\\<user>\\.enkrypt\\docker:/app/.enkrypt/docker",
        "secure-mcp-gateway"
      ],
      "env": {
        "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
        "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
        "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
      }
    }
  }
}

```

</details>

#### 4.3.5 Install the Gateway in Cursor

- You can find the Cursor config location at the below locations. [For reference see Cursor docs.](https://docs.cursor.com/context/model-context-protocol#configuration-locations)
  - macOS: `~/.cursor`
  - Windows: `%USERPROFILE%\.cursor`

> **Note:** The generated config includes `MCP_TRANSPORT=stdio` for stdio mode communication with Cursor.

```bash

# On 🍎 Linux/macOS run the below
docker run --rm -i -e HOST_OS=macos -e HOST_ENKRYPT_HOME=$HOME/.enkrypt -v ~/.enkrypt/docker:/app/.enkrypt/docker -v ~/.cursor:/app/.cursor --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client cursor

# On 🪟 Windows (CMD) run the below
docker run --rm -i -e HOST_OS=windows -e HOST_ENKRYPT_HOME=%USERPROFILE%\.enkrypt -v %USERPROFILE%\.enkrypt\docker:/app/.enkrypt/docker -v %USERPROFILE%\.cursor:/app/.cursor --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client cursor

# On 🪟 Windows (📟 PowerShell) run the below
docker run --rm -i -e HOST_OS=windows -e "HOST_ENKRYPT_HOME=$env:USERPROFILE\.enkrypt" -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" -v "$env:USERPROFILE\.cursor:/app/.cursor" --entrypoint python secure-mcp-gateway -m secure_mcp_gateway.cli install --client cursor

```

#### 4.3.6 Install the Gateway in Claude Code

Claude Code uses its own CLI (`claude mcp add`) to manage MCP servers. When running the gateway in Docker, Claude Code connects via `npx mcp-remote` to the gateway's Streamable HTTP endpoint.

> **Prerequisites:** Node.js and npm must be installed on your machine (`node -v` and `npm -v` to verify).

**Step 1: Run the gateway container**

Start the gateway as a background Docker container with the Streamable HTTP endpoint exposed:

```bash
# On 🍎 Linux/macOS
docker run -d --name enkrypt-gateway -p 8000:8000 -v ~/.enkrypt/docker:/app/.enkrypt/docker secure-mcp-gateway

# On 🪟 Windows (PowerShell)
docker run -d --name enkrypt-gateway -p 8000:8000 -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" secure-mcp-gateway
```

**Step 2: Add the gateway to Claude Code**

```bash
claude mcp add --transport http --header "apikey:YOUR_GATEWAY_KEY" --header "project_id:YOUR_PROJECT_ID" --header "user_id:YOUR_USER_ID" --scope user Enkrypt-Secure-MCP-Gateway http://localhost:8000/mcp/
```

Replace `YOUR_GATEWAY_KEY`, `YOUR_PROJECT_ID`, and `YOUR_USER_ID` with the values from your `enkrypt_mcp_config.json`.

<details>
<summary><strong>Alternative: stdio mode via Docker</strong></summary>
<br>

If you prefer stdio mode (no persistent container), you can add Claude Code's MCP config using JSON directly. Create or edit `~/.claude.json` and add the server under `mcpServers`:

```json
{
  "mcpServers": {
    "Enkrypt Secure MCP Gateway": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "MCP_TRANSPORT=stdio",
        "-v",
        "/Users/<user>/.enkrypt/docker:/app/.enkrypt/docker",
        "secure-mcp-gateway"
      ],
      "env": {
        "ENKRYPT_GATEWAY_KEY": "YOUR_GATEWAY_KEY",
        "ENKRYPT_PROJECT_ID": "YOUR_PROJECT_ID",
        "ENKRYPT_USER_ID": "YOUR_USER_ID"
      }
    }
  }
}
```

Or use the Claude Code CLI:

```bash
claude mcp add-json Enkrypt-Secure-MCP-Gateway '{
  "type": "stdio",
  "command": "docker",
  "args": ["run", "--rm", "-i", "-e", "MCP_TRANSPORT=stdio", "-v", "/Users/<user>/.enkrypt/docker:/app/.enkrypt/docker", "secure-mcp-gateway"],
  "env": {
    "ENKRYPT_GATEWAY_KEY": "YOUR_GATEWAY_KEY",
    "ENKRYPT_PROJECT_ID": "YOUR_PROJECT_ID",
    "ENKRYPT_USER_ID": "YOUR_USER_ID"
  }
}'
```

> **Note on Windows (PowerShell):** Replace `/Users/<user>/.enkrypt/docker` with your Windows path (e.g., `C:\Users\<user>\.enkrypt\docker`) and adjust the volume mount syntax accordingly.

</details>

**Step 3: Verify**

```bash
claude mcp list
```

**Step 4: Use in Claude Code**

Launch Claude Code and try prompts like `list all servers, get all tools available`.

#### 4.3.7 Running Gateway with Docker Run (Advanced)

For advanced Docker deployments, you can run the gateway container directly with custom configurations:

```bash
# Basic Docker run command
docker run -d --name enkrypt-gateway -p 8000:8000 -v ~/.enkrypt/docker:/app/.enkrypt/docker -e ENKRYPT_GATEWAY_KEY="your-gateway-key" -e ENKRYPT_PROJECT_ID="your-project-id" -e ENKRYPT_USER_ID="your-user-id" secure-mcp-gateway:latest
```

**Windows PowerShell:**

```powershell
docker run -d `
  --name enkrypt-gateway `
  -p 8000:8000 `
  -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" `
  -e ENKRYPT_GATEWAY_KEY="your-gateway-key" `
  -e ENKRYPT_PROJECT_ID="your-project-id" `
  -e ENKRYPT_USER_ID="your-user-id" `
  secure-mcp-gateway:latest
```

##### Environment Variables

| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `ENKRYPT_GATEWAY_KEY` | API key for authentication | - | Yes |
| `ENKRYPT_PROJECT_ID` | Project ID from config | - | Yes |
| `ENKRYPT_USER_ID` | User ID from config | - | Yes |
| `MCP_TRANSPORT` | Transport mode: `streamable-http` or `stdio` | `streamable-http` | No |
| `SKIP_DEPENDENCY_INSTALL` | Skip runtime dependency installation | `true` (Docker), `false` (other) | No |
| `HOST` | Gateway bind address | `0.0.0.0` | No |
| `FASTAPI_HOST` | FastAPI server bind address | `0.0.0.0` | No |

##### MCP_TRANSPORT

The `MCP_TRANSPORT` environment variable controls the transport mode for the gateway.

**Transport modes:**

- **`streamable-http`** (default): HTTP server mode on port 8000. Use with `-p 8000:8000` for port mapping.
- **`stdio`**: Standard input/output mode for MCP clients that communicate via stdin/stdout. Use with `-i` flag.

**Example for stdio mode (Claude Desktop, Cursor):**

```bash
docker run --rm -i -e MCP_TRANSPORT=stdio -v ~/.enkrypt/docker:/app/.enkrypt/docker -e ENKRYPT_GATEWAY_KEY="your-gateway-key" secure-mcp-gateway
```

##### SKIP_DEPENDENCY_INSTALL

The `SKIP_DEPENDENCY_INSTALL` environment variable controls whether the gateway reinstalls Python dependencies at runtime.

**Default behavior:**

- **Docker environments**: Defaults to `true` (auto-detected). Dependencies are pre-installed in the Docker image, so runtime installation is skipped automatically.
- **Non-Docker environments**: Defaults to `false`. Dependencies are installed at startup to ensure compatibility.

**When to explicitly set `SKIP_DEPENDENCY_INSTALL=false` in Docker:**

- Development environments where you're testing new dependencies
- When mounting source code volumes for live development
- If you're unsure whether all dependencies are properly installed

**When to explicitly set `SKIP_DEPENDENCY_INSTALL=true` outside Docker:**

- Production deployments where dependencies are pre-installed
- To reduce startup time (faster cold starts)
- In environments where you've already run `pip install`

**Example with docker-compose integration:**

```bash
# Connect to observability stack network
# Note: SKIP_DEPENDENCY_INSTALL defaults to true in Docker, so it's optional
docker run -d --name enkrypt-gateway --network secure-mcp-gateway-infra_default -p 8000:8000 -p 8080:8080 -v ~/.enkrypt/docker:/app/.enkrypt/docker -e ENKRYPT_GATEWAY_KEY="your-gateway-key" -e ENKRYPT_PROJECT_ID="your-project-id" -e ENKRYPT_USER_ID="your-user-id" secure-mcp-gateway:latest
```

**Windows PowerShell:**

```powershell
# Note: SKIP_DEPENDENCY_INSTALL defaults to true in Docker, so it's optional
docker run -d `
  --name enkrypt-gateway `
  --network secure-mcp-gateway-infra_default `
  -p 8000:8000 `
  -p 8080:8080 `
  -v "$env:USERPROFILE\.enkrypt\docker:/app/.enkrypt/docker" `
  -e ENKRYPT_GATEWAY_KEY="your-gateway-key" `
  -e ENKRYPT_PROJECT_ID="your-project-id" `
  -e ENKRYPT_USER_ID="your-user-id" `
  secure-mcp-gateway:latest
```

**Note:** The `--network` flag connects the gateway to the observability stack (Grafana, Prometheus, Loki, Jaeger) if you're running the monitoring services from section 5.

##### Port Mapping

- `8000`: Gateway MCP server (required)
- `8080`: OAuth callback server (optional, only needed for Authorization Code flow)
- `8001`: REST API server (optional, if you want to expose the management API)

##### Volume Mounts

- `~/.enkrypt/docker:/app/.enkrypt/docker` - Config file location (required)
- Additional mounts may be needed if your MCP servers require access to local files

> **⚠️ Important:** MCP clients (Claude Desktop, Cursor, Claude Code) spawn Docker directly without a shell, so `~` (tilde) will **not** be expanded. Always use **absolute paths** in your MCP client config JSON files (e.g. `/Users/yourname/.enkrypt/docker:/app/.enkrypt/docker` on macOS/Linux or `C:\\Users\\yourname\\.enkrypt\\docker:/app/.enkrypt/docker` on Windows).

#### ⚠️ Important: Configuring MCP Servers When Gateway Runs in Docker

When running the Enkrypt Gateway in Docker, **DO NOT configure your MCP servers to also run in Docker mode**. This causes Docker-in-Docker issues, networking problems, and volume mount complications.

**❌ Avoid (Docker-based MCP servers):**

```json
{
  "server_name": "github_server",
  "config": {
    "command": "docker",
    "args": [
      "run",
      "-i",
      "--rm",
      "-e",
      "GITHUB_PERSONAL_ACCESS_TOKEN",
      "ghcr.io/github/github-mcp-server"
    ],
    "env": {
      "GITHUB_PERSONAL_ACCESS_TOKEN": "your-token"
    }
  }
}
```

**✅ Use instead (npx/npm/Python-based servers):**

```json
{
  "server_name": "github_server",
  "config": {
    "command": "npx",
    "args": [
      "-y",
      "@modelcontextprotocol/server-github"
    ],
    "env": {
      "GITHUB_PERSONAL_ACCESS_TOKEN": "your-token"
    }
  }
}
```

**Why?**

- Docker-in-Docker requires privileged mode and special socket mounting
- Network isolation prevents containers from communicating properly
- Volume mounts don't work as expected across container boundaries
- Performance overhead and security concerns
- Increased complexity and debugging difficulty

**Recommended MCP Server Formats When Gateway is in Docker:**

- ✅ **npx-based servers**: `npx -y @modelcontextprotocol/server-*`
- ✅ **npm-based servers**: `npm exec -y server-name`
- ✅ **Python-based servers**: `python /path/to/server.py` or `uv run server.py`
- ✅ **Node.js-based servers**: `node /path/to/server.js`
- ✅ **Remote MCP servers**: `npx mcp-remote https://api.example.com/mcp/`

**Exception:**

If you absolutely must use Docker-based MCP servers, consider:

1. Running the gateway outside of Docker (local installation), OR
2. Setting up proper Docker networking with `--network host` or custom bridge networks, OR
3. Using Docker-in-Docker with proper configuration (requires `--privileged` flag and `/var/run/docker.sock` mount - **not recommended for production**)

</details>

### 4.4 Remote Installation

<details>
<summary><strong>🌐 Remote Installation Steps </strong></summary>

#### 4.4.1 Run the Gateway in a remote server

```bash
python gateway.py

```

- Or run in k8s using our docker image `enkryptai/secure-mcp-gateway:vx.x.x`

- Example: `enkryptai/secure-mcp-gateway:v2.1.2`

- Use the latest version from Docker Hub: <https://hub.docker.com/r/enkryptai/secure-mcp-gateway/tags>

- You can either mount the config file locally or download the json file from a remote place like `S3` using an `initContainer` and mount the volume

- See `docs/secure-mcp-gateway-manifest-example.yaml` for the complete manifest file reference

#### 4.4.2 Modify your MCP Client config to use the Gateway

- You can find the Claude Desktop config location at the below locations in your system. [For reference see Claude docs.](https://modelcontextprotocol.io/quickstart/user#:~:text=This%20will%20create%20a%20configuration%20file%20at%3A)
  - macOS: `~/Library/Application Support/Claude`
  - Windows: `%APPDATA%\Claude`

- You can find the Cursor config location at the below locations. [For reference see Cursor docs.](https://docs.cursor.com/context/model-context-protocol#configuration-locations)
  - macOS: `~/.cursor`
  - Windows: `%USERPROFILE%\.cursor`

- Replace the `ENKRYPT_GATEWAY_KEY` with the key you got from the `enkrypt_mcp_config.json` file

- Replace the `http://0.0.0.0:8000/mcp/` with the `http(s)://<remote_server_ip>:<port>/mcp/`

- If you are running this locally, you can use `http://0.0.0.0:8000/mcp/`

- You can setup ingress to route the traffic to the MCP Gateway over `https`

- Example: `https://mcp.enkryptai.com/mcp/`

- **NOTE: Please make sure node and npm are installed on the client machine**
  - To verify, run `node -v` and `npm -v`

- **NOTE: Make sure to use the trailing slash `/` in the MCP URL like `/mcp/`**

**For Claude Desktop and Cursor** — add the following to your `claude_desktop_config.json` or `mcp.json`:

```json
{
  "mcpServers": {
    "Enkrypt Secure MCP Gateway": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://0.0.0.0:8000/mcp/",
        "--allow-http",
        "--header",
        "apikey:${ENKRYPT_GATEWAY_KEY}",
        "--header",
        "project_id:${ENKRYPT_PROJECT_ID}",
        "--header",
        "user_id:${ENKRYPT_USER_ID}"
      ],
      "env": {
        "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
        "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
        "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
      }
    }
  }
}

```

**For Claude Code** — use the `claude mcp add` command:

```bash
# Connect Claude Code to the remote gateway via HTTP transport
claude mcp add --transport http --header "apikey:YOUR_GATEWAY_KEY" --header "project_id:YOUR_PROJECT_ID" --header "user_id:YOUR_USER_ID" --scope user Enkrypt-Secure-MCP-Gateway https://mcp.your-domain.com/mcp/
```

> **Note:** For local testing with HTTP (not HTTPS), add `--allow-http` if required, or use `http://0.0.0.0:8000/mcp/` as the URL.

</details>

## 5. (Optional) OpenTelemetry Setup

<details>
<summary><strong>📊 OpenTelemetry Setup and Usage </strong></summary>
<br>

This section explains how to set up and use OpenTelemetry (OTEL) with the Enkrypt Secure MCP Gateway for observability.

### 5.1 Architecture

The observability stack includes:

- OpenTelemetry Collector: Collects telemetry data (traces, metrics, logs)

- Jaeger: Distributed tracing visualization

- Loki: Log aggregation and querying

- Prometheus: Metrics aggregation

- Grafana: Unified visualization for metrics and logs
  - Traces are not visible in Grafana for some reason. Please use Jaeger for traces.

### 5.2 Prerequisites

- Docker and Docker Compose installed

- Gateway installed and running (follow [section 4](#4-gateway-setup))

### 5.3 Setup Steps

1. **Start the Observability Stack**

   ```bash
   cd infra

   docker-compose up -d
   ```

2. **To stop the Observability Stack**

   ```bash
   # When we want to stop the Observability Stack, run the below command
   docker-compose down
   ```

### 5.4 Configuration

- Edit the `enkrypt_mcp_config.json` file to enable telemetry

  ```json
  {
    "common_mcp_gateway_config": {
      ...
      "enkrypt_telemetry": {
        "enabled": true,
        "insecure": true,
        "endpoint": "http://localhost:4317"
      }
    },
    ...
  }
  ```

### 5.5 Verification Steps

1. **Verify Services are Running**

   ```bash
   # On Windows
   docker ps | findstr "loki grafana jaeger otel prometheus"

   # On Linux/macOS
   docker ps | grep -E "loki|grafana|jaeger|otel|prometheus"
   ```

2. **Access Service UIs**

   - Grafana: <http://localhost:3000> (default credentials: admin/admin)

   - Jaeger: <http://localhost:16686>

   - Prometheus: <http://localhost:9090>

   - Loki: Access through Grafana

      1. Open Grafana (<http://localhost:3000>)
      2. Go to Explore (left sidebar)
      3. Select "Loki" from the data source dropdown

3. **Verify Gateway Telemetry**
   - Make test requests through the Gateway like `List all servers and tools` and `echo test`

   - Check traces in Jaeger:
      - Add optional tags like `enkrypt_email=default@example.com` or `enkrypt_project_name=default_project` or `enkrypt_mcp_config_id=fcbd4508-1432-4f13-abb9-c495c946f638` to see the traces for a specific user, project or MCP config etc.
      - We can also combine tags by separating them with spaces like `enkrypt_email=default@example.com enkrypt_project_name=default_project`
      - Look for `enkrypt_discover_all_tools` spans
      - Examine child spans for cache, tool discovery, etc.

        ![jaeger-1](./docs/images/jaeger-1.png)

   - Check metrics in Grafana:
     - Navigate to `Drilldown` -> `metrics`
     - We can filter on various labels like `email`, `user_id`, `mcp_config_id`, `project_id`, `project_name` etc.

        ![grafana-metrics-1](./docs/images/grafana-metrics-1.png)

   - Check logs in Grafana
     - Navigate to `Drilldown` -> `Logs`
     - Select label as `service_name=secure-mcp-gateway` and click `Show logs`
     - Now we can filter by various labels like `attributes_project_name`, `attributes_project_id`, `attributes_email`, `attributes_user_id`, `attributes_mcp_config_id`, `attributes_tool_name` etc.

        ![grafana-logs-1](./docs/images/grafana-logs-1.png)

   - Check Dashboards in Grafana by navigating to `Dashboards` -> `OpenTelemetry Gateway Metrics`
      - **Due to issues in Grafana, you may need to edit each tile and click `Run queries` to see the data**

### 5.6 Available Telemetry (Not exhaustive)

1. **Traces**
   - Request processing pipeline
   - Tool invocations with duration tracking
   - Cache operations (hits/misses)
   - Guardrail checks
   - Error tracking and status monitoring
   - Detailed attributes for debugging

2. **Metrics**
   - `enkrypt_list_all_servers_calls`: API endpoint usage
   - `mcp_cache_misses_total`: Cache efficiency tracking
   - `enkrypt_servers_discovered`: Server discovery monitoring
   - `mcp_tool_calls_total`: Tool invocation tracking
   - `mcp_tool_call_duration_seconds`: Performance monitoring (histogram)

3. **Logs**
   - Structured JSON format for better querying
   - Gateway operations with context
   - Error conditions with stack traces
   - Security events and guardrail checks
   - Performance data with timing information

</details>

## 6. Verify Installation and check the files generated

<details>
<summary><strong>✅ Verification steps and files generated</strong></summary>

### 6.1 Verify Claude Desktop

- To verify Claude installation, navigate to `claude_desktop_config.json` file by [following these instructions](https://modelcontextprotocol.io/quickstart/user#2-add-the-filesystem-mcp-server)

  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`

  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`

### 6.2 Example MCP config file generated

<details>
<summary><strong>🍎 Example file in macOS</strong></summary>
<br>

- `~/Library/Application Support/Claude/claude_desktop_config.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "mcp",
        "args": [
          "run",
          "/Users/user/enkryptai/secure-mcp-gateway/src/secure_mcp_gateway/gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>
<details>
<summary><strong>🪟 Example file in Windows</strong></summary>
<br>

- `%USERPROFILE%\AppData\Roaming\Claude\claude_desktop_config.json`

  ```json
  {
    "mcpServers": {
      "Enkrypt Secure MCP Gateway": {
        "command": "mcp",
        "args": [
          "run",
          "C:\\Users\\<User>\\Documents\\GitHub\\EnkryptAI\\secure-mcp-gateway\\src\\secure_mcp_gateway\\gateway.py"
        ],
        "env": {
          "ENKRYPT_GATEWAY_KEY": "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat",
          "ENKRYPT_PROJECT_ID": "3c09f06c-1f0d-4153-9ac5-366397937641",
          "ENKRYPT_USER_ID": "6469a670-1d64-4da5-b2b3-790de21ac726"
        }
      }
    }
  }
  ```

</details>

### 6.3 Restart Claude Desktop to run the Gateway

- After restarting, navigate to Claude Desktop `Settings`

  ![Claude Desktop Settings](./docs/images/claude-desktop-settings.png)

- Click on `Developer` -> `Enkrypt Secure MCP Gateway`

  ![Claude Desktop MCP Gateway Running](./docs/images/claude-desktop-mcp-running.png)

<br>
<details>
<summary><strong>🧰 Check tools and logs </strong></summary>
<br>

- You can also click on the settings icon below the search bar to see the Gateway in available

  ![Claude Desktop Gateway in Search](./docs/images/claude-desktop-gateway-in-search.png)

- Click on `Enkrypt Secure MCP Gateway` to see the list of tools available

  ![Claude Desktop MCP Gateway Tools](./docs/images/claude-desktop-gateway-tools-in-search.png)

- You can check Claude logs while asking Claude to do something to see the Gateway in action

  - Example 🍎 Linux/macOS log path: `~/Library/Application Support/Claude/logs/mcp-server-Enkrypt Secure MCP Gateway.log`

  - Example 🪟 Windows log path: `%USERPROFILE%\AppData\Roaming\Claude\logs\mcp-server-Enkrypt Secure MCP Gateway.log`

</details>

### 6.4 Example prompts

- `list all servers, get all tools available and echo test`
  - This uses a test MCP server `echo_server` which is in `bad_mcps/echo_mcp.py`

![claude-mcp-chat-1](./docs/images/claude-mcp-chat-1.png)

<br>
<details>
<summary><strong>💡 Other examples</strong></summary>
<br>

- We can also combine multiple prompts into one that trigger multiple tool calls at once

- Example: `echo test and also echo best`

![claude-mcp-chat-multiple](./docs/images/claude-mcp-chat-multiple.png)

- **Example: `echo "hello; ls -la; whoami"`**

- This could be a malicious prompt but because no guardrails are enabled, it will not be blocked

![claude-mcp-chat-echo-not-blocked](./docs/images/claude-mcp-chat-echo-not-blocked.png)

</details>

### 6.5 Example config file generated

- Example `enkrypt_mcp_config.json` generated by the `setup` script in `~/.enkrypt/enkrypt_mcp_config.json` on macOS and `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json` on Windows

- *If you ran docker command to install the Gateway, the config file will be in `~/.enkrypt/docker/enkrypt_mcp_config.json` on macOS and `%USERPROFILE%\.enkrypt\docker\enkrypt_mcp_config.json` on Windows*

  ```json
  {
    "admin_apikey": "AUTO_GENERATED_256_CHAR_ADMIN_API_KEY_FOR_ADMINISTRATIVE_OPERATIONS",
    "common_mcp_gateway_config": {
      "enkrypt_log_level": "INFO",
      "enkrypt_base_url": "https://api.enkryptai.com",
      "enkrypt_api_key": "YOUR_ENKRYPT_API_KEY",
      "enkrypt_use_remote_mcp_config": false,
      "enkrypt_remote_mcp_gateway_name": "enkrypt-secure-mcp-gateway-1",
      "enkrypt_remote_mcp_gateway_version": "v1",
      "enkrypt_mcp_use_external_cache": false,
      "enkrypt_cache_host": "localhost",
      "enkrypt_cache_port": 6379,
      "enkrypt_cache_db": 0,
      "enkrypt_cache_password": null,
      "enkrypt_tool_cache_expiration": 4,
      "enkrypt_gateway_cache_expiration": 24,
      "enkrypt_async_input_guardrails_enabled": false,
      "enkrypt_async_output_guardrails_enabled": false,
      "enkrypt_telemetry": {
        "enabled": true,
        "insecure": true,
        "endpoint": "http://localhost:4317"
      }
    },
    "mcp_configs": {
      "fcbd4508-1432-4f13-abb9-c495c946f638": {
        "mcp_config_name": "default_config",
        "mcp_config": [
          {
            "server_name": "echo_server",
            "description": "Simple Echo Server",
            "config": {
              "command": "python",
              "args": [
                "C:\\Users\\<User>\\Documents\\GitHub\\EnkryptAI\\secure-mcp-gateway\\src\\secure_mcp_gateway\\bad_mcps\\echo_mcp.py"
              ]
            },
            "tools": {},
            "input_guardrails_policy": {
              "enabled": false,
              "policy_name": "Sample Airline Guardrail",
              "additional_config": {
                "pii_redaction": false
              },
              "block": [
                "policy_violation"
              ]
            },
            "output_guardrails_policy": {
              "enabled": false,
              "policy_name": "Sample Airline Guardrail",
              "additional_config": {
                "relevancy": false,
                "hallucination": false,
                "adherence": false
              },
              "block": [
                "policy_violation"
              ]
            }
          }
        ]
      }
    },
    "projects": {
      "3c09f06c-1f0d-4153-9ac5-366397937641": {
        "project_name": "default_project",
        "mcp_config_id": "fcbd4508-1432-4f13-abb9-c495c946f638",
        "users": [
          "6469a670-1d64-4da5-b2b3-790de21ac726"
        ],
        "created_at": "2025-07-16T17:02:00.406877"
      }
    },
    "users": {
      "6469a670-1d64-4da5-b2b3-790de21ac726": {
        "email": "default@example.com",
        "created_at": "2025-07-16T17:02:00.406902"
      }
    },
    "apikeys": {
      "2W8UupCkazk4SsOcSu_1hAbiOgPdv0g-nN9NtfZyg-rvYGat": {
        "project_id": "3c09f06c-1f0d-4153-9ac5-366397937641",
        "user_id": "6469a670-1d64-4da5-b2b3-790de21ac726",
        "created_at": "2025-07-16T17:02:00.406905"
      }
    }
  }
  ```

### 6.6 Verify Cursor

- You can see the MCP server in the list of MCP servers in Cursor by navigating to `~/.cursor/mcp.json` and also by clicking on the settings icon on the top right and then clicking on `Tools & Integrations` or on the `MCP` tab

- *Generally restarting is not needed but if it is in loading state for a long time, please restart Cursor*

  ![cursor-mcp-running](./docs/images/cursor-mcp-running.png)

- Now you can chat with the MCP server.

  - **Example prompts:**

    - *(Click `Run Tool` when Cursor asks you to)*

    - `list all servers, get all tools available and echo test`
      - This uses a test MCP server `echo_server` which is in `bad_mcps/echo_mcp.py`

    ![cursor-mcp-chat](./docs/images/cursor-mcp-chat.png)

### 6.7 Verify Claude Code

- Run `claude mcp list` to see the gateway in the list of configured MCP servers

- Launch Claude Code and run `/mcp` to check server status

- Try `list all servers, get all tools available and echo test` as a prompt to verify the gateway is working

</details>

## 7. Edit the Gateway config as needed

<details>
<summary><strong>✂️ Edit Gateway Config </strong></summary>

- **Important:**

  - **We need to restart Claude Desktop after editing the config file**
  - **To make all new tools accessible, please use prompt "`list all servers, get all tools available`" for the MCP Client to discover all new tools. After this the MCP Client should be able to use all tools of the servers configured in the Gateway config file**

- You can add many MCP servers inside the `mcp_config` array of this gateway config

  - You can [look here for example servers](https://github.com/modelcontextprotocol/servers)

  - You can also try the [Enkrypt MCP Server](https://github.com/enkryptai/enkryptai-mcp-server)

  - Example:

      ```json
      {
        "common_mcp_gateway_config": {...},
        "mcp_configs": {
          "UNIQUE_MCP_CONFIG_ID": {
            "mcp_config_name": "default_config",
            "mcp_config": [
              {
                "server_name": "MCP_SERVER_NAME_1",
                "description": "MCP_SERVER_DESCRIPTION_1",
                "config": {
                  "command": "python/npx/etc.",
                  "args": [
                    "arg1", "arg2", ...
                  ],
                  "env": { "key": "value" }
                },
                // Set explicit tools to restrict access to only the allowed tools
                // Example: "tools": { "tool_name": "tool_description" }
                // Example: "tools": { "echo": "Echo a message" }
                // Or leave the tools empty {} to discover all tools dynamically
                "tools": {},
                "enable_server_info_validation": false,
                "enable_tool_guardrails": false,
                "input_guardrails_policy": {...},
                "output_guardrails_policy": {...}
              },
              {
                "server_name": "MCP_SERVER_NAME_2",
                "description": "MCP_SERVER_DESCRIPTION_2",
                "config": {...},
                "tools": {},
                "enable_server_info_validation": false,
                "enable_tool_guardrails": false,
                "input_guardrails_policy": {...},
                "output_guardrails_policy": {...}
              }
            ]
          },
          "UNIQUE_MCP_CONFIG_ID_2": {...}
        },
        "projects": {
          "UNIQUE_PROJECT_ID": {
            "project_name": "default_project",
            "mcp_config_id": "UNIQUE_MCP_CONFIG_ID",
            "users": [
              "UNIQUE_USER_ID"
            ],
            "created_at": "2025-01-01T00:00:00.000000"
          },
          "UNIQUE_PROJECT_ID_2": {...}
        },
        "users": {
          "UNIQUE_USER_ID": {
            "email": "default@example.com",
            "created_at": "2025-01-01T00:00:00.000000"
          },
          "UNIQUE_USER_ID_2": {...}
        },
        "apikeys": {
          "UNIQUE_GATEWAY_KEY": {
            "project_id": "UNIQUE_PROJECT_ID",
            "user_id": "UNIQUE_USER_ID",
            "created_at": "2025-01-01T00:00:00.000000"
          },
          "UNIQUE_GATEWAY_KEY_2": {...}
        }
      }
      ```

<br>
<details>
<summary><strong>⛩️ Gateway Config Schema</strong></summary>

- **`admin_apikey`** (root level): A 256-character random string used for authenticating REST API administrative operations (user management, project management, configuration management, API key management). This is automatically generated when you run `secure-mcp-gateway generate-config`.

  - **Important**: Keep this key secure! It provides full administrative access to the gateway.
  - Used with `Authorization: Bearer <admin_apikey>` header for REST API calls.
  - Different from regular API keys used by MCP clients to connect to the gateway.
  - See [Section 12: REST API for Administrative Operations](#12-other-tools-available) for details.

- If you want a different set of MCP servers for a separate client/user, you can add a new `mcp_config` section to the config file. Also, you can run cli commands. See [CLI-Commands-Reference.md](./CLI-Commands-Reference.md) section `2. CONFIGURATION MANAGEMENT` for details

- Set `enkrypt_log_level` to `DEBUG` to get more detailed logs inside `common_mcp_gateway_config` part of the config file

  - This defaults to `INFO`

- Now, inside `mcp_configs` array, for each individual MCP config, you can set the following:

  - `server_name`: A name of the MCP server which we connect to

  - `description` (optional): A description of the MCP server

  - `config`: The config for the MCP server as instructed by the MCP server's documentation

    - Generally you have the below keys in the config:

      - `command`: The command to run the MCP server

      - `args`: The arguments to pass to the command

      - `env`: The environment variables to set for the command

  - `tools`: The tools exposed by the MCP server

    - Either set explicit tools to restrict access to only the allowed tools or **leave it empty `tools": {}` for the Gateway to discover all tools dynamically**

    - Tools need to be given a name and a description like `"tools": { "dummy_echo": "Echo a message" }`

</details>
<details>
<summary><strong>🔒 Optional Guardrails Schema</strong></summary>

- Get your `enkrypt_api_key` from [Enkrypt Dashboard](https://app.enkryptai.com/settings) and add it to `common_mcp_gateway_config` section of the config file

- `enkrypt_use_remote_mcp_config` is used to fetch MCP server config from Enkrypt server remotely *(Coming soon)*

  - Please use `false` for now

  - This enables you to configure and manage MCP gateway config in Enkrypt Dashboard in a centralized place *(Coming soon)*

- If you have any external cache server like KeyDB running, you can set `enkrypt_mcp_use_external_cache` to `true` in your `common_mcp_gateway_config`

  - Set other relevant keys related to cache in your `common_mcp_gateway_config`

- `enkrypt_tool_cache_expiration` (in hours) decides how long the tools discovered from the MCP servers are cached locally or in the external cache server

- `enkrypt_gateway_cache_expiration` (in hours) decides how long the gateway config is cached locally or in the external cache server. This is useful when we integrate this with Enkrypt Auth server *(Coming soon)*

- `enkrypt_async_input_guardrails_enabled`

  - `false` by default

  - **Async mode is not recommended for tools that perform actions which cannot be undone**

  - Because the tool call is made parallel to guardrails call, it can't be blocked if input guardrails violations are detected

  - Useful for servers that return just info without performing actions i.e., only read operations

- `enkrypt_async_output_guardrails_enabled` *(Coming soon)*

  - This makes output side guardrails calls asynchronously to save time

  - i.e., Guardrails detect call, relevancy check, adherence check, PII unredaction, etc. are made in parallel after getting the response from the MCP server

- **Inside each MCP server config, you can set the following:**

  - `input_guardrails_policy`: Use this if we plan to use Enkrypt Guardrails on input side

  - `policy_name`: Name of the guardrails policy that you have created in the Enkrypt App or using the API/SDK

  - `enabled`: Whether to enable guardrails on the input side or not. This is `false` in the example config file

  - `additional_config`: Additional config for the guardrails policy

    - `pii_redaction`: Whether to redact PII in the request sent to the MCP server or not

      - If `true`, this also auto unredacts the PII in the response from the MCP server

  - `block`: List of guardrails to block

    - Possible values in the array are:

      - `topic_detector, nsfw, toxicity, pii, injection_attack, keyword_detector, policy_violation, bias, sponge_attack`

      - `system_prompt_protection, copyright_protection` *(Coming soon)*

      - This is similar to our AI Proxy deployments config. [Refer to our docs](https://docs.enkryptai.com/deployments-api-reference/endpoint/add-deployment#body-input-guardrails-policy-block)

- `output_guardrails_policy`: Use this if we plan to use Enkrypt Guardrails on output side

  - `policy_name`: Name of the guardrails policy that you have created in the Enkrypt App or using the API/SDK

  - `enabled`: Whether to enable guardrails on the output side or not. This is `false` in the example config file

  - `additional_config`: Additional config for the guardrails policy

    - `relevancy`: Whether to check for relevancy of the response from the MCP server

    - `adherence`: Whether to check for adherence of the response from the MCP server

    - `hallucination`: Whether to check for hallucination in the response from the MCP server *(Coming soon)*

  - `block`: List of guardrails to block

    - Possible values in the array are:

      - All possible values in input block array plus `adherence, relevancy`

      - `system_prompt_protection, copyright_protection, hallucination` *(Coming soon)*

      - This is similar to our AI Proxy deployments config. [Refer to our docs](https://docs.enkryptai.com/deployments-api-reference/endpoint/add-deployment#body-output-guardrails-policy-block)

</details>

</details>

## 8. CLI Quick Start Guide

<details>
<summary><strong>🖥️ CLI Quick Start Guide </strong></summary>

This section walks you through managing the gateway entirely via the CLI — from first setup to adding servers, managing projects, and day-to-day operations.

> **Tip:** All commands below show the **local (pip)** version. For Docker, just add `--docker` to any command — see the [Docker command pattern](#docker-command-pattern) at the bottom of this section.
>
> For the complete CLI reference, see [CLI-Commands-Reference.md](./CLI-Commands-Reference.md).

---

### Step 1: Generate your config

If you haven't already, generate the default config file. This creates everything you need to get started — a config with a sample echo server, a default project, user, and API key.

```bash
secure-mcp-gateway generate-config

# To overwrite an existing config and start fresh
secure-mcp-gateway generate-config --overwrite
```

**What this creates:**

| Item | Details |
|---|---|
| Config file | `~/.enkrypt/enkrypt_mcp_config.json` (macOS/Linux) or `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json` (Windows) |
| Default config | `default_config` with one `echo_server` |
| Default project | `default_project` linked to that config |
| Default user | `default@example.com` |
| Gateway API key | Auto-generated key for authentication |

Now that your config is ready, the next step is to tell your MCP client (Claude Desktop, Cursor, or Claude Code) about the gateway. This is a one-time setup — the install command writes the connection details into your client's config so it knows how to talk to the gateway.

---

### Step 2: Install the gateway for your MCP client

Pick the client you use and run the matching command:

```bash
# For Claude Desktop
secure-mcp-gateway install --client claude-desktop

# For Cursor
secure-mcp-gateway install --client cursor

# For Claude Code (requires the `claude` CLI — see https://docs.anthropic.com/en/docs/claude-code)
secure-mcp-gateway install --client claude-code
```

What this does behind the scenes: the install command reads the gateway key, project ID, and user ID from your generated config and writes them into your MCP client's config file.

For **Cursor** and **Claude Desktop**, you'll see output like:

```
INFO:  Updated 'Enkrypt Secure MCP Gateway' in C:\Users\<user>\.cursor\mcp.json
INFO: Successfully configured Cursor.
```

And your client's config file (e.g. `~/.cursor/mcp.json` or `~/Library/Application Support/Claude/claude_desktop_config.json`) will now contain:

```json
{
  "mcpServers": {
    "Enkrypt Secure MCP Gateway": {
      "command": "mcp",
      "args": [
        "run",
        "<path-to-your-install>/secure_mcp_gateway/gateway.py"
      ],
      "env": {
        "ENKRYPT_GATEWAY_KEY": "<your-auto-generated-gateway-key>",
        "ENKRYPT_PROJECT_ID": "<your-project-id>",
        "ENKRYPT_USER_ID": "<your-user-id>"
      }
    }
  }
}
```

For **Claude Code**, the install command runs `claude mcp add` behind the scenes and you'll see:

```
INFO: Successfully installed gateway for Claude Code
INFO: Server name: Enkrypt-Secure-MCP-Gateway
INFO: Scope: user (available across all Claude Code projects)
INFO: Verify with: claude mcp list
```

Once the install finishes, **restart your MCP client** so it picks up the new configuration. After the restart, the gateway will appear as a connected MCP server and you're ready to go.

At this point your setup looks like this:

```
default_project
 └── default_config
      └── echo_server  (a simple test server that echoes back your input)

 default@example.com  ← default user
 oQrnCFS43o-...rDjX   ← auto-generated gateway API key
```

You have one **project** (`default_project`) that points to one **config** (`default_config`), and that config has one **server** (`echo_server`). A default user and gateway API key were also created so everything works out of the box.

---

### Step 3: Check what you have

You can verify this setup at any time:

```bash
# List all configs
secure-mcp-gateway config list

# List servers in a config
secure-mcp-gateway config list-servers --config-name "default_config"

# List projects linked to a config
secure-mcp-gateway config list-projects --config-name "default_config"
```

---

### What can you do from here?

You have three paths depending on what you need. Pick the one that fits and follow the steps underneath it.

> **Rule of thumb:** If you only add or remove servers within your current config, just restart your MCP client. If you switch to a different config or create a new project, you need to reinstall.

---

<details>
<summary><strong>Path A — Add a server to your existing config (simplest, no reinstall needed)</strong></summary>
<br>

This is the most common path. You already have `default_config` — just add more servers to it.

```
  default_project
   └── default_config
        ├── echo_server       (already there)
        └── github_server     ← you are adding this
```

**1. Add the server:**

```bash
secure-mcp-gateway config add-server --config-name "default_config" --server-name "github_server" --server-command "npx" --args="-y,@modelcontextprotocol/server-github" --env '{"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_YOUR_TOKEN"}' --description "GitHub MCP Server"
```

**2. Verify it was added:**

```bash
secure-mcp-gateway config list-servers --config-name "default_config"
```

You should see:

```
Servers in config "default_config":
  1. echo_server - Simple Echo Server
  2. github_server - GitHub MCP Server
```

**3. Restart your MCP client** — no reinstall needed, just restart:

| Client | How to Restart |
|---|---|
| **Cursor** | `Ctrl+Shift+P` (or `Cmd+Shift+P`) then "Developer: Reload Window" |
| **Claude Desktop** | Quit the app completely, then reopen it |
| **Claude Code** | Exit and relaunch with `claude` |

**4. Update or remove a server later:**

```bash
# Update a server's description or settings
secure-mcp-gateway config update-server --config-name "default_config" --server-name "github_server" --description "Updated GitHub Server"

# Remove a server you no longer need
secure-mcp-gateway config remove-server --config-name "default_config" --server-name "github_server"
```

</details>

<details>
<summary><strong>Path B — Create a new config under the existing project</strong></summary>
<br>

Useful when you want separate configs for different environments (e.g. dev vs. production) under the same project.

```
  default_project
   ├── default_config         (original, untouched)
   │    └── echo_server
   └── production_config      ← new config you are creating
        └── github_server
```

**1. Create the new config** — pick one of these two options:

```bash
# Option A: Create an empty config and add servers manually (Step 2 below)
secure-mcp-gateway config add --config-name "production_config"

# Option B: Copy an existing config (including all its servers) — skip Step 2
secure-mcp-gateway config copy --source-config "default_config" --target-config "production_config"
```

> You cannot do both — `config copy` creates the target config for you. If you already ran `config add`, use Option A and add servers in Step 2.

**2. Add servers to it** (skip this if you used Option B above):



```bash
secure-mcp-gateway config add-server --config-name "production_config" --server-name "github_server" --server-command "npx" --args="-y,@modelcontextprotocol/server-github" --env '{"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_YOUR_TOKEN"}' --description "GitHub MCP Server"
```

**3. Point your project to the new config:**

```bash
secure-mcp-gateway project assign-config --project-name "default_project" --config-name "production_config"
```

**4. Reinstall the gateway for your MCP client** — this is required because the project now points to a different config:

```bash
secure-mcp-gateway install --client cursor
# or: secure-mcp-gateway install --client claude-desktop
# or: secure-mcp-gateway install --client claude-code
```

**5. Restart your MCP client** to pick up the changes.

**Other config management commands:**

```bash
# Rename a config
secure-mcp-gateway config rename --config-name "production_config" --new-name "staging_config"

# Get full details of a config
secure-mcp-gateway config get --config-name "production_config"

# Delete a config you no longer need
secure-mcp-gateway config remove --config-name "production_config"
```

</details>

<details>
<summary><strong>Path C — Create an entirely new project with its own config</strong></summary>
<br>

Creates a completely fresh project. Since a new project gets its own API key, your MCP client must be updated to use it.

```
  default_project              (original, untouched)
   └── default_config
        └── echo_server

  my_new_project               ← new project you are creating
   └── my_new_config           ← new config
        └── github_server
```

**1. Create the new config:**

```bash
secure-mcp-gateway config add --config-name "my_new_config"
```

**2. Add servers to it:**

```bash
secure-mcp-gateway config add-server --config-name "my_new_config" --server-name "github_server" --server-command "npx" --args="-y,@modelcontextprotocol/server-github" --env '{"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_YOUR_TOKEN"}' --description "GitHub MCP Server"
```

**3. Create the new project and link it to the config:**

```bash
# Create the project
secure-mcp-gateway project create --project-name "my_new_project"

# Link the config to the project
secure-mcp-gateway project assign-config --project-name "my_new_project" --config-name "my_new_config"
```

**4. Add a user to the project** (or use the existing default user):

```bash
# Use existing user
secure-mcp-gateway project add-user --project-name "my_new_project" --email "default@example.com"

# Or create a new user first, then add them
secure-mcp-gateway user create --email "alice@company.com"
secure-mcp-gateway project add-user --project-name "my_new_project" --email "alice@company.com"
```

**5. Generate an API key for the user in this project:**

```bash
secure-mcp-gateway user generate-api-key --project-name "my_new_project" --email "alice@company.com"
```

**6. Reinstall the gateway for your MCP client** — this is **required** because the new project has a different API key. Without reinstalling, your client will still use the old project's key and won't see the new project's servers:

```bash
secure-mcp-gateway install --client cursor
# or: secure-mcp-gateway install --client claude-desktop
# or: secure-mcp-gateway install --client claude-code
```

**7. Restart your MCP client** to pick up the new configuration.

</details>

---

### Step 4: Set your Enkrypt API key (for guardrails)

If you want to use Enkrypt AI guardrails (input/output protection, PII redaction, toxicity filtering, etc.), you need to set your Enkrypt API key. You can get one from the [Enkrypt AI dashboard](https://app.enkryptai.com).

```bash
# Set the Enkrypt API key
secure-mcp-gateway config set-enkrypt-api-key --api-key "YOUR_ENKRYPT_API_KEY"

# Verify it was set
secure-mcp-gateway config get-enkrypt-api-key
```

> Without this key, guardrails features won't work — but the gateway itself will still route tools normally.

---

### Step 5: Turn telemetry on or off

The gateway ships with OpenTelemetry support for logging, tracing, and metrics. By default it's enabled but will silently skip if the collector endpoint isn't reachable. You can explicitly enable or disable it:

```bash
# Disable telemetry
secure-mcp-gateway config configure-telemetry --enabled false

# Enable telemetry with a custom collector URL
secure-mcp-gateway config configure-telemetry --enabled true --url "http://localhost:4317"

# Allow insecure (non-TLS) connections to the collector
secure-mcp-gateway config configure-telemetry --insecure true
```

**Starting the telemetry stack:** The gateway sends telemetry data to an OpenTelemetry collector — it doesn't run one itself. The repo includes a ready-made stack (collector, Prometheus, Grafana, Jaeger, Loki) in the `infra/` directory:

```bash
cd infra
docker compose up -d
```

| Service | URL |
|---|---|
| Grafana dashboards | http://localhost:3000 |
| Jaeger trace viewer | http://localhost:16686 |
| Prometheus metrics | http://localhost:9090 |
| OTLP gRPC endpoint | http://localhost:4317 |

Once the stack is running, the gateway will automatically start sending traces, logs, and metrics to the collector.

---

### Step 6: System operations

```bash
# Check gateway health
secure-mcp-gateway system health-check

# Backup your entire config
secure-mcp-gateway system backup

# Restore from a backup
secure-mcp-gateway system restore --file <backup_file>

# Reset to defaults (⚠️ destructive)
secure-mcp-gateway system reset
```

---

### Docker command pattern

Add `--docker` to **any** CLI command to run it inside the Docker container automatically.
The flag auto-detects your OS, sets `HOST_OS` and `HOST_ENKRYPT_HOME`, and mounts the
`~/.enkrypt/docker` volume — no long `docker run` incantation needed.

```bash
# Quick (recommended) — works on macOS, Linux, and Windows (all shells)
secure-mcp-gateway --docker <COMMAND_HERE>

# Use a custom Docker image
secure-mcp-gateway --docker --docker-image my-registry/secure-mcp-gateway:v2.1.2 <COMMAND_HERE>
```

**Examples:**

```bash
# List configs
secure-mcp-gateway --docker config list

# Add a server
secure-mcp-gateway --docker config add-server --config-name "default_config" --server-name "my_server" --server-command "npx" --args="-y,@example/mcp-server" --description "My Server"

# Generate config
secure-mcp-gateway --docker generate-config

# Health check
secure-mcp-gateway --docker system health-check
```

---

<details>
<summary><strong>🔧 Troubleshooting — Server not showing up?</strong></summary>
<br>

```
┌──────────────────────────────────────────────────────────────────┐
│  ❓ DIAGNOSTIC FLOWCHART                                         │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Server not showing up after restart?                            │
│       │                                                          │
│       ▼                                                          │
│  Did you verify with "config list-servers"?                      │
│       │                                                          │
│       ├── NO → Run it. Is server listed?                         │
│       │          │                                               │
│       │          ├── NO → Wrong --config-name. Go to Step 3.     │
│       │          │                                               │
│       │          └── YES → Continue below ▼                      │
│       │                                                          │
│       └── YES, server IS in list-servers                         │
│              │                                                   │
│              ▼                                                   │
│  Did you restart the MCP client?                                 │
│       │                                                          │
│       ├── NO → Restart it (Step 5)                               │
│       │                                                          │
│       └── YES, I restarted                                       │
│              │                                                   │
│              ▼                                                   │
│  Check: does your ENKRYPT_PROJECT_ID in the                      │
│  MCP client config match a project that uses                     │
│  this config name?                                               │
│       │                                                          │
│       ├── NO → Your gateway key points to a                      │
│       │        different config. Either:                          │
│       │        a) Add server to the correct config, OR           │
│       │        b) Change the project's config assignment          │
│       │                                                          │
│       └── YES → Check if the server's command is                 │
│                 available in the gateway environment              │
│                 (e.g., npx requires Node.js)                     │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
```

</details>

</details>

## 9. (Optional) Add GitHub MCP Server to the Gateway

<details>
<summary><strong>👨🏻‍💻 Configure GitHub </strong></summary>

> **⚠️ Important Note for Docker Users:**
>
> If you're running the Enkrypt Gateway in Docker, **use the npx version** of the GitHub MCP server instead of the Docker version shown below. See the [npx-based configuration example](#github-server-configuration-npx-version) at the end of this section.
>
> For details on why, see [Section 4.3.7: Configuring MCP Servers When Gateway Runs in Docker](#-important-configuring-mcp-servers-when-gateway-runs-in-docker).

- `GitHub MCP Server` can be run with `docker` or `npx`. The Docker version requires Docker to be installed and running on your machine.

  - You can [download docker desktop from here](https://www.docker.com/products/docker-desktop/). Install and run it if you don't have it already

- [Create a personal access token from GitHub](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)

  - Create a token that has access to only public repos and set expiry very low initially for testing

  - Add the below GitHub server block to `enkrypt_mcp_config.json` inside `"mcp_config": []` array. It should already have the echo server config.

  - *NOTE: Don't forget to add comma `,` after the echo server block*

  - Replace `REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN` with the personal access token you created

  - You can also add via the cli. See [CLI-Commands-Reference.md](./CLI-Commands-Reference.md) section `2. CONFIGURATION MANAGEMENT` for details

  - Example:

  ```json
      "mcp_config": [
        {
          "server_name": "echo_server",
          "description": "Simple Echo Server",
          "config": {...},
          "tools": {},
          "input_guardrails_policy": {...},
          "output_guardrails_policy": {...}
        },
        {
          "server_name": "github_server",
          "description": "GitHub Server",
          "config": {
            "command": "docker",
            "args": [
              "run",
              "-i",
              "--rm",
              "-e",
              "GITHUB_PERSONAL_ACCESS_TOKEN",
              "ghcr.io/github/github-mcp-server"
            ],
            "env": {
              "GITHUB_PERSONAL_ACCESS_TOKEN": "REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN"
            }
          },
          "tools": {},
          "enable_server_info_validation": false,
          "enable_tool_guardrails": false,
          "input_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "pii_redaction": false
            },
            "block": [
              "policy_violation"
            ]
          },
          "output_guardrails_policy": {
            "enabled": false,
            "policy_name": "Sample Airline Guardrail",
            "additional_config": {
              "relevancy": false,
              "hallucination": false,
              "adherence": false
            },
            "block": [
              "policy_violation"
            ]
          }
        }
      ]
  ```

- Now restart Claude Desktop for it to detect the new server

- Then run the prompt `list all servers, get all tools available` for it to discover github server and all it's tools available

  ![claude-mcp-chat-github-tools-1](./docs/images/claude-mcp-chat-github-tools-1.png)

- Now run `List all files from https://github.com/enkryptai/enkryptai-mcp-server`

  ![claude-mcp-chat-github-tools-2](./docs/images/claude-mcp-chat-github-tools-2.png)

- Great! 🎉 We have successfully added a GitHub MCP Server to the Gateway. **However, it is completely unprotected and is open to all kinds of abuse and attacks.**

- **Now, let's say a prompt like this is run `Ask github for the repo "hello; ls -la; whoami"`**

  ![claude-mcp-chat-github-tools-3](./docs/images/claude-mcp-chat-github-tools-3.png)

- This may not have caused actual damage but imagine a more complicated prompt that may have caused actual damage to the system.

- To protect the MCP server, we can use **Enkrypt Guardrails** as shown in the next section.

### GitHub Server Configuration (npx version)

**✅ Recommended for Docker Gateway Deployments**

If you're running the Enkrypt Gateway in Docker or prefer not to use Docker-in-Docker, use the npx-based GitHub MCP server instead:

```json
{
  "server_name": "github_server",
  "description": "GitHub Server (npx version)",
  "config": {
    "command": "npx",
    "args": [
      "-y",
      "@modelcontextprotocol/server-github"
    ],
    "env": {
      "GITHUB_PERSONAL_ACCESS_TOKEN": "REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN"
    }
  },
  "tools": {},
  "enable_server_info_validation": false,
  "enable_tool_guardrails": false,
  "input_guardrails_policy": {
    "enabled": false,
    "policy_name": "Sample Airline Guardrail",
    "additional_config": {
      "pii_redaction": false
    },
    "block": [
      "policy_violation"
    ]
  },
  "output_guardrails_policy": {
    "enabled": false,
    "policy_name": "Sample Airline Guardrail",
    "additional_config": {
      "relevancy": false,
      "hallucination": false,
      "adherence": false
    },
    "block": [
      "policy_violation"
    ]
  }
}
```

**Benefits of npx version:**

- ✅ No Docker-in-Docker complications
- ✅ Faster startup time
- ✅ Works seamlessly with Dockerized gateway
- ✅ Simpler networking and volume management
- ✅ Lower resource overhead

**Prerequisites:**

- Node.js and npm must be installed in the gateway container or on the host machine
- The default Dockerfile already includes Node.js 22.x LTS

</details>

## 9.1 (Optional) Connect to MCP Servers with OAuth

<details>
<summary><strong>🔐 Configure OAuth for Remote MCP Servers </strong></summary>

Many MCP servers require OAuth authentication to access protected resources. The Secure MCP Gateway supports OAuth 2.0/2.1 with both **Client Credentials** and **Authorization Code + PKCE** flows for seamless integration with OAuth-enabled servers.

### Overview

The Gateway handles OAuth token acquisition, caching, and automatic refresh so you don't have to manage tokens manually. Tokens are automatically injected into requests when connecting to remote MCP servers.

**Supported Grant Types:**

- **Client Credentials** - For server-to-server authentication (machine-to-machine)
- **Authorization Code + PKCE** - For user authorization flows with enhanced security

**Key Features:**

- Automatic browser authorization for Authorization Code flow
- Local and remote callback URL support
- Automatic token refresh before expiration
- Secure token caching
- PKCE (S256) for enhanced security
- State parameter for CSRF protection

### OAuth Configuration Examples

#### Client Credentials Flow (Server-to-Server)

For machine-to-machine authentication, use the Client Credentials flow:

```json
{
  "server_name": "oauth-enabled-server",
  "description": "Remote MCP Server with OAuth",
  "config": {
    "command": "npx",
    "args": ["-y", "mcp-remote", "https://api.example.com/mcp", "--allow-http"]
  },
  "oauth_config": {
    "enabled": true,
    "is_remote": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_GRANT_TYPE": "client_credentials",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token",
    "OAUTH_AUDIENCE": "https://api.example.com"
  },
  "tools": {},
  "enable_server_info_validation": false,
  "enable_tool_guardrails": false,
  "input_guardrails_policy": {
    "enabled": false
  },
  "output_guardrails_policy": {
    "enabled": false
  }
}
```

### Key OAuth Fields

#### Core Configuration

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `enabled` | Yes | `false` | Enable OAuth for this server |
| `is_remote` | Recommended | Auto-detected | Set to `true` for remote servers, `false` for local servers |
| `OAUTH_VERSION` | No | `"2.1"` | OAuth version: `"2.0"` or `"2.1"` |
| `OAUTH_GRANT_TYPE` | No | `"client_credentials"` | Grant type: `"client_credentials"` or `"authorization_code"` |
| `OAUTH_CLIENT_ID` | Yes | - | Your OAuth client ID |
| `OAUTH_CLIENT_SECRET` | Yes | - | Your OAuth client secret |
| `OAUTH_TOKEN_URL` | Yes | - | Token endpoint URL (must be HTTPS for OAuth 2.1) |
| `OAUTH_AUTHORIZATION_URL` | Conditional | - | Authorization endpoint (required for `authorization_code` grant) |
| `OAUTH_REDIRECT_URI` | Conditional | - | Callback URL (required for `authorization_code` grant) |

#### Optional OAuth Parameters

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `OAUTH_AUDIENCE` | No | `null` | Intended audience for the token (aud claim) |
| `OAUTH_ORGANIZATION` | No | `null` | Organization ID (for multi-tenant OAuth providers) |
| `OAUTH_SCOPE` | No | `null` | Space-separated scopes (e.g., "read write") |
| `OAUTH_RESOURCE` | No | `null` | Resource indicator (RFC 8707) |
| `OAUTH_TOKEN_EXPIRY_BUFFER` | No | `300` | Seconds before token expiry to trigger refresh (default: 5 minutes) |
| `OAUTH_USE_PKCE` | No | `false` | Enable PKCE for Authorization Code flow (recommended) |
| `OAUTH_CODE_CHALLENGE_METHOD` | No | `"S256"` | PKCE challenge method: `"S256"` (recommended) or `"plain"` |
| `OAUTH_ADDITIONAL_PARAMS` | No | `{}` | Additional parameters to include in token requests (JSON object) |
| `OAUTH_CUSTOM_HEADERS` | No | `{}` | Custom HTTP headers for token requests (JSON object) |

#### Security & Authentication Settings

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `OAUTH_USE_BASIC_AUTH` | No | `true` | Use HTTP Basic Auth for client credentials (RFC 6749 §2.3.1) |
| `OAUTH_ENFORCE_HTTPS` | No | `true` | Enforce HTTPS for OAuth 2.1 compliance (set `false` only for local testing) |
| `OAUTH_TOKEN_IN_HEADER_ONLY` | No | `true` | Send token only in Authorization header (recommended) |
| `OAUTH_VALIDATE_SCOPES` | No | `true` | Verify returned token contains requested scopes |

#### Mutual TLS (mTLS) Configuration

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `OAUTH_USE_MTLS` | No | `false` | Enable mutual TLS (RFC 8705) for enhanced security |
| `OAUTH_CLIENT_CERT_PATH` | Conditional | `null` | Path to client certificate file (required if mTLS enabled) |
| `OAUTH_CLIENT_KEY_PATH` | Conditional | `null` | Path to client private key file (required if mTLS enabled) |
| `OAUTH_CA_BUNDLE_PATH` | No | `null` | Path to CA bundle for server certificate verification |

#### Token Revocation

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `OAUTH_REVOCATION_URL` | No | `null` | Token revocation endpoint URL (RFC 7009) |

### Authorization Code + PKCE Flow

For user authorization with enhanced security, use the Authorization Code flow with PKCE:

```json
{
  "server_name": "user-auth-server",
  "description": "MCP Server requiring user authorization",
  "config": {
    "command": "npx",
    "args": ["-y", "mcp-remote", "https://api.example.com/mcp"]
  },
  "oauth_config": {
    "enabled": true,
    "is_remote": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_GRANT_TYPE": "authorization_code",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_AUTHORIZATION_URL": "https://auth.example.com/authorize",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token",
    "OAUTH_REDIRECT_URI": "http://localhost:8080/callback",
    "OAUTH_SCOPE": "openid profile email",
    "OAUTH_USE_PKCE": true,
    "OAUTH_CODE_CHALLENGE_METHOD": "S256"
  },
  "tools": {},
  "enable_server_info_validation": false,
  "enable_tool_guardrails": true
}
```

#### Automatic Browser Authorization

When using Authorization Code flow, the gateway automatically:

1. Opens your browser to the authorization URL
2. Handles the callback (localhost or remote)
3. Exchanges the authorization code for tokens
4. Caches tokens for future use

**Flow Options:**

**Localhost Callback (Automatic):**

```json
"OAUTH_REDIRECT_URI": "http://localhost:8080/callback"
```

- Gateway starts local server on port 8080
- Automatically captures authorization code
- No manual intervention needed

**Remote Callback (Manual Code Entry):**

```json
"OAUTH_REDIRECT_URI": "https://oauth.yourdomain.com/callback"
```

- Gateway opens browser for authorization
- User completes authorization on remote page
- User copies code from callback page
- User pastes code into terminal
- Gateway exchanges code for token

#### Setting Up Remote Callback

If you want to use a remote callback URL (professional, branded experience):

1. **Host the callback page:**

   ```bash
   # Quick start with Python
   python host_oauth_callback.py
   
   # Or with Docker
   docker-compose -f docker-compose.oauth-callback.yml up -d
   
   # Or deploy oauth_callback.html to any static hosting
   # (GitHub Pages, Vercel, Netlify, AWS S3, etc.)
   ```

2. **Update your config:**

   ```json
   "OAUTH_REDIRECT_URI": "https://your-domain.com/callback"
   ```

3. **Register with OAuth provider:**
   - Add callback URL to your OAuth app settings
   - Auth0: "Allowed Callback URLs"
   - Okta: "Sign-in redirect URIs"
   - Azure AD: "Redirect URIs"
   - Google: "Authorized redirect URIs"

### Testing with Echo OAuth Server

The Gateway includes a test echo server that demonstrates OAuth header injection. You can use it to verify OAuth is working correctly.

#### Step 1: Start the Echo OAuth Server

The echo OAuth server needs to run in HTTP mode to accept remote connections:

**macOS/Linux:**

```bash
# Export the environment variable
export MCP_HTTP_MODE=true

# Start the server
python src/secure_mcp_gateway/bad_mcps/echo_oauth_mcp.py
```

**Windows (PowerShell):**

```powershell
# Set the environment variable
$env:MCP_HTTP_MODE = "true"

# Start the server
python src/secure_mcp_gateway/bad_mcps/echo_oauth_mcp.py
```

**Windows (Command Prompt):**

```cmd
# Set the environment variable
set MCP_HTTP_MODE=true

# Start the server
python src/secure_mcp_gateway/bad_mcps/echo_oauth_mcp.py
```

The server will start on `http://localhost:8001/mcp/` and print OAuth-related headers whenever tools are called.

#### Step 2: Add Echo OAuth Server to Gateway Config

Add this configuration to your `enkrypt_mcp_config.json` in the `mcp_config` array:

```json
{
  "server_name": "echo_oauth_server",
  "description": "Echo Server with OAuth Testing",
  "config": {
    "command": "npx",
    "args": [
      "-y",
      "mcp-remote",
      "http://localhost:8001/mcp/",
      "--allow-http"
    ]
  },
  "oauth_config": {
    "enabled": true,
    "is_remote": true,
    "OAUTH_VERSION": "2.0",
    "OAUTH_GRANT_TYPE": "client_credentials",
    "OAUTH_CLIENT_ID": "test-client-id",
    "OAUTH_CLIENT_SECRET": "test-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token",
    "OAUTH_ENFORCE_HTTPS": false
  },
  "tools": {},
  "enable_server_info_validation": false,
  "enable_tool_guardrails": false,
  "input_guardrails_policy": {
    "enabled": false
  },
  "output_guardrails_policy": {
    "enabled": false
  }
}
```

**Note:** `OAUTH_ENFORCE_HTTPS: false` is set only for local testing. Always use HTTPS in production!

#### Step 3: Test OAuth Token Injection

1. Restart Claude Desktop (or your MCP client) to pick up the new server configuration

2. Use the prompt: `list all servers and discover tools from echo_oauth_server`

3. Call the echo tool: `call the echo tool from echo_oauth_server with message "test oauth"`

4. Check the echo server terminal output - you should see OAuth headers being printed:

```text
================================================================================
🔐 OAuth HTTP Headers Check (Remote Mode)
================================================================================
  ✅ AUTHORIZATION: Bearer <token>...
  ❌ X-OAUTH-TOKEN: Not set
  ❌ X-ACCESS-TOKEN: Not set

📋 All Request Headers:
  authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  content-type: application/json
  user-agent: python-requests/2.31.0
================================================================================
```

This confirms the OAuth token is being automatically acquired and injected into the Authorization header.

### OAuth Token Flows

#### Client Credentials Flow

1. **First Request**: Gateway acquires token from OAuth provider
2. **Caching**: Token is cached with expiration tracking
3. **Token Injection**:
   - **Remote servers**: Token added as `Authorization: Bearer <token>` header
   - **Local servers**: Token available in environment variables
4. **Auto-refresh**: Token refreshed 5 minutes before expiry (configurable)

#### Authorization Code + PKCE Flow

1. **Initial Setup**: Gateway generates PKCE code verifier and challenge
2. **Browser Authorization**:
   - Gateway opens browser to authorization URL
   - User logs in and authorizes the application
3. **Callback Handling**:
   - **Localhost**: Gateway automatically captures code from callback
   - **Remote**: User copies code and pastes into terminal
4. **Token Exchange**: Gateway exchanges authorization code for tokens
5. **Caching & Refresh**: Tokens cached and automatically refreshed before expiry

### Advanced Features

- **Authorization Code + PKCE**: User authorization with enhanced security (S256)
- **Automatic Browser Flow**: Opens browser and handles callback automatically
- **Remote Callback Support**: Host callback page on your domain
- **Mutual TLS (mTLS)**: Enhanced security with client certificates (RFC 8705)
- **Token Revocation**: Programmatically revoke tokens (RFC 7009)
- **Scope Validation**: Verify returned token has requested scopes
- **Custom Headers**: Add custom HTTP headers to token requests
- **State Parameter**: CSRF protection for Authorization Code flow
- **Metrics**: Track token acquisition success/failure, cache hit ratio

### Troubleshooting

**OAuth token request failed:**

- Verify CLIENT_ID and CLIENT_SECRET are correct
- Check TOKEN_URL is reachable
- Ensure HTTPS is used (or set `OAUTH_ENFORCE_HTTPS: false` for testing)

**Token not appearing in requests:**

- Confirm `is_remote: true` for remote servers
- Check server logs for OAuth acquisition messages
- Enable debug logging: `"enkrypt_log_level": "DEBUG"`

**Authorization Code flow issues:**

- Verify AUTHORIZATION_URL and REDIRECT_URI are correct
- Ensure callback URL is registered with OAuth provider
- Check that browser opens automatically (or use manual URL)
- For remote callbacks, verify callback page is accessible

**Callback not working:**

- Localhost: Gateway automatically tries next available port if 8080 is in use (up to 10 ports)
- Remote: Verify callback URL is accessible and matches OAuth provider settings
- Check for firewall blocking the callback

**Echo server not receiving headers:**

- Ensure `MCP_HTTP_MODE=true` environment variable is set
- Verify server is running on http://localhost:8001/mcp/

</details>

## 10. (Optional) Protect GitHub MCP Server and Test Echo Server

<details>
<summary><strong>🎁 Protect with Enkrypt Guardrails for FREE </strong></summary>
<br>
<details>
<summary><strong>10.1 🌐 Create a Guardrail in Enkrypt App </strong></summary>
<br>

- You can use a prompt to generate rules or generate a PDF file while you can then paste or upload while creating a policy in the App

<br>
<details>
<summary><strong>10.1.1 🔍 Rules to copy </strong></summary>
<br>

```text

1. MCP-Specific Security Policies
Scan all tool descriptions for hidden instructions/malicious patterns.

Authenticate MCP servers with cryptographic verification.

Lock and pin tool versions to prevent rug-pull attacks.

Enforce isolation between MCP servers to avoid interference.

Restrict GitHub MCP access to specific repositories and users.

2. Code Filtering and Prohibited Patterns
Block known malicious code patterns (e.g., buffer overflows, SQL injection).

Detect malware signatures (e.g., keylogger, trojan).

Prevent crypto mining code.

Identify network attack patterns (e.g., DDoS, botnet).

Block privilege escalation code (e.g., root exploits).

3. Repository Access Control
Enforce role-based read access for private repositories.

Enable strict content filtering for all access types.

Mandate audit logging for private repositories.

Quarantine access to sensitive repositories.

4. AI-Specific Guardrails
Detect tool poisoning via hidden tags and file access commands.

Monitor behavior for file access and network activity.

Require explicit UI approval for suspicious tools.

Protect against prompt injection in GitHub issues.

Block PRs that expose private repo data.

Quarantine suspicious GitHub issues.

5. RADE (Retrieval-Agent Deception) Mitigation
Scan retrieved content for embedded commands.

Validate document integrity and modification timestamps.

Sandbox retrieved content to prevent auto-execution.

6. Input Validation
Limit prompt length (max 4096 tokens).

Block forbidden keywords (e.g., "ignore previous instructions").

Detect encoded/injection patterns (base64, hex, unicode).

7. Model Behavior Constraints
Limit code generation by complexity and size.

Restrict certain languages (e.g., shell scripts, assembly).

Monitor API/system calls and network activity.

Enforce strict context boundaries across repositories.

```

</details>
<br>
<details>
<summary><strong>10.1.2 💡 Prompt used to generate the rules </strong></summary>
<br>

- `Give numbered list of security rules in plain text for configuring AI guardrails for a GitHub server on the rules and policies it needs to follow to prevent malicious use of the GitHub services`

- Then say `Research latest GitHub MCP hacks and abuses people are trying and update the rules to prevent those. Keep research to the most severe topics`

- Then say `Only keep essential security rules to reduce size. Remove unwanted sections like post incident, compliance, audit, etc which cannot be used while prevention`

- Then you can copy paste the rules while creating the policy

</details>
<br>

- Go to [Enkrypt App](https://app.enkryptai.com) and login with either OTP or Google or Microsoft account

- Click on `Policies`

  ![enkrypt-github-guardrail-1](./docs/images/enkrypt-github-guardrail-1.png)

- Click on `Add new policy`

  ![enkrypt-github-guardrail-2](./docs/images/enkrypt-github-guardrail-2.png)

- Name it `GitHub Safe Policy` and paste the policy rules and click `Save`

  ![enkrypt-github-guardrail-3](./docs/images/enkrypt-github-guardrail-3.png)

- This is how a saved policy looks like with the rules applied for `Policy violation` Guardrails

  ![enkrypt-github-guardrail-4](./docs/images/enkrypt-github-guardrail-4.png)

- Now navigate back to home or hover over left sidebar and click `Guardrails`

- Click on `Add New Guardrail` button on the top right

  ![enkrypt-app-add-guardrail-button](./docs/images/enkrypt-app-add-guardrail-button.png)

- Name it `GitHub Guardrail`, toggle `Injection Attack` OFF

  ![enkrypt-app-add-guardrail-add-1](./docs/images/enkrypt-app-add-guardrail-add-1.png)

- Scroll down on `Configure Guardrails` side panel and toggle `Policy Violation` ON, select the newly created policy and tick `Need Explanation` if needed

  ![enkrypt-app-add-guardrail-add-2](./docs/images/enkrypt-app-add-guardrail-add-2.png)

- Now, click on `Save` button on the bottom right to save the guardrail

  ![enkrypt-app-add-guardrail-add-3](./docs/images/enkrypt-app-add-guardrail-add-3.png)

- We can see the newly added guardrail in the list of guardrails

  ![enkrypt-app-add-guardrail-add-4](./docs/images/enkrypt-app-add-guardrail-add-4.png)

</details>
<details>
<summary><strong>10.2 🔑 Get Enkrypt API Key </strong></summary>
<br>

- Now, we need get out FREE API Key from Enkrypt App. Hover over the left sidebar for it to expand and click on `Settings`

  - You can also directly navigate to [https://app.enkryptai.com/settings](https://app.enkryptai.com/settings)

  ![enkrypt-app-settings-1](./docs/images/enkrypt-app-settings-1.png)

- Now click on the `Copy` icon next to your obfuscated API Key to copy the key to your clipboard as highlighted in the screenshot below

  ![enkrypt-app-settings-2](./docs/images/enkrypt-app-settings-2.png)

</details>
<details>
<summary><strong>10.3 🔑 Add API Key and the Guardrail to Config File </strong></summary>
<br>

- Now we have everything we need from the App. Let's add the API Key to the `enkrypt_mcp_config.json` file

- Open the `enkrypt_mcp_config.json` file from `~/.enkrypt/enkrypt_mcp_config.json` on macOS or `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json` on Windows

  - *If you ran docker command to install the Gateway, the config file will be in `~/.enkrypt/docker/enkrypt_mcp_config.json` on macOS and `%USERPROFILE%\.enkrypt\docker\enkrypt_mcp_config.json` on Windows*

- Add the API Key to the `common_mcp_gateway_config` section by replacing `YOUR_ENKRYPT_API_KEY` with the API Key you copied from the App

- Inside the **`GitHub`** server block we added in the previous section,

  - Add the newly created Guardrail `GitHub Guardrail` to the `input_guardrails_policy` and `output_guardrails_policy` sections

  - By replacing `"policy_name": "Sample Airline Guardrail"` with `"policy_name": "GitHub Guardrail"`

  - Now change `enabled` to `true` for `input_guardrails_policy` from previous `false`

    - We will leave `output_guardrails_policy` as `false` for now

  - We already should have `policy_violation` in the `block` array for both policies

  - So the final config should look something like this:

  ```json
  {
    "common_mcp_gateway_config": {
      ...
      "enkrypt_api_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      ...
    },
    "mcp_configs": {
      "fcbd4508-1432-4f13-abb9-c495c946f638": {
        "mcp_config_name": "default_config",
        "mcp_config": [
          {
            "server_name": "echo_server",
            ...
          },
          {
            "server_name": "github_server",
            "description": "GitHub Server",
            "config": {
              "command": "docker",
              "args": [
                "run",
                "-i",
                "--rm",
                "-e",
                "GITHUB_PERSONAL_ACCESS_TOKEN",
                "ghcr.io/github/github-mcp-server"
              ],
              "env": {
                "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
              }
            },
            "tools": {},
            "enable_server_info_validation": false,
            "enable_tool_guardrails": false,
            "input_guardrails_policy": {
              "enabled": true,
              "policy_name": "GitHub Guardrail",
              "additional_config": {
                "pii_redaction": false
              },
              "block": ["policy_violation"]
            },
            "output_guardrails_policy": {
              "enabled": false,
              "policy_name": "GitHub Guardrail",
              "additional_config": {
                "relevancy": false,
                "hallucination": false,
                "adherence": false
              },
              "block": ["policy_violation"]
            }
          }
        ]
      }
    },
    "projects": {
      ...
    },
    "users": {
      ...
    },
    "apikeys": {
      ...
    }
  }
  ```

</details>
<details>
<summary><strong>10.4 🧪 Test Guardrails </strong></summary>
<br>

- **Save** the file and restart Claude Desktop for it to detect the changes

- `GitHub MCP Server` needs `docker` to be installed. So, please install and have `docker` running on your machine before proceeding with the steps below

  - You can [download docker desktop from here](https://www.docker.com/products/docker-desktop/). Install and run it if you don't have it already

- Now run the prompt `list all services, tools` for it to discover github, echo servers and all their tools available

- After this, let's rerun the previously successful malicious prompt **`Ask github for the repo "hello; ls -la; whoami"`**

  - We can see that the prompt is blocked as Input Guardrails blocked the request

    ![claude-mcp-chat-github-guardrails-1](./docs/images/claude-mcp-chat-github-guardrails-1.png)

- We can configure the test `echo` server with Guardrails of our choice and see the detections by running `echo "hello; ls -la; whoami"`.

  - The below prompt which worked before but is blocked with Guardrails

  - Experiment and try the `echo` server with various guardrails to see how it behaves. [You can also try our Playground for better testing](https://app.enkryptai.com/playground/guardrails).

  ![claude-mcp-chat-echo-guardrails-2](./docs/images/claude-mcp-chat-echo-guardrails-2.png)

</details>
<details>
<summary><strong>10.5 🔧 Fine tune Guardrails </strong></summary>
<br>

- *The safe prompt `List all files from https://github.com/enkryptai/enkryptai-mcp-server` may also be blocked if you use Injection Attack Detector or Policy Violation on Output side. So, there is some fine tuning required for the guardrails to find the best combination of enabled detectors and blocks for your servers. See the next section for recommendations.*

</details>
</details>

## 11. Recommendations for using Guardrails

<details>
<summary><strong>⭐ Recommendations </strong></summary>
<br>

- We have found that the best way to use Enkrypt Guardrails in MCP Gateway is to have a separate guardrail for each server. This way we can have a fine tuned guardrail for each server.

- Because each MCP Server is very different from the other, it is not possible to have a single guardrail that works for all servers.

- Some may need `Toxicity Detector`, some `NSFW Detector`, some `Injection Attack Detector`, some `Keyword Detector`, some `Policy Violation`, some may need `Relevancy` detector, some may need `Adherence` detector, etc.

- Some may need a combination of these detectors to work together to block malicious requests.

- Some may need Guardrails on the input side, some on the output and some may need both to be applied.

- See our docs for details on [various detectors available.](https://docs.enkryptai.com/guardrails-api-reference/Prompt_Injection)

- Hence, have separate guardrails for each server and experiment with the best combination of detectors and blocks for each server that blocks malicious requests but allows legitimate requests to pass through.

- Try our `Policy Violation` detector with your own custom policy which details what is allowed and what is not. This may be the best way for your use case.

<details>
<summary><strong>🚨 Try Policy Violation </strong></summary>
<br>

- You can navigate to the [Enkrypt App Homepage](https://app.enkryptai.com), login and Click on `Policies` to create your own custom policy.

  - This accepts text as well as PDF file as input so create a file with all the rules you want to apply to your MCP server and upload it

  - Once created, you can use it while configuring the Guardrail like we say with `GitHub Guardrail` in the previous section

  ![enkrypt-app-homepage-policies](./docs/images/enkrypt-app-homepage-policies.png)

</details>

### 11.1 Per-Server Guardrail Configuration

You can control guardrail behavior for each server individually using per-server flags in your configuration.

**Note:** While both fields default to `false`, it's recommended to explicitly include them in your server configurations for clarity and maintainability.

#### `enable_server_info_validation` (boolean, default: `false`)

Controls whether server descriptions are validated during discovery/registration for harmful content (injection attacks, policy violations, etc.).

**When to disable:**

- Testing/development environments with known safe servers
- Internal servers where content is fully trusted
- When server metadata contains technical terms that trigger false positives

**Example:**

```json
{
  "server_name": "test_server",
  "description": "Development test server",
  "config": {
    "command": "python",
    "args": ["test_server.py"]
  },
  "enable_server_info_validation": false,
  "enable_tool_guardrails": false,
  "input_guardrails_policy": {
    "enabled": false
  },
  "output_guardrails_policy": {
    "enabled": false
  }
}
```

#### `enable_tool_guardrails` (boolean, default: `false`)

Controls whether individual tool descriptions and schemas are validated during discovery.

**Guardrail Levels:**

The gateway has three distinct levels of guardrails:

1. **Server Registration Validation** (`enable_server_info_validation`)
   - **When**: During server discovery, before any tools are loaded
   - **What**: Validates server names and descriptions for harmful content
   - **Blocks**: Servers with malicious metadata

2. **Tool Registration Validation** (`enable_tool_guardrails`)
   - **When**: During tool discovery
   - **What**: Validates tool descriptions and schemas
   - **Blocks**: Individual tools with harmful content

3. **Runtime Guardrails** (`input_guardrails_policy` / `output_guardrails_policy`)
   - **When**: During tool execution (input before, output after)
   - **What**: Validates tool arguments and responses
   - **Blocks**: Requests/responses violating policies

**Note:** All three levels are independent and can be configured separately per server.

</details>

## 12. Other Tools Available

<details>
<summary><strong>🔧 REST API for Administrative Operations </strong></summary>
<br>

The Gateway provides a REST API server for administrative operations like managing users, projects, configurations, and API keys.

### Starting the REST API Server

```bash
secure-mcp-gateway system start-api --host 0.0.0.0 --port 8001
```

- **API Documentation**: Available at `http://localhost:8001/docs` (Swagger UI)
- **Health Check**: `http://localhost:8001/health`
- **OpenAPI Schema**: Loaded from `openapi.json` in the project root

### Admin API Key Authentication

**Important**: Administrative operations require a special `admin_apikey` that is separate from regular user API keys. This provides enhanced security for admin operations.

#### Getting Your Admin API Key

The `admin_apikey` is automatically generated when you run `secure-mcp-gateway generate-config`. Find it in your configuration file:

- **Windows**: `%USERPROFILE%\.enkrypt\enkrypt_mcp_config.json`
- **macOS/Linux**: `~/.enkrypt/enkrypt_mcp_config.json`

```json
{
  "admin_apikey": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6...",
  "apikeys": {
    "regular_user_key_1": { ... },
    "regular_user_key_2": { ... }
  },
  ...
}
```

#### Key Differences

- **`admin_apikey`** (root level): Used for all administrative operations (user management, project management, etc.)
  - 256-character random string for maximum security
  - Generated during `secure-mcp-gateway generate-config`
  - Required for REST API endpoints

- **`apikeys`** (in the `apikeys` section): Used for gateway access by users
  - Used by MCP clients to connect to the gateway
  - Associated with specific users and projects
  - Not used for administrative operations

#### Using the Admin API Key

Include the `admin_apikey` in the Authorization header for all administrative API calls:

```bash
curl -X GET "http://localhost:8001/api/v1/users" -H "Authorization: Bearer YOUR_ADMIN_API_KEY_HERE"
```

**Security Note**:
- Keep your admin API key secure and never commit it to version control
- Only share the admin API key with authorized administrators
- Regular users should never have access to the admin API key

### Available Administrative Operations

The REST API provides endpoints for:

1. **User Management**: Create, list, update, and delete users
2. **Project Management**: Create projects, assign configurations, manage users
3. **API Key Management**: Generate, rotate, disable/enable, and delete API keys
4. **Configuration Management**: Create, update, and manage MCP configurations and servers

For complete API documentation and examples, see:
- [API-Reference.md](./API-Reference.md)
- Interactive API docs at `http://localhost:8001/docs`

</details>

<details>
<summary><strong>💾 Cache Management </strong></summary>
<br>
<details>
<summary><strong>12.1 📊 Get Cache Status </strong></summary>
<br>

- The Gateway can give the summary of it's cache status by looking at the local/external cache server

- This is useful to debug issues if for example a tool was updated remotely by a server but the Gateway is not aware of it yet

  ![claude-mcp-chat-get-cache-status](./docs/images/claude-mcp-chat-get-cache-status.png)

</details>
<details>
<summary><strong>12.2 🧹 Clear Cache </strong></summary>

- The Gateway can clear it's cache from local/external cache server

- This is useful to clear the cache if for example a tool was updated remotely by a server but the Gateway is not aware of it yet

- You can either clear all cache or specific cache by providing the `server_name`

  - Example: `clear cache for echo_server`

- You can also clear all cache or just the gateway cache or just the server cache

  - Example: `clear all cache`, `clear just gateway cache`, `clear server cache for echo_server`, `Clear all server cache`

  ![claude-mcp-chat-clear-cache](./docs/images/claude-mcp-chat-clear-cache.png)

</details>
</details>

## 13. Deployment patterns

<details>
<summary><strong>🪂 Deployment Patterns </strong></summary>

1. [Local Gateway, Local Guardrails and Local MCP Server](#131-local-gateway-local-guardrails-and-local-mcp-server)

2. [Local Gateway, Local MCP Server with Remote Guardrails](#132-local-gateway-local-mcp-server-with-remote-guardrails)

3. [Local Gateway with Remote MCP Server and Remote Guardrails](#133-local-gateway-with-remote-mcp-server-and-remote-guardrails)

4. [Remote Gateway, Remote MCP Server and Remote Guardrails](#134-remote-gateway-remote-mcp-server-and-remote-guardrails)

### 13.1 Local Gateway, Local Guardrails and Local MCP Server

![Local Gateway with Local Guardrails Flow](./docs/images/enkryptai-apiaas-MCP%20Gateway%20All%20Local.drawio.png)

### 13.2 Local Gateway, Local MCP Server with Remote Guardrails

![Local Gateway with Remote Guardrails Flow](./docs/images/enkryptai-apiaas-MCP%20Gateway%20Local.drawio.png)

### 13.3 Local Gateway with Remote MCP Server and Remote Guardrails

![Local Gateway with Remote Guardrails and Remote MCP Server Flow](./docs/images/enkryptai-apiaas-MCP%20Gateway%20Local%20with%20Remote.drawio.png)

### 13.4 Remote Gateway, Remote MCP Server and Remote Guardrails

![Remote Gateway with Remote Guardrails and Remote MCP Server Flow](./docs/images/enkryptai-apiaas-MCP%20Gateway%20Full%20Remote.drawio.png)

</details>

## 14. Uninstall the Gateway

<details>
<summary><strong>🗑️ Uninstall the Gateway </strong></summary>

- To remove the Gateway from any MCP client, just remove the MCP server block `"Enkrypt Secure MCP Gateway": {...}` from the client's config file. For Claude Code, run `claude mcp remove Enkrypt-Secure-MCP-Gateway`.

  - Restart the MCP client to apply the changes for some clients like Claude Desktop. Cursor does not require a restart.

- To uninstall the pip package, run the following command:

  ```bash
  pip uninstall secure-mcp-gateway
  ```

</details>

## 15. Troubleshooting

<details>
<summary><strong>🕵 Troubleshooting </strong></summary>

- If any calls fail in the client, please look at the mcp logs of the respective client

  - [See this for Claude logs location](https://modelcontextprotocol.io/docs/tools/debugging#viewing-logs)

    - Example 🍎 Linux/macOS log path: `~/Library/Logs/Claude/mcp-server-Enkrypt Secure MCP Gateway.log`
    - Example 🪟 Windows log path: `%USERPROFILE%\AppData\Roaming\Claude\logs\mcp-server-Enkrypt Secure MCP Gateway.log`

  - [See this discussion for Cursor logs](https://forum.cursor.com/t/where-can-we-find-mcp-error-log/74719)

- If you see errors like `Exception: unhandled errors in a TaskGroup (1 sub-exception)` then maybe the MCP server the gateway is trying to use is not running.
  - So, please make sure the file it is trying to access is available
  - Any pre-requisites for the MCP server to run are met like `docker` running, etc.

- If we need more detailed logs, please set the `enkrypt_log_level` to `debug` in the `enkrypt_mcp_config.json` file and restart the MCP client.

### 15.1 OpenTelemetry Troubleshooting

1. **SSL Handshake Errors**

   If you see SSL errors like:

   ```bash
   SSL_ERROR_SSL: error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER
   ```

   Solution: Add `insecure=True` to the OTLP exporter configuration in `telemetry.py`

2. **No Logs in Loki**

   - Verify OTLP collector is running:

     ```bash
     docker logs secure-mcp-gateway-otel-collector-1
     ```

   - Check collector config in `otel_collector/otel-collector-config.yaml`

   - Verify Loki is receiving data:

     ```bash
     curl -G -s "http://localhost:3100/loki/api/v1/query" --data-urlencode 'query={job="enkrypt"}'
     ```

3. **Missing Metrics**

   - Check OTLP collector metrics pipeline:

     ```bash
     curl http://localhost:8888/metrics
     ```

   - Verify metrics in collector logs:

     ```bash
     docker logs secure-mcp-gateway-otel-collector-1 | grep "metrics"
     ```

4. **Docker Issues**

   ```bash
   # Restart the stack
   docker-compose down
   docker-compose up -d

   # Check individual service logs
   docker logs <service-name>
   ```

</details>

## 16. Known Issues being worked on

- Output guardrails are not being applied to non-text tool results. Support for other media types like images, audio, etc. is coming soon.

## 17. Known Limitations

- The Gateway does not support a scenario where the Gateway is deployed remotely but the MCP server is deployed locally (without being exposed to the internet). This is because the Gateway needs to know the MCP server's address to forward requests to it.

## 18. Contribute

We welcome contributions. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for how to submit changes and our [Contributor License Agreement (CLA)](CLA.md), which you agree to by submitting a pull request.

- Look at the `TODO` file for the current work in progress and yet to be implemented features

- Install the gateway locally to test your changes
  - by following the [Git clone steps](#42-local-installation-with-git-clone)
  - or build it using `python -m build`, activate the venv and install using `pip install .`

- Report or fix any bugs you encounter 😊

## 19. Testing

<details>
<summary><strong>🧪 Running Tests </strong></summary>

The gateway includes a comprehensive test suite that validates all core functionality including server discovery, tool execution, guardrails, caching, telemetry, and more.

### Prerequisites

- Gateway installed locally (follow [Local Installation](#42-local-installation-with-git-clone))
- Virtual environment activated
- Echo OAuth MCP server running (for testing remote server scenarios)

### Running the Test Suite

#### Step 1: Set Environment Variable

**Windows PowerShell:**

```powershell
$env:MCP_HTTP_MODE="true"
```

**Windows Command Prompt:**

```cmd
set MCP_HTTP_MODE=true
```

**macOS/Linux:**

```bash
export MCP_HTTP_MODE="true"
```

This environment variable enables the echo OAuth server to run in HTTP mode for testing.

#### Step 2: Start the Echo OAuth Server

Navigate to the echo server directory and start it:

**Windows PowerShell:**

```powershell
cd src\secure_mcp_gateway\bad_mcps
python .\echo_oauth_mcp.py
```

**macOS/Linux:**

```bash
cd src/secure_mcp_gateway/bad_mcps
python echo_oauth_mcp.py
```

The server will start on `http://localhost:8001/mcp/` and remain running. Keep this terminal open.

#### Step 3: Run the Test Suite

Open a new terminal, activate your virtual environment, and run the tests:

**Windows PowerShell:**

```powershell
# Activate virtual environment
.\.venv\Scripts\activate

# Navigate to tests directory
cd tests

# Run tests
python .\test_gateway.py
```

**macOS/Linux:**

```bash
# Activate virtual environment
source ./.venv/bin/activate

# Navigate to tests directory
cd tests

# Run tests
python test_gateway.py
```

### Test Coverage

The test suite includes:

- **Server Discovery Tests**: List servers, get server info, discover tools
- **Tool Execution Tests**: Call tools, multiple tool calls, error handling
- **Cache Tests**: Cache status, cache clearing, cache expiration
- **Guardrails Tests**: Input/output guardrails, async guardrails, PII redaction
- **Telemetry Tests**: OpenTelemetry integration, metrics, traces, logs
- **Configuration Tests**: Timeout settings, log levels, external cache
- **Integration Tests**: Full workflows, error recovery, performance

### Expected Output

The test runner will display:

- Progress for each test
- Success/failure status
- Execution duration
- Final summary with pass/fail counts

Example output:

```text
=== Gateway Tools Test Runner ===

Setting up test environment...
Setup complete.

Running Tests...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✅ test_list_all_servers_basic (0.45s)
✅ test_discover_all_tools_all_servers (1.23s)
✅ test_secure_call_tools_basic (0.89s)
...

=== Test Summary ===
Total Tests: 45
Passed: 45
Failed: 0
Success Rate: 100.0%
Total Duration: 45.67s
```

### Troubleshooting Tests

**Echo server connection errors:**

- Verify the echo OAuth server is running on port 8001
- Check that `MCP_HTTP_MODE` environment variable is set
- Ensure no other service is using port 8001

**Gateway configuration errors:**

- Verify `enkrypt_mcp_config.json` exists in `~/.enkrypt/` directory
- Check that the config file has valid gateway keys and server configurations
- Ensure virtual environment has all dependencies installed

**Test failures:**

- Enable debug logging by setting `enkrypt_log_level: "DEBUG"` in config
- Check MCP client logs for detailed error messages
- Verify all prerequisites are installed (Python 3.11+, pip, uv)

</details>

## 20. License

### 20.1 Enkrypt AI MCP Gateway Core

This project's core functionality is licensed under the Apache License, Version 2.0.

For the full license text, see the `LICENSE` file in this repository.

### 20.2 Enkrypt AI Guardrails, Logo, and Branding

© 2025 Enkrypt AI. All rights reserved.

Enkrypt AI software is provided under a proprietary license. Unauthorized use, reproduction, or distribution of this software or any portion of it is strictly prohibited.

Terms of Use: [https://www.enkryptai.com/terms-and-conditions](https://www.enkryptai.com/terms-and-conditions)

Privacy Policy: [https://app.enkryptai.com/privacy-policy](https://app.enkryptai.com/privacy-policy)

Enkrypt AI and the Enkrypt AI logo are trademarks of Enkrypt AI, Inc.

[Go to top](#enkrypt-ai-secure-mcp-gateway)
