Skip to content

MCP Servers

HolmesGPT can integrate with MCP (Model Context Protocol) servers to access external data sources and tools in real time.

Transport Modes

HolmesGPT supports three MCP transport modes:

  1. streamable-http (Recommended): Modern HTTP-based transport. Use this for new integrations.
  2. stdio: Direct process communication via standard input/output. Supported directly in CLI; supported on Kubernetes via Supergateway.
  3. sse (Deprecated): Legacy Server-Sent Events transport. Use streamable-http instead.

Set the environment variable:

export DYNATRACE_API_KEY=<YOUR_DYNATRACE_API_KEY>

Add the following to ~/.holmes/config.yaml. Create the file if it doesn't exist:

mcp_servers:
  dynatrace:
    description: "Dynatrace observability platform"
    config:
      url: "http://dynatrace-mcp:8000/mcp/messages"
      mode: streamable-http
      headers:
        Authorization: "Bearer {{ env.DYNATRACE_API_KEY }}"
      icon_url: "https://cdn.simpleicons.org/dynatrace/1496FF"  # Optional: icon for UI
    # llm_instructions tells Holmes WHEN and HOW to use this server
    llm_instructions: "Use Dynatrace to investigate application performance issues, analyze distributed traces, and query infrastructure metrics. Prefer this over Prometheus for APM data."

After making changes to your configuration, run:

holmes toolset refresh

To test, run:

holmes ask "What services have high error rates in Dynatrace?"

Create a Kubernetes secret in the namespace Holmes runs in:

kubectl create secret generic holmes-remote-mcp-servers \
  --from-literal=DYNATRACE_API_KEY=<YOUR_DYNATRACE_API_KEY> \
  -n <namespace>

When using the standalone Holmes Helm Chart, update your values.yaml:

extraEnvVarsSecrets:
  - holmes-remote-mcp-servers

mcp_servers:
  dynatrace:
    description: "Dynatrace observability platform"
    config:
      url: "http://dynatrace-mcp:8000/mcp/messages"
      mode: streamable-http
      headers:
        Authorization: "Bearer {{ env.DYNATRACE_API_KEY }}"
      icon_url: "https://cdn.simpleicons.org/dynatrace/1496FF"  # Optional: icon for UI
    # llm_instructions tells Holmes WHEN and HOW to use this server
    llm_instructions: "Use Dynatrace to investigate application performance issues, analyze distributed traces, and query infrastructure metrics. Prefer this over Prometheus for APM data."

Apply the configuration:

helm upgrade holmes robusta/holmes -f values.yaml

Create a Kubernetes secret in the namespace Holmes runs in:

kubectl create secret generic holmes-remote-mcp-servers \
  --from-literal=DYNATRACE_API_KEY=<YOUR_DYNATRACE_API_KEY> \
  -n <namespace>

When using the Robusta Helm Chart (which includes HolmesGPT), update your generated_values.yaml:

holmes:
  extraEnvVarsSecrets:
    - holmes-remote-mcp-servers

  mcp_servers:
    dynatrace:
      description: "Dynatrace observability platform"
      config:
        url: "http://dynatrace-mcp:8000/mcp/messages"
        mode: streamable-http
        headers:
          Authorization: "Bearer {{ env.DYNATRACE_API_KEY }}"
        icon_url: "https://cdn.simpleicons.org/dynatrace/1496FF"  # Optional: icon for UI
      # llm_instructions tells Holmes WHEN and HOW to use this server
      llm_instructions: "Use Dynatrace to investigate application performance issues, analyze distributed traces, and query infrastructure metrics. Prefer this over Prometheus for APM data."

Apply the configuration:

helm upgrade robusta robusta/robusta -f generated_values.yaml --set clusterName=<YOUR_CLUSTER_NAME>

The URL path depends on your MCP server (e.g., /mcp/messages, /mcp, or a custom path). Check your server's documentation.

Stdio

Stdio mode runs MCP servers as subprocesses, communicating via standard input/output.

Stdio requires Supergateway for Kubernetes

In Kubernetes, stdio mode cannot run directly in the Holmes container due to missing dependencies. Run your stdio MCP server in a separate pod using Supergateway to expose it as HTTP.

Step 1: Create a Docker image with your MCP server

CLI users can skip this step and the next: the CLI runs the server as a subprocess.

