MCP server for querying Loki logs with semantic understanding
Loki-MCP provides 5 well-named, READ_ONLY tools for Kubernetes log querying. Tool names follow verb_noun convention (get_*, search_*, list_, find_*) and are clear. However, there are critical gaps: output schemas are not explicitly documented in the source code, tools return formatted strings rather than structured objects with typed fields. Parameter descriptions exist but lack key constraints (regex patterns for 'query', valid ranges for 'hours' and 'limit'). Error handling is minimal (only nil checks, no guidance for recovery). The codebase shows proper namespace/pod/hours parameter structure with defaults, but the string-based output design violates the 'structured response' pattern and forces LLMs to parse free-form text. No tool annotations (readOnlyHint, idempotentHint) are declared. The server initializes without explicit tool registration decorators visible, relying on FastMCP @mcp.tool() decorators, which do support schemas but the schemas here are inferred from function signatures rather than explicitly documented.
Find pods that have restarted or crashed recently. Args: namespace: Filter to specific namespace (empty = all namespaces) hours: Look back this many hours (default: 1) Returns: List of pods with restart counts and reasons Example: "Which pods are crashing in my cluster?" -> Call with namespace="", hours=2
Get a summary of errors happening in your cluster. Args: namespace: Filter to specific namespace (empty = all namespaces) hours: Look back this many hours (default: 1) Returns: Summary of error counts, types, affected pods, and sample errors Example: "What errors are happening in my cluster?" -> Call with namespace="", hours=1
Get logs for a specific pod. Args: pod_name: Name of the pod to query namespace: Namespace of the pod (empty = search all) hours: Look back this many hours (default: 1) limit: Maximum log lines to return (default: 100) Returns: Recent logs from the specified pod Example: "Show me logs from the ollama pod" -> Call with pod_name="ollama*", namespace="ai", hours=1
List all namespaces that have logs in Loki. Returns: List of namespace names Example: "What namespaces are in my cluster?"
Search logs with a regex pattern. Args: query: Regex pattern to search for namespace: Filter to specific namespace (empty = all namespaces) hours: Look back this many hours (default: 1) limit: Maximum number of log lines to return (default: 100) Returns: Matching logs grouped by pod Example: "Find all logs mentioning 'timeout'" -> Call with query="timeout", namespace="", hours=2
Output schemas not documented. All tools return plain strings (formatted summaries) instead of structured objects with typed fields. LLMs cannot reliably extract data or chain downstream tools. Pattern:response-shaper requires structured, typed output.
Parameter 'query' in search_logs lacks format/pattern constraint. Description says 'Regex pattern' but does not explain valid regex syntax, escaping rules, or what happens if regex is invalid. No validation shown in code.
Numeric parameters (hours, limit) lack explicit min/max ranges in descriptions. Baseline rubric requires 'hours 1-365, limit 1-100' style constraints. LLMs may pass absurd values (hours=-5, limit=1000000).
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 59 | <=2025-11-25 | v2 |
| 2026-03-09 | D | 55 | - | v1 |
Error handling minimal. Tools only check 'if loki is None' and return generic string. No guidance for recovery (e.g., 'Loki service unavailable. Check LOKI_URL environment variable'). Pattern:recovery-guide requires actionable error messages.
No tool annotations declared. FastMCP tools do not specify readOnlyHint or idempotentHint. While all tools are read-only, explicit annotation helps clients understand safety guarantees.
Result limits not enforced or documented. search_logs and get_pod_logs default to limit=100, but code shows 'list()[:5]' or '[:3]' truncation in response formatting. If a user omits 'limit', will they get 100 lines or 5? Inconsistent truncation breaks expectations.
No pagination support. search_logs and get_pod_logs cap results in-memory (limit param), but do not return a 'next_cursor' or 'total_count'. Large log sets force truncation; agent cannot fetch more without re-querying with different time ranges.
Tool descriptions include example values in docstrings (e.g., 'query="timeout", namespace="", hours=2'). Baseline rubric warns: 'LLMs tend to reuse example values literally rather than adapting to context.' Should replace with formal constraints instead.