Skip to main content

# MCP Connectors

MCP connectors are tools to connect to MCP servers. They provide full MCP protocol support including tools, resources, resource templates, and prompts.

You can find a list of pre-build MCP servers from this repository

We will add examples with pre-build entities from our repository

## Supported MCP capabilities​

MCP connectors support the full MCP protocol:

Capability | Description
--- | ---
Tools | Discover and call tools exposed by the MCP server
Resources | List and read resources (files, data, documents, etc.) from the MCP server. Resources are returned with `uri`, `name`, `description`, and `mimeType`. Contents can be text or binary (blob).
Resource Templates | List parameterized resource templates from the MCP server. Templates are returned with `uriTemplate`, `name`, `description`, and `mimeType`.
Prompts | List and retrieve prompt templates from the MCP server. Prompts include `name`, `description`, and their `arguments` (each with `name`, `description`, and `required`). Getting a prompt returns its `description` and `messages` (each with `role` and `content`).

All capabilities support filtering via include/exclude patterns and advanced rules. See the Filtering and Advanced rules sections below.

## Configuration​

Here is the `MCP connector` configuration :

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "mcp-connector_23d9b4db-1593-426e-b205-b5f331f78f1d",
      "name": "github-mcp",
      "description": "github-mcp",
      "metadata": {},
      "tags": [],
      "pool": {
        "size": 1
      },
      "transport": {
        "kind": "stdio",
        "options": {
          "command": "npx",
          "args": [
            "-y",
            "@modelcontextprotocol/server-github"
          ],
          "env": {
            "GITHUB_PERSONAL_ACCESS_TOKEN": "${vault://local/mcp-github-token}"
          }
        }
      },
      "strict": false,
      "kind": "ai-gateway.extensions.cloud-apim.com/McpConnector"
    }

Here is the `LLM Provider` configuration :

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "provider_480ec0b7-bc9e-487f-8376-b9b8111bfe5e",
      "name": "OpenAI provider",
      "description": "An OpenAI LLM api provider",
      "metadata": {},
      "tags": [],
      "provider": "openai",
      "connection": {
        "base_url": "https://api.openai.com/v1",
        "token": "${vault://local/openai-token}",
        "timeout": 30000
      },
      "options": {
        "model": "gpt-4o-mini",
        "frequency_penalty": null,
        "logit_bias": null,
        "logprobs": null,
        "top_logprobs": null,
        "max_tokens": null,
        "n": 1,
        "presence_penalty": null,
        "response_format": null,
        "seed": null,
        "stop": null,
        "stream": false,
        "temperature": 1,
        "top_p": 1,
        "tools": null,
        "tool_choice": null,
        "user": null,
        "wasm_tools": [],
        "mcp_connectors": [
          "mcp-connector_23d9b4db-1593-426e-b205-b5f331f78f1d"
        ],
        "allow_config_override": true
      },
      "provider_fallback": null,
      "context": {
        "default": null,
        "contexts": []
      },
      "models": {
        "include": [],
        "exclude": []
      },
      "guardrails": [],
      "guardrails_fail_on_deny": false,
      "cache": {
        "strategy": "none",
        "ttl": 300000,
        "score": 0.8
      },
      "kind": "ai-gateway.extensions.cloud-apim.com/Provider"
    }

