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:
streamable-http(Recommended): Modern HTTP-based transport. Use this for new integrations.stdio: Direct process communication via standard input/output. Supported directly in CLI; supported on Kubernetes via Supergateway.sse(Deprecated): Legacy Server-Sent Events transport. Usestreamable-httpinstead.
Streamable-HTTP (Recommended)¶
Set the environment variable:
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:
To test, run:
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:
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:
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."
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:
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:
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:
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:
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:
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:
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:
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, orwhoami— Holmes auto-detects and uses it. No configuration needed. - Explicit: for any other server, set
health_check_toolto 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):
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.