Toad Telemetry Skill
OpenTelemetry instrumentation for Batrachian Toad AI agent terminal.
Overview
Provides deep observability into Toad agent sessions:
- Per-tool spans: Every tool call (Bash, Read, Write, etc.) gets its own span
- Turn tracking: Prompt → response cycles with timing
- Session lifecycle: Start/stop with agent metadata
- Error tracking: Exceptions and error states captured
Installation
# Install Toad
uv tool install batrachian-toad --python 3.14
# Install OTEL SDK in Toad's environment
uv pip install opentelemetry-sdk opentelemetry-exporter-otlp \
--python ~/.local/share/uv/tools/batrachian-toad/bin/python3
# Install toad_telemetry library
mkdir -p ~/.local/lib/toad_telemetry
cp instrumentation.py ~/.local/lib/toad_telemetry/
cp __init__.py ~/.local/lib/toad_telemetry/
# Install wrapper script
cp toadia ~/.local/bin/
chmod +x ~/.local/bin/toadia
Usage
Via Wrapper Script (Recommended)
# Run toad with telemetry enabled
toadia -a claude .
# Run ACP agent with telemetry
toadia acp "my-command" .
# Disable telemetry
TOAD_TELEMETRY_ENABLED=0 toadia -a claude .
Programmatic
from toad_telemetry import instrument_toad, ToadTelemetryConfig
config = ToadTelemetryConfig(
service_name="my-toad-session",
endpoint="http://localhost:4317",
agent_name="claude",
project_dir="/path/to/project",
)
instrument_toad(config)
# Now run toad normally
from toad.cli import main
main()
Environment Variables
| Variable | Default | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4317 |
OTLP collector endpoint |
TOAD_TELEMETRY_ENABLED |
1 |
Enable/disable telemetry |
TOAD_TELEMETRY_CONSOLE |
0 |
Print spans to console |
TOAD_SERVICE_NAME |
toad |
Service name in traces |
Quick Start with Jaeger
# Start Jaeger
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
jaegertracing/all-in-one:latest
# Run toadia
toadia -a claude .
# View traces at http://localhost:16686
Span Attributes
Tool Spans (tool.<name>)
toad.tool.id- Unique tool call IDtoad.tool.name- Tool name (Bash, Read, Write, etc.)toad.tool.kind- Tool categorytoad.tool.status- Completion statustoad.tool.duration_ms- Execution timetoad.tool.error- Error message (if any)toad.session.id- Session identifiertoad.agent- Agent name
Turn Spans (agent.turn)
toad.prompt.length- Input prompt character counttoad.turn.stop_reason- Why the turn endedtoad.turn.duration_ms- Total turn timetoad.agent- Agent name
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Toad TUI │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Prompt │ │ Conversation│ │ Agent ACP │ │
│ └─────────────┘ └─────────────┘ └──────────┬──────────┘ │
│ │ │
│ ┌────────────────────────────────────────────┴───────────┐ │
│ │ toad_telemetry (monkey-patch) │ │
│ │ ┌───────────────────┐ ┌──────────────────────────┐ │ │
│ │ │ rpc_session_update│ │ send_prompt │ │ │
│ │ │ (tool spans) │ │ (turn spans) │ │ │
│ │ └─────────┬─────────┘ └────────────┬─────────────┘ │ │
│ └────────────┼─────────────────────────┼─────────────────┘ │
└───────────────┼─────────────────────────┼───────────────────┘
│ │
▼ ▼
┌──────────────────────────────────┐
│ OTEL Collector │
│ (Jaeger / Tempo / Honeycomb) │
└──────────────────────────────────┘
How It Works
The instrumentation uses deferred monkey-patching via Python import hooks to avoid circular imports:
instrument_toad()installs an import hook that waits fortoad.appto load- Once Toad is fully initialized, it patches
Agent.rpc_session_updateandAgent.send_prompt rpc_session_updatereceives all ACP protocol events (tool_call, tool_call_update, etc.)- Each tool call gets a span that tracks its lifecycle from start to completion
send_promptwraps each agent turn with timing and stop reason
Files
SKILL.md- This documentation__init__.py- Package entry pointinstrumentation.py- Core monkey-patching and OTEL integrationtoadia- Wrapper script with Jaeger quickstart help
Backends
Jaeger (Local Development)
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
jaegertracing/all-in-one:latest
Grafana Tempo
# With Grafana stack
docker-compose up -d # includes tempo, grafana
# View in Grafana Explore with Tempo datasource
Honeycomb (Cloud)
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io:443 \
OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=YOUR_API_KEY" \
toadia -a claude .
Troubleshooting
"OpenTelemetry not available"
Install the SDK in Toad's Python environment:
uv pip install opentelemetry-sdk opentelemetry-exporter-otlp \
--python ~/.local/share/uv/tools/batrachian-toad/bin/python3
No spans appearing
- Check collector is running:
docker ps | grep jaeger - Enable console export:
TOAD_TELEMETRY_CONSOLE=1 toadia ... - Verify telemetry enabled:
TOAD_TELEMETRY_ENABLED=1
gRPC connection errors
Try HTTP exporter (port 4318 instead of 4317):
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 toadia -a claude .
Related Skills
otel-cli- Command-line OTEL span emissionclaude_telemetry- Similar instrumentation for Claude Code CLI
ACP atlas
Part of: acp-commons.