Here is the `route` configuration :

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "route_79c1d15ae-5e64-482c-9a29-4b7dcad36089",
      "name": "mcp-openai",
      "description": "mcp-openai",
      "tags": [],
      "metadata": {},
      "enabled": true,
      "debug_flow": false,
      "export_reporting": false,
      "capture": false,
      "groups": [
        "default"
      ],
      "bound_listeners": [],
      "frontend": {
        "domains": [
          "mcp-openai.oto.tools"
        ],
        "strip_path": true,
        "exact": false,
        "headers": {},
        "query": {},
        "methods": []
      },
      "backend": {
        "targets": [
          {
            "id": "target_1",
            "hostname": "request.otoroshi.io",
            "port": 443,
            "tls": true,
            "weight": 1,
            "predicate": {
              "type": "AlwaysMatch"
            },
            "protocol": "HTTP/1.1",
            "ip_address": null,
            "tls_config": {
              "certs": [],
              "trusted_certs": [],
              "enabled": false,
              "loose": false,
              "trust_all": false
            }
          }
        ],
        "root": "/",
        "rewrite": false,
        "load_balancing": {
          "type": "RoundRobin"
        },
        "client": {
          "retries": 1,
          "max_errors": 20,
          "retry_initial_delay": 50,
          "backoff_factor": 2,
          "call_timeout": 30000,
          "call_and_stream_timeout": 120000,
          "connection_timeout": 10000,
          "idle_timeout": 60000,
          "global_timeout": 30000,
          "sample_interval": 2000,
          "proxy": {},
          "custom_timeouts": [],
          "cache_connection_settings": {
            "enabled": false,
            "queue_size": 2048
          }
        },
        "health_check": {
          "enabled": false,
          "url": "",
          "timeout": 5000,
          "healthyStatuses": [],
          "unhealthyStatuses": []
        }
      },
      "backend_ref": null,
      "plugins": [
        {
          "enabled": true,
          "debug": false,
          "plugin": "cp:otoroshi.next.plugins.OverrideHost",
          "include": [],
          "exclude": [],
          "config": {},
          "bound_listeners": [],
          "plugin_index": {
            "transform_request": 0
          },
          "nodeId": "cp:otoroshi.next.plugins.OverrideHost"
        },
        {
          "enabled": true,
          "debug": false,
          "plugin": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.OpenAiCompatProxy",
          "include": [],
          "exclude": [],
          "config": {
            "refs": [
              "provider_480ec0b7-bc9e-487f-8376-b9b8111bfe5e"
            ]
          },
          "bound_listeners": [],
          "plugin_index": {},
          "nodeId": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.OpenAiCompatProxy"
        }
      ],
      "kind": "proxy.otoroshi.io/Route"
    }

## Default config​

    {
      "command": "node",
      "args": [
        "/foo/bar/server.js"
      ],
      "env": {
        "TOKEN": "secret"
      }
    }

## Example with Filesystem MCP Server​

### Filesystem MCP Connector configuration​

    {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/your-user/Desktop"
      ]
    }

## Example with Github MCP Server​

To use the Github MCP server you will need to generate a Github access token.

Then, we will put the token into our local vault.

Go to your Otoroshi danger zone and go to `Global Metadata` section.

Right after, you can create a new KEY, VALUE pair like this example (in the Otoroshi environment) :

    {
      "mcp-github-token": "YOUR_GITHUB_TOKEN_HERE"
    }

### Github MCP Connector configuration​

    {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-github"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${vault://local/mcp-github-token}"
      }
    }

Now you can use Github MCP server's functions to Create issues, list all issues, list pull requests and much more with your favorite LLMs !

## Filtering tools, resources and prompts​

MCP connectors expose tools, resources, resource templates, and prompts from the MCP server. You can filter which ones are available using include/exclude patterns. All patterns support regex matching.

### Filtering tools (functions)​

Use `include_functions` and `exclude_functions` to control which tools are exposed:

    {
      "id": "mcp-connector_xxxxx",
      "name": "filtered-mcp",
      "transport": { ... },
      "include_functions": ["get_.*", "list_.*"],
      "exclude_functions": ["delete_.*", "drop_.*"]
    }

### Filtering resources​

Use `include_resources` and `exclude_resources` to control which resources are exposed:

    {
      "include_resources": ["users", "projects"],
      "exclude_resources": ["admin_.*"]
    }

### Filtering resource templates​

Use `include_resource_templates` and `exclude_resource_templates` to filter resource templates by name, and `include_resource_template_uris` / `exclude_resource_template_uris` to filter by URI template:

    {
      "include_resource_templates": ["user_template"],
      "exclude_resource_templates": [],
      "include_resource_template_uris": ["file://.*"],
      "exclude_resource_template_uris": ["file:///etc/.*"]
    }

### Filtering prompts​

