mizpah

tail -f is a slog. Pipe JSON into mzp for a live UI you can actually search, and agents that query the buffer instead of burning tokens on paste and re-runs.

One Homebrew install. Hub opens locally in under a second. No account, nothing in the cloud. You get a fast filterable stream; Cursor, Claude, Codex, and Grok pull small CEL slices over MCP when they need context.

$ brew install ethira-dev/tap/mizpah
$ my-app 2>&1 | mzp --service api
$ npx skills add ethira-dev/mizpah
mizpah
Filter with CEL… ⌘K
+
live Showing 0 / 0 stored
4 services

Why mizpah

A calmer log UX for you, and a cheaper way for agents to see the same buffer.

Teach your agent to save tokens

The Mizpah skill trains the agent to stop stuffing chat with log dumps and pretty-printed stdout. Prefer a JSON flag when the tool supports it, pipe into the hub, then filter with CEL so only the rows that matter enter context.

agent · paste dump

Why are requests failing? Here are the logs:

~48k tokens in context noise + one error

Without Mizpah

The agent reads the whole dump. Most of the context is noise; the bill isn’t.

agent · mizpah skill

Why are requests failing?

mcp__mizpah__search_logs
level == "error" · service=api · limit=5
entries[3]:
  - id: a1f3
    service: api
    data:
      level: error
      msg: "timeout calling redis"
  - id: b8c2
    service: api
    data:
      level: error
      msg: "redis: connection refused"
  - id: c0e9
    service: api
    data:
      level: warn
      msg: "retry exhausted after 3 attempts"
hasMore: false
~400 tokens in context TOON · only what matters

With the Mizpah skill

The skill teaches the agent to query CEL filters over MCP — small slices, not pasted transcripts.

agent · pretty-print scan

Which tests failed?

$ npm test
agent skims pretty stdout _raw · hard to filter

Pretty-print in the terminal

The agent runs the command, then reads a multi-line human dump. Failures are buried in chrome the model has to re-parse every time.

agent · JSON → mizpah

Which tests failed?

$ npm test -- --json 2>&1 | mzp --service tests
mcp__mizpah__search_logs
level == "error" · service=tests · limit=5
entries[2]:
  - id: e91a
    service: tests
    data:
      level: error
      msg: "redis › reconnects on timeout"
  - id: f2b0
    service: tests
    data:
      level: error
      msg: "Expected true, Received false"
hasMore: false
structured fields CEL → TOON

JSON flag + Mizpah filter

When the tool can emit JSON, the skill prefers that, pipes into mzp, then filters — so the agent gets fields, not a pretty-printed wall of text.

$ npx skills add ethira-dev/mizpah
$ mzp mcp install

Full walkthrough in MCP & agents.

How it fits together

Sources ingest into one hub. You watch it in the web UI; agents read the same buffer over MCP without another full command run.

schema ingest -> hub -> expose bind 127.0.0.1:3149

01 sources

  • Chrome CDP
    mzp attach browser console.* + network
  • Cursor hooks
    mzp attach cursor ~/.cursor/hooks.json
  • Claude hooks
    mzp attach claude ~/.claude/settings.json
  • Shell tee
    mzp attach shell stdout/stderr -> ingest
  • Pipe stdin
    app 2>&1 | mzp JSON / NDJSON / _raw

02 hub

mizpah
axum · ring buffer · :3149
/api/ingest /api/logs /ws mcp stdio
shared in-memory buffer (1 GiB default)

03 expose

  • Web UI HTTP
    GET / · WS /ws 127.0.0.1:3149
  • MCP stdio
    mzp mcp search_logs · get_logs_around
fan-in: 5 sources · fan-out: ui + mcp · no cloud egress
Chrome, Cursor, Claude, shell, and anything you pipe in land in one local hub. You browse it in the web UI; agents query the same buffer over MCP with small slices instead of pastes and re-runs.

Agents ask the hub

With mzp mcp install, Claude Code, Codex, and Grok call search_logs for a few matching rows. Feels like the agent can see your terminal; costs like a small tool call instead of a dumped transcript.

Claude Code
Claude Code v2.1.206
Welcome back lucas!
Opus 4 with high effort · Claude Max
mizpah demo
~/dev/api
Tips for getting started
Ask Claude to search the live mizpah buffer over MCP
Keep limits small: search_logs default 20, max 50
What's new
mizpah MCP: search_logs / get_logs_around return TOON
CEL filters: level == "error", msg.contains("timeout")
/release-notes for more
MCP server mizpah connected · search_logs ready
what errors did api emit in the last few minutes?
search_logs(service: "api", q: level == "error", limit: 5)3 matching logs (TOON)(ctrl+o to expand)
entries[3]: - id: 1842 service: api data: level: error msg: "POST /api/ingest failed: connection refused" - id: 1837 service: api data: level: error msg: "timeout waiting for redis at 127.0.0.1:6379" - id: 1829 service: api data: level: error msg: "Unhandled exception in JobsController" hasMore: false
get_logs_around(id: 1837, before: 2, after: 2)5 logs around #1837 (TOON)(ctrl+o to expand)
entries[5]: - id: 1826 data: level: info msg: "redis pool warm" - id: 1835 data: level: warn msg: "redis latency p99=210ms" - id: 1837 data: level: error msg: "timeout waiting for redis at 127.0.0.1:6379" - id: 1838 data: level: info msg: "retry scheduled in 250ms" - id: 1840 data: level: info msg: "request completed 503" hasMore: false
Three api errors recently. The redis timeout at #1837 is the spike: latency warning just before, then a 503 and a retry. The ingest connection refused and JobsController exception look separate.
high · /effort
auto mode on (shift+tab to cycle) · ← for agents

Faster loops, lower bills

Tests, lint, and debug get messy when every question means another run or another paste. Pipe once into mizpah. Browse the stream yourself; let agents search the same output without starting over.

  • Tests

    Run the suite into mzp. Jump straight to failures in the UI, or have the agent pull them with search_logs instead of waiting on another full run.

  • Linting

    One lint pass fills the hub. Skim warnings in a filterable list, or let agents read them from the buffer without re-invoking the linter and stuffing stdout into context.

  • Debugging

    Keep the app streaming. Open a row for JSON context, or call get_logs_around for a tight slice. No more hunting a stack frame inside a 10k-line paste.

Clearer workflow for you, fewer tool rounds and thinner context for agents. Same idea in Claude Code, Codex, Grok, or Cursor.

Get going in 30 seconds

Homebrew, pipe a service, then install the agent skill so Cursor and friends query the hub instead of pasting dumps.

# install mizpah
brew install ethira-dev/tap/mizpah

# pipe a service
api-server 2>&1 | mzp --service api

# agent skill (token-saving workflow)
npx skills add ethira-dev/mizpah

# MCP tools (Cursor / Claude / Codex)
mzp mcp install

# or attach
mzp attach              # shell
mzp attach browser --launch
mzp attach cursor
mzp open