Skip to main content

Overview

Gateway provides a runtime API for Model Context Protocol (MCP) connections with:
  • Discovery - Automatic tool discovery from MCP servers with caching
  • Filtering - Connection-level and per-subject tool policies (allow/deny lists)
  • Execution - Validated tool calls with circuit breaker protection
  • Explain - Policy decision transparency for tool access
MCP connections use protocol: "mcp" and communicate via streamable HTTP transport.

MCP Session Lifecycle

Gateway maintains explicit MCP session states:
  • initialize_required - New connection, needs MCP initialize call
  • ready - Session active, tools available
  • reinitialize_pending - Temporary failure, attempting recovery

Discovery Cache Policy

Gateway caches MCP discovery results with TTL and stale-if-error fallback:
GATEWAY_MCP_DISCOVERY_CACHE_TTL_SECONDS=300        # Fresh cache window
GATEWAY_MCP_DISCOVERY_STALE_IF_ERROR_SECONDS=3600  # Stale fallback window
Cache Behavior:
  1. Fresh (within TTL) - Return cached results immediately
  2. Stale (past TTL, within stale-if-error) - Attempt refresh, fall back to stale on error
  3. Expired (past stale-if-error) - Force refresh, fail if unavailable

Circuit Breaker

Gateway opens a per-connection circuit breaker after repeated upstream failures:
GATEWAY_MCP_CIRCUIT_BREAKER_FAILURES=3          # Failures before opening
GATEWAY_MCP_CIRCUIT_BREAKER_COOLDOWN_SECONDS=10 # Fail-fast duration
When open, all requests fail immediately with 503 Service Unavailable until cooldown elapses.

List MCP Tools

connection_id
string
required
MCP connection identifier (must have protocol: "mcp")
refresh
string
default:"auto"
Discovery refresh mode:
  • auto - Use cache policy (default)
  • force - Bypass cache and refresh from upstream

Required Headers

signature-input
string
required
RFC 9421 Signature-Input header (same as proxy endpoints)
signature
string
required
RFC 9421 Signature header
sigilum-namespace
string
required
Namespace identifier
sigilum-subject
string
required
Subject identifier for tool policy filtering
sigilum-agent-key
string
required
Public key of signing agent
sigilum-agent-cert
string
required
Agent certificate

Response

tools
array
required
List of MCP tools filtered by subject policy
server
object
MCP server metadata
last_discovered_at
string
RFC3339 timestamp of last successful discovery

Example Request

curl -X GET 'http://localhost:38100/mcp/linear-mcp/tools?refresh=auto' \
  -H 'signature-input: sig1=("@method" "@path" "@authority" "sigilum-namespace" "sigilum-subject" "sigilum-agent-key" "sigilum-nonce");created=1709550000;keyid="key-abc";nonce="nonce_xyz"' \
  -H 'signature: sig1=:MEUCIA...:' \
  -H 'sigilum-namespace: acme-corp' \
  -H 'sigilum-subject: user_alice' \
  -H 'sigilum-agent-key: MFkwEwYHK...' \
  -H 'sigilum-agent-cert: MIIBkTC...' \
  -H 'sigilum-nonce: nonce_xyz'

Example Response

{
  "tools": [
    {
      "name": "linear.searchIssues",
      "description": "Search Linear issues by text query",
      "input_schema": "{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"]}"
    },
    {
      "name": "linear.getIssue",
      "description": "Get a specific Linear issue by ID",
      "input_schema": "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\"}},\"required\":[\"id\"]}"
    }
  ],
  "server": {
    "name": "linear-mcp-server",
    "version": "1.0.0",
    "protocol_version": "2024-11-05"
  },
  "last_discovered_at": "2026-03-04T10:25:00Z"
}
Tools are filtered by connection-level and subject-level policies before returning. Denied tools are excluded from the response.

Explain Tool Policy

connection_id
string
required
MCP connection identifier
tool
string
required
Tool name (e.g., linear.searchIssues)
refresh
string
default:"auto"
Discovery refresh mode (auto or force)

Response