FROM supercorp/supergateway:latest

USER root
# Install your MCP server dependencies
# Example: RUN apk add --no-cache python3 py3-pip
# Example: RUN pip3 install --no-cache-dir --break-system-packages your-mcp-package
USER node

EXPOSE 8000
# Replace with your MCP server command. Examples:
#   CMD ["--port", "8000", "--stdio", "python3", "-m", "your_mcp_module"]
#   CMD ["--port", "8000", "--stdio", "python3", "/app/stdio_server.py"]
#   CMD ["--port", "8000", "--stdio", "npx", "-y", "@your-org/your-mcp-server@latest"]
CMD ["--port", "8000", "--stdio", "python3", "-m", "your_mcp_module"]

Step 2: Deploy the MCP server pod

apiVersion: v1
kind: Pod
metadata:
  name: ticket-db-mcp
  labels:
    app: ticket-db-mcp
spec:
  containers:
    - name: supergateway
      image: your-registry/your-mcp-server:latest
      ports:
        - containerPort: 8000
      args:
        - "--stdio"
        # Replace with your MCP server command
        # Examples: "python3 -m your_mcp_module", "python3 /app/stdio_server.py", "npx -y @your-org/your-mcp-server@latest"
        - "python3 -m your_mcp_module"
        - "--port"
        - "8000"
        - "--logLevel"
        - "debug"
      env:
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: mcp-credentials
              key: api_key
      stdin: true
      tty: true
---
apiVersion: v1
kind: Service
metadata:
  name: ticket-db-mcp
spec:
  selector:
    app: ticket-db-mcp
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000
  type: ClusterIP

Step 3: Configure HolmesGPT

Add to ~/.holmes/config.yaml:

mcp_servers:
  ticket_db:
    description: "Internal ticket database"
    config:
      mode: stdio
      command: "python3"
      args:
        - "/path/to/my_mcp_server.py"
      env:
        CUSTOM_VAR: "value"
    # llm_instructions tells Holmes WHEN and HOW to use this server
    llm_instructions: "Use this server to query the internal ticket database. Search for related incidents by error message or service name."
holmes ask "Find tickets related to payment service errors"

Ensure required dependencies (e.g., mcp, fastmcp packages) are installed in your environment.

When using the standalone Holmes Helm Chart, update your values.yaml:

mcp_servers:
  ticket_db:
    description: "Internal ticket database"
    config:
      url: "http://ticket-db-mcp.default.svc.cluster.local:8000/sse"
      mode: sse
    # llm_instructions tells Holmes WHEN and HOW to use this server
    llm_instructions: "Use this server to query the internal ticket database. Search for related incidents by error message or service name."

Apply the configuration:

helm upgrade holmes robusta/holmes -f values.yaml

When using the Robusta Helm Chart (which includes HolmesGPT), update your generated_values.yaml:

holmes:
  mcp_servers:
    ticket_db:
      description: "Internal ticket database"
      config:
        url: "http://ticket-db-mcp.default.svc.cluster.local:8000/sse"
        mode: sse
      # llm_instructions tells Holmes WHEN and HOW to use this server
      llm_instructions: "Use this server to query the internal ticket database. Search for related incidents by error message or service name."

Apply the configuration:

helm upgrade robusta robusta/robusta -f generated_values.yaml --set clusterName=<YOUR_CLUSTER_NAME>

SSE (Deprecated)

SSE transport is deprecated. Use streamable-http for new integrations.

Add the following to ~/.holmes/config.yaml. Create the file if it doesn't exist:

mcp_servers:
  legacy_analytics:
    description: "Legacy analytics platform (SSE transport)"
    config:
      url: "http://analytics-mcp:8000/sse"
      mode: sse
    llm_instructions: "Query historical analytics data. Use for trend analysis over periods longer than 30 days."

After making changes to your configuration, run:

holmes toolset refresh

When using the standalone Holmes Helm Chart, update your values.yaml:

mcp_servers:
  legacy_analytics:
    description: "Legacy analytics platform (SSE transport)"
    config:
      url: "http://analytics-mcp:8000/sse"
      mode: sse
    llm_instructions: "Query historical analytics data. Use for trend analysis over periods longer than 30 days."

Apply the configuration:

helm upgrade holmes robusta/holmes -f values.yaml

