Tip: Read this guide via the MCP resource
agent-session-analytics://guidefor usage patterns and best practices.
Agent Session Analytics provides queryable analytics on Claude Code session logs. It parses
the JSONL files from ~/.claude/projects/ and stores them in SQLite for fast querying.
Use it to understand your Claude Code usage patterns, find workflow improvements, and
identify permission gaps.
| Tool | Purpose |
|---|---|
get_status() |
Database stats, last ingestion time |
ingest_logs(days?, project?, force?) |
Refresh data from JSONL files |
For setups where the database lives on a central server (e.g., via Tailscale):
| Tool | Purpose |
|---|---|
get_sync_status(session_ids?) |
Get latest timestamp per session for incremental sync |
upload_entries(entries, project_path) |
Upload raw JSONL entries from remote clients |
finalize_sync() |
Update session statistics after batch uploads complete |
CLI usage:
# Set remote server URL
export AGENT_SESSION_ANALYTICS_URL=https://server.tailnet.ts.net/mcp
# Push local session data (incremental - only sends new entries)
agent-session-analytics-cli push --days 365
# Force re-send all entries (re-populates raw_entries table)
agent-session-analytics-cli push --days 365 --forceThe push command queries get_sync_status() first to determine what the server already has, then only uploads entries newer than the server's latest per session.
Raw entry storage: All uploaded entries are stored in both parsed form (events table) and raw form (raw_entries table). This allows re-parsing historical data when the parser improves.
When projects are renamed, historical data doesn't match new filters. Project aliases solve this:
| Tool | Purpose |
|---|---|
add_project_alias(alias, target) |
Link an alias to a target pattern |
remove_project_alias(alias, target?) |
Remove alias (all targets if target omitted) |
list_project_aliases(alias?) |
List configured aliases |
Example: Your project was renamed from rust-genai to genai-rs:
add_project_alias("genai-rs", "rust-genai")
Now --project genai-rs will match both genai-rs AND rust-genai in all queries.
CLI usage:
# Add alias
agent-session-analytics-cli alias add genai-rs rust-genai
# List all aliases
agent-session-analytics-cli alias list
# Remove specific alias-target pair
agent-session-analytics-cli alias remove genai-rs rust-genai
# Remove all targets for an alias
agent-session-analytics-cli alias remove genai-rsNotes:
- Matching is case-insensitive (
GenAI-RSmatchesgenai-rs) - Aliases expand to OR clauses:
WHERE project_path LIKE '%genai-rs%' OR project_path LIKE '%rust-genai%' - Multiple targets can be added per alias (e.g., for projects renamed multiple times)
Cross-session knowledge events from the agent-event-bus (gotchas, patterns, improvement suggestions). Events are ingested from the co-located event-bus SQLite database and stored in both parsed form (bus_events table) and raw JSON form (raw_bus_events table) for future re-parsing.
| Tool | Purpose |
|---|---|
get_bus_events(days?, event_type?, repo?, session_id?, limit?) |
Query cross-session knowledge events |
ingest_bus_events(days?) |
Force refresh from event-bus database |
Key event types:
gotcha_discovered— Non-obvious bugs or pitfallspattern_found— Reusable solutions and techniquesimprovement_suggested— Workflow or tooling gap proposals
Example usage:
get_bus_events(event_type="gotcha_discovered", days=30)
get_bus_events(repo="agent-event-bus", limit=10)
Automatic ingestion: Bus events are ingested on server startup and every 5 minutes by the background loop. Use ingest_bus_events() to force an immediate refresh.
| Tool | Purpose |
|---|---|
get_tool_frequency(days?, project?, expand?) |
Tool usage counts with Bash/Skill/Task breakdown |
list_sessions(days?, project?, limit?) |
Session metadata and token totals |
get_token_usage(days?, by?, project?) |
Token usage by day, session, or model |
get_session_events(start?, end?, tool?, session_id?, limit?) |
Recent events with filtering |
get_file_activity(days?, project?, limit?, collapse_worktrees?) |
File reads/edits/writes breakdown |
get_projects(days?) |
Activity across all projects |
get_mcp_usage(days?, project?) |
MCP server and tool usage |
| Tool | Purpose |
|---|---|
get_tool_sequences(days?, min_count?, length?, limit?, expand?) |
Common tool chains (e.g., Read → Edit → Bash) |
sample_sequences(pattern, limit?, context_events?, expand?) |
Random samples of a pattern with surrounding context |
get_permission_gaps(days?, min_count?) |
Commands not covered by settings.json (supports glob patterns) |
get_insights(days?, refresh?) |
Pre-computed patterns for /improve-workflow |
expand: When True, expands tool names to specific variants:
- Bash → specific command (e.g., "git", "make")
- Skill → skill name (e.g., "commit", "pr-review")
- Task → subagent type (e.g., "Explore", "Plan")
Use get_tool_sequences(expand=True) to discover expanded patterns, then sample_sequences(pattern, expand=True) to get examples.
| Tool | Purpose |
|---|---|
analyze_failures(days?, project?) |
Failure patterns with drill-down to specific commands |
get_error_details(days?, tool?, limit?) |
Detailed errors with tool parameters (patterns, commands, files) |
analyze_failures() returns:
errors_by_tool: Count of errors per toolerror_examples: Top failing commands (Bash) or files (Edit/Read/Write) for drill-downrework_patterns: Files edited 3+ times within 10 minutes
get_error_details() shows which specific parameters caused failures:
- Glob/Grep: The pattern that failed (e.g.,
"*"with 922 errors) - Bash: The command that failed (e.g.,
pwdwith 492 errors) - Edit/Read/Write: The file path that failed
| Tool | Purpose |
|---|---|
classify_sessions(days?, project?, limit?) |
Categorize sessions with explanation of why |
Each session includes classification_factors explaining WHY it was categorized:
trigger: The threshold that was exceeded (e.g., "error_rate > 15%")- Relevant metrics (error_rate, edit_rate, etc.)
Each session also includes efficiency metrics:
compaction_count: Number of context resetstotal_result_mb: Total tool result sizefiles_read_multiple_times: Indicator of reworkburn_rate: "high", "medium", or "low" based on compactions/hour
| Tool | Purpose |
|---|---|
analyze_trends(days?, compare_to?) |
Token/event trends with efficiency metrics |
Returns both core metrics (events, sessions, errors, tokens) and efficiency metrics:
avg_compactions_per_session: Context resets per session (lower is better)avg_result_mb_per_session: Context consumption per sessionfiles_read_multiple_times: Rework indicator
| Tool | Purpose |
|---|---|
get_session_messages(days?, session_id?, limit?, entry_types?, max_message_length?) |
Messages across sessions chronologically (user + assistant by default) |
search_messages(query, limit?, entry_types?) |
Full-text search across all message types (FTS5) |
entry_types: Filter by ["user"], ["assistant"], ["tool_result"], ["summary"] or any combination.
get_session_messages: Default:["user", "assistant"](conversational context)search_messages: Default: all types (no filter) for comprehensive search
max_message_length: Truncate messages (default: 500, 0=no limit).
| Tool | Purpose |
|---|---|
detect_parallel_sessions(days?, min_overlap_minutes?) |
Find simultaneously active sessions |
find_related_sessions(session_id, method?, days?, limit?) |
Find sessions with similar patterns |
| Tool | Purpose |
|---|---|
ingest_git_history(days?, all_projects?) |
Ingest commits and auto-correlate with sessions |
get_session_commits(session_id?) |
Get commits associated with a session |
| Tool | Purpose |
|---|---|
get_session_signals(days?, min_count?) |
Raw session metrics for LLM interpretation |
get_handoff_context(session_id?, days?, limit?) |
Recent activity summary for session continuity |
| Tool | Purpose |
|---|---|
get_agent_activity(days?, project?) |
Task subagent activity vs main session (RFC #41) |
| Tool | Purpose |
|---|---|
get_compaction_events(days?, session_id?, limit?, aggregate?) |
List compaction events (context resets) |
get_large_tool_results(days?, min_size_kb?, limit?) |
Find tool results consuming context space |
get_session_efficiency(days?, project?, limit?) |
Session efficiency metrics and burn rate |
Context efficiency helps identify why sessions hit context limits:
- Compactions: Context resets when Claude summarizes conversation
- Large results: Tool outputs consuming significant context space
- Burn rate: How fast sessions consume their context budget
- Read/Edit ratio: High ratio suggests inefficient exploration (should use Task/Explore)
- Files read multiple times: Redundant reads indicate opportunity to cache context
get_status()
→ {last_ingestion: "2025-01-15T10:30:00", event_count: 5432, db_size_mb: 2.1}
ingest_logs(days=7)
→ {files_processed: 12, entries_added: 847, entries_skipped: 23}
The server automatically ingests local data on startup and every 5 minutes. For on-demand refresh (e.g., after pushing remote data), call ingest_logs() directly.
get_tool_frequency(days=30)
→ {tools: [{name: "Read", count: 1234}, {name: "Edit", count: 567}, ...]}
These are common patterns for using the analytics API. They're suggestions, not requirements— use the APIs however best fits your needs.
┌─────────────────────────────────────────────────────────────────┐
│ BROAD OVERVIEW │
├─────────────────────────────────────────────────────────────────┤
│ get_status() → Is data fresh? How many events? │
│ get_tool_frequency() → What tools are used most? │
├─────────────────────────────────────────────────────────────────┤
│ DISCOVER PATTERNS │
├─────────────────────────────────────────────────────────────────┤
│ list_sessions() → What sessions exist? │
│ get_session_signals() → Which sessions look interesting? │
│ classify_sessions() → What type of work (debug, dev, etc)? │
├─────────────────────────────────────────────────────────────────┤
│ DRILL INTO SPECIFICS │
├─────────────────────────────────────────────────────────────────┤
│ get_session_events(session_id=X) → Full event trace │
│ get_session_messages(session_id=X) → User+assistant messages │
│ get_session_commits(session_id=X) → Work products │
│ search_messages("query") → Find across all messages │
└─────────────────────────────────────────────────────────────────┘
| Question | Tools to Use |
|---|---|
| "What have I been working on?" | list_sessions() → get_session_messages() |
| "Why did session X struggle?" | get_session_signals(session_id=X) → get_session_events(session_id=X) |
| "What workflows can I automate?" | get_tool_sequences() → get_permission_gaps() |
| "How has my usage changed?" | analyze_trends() |
| "What did I do with feature X?" | search_messages("feature X") |
get_permission_gaps() → "Add these commands to settings.json"
get_tool_sequences() → "These patterns could be automated"
analyze_failures() → "These commands tend to fail"
analyze_trends() → "Usage is increasing/decreasing"
get_compaction_events() → "When did context resets happen?"
get_compaction_events(aggregate=True) → "Which sessions had most compactions?"
get_session_efficiency() → "Which sessions burn context fastest?"
get_large_tool_results() → "What operations consume the most space?"
classify_sessions() returns one of these categories, with classification_factors explaining why:
| Category | Criteria | Trigger Example |
|---|---|---|
| debugging | High error rate (>15%) or 5+ errors | "error_rate > 15%" |
| development | Heavy editing (>30% edits or 3+ writes) | "edit_rate > 30%" |
| maintenance | Git/build focus without much editing | "git_build_rate > 30%" |
| research | Mostly reading/searching codebase | "read_search_rate > 50%" |
| mixed | No dominant pattern | "no_dominant_pattern" |
get_permission_gaps() returns commands to add to ~/.claude/settings.json:
get_permission_gaps(min_count=5)
→ [{command: "npm", count: 23, suggestion: "Bash(npm:*)"}]
Add suggestions to permissions.allow in your settings.
Notes:
- Supports glob pattern matching. Patterns like
Bash(make*)will correctly match commandsmake,make-test, etc. using fnmatch. - Automatically filters non-actionable commands (shell builtins like
pwd,cd,echo, control flow likefor,if, and info commands likehostname,whoami) to reduce noise.
Git ingestion automatically correlates commits with sessions:
# Ingest from all known projects (recommended)
ingest_git_history(all_projects=True, days=30)
# Or from current repo only
ingest_git_history(days=30)
# Query results
get_session_commits(session_id="abc")
- Auto-ingestion: The server automatically ingests local JSONL files on startup and every 5 minutes. Use
get_status()to check last ingestion time. Manualingest_logs()is available for on-demand refresh. - Project filter: Most queries accept
project- uses LIKE matching, partial names work. - Day filters:
days=7for recent trends,days=30for patterns. - Permission gaps: Compare against
~/.claude/settings.json. Highermin_count= less noise. - Sequences:
length=3finds complex patterns but needs more data. - CLI parity:
agent-session-analytics-climirrors all MCP tools for terminal use.
| Item | Path |
|---|---|
| Database | ~/.claude/contrib/agent-session-analytics/data.db |
| Source logs | ~/.claude/projects/**/*.jsonl |
Ingestion is incremental - only changed files are re-parsed.