Multi-Container Log Correlator MCP Server
Provides tools for monitoring and correlating logs from Docker containers, including listing active containers, searching logs by trace ID, tailing service logs and errors, and retrieving container statistics.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Multi-Container Log Correlator MCP Server Trace request req-998877 across all containers"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Multi-Container Log Correlator MCP Server
An MCP (Model Context Protocol) server that continuously ingests logs from all running Docker containers, indexes them by trace/request ID, and exposes a clean tools so an LLM can debug distributed transactions without ever switching terminal windows.
How it works
Docker daemon
Windows → //./pipe/docker_engine (named pipe, auto-detected)
Linux/Mac → /var/run/docker.sock (Unix socket, auto-detected)
Custom → DOCKER_HOST=tcp://... (TCP via env var)
│ (multiplexed stdout/stderr streams)
▼
DockerLogWatcher ──► LogParser ──► TraceIndexedRingBuffer
(per-container) (zero-copy (10 000 lines/container +
log streams) parsing) O(1) trace_id index)
│
▼
MCP stdio transport
│
▼
LLM tool callsThe ring buffer retains the last 10 000 log lines per container. A
secondary Map<traceId, Set<slotKey>> index lets search_by_trace_id resolve
cross-service timelines in O(1) time regardless of buffer size.
Related MCP server: mcplogview
Exposed MCP Tools
The server exposes 7 tools. Use them in the order shown below for a typical debugging session start with topology, narrow to errors, then correlate by trace ID.
1. list_active_containers
Lists all Docker containers currently being monitored by the server.
Use this first to understand the service topology before calling any other tool it tells you the exact service names you will need to pass as arguments.
Argument | Type | Required | Description |
| string | No | Filter by Docker Compose project name. Omit to list all running containers. |
Returns: Service name, short container ID (12-char), Docker image, running status, and Compose project for each container.
Example:
list_active_containers()
→ api-gateway (id: a1b2c3d4e5f6, image: nginx:latest, Up 3 hours)
→ checkout-worker (id: b2c3d4e5f6a1, image: node:20, Up 3 hours)
→ inventory-db (id: c3d4e5f6a1b2, image: postgres:16, Up 3 hours)2. search_by_trace_id — Primary debug tool
The core power tool. Takes a trace/request/correlation ID and returns a chronologically merged timeline of every log event across all containers that share that ID resolved in O(1) time using the in-memory trace index.
This is the tool that turns a 500 error into a root cause in seconds. A single call spans every service that touched the request.
Argument | Type | Required | Description |
| string | Yes | The correlation ID to search for e.g. |
| number | No | Maximum log events to return. Default |
Returns: A merged, chronologically sorted list of log lines from all services that logged this trace ID, including the service name, timestamp, log level, and full message for each event.
Example:
search_by_trace_id({ trace_id: "req-998877" })
→ 14:03:21.001 [api-gateway] INFO POST /checkout received
→ 14:03:21.045 [checkout-worker] INFO Reserving 2x item #42
→ 14:03:22.100 [inventory-db] ERROR Deadlock detected on stock_reservations
→ 14:03:22.441 [checkout-worker] ERROR DB timeout after 1400ms
→ 14:03:22.443 [api-gateway] ERROR Responding 500 to client3. tail_service_errors
Retrieves the most recent ERROR and FATAL log lines from a specific container. The fastest way to find an initial failure point when you do not yet have a trace ID grab the error, extract its trace ID, then pass it to search_by_trace_id.
Argument | Type | Required | Description |
| string | Yes | Docker container name or Compose service name (e.g. |
| number | No | Number of most-recent error lines to return. Default |
| boolean | No | Also include |
Returns: The N most-recent ERROR/FATAL (and optionally WARN) log lines from the named service in chronological order.
Detected levels: FATAL, CRITICAL, ERROR and optionally WARN / WARNING.
Example:
tail_service_errors({ service_name: "checkout-worker", limit: 10, include_warnings: true })
→ 14:03:22.441 ERROR DB timeout after 1400ms trace_id=req-998877
→ 14:03:19.100 WARN Slow query detected (980ms) trace_id=req-9988004. tail_service_logs
Retrieves the N most-recent log lines from a specific container with optional level filtering. Use this to get general context around a service see what it was doing before or after an event without restricting to errors only.
Argument | Type | Required | Description |
| string | Yes | Docker container name or Compose service name. |
| number | No | Number of most-recent lines to return. Default |
| string[] | No | Restrict to specific levels: |
Returns: The most-recent matching log lines in chronological order with timestamp, service name, level, and message.
Example:
tail_service_logs({ service_name: "api-gateway", limit: 50, level_filter: ["INFO", "ERROR"] })
→ Returns the last 50 INFO and ERROR lines from api-gateway5. search_logs
Case-insensitive full-text search across the buffered log lines of one or all containers. Useful when you know part of an error message but not which service threw it, or when hunting for a specific SQL query, function name, or stack trace snippet that does not carry a trace ID.
Argument | Type | Required | Description |
| string | Yes | Case-insensitive substring to match against log messages (e.g. |
| string | No | Restrict search to one container. Omit to search all containers simultaneously. |
| number | No | Maximum matching lines to return. Default |
| string[] | No | Restrict results to specific log levels. |
Returns: All matching log lines in chronological order annotated with service name, timestamp, and level. Indicates when results are truncated.
Example:
search_logs({ query: "connection refused", limit: 20 })
→ Finds every "connection refused" line across all running containers6. get_container_stats
Returns buffer statistics for one or all containers at a glance: total lines buffered, error/warn counts, a sample of known trace IDs, and the oldest/newest log timestamps. Useful for a quick system health overview or to verify the buffer is actively filling with live data.
Argument | Type | Required | Description |
| string | No | A specific container to inspect. Omit to get stats for all running containers. |
Returns per container:
Lines buffered (ring buffer holds up to 10,000 lines per container)
Error count and warning count
Oldest and newest log timestamps in the buffer
Sample of known trace IDs seen in that container's logs
Example:
get_container_stats()
→ checkout-worker: 4,821 lines | Errors: 12 | Warnings: 34 | Traces: req-998877, req-998800 …
→ inventory-db: 2,103 lines | Errors: 3 | Warnings: 1 | Traces: req-998877 …
→ api-gateway: 9,441 lines | Errors: 12 | Warnings: 89 | Traces: req-998877, req-998800 …7. list_trace_ids
Lists all trace/request IDs currently held in the in-memory buffer. Use this when a user reports "something went wrong" with no trace ID browse the available IDs, pick the one closest to the reported time, and feed it directly into search_by_trace_id.
Argument | Type | Required | Description |
| string | No | Only list trace IDs seen in this specific service. Omit for all services. |
| number | No | Maximum number of trace IDs to return. Default |
Returns: A numbered list of trace ID strings currently in the buffer.
Example:
list_trace_ids({ service_name: "checkout-worker", limit: 5 })
→ 1. req-998877
→ 2. req-998800
→ 3. req-998741
→ 4. req-998603
→ 5. req-998512Trace ID Detection
The parser automatically extracts trace identifiers from:
JSON fields:
trace_id,traceIdW3C
traceparentheader values embedded in logsX-Request-ID,X-Trace-ID,requestid,request_idkey-value pairsreq-XXXXXstyle short IDs (common in Node.js / Express apps)OpenTelemetry
trace_id=<hex32>fieldsOpenTelemetry
span_id/spanIdfor span-level correlation
Prerequisites
Node.js 18+
Docker running locally (or accessible socket)
Windows: Docker Desktop running uses
//./pipe/docker_engineautomaticallyLinux/Mac: Read access to
/var/run/docker.sock
Installation & Usage
npm install
node index.jsEnvironment variables
The server auto-detects the Docker connection for your platform no configuration needed in most cases.
Variable | Default | Description |
| (auto) | Override with |
|
| Linux/Mac only override the Unix socket path |
Windows tip: If the server cannot find containers, enable "Expose daemon on tcp://localhost:2375" in Docker Desktop → Settings → General, then set
DOCKER_HOST=tcp://localhost:2375in your MCP config (see below).
Claude Desktop / Cursor config
Add this to your claude_desktop_config.json:
Config file location
Windows:
C:\Users\<you>\AppData\Roaming\Claude\claude_desktop_config.jsonMac:
~/Library/Application Support/Claude/claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
Linux / Mac
{
"mcpServers": {
"multi-container-log-correlator-mcp-server": {
"command": "node",
"args": ["/absolute/path/to/multi-container-log-correlator-mcp-server/index.js"]
}
}
}Windows (named pipe works out of the box with Docker Desktop)
{
"mcpServers": {
"multi-container-log-correlator-mcp-server": {
"command": "node",
"args": ["C:\\path\\to\\multi-container-log-correlator-mcp-server\\index.js"]
}
}
}Windows (TCP fallback if named pipe does not work, enable TCP in Docker Desktop → Settings → General first)
{
"mcpServers": {
"multi-container-log-correlator-mcp-server": {
"command": "node",
"args": ["C:\\path\\to\\multi-container-log-correlator-mcp-server\\index.js"],
"env": {
"DOCKER_HOST": "tcp://localhost:2375"
}
}
}
}Typical debugging session
User: "The frontend is throwing a 500 error on checkout. Trace ID is req-998877. What happened?"
LLM:
1. list_active_containers() → sees api-gateway, checkout-worker, inventory-db
2. search_by_trace_id("req-998877") → gets merged timeline across all 3 services
3. Reads the timeline, spots the DB deadlock in inventory-db at 14:03:22.441
4. Reports: "inventory-db hit a deadlock on the stock reservation table,
which caused checkout-worker to time out, propagating a 500 to api-gateway."Project structure
src/
├── constants.js # Buffer sizes, regex patterns, socket path
├── services/
│ ├── ringBuffer.js # TraceIndexedRingBuffer (core data structure)
│ ├── dockerClient.js # DockerLogWatcher streams from daemon
│ ├── logParser.js # Stateless line parser (timestamps, levels, trace IDs)
│ └── formatter.js # Human-readable output helpers
├── schemas/
│ └── toolSchemas.js # Zod input schemas for every MCP tool
└── tools/
└── containerTools.js # All 7 MCP tool registrations
── index.js # Entry point wires everything togetherAvailable Tools
7 toolsget_container_statsGet Container StatsARead-onlyIdempotent
Return buffer statistics for one or all containers: total lines buffered, error/warn counts, known trace IDs, and oldest/newest timestamps.
Args:
service_name (string, optional): A specific service to inspect. Omit for all.
Returns: Per-container stats including line counts by level and a sample of known trace IDs. Useful for a quick health overview before deeper investigation.
Inputs: service_name.
list_active_containersList Active ContainersARead-onlyIdempotent
List all currently running Docker containers being monitored.
Returns the container name, short ID, Docker image, status, and the Compose project it belongs to (if any).
Args: - project_name (string, optional): Filter by Docker Compose project name. Omit to list all.
Returns: A formatted list of containers with: serviceName, containerId (12-char), image, status, project.
Use this first to understand the service topology before issuing other queries.
Inputs: project_name.
list_trace_idsList Known Trace IDsARead-onlyIdempotent
List all trace/request IDs currently in the in-memory buffer.
Useful when you don't already know a trace ID — browse available ones and pick the suspicious one to investigate with search_by_trace_id.
Args:
service_name (string, optional): Only list trace IDs seen in this service's logs.
limit (number, 1-1000): Max IDs to return. Default 50.
Returns: A list of trace ID strings, most-recently seen first.
Inputs: limit, service_name.
search_by_trace_idSearch by Trace IDARead-onlyIdempotent
Return a chronologically merged timeline of all log events across every container that share a specific trace / request ID.
This is the primary debugging tool. It correlates distributed transactions in O(1) time using the in-memory trace index.
Args:
trace_id (string): The correlation ID to search for (e.g. "req-998877", a W3C traceparent trace hex).
limit (number, 1-500): Maximum events to return. Default 200.
Returns: A chronological list of log lines across all services tagged with this trace_id, showing the full distributed call path — ideal for pinpointing cascading failures.
Example: search_by_trace_id({ trace_id: "req-998877" })
Inputs: trace_id (required), limit.
search_logsSearch LogsARead-onlyIdempotent
Full-text search across all (or a specific) container's buffered log lines.
Useful for finding specific error messages, stack trace snippets, SQL queries, or any freeform string that doesn't have a structured trace ID.
Args:
query (string): Case-insensitive substring to search for in log messages.
service_name (string, optional): Restrict search to this container. Omit to search all.
limit (number, 1-500): Max matching lines to return. Default 100.
level_filter (string[], optional): Restrict to specific log levels.
Returns: Matching log lines in chronological order with their service name, timestamp, and level.
Inputs: query (required), limit, level_filter, service_name.
tail_service_errorsTail Service ErrorsBRead-onlyIdempotent
Retrieve the most recent ERROR and FATAL log lines = require(a specific container — the fastest way to find the initial failure point before diving into a full trace.
Args:
service_name (string): Docker container name or Compose service name (e.g. "checkout-worker").
limit (number, 1–500): How many error lines to return. Default 50.
include_warnings (boolean): Also include WARN-level lines. Default false.
Returns: The N most-recent ERROR/FATAL (and optionally WARN) log lines = require(the named service, in chronological order.
Error Handling:
Returns an error message if the service_name does not match any running container.
Inputs: service_name (required), limit, include_warnings.
tail_service_logsTail Service LogsARead-onlyIdempotent
Retrieve the N most-recent log lines = require(a specific container, with optional level filtering.
Args:
service_name (string): Container name or Compose service name.
limit (number, 1-500): Lines to return. Default 100.
level_filter (string[], optional): One or more of TRACE, DEBUG, INFO, WARN, ERROR, FATAL, UNKNOWN. return for all levels.
Returns: The most-recent matching log lines in chronological order. Use this to get general context around a service before narrowing to a trace ID.
Inputs: service_name (required), limit, level_filter.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 7 tools
Most tools have clearly distinct purposes, with the main overlap being tail_service_errors and tail_service_logs when level_filter includes ERROR/FATAL. The descriptions mitigate this by positioning tail_service_errors as the fast error-spotting tool, but an agent could still reasonably confuse which to call.
All tool names follow a consistent snake_case verb-first pattern: list, search, get, tail. The naming style is predictable and makes it easy to infer what each tool does before reading its description.
Seven tools is a well-scoped count for a log correlation server. Each tool covers a distinct aspect of the debugging workflow without feeling bloated or too sparse.
The set covers container discovery, stats, error tailing, general log tailing, full-text search, trace ID listing, and trace-correlated timelines. A notable minor gap is the lack of explicit time-range based log queries, but the core distributed debugging workflow is well supported.
Maintenance
Related MCP Connectors
Query application logs, traces, and metrics from your AI coding assistant via Foam's MCP server.
Hosted MCP messaging across owners, tools, and machines, with readable transcripts.
Analytics and debugging for your MCP server — explore usage and sessions, then root-cause errors.
Ingest and search LogsLoom logs from coding agents.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceProvides comprehensive logging and monitoring capabilities for MCP services with real-time log tailing, advanced search, error analysis, and anomaly detection. Enables centralized log aggregation, correlation tracking, and health monitoring across all MCP ecosystem services.-
- AlicenseNot gradedqualityFmaintenanceExposes configured log files as MCP tools, enabling agents to list, query, and follow logs from local and SSH sources.MIT
- FlicenseNot gradedqualityCmaintenanceEnables querying and searching logs from Kibana (ELK stack) via MCP, with support for project-based data views, trace ID correlation, and log expansion.-
- AlicenseAqualityDmaintenanceEnables MCP-compatible coding agents to debug applications using runtime log data, by providing tools to start a local log-ingestion server, track debugging sessions and hypotheses, and correlate logs to specific executions.194 npmMIT