When using the Robusta Helm Chart (which includes HolmesGPT), update your generated_values.yaml:

holmes:
  mcp_servers:
    legacy_analytics:
      description: "Legacy analytics platform (SSE transport)"
      config:
        url: "http://analytics-mcp:8000/sse"
        mode: sse
      llm_instructions: "Query historical analytics data. Use for trend analysis over periods longer than 30 days."

Apply the configuration:

helm upgrade robusta robusta/robusta -f generated_values.yaml --set clusterName=<YOUR_CLUSTER_NAME>

The URL should end with /sse. If it doesn't, HolmesGPT will automatically append it.

OAuth Authentication

For MCP servers that require OAuth authentication (e.g. Atlassian, Notion), see the dedicated OAuth MCP Servers page.

Advanced Configuration

Dynamic Headers with Request Context

MCP servers can forward HTTP headers from the incoming request to the MCP backend. Use extra_headers with Jinja2 templates referencing request_context.headers. Header lookups are case-insensitive. You can also use environment variables ({{ env.MY_VAR }}) or combine them (Bearer {{ request_context.headers['token'] }}).

This does not apply to the CLI: request context is only available when running Holmes as a server.

When using the standalone Holmes Helm Chart, update your values.yaml:

mcp_servers:
  customer_data:
    description: "Customer data API (requires per-request auth)"
    config:
      url: "http://customer-api:8000/mcp"
      mode: streamable-http
      extra_headers:
        X-Auth-Token: "{{ request_context.headers['X-Auth-Token'] }}"
    llm_instructions: "Query customer account details and subscription status. Use when investigating user-reported issues."

Apply the configuration:

helm upgrade holmes robusta/holmes -f values.yaml

When using the Robusta Helm Chart (which includes HolmesGPT), update your generated_values.yaml:

holmes:
  mcp_servers:
    customer_data:
      description: "Customer data API (requires per-request auth)"
      config:
        url: "http://customer-api:8000/mcp"
        mode: streamable-http
        extra_headers:
          X-Auth-Token: "{{ request_context.headers['X-Auth-Token'] }}"
      llm_instructions: "Query customer account details and subscription status. Use when investigating user-reported issues."

Apply the configuration:

helm upgrade robusta robusta/robusta -f generated_values.yaml --set clusterName=<YOUR_CLUSTER_NAME>

For full details on template syntax, blocked headers, precedence rules, and examples for other toolset types, see HTTP Header Propagation.

Validating Authentication at Startup (Health Check)

Many MCP servers return their full tool list even when the configured credential is invalid (a bad token, expired key, etc.). Because of this, simply connecting and listing tools does not prove that authentication works — the toolset can appear "connected" while every real call would fail.

To catch this at startup, Holmes can invoke a single read-only tool during the toolset's health check. If the call fails (e.g. 401 Unauthorized), the toolset is marked disabled with the underlying error surfaced, instead of showing as enabled.

  • Automatic: if the server exposes a well-known identity tool — get_me, get_current_user, get_authenticated_user, or whoami — Holmes auto-detects and uses it. No configuration needed.
  • Explicit: for any other server, set health_check_tool to a tool of your choice. This takes precedence over auto-detection.

The chosen tool must be read-only, take no required arguments (it is called with empty arguments {}), and actually exercise the credential. If health_check_tool is unset and the server exposes none of the known identity tools, the auth health check is skipped and the toolset loads as long as listing tools succeeds.

mcp_servers:
  my_server:
    description: "My custom MCP server"
    config:
      url: "http://my-mcp:8000/mcp"
      mode: streamable-http
      headers:
        Authorization: "Bearer {{ env.MY_API_KEY }}"
      # Invoked with empty args at startup to verify the credential is valid.
      # Must be a read-only, no-argument tool exposed by your server.
      health_check_tool: get_current_user
    llm_instructions: "..."

Configuration Format Migration

The MCP server configuration format has been updated. The url field must now be inside the config section.

Old format (deprecated):

mcp_servers:
  my_server:
    url: "http://example.com:8000/mcp/messages"
    description: "My server"

New format:

mcp_servers:
  my_server:
    description: "My server"
    config:
      url: "http://example.com:8000/mcp/messages"
      mode: streamable-http

The old format still works but will log a migration warning.