Local stdout/stderr capture for AI coding agents.
Run any process under trail, then ask Codex, Claude Code, Cursor, Windsurf, or another MCP-capable agent to query the captured logs without pasting terminal output into chat.
Single static binary. Plain JSONL files on disk. No daemon, no database, no cloud.
Trail is most useful when the agent needs to move beyond "read the last error" and run a disciplined debugging loop: inspect captured logs, keep a ledger, add temporary probes only when needed, wait for the dev server to restart, reproduce the issue, verify the fix, and remove every probe.
The important constraint is cleanup. Every temporary log line carries a unique TRAIL-DEBUG-<id> marker, and the workflow is not done until the agent verifies that marker is gone from the repo.
All integrations need the trail binary on your PATH first:
curl -fsSL https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | sh
trail versionThen add the integration for your agent.
codex plugin marketplace add Pratham-Mishra04/trail --ref main
codex plugin add trail@prathamStart a new Codex thread, then ask:
Use Trail to list my captured sessions.
/plugin marketplace add Pratham-Mishra04/trail
/plugin install trail@pratham
/reload-plugins
Verify by asking:
What skills are available?
You should see pratham:trail:debug-with-trail.
Use the manual MCP config in Editor Setup. You will get the list_sessions and get_logs tools, but not the bundled Codex/Claude guided debugging skill.
Wrap the process you want to debug:
trail run -- npm run devTrail prints a session id and file path, then forwards your app's stdout/stderr normally while also capturing every line:
trail: capturing "npm run dev" -> 39f6875e-3417-4146-867b-c430971b7489 (file: /Users/you/.config/trail/sessions/39f6875e-3417-4146-867b-c430971b7489.jsonl)
Now ask your agent:
List my Trail sessions, then show me the errors from the most recent one.
Or query from the terminal:
trail sessions
trail logs --session 39f6875e-3417-4146-867b-c430971b7489 --level error
trail logs --session 39f6875e-3417-4146-867b-c430971b7489 --query "ECONNREFUSED"The agent calls list_sessions and get_logs over MCP. Trail filters the logs in the Go process and returns structured results instead of a wall of text.
- Process capture:
trail run -- <cmd>wraps any command, captures stdout/stderr, forwards signals, and mirrors the child's exit code. - Docker capture:
trail docker <container>capturesdocker logs -ffor an already-running container. - Agent tools:
trail mcpexposeslist_sessionsandget_logsover stdio MCP. - Human tools:
trail sessionsandtrail logs --session <id>expose the same query surface in the terminal. - Local files: captured logs are plain JSONL under
~/.config/trail/sessions/.
The Codex and Claude Code plugins ship a debug-with-trail skill for runtime debugging. It turns Trail into a repeatable agent workflow for server crashes, failing tests, silent failures, and "what happened in the logs?" investigations.
Ask in plain English:
My Express server returns 500 on POST /orders. Use Trail to debug it.
The skill walks the agent through:
- Prerequisites: verify
trailis installed and the relevant process is being captured. - Read existing logs first: query errors, recent output, and relevant terms before touching code.
- Instrument only when needed: add temporary targeted log lines with a unique
TRAIL-DEBUG-<id>marker. - Verify the fix: query the marker output after the change, not just static code.
- Clean up: remove every temporary log line and verify
rg TRAIL-DEBUG-<id>returns nothing.
The cleanup rule is the important part: every added probe carries a unique marker, and the agent is instructed to remove the whole log statement after the fix is verified.
Read the exact skill instructions:
- Debugging a running app: wrap
npm run dev,python manage.py runserver,go run ., or any other server and let the agent query only the relevant logs. - Investigating long test runs:
trail run -- go test -v ./...,trail run -- pytest -v, ortrail run -- npm test, then ask what failed and why. - Build, lint, and typecheck output: capture long compiler or linter output once, then filter by level, file name, symbol, or regex.
- Comparing flaky runs: capture a passing run and a failing run, then ask the agent what changed.
- Long-running scripts and migrations: keep a permanent, queryable local record instead of relying on terminal scrollback.
- Noisy Docker containers: use
trail docker <name>to make container logs queryable without flooding the agent context.
In every case, the pattern is the same: attach terminal output to your agent by handing it a session id, instead of piping logs through prompts.
The Codex and Claude Code plugins are the recommended setup because they include both MCP wiring and the guided debugging skill.
For manual MCP setup, make sure trail is on the $PATH of the shell that launches your editor. macOS GUI apps do not always inherit your shell path; if the MCP server fails to start, check which trail from the editor's terminal.
claude mcp add trail -s user -- trail mcpThis path wires up the raw MCP tools (list_sessions, get_logs) only — it does not include the bundled debug-with-trail skill.
.cursor/mcp.json:
{
"mcpServers": {
"trail": {
"command": "trail",
"args": ["mcp"]
}
}
}Settings -> MCP:
{
"mcpServers": {
"trail": {
"command": "trail",
"args": ["mcp"]
}
}
}claude_desktop_config.json:
{
"mcpServers": {
"trail": {
"command": "trail",
"args": ["mcp"]
}
}
}curl -fsSL https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | shThe installer detects your OS/arch, downloads the matching prebuilt binary from the latest GitHub Release, verifies its SHA-256 checksum, and installs trail to ~/.local/bin/trail.
If ~/.local/bin is not on your $PATH, the installer prints the exact export line to add. Override the destination with BIN_DIR=/usr/local/bin or pin a version with VERSION=v0.1.0.
wget works too:
wget -qO- https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | shARCHIVE=trail_darwin_arm64.tar.gz
BASE=https://github.com/Pratham-Mishra04/trail/releases/latest/download
wget "$BASE/$ARCHIVE"
wget "$BASE/checksums.txt"
grep " $ARCHIVE\$" checksums.txt | shasum -a 256 -c -
tar xzf "$ARCHIVE"
sudo mv trail /usr/local/bin/Available archives:
trail_darwin_arm64.tar.gztrail_darwin_amd64.tar.gztrail_linux_arm64.tar.gztrail_linux_amd64.tar.gz
go install github.com/Pratham-Mishra04/trail@latestGo's go install does not inject release metadata, so trail version may print trail dev (commit none, built unknown). The binary still works normally.
git clone https://github.com/Pratham-Mishra04/trail
cd trail
make installThis runs go install with version metadata baked in. The binary lands at $(go env GOPATH)/bin/trail.
macOS and Linux on amd64/arm64. Windows is not supported.
When an agent reads raw log files into its context to filter them, every query pays for re-loading the file's tokens through the LLM. Trail filters in the Go process and returns only matching entries.
This means:
- The agent does not burn context on log lines that will not matter.
- Regex and level filters are deterministic.
- Latency is bounded by the filter work, not token generation speed.
| Source | Command | Notes |
|---|---|---|
| Wrapped command | trail run -- <cmd> |
Wraps any binary; separate stdout/stderr pipes preserve stream attribution. |
| Docker container | trail docker <name> |
Wraps docker logs -f; passes --since through. |
Both commands accept --name <label> to override the auto-derived session name and --ephemeral to delete the session file when the capturer exits cleanly. The file survives a SIGKILL; only graceful shutdown triggers cleanup.
Trail does not capture:
- A bare PID you did not start under Trail. That would require
ptraceoreBPF. - Arbitrary log files written to disk by another tool. Use
tail,lnav, or normal file tools for those.
The MCP server exposes two tools:
list_sessions(active_only?, limit?): returns session metadata including the absolute file path of each session JSONL file, ordered active-first.get_logs(session_id, filters?): returns matching entries.
get_logs filters include:
limit,page, andorderqueryas a case-insensitive regexlevel:error,warn,info,debug,unknown, orallstart_time/end_timeas RFC3339 timestampsdurationas a Go-style duration, such as"10m"or"2h"start_line/end_line
Time-window, duration, and line-range filters are mutually exclusive. Combining them returns an error.
If a result looks incomplete, the response includes the absolute file_path and the agent can read the JSONL file directly with its file tools.
trail sessions # table
trail sessions --json # JSON for scripting
trail sessions rm <id> # delete one
trail sessions rm --all # delete all
trail logs --session <id> # last 100, pretty
trail logs --session <id> -n 50 # last 50
trail logs --session <id> --level error # errors only
trail logs --session <id> --query "ECONNREFUSED" # case-insensitive regex
trail logs --session <id> --format json # raw JSON entriestrail logs --session is required. There is no implicit "most recent" default; the MCP tool follows the same rule.
- Two-process model: capture processes (
trail run/trail docker) own session files. The MCP server is read-only and spawned by the editor on demand; it never starts, modifies, or stops captures. - One JSONL file per session: files live at
~/.config/trail/sessions/<uuid>.jsonl. The first line is a meta header; every subsequent line is one captured entry. - Append-only writes + tolerant reader: entries are appended to a regular file (
os.OpenFile(O_APPEND));maxRawLencaps theRaw/Messagefields but not the whole marshaled JSON line, so concurrent reads rely on the reader being tolerant of malformed or partial trailing lines (seeinternal/store/reverse.goandTestRead_TolerantOfPartialLastLine) rather than on kernel-level atomicity. - Server-side filtering: cheap filters run before full JSON decode; eligible newest-first queries (
Order=newest,Limit>0,Page≤1, no time/line bounds) use a reverse scan from the end of the file. - Conservative log-level detection: Trail only assigns a level when there is clear evidence, such as a JSON
levelfield, logfmtlevel=, or an anchored prefix likeERROR:.
MIT
