Skip to main content

# MCP server exposition

Otoroshi can expose tool functions and MCP connectors as MCP servers using three different transport protocols: HTTP, SSE, and WebSocket. This allows external MCP clients to connect to your Otoroshi-managed tools.

## Supported MCP methods​

All three transport plugins (HTTP, SSE, WebSocket) implement the full MCP protocol:

Method | Description
--- | ---
`initialize` | Initialize the MCP session
`tools/list` | List all available tools (from tool functions and MCP connectors)
`tools/call` | Call a tool by name with arguments
`resources/list` | List all available resources from connected MCP servers
`resources/read` | Read a resource by its URI. Returns text or binary content.
`resources/templates/list` | List all available resource templates from connected MCP servers
`prompts/list` | List all available prompts with their arguments
`prompts/get` | Get a prompt by name with arguments. Returns the prompt description and messages (each with role and content).

### Resources​

When listing resources, each resource includes:

- `uri` - The resource URI
- `name` - The resource name
- `description` - A description of the resource
- `mimeType` - The MIME type of the resource

When reading a resource, the content is returned as either text (with `uri`, `mimeType`, `text`) or binary blob (with `uri`, `mimeType`, `blob`).

### Resource Templates​

When listing resource templates, each template includes:

- `uriTemplate` - The URI template with parameters
- `name` - The template name
- `description` - A description of the template
- `mimeType` - The MIME type of resources generated by this template

### Prompts​

When listing prompts, each prompt includes:

- `name` - The prompt name
- `description` - A description of the prompt
- `arguments` - An array of arguments, each with `name`, `description`, and `required`

When getting a prompt, the response includes:

- `description` - The prompt description
- `messages` - An array of messages, each with `role` (e.g. `user`, `assistant`) and `content` (text or image)

## Filtering at the plugin level​

All MCP server exposition plugins (HTTP, SSE, WebSocket) support filtering which tools, resources, resource templates, and prompts are exposed to clients. These filters are applied on top of any filters already configured on the MCP connectors themselves.

### Available filters​

Parameter | Type | Description
--- | --- | ---
`include_functions` | array of strings | Only expose functions matching these regex patterns
`exclude_functions` | array of strings | Hide functions matching these regex patterns
`include_resources` | array of strings | Only expose resources matching these patterns
`exclude_resources` | array of strings | Hide resources matching these patterns
`include_resource_templates` | array of strings | Only expose resource templates matching these patterns
`exclude_resource_templates` | array of strings | Hide resource templates matching these patterns
`include_resource_template_uris` | array of strings | Only expose resource template URIs matching these patterns
`exclude_resource_template_uris` | array of strings | Hide resource template URIs matching these patterns
`include_prompts` | array of strings | Only expose prompts matching these patterns
`exclude_prompts` | array of strings | Hide prompts matching these patterns
`allow_rules` | object | Advanced JsonPath-based allow rules (see MCP Connectors - Advanced rules)
`disallow_rules` | object | Advanced JsonPath-based disallow rules
`emit_audit_events` | boolean | Emit audit events for every MCP method call (default: `false`)

### Example plugin configuration with filters​

    {
      "name": "filtered-mcp-server",
      "version": "1.0.0",
      "refs": ["tool-function_xxxxx"],
      "mcp_refs": ["mcp-connector_xxxxx"],
      "include_functions": ["get_.*", "list_.*"],
      "exclude_functions": ["delete_.*"],
      "include_prompts": ["summarize"],
      "exclude_prompts": [],
      "include_resources": [],
      "exclude_resources": ["admin_.*"]
    }

These plugin-level filters are merged with the filters defined on each MCP connector. This means you can have broad access on the connector itself and restrict it further at the exposition level, or vice versa.

## Audit events​

All MCP server exposition plugins (HTTP, SSE, WebSocket) can emit audit events for every MCP method call. This is useful for monitoring, debugging, and auditing MCP usage across your infrastructure.

### Enabling audit events​

Set `emit_audit_events` to `true` in the plugin configuration:

    {
      "name": "my-mcp-server",
      "version": "1.0.0",
      "refs": ["tool-function_xxxxx"],
      "mcp_refs": ["mcp-connector_xxxxx"],
      "emit_audit_events": true
    }

By default, this option is disabled (`false`).

### Audit event payload​

Each MCP method call will produce an `AuditEvent` with the audit type `McpAudit`. The event contains the following fields:

Field | Type | Description
--- | --- | ---
`mcp_method` | string | The MCP JSON-RPC method called (e.g. `tools/call`, `resources/list`, `initialize`)
`mcp_id` | number | The JSON-RPC request id
`mcp_request_payload` | object | The full JSON-RPC request payload
`mcp_response` | object | The JSON-RPC response (available for WebSocket transport, `null` for HTTP and SSE)
`transport` | string | The transport protocol used: `http`, `sse`, or `websocket`
`duration` | number | The request duration in milliseconds
`status` | string | `success` or `error`
`error` | string | Error message if the request failed, `null` otherwise
`user` | object | The authenticated user information (if available)
`apikey` | object | The API key used for the request (if available)
`route` | object | The Otoroshi route that handled the request

