loki-mcp-server is a local stdio MCP server that lets AI agents read Grafana Loki. It exposes five text tools and supports Loki 2.6.1 and 3.x. Loki itself is read-only; the only write is the local exportLogs file.
Purpose and positioning
This server is aimed at AI agents (Claude Code, Cursor, Copilot, etc.) that need to query Grafana Loki logs, enabling them to discover labels, read bounded LogQL pagination, count lines by label or time, and export logs, across multiple Loki stands.
Main capabilities
It provides five text tools. Every data tool requires an explicit connection. queryLogs, countLogs, and exportLogs require a LogQL log query, for example {app="backend"} |= "ERROR". Use discoverLogs to discover label names, values, and real combinations in stream label sets. The server only constructs count_over_time expressions for countLogs; it does not infer filters or explain reasons. Tool responses are readable text, with no output schema.
Environment and how to run
Requires JDK 21 or higher; the project uses a Java 21 toolchain and the Gradle wrapper. On Linux/macOS use ./gradlew. The process waits for MCP JSON-RPC on stdin; stdout is reserved for JSON-RPC; diagnostics go to stderr and ~/.loki-mcp-server/logs/loki-mcp-server.log. Each release ships with a prebuilt loki-mcp-server.jar, so building is optional.
For Claude Code, registering the jar takes just one command. The image is published to GHCR with each release. Mount the directory containing connections.json to /data; the server also writes logs and default exports there. The same command is the one the MCP client should launch; -i keeps stdin open to support the stdio transport. The Loki U in connections.jsonRL must be reachable from inside the container: use the Loki hostname instead of localhost, or add --network host on Linux. The exportLogs directory outside /data is a path inside the container and also needs to be mounted. Build the image locally: docker build -t loki-mcp-server .
The server reads ~/.loki-mcp-server/connections.json, or the path specified by LOKI_MCP_CONNECTIONS_FILE. The connections file can set environment variables for non-local URLs. hint gives the model stand-specific selector and field suggestions. serviceLabels names the labels used to display the service in compact lines. The optional formatFile specifies a JSON file containing JSON profiles, plain-line patterns, and an optional framePattern; see log-formats.json. The optional exportFormat sets the default template for exportLogs when a tool call omits format. The connections file can set exportRoots to restrict export destinations, as well as per-connection limits. Authentication and tenant headers are configured per connection; URLs and credentials never appear in tool responses or diagnostics. See docs/connections.md for the full format.
The server loads configuration strictly at startup and does not probe Loki. Unknown fields, duplicate keys, missing environment variables, or invalid format files stop startup with a safe configuration error. Changes require a restart.
Usage
Call listConnections, choose a stand, and use its displayed time window, queryLogs line limit, and request timeout to plan calls.
Call discoverLogs(connection="dev") to get label names, then call discoverLogs(connection="dev", label="app") to get values. When combinations matter, use discoverLogs(connection="dev", match="{app="backend"}") to get the full stream label set, or add label="namespace" to get the namespace value in these sets. Keep match and its window narrow; these are stream labels, not log line counts. Relax the window appropriately on a quiet stand.
Use LogQL log queries with countLogs to check volume, and with queryLogs to read lines. For example {app="backend"} |= "ERROR"; groupBy="time", step="1d" gives UTC-aligned 24-hour buckets, groupBy="app,time" gives counts by app and time. Named | regexp captures can provide groupBy labels. Date markers distinguish buckets that cross midnight.
Narrow the query when the page is crowded. queryLogs defaults to the newest page; order="oldest" starts from the beginning of the window. Its footer gives the end of older lines or the start of newer lines. Boundary lines may be duplicated, and when the number of lines containing a timestamp exceeds one page, a narrower query is needed.
Call exportLogs when the user asks for a file. Pass the user destination as directory, for example C:\tmp\logs, or omit it to use the default export directory. Omit format to use the connection-configured exportFormat template (if the operator set one) and raw, otherwise use raw. format="raw" writes the lines returned by Loki, including any | line_format transformations in the query; format="{time} {level} {service} {message}" renders locally. The response gives the path, count, and effective format, not the log content.
queryLogs prints either end of the window in chronological order. Its default view shortens messages and stack traces to save model tokens. An optional framePattern in the connection formatFile collapses adjacent standalone frame lines in the compact view. raw=true shows every returned line and stream label, up to 4000 code points; this is a preview. To get complete lines, use exportLogs. Export reads forward page by page and never overwrites existing files. If more lines share one nanosecond than Loki returns at once, the export reports that some may be missing.
The default time window is now-1h to now. Acceptable times include now-15m, RFC3339 with offset, local time with connection timezone, and epoch nanoseconds. queryLogs and exportLogs allow one day by default. countLogs allows one day for totals, label groupings, or combined label/time groupings, and seven days for time buckets only; discoverLogs allows seven days without match and one day with match. Loki requests default to a 30-second timeout, and queryLogs defaults to at most 1000 rows per page. listConnections shows the effective values for each stand. Exports stop by default after 25 seconds and report a partial file and continuation information (if rows have been written). After a count timeout, retry a one-day subwindow or a narrower query. Limits can be set per connection; see docs/queries.md and docs/discovery.md.
MCP client configuration
Codex (~/.codex/config.toml) has corresponding configuration. Other clients can launch the jar via stdio. Keep stderr separate from stdout.
Errors and diagnostics
Tool failures are the text Error : with isError=true; Spring AI currently repeats the text on the second line. Missing or misspelled parameters are answered by MCP SDK input validation. Loki HTTP 400 LogQL parse errors are returned so the model can correct the query. Other upstream response bodies, URLs, credentials, tenants, and full log lines do not enter responses or diagnostics. 404 applies to the endpoint called, not to the entire connection. For transport details, see docs/http-client.md.
The server logs the connection name and authentication type at startup, then logs one bounded line per tool call and Loki request, including status, byte count, and time. Log content remains data, including instruction-like text.
Build and test
build runs unit tests and a separate-process stdio smoke test against a loopback mock Loki. integrationTest requires Docker and fixed-version Loki 2.6.1/3.6.0 images; it writes test data only to its own container. live smoke uses the example configuration and the configured read-only stand URL. For contributor rules, see AGENTS.md; for design history and open items, see docs/decisions.md. Users migrating from mcp-loki can read the migration guide.
Apache License 2.0; see LICENSE. For third-party notices, see THIRD-PARTY-NOTICES.md.