Use `include_prompts` and `exclude_prompts` to control which prompts are exposed:

    {
      "include_prompts": ["summarize", "translate"],
      "exclude_prompts": ["debug_.*"]
    }

### Complete filtering example​

    {
      "id": "mcp-connector_xxxxx",
      "name": "filtered-github-mcp",
      "transport": {
        "kind": "stdio",
        "options": {
          "command": "npx",
          "args": ["-y", "@modelcontextprotocol/server-github"],
          "env": {
            "GITHUB_PERSONAL_ACCESS_TOKEN": "${vault://local/mcp-github-token}"
          }
        }
      },
      "include_functions": ["list_.*", "get_.*", "search_.*"],
      "exclude_functions": ["delete_.*"],
      "include_resources": [],
      "exclude_resources": [],
      "include_prompts": [],
      "exclude_prompts": []
    }

## Advanced rules​

Beyond simple include/exclude patterns, MCP connectors support advanced rules based on JsonPath validation. Rules allow you to control access to tools, resources, resource templates, and prompts based on the request context (consumer identity, apikey metadata, request details, etc.).

Rules are organized by category and can target specific items by name or all items using the `*` wildcard.

### Rule structure​

    {
      "allow_rules": {
        "tool_rules": {
          "sensitive_tool": [
            { "path": "$.apikey.metadata.role", "value": "admin" }
          ],
          "*": [
            { "path": "$.apikey.enabled", "value": true }
          ]
        },
        "prompt_rules": { ... },
        "resource_rules": { ... },
        "resource_templates_rules": { ... }
      },
      "disallow_rules": {
        "tool_rules": {
          "dangerous_tool": [
            { "path": "$.apikey.metadata.env", "value": "production" }
          ]
        },
        "prompt_rules": { ... },
        "resource_rules": { ... },
        "resource_templates_rules": { ... }
      }
    }

### How rules work​

1. Disallow rules are evaluated first: if a disallow rule matches, the item is blocked
2. Allow rules are evaluated next: if an allow rule is defined for the item, it must match for the item to be accessible
3. If no rule is defined for an item, it is accessible by default

Rules use Otoroshi's `JsonPathValidator` format to validate against the request context, which includes consumer identity, apikey metadata, request headers, and more.

### Example: restrict tools by consumer role​

    {
      "allow_rules": {
        "tool_rules": {
          "create_repository": [
            { "path": "$.apikey.metadata.github_role", "value": "maintainer" }
          ],
          "delete_repository": [
            { "path": "$.apikey.metadata.github_role", "value": "admin" }
          ]
        }
      },
      "disallow_rules": {
        "tool_rules": {
          "delete_repository": [
            { "path": "$.apikey.metadata.env", "value": "production" }
          ]
        }
      }
    }

In this example:

- `create_repository` is only available to consumers with `github_role: maintainer` in their apikey metadata
- `delete_repository` is only available to consumers with `github_role: admin`, and is always blocked in production environments

## Authentication passthrough​

MCP connectors can forward the consumer's authentication token to the MCP server. This is useful when the MCP server needs to authenticate the consumer or apply its own authorization logic.

In the transport headers configuration, use the `{input_token}` placeholder to inject the consumer's Bearer token:

    {
      "transport": {
        "kind": "sse",
        "options": {
          "url": "https://my-mcp-server.example.com/sse",
          "headers": {
            "Authorization": "Bearer {input_token}"
          }
        }
      }
    }

The `{input_token}` placeholder is replaced at runtime with the Bearer token from the incoming request's `Authorization` header. This works with SSE, WebSocket, and Streamable HTTP transports.

## Postgres MCP Server​

User prompt to the LLM

    {
      "messages": [
        {
           "role": "user",
           "content": "Could you list me all the users from the 'users' table please ?"
        }
      ]
    }

Curl request

    curl --request POST \
      --url http://mcp-pg-openai.oto.tools:8080/ \
      --header 'content-type: application/json' \
      --data '{
      "messages": [
        {
           "role": "user",
           "content": "Could you list me all the users from the '\''users'\'' table please ?"
        }
      ]
    }'

