SkillAgentSearch skills...

Agtap

Zero-instrumentation LLM API and MCP tracer for your agents powered by eBPF — latency, tokens, and tool use in realtime

Install / Use

/learn @zhebrak/Agtap
About this skill

Quality Score

0/100

Supported Platforms

Claude Code
Cursor

README

agtap

eBPF-based LLM API and MCP call tracer for Linux. Captures Anthropic, OpenAI, Google Gemini, and MCP traffic with zero application changes. Uses eBPF to intercept TLS via OpenSSL uprobes and I/O via kernel probes. OTel and Prometheus metrics out of the box.

agtap TUI screenshot

Requirements

  • Linux 5.8+ with BTF support
  • Root privileges for the daemon (TUI does not need root)
  • glibc 2.35+ (Ubuntu 22.04+, Debian 12+, Fedora 36+, etc.)

Install

Quick install:

curl -sSL https://raw.githubusercontent.com/zhebrak/agtap/main/install.sh | sudo sh

Manual download: grab the latest tarball and checksum from GitHub Releases, verify, and extract:

tar xzf agtap-v*.tar.gz
sudo mv agtap /usr/local/bin/

Build from source:

cargo build -p agtap --release

See CONTRIBUTING.md for full build instructions.

Quick start

Start the daemon (captures traffic and writes events):

sudo agtap

Connect the live dashboard (no root needed):

agtap tui

What it captures

  • Anthropic (/v1/messages) — streaming & non-streaming, tool use, cache tokens, error responses
  • OpenAI (/v1/chat/completions, /v1/responses) — streaming & non-streaming, function calls, error responses
  • Google Gemini (generateContent, streamGenerateContent) — streaming & non-streaming, function calls, cache tokens, error responses. Google AI and Vertex AI endpoints.
  • MCP (Model Context Protocol) — JSON-RPC 2.0 over stdio pipes and HTTP, including notifications

Per call: model, input/output tokens, latency, time-to-first-token, stop reason, tool names, streaming status, full request/response bodies.

Runtimes & frameworks

| Scenario | How | |----------|-----| | Python (requests, httpx, aiohttp) | OpenSSL via ssl module + glibc send/recv | | Node.js (official binaries, nvm, distro packages) | Statically linked OpenSSL with exported symbols, or shared libssl | | Node.js MCP servers (stdio pipes) | vfs_writev/vfs_readv kprobes (libuv) | | curl HTTPS (with --http1.1) | OpenSSL uprobes (curl defaults to HTTP/2; use --http1.1) | | curl, Ruby, Java plain HTTP | glibc send/recv uprobes | | musl libc (Alpine containers) | musl send/recv uprobes + vfs_writev/vfs_readv kprobes | | Rust with native-tls (OpenSSL) | OpenSSL uprobes | | LangChain, CrewAI, AutoGen (Python) | Same as Python | | Claude Code (MCP tool calls) | vfs_writev/vfs_readv kprobes (stdio pipes/socketpair) | | OpenClaw (Node.js) | Same as Node.js |

<details> <summary>What's NOT captured</summary>
  • Bun — uses BoringSSL with stripped symbols; not matched by uprobes
  • Deno — uses rustls (pure Rust TLS); no SSL_read/SSL_write symbols
  • Go agentscrypto/tls is built into the Go runtime; no OpenSSL symbols
  • Rust with rustls — pure Rust TLS; no standard symbol exports
  • Java/Kotlin TLS — uses JSSE, not OpenSSL (plain HTTP works via glibc)
  • Electron apps — BoringSSL via Chromium; same limitation as Bun (unless SSL symbols are exported, in which case it works)
  • Stripped/static binaries — if OpenSSL is statically linked without exported symbols, detection fails
  • HTTP/2 and HTTP/3 — only HTTP/1.1 is reassembled; h2 binary framing and QUIC are not parsed
  • Other API endpoints — embeddings, assistants, batch, image APIs are not parsed (only /v1/messages, /v1/chat/completions, /v1/responses, generateContent, streamGenerateContent)
</details>

Configuration

Flags

