-
Notifications
You must be signed in to change notification settings - Fork 614
[BUG]: Gateway Rejects Loki Query Tools Due to Backtick Validation #2576
Description
🐞 Bug Summary
IBM Context Forge Gateway rejects 5 critical Loki query tools during backend registration due to overly strict validation that flags backtick (`) characters in tool descriptions as "unsafe". This results in only 7 out of 12 tools being exposed through the gateway, blocking all primary log querying functionality.
🧩 Affected Component
Select the area of the project impacted:
-
mcpgateway- API (Gateway Service - Tool Validation) -
mcpgateway- UI (admin panel) -
mcpgateway.wrapper- stdio wrapper - Federation or Transports
- CLI, Makefiles, or shell scripts
- Container setup (Docker/Podman/Compose)
- Other (explain below)
Specific Component: mcpgateway.services.gateway_service - Tool validation during backend registration
🔁 Steps to Reproduce
-
Deploy Grafana MCP server (grafana/mcp-grafana:latest) with Loki datasource enabled:
kubectl apply -f k8s/loki-mcp-server.yaml
-
Register the Loki MCP server as a gateway backend:
curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "loki_ws4r1_streamable", "url": "http://<gateway-ip>/loki-mcp/mcp", "description": "WS4R1 Loki MCP Server", "transport": "STREAMABLEHTTP" }' \ http://<gateway-ip>/mcp-gateway/gateways
-
Query the MCP Gateway for available tools:
curl -X POST "http://<gateway-ip>/mcp-gateway/mcp/" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":1}'
-
Observe only 7 tools returned instead of 12
-
Check gateway logs for validation errors:
kubectl logs -n common -l app=mcp-gateway | grep "Validation failed"
🤔 Expected Behavior
Expected: All 12 tools from Grafana MCP server should be registered and exposed:
Loki Query Tools (5) - MISSING:
query_loki_logs- Execute LogQL querieslist_loki_label_names- List available log labelslist_loki_label_values- List values for specific labelsquery_loki_stats- Get log stream statisticsquery_loki_patterns- Query detected log patterns
Non-Loki Tools (7) - Working:
get_annotations- Fetch Grafana annotationsget_annotation_tags- List annotation tagsget_datasource_by_name- Get datasource by nameget_datasource_by_uid- Get datasource by UIDlist_datasources- List all datasourcessearch_dashboards- Search dashboardssearch_folders- Search folders
Actual: Only 7 tools registered. All Loki query tools are rejected during validation.
📓 Logs / Error Output
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - ERROR - Validation failed for tool 'list_loki_label_names': [{'type': 'value_error', 'loc': ('description',), 'msg': "Value error, Description contains unsafe characters: '`'", 'input': 'Lists all available label names (keys) found in logs within a specified Loki datasource and time range. Returns a list of unique label strings (e.g., `["app", "env", "pod"]`). If the time range is not provided, it defaults to the last hour.', 'ctx': {'error': ValueError("Description contains unsafe characters: '`'")}, 'url': 'https://errors.pydantic.dev/2.12/v/value_error'}]
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - ERROR - Validation failed for tool 'list_loki_label_values': [{'type': 'value_error', 'loc': ('description',), 'msg': "Value error, Description contains unsafe characters: '`'", 'input': 'Retrieves all unique values associated with a specific `labelName` within a Loki datasource and time range. Returns a list of string values (e.g., for `labelName="env"`, might return `["prod", "staging", "dev"]`). Useful for discovering filter options. Defaults to the last hour if the time range is omitted.', 'ctx': {'error': ValueError("Description contains unsafe characters: '`'")}, 'url': 'https://errors.pydantic.dev/2.12/v/value_error'}]
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - ERROR - Validation failed for tool 'query_loki_logs': [{'type': 'value_error', 'loc': ('description',), 'msg': "Value error, Description contains unsafe characters: '`'", 'input': 'Executes a LogQL query against a Loki datasource to retrieve log entries or metric values. Returns a list of results, each containing a timestamp, labels, and either a log line (`line`) or a numeric metric value (`value`). Defaults to the last hour, a limit of 10 entries, and \'backward\' direction (newest first). Supports full LogQL syntax for log and metric queries (e.g., `{app="foo"} |= "error"`, `rate({app="bar"}[1m])`). Prefer using `query_loki_stats` first to check stream size and `list_loki_label_names` and `list_loki_label_values` to verify labels exist.', 'ctx': {'error': ValueError("Description contains unsafe characters: '`'")}, 'url': 'https://errors.pydantic.dev/2.12/v/value_error'}]
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - ERROR - Validation failed for tool 'query_loki_patterns': [{'type': 'value_error', 'loc': ('description',), 'msg': "Value error, Description contains unsafe characters: '`'", 'input': 'Retrieves detected log patterns from a Loki datasource for a given stream selector and time range. Returns a list of patterns, each containing a pattern string and a total count of occurrences. Patterns help identify common log structures and anomalies. The `logql` parameter must be a stream selector (e.g., `{job="nginx"}`) and does not support line filters or aggregations. Defaults to the last hour if the time range is omitted.', 'ctx': {'error': ValueError("Description contains unsafe characters: '`'")}, 'url': 'https://errors.pydantic.dev/2.12/v/value_error'}]
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - ERROR - Validation failed for tool 'query_loki_stats': [{'type': 'value_error', 'loc': ('description',), 'msg': "Value error, Description contains unsafe characters: '`'", 'input': 'Retrieves statistics about log streams matching a given LogQL *selector* within a Loki datasource and time range. Returns an object containing the count of streams, chunks, entries, and total bytes (e.g., `{"streams": 5, "chunks": 50, "entries": 10000, "bytes": 512000}`). The `logql` parameter **must** be a simple label selector (e.g., `{app="nginx", env="prod"}`) and does not support line filters, parsers, or aggregations. Defaults to the last hour if the time range is omitted.', 'ctx': {'error': ValueError("Description contains unsafe characters: '`'")}, 'url': 'https://errors.pydantic.dev/2.12/v/value_error'}]
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - WARNING - Tool validation completed with 5 error(s). Successfully validated 7 tool(s).
2026-01-29T19:24:42 - mcpgateway.services.gateway_service - INFO - Fetched 7 tools from gateway
Database verification:
$ kubectl exec -n common deployment/database -- psql -U postgres -d mcp \
-c "SELECT COUNT(*) FROM tools WHERE name LIKE '%loki%';"
total_tools
-------------
7
(1 row)Direct Loki MCP server query shows all 12 tools:
$ curl -X POST "http://<gateway-ip>/loki-mcp/mcp" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}' | \
jq -r '.result.tools[] | .name' | wc -l
12🧠 Environment Info
| Key | Value |
|---|---|
| Version or commit | ghcr.io/ibm/mcp-context-forge:1.0.0-BETA-2 |
| Runtime | Python 3.11 (inferred from Pydantic v2.12) |
| Platform / OS | Kubernetes/K3s on Ubuntu 22.04 |
| Container | Docker (deployed in K3s cluster) |
| Backend MCP Server | grafana/mcp-grafana:latest (v0.9.0) |
| Backend Transport | STREAMABLEHTTP |
| Gateway Deployment | Namespace: common, LoadBalancer: <gateway-ip> |
🧩 Additional Context
Root Cause Analysis
The validation error originates from Pydantic model validation in mcpgateway.services.gateway_service. The validator checks for "unsafe characters" in tool descriptions and rejects backticks (`).
Why Grafana MCP uses backticks:
Grafana MCP server uses backticks in descriptions for:
- Code examples:
`{app="foo"}` - JSON examples:
`{"streams": 5}` - Array examples:
`["app", "env"]` - Parameter references:
`labelName`
This is standard Markdown/documentation formatting.
Impact
Critical: All primary Loki functionality is unavailable through the gateway:
- ❌ Cannot query logs
- ❌ Cannot discover labels
- ❌ Cannot get stream statistics
- ❌ Cannot analyze log patterns
- ✅ Can only list datasources and search dashboards (limited utility)
Workarounds
Temporary workaround: Connect directly to Loki MCP server (bypasses gateway):
{
"servers": {
"loki-direct": {
"type": "http",
"url": "http://<gateway-ip>/loki-mcp/mcp"
}
}
}This works but defeats the purpose of using the gateway for aggregation, authentication, and observability.
Proposed Solutions
Option 1: Relax validation to allow backticks (RECOMMENDED)
- Backticks are safe in descriptions (not executed code)
- Standard in documentation and Markdown
- Matches MCP specification (no restriction on description content)
Option 2: Add configuration flag to disable strict validation
GATEWAY_STRICT_TOOL_VALIDATION: "false"Option 3: Sanitize descriptions during registration
- Strip or escape backticks before validation
- Preserves security while allowing tool registration
- May reduce description clarity
Related Documentation
- MCP Specification: https://spec.modelcontextprotocol.io/
- Grafana MCP Server: https://github.com/grafana/mcp-grafana
- Pydantic Validation: https://errors.pydantic.dev/2.12/v/value_error
Testing Commands
Verify Loki MCP server tools directly:
SESSION_ID=$(curl -s -X POST "http://<gateway-ip>/loki-mcp/mcp" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' \
-D - | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\r\n')
curl -s -X POST "http://<gateway-ip>/loki-mcp/mcp" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}' | \
jq -r '.result.tools[] | .name' | sortExpected output: 12 tools including query_loki_logs, list_loki_label_names, etc.
Verify gateway-exposed tools:
SESSION_ID=$(curl -s -X POST "http://<gateway-ip>/mcp-gateway/mcp/" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' \
-D - | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\r')
curl -s -X POST "http://<gateway-ip>/mcp-gateway/mcp/" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}' | \
jq -r '.result.tools[] | .name' | sortCurrent output: 7 tools (missing all query_loki_* and list_loki_* tools)