A lightweight TUI log viewer with a built-in MCP server. Pipe JSON logs in, get a searchable terminal UI and an AI-queryable endpoint out.
your-app 2>&1 | logpond --config ./config.yaml
- TUI — Column-based log viewer with live scrolling, search, and copy
- MCP Server — AI agents query your logs via
stats,search_logs,tail,get_schema - Hub — Auto-spawning aggregator discovers all running instances, one MCP endpoint for all services
- Config-driven — YAML defines columns, field mappings, and MCP behavior
- JSON + logfmt — Supports structured JSON and key=value logfmt formats
- Ring buffer — Fixed-capacity circular buffer (default 50k entries), zero-copy iteration
- Cross-platform — macOS and Linux, clipboard support on both
brew tap lodibrahim/tap
brew install logpondgo install github.com/lodibrahim/logpond/cmd/logpond@latestDownload from GitHub Releases.
# config.yaml
name: my-app
type: json
mapping:
timestamp:
field: ts
severity:
field: level
body:
field: msg
columns:
- name: Time
source: timestamp
format: time_short
width: 8
- name: Level
source: severity
width: 5
- name: Message
source: body
flex: truemy-app 2>&1 | logpond --config ./config.yamlThe first instance auto-spawns the hub on port 9800. No extra setup.
| Key | Action |
|---|---|
j / k |
Scroll down / up |
G / g |
Jump to bottom / top |
/ |
Live search (filters as you type) |
Esc |
Clear search |
y |
Copy visible entries to clipboard |
c |
Clear all logs |
q |
Quit |
Mouse wheel scrolling is also supported.
Point your AI agent at the hub (not the individual instance):
// .mcp.json (Claude Code)
{
"mcpServers": {
"logpond": {
"type": "http",
"url": "http://localhost:9800/mcp"
}
}
}The hub discovers all running logpond instances automatically. Start more services, they appear. Stop them, they disappear.
The hub is an MCP aggregator that sits in front of all logpond instances.
- First logpond instance checks if port 9800 is open
- If not, spawns
logpond hubas a detached background process - Each instance registers itself in
~/.logpond/<name>-<pid>.json - Hub discovers instances on every query (no polling)
- Hub persists across instance restarts — start/stop services freely
Note: The hub must be running before the MCP client (e.g. Claude Code) connects. If you start the client first, run any logpond instance to auto-spawn the hub, then restart the client.
All tools fan out to every live instance and merge results:
list_instances— show all discovered instances with statusstats— merged severity breakdown, field values, time range per instancesearch_logs— search across all instances, results interleaved by timestampget_schema— all instance schemas and contexts in one calltail— last N entries merged across all instances
Use the instance parameter on search_logs and tail to target a specific service.
The hub is normally auto-spawned, but you can start it manually:
logpond hub # default port 9800
logpond hub --port 9900 # custom portEach logpond instance also exposes its own MCP endpoint (default port 9876):
Quick overview — total entries, severity breakdown, active field values, time range.
{
"instance": "my-app",
"total_entries": 1731,
"time_range": { "oldest": "...", "newest": "..." },
"severity": { "INFO": 1365, "WARN": 50, "DEBUG": 316 },
"fields": {
"component": [{ "value": "engine", "count": 578 }]
}
}Search with regex, field filters, severity, and time range. All filters are AND-ed.
| Param | Type | Description |
|---|---|---|
text |
string | Regex against body and all field values (case-insensitive) |
fields |
object | Exact field matches, e.g. {"symbol": "NVDA"} |
level |
string | INFO, WARN, ERROR, DEBUG |
after / before |
string | ISO 8601 time range |
limit |
int | Max results (default from config) |
count_only |
bool | Return just the count, no entries |
Last N entries (default 10).
Returns column definitions, sample values, and the context string from your config — gives AI agents operational context about your app.
name: my-app # Instance name (shown in MCP responses)
type: json
mapping:
timestamp:
field: timestamp # JSON path to timestamp
severity:
field: level # JSON path to log level
body:
field: fields.message # Supports nested paths (dot-separated)
auto_map_remaining: true # Append unmapped fields to message body
mcp:
exclude_fields: # Fields to strip from MCP responses
- span
- target
default_limit: 100 # Default search result limit
context: | # Operational context for AI agents
my-app is a web server.
"Connection reset" warnings are normal during deploys.
columns:
- name: Time
source: timestamp
format: time_short # HH:MM:SS
width: 8
- name: Level
source: severity
width: 5
- name: Service
source: field:service # Extract from top-level JSON field
width: 10
- name: Trace
source: span_field:trace_id # Extract from spans[] array
width: 12
- name: Message
source: body
flex: true # Exactly one column must be flex
exclude: # Fields to hide from auto_map body
- target| Source | Example | Description |
|---|---|---|
timestamp |
-- | Parsed timestamp |
severity |
-- | Log level |
body |
-- | Log message |
field:<name> |
field:service |
Top-level JSON field |
span_field:<name> |
span_field:trace_id |
Field from spans[] array |
# Instance mode (pipe logs)
app 2>&1 | logpond --config ./config.yaml [flags]
Flags:
--config Path to YAML config file (required)
--buffer Ring buffer capacity (default: 50000)
--mcp-port MCP server port (default: 9876)
--name Instance name override (default: from config)
# Hub mode (aggregator)
logpond hub [flags]
Flags:
--port Hub MCP server port (default: 9800)
┌──────────────────────────┐
│ Hub (:9800/mcp) │
│ Auto-spawned by first │
│ instance. Fans out │
│ queries to all live │
│ instances. │
└─────┬──────────┬─────────┘
│ │
┌──────────────┘ └──────────────┐
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ Instance A (:9876) │ │ Instance B (:9877) │
│ │ │ │
│ stdin ─▶ Parser │ │ stdin ─▶ Parser │
│ ▼ │ │ ▼ │
│ Store ◀── MCP │ │ Store ◀── MCP │
│ ▼ │ │ ▼ │
│ TUI │ │ TUI │
└─────────────────────────┘ └─────────────────────────┘
~/.logpond/app-a-1234.json ~/.logpond/app-b-5678.json
- Hub discovers instances via
~/.logpond/*.jsonregistration files - Instances register on startup, deregister on exit
- AI agent connects to hub once — sees all services
Just pipe each service through its own logpond instance:
# Terminal 1
api-server 2>&1 | logpond --config ./api.yaml
# Terminal 2
worker 2>&1 | logpond --config ./worker.yaml --mcp-port 9877Each instance gets its own TUI, its own config, and its own columns. The hub merges them all — the AI agent sees everything from one endpoint.
MIT