Skip to main content
Glama
agent-activity

agent-activity

by patrickdaj

agent-activity

A read-only MCP server that exposes your local coding-agent session logs (the JSONL transcripts written by Claude Code at ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl) as three tools, so you — or an agent — can introspect recent work, debug tool failures, and track token/estimated cost without hand-parsing log files.

Guarantees

  • Read-only. The server only ever reads log files; it never writes to, deletes, or otherwise mutates anything under the log root.

  • Local. All data comes from the filesystem on the machine the server runs on.

  • No network. The server never makes an outbound network call — not for logs, not for pricing data, not for telemetry. Pricing is a local, configurable table (see Cost is an estimate).

  • stdio transport. Standard MCP over stdio, via the official mcp Python SDK (FastMCP).

Related MCP server: log-mcp

Tools

list_recent_sessions(limit=20, repo=None)

Recent sessions, most-recently-active first.

  • limit — max sessions to return (default 20, max 200).

  • repo — optional substring filter against the session's repo/cwd.

Returns, per session: session_id, repo, cwd, git_branch, start/ end timestamps, message_count, schema_status ("verified" or "unverified" — whether the log's shape matched what this server expects; degrades gracefully rather than failing on drift), and a usage block (total_tokens, total_cost, cost_status, and a main/sidechain breakdown — see Sidechains).

tail_agent_log(session, limit=100, include_current=False, cwd=None)

The tail of one session's transcript, oldest-first.

  • session — a full session UUID, a unique UUID prefix, or "latest" (see Session resolution).

  • limit — max entries to return (default 100, max 1000).

  • include_current — when resolving "latest", whether to allow it to resolve to the session currently calling this tool (default False).

  • cwd — optional exact working-directory path; scopes "latest" resolution and current-session detection to sessions from that directory.

Returns session_id, repo, schema_status (as in list_recent_sessions), and a list of entries, each with type, timestamp, any tool_calls (tool_use_id, tool_name, and outcome — ok/error/pending, joined against the whole session so a call's outcome is known even when its result entry falls outside the returned tail) and tool_results (tool_use_id, is_error), and — for assistant entries that carry usage — a usage block with model, per-entry tokens, and estimated cost.

summarize_tool_calls(session, cwd=None)

Per-tool aggregate for a session: invocation counts, error counts, which calls failed, and total usage.

  • session — a full session UUID, a unique UUID prefix, or "latest" (resolved including the current session — summarizing your own live session is a valid use case here).

  • cwd — optional exact working-directory path; scopes "latest" resolution and current-session detection to sessions from that directory.

Returns session_id, repo, schema_status (as in list_recent_sessions), is_live_session, per_tool (per tool name: ok/error/pending/in_progress counts), totals (the same breakdown across all tools, plus rejected), by_source (main vs sidechain totals), failing_calls (each labeled error or rejected), and a session-level usage block (tokens + estimated cost).

All three tools return a structured {"error": ...} dict (never raise) when a session argument is ambiguous ("ambiguous_session", with candidates) or matches nothing ("session_not_found").

Cost is an estimate, not a bill

Session logs record token counts (message.usage) but not dollar cost. Cost is computed by multiplying token counts by a per-model rate table (pricing.py) — order-of-magnitude, hand-maintained figures that will drift as providers change pricing. Treat any cost field as a ballpark to sanity-check spend, not an authoritative bill. Unknown models degrade to cost_status: "unknown" (tokens are still reported) rather than raising.

Override the table with your own by pointing AGENT_ACTIVITY_PRICING_FILE at a JSON file shaped like:

{
  "claude-sonnet-5": {
    "input": 3.00,
    "output": 15.00,
    "cache_write": 3.75,
    "cache_read": 0.30
  }
}

Rates are USD per 1,000,000 tokens. A model listed in the override file fully replaces that model's default entry; models you don't mention keep their built-in defaults.

Sidechains and subagents

Subagent ("sidechain") activity lives in separate sibling files (<session-dir>/subagents/agent-*.jsonl), not inline in the main session file. This server associates those files with their parent session and includes their tool calls and token usage in summarize_tool_calls and list_recent_sessions, but keeps every count and token total tagged main vs sidechain so totals stay decomposable rather than blended.

Sidechain usage is summed only from the sidechain files' own message.usage entries. The parent log's separate rollup fields (toolUseResult.totalTokens / the <usage>...</usage> text tag on the spawning Agent tool's result) are a second, independently-computed aggregate of the same work — summing both would double-count, so those fields are never added into the totals here.

Config

Env var

Default

Purpose

AGENT_ACTIVITY_LOG_ROOT

~/.claude/projects/

Root directory to scan for */*.jsonl session logs.

AGENT_ACTIVITY_PRICING_FILE

(none — built-in table)

Path to a JSON pricing-table override (see above).

AGENT_ACTIVITY_CALLER_SESSION_ID

(none — falls back to a heuristic)

The calling session's own id, if your MCP client can supply it. Used to reliably identify "the current session" for "latest" resolution; without it, the server falls back to a newest-mtime heuristic (documented limitation: can misfire with concurrent sessions).

Install

This project uses uv. With uv installed (curl -LsSf https://astral.sh/uv/install.sh | sh), from the repo root:

uv sync

This creates a locked virtual environment (.venv/) from the committed uv.lock, pins the interpreter via .python-version, and installs the agent-activity package plus its one runtime dependency (mcp).

Fallback (no uv): pip install -e . still works and installs the agent-activity console script and its mcp dependency — it just isn't locked or interpreter-pinned.

Run

Any of the following start the same stdio server (via uv, no manual activation needed):

uv run agent-activity
uv run python -m agent_activity
uv run python -m agent_activity.server

If you've activated the environment (or installed with pip install -e .), you can drop the uv run prefix and invoke agent-activity / python -m agent_activity directly.

The process speaks MCP over stdio (stdin/stdout) — it's meant to be launched by an MCP client, not run interactively.

Register with an MCP client

An example client registration is checked in at .mcp.json:

{
  "mcpServers": {
    "agent-activity": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/agent-activity-mcp-server", "agent-activity"],
      "env": {
        "AGENT_ACTIVITY_LOG_ROOT": "~/.claude/projects"
      }
    }
  }
}

uv run resolves the project (and its locked environment) from a working directory, so set --directory to the absolute path of this repo — MCP clients launch the command from an arbitrary cwd. This form doesn't require the console script to be on PATH.

Alternatively, if you've installed the package into the environment your MCP client uses (e.g. via pip install -e .), point command directly at the agent-activity console script (with args: []), or use python with args: ["-m", "agent_activity"] to invoke the module.

Example tool calls

Once registered, a client can call e.g.:

list_recent_sessions(limit=5)
tail_agent_log(session="latest", limit=50)
summarize_tool_calls(session="latest")

or target a specific session by its full UUID or an unambiguous prefix:

tail_agent_log(session="4140dfe3", limit=200)

How session resolution works

The session argument to tail_agent_log and summarize_tool_calls accepts:

  • a full session UUID — exact match against the log filename stem;

  • a unique UUID prefix — resolves if exactly one discovered session id starts with it; an ambiguous prefix (matches more than one) returns a structured error listing the candidates rather than guessing;

  • "latest" — the most-recently-active session (newest log file mtime). By default this excludes the session currently calling the tool, so tail_agent_log(session="latest") doesn't tail its own still-being-written log; pass include_current=True to opt back in. (summarize_tool_calls resolves "latest" including the current session by default, since summarizing your own live session is a normal thing to want.)

Identifying "the current session" is inherently a heuristic — an MCP server isn't told its caller's session id by the protocol. If the AGENT_ACTIVITY_CALLER_SESSION_ID env var is set, it's used directly (authoritative). Otherwise the server falls back to "the session with the newest file mtime" (optionally scoped to the caller's cwd), which can misfire if two sessions are active at once.

Development

uv sync installs the dev dependencies (the dev dependency group, which includes pytest) alongside the runtime deps. Run the tests with:

uv sync
uv run pytest tests/ -q

An end-to-end stdio smoke test (spawns the real server as a subprocess, performs the MCP handshake, and calls all three tools against real logs) lives at scripts/smoke_test.py:

uv run python scripts/smoke_test.py

Available Tools

3 tools
list_recent_sessionsA

List recent coding-agent sessions, newest first.

Discovers session logs under the configured log root (default ~/.claude/projects/) and returns up to limit sessions (default 20, max 200), most-recently-active first. Each entry includes the session id, repo/cwd, git branch, start/end timestamps, message count, schema-verification status, and a token/cost usage rollup (total tokens, estimated cost, cost status, and the main-vs-sidechain split). Pass repo to filter to sessions whose cwd/repo directory name contains that substring. Read-only; never writes to or modifies the underlying log files.

Inputs: repo, limit. Returns: result

summarize_tool_callsA

Summarize a session's tool calls: per-tool outcomes, failures, usage.

session accepts a full session UUID, a unique UUID prefix, or "latest" (resolved including the current session, since summarizing "my own" live session is a valid use case here). Returns per-tool invocation counts broken down by outcome (ok / error / pending / in_progress), overall totals, a main/sidechain split (tool calls made by subagents are included and tagged, never blended in untagged), the list of failing calls (each labeled error or rejected for permission denials), and the session's total token/cost usage. If this session is currently live (the most recently active discovered session), its trailing pending calls are labeled in_progress instead of pending. If session is ambiguous or matches no session, returns a structured {"error": ...} dict instead of raising. Read-only.

Inputs: session (required).

tail_agent_logA

Return the tail of a session's transcript, oldest-first.

session accepts a full session UUID, a unique UUID prefix, or the keyword "latest". By default "latest" excludes the session currently calling this tool (so it never tails its own still-being-written log); pass include_current=True to opt in. Returns up to limit entries (default 100, max 1000) in chronological order. Each entry reports its type and timestamp; assistant entries list any tool(s) called and, for entries carrying usage, per-entry token counts and estimated cost; tool-result entries report each result's tool_use_id and whether it was an error. If session is ambiguous (a prefix matching multiple sessions) or matches no session, returns a structured {"error": ...} dict instead of raising. Read-only.

Inputs: session (required), limit, include_current.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 3 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: listing sessions, summarizing tool calls within a session, and tailing session transcripts. There is no overlap or ambiguity.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun snake_case pattern (list_recent_sessions, summarize_tool_calls, tail_agent_log), making them predictable and clear.

    Tool Count4/5

    Three tools is slightly low but appropriate for the narrow domain of inspecting agent sessions. The set covers the essential operations without superfluous tools.

    Completeness4/5

    The tool surface covers listing sessions, inspecting tool calls, and viewing transcripts. A minor gap is the lack of a dedicated 'get session details' tool, but the list tool returns metadata, and summary/tail provide depth.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Local-first dashboard + MCP server that parses Claude Code and Codex JSONL files into a SQLite cost / token tracker. Per-MCP and per-tool breakdown, session drill-down, dedup by request_id; never talks to vendor APIs
      5
      104 npm
      1
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      MCP server for log file analysis. Gives LLMs the ability to efficiently analyze large log files without loading them into context.
      7
      101
      MIT
    • F
      license
      Not graded
      quality
      A
      maintenance
      Local MCP server that lets your AI coding agent query its own cross-tool project history - file/command freshness, past test failures, cost & token spend, cache status, and session handoff - over stdio, 100% local, no telemetry.
      58
      -