Auditing and the watch viewer
execkit can record everything an agent does in the shell, and let you watch it live.
The audit log
Point the server at a destination and every open / exec / close event is
appended as JSON, with the session id, transport, an epoch-millisecond timestamp,
and the command plus its (redacted, bounded) output.
EXECKIT_MCP_AUDIT=/var/log/execkit.jsonlwrites one shared file for all sessions.EXECKIT_MCP_AUDIT_DIR=/var/log/execkit/writes one file per session, named<session_id>-<open_ms>.jsonl. This mode takes precedence when both are set.EXECKIT_MCP_AUDIT_RETENTION_DAYS(default 14,0disables) prunes per-session files older than N days at startup. Files with a future-skewed mtime are never deleted.
The audit destination is operator-chosen and never a tool argument, so an injected agent cannot redirect or suppress it.
The watch viewer
Point watch at the audit file or directory from another terminal:
execkit-mcp watch /var/log/execkit.jsonl # or: execkit-mcp watch (uses $EXECKIT_MCP_AUDIT)
execkit-mcp watch /var/log/execkit/ # a directory; uses $EXECKIT_MCP_AUDIT_DIR
It is a live, read-only TUI: the agent’s sessions on the left, the selected
session’s shell transcript on the right (prompt, command, stdout, stderr in red,
exit status), rendered like a normal shell rather than JSON. Command, opened,
closed and blocked lines start with their local time ([HH:MM:SS], following
$TZ), with a -- YYYY-MM-DD -- line when the date changes, as in the browser
viewer. Switch sessions with
1-9 or the arrow keys, scroll with PgUp/PgDn, quit with q. It only ever
reads the log. Because the data comes from the server, it works the same under
any MCP client.
Browser viewer
For a richer view in a normal browser tab, serve the transcript as a local web page:
execkit-mcp watch --serve /var/log/execkit/ # prints a loopback URL with a token
execkit-mcp watch --serve --open /var/log/execkit/ # ...and open it in your browser
The MCP server also starts the viewer automatically when EXECKIT_MCP_WATCH_WEB
is set: it binds 127.0.0.1 only, prints the tokened URL and pushes it to the
client as a notification, and keeps the URL stable across restarts so an open tab
reconnects. EXECKIT_MCP_WATCH_PORT sets the port (default 7878, falls back to
a random one if taken) and EXECKIT_MCP_WATCH_OPEN also opens the browser for
you.
The page is read-only and local by construction: it binds loopback only, every request needs the URL token, and it can read the audit stream but never touch a session, a command, or your files. What you get:
- Sidebar grouped by transport (
local/ssh/docker), then by host or target inside each transport (the ssh host or alias such asetlstage, the docker container, orlocal). Each group header shows its session count. Every session gets its own row: start time (with the date when it is not today),#<n>, theuser@hostlabel or your alias, the command count, and a green (live) or grey (closed) dot. The History list uses the same grouping. - Timestamps: each command line starts with the local time it started, for
example
[19:19:22] /tmp $ echo hi; hover it for the full date, time and timezone offset. The audit log records an exec event when the command finishes, so the viewer showsts - duration_ms. The opened, closed and blocked lines show their event time. A-- YYYY-MM-DD --line marks a date change, and the first one also appears at the top when a session did not start today. Exports and screenshots include the same times (JSON exports keep the rawtsin unix milliseconds plus the local time). - Colored transcript with a header legend (
cmd/out/err/ok). Click a legend item to show or hide that line type. - Search: press
/to find within the transcript, step matches withEnter/Shift+Enter, and presseto jump to the next error or blocked line. - History of past sessions, with relative times, when
EXECKIT_MCP_AUDIT_DIRis set. Sessions are grouped by transport, then by host, and listed newest first within each host. It shows the 20 newest sessions plus any you kept. It sits at the bottom of the sidebar, collapsed to aHistory (N)header, orHistory (20 of 57)when not every session is listed; click the header to expand it (the viewer remembers your choice), then click a session to read its transcript. With a singleEXECKIT_MCP_AUDITfile there is no per-session history. - Per-session actions from a 3-dots menu: rename (a display alias), pin, keep,
export to
.txt/.log/.md/.json, and screenshot to.png.
- A status bar that shows the selected session’s details; click it to copy the session id.
Rename / pin / keep and the sidebar width persist in ~/.execkit/viewer-state.json
(mode 0600). That file is the viewer’s only write surface: display metadata
only, never able to affect a session, a command, or the audit log.
Headless follow mode
For a pipeable, no-TTY view, use --follow instead of the TUI. It prints each
command and its output as a line prefixed with the session id, as it happens.
Command, opened, closed and blocked lines carry their local time, and a
-- YYYY-MM-DD -- line appears first when the events are not from today and
again whenever the date changes:
execkit-mcp watch --follow /var/log/execkit/
# [a3f9-1_local] [14:02:11] /home/u $ npm run build
# [a3f9-1_local] x exit 1 (3420ms)
# [a3f9-2_ssh_deploy@web-01] [14:02:40] /srv $ systemctl restart app
Live notifications in the client
Even with no audit log configured, the server streams each command to the MCP
client as it runs, so a host agent can surface its own shell activity without
anyone opening a separate terminal. Every session_exec emits:
- a log notification (
notifications/message) carrying the full shell transcript,infoon success andwarningon a non-zero exit; and - a progress notification (
notifications/progress) with a one-line summary, when the call supplied aprogressToken.
This reveals nothing new: the client already receives the same output in the tool result, redacted and bounded. How the activity is surfaced is up to the client.