pylow
One CLI. Zero code changes. Full system flow visibility for any Python service or distributed system.
Version 0.1.14 — 118 MCP tools + 118 CLI commands across kernel tracing, Python internals, JSON analysis, HTTP tooling, protocol diagnostics, pipeline patterns, distributed workflow orchestration, and MCP server.
Installation & Setup
pip install pylow
MCP Server
pylow ships a fully-featured Model Context Protocol server — every CLI command is available as an MCP tool, callable directly from Claude Desktop, Cursor, or any MCP-compatible client.
Run locally (stdio — Claude Desktop)
pip install pylow
pylow-mcp --stdio
Add to claude_desktop_config.json:
{
"mcpServers": {
"pylow": {
"command": "pylow-mcp",
"args": ["--stdio"]
}
}
}
Run via Docker (Streamable-HTTP — MCP Inspector / remote clients)
# Pull and run
docker run -p 8765:8765 chiefj/pylow-mcp:latest
# Full stack: MCP server + Inspector UI + Jaeger + Prometheus
docker compose up
# Then open MCP Inspector at http://localhost:6274
Add to MCP client config (HTTP transport):
{
"mcpServers": {
"pylow": {
"url": "http://localhost:8765/mcp"
}
}
}
Docker
# Pull the image
docker pull chiefj/pylow-mcp:latest
# Run standalone MCP server
docker run --rm -p 8765:8765 chiefj/pylow-mcp:latest
# Run with stdio transport (pipe into Claude)
docker run --rm -i chiefj/pylow-mcp:latest pylow-mcp --stdio
# Full observability stack (MCP + Inspector + Jaeger + Prometheus)
docker compose up --build
# Include optional webhook receiver
docker compose --profile webhook up
Ports exposed by docker-compose:
| Service | Port | URL |
|---|---|---|
| pylow MCP server | 8765 | http://localhost:8765/mcp |
| MCP Inspector UI | 6274 | http://localhost:6274 |
| Jaeger trace UI | 16686 | http://localhost:16686 |
| Prometheus | 9090 | http://localhost:9090 |
| Webhook receiver | 9000 | http://localhost:9000 |
Image: chiefj/pylow-mcp — multi-stage build on python:3.10-slim. Includes curl, jq, tcpdump, openssl, iproute2 for the diagnostic sub-process pipelines. Non-root pylow user.
How It Works
Three layers working together:
Layer 1 → OTel auto-instrumentation (HTTP, DB, gRPC, queues — zero code changes)
Layer 2 → bpftrace USDT (Python function call tree, syscalls, kernel)
Layer 3 → pylow CLI (stitches both, renders the flow)
Quick Start
# Find the PID of your process first
pgrep -f "your_app.py"
# Attach and start collecting
pylow attach <PID>
# Render the execution flow
pylow flow --last
# 10-second triage — tells you exactly what category problem you have
pylow triage <PID>
Complete Command Reference
All 113 commands, grouped by category. Every command is also available as an MCP tool via pylow-mcp.
CATEGORY 1 — Core Tracing
pylow attach <PID>
Attach to any running Python process and start collecting trace logs.
pylow attach 4821
✓ Attached to process 4821. Monitoring execution events...
pylow flow [--last]
Render the full execution flow tree from collected spans.
pylow flow --last
handle_request 450ms
├── authenticate_user 20ms
│ └── [redis GET session_id] 15ms
└── call_llm_chain 410ms
└── [POST api.openai.com/v1/chat/completions] 400ms
└── waiting (epoll_wait) 390ms ← bottleneck
pylow stitch --services <list>
Stitch distributed traces together across service boundaries.
pylow stitch --services api,worker,ml-service
REQUEST trace-id: t_demo_flow_123
api-gateway 450ms handle_request
└── api-gateway 20ms authenticate_user
└── api-gateway 15ms redis GET session_id
pylow slow [--threshold <ms>] [--watch]
Surface slow execution paths exceeding a latency threshold.
pylow slow --threshold 200ms --watch
Continuous monitoring daemon started. Threshold: 200ms
SLOW PATHS detected (last 5 min):
#1 handle_request → call_llm_chain → [waiting (epoll_wait)]
avg: 390ms occurrences: 1
root cause: epoll_wait 310ms — network latency
pylow diff --before <id> --after <id>
Compare execution flow metrics between two releases to detect regressions.
pylow diff --before deploy-v1.2 --after deploy-v1.3
REGRESSIONS:
handle_request +150ms avg (was 200ms, now 350ms)
serialize +12ms avg (was 3ms, now 15ms)
NEW CALLS in v1.3:
validate_schema 8ms
REMOVED in v1.3:
legacy_cache_check
CATEGORY 2 — Kernel & Syscall Tracing
pylow syscall <PID>
Trace syscall counts and histogram latency.
pylow syscall 4821
--- BPF Map: @sys_counts ---
sys_enter_read: 231
sys_enter_write: 184
sys_enter_epoll_wait: 42
pylow malloc <PID>
Profile memory allocation sizes and callers.
pylow malloc 4821
--- BPF Map: @alloc_sizes (bytes) ---
[64, 127] 45 |@@@@@@@@@@@ |
[512, 1023] 120 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[4096, 8191] 18 |@@@@ |
pylow tcp <PID>
Trace outbound TCP sendmsg latency.
pylow tcp 4821
--- BPF Map: @tcp_send_us (ns delay) ---
[100000, 200000] 21 |@@@@@@@@@@ |
pylow io <PID>
Trace File and Block I/O read/write latencies.
pylow io 4821
--- BPF Map: @read_lat (ns) ---
[4096, 8191] 150 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[16384, 32767] 23 |@@@@@ |
pylow flame <PID> [--duration <sec>]
Generate CPU sampling flame graphs (SVG).
pylow flame 4821 --duration 5
✓ Sampling for 5s...
✓ Saved flame graph to flamegraph.svg
pylow sched <PID>
Trace runqueue scheduler latency.
pylow sched 4821
--- BPF Map: @runq_latency_us ---
[1, 2] 98 |@@@@@@@@@@@@@@@@@@@@@@@@ |
[4, 8] 142 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
pylow page-faults <PID>
Trace page fault hotspots — which code is triggering cold memory access.
pylow page-faults 4821
=== PAGE FAULT HOTSPOTS ===
@faults[
malloc+0x24
PyBytes_FromStringAndSize+0x18
load_dataset @ ml/data.py:12
]: 421
pylow context-switches <PID>
Profile preemption and voluntary context switches.
pylow context-switches 4821
OFF CPU 87ms next_cpu=2
--- BPF Map: @off_cpu_ms ---
[0, 1] 12 |@@@@ |
[64, 128] 210 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
pylow kernel-blocked <PID>
Trace the exact kernel call path where the process is sleeping uninterruptibly.
pylow kernel-blocked 4821
BLOCKED IN KERNEL:
futex_wait+0x120
take_gil+0x42
execute_query+0x91 db.py:102
pylow tlb-shootdowns <PID>
Profile TLB flush rates and reasons.
pylow tlb-shootdowns 4821
--- BPF Map: @tlb_reason ---
[0] (TLB_FLUSH_ON_TASK_SWITCH) 42
[1] (TLB_FLUSH_ON_PAGE_FAULT) 187
pylow irq-impact <PID>
Monitor soft and hard IRQ CPU cycles stolen from the process.
pylow irq-impact 4821
--- BPF Map: @sirq_type ---
[3] (NET_RX_SOFTIRQ) 187
--- BPF Map: @sirq_lat (us) ---
[8, 16] 187 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
CATEGORY 3 — Python Runtime Tracing
pylow pycall <PID>
Profile Python PyObject_Call function execution timings.
pylow pycall 4821
SLOW execute_query took 12400us
SLOW process_job took 1850us
pylow pyframe <PID>
Trace exact Python frames: file, function name, line number.
pylow pyframe 4821
ENTER execute_query() @ db/models.py:142
slow_query() @ db/models.py:142 — p99: 340ms
pylow pycpu <PID>
Identify CPU hotspots in Python stacks.
pylow pycpu 4821
--- BPF Map: @stacks ---
→ Resolved: call_llm_chain @ gateway/orchestrator.py:120 (145 samples)
pylow pyexcept <PID>
Trace raised Python exceptions with user stack.
pylow pyexcept 4821
EXCEPTION KeyError @ tid=10234
get_user_context @ db/client.py:48
pylow pyiowait <PID>
Trace Python code blocking on I/O.
pylow pyiowait 4821
BLOCKING READ 12ms
fetch_metadata @ db/client.py:54
pylow pygil <PID>
Profile GIL acquisition delays and thread contention.
pylow pygil 4821
GIL WAIT 1250us tid=10234
calculate_features @ ml/engine.py:89
pylow pyleak <PID>
Profile heap allocations to detect memory leak patterns.
pylow pyleak 4821
=== TOP ALLOCATORS ===
@allocs[0x7f3b821034bc]: 10485760 bytes
→ load_dataset @ ml/data.py:12
pylow pyreq <PID>
Measure end-to-end request lifecycle breakdown.
pylow pyreq 4821
REQ START tid=10234
REQ DONE total=340ms db=310ms other=30ms
pylow timeline <PID> [--duration <sec>] [--threshold <ms>]
Render a chronological timeline call graph with entry/exit timestamps.
pylow timeline 4821 --duration 5.0 --threshold 2.0
[ 0.000ms] → handle_request() server.py:45
[ 0.040ms] → parse_headers() http.py:12
[ 0.051ms] ← parse_headers() [0.011ms]
[ 91.230ms] ← execute_query() [91.175ms] ⚠️ SLOW
pylow pythread <PID>
Trace thread-aware function call timelines with self-time per thread.
pylow pythread 4821
--- Thread ID: 10001 ---
parse_headers() spent 0.00ms (Self time: 0.00ms)
pylow pyasync <PID>
Trace async coroutine suspend/resume metrics.
pylow pyasync 4821
--- Coroutine: 0x7f3b821034bc ---
Suspended counts: 2
Total CPU Time: 80us
pylow pyargs <PID>
Profile Python function call argument types and layout (CPython struct dereference).
pylow pyargs 4821
1000 CALL obj=0x7f3b821034bc args=0x7f3b821051fa
pylow pysyscall <PID>
Profile syscalls attributed directly to Python frames.
pylow pysyscall 4821
=== SLOW READ fd=4 dur=12ms ===
fetch_metadata @ db/client.py:54
pylow pynplus1 <PID>
Detect N+1 query loop patterns from ORM calls.
pylow pynplus1 4821
⚠️ N+1 CANDIDATE: db/models.py:142
Called 15x in 5s (3.0/s)
pylow pygraph <PID>
Trace hierarchical call relationships as a call graph.
pylow pygraph 4821
handle_request() calls=1 avg=0.00ms (server.py:45)
execute_query() calls=1 avg=0.00ms (db.py:88)
pylow pyanomaly <PID>
Identify statistically anomalous slow function calls vs baseline.
pylow pyanomaly 4821
[BASELINE] execute_query(): mean=10.33ms stddev=0.76ms
🚨 ANOMALY execute_query(): 45.00ms vs baseline 10.33ms
pylow pydash <PID>
Stream live function call metrics to a curses dashboard.
pylow pydash 4821
=== LIVE FUNCTION TRACER ===
RECENT CALLS:
handle_request() 120.40ms
pylow pysingle <PID> <func>
Trace a single function's complete execution tree with self-time.
pylow pysingle 4821 handle_request
[ 0.000ms] → handle_request() server.py:45
[ 0.011ms] → validate_token() auth.py:12
[ 0.015ms] ← validate_token() total=0.004ms self=0.003ms
CATEGORY 4 — Diagnostic Workflows
pylow triage <PID>
Run a 10-second triage profile. Tells you exactly which problem category to investigate.
pylow triage 4821
cpu_samples : 842 ← high → CPU BOUND
off_cpu : 12
syscalls : 234
page_faults : 8
→ DIAGNOSIS: CPU BOUND — use pylow cpu-bound 4821
pylow cpu-bound <PID>
Full CPU-bound diagnostic: hottest stacks + duration attribution.
pylow cpu-bound 4821
CPU HOTSPOT: call_llm_chain @ gateway/orchestrator.py:120
Samples: 145 / 200 total (72.5%)
Avg duration: 1240ms
pylow io-bound <PID>
Full I/O-bound diagnostic: blocking syscall + Python origin stack.
pylow io-bound 4821
BLOCKED syscall=read 87ms
fetch_metadata @ db/client.py:54
→ fd=7 → /var/run/postgresql/.s.PGSQL.5432
pylow syscall-storm <PID> [--id <syscall_id>]
Diagnose high-frequency syscall storm. Optionally filter to a specific syscall ID.
pylow syscall-storm 4821
pylow syscall-storm 4821 --id 1 # filter to sys_write only
TOP SYSCALLS (5s):
id=1 (write) 3841 calls
id=0 (read) 1204 calls
id=202 (futex) 892 calls
pylow deadlock <PID>
Detect deadlocks: locks acquired but never released within the observation window.
pylow deadlock 4821
=== LOCKS HELD > 5s ===
@lock[tid=10234, lock=0x7f3b820f1234]: held since 8234ms
→ PyThread_acquire_lock @ db/connection_pool.py:88
pylow service-map <PID>
Map inbound and outbound request flow: connections, bytes in/out.
pylow service-map 4821
=== SERVICE pid=4821 ===
outbound_calls : 14
bytes_in : 204800
bytes_out : 81920
conn_time_ms:
[10, 20] 8 |@@@@@@@@@@@@ |
pylow ordered-log <PID> [--filter-internals]
Output a chronological ordered log of every Python function call and return.
pylow ordered-log 4821
pylow ordered-log 4821 --filter-internals # strip CPython bootstrap noise
1749558821000001 ENTER handle_request server.py:45
1749558821000040 ENTER authenticate_user auth.py:12
1749558821000055 EXIT authenticate_user
1749558821000060 ENTER execute_query db.py:88
1749558821091000 EXIT execute_query 91ms ⚠️ SLOW
pylow intercept <PID> [func]
Intercept entry/exit payloads at a boundary function. Shows file, line, tid, stack.
pylow intercept 4821 process_payment
=== process_payment CALLED ===
file : payments/service.py
line : 142
tid : 10234
time : 1749558821000060
=== process_payment RETURNED ===
duration: 88ms
pylow anomaly-trigger <PID> [func]
Arm a trigger on a function — capture full call tree only when an exception or anomaly fires.
pylow anomaly-trigger 4821 validate_payment
[WATCHING] validate_payment — armed, zero noise until trigger
!!! EXCEPTION DURING PAYMENT !!!
type : ValueError
after : 23ms
→ process_refund @ payments/service.py:89
→ validate_payment @ payments/service.py:34
pylow correlation <PID> [service_name]
Trace cross-service chronological correlation. Stamp every call with service name + nanosecond timestamp.
pylow correlation 4821 api-gateway
# Run simultaneously on all service PIDs, then merge:
# cat /tmp/trace_*.log | sort -k2 -t=
SVC=api-gateway TS=1749558821000001 ENTER handle_request server.py:45
SVC=api-gateway TS=1749558821000040 CONNECT
SVC=api-gateway TS=1749558821000060 EXIT handle_request
CATEGORY 5 — HTTP & API Tooling
pylow curl-perf <PID> [url]
Trace HTTP request timing breakdown: DNS, connect, TTFB, total.
pylow curl-perf 4821 https://api.example.com/payments
=== HTTP PERFORMANCE: https://api.example.com/payments ===
time_namelookup : 0.012s
time_connect : 0.031s
time_starttransfer: 0.087s
time_total : 0.091s
http_code : 200
size_download : 4096 bytes
pylow jwt-decode <PID>
Intercept and decode JWT Authorization tokens inline from HTTP headers.
pylow jwt-decode 4821
=== JWT DECODED ===
Header : {"alg":"RS256","typ":"JWT"}
Payload: {"sub":"user_9821","exp":1749645221,"roles":["admin"]}
Expiry : 2026-06-12T06:00:21Z (valid)
pylow cert-check <PID> [domain]
Trace SSL/TLS handshake and certificate expiry for a domain.
pylow cert-check 4821 api.example.com
=== TLS CERTIFICATE: api.example.com ===
Subject : CN=api.example.com
Issuer : Let's Encrypt Authority X3
Expires : 2026-09-01T00:00:00Z
Days left: 82
Status : ✓ VALID
pylow rate-limit-test <PID>
Diagnose API rate limit backoff — detect HTTP 429 responses and retry delays.
pylow rate-limit-test 4821
HTTP 429 received — Retry-After: 60s
Backoff attempt 1/3 — waiting 60s
Backoff attempt 2/3 — waiting 120s
Total blocked time: 180s
pylow parallel-fetch <PID> [--concurrency <n>]
Trace parallel request pipelines and concurrent connection activity.
pylow parallel-fetch 4821 --concurrency 8
Active parallel workers: 8
Fetched 10 payment details in parallel (duration: 120ms)
Successfully processed items: 10/10
CATEGORY 6 — Shell Pipeline Patterns
pylow jq-search <PID> [query]
Search JSON stream for field names or patterns.
pylow jq-search 4821 error
Searching stream for "error"...
.actStatus.name = "ERROR"
.response.errorCode = "PAYMENT_FAILED"
pylow awk-stats <PID>
Compute statistics (count, sum, avg, min, max, percentages) on tabular data streams.
pylow awk-stats 4821
=== STREAM STATISTICS ===
count : 1042
sum : 94821ms
avg : 91.0ms
min : 12ms
max : 3421ms
p95 : 340ms
pylow tee-branch <PID>
Monitor output stream branching — writes to multiple sinks simultaneously.
pylow tee-branch 4821
Stream branching active:
→ stdout (live display)
→ /tmp/response.log (file capture)
Bytes written: 40960
pylow pipe-decouple <PID> [fifo_path]
Monitor named pipe FIFO decoupler. Shows producer/consumer throughput.
pylow pipe-decouple 4821 /tmp/payment_pipe
Named pipe: /tmp/payment_pipe
Producer writes: 412
Consumer reads : 409
Buffer lag : 3 items
pylow sed-mask <PID>
Monitor stream for PII masking rules applied by sed filters.
pylow sed-mask 4821
=== MASKING ACTIVE ===
Pattern: [0-9]{16} → CARD-XXXX-XXXX-XXXX-XXXX
Pattern: \b[\w.]+@[\w.]+\b → EMAIL-MASKED
Lines processed: 1042
Patterns matched: 87
CATEGORY 7 — JSON Response Analysis
All jq-* commands analyze live JSON responses from the traced process. Pass a pid to attach.
pylow jq-schema <PID>
Discover the full recursive type schema of a JSON response (shape only, no values).
pylow jq-schema 4821
{
"id": "number",
"actStatus": { "id": "number", "name": "string" },
"refLinkTo": { "id": "number", "payModes": "null" }
}
pylow jq-nulls <PID>
List all null fields as a flat array of dotted key paths.
pylow jq-nulls 4821
["newconconno", "newconleadsid", "reminderdate", "actStatus.salesActivities"]
pylow jq-null-paths <PID>
Same as jq-nulls with full dotted path for nested nulls.
pylow jq-null-paths 4821
actStatus.salesActivities = null
refLinkTo.payModes = null
reminderdate = null
pylow jq-locate-key <PID> [key]
Find all paths in the document tree that contain a given key name.
pylow jq-locate-key 4821 salesActivities
Paths containing "salesActivities":
.actStatus.salesActivities
.refLinkTo.actStatus.salesActivities
pylow jq-key-path <PID> [key]
Find all paths and their values for a given key name.
pylow jq-key-path 4821 name
.actStatus.name = "COMPLETED"
.refLinkTo.name = "PAYMENT_MODE_CASH"
pylow jq-all-keys <PID>
Discover the full vocabulary of unique keys across the entire document.
pylow jq-all-keys 4821
All unique keys (32):
id, enddate, actStatus, name, salesActivities, refLinkTo,
payModes, reminderdate, newconconno, ...
pylow jq-leaf-paths <PID> [--filter-val <term>]
List all leaf (non-object) paths with their values. Optionally filter.
pylow jq-leaf-paths 4821
pylow jq-leaf-paths 4821 --filter-val COMPLETED
.id = 28920212268
.enddate = "2026-06-10"
.actStatus.name = "COMPLETED"
pylow jq-clean-nulls <PID>
Output the response with all null fields removed.
pylow jq-clean-nulls 4821
{
"id": 28920212268,
"enddate": "2026-06-10",
"actStatus": { "id": 1, "name": "COMPLETED" }
}
pylow jq-depth-map <PID>
Calculate nesting depth per key branch across the entire document.
pylow jq-depth-map 4821
Root depth: 1
actStatus: depth=2
salesActivities: depth=3
refLinkTo: depth=2
payModes: depth=3
pylow jq-type-map <PID>
Map every leaf path to its resolved data type.
pylow jq-type-map 4821
.id → number
.enddate → string
.actStatus.name → string
.refLinkTo.payModes → null
pylow jq-find-value <PID> [value]
Find the exact path where a specific value occurs.
pylow jq-find-value 4821 gravity_admin
Value "gravity_admin" found at:
.createdBy.username
.modifiedBy.username
pylow jq-structural-diff <PID>
Compare two response snapshots and show added/removed/changed fields.
pylow jq-structural-diff 4821
STRUCTURAL DIFF:
ADDED : .actStatus.resolvedAt
REMOVED : .legacyFlag
CHANGED : .actStatus.name (PENDING → COMPLETED)
pylow jq-extract-subtree <PID> [key]
Surgically extract a subtree from the document by key name.
pylow jq-extract-subtree 4821 appModulesId
{
"appModulesId": {
"id": 5,
"name": "PAYMENT_MODULE",
"active": true
}
}
pylow jq-summary <PID>
Print response statistics: total keys, null count, max depth, array sizes.
pylow jq-summary 4821
=== RESPONSE SUMMARY ===
Total keys : 47
Null fields : 12 (25.5%)
Max depth : 4
Arrays : 3
Largest array: salesActivities (0 items — empty)
pylow jq-validate-schema <PID> [schema_file]
Validate the response structure against a JSON schema template file.
pylow jq-validate-schema 4821 schema.json
Schema validation against schema.json:
✓ id present
✓ actStatus present
✗ paymentRef MISSING
Result: INVALID — 1 field missing
pylow jq-array-schema <PID>
Inspect all arrays: their sizes and the schema of their items.
pylow jq-array-schema 4821
.salesActivities : 0 items (empty)
.paymentHistory : 3 items → {id: number, date: string, amount: number}
.tags : 5 items → string
pylow jq-null-pct <PID>
Calculate the percentage of null fields relative to total fields.
pylow jq-null-pct 4821
Null field percentage: 25.5%
Total fields : 47
Null fields : 12
Filled fields: 35
pylow jq-non-null-leaves <PID>
List only the fields that have actual non-null values.
pylow jq-non-null-leaves 4821
Non-null leaves (35):
.id = 28920212268
.enddate = "2026-06-10"
.actStatus.name = "COMPLETED"
...
pylow jq-parent-context <PID> [key]
Show a key along with its parent object and sibling fields for context.
pylow jq-parent-context 4821 salesActivities
Key: salesActivities
Parent: actStatus
Siblings:
id = 1
name = "COMPLETED"
salesActivities = null ← target
pylow jq-locate-value-contains <PID> [partial]
Find all paths where a value contains a partial string match.
pylow jq-locate-value-contains 4821 Kernel
Paths with values containing "Kernel":
.modules[2].name = "Kernel Payment Module"
.description = "Kernel-level trace event"
pylow jq-trace-all-keys <PID> [key]
Find every occurrence of a key across all nested objects and arrays.
pylow jq-trace-all-keys 4821 username
All occurrences of "username":
.createdBy.username = "gravity_admin"
.modifiedBy.username = "system"
.audit[0].username = "gravity_admin"
pylow jq-heavy-objects <PID>
Identify the heaviest nested objects by field count.
pylow jq-heavy-objects 4821
=== HEAVIEST OBJECTS ===
.actStatus : 8 fields
.refLinkTo.payModes : 6 fields
.createdBy : 5 fields
pylow jq-repeated-schema <PID>
Find repeated structural patterns (e.g. audit trails, references) across the document.
pylow jq-repeated-schema 4821
Repeated schemas detected:
Pattern {id, name} appears 6x at:
.actStatus, .refLinkTo, .createdBy, ...
pylow jq-common-audit <PID>
Find nested objects that share the same audit trail key set (createdBy, modifiedBy, etc).
pylow jq-common-audit 4821
=== AUDIT TRAIL ANALYSIS ===
Objects with common audit keys:
.actStatus → createdBy, modifiedBy, createdAt
.refLinkTo → createdBy, modifiedBy
pylow jq-schema-evolution <PID>
Compare schema across paginated response pages to detect structural drift.
pylow jq-schema-evolution 4821
=== SCHEMA EVOLUTION (page 1 → page 2) ===
ADDED : .newField
REMOVED : .deprecatedFlag
STABLE : 44 fields unchanged
pylow jq-validate-fields <PID>
Assert that mandatory fields are present and non-null.
pylow jq-validate-fields 4821
=== FIELD VALIDATION ===
✓ id present
✓ actStatus present
✗ paymentRef MISSING
✗ enddate NULL
Result: 2 violations
pylow jq-watch-changes <PID>
Snapshot responses over time and track which fields change between calls.
pylow jq-watch-changes 4821
=== Snapshot 1 → Snapshot 2 ===
CHANGED: .actStatus.name PENDING → COMPLETED
CHANGED: .modifiedAt 2026-06-10T10:00Z → 2026-06-10T10:05Z
STABLE : 45 fields
CATEGORY 8 — DAG Execution Engine
Run arbitrary step graphs with proper dependency resolution, cycle detection, and level-by-level parallel execution.
DAG Spec Format
"step_name:dep1,dep2 step2:dep1 step3:"
- Each node:
name:comma-separated-deps - No deps:
auth:or justauth - Spaces separate nodes
pylow dag-dry-run --dag "<spec>" [--dag-file <path>] [--workers <n>]
Validate DAG — resolve levels, detect cycles. No execution.
pylow dag-dry-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog"
=== DAG EXECUTION ENGINE ===
Steps : 4
Levels : 3
Workers : 4
Mode : DRY RUN
Level 0 [auth]
Level 1 [get_user, get_catalog]
Level 2 [create_order]
✓ DAG is valid. No cycles. Dry-run complete.
Cycle detection:
pylow dag-dry-run --dag "a:b b:a"
# [dag-engine] CYCLE DETECTED — aborting.
pylow dag-run --dag "<spec>" [--dag-file <path>] [--workers <n>]
Execute the DAG. Runs each level in parallel (up to --workers at a time). Stops at first failed level.
pylow dag-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog" --workers 4
=== DAG EXECUTION ENGINE ===
Steps : 4
Levels : 3
Workers : 4
Mode : EXECUTE
Level 0 [auth]
Level 1 [get_user, get_catalog]
Level 2 [create_order]
▶ Level 0: ['auth']
✓ auth 51.2ms
▶ Level 1: ['get_user', 'get_catalog']
✓ get_user 50.8ms
✓ get_catalog 51.1ms
▶ Level 2: ['create_order']
✓ create_order 50.9ms
✓ All 4 steps completed in 153.8ms
Load from a JSON file:
# dag.json
{
"auth": [],
"get_user": ["auth"],
"get_catalog": ["auth"],
"create_order": ["get_user", "get_catalog"]
}
pylow dag-run --dag-file dag.json --workers 8
pylow dag-status
Show the step-level status of the last DAG run in this session.
pylow dag-status
=== DAG STATUS ===
DONE auth
DONE get_user
DONE get_catalog
DONE create_order
CATEGORY 9 — Saga Orchestrator
Forward/compensate pattern. Steps run in order. On failure, completed steps are rolled back in reverse. Every event is appended to a JSON Lines saga log.
pylow saga-run --steps <list> [--fail-at <step>] [--log-file <path>]
Run forward steps. On failure, compensating transactions execute in reverse for all completed steps.
# Happy path — all steps commit
pylow saga-run --steps auth,create_order,reserve_inventory,create_payment
=== SAGA ORCHESTRATOR [saga-1749558821] ===
Steps : auth, create_order, reserve_inventory, create_payment
Log file : /tmp/pylow_saga.log
Inject : none
▶ Forward auth ✓
▶ Forward create_order ✓
▶ Forward reserve_inventory ✓
▶ Forward create_payment ✓
✓ Saga committed. All 4 steps succeeded.
Log: /tmp/pylow_saga.log
# Inject failure to test rollback
pylow saga-run --steps auth,create_order,reserve_inventory --fail-at create_order
=== SAGA ORCHESTRATOR [saga-1749558822] ===
Steps : auth, create_order, reserve_inventory
Inject : create_order
▶ Forward auth ✓
▶ Forward create_order ✗ FAILED
Rolling back completed steps...
↩ Compensate auth ✓
✗ Saga rolled back. Failed at: create_order
Log: /tmp/pylow_saga.log
Custom log file:
pylow saga-run --steps auth,create_order --log-file /var/log/my_saga.log
pylow saga-log [--log-file <path>]
Display the saga event log with color-coded events.
pylow saga-log
pylow saga-log --log-file /var/log/my_saga.log
=== SAGA LOG: /tmp/pylow_saga.log ===
2026-06-11T06:09:01Z SAGA_START — steps=['auth', 'create_order', ...]
2026-06-11T06:09:01Z FORWARD_START [auth]
2026-06-11T06:09:01Z FORWARD_OK [auth]
2026-06-11T06:09:01Z FORWARD_START [create_order]
2026-06-11T06:09:01Z FORWARD_OK [create_order]
2026-06-11T06:09:02Z SAGA_COMMITTED — all_steps=['auth', 'create_order']
Event color key:
| Event | Color |
|---|---|
SAGA_START / SAGA_COMMITTED |
Cyan / Green |
FORWARD_START |
Yellow |
FORWARD_OK |
Green |
FORWARD_FAIL |
Red |
COMPENSATE_START / COMPENSATE_OK |
Magenta / Green |
SAGA_ROLLED_BACK |
Red |
pylow saga-replay [--log-file <path>]
Re-run the steps from a committed saga log (no injected failures).
pylow saga-replay
pylow saga-replay --log-file /var/log/my_saga.log
[saga-replay] Replaying 4 steps from /tmp/pylow_saga.log
=== SAGA ORCHESTRATOR [saga-1749558900] ===
▶ Forward auth ✓
▶ Forward create_order ✓
▶ Forward reserve_inventory ✓
▶ Forward create_payment ✓
✓ Saga committed. All 4 steps succeeded.
Log: /tmp/pylow_saga.log.replay
Diagnostic Decision Tree
Run pylow triage <PID> first. Read the output and go to the right command:
cpu_samples high + offcpu low → pylow cpu-bound <PID>
cpu_samples low + offcpu high → pylow io-bound <PID>
syscalls very high → pylow syscall-storm <PID>
page_faults high → pylow page-faults <PID>
all counts low → pylow deadlock <PID>
Full Drill-Down by Symptom
| Symptom | First Command | Drill-Down |
|---|---|---|
| App is slow | triage |
→ cpu-bound or io-bound |
| Memory growing | malloc |
→ pyleak |
| Random crashes | pyexcept |
→ anomaly-trigger |
| Process hangs | deadlock |
→ kernel-blocked |
| Too many syscalls | syscall-storm |
→ syscall |
| Wrong output / logic | ordered-log |
→ intercept → jq-structural-diff |
| Multi-service slowdown | service-map |
→ correlation |
| Unknown JSON schema | jq-schema |
→ jq-nulls → jq-validate-fields |
| API HTTP issue | curl-perf |
→ jwt-decode or cert-check |
| Distributed workflow | dag-dry-run |
→ dag-run |
| Rollback on failure | saga-run |
→ saga-log |
bpftrace Prerequisites
The kernel tracing commands (syscall, malloc, tcp, io, flame, sched, page-faults, context-switches, kernel-blocked, tlb-shootdowns, irq-impact, pycall, pyframe, pycpu, pyexcept, pyiowait, pygil, pyleak, pyreq, pythread, pyasync, pyargs, pysyscall, pynplus1, pygraph, pyanomaly, pydash, pysingle, timeline, triage, cpu-bound, io-bound, syscall-storm, deadlock, service-map, ordered-log, intercept, anomaly-trigger, correlation) require bpftrace on the host.
Install bpftrace:
# Ubuntu / Debian
sudo apt-get install bpftrace
# Fedora / RHEL
sudo dnf install bpftrace
USDT Python probes require a debug-enabled Python build:
python3 -c "import sys; print(sys.version)"
# Should show: with DTrace support
# Or check: readelf -n /usr/bin/python3 | grep stapsdt
Commands that do not require bpftrace (pure Python):
attach, flow, stitch, slow, diff, pyanomaly, pydash, jq-*, curl-perf, jwt-decode, cert-check, rate-limit-test, parallel-fetch, jq-search, awk-stats, tee-branch, pipe-decouple, sed-mask, dag-run, dag-dry-run, dag-status, saga-run, saga-log, saga-replay
Command Quick Reference
# ── CORE ──────────────────────────────────────────────────────
pylow attach <PID>
pylow flow --last
pylow stitch --services api,worker,ml-service
pylow slow --threshold 200ms --watch
pylow diff --before v1.2 --after v1.3
# ── KERNEL & SYSCALL ──────────────────────────────────────────
pylow syscall <PID>
pylow malloc <PID>
pylow tcp <PID>
pylow io <PID>
pylow flame <PID> --duration 5
pylow sched <PID>
pylow page-faults <PID>
pylow context-switches <PID>
pylow kernel-blocked <PID>
pylow tlb-shootdowns <PID>
pylow irq-impact <PID>
# ── PYTHON RUNTIME ────────────────────────────────────────────
pylow pycall <PID>
pylow pyframe <PID>
pylow pycpu <PID>
pylow pyexcept <PID>
pylow pyiowait <PID>
pylow pygil <PID>
pylow pyleak <PID>
pylow pyreq <PID>
pylow timeline <PID> --duration 5.0
pylow pythread <PID>
pylow pyasync <PID>
pylow pyargs <PID>
pylow pysyscall <PID>
pylow pynplus1 <PID>
pylow pygraph <PID>
pylow pyanomaly <PID>
pylow pydash <PID>
pylow pysingle <PID> handle_request
# ── DIAGNOSTIC WORKFLOWS ──────────────────────────────────────
pylow triage <PID>
pylow cpu-bound <PID>
pylow io-bound <PID>
pylow syscall-storm <PID>
pylow deadlock <PID>
pylow service-map <PID>
pylow ordered-log <PID> --filter-internals
pylow intercept <PID> process_payment
pylow anomaly-trigger <PID> validate_payment
pylow correlation <PID> api-gateway
# ── HTTP & API ────────────────────────────────────────────────
pylow curl-perf <PID> https://api.example.com/endpoint
pylow jwt-decode <PID>
pylow cert-check <PID> api.example.com
pylow rate-limit-test <PID>
pylow parallel-fetch <PID> --concurrency 8
# ── PIPELINE PATTERNS ─────────────────────────────────────────
pylow jq-search <PID> error
pylow awk-stats <PID>
pylow tee-branch <PID>
pylow pipe-decouple <PID> /tmp/payment_pipe
pylow sed-mask <PID>
pylow wire-grep --payload '{"payment_id": "pay_982"}' --pattern '"payment_id"\s*:\s*"([^"]+)"'
pylow jq-foreach <PID>
pylow jq-custom-stdlib --action percentile --data "10,20,30,40,50"
pylow pipeline-etl
pylow grep-jq-interleave --size 1000
pylow jq-sql-export <PID> --table payments
# ── JSON ANALYSIS ─────────────────────────────────────────────
pylow jq-schema <PID>
pylow jq-nulls <PID>
pylow jq-null-paths <PID>
pylow jq-locate-key <PID> salesActivities
pylow jq-key-path <PID> name
pylow jq-all-keys <PID>
pylow jq-leaf-paths <PID> --filter-val COMPLETED
pylow jq-clean-nulls <PID>
pylow jq-depth-map <PID>
pylow jq-type-map <PID>
pylow jq-find-value <PID> gravity_admin
pylow jq-structural-diff <PID>
pylow jq-extract-subtree <PID> appModulesId
pylow jq-summary <PID>
pylow jq-validate-schema <PID> schema.json
pylow jq-array-schema <PID>
pylow jq-null-pct <PID>
pylow jq-non-null-leaves <PID>
pylow jq-parent-context <PID> salesActivities
pylow jq-locate-value-contains <PID> Kernel
pylow jq-trace-all-keys <PID> username
pylow jq-heavy-objects <PID>
pylow jq-repeated-schema <PID>
pylow jq-common-audit <PID>
pylow jq-schema-evolution <PID>
pylow jq-validate-fields <PID>
pylow jq-watch-changes <PID>
pylow jq-reduce <PID>
pylow jq-early-exit <PID> --threshold 1000.0
pylow jq-path-find <PID> --type null
pylow jq-stream-filter <PID>
pylow jq-flat-map <PID>
pylow jq-format-matrix <PID> --format csv
# ── DAG ENGINE ────────────────────────────────────────────────
pylow dag-dry-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog"
pylow dag-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog" --workers 4
pylow dag-run --dag-file dag.json
pylow dag-status
# ── SAGA ORCHESTRATOR ─────────────────────────────────────────
pylow saga-run --steps auth,create_order,reserve_inventory,create_payment
pylow saga-run --steps auth,create_order --fail-at create_order
pylow saga-log
pylow saga-log --log-file /var/log/my_saga.log
pylow saga-replay
pylow saga-replay --log-file /var/log/my_saga.log
# ── PROTOCOL & ADVANCED PIPELINE CONTROL ───────────────────────
pylow net-tcp <PID> --url https://api.example.com
pylow chaos-run --url https://api.example.com/payments --rate 30
pylow contract-test <PID> --contract /path/to/contract.json
pylow rate-limit-check <PID> --headers-file /tmp/headers.txt
pylow shadow-compare --prod-file /tmp/prod.json --staging-file /tmp/staging.json
pylow load-gen --url https://api.example.com/payments --rps 20 --duration 10
pylow webhook-mock --port 9000 --secret whsec_secretkey
pylow behavior-fingerprint --url https://api.example.com/payments
pylow mtls-diagnose --cert /tmp/client.crt --key /tmp/client.key
pylow grpc-proto --method PaymentsService/GetPayment --payload '{"id": 462}'
pylow graphql-nplus1 --url https://api.example.com/graphql
pylow ws-handshake --url wss://api.example.com/ws
pylow infra-fingerprint --url https://api.example.com/payments
pylow diff-fuzz --url-a https://api-v1.example.com --url-b https://api-v2.example.com
pylow mtls-diagnose
Verify mutual TLS configuration, certificate chains validity and pin server certificates.
pylow mtls-diagnose --cert /tmp/client.crt --key /tmp/client.key
pylow grpc-proto
Encode protobuf structures to gRPC 5-byte frame binary formats and analyze status trailers.
pylow grpc-proto --method PaymentsService/GetPayment --payload '{"id": 462}'
pylow graphql-nplus1
Audit GraphQL schemas for N+1 database round-trip loop overheads and depth limits checks.
pylow graphql-nplus1 --url https://api.example.com/graphql
pylow ws-handshake
Verify WebSocket protocol upgrades and handshake connection status.
pylow ws-handshake --url wss://api.example.com/ws
pylow infra-fingerprint
Probe reverse proxy paths, CDN edge caching latency, and WAF rulesets.
pylow infra-fingerprint --url https://api.example.com/payments
pylow diff-fuzz
Execute differential fuzz testing between two API versions using prototype pollution and boundary payloads.
pylow diff-fuzz --url-a https://api-v1.example.com --url-b https://api-v2.example.com
pylow jq-reduce <PID>
Calculate group summary reductions and running averages from streaming lists.
pylow jq-reduce 4821
pylow jq-early-exit <PID>
Perform conditional breaks inside pipelines to exit loops early when values exceed thresholds.
pylow jq-early-exit 4821 --threshold 1000.0
pylow jq-path-find <PID>
Locate absolute nested keys matching targeted types (e.g. null, number, string, boolean).
pylow jq-path-find 4821 --type null
pylow jq-stream-filter <PID>
Process heavy arrays incrementally without memory overflows using streaming paths.
pylow jq-stream-filter 4821
pylow jq-flat-map <PID>
Serialize nested structures into a flat map of dotted keys, and reconstruct them back.
pylow jq-flat-map 4821
pylow jq-format-matrix <PID>
Convert JSON payloads to TSV, CSV, Prometheus metrics, or Elasticsearch bulk index maps.
pylow jq-format-matrix 4821 --format csv
pylow wire-grep
Extract raw structured values directly from text payloads using PCRE regex capture groups.
pylow wire-grep --payload '{"payment_id": "pay_982"}' --pattern '"payment_id"\s*:\s*"([^"]+)"'
pylow jq-foreach <PID>
Track running average values and state bounds dynamically over lists of entries.
pylow jq-foreach 4821
pylow jq-custom-stdlib
Run JQ Custom stdlib math routines (percentile, zscore, sliding window) over a list of parameters.
pylow jq-custom-stdlib --action percentile --data "10,20,30,40,50"
pylow pipeline-etl
Perform multi-stage ETL extraction, data cleaning, type coercion, and validation checking.
pylow pipeline-etl
pylow grep-jq-interleave
Benchmark DFA regex pre-filtering speeds vs JQ AST interpreter parser checks.
pylow grep-jq-interleave --size 1000
pylow jq-sql-export <PID>
Convert JSON attributes into SQL INSERT statements.
pylow jq-sql-export 4821 --table payments
pylow pipeline-run
Run the source-agnostic live transformation pipeline.
pylow pipeline-run
Integrating Custom Curl Sources (Streaming & Polling)
The pipeline is designed to be completely source-agnostic—meaning the core pipeline doesn't care what format your API returns.
To connect any custom curl command, read the target input pipe location from /tmp/pylow_pipeline_latest and append to it:
# 1. Start the pipeline in the background or another terminal
pylow pipeline-run
# 2. Feed any HTTP JSON stream directly into the pipeline
curl -sN "https://stream.example.com/events" >> $(cat /tmp/pylow_pipeline_latest)/pipes/ingest_in
# 3. Poll a REST API every 10 seconds and pipe it in
while true; do
curl -s -H "Authorization: Bearer KEY" "https://api.example.com/payments" >> $(cat /tmp/pylow_pipeline_latest)/pipes/ingest_in
sleep 10
done
pylow pipeline-inject <record>
Inject a manual test record into the live pipeline.
pylow pipeline-inject '{"id": "pay_1", "amount": 100, "currency": "USD"}' --stage ingest
pylow pipeline-replay
Replay dead letter box records into the pipeline.
pylow pipeline-replay --filter "." --stage normalize
pylow pipeline-test-stage <record> <transform>
Test a single stage transform without the full pipeline.
pylow pipeline-test-stage '{"id": "pay_1", "amount": 100, "currency": "USD"}' 01_normalize.jq
pylow pipeline-tap <stage>
Tap and display live logs from a specific pipeline stage.
pylow pipeline-tap normalize
CATEGORY 10 — MCP Server
pylow-mcp
Start the MCP server (Streamable-HTTP on port 8765 by default). Exposes all 113 pylow tools to any MCP-compatible client.
# HTTP mode (MCP Inspector, Cursor, remote clients)
pylow-mcp
# stdio mode (Claude Desktop local config)
pylow-mcp --stdio
# Custom host/port via env vars
MCP_HOST=0.0.0.0 MCP_PORT=9000 pylow-mcp
MCP Tool Naming
Every CLI command maps to an MCP tool with the same name (hyphens become underscores):
| CLI command | MCP tool name |
|---|---|
pylow attach <PID> |
attach(pid) |
pylow wire-grep --payload ... --pattern ... |
wire_grep(payload, pattern) |
pylow jq-locate-value-contains <PID> <term> |
jq_locate_value_contains(pid, partial_val) |
pylow dag-run --dag ... |
dag_run(dag, dag_file, workers) |
pylow saga-run --steps ... |
saga_run(steps, fail_at, log_file) |
pylow pipeline-etl |
pipeline_etl() |
pylow graphql-nplus1 --url ... |
graphql_nplus1(url) |
pylow mtls-diagnose --cert ... --key ... |
mtls_diagnose(cert, key) |
Links
- PyPI: https://pypi.org/project/pylow/
- Docker Hub: https://hub.docker.com/r/chiefj/pylow-mcp
- MCP Inspector: https://github.com/modelcontextprotocol/inspector
- MCP Protocol: https://modelcontextprotocol.io
Metadata
Release files for pylow 0.1.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pylow-0.1.14.tar.gz | 136.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pylow-0.1.14-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 302.5 kB
Release files / pylow-0.1.14.tar.gz
| Download URL | pylow-0.1.14.tar.gz |
|---|---|
| Size | 136.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
832c8b5830e3fc13957cf246b127b9992a3e162bc6698c08d0b8d5084307b06f
|
|
BLAKE2b-256 checksum How to use checksums |
a565ca0624ea2955d452db8df3c683825865ace15e4423a8d2b1edc99a7eae90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|
Release files / pylow-0.1.14-py3-none-any.whl
| Download URL | pylow-0.1.14-py3-none-any.whl |
|---|---|
| Size | 165.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
69ae5f2204e5e1d5906d353f742568e50e45aec5f42e73d61008dd84accb69f6
|
|
BLAKE2b-256 checksum How to use checksums |
3692e9cd605cf810227cd004aee1acd4aab1f4a0a23db0c2d1d0d4e361557b4c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|