tool
string
required
Tool name being explained
allowed
boolean
required
Whether the tool is allowed for the subject
policy_source
string
required
Policy decision source:
  • connection_allowlist - Connection-level allowlist
  • connection_denylist - Connection-level denylist
  • subject_allowlist - Subject-specific allowlist
  • subject_denylist - Subject-specific denylist
  • default_allow - No explicit policy, default allow
subject
string
Subject identifier from request

Example Request

curl -X GET 'http://localhost:38100/mcp/linear-mcp/tools/linear.createIssue/explain' \
  -H 'signature-input: ...' \
  -H 'signature: ...' \
  -H 'sigilum-namespace: acme-corp' \
  -H 'sigilum-subject: user_alice' \
  -H 'sigilum-agent-key: ...' \
  -H 'sigilum-agent-cert: ...'

Example Response (Denied)

{
  "tool": "linear.createIssue",
  "allowed": false,
  "policy_source": "connection_denylist",
  "subject": "user_alice"
}

Example Response (Allowed)

{
  "tool": "linear.searchIssues",
  "allowed": true,
  "policy_source": "connection_allowlist",
  "subject": "user_alice"
}
Use /explain to debug tool access issues before attempting execution. This helps understand why a tool may be missing from /tools list.

Call MCP Tool

connection_id
string
required
MCP connection identifier
tool
string
required
Tool name to execute (must be in allowlist)
refresh
string
default:"auto"
Discovery refresh mode (auto or force)

Request Body

arguments
object
Tool-specific input arguments (validated against tool’s input_schema)

Response

content
array
required
Tool execution results as MCP content blocks
isError
boolean
Whether the tool execution failed

Example Request

curl -X POST 'http://localhost:38100/mcp/linear-mcp/tools/linear.searchIssues/call' \
  -H 'Content-Type: application/json' \
  -H 'signature-input: sig1=("@method" "@path" "@authority" "content-digest" "sigilum-namespace" "sigilum-subject" "sigilum-agent-key" "sigilum-nonce");created=1709550000;keyid="key-abc";nonce="nonce_xyz2"' \
  -H 'signature: sig1=:MEUCIA...:' \
  -H 'sigilum-namespace: acme-corp' \
  -H 'sigilum-subject: user_alice' \
  -H 'sigilum-agent-key: MFkwEwYHK...' \
  -H 'sigilum-agent-cert: MIIBkTC...' \
  -H 'sigilum-nonce: nonce_xyz2' \
  -H 'content-digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:' \
  -d '{
    "arguments": {
      "query": "bug in authentication"
    }
  }'

Example Response (Success)

{
  "content": [
    {
      "type": "text",
      "text": "Found 3 issues:\n1. AUTH-123: Login fails with invalid token\n2. AUTH-456: Session timeout not enforced\n3. AUTH-789: Password reset email broken"
    }
  ],
  "isError": false
}

Example Response (Tool Error)

{
  "content": [
    {
      "type": "text",
      "text": "Linear API error: Invalid query syntax"
    }
  ],
  "isError": true
}

Example Response (Tool Denied)

{
  "error": "tool not allowed for subject",
  "code": "MCP_TOOL_DENIED",
  "request_id": "req_abc123",
  "timestamp": "2026-03-04T10:40:00Z"
}

Tool Filtering Policies

Gateway applies multi-level tool filtering:

1. Connection-Level Policy

Applies to all subjects using the connection:
{
  "id": "linear-mcp",
  "protocol": "mcp",
  "mcp_tool_policy": {
    "allowlist": ["linear.searchIssues", "linear.getIssue"],
    "denylist": ["linear.deleteIssue"],
    "max_tools_exposed": 10
  }
}
allowlist
array<string>
Explicit tool names to allow (if present, only these tools are exposed)
denylist
array<string>
Explicit tool names to deny (takes precedence over allowlist)
max_tools_exposed
integer
Maximum number of tools to expose (additional tools truncated)

2. Subject-Level Policy

Overrides connection policy for specific subjects:
{
  "id": "linear-mcp",
  "protocol": "mcp",
  "mcp_subject_tool_policies": {
    "user_alice": {
      "allowlist": ["linear.searchIssues"],
      "denylist": ["linear.createIssue", "linear.updateIssue"]
    },
    "admin_bob": {
      "allowlist": ["linear.*"]
    }
  }
}
Subject policies are keyed by sigilum-subject header value. Wildcard patterns are supported in allowlist/denylist.

