Skip to main content

Dot Plot MCP

See individual users, not aggregate charts.

English | 한국어

report

DAU/MAU charts trend "up and to the right" as long as new users arrive — even when nobody sticks. This MCP server implements YC's Dot Plot methodology (David Lieb): until you have hundreds of users, the most informative dashboard is one row per user, one cell per day.

Design principle: code computes the numbers, AI only interprets them. Statistics never come from an LLM, so they are never wrong.

What it does

1. Tracking audit   compare events in your code vs events in your data → find broken/missing tracking
2. Dot plot         every user's activity as dots — churn, weekend-only, core fans at a glance
3. Classification   used-once / weekend-only / almost-daily, automatically
4. Aha moments      scan every action for "what turns users into regulars"
5. Report           hand-drawn style HTML + plain-language insights → share as a link
6. Benchmark        (opt-in) compare your metrics with teams at your industry & stage

30-second demo

demo

Quick start

Requirements: uv, and three columns of data: who, when, what — user_id, date, event.

Whatever your database tool already exports is fine. CSV, TSV, JSON, and JSONL are read directly, columns are matched by name (uid, customer_id, created_at, event_name, … all work), and timestamps are cut down to days:

psql -c "..." --csv > events.csv          # Postgres
mysql --json -e "..." > events.json       # MySQL
mongoexport --collection=orders ...       # MongoDB
bq query --format=json "..." > events.json # BigQuery

That is the whole "which databases are supported" answer: the ones you can already query. The data goes from your database to a file to the report — it never passes through the model.

One command — no clone, no setup:

claude mcp add dotplot -- uvx dotplot-mcp

No data yet? Clone and try the sample:

uv run sample_data.py   # generates events.csv (40 fake users)
uv run harness.py       # watch the whole pipeline run

Then say one thing:

"Analyze my product"

That's the whole interface. Claude finds your data, picks the action that means "this user got value", and hands back the report above. If your database has no events table — most early products don't — it builds the events out of the tables you already have:

SELECT user_id, created_at::date AS date, 'purchase' AS event FROM orders
UNION ALL
SELECT user_id, added_at::date, 'add_to_wishlist' FROM wishlist_items

Your orders table is an event log. It just isn't named like one. No SDK, no tracking code, no signup.

Tools

analyze is the whole product. Everything below it is a part that analyze already uses — reach for one only when you want a single number on its own ("just show me retention").

Tool What it does
analyze Data in, finished report out. Start here.
describe_events Understand the data shape
dot_plot Text dot plot (◎ signup day, ● active day, custom marks)
classify_users Automatic behavioral pattern classification
find_aha_moments Scan all events for "regular-converting" actions (before/after behavior change)
onboarding_funnel Signup → first value → return → still active: where users leak
retention_curve Weekly retention — the number investors always ask
load_from_db Pull events straight from Postgres/Supabase (no CSV export step)
history_compare "Since last report" deltas — snapshots auto-saved locally on every report
find_similar_cases Match your diagnosis to real documented cases (Facebook's 7-friends, Slack's 2k messages...)
audit_tracking Compare events in code vs data (find tracking gaps)
generate_report Hand-drawn style HTML report + rule-based insights
publish_report Host the report at a random URL, get a share link (Vercel)
submit_benchmark Submit aggregates to the anonymous benchmark (explicit consent required)
compare_benchmark Compare your metrics with percentiles of similar teams

Languages

Reports work in any language. English, 한국어, and 日本語 are built in; for every other language the agent translates the report strings on the fly (get_report_strings → translate → custom_strings), while the code validates that number placeholders survive translation — so statistics stay exact. Want your language built in? It's one dictionary in i18n.py. PRs welcome.

See the same report in English · 한국어 · 日本語.

Anonymous benchmark — what gets sent

Opt-in only. Nothing is ever sent without explicit consent.

If you consent, these five aggregates are sent — and this is everything:

{
  "users_count": 40,
  "churned_rate": 0.30,
  "weekend_rate": 0.175,
  "regular_rate": 0.275,
  "aha_lift": 0.82
}

Never sent: user IDs, event logs, dates, your service's name, IP-based identifiers.

The backend is INSERT-only (row-level security) — submitted data cannot be read back with the public key, and comparisons go through a function that returns percentile statistics only. Verify yourself: benchmark.py (~60 lines).

Architecture

analysis.py    all computation — pure Python, knows nothing about MCP (the brain)
server.py      thin shell exposing computations as MCP tools
report.py      HTML report rendering + rule-based insight sentences
benchmark.py   anonymous benchmark client
i18n.py        every user-facing sentence, per language
harness.py     run the whole pipeline end-to-end without an agent
sample_data.py sample data with planted patterns (for verifying the tool)
hosting/       Vercel project template for report hosting

Why it's built this way

  • LLMs don't compute — same data, same numbers, every time
  • Small samples withhold judgment — groups under 5 users are excluded from aha candidates
  • Correlation ≠ causation — every insight ships with a "verify with an experiment" warning
  • Vanity metrics blocked — pick open_app, page_view, session_start (and friends) as your value event and the code refuses, with a list of what you can pick instead
  • Typos can't lie to you — a value event that isn't in your data is rejected, so you never get a plausible-looking "100% churned" report from a misspelling

FAQ

How do I analyze user flows / user behavior for my early-stage product? If you have under ~1,000 users, skip the heavyweight analytics suites. Export a 3-column CSV (user_id, date, event) or connect your Postgres, then ask Claude to draw a dot plot — one row per user, one dot per active day. Churn, weekend-only users, and habit changes become visible in seconds. That's exactly what this MCP does.

How do I find my product's aha moment? find_aha_moments scans every event and measures, per user, how activity changed before vs after first doing that action — so frequency noise (scrolling, popups) doesn't fool the ranking. The report aligns all users on "day zero" so you can see the habit change with your own eyes.

How is this different from Mixpanel / Amplitude / PostHog? Those are built for thousands of users and aggregate charts. This is built for your first hundred: per-user visibility, runs locally inside your coding agent, no SDK, no signup, stats computed by code (never by the LLM). Graduate to the big tools later — this is the stage before them.

License

MIT

Release files for dotplot-mcp 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dotplot-mcp 0.1.2
File Size Uploaded
dotplot_mcp-0.1.2.tar.gz 50.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dotplot-mcp 0.1.2
File Interpreter ABI Platform
dotplot_mcp-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 94.9 kB

Release files / dotplot_mcp-0.1.2.tar.gz

Download URL dotplot_mcp-0.1.2.tar.gz
Size 50.8 kB
Tags Source
SHA-256 checksum
How to use checksums
668ce2f96f83657ec2cb9324ebd1921186bacbc63a371ac9ec17f0d885a6ecd8
BLAKE2b-256 checksum
How to use checksums
ff916ed7c8c9d15cb4352c8fd12160f21730d0388fba3fc435de4308c5dc6ef5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / dotplot_mcp-0.1.2-py3-none-any.whl

Download URL dotplot_mcp-0.1.2-py3-none-any.whl
Size 44.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b13c7bf722ae3f839dda33ab04cb500f1fe26a66862867978447b8515ced8dbd
BLAKE2b-256 checksum
How to use checksums
48100a76f37bb394db3bfab12c65e8c580bba9d8fee40d75109a6236946cdceb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.4

2 release files

0.1.3

2 release files

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page