### Example audit event​

    {
      "@type": "AuditEvent",
      "audit": "McpAudit",
      "mcp_method": "tools/call",
      "mcp_id": 3,
      "mcp_request_payload": {
        "jsonrpc": "2.0",
        "id": 3,
        "method": "tools/call",
        "params": {
          "name": "get_weather",
          "arguments": { "city": "Paris" }
        }
      },
      "mcp_response": null,
      "transport": "http",
      "duration": 142,
      "status": "success",
      "error": null,
      "user": null,
      "apikey": { "clientId": "xxx", "clientName": "my-api-key" },
      "route": { "id": "route_xxx", "name": "mcp-server" }
    }

### Filtering audit events in data exporters​

You can use Otoroshi's data exporter filtering to collect only MCP audit events:

    {
      "include": [
        {
          "@type": "AuditEvent",
          "audit": "McpAudit"
        }
      ]
    }

## MCP Http Plugin​

### Configuration​

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "route_2629d12c2-130b-42d5-aba5-b584b95961c9",
      "name": "MCP HTTP server exposition",
      "description": "MCP HTTP server exposition",
      "tags": [],
      "metadata": {},
      "enabled": true,
      "debug_flow": false,
      "export_reporting": false,
      "capture": false,
      "groups": [
        "default"
      ],
      "bound_listeners": [],
      "frontend": {
        "domains": [
          "mcp-http-expo.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.McpRespEndpoint",
          "include": [],
          "exclude": [],
          "config": {
            "name": "first-mcp-http-server",
            "version": "1.0.0",
            "refs": [
              "tool-function_0511ff44-0ce5-487d-aa1c-d1980ac9a4b8"
            ],
            "mcp_refs": []
          },
          "bound_listeners": [],
          "plugin_index": {},
          "nodeId": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.McpRespEndpoint"
        }
      ],
      "kind": "proxy.otoroshi.io/Route"
    }

## MCP SSE Plugin​

### Configuration​

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "route_73e79129a-1cc6-4473-970a-b80905e80c7b",
      "name": "mcp-sse-exposition",
      "description": "mcp-sse-exposition",
      "tags": [],
      "metadata": {},
      "enabled": true,
      "debug_flow": false,
      "export_reporting": false,
      "capture": false,
      "groups": [
        "default"
      ],
      "bound_listeners": [],
      "frontend": {
        "domains": [
          "mcp-sse-exposition.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": [],
          "nodeId": "cp:otoroshi.next.plugins.OverrideHost",
          "plugin_index": {
            "transform_request": 0
          }
        },
        {
          "plugin_index": {},
          "nodeId": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.McpSseEndpoint-0",
          "plugin": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.McpSseEndpoint",
          "enabled": true,
          "debug": false,
          "include": [],
          "exclude": [],
          "bound_listeners": [],
          "config": {
            "name": "mcp-sse",
            "version": "1.0.0",
            "refs": [
              "tool-function_0511ff44-0ce5-487d-aa1c-d1980ac9a4b8"
            ],
            "mcp_refs": []
          }
        }
      ],
      "kind": "proxy.otoroshi.io/Route"
    }

## MCP WebSocket Plugin​

### Configuration​

    {
      "_loc": {
        "tenant": "default",
        "teams": [
          "default"
        ]
      },
      "id": "route_62c427cac-43c9-4d86-bab4-56cb0b61040b",
      "name": "mcp-websocket-exposition",
      "description": "mcp-websocket-exposition",
      "tags": [],
      "metadata": {},
      "enabled": true,
      "debug_flow": false,
      "export_reporting": false,
      "capture": false,
      "groups": [
        "default"
      ],
      "bound_listeners": [],
      "frontend": {
        "domains": [
          "mcp-websocket-exposition.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": [],
          "nodeId": "cp:otoroshi.next.plugins.OverrideHost",
          "plugin_index": {
            "transform_request": 0
          }
        },
        {
          "plugin_index": {},
          "nodeId": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.McpWebsocketEndpoint-0",
          "plugin": "cp:otoroshi_plugins.com.cloud.apim.otoroshi.extensions.aigateway.plugins.McpWebsocketEndpoint",
          "enabled": true,
          "debug": false,
          "include": [],
          "exclude": [],
          "bound_listeners": [],
          "config": {
            "name": "mcp-server",
            "version": "1.0.0",
            "refs": [
              "tool-function_0511ff44-0ce5-487d-aa1c-d1980ac9a4b8"
            ],
            "mcp_refs": []
          }
        }
      ],
      "kind": "proxy.otoroshi.io/Route"
    }