| Flag | Description | |------|-------------| | -p, --pid <PID> | Target a specific process | | -c, --comm <NAME> | Target by process name (e.g., python3) | | -v, --verbose | Show BPF loading and TLS discovery details | | --otel <ENDPOINT> | Export traces via OpenTelemetry (e.g., http://localhost:4318) | | --metrics-addr <ADDR> | Expose Prometheus metrics (e.g., 127.0.0.1:9464) | | --max-body-store <MB> | Max body storage on disk (default: 500 MB) | | --body-retention <HOURS> | Body file retention period (default: 24h) | | --max-log-size <MB> | Max event log size before rotation (default: 50) | | --max-log-files <N> | Max rotated log files to keep (default: 3) |

Environment variables

| Variable | Description | |----------|-------------| | AGTAP_LOG | Override default event log path (default: /tmp/agtap/events.jsonl) | | AGTAP_SOCK | Override default Unix socket path (default: /tmp/agtap/agtap.sock) |

Output

  • JSONL — to stdout and /tmp/agtap/events.jsonl
  • Bodies — full request/response payloads in /tmp/agtap/bodies/
  • TUI — live terminal dashboard via Unix socket
  • OpenTelemetry — OTLP/HTTP export
  • Prometheus/metrics endpoint

JSONL event schema

{
  "ts": "2023-11-14T22:13:21.000Z",
  "id": "msg_snapshot",
  "pid": 42,
  "proc": "python3",
  "provider": "anthropic",
  "server_address": "api.anthropic.com",
  "kind": "llm_call",
  "model": "claude-haiku-4-5-20251001",
  "input_tokens": 100,
  "output_tokens": 25,
  "latency_ms": 1000,
  "stop_reason": "end_turn",
  "tools": [],
  "streaming": false,
  "ttft_ms": 500,
  "request_bytes": 256,
  "response_bytes": 512
}

| Field | Type | Description | |-------|------|-------------| | ts | string | Wall-clock ISO 8601 timestamp | | id | string | API response ID (msg_..., chatcmpl-...) | | pid | number | Process ID | | proc | string | Process name | | provider | string | anthropic, openai, google, or mcp | | server_address | string | Target host | | kind | string | llm_call, tool_use, or mcp_call | | model | string | Model name or MCP method | | input_tokens | number? | Input tokens (absent for MCP) | | output_tokens | number? | Output tokens (absent for MCP) | | cache_creation_input_tokens | number? | Cache creation tokens (Anthropic) | | cache_read_input_tokens | number? | Cache read tokens (Anthropic, Gemini) | | latency_ms | number | Request-to-response latency in ms | | stop_reason | string | Stop reason (end_turn, stop, error:...) | | tools | string[] | Tool names used | | streaming | boolean | Whether response was SSE-streamed | | ttft_ms | number? | Time to first token (typically present for streaming) | | body_path | string? | Path to full HTTP body JSON | | request_bytes | number? | Raw request size | | response_bytes | number? | Raw response size |

Prometheus metrics

| Metric | Type | Labels | Description | |--------|------|--------|-------------| | agtap_request_duration_seconds | histogram | model, provider, process, kind, streaming | Call latency | | agtap_token_count | histogram | model, provider, process, kind, streaming, token_type | Token counts | | agtap_time_to_first_token_seconds | histogram | model, provider, process, kind, streaming | TTFT | | agtap_requests | counter | provider, process, kind, streaming, status | Total requests | | agtap_request_bytes | histogram | model, provider, process, kind, streaming | Request body size | | agtap_response_bytes | histogram | model, provider, process, kind, streaming | Response body size |

OpenTelemetry

Spans follow GenAI semantic conventions:

Span attributes: gen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.id, gen_ai.response.finish_reasons, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.cache_creation_input_tokens, gen_ai.usage.cache_read_input_tokens, server.address

Custom attributes: agtap.pid, agtap.process, agtap.kind, agtap.streaming, agtap.tools, agtap.ttft_ms, agtap.request_bytes, agtap.response_bytes

OTel metric instruments (different names from Prometheus):

| Instrument | Unit | Description | |------------|------|-------------| | gen_ai.client.operation.duration | s | Call latency | | gen_ai.client.token.usage | {token} | Token counts | | gen_ai.server.time_to_first_token | s | TTFT |

Troubleshooting

No SSL symbols detected — the target process's libssl must have exported symbols. Statically linked or stripped OpenSSL builds won't be matched. Check with nm -D /path/to/libssl.so | grep SSL_read.

Permission denied — the daemon requires root to load eBPF probes. Run with sudo agtap.

No events captured — verify the target process uses a supported TLS library (OpenSSL with exported symbols). BoringSSL (Bun, Electron), rustls (Deno), and Go's crypto/tls are not supported.

HTTP/2 traffic not captured — only HTTP/1.1 is supported. For curl, use curl --http1.1.

Uninstall

agtap clean
sudo rm /usr/local/bin/agtap

agtap clean removes runtime data (/tmp/agtap/). Then remove the binary.


See CONTRIBUTING.md for build instructions, testing, and project structure.

View on GitHub
GitHub Stars16
CategoryDevelopment
Updated16d ago
Forks2

Languages

Rust

Security Score

90/100

Audited on Mar 16, 2026

No findings