Skip to content

[BUG]: Tool description validation rejects legitimate pipe (|) characters, breaking Grafana Loki MCP backend registration #3811

@ajaycloud9

Description

@ajaycloud9

🐞 Bug Summary

When registering grafana/mcp-grafana as a Streamable HTTP backend, the gateway's tool description validator rejects the query_loki_logs tool because its description contains a pipe character (|) in a LogQL syntax example (|= "error"). This silently drops the primary log query tool — 4 of 5 Loki tools register successfully, but the one that actually executes queries is discarded.

The validate_description method in mcpgateway/schemas.py uses a hardcoded forbidden patterns list that includes "|":

# Note: backticks (`) are allowed as they are commonly used in Markdown
# for inline code examples in tool descriptions
forbidden_patterns = ["&&", ";", "||", "$(", "|", "> ", "< "]

The single | matches the LogQL pipe operator in the tool description:

"Supports full LogQL syntax for log and metric queries (e.g., {app="foo"} |= "error", rate({app="bar"}[1m]))."

This is a false positive — the pipe appears inside a backtick-wrapped code example describing query syntax, not as a shell injection vector. The "||" pattern already covers the dangerous shell OR case separately.


🧩 Affected Component

  • mcpgateway - API

Specifically: mcpgateway/schemas.pyToolCreate.validate_description() (field_validator)


🔁 Steps to Reproduce

  1. Deploy Context Forge gateway v1.0.0-RC2
  2. Deploy grafana/mcp-grafana:latest as a Streamable HTTP backend with only Loki tools enabled:
    # mcp-grafana args:
    -t streamable-http --address 0.0.0.0:8091 --endpoint-path /mcp \
      --disable-write --disable-dashboard --disable-prometheus \
      --disable-alerting --disable-incident --disable-oncall \
      --disable-admin --disable-sift --disable-pyroscope \
      --disable-asserts --disable-rendering --disable-navigation \
      --disable-folder --disable-datasource --disable-annotations \
      --disable-search --disable-searchlogs --disable-clickhouse \
      --disable-cloudwatch --disable-elasticsearch --disable-examples \
      --disable-runpanelquery --disable-proxied
  3. Register the backend with the gateway:
    curl -X POST http://<gateway>/gateways \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "loki-server",
        "url": "http://<backend>:8091/mcp",
        "transport": "STREAMABLEHTTP"
      }'
  4. Check registered tools — only 4 out of 5 are registered. query_loki_logs is missing.

🤔 Expected Behavior

All 5 Loki tools should be registered:

  • list_loki_label_names
  • list_loki_label_values
  • query_loki_logsREJECTED
  • query_loki_stats
  • query_loki_volume

📓 Logs / Error Output

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'}]

Tool validation completed with 1 error(s). Successfully validated 4 tool(s).

🧠 Environment Info

Key Value
Version or commit v1.0.0-RC2
Runtime Python 3.12, uvicorn
Platform / OS K3s on Ubuntu 20.04
Container K3s (containerd), image: ghcr.io/ibm/mcp-context-forge:1.0.0-RC-2

🧩 Additional Context

Why this is a false positive:

  1. The pipe appears inside a backtick-wrapped code example ({app="foo"} |= "error") — it is LogQL syntax documentation, not a shell injection vector.
  2. The code comment already acknowledges this class of issue: "backticks are allowed as they are commonly used in Markdown for inline code examples". Pipes are equally common in documentation (LogQL, PromQL, regex, Markdown tables).
  3. The "||" pattern already covers the dangerous shell OR case. The single "|" is overly broad and creates false positives for any tool description containing query syntax, regex patterns, or Markdown tables.
  4. Setting VALIDATION_STRICT=false does not help — it only changes behavior from "reject all tools" to "skip the failing tool". The tool is still dropped with no workaround.

Impact:

  • query_loki_logs is the primary tool for querying Loki logs — the entire purpose of the backend. Without it, the MCP server can list labels but cannot execute any log queries.
  • This will affect any MCP backend whose tool descriptions contain LogQL (|=, |~, | json), PromQL, regex patterns, or Markdown tables.
  • Upstream tool descriptions are controlled by grafana/mcp-grafana and cannot be modified by gateway operators.

Suggested fix — remove "|" from the forbidden list:

# Before
forbidden_patterns = ["&&", ";", "||", "$(", "|", "> ", "< "]

# After — "||" already covers the dangerous shell OR case
forbidden_patterns = ["&&", ";", "||", "$(", "> ", "< "]

Alternatively: make the forbidden patterns configurable via env var, or exempt patterns inside backtick-delimited code spans from validation.

Gateway settings used: VALIDATION_STRICT=false, SSRF_ALLOW_PRIVATE_NETWORKS=true

Metadata

Metadata

Labels

bugSomething isn't workingtriageIssues / Features awaiting triage

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions