How to build Discourse community dashboards in Metabase
Discourse is the open-source discussion platform that runs a large share of developer and product communities — topics, posts, categories, tags, trust levels, and badges, all in a PostgreSQL database you can host yourself. Metabase is where you turn that activity into shared, trustworthy community dashboards. This guide covers two complementary paths: a lightweight MCP + CLI route that pulls a scoped slice with the Discourse MCP server and loads a CSV into Metabase with the Metabase CLI, and a durable pipeline route that lands Discourse data in a database so you can build dashboards anyone can read.
How do you connect Discourse to Metabase?
Most teams combine both routes: quick answers through MCP and CLI uploads first, then recurring community reporting on a warehouse-backed model.
Discourse is the one tool in this category where you may already own the database. Self-hosted forums put PostgreSQL right there — and the bundled Data Explorer plugin lets you run SQL through the API without touching the server at all.
Live community answers in, quick analysis out
Pair the Discourse MCP server with the Metabase CLI. Use MCP for live lookups, write a scoped result to CSV, then load it into Metabase as a ready-to-query table and model.
- Quick questions such as "show me member growth and activation of new signups"
- Loading Discourse exports into Metabase in seconds
- Spot-checks and one-off questions without pipeline work
- Great for exploration, not the weekly community or event report
- Use read-only credentials or scoped API keys wherever supported
- CSV uploads are snapshots — refresh or move to the pipeline for history
Durable community dashboards with history
Land Discourse data in a database or warehouse — via connector, scheduled API pulls, or a replica of the underlying database — then point Metabase at it.
- Discourse dashboards the community, marketing, and support teams all trust
- Joining community activity with product, support, and CRM data
- Long-run trends for member growth and activation of new signups and engagement: posts, replies, and reactions over time
- You own the refresh schedule and the grain
- Define "active member" once, in the model layer, and state the window
- Identity matching across tools caps how far member-level analysis goes
What can you analyze from Discourse data in Metabase?
These all come from the same core objects — topics and posts, plus the users and trust levels, categories and tags, likes and reactions your sync exposes:
- Member growth and activation of new signups
- Engagement: posts, replies, and reactions over time
- Community health: answered questions and response times
- Category and tag activity, including quiet corners
- Top contributors and contributor concentration
Which Discourse community dashboards should you build in Metabase?
Membership
Who joins, and who stays.
- New members by month (line)
- Members who posted in the last 30 days (number + trend)
- Activation: new members posting within 7 days (number)
- Members by trust level (bar)
Engagement
Where the conversation actually happens.
- Topics and posts per month (combo)
- Posts by category, top 10 (bar)
- Replies per topic distribution (bar)
- Likes and reactions per post (line)
Answer quality
Whether questions get answered, and how fast.
- Median time to first reply (line)
- Share of topics with no reply (number + trend)
- Topics with an accepted answer (number + trend)
- Oldest unanswered topics (table)
Contribution health
How concentrated the community is.
- Top contributors by posts (table)
- Share of posts from the top 10 members (number + trend)
- First-time posters per month (line)
- Categories with no new topic in 30 days (table)
How do you use the Discourse MCP server with the Metabase CLI?
Pair the Discourse MCP server with the Metabase CLI for fast, hands-on analysis. MCP is useful for scoped lookups and summarized exports; the Metabase CLI's upload command loads CSV data into Metabase and creates a ready-to-query table and model.
Example workflow
- Ask the MCP server for last quarter's topics and posts summarized by category and month, with reply counts and first-reply timestamps — not raw post bodies.
- Export the result as CSV, keeping stable IDs, timestamps, and the category, space, channel, or event keys you group by.
- Run
mb upload csvto load it into Metabase as a table and model, then build questions and dashboards on top.
Be honest about the limits
- MCP lookups are excellent for exploration, not scheduled reporting.
- A CSV upload is a snapshot; refresh it with
mb upload replaceor move to the pipeline for real history. - Growth and response-time trends need complete months of topics and posts — and deleted or anonymized accounts quietly rewrite historical member counts, so snapshot member totals daily instead of recomputing them from the current user table.
mb upload csvneeds an uploads database configured under Admin → Settings → Uploads.
How do you set up Discourse MCP and the Metabase CLI?
Discourse MCP serverofficial
- Transport
- Local server (npx) over stdio, or `--transport http`
- Auth
- Anonymous read-only by default; admin or user API key for scoped access
- Best for
- Live scoped lookups and summarized exports
Metabase CLIofficial
- Install
npm install -g @metabase/cli- Auth
mb auth login- Load data
mb upload csv --file data.csv- Requires
- An uploads database (Admin → Settings → Uploads)
{
"mcpServers": {
"discourse": {
"command": "npx",
"args": [
"-y",
"@discourse/mcp@latest",
"--site",
"https://forum.example.com"
]
}
}
}Published by the Discourse team itself (@discourse/mcp, requires Node 24+). It defaults to anonymous, read-only access — writes need both --allow_writes and --read_only=false, which analytics work never does. Pass --api_key and --api_username for admin-scoped reads. There is no vendor-hosted remote endpoint: the server runs on your machine and talks to your forum over HTTPS.
# Install the Metabase CLI
npm install -g @metabase/cli
# Log in (opens your browser; requires Metabase v62+)
mb auth login --url https://your-metabase.example.com
# Load a topics-and-posts export — creates a table AND a model
mb upload csv --file discourse-topics-and-posts.csv --collection root
# Refresh that same table later from a new export
mb upload replace <table-id> --file discourse-topics-and-posts.csvCan you generate a Discourse dashboard with AI?
Yes. Use the prompt below with any assistant that can run the Discourse MCP server and the Metabase CLI. It works end to end: if Discourse tables already exist in Metabase it analyzes those; otherwise it pulls scoped, summarized data, loads it with mb upload csv, then builds the dashboard and caveats any metric that needs missing history.
Create a polished Metabase dashboard for Discourse community analytics.
Work end to end: get the data into Metabase if it isn't there yet, then build.
Goal: Help community, marketing, and support leaders understand member growth, posting activity, time to first reply, unanswered topics, and contributor concentration from Discourse data.
Step 1 — Find or load the data:
- First, check what already exists in Metabase (search for discourse tables and
models). If durable Discourse data is already present — a warehouse sync, a
database replica, or an earlier upload — use it and skip to Step 2.
- If nothing is there, pull a scoped, summarized export with the Discourse MCP server:
topics and posts, plus users and trust levels, categories and tags, likes and reactions.
Prefer summarized rollups by month, category, space, channel, or event over raw
message and post bodies. Write each result to a CSV, then load it with the
Metabase CLI — run "mb upload csv --file <export>.csv" so each upload creates a
table and a ready-to-query model. Use "mb upload replace <table-id> --file
<export>.csv" to refresh an existing table instead of creating duplicates.
Step 2 — Inspect before querying:
Do not assume exact table or column names. Inspect available fields, categories,
spaces or channels, events, and date ranges before creating trend cards.
Important:
- Build on whatever data is present; don't claim Metabase connects natively to
Discourse — it reads a database or CLI-uploaded tables.
- Every "active member" card must state its window and its action (posted?
logged in?) in the card title or description. There is no standard definition.
- Deleted and anonymized accounts disappear from the current member list, which
silently rewrites historical member counts. Prefer daily snapshots over
recomputing history.
- Attendance is unknowable without check-in data. Exclude events where nobody
checked anyone in rather than scoring them as 0% attendance.
- A single CSV is a point-in-time snapshot: only build trend cards if there is
a usable date column or multiple periods have been uploaded.
Dashboard title: Discourse Community Overview
Sections:
1. Executive summary: New members this month; Active members (30 days,
stated on the card); Median time to first reply; No-reply share;
Posts per month.
2. Membership: New members by month; activation within 7 days; trust levels.
3. Engagement: Topics and posts by month; posts by category; replies per topic.
4. Answer quality: Time to first reply; no-reply share; oldest unanswered.
5. Contribution: Top contributors; top-10 share of posts; first-time posters.
Filters: Date range, Category or Space or Channel, Event, Member segment,
Ticket type.
Output: Build the dashboard if you have permission; otherwise provide the exact
questions, SQL, model definitions, and layout. Include caveats for any metric
that cannot be calculated from the available data.How do you sync Discourse data into a database or warehouse?
For dashboards that need history and reliability, land Discourse data in a database first, then connect Metabase to that database.
Connector options
- Managed connector or database replica — use a connector when one genuinely covers the objects you need, or replicate the underlying database when you host it yourself.
- Custom pipeline — use the Discourse API for control over grain, fields, and refresh cadence.
- MCP + CSV — use this for quick exploration and one-off slices.
Three real options, in rough order of preference. Self-hosting? Replicate the forum's PostgreSQL database into your warehouse and you have every table. Otherwise, run Data Explorer queries through the API on a schedule — it's bundled into Discourse core and returns query results as JSON or CSV, though CSV exports cap at 10,000 rows, so page by date. Fivetran also offers a Discourse connector, but it's an Application Lite connector: check its schema covers the objects your dashboards need before committing to it. Airbyte has no Discourse source.
Notes
- Decide the grain first (one row per post, message, or registration; daily rollups on top) — it drives every trend card.
- Land raw tables first, then build clean Metabase models on top of them.
- Normalize user, topic, category, tag, created-at, and reply-to fields.
How should you model Discourse data in Metabase?
Core tables
| Table | Grain | Key columns |
|---|---|---|
community_members | one row per forum user | user_id, username, created_at, trust_level, last_seen_at, post_count, is_staff |
community_posts | one row per post (topic starters and replies) | post_id, topic_id, user_id, category_id, created_at, is_first_post, reply_to_post_id, like_count |
community_topics | one row per topic | topic_id, category_id, created_by, created_at, reply_count, views, has_accepted_answer, first_reply_at |
Modeling advice
- Build a clean
community_postsmodel with common columns across forums, community platforms, chat servers, and event tools, so multi-source dashboards don't fork definitions. - Snapshot member counts daily into a table you own. Deleted and anonymized accounts vanish from the live member list, which changes last quarter's numbers every time you refresh.
- Keep the definition of "active member" — the window and the action — in the model layer, and surface it in card titles. There is no standard.
- Treat attendance as missing, not zero, when no check-in was recorded. Flag those events explicitly so conversion cards can exclude them.
- Hash or drop email addresses at the pipeline boundary and join members across tools on a stable ID wherever you have one.
Which Discourse community metrics should you track in Metabase?
| Metric | Definition | Notes |
|---|---|---|
| Member growth rate | Net new members per period over members at the start of it. | Deleted accounts rewrite history — snapshot daily. |
| Engagement rate | Members who posted or reacted over total members. | State the window; 30 days is a choice, not a standard. |
| Reply rate | Share of topics that get at least one reply. | Its inverse, the no-reply share, is the honest alarm. |
| Conversion rate | New members who post within their first 7 days. | An activation rate: fix the window and never move it. |
What SQL powers Discourse community dashboards in Metabase?
These assume a cleaned analytical model in a warehouse (PostgreSQL dialect). Adjust table and column names to match your pipeline — the modeled names above rarely survive a sync unchanged.
Signups per month against the members you already had.
SELECT
date_trunc('month', created_at) AS month,
COUNT(*) AS new_members,
ROUND(
100.0 * COUNT(*) / NULLIF(
SUM(COUNT(*)) OVER (
ORDER BY date_trunc('month', created_at)
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
), 0
), 1
) AS growth_rate_pct
FROM community_members
WHERE NOT is_staff
GROUP BY 1
ORDER BY 1;The percentile answer, not the average one.
-- Median, not average: one topic answered three weeks late
-- drags an average past anything a member would recognize.
SELECT
date_trunc('month', created_at) AS month,
COUNT(*) AS topics,
ROUND(
PERCENTILE_CONT(0.5) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (first_reply_at - created_at)) / 3600
)::numeric, 1
) AS median_hours_to_first_reply
FROM community_topics
WHERE first_reply_at IS NOT NULL
GROUP BY 1
ORDER BY 1;The number that tells you whether the forum is working.
SELECT
date_trunc('month', t.created_at) AS month,
COUNT(*) AS topics,
COUNT(*) FILTER (WHERE t.reply_count = 0) AS unanswered_topics,
ROUND(
100.0 * COUNT(*) FILTER (WHERE t.reply_count = 0)
/ NULLIF(COUNT(*), 0), 1
) AS no_reply_pct
FROM community_topics t
GROUP BY 1
ORDER BY 1;