Policy Evaluation Order

  1. Check subject-level denylist → deny if matched
  2. Check subject-level allowlist → allow if matched, deny if allowlist present but not matched
  3. Check connection-level denylist → deny if matched
  4. Check connection-level allowlist → allow if matched, deny if allowlist present but not matched
  5. Default → allow

Rate Limiting

MCP tool calls are rate-limited per connection + namespace:
GATEWAY_MCP_TOOL_CALL_RATE_LIMIT_PER_MINUTE=120  # Set 0 to disable
Response (429 Too Many Requests):
{
  "error": "tool call rate limit exceeded",
  "code": "MCP_TOOL_CALL_RATE_LIMITED",
  "request_id": "req_xyz789",
  "timestamp": "2026-03-04T10:45:00Z"
}

Retry Behavior

Gateway retries MCP requests only for retryable conditions: Retryable:
  • Network timeouts
  • HTTP 429 (Too Many Requests)
  • HTTP 502, 503, 504 (Temporary upstream failures)
Non-Retryable:
  • HTTP 4xx (except 429)
  • Tool policy denials
  • Invalid request formats
Retries use bounded exponential backoff with jitter.

MCP Connection Configuration

HTTP MCP Connection

{
  "id": "linear-mcp",
  "name": "Linear MCP",
  "protocol": "mcp",
  "base_url": "https://mcp.linear.app",
  "mcp_transport": "streamable_http",
  "mcp_endpoint": "/mcp",
  "auth_mode": "bearer",
  "auth_header_name": "Authorization",
  "auth_prefix": "Bearer ",
  "auth_secret_key": "api_key",
  "secrets": {
    "api_key": "lin_api_abc123xyz789"
  },
  "mcp_tool_policy": {
    "allowlist": ["linear.searchIssues", "linear.getIssue"],
    "denylist": ["linear.deleteIssue"]
  }
}

MCP with Query Parameter Auth

{
  "id": "typefully-mcp",
  "name": "Typefully MCP",
  "protocol": "mcp",
  "base_url": "https://mcp.typefully.com",
  "mcp_transport": "streamable_http",
  "mcp_endpoint": "https://mcp.typefully.com/mcp?TYPEFULLY_API_KEY={{__API_KEY__}}",
  "auth_mode": "query_param",
  "auth_header_name": "TYPEFULLY_API_KEY",
  "auth_secret_key": "__API_KEY__",
  "secrets": {
    "__API_KEY__": "tf_secret_xyz789"
  }
}
MCP connections support optional auth secrets. Only configure auth_mode and secrets when the upstream MCP server requires credentials.

Discovery Workflow

To set up a new MCP connection:
  1. Create Connection via POST /api/admin/connections with protocol: "mcp"
  2. Run Discovery via POST /api/admin/connections/{id}/discover?refresh=force
  3. Review Tools in mcp_discovery.tools field
  4. Configure Policies via PATCH /api/admin/connections/{id} to set allowlist/denylist
  5. Test Runtime via GET /mcp/{id}/tools with signed request
See Admin Endpoints for discovery API details.

Error Scenarios

Circuit Breaker Open

{
  "error": "mcp circuit breaker open",
  "code": "MCP_CIRCUIT_OPEN",
  "request_id": "req_circuit123",
  "timestamp": "2026-03-04T10:50:00Z"
}

Discovery Cache Unavailable

{
  "error": "mcp discovery cache expired and refresh failed",
  "code": "MCP_DISCOVERY_UNAVAILABLE",
  "request_id": "req_cache456",
  "timestamp": "2026-03-04T10:51:00Z"
}

Invalid Tool Arguments

{
  "error": "tool arguments do not match input schema",
  "code": "MCP_INVALID_ARGUMENTS",
  "request_id": "req_args789",
  "timestamp": "2026-03-04T10:52:00Z"
}

Next Steps

Admin Endpoints

Learn how to create and discover MCP connections

Proxy Endpoints

Explore HTTP proxy functionality

Build docs developers (and LLMs) love