LLM response :

    {
      "id": "chatcmpl-3WFxjlu6MWpvw4ZaOGtyc4OqvFycGDP4",
      "object": "chat.completion",
      "created": 1743155603,
      "model": "gpt-4o-mini",
      "system_fingerprint": "fp-RvGvrs3QlRhPqmXM7NqGd61qeOPudtEZ",
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "Here are all the users from the 'users' table:\n\n1. **Alice Johnson**\n   - Email: alice@example.com\n   - Age: 30\n   - Address: 123 Main St, Springfield\n   - Phone: 123-456-7890\n   - Created At: 2025-03-28T09:47:34.121Z\n\n2. **Bob Smith**\n   - Email: bob@example.com\n   - Age: 25\n   - Address: 456 Elm St, Shelbyville\n   - Phone: 987-654-3210\n   - Created At: 2025-03-28T09:47:34.121Z\n\n3. **Charlie Brown**\n   - Email: charlie@example.com\n   - Age: 35\n   - Address: 789 Oak St, Capital City\n   - Phone: 555-123-4567\n   - Created At: 2025-03-28T09:47:34.121Z"
          },
          "logprobs": null,
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 342,
        "completion_tokens": 214,
        "total_tokens": 556,
        "completion_tokens_details": {
          "reasoning_tokens": 0
        }
      }
    }

#### Enabling no personal information guardrail​

Using the No personal information guardrail will block all the requests mentioning personal information from the prompt to the LLM or from the response.

Warning : To enable the guardrail, you will need to create another provider that will be used to apply the guardrail rule :

Response from the LLM :

    {
      "id": "chatcmpl-5iTwZroMP6B3qKEWqfHLbvcVsQhucOHE",
      "object": "chat.completion",
      "created": 1743177176,
      "model": "gpt-4o-mini",
      "system_fingerprint": "fp-1HS2LIBA0cK0N8rBVi30YVUbayejNDAo",
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "This message has been blocked by the 'personal-information' guardrail !"
          },
          "logprobs": null,
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 0,
        "completion_tokens": 0,
        "total_tokens": 0,
        "completion_tokens_details": {
          "reasoning_tokens": 0
        }
      }
    }

Full route configuration with guardrail :

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "provider_6bfe8012-1ccb-4884-bef2-00e307862e7e",
      "name": "OpenAI provider + Postgres MCP",
      "description": "OpenAI provider + Postgres MCP",
      "metadata": {},
      "tags": [],
      "provider": "openai",
      "connection": {
        "base_url": "https://api.openai.com/v1",
        "token": "${vault://local/openai-token}",
        "timeout": 30000
      },
      "options": {
        "model": "gpt-4o-mini",
        "frequency_penalty": null,
        "logit_bias": null,
        "logprobs": null,
        "top_logprobs": null,
        "max_tokens": null,
        "n": 1,
        "presence_penalty": null,
        "response_format": null,
        "seed": null,
        "stop": null,
        "stream": false,
        "temperature": 1,
        "top_p": 1,
        "tools": null,
        "tool_choice": null,
        "user": null,
        "wasm_tools": [],
        "mcp_connectors": [
          "mcp-connector_c0451105-afd5-4ae5-9258-7cf96ef22cbf"
        ],
        "allow_config_override": true
      },
      "provider_fallback": null,
      "context": {
        "default": null,
        "contexts": []
      },
      "models": {
        "include": [],
        "exclude": []
      },
      "guardrails": [
        {
          "enabled": true,
          "before": true,
          "after": true,
          "id": "pif",
          "config": {
            "provider": "provider_fc0b30ef-35e2-4a5d-8286-8b0e24f0b028",
            "pif_items": [
              "EMAIL_ADDRESS",
              "PHONE_NUMBER",
              "LOCATION_ADDRESS",
              "NAME",
              "IP_ADDRESS",
              "CREDIT_CARD",
              "SSN"
            ]
          }
        }
      ],
      "guardrails_fail_on_deny": false,
      "cache": {
        "strategy": "none",
        "ttl": 300000,
        "score": 0.8
      },
      "kind": "ai-gateway.extensions.cloud-apim.com/Provider"
    }