EpiDash
A light, MCP-controlled dashboard service. EpidBot (and any MCP client holding a control key) builds, updates, publishes and unpublishes dashboards through an MCP tool surface; anyone with a dashboard link views it in a minimal web UI. Viewers can ask EpidBot to tweak a dashboard from the browser.
Live instance: https://dash.kwar-ai.com.br
EpidBot agent ──(MCP client, dk_ key)──▶ EpiDash /mcp (FastMCP tools)
│
SQLite ◀─────────────┤ (dashboards, charts, keys, audit)
│
Viewer browser ──(public link)──▶ EpiDash / (vanilla JS + Plotly)
│ │
└── tweak request ──▶ POST /api/dashboards/{slug}/tweak
│ (X-Internal-Request: shared secret)
▼
EpidBot /api/v1/chat ──agent──▶ EpiDash MCP tools
Quick start
uv sync
uv run uvicorn epidash.main:app --port 8000
Then issue a control key and talk to the MCP endpoint:
uv run python -m epidash.cli add-key "local"
# → dk_... (shown once)
# The MCP endpoint is stateless: one POST per call, no handshake needed.
curl -s -X POST http://localhost:8000/mcp \
-H "X-API-Key: dk_..." -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
In an MCP client (e.g. Claude Desktop, Cursor, EpidBot):
{
"mcpServers": {
"epidash": {
"url": "http://localhost:8000/mcp",
"headers": { "X-API-Key": "dk_..." }
}
}
}
Configuration (environment variables)
| Variable | Default | Purpose |
|---|---|---|
EPIDASH_DB_PATH |
data/epidash.db |
SQLite file (WAL) |
EPIDASH_BASE_URL |
http://localhost:8000 |
Public base URL for share links |
EPIDASH_MCP_PATH |
/mcp |
Mount path of the MCP endpoint |
EPIDASH_ALLOWED_HOSTS |
localhost,127.0.0.1 |
Host header allowlist (DNS-rebinding protection) |
EPIDASH_BOOTSTRAP_KEY |
— | Pre-shared dk_ key seeded at startup (infra automation) |
EPIDBOT_BASE_URL |
— | EpidBot base URL; enables the tweak flow |
EPIDBOT_INTERNAL_SECRET |
— | Shared secret (EpidBot INTERNAL_API_SECRET) or ek_ API key |
EPIDASH_TWEAK_RATE_LIMIT_PER_HOUR |
20 |
Per-dashboard viewer tweak budget |
EPIDASH_ASK_RATE_LIMIT_PER_HOUR |
30 |
Per-dashboard viewer question budget |
EPIDASH_TWEAK_TIMEOUT_S |
300 |
Max wait for an EpidBot tweak turn |
MCP tools
Read-only: list_dashboards, get_dashboard, get_view_stats, get_audit_log
(filterable by action and dashboard_id).
Mutations (audited): create_dashboard, update_dashboard,
publish_dashboard (confirm), unpublish_dashboard (confirm),
delete_dashboard (confirm), add_chart, update_chart, remove_chart
(confirm), duplicate_dashboard, style_charts.
Live data sources
Charts can auto-update from a remote URL instead of holding a static
snapshot. attach_data_source(chart_id, kind, url, refresh_interval_s=300, path=None) attaches a source:
| kind | payload |
|---|---|
url_json |
the chart's data envelope itself ({"columns": ..., "rows": ...}); path optionally extracts a nested object (dotted keys, e.g. "result.week") |
url_csv |
header row + data rows (numeric cells auto-converted); feeds line/bar/pie/table charts |
A background refresher fetches due sources every 30s (minimum interval 30s)
and replaces the chart's data on success — viewers pick the change up on
their next poll, and chart cards show an ⇣ auto badge with refresh
status. Failures never lose existing data; inspect them with
get_data_source(chart_id), force a run with refresh_data_source, or
stop with remove_data_source (confirm).
Chart data envelopes
| kind | data |
|---|---|
line, bar, pie, table |
{"columns": ["month", "cases"], "rows": [["Jan", 120], ...]} |
kpi |
{"value": 1234, "label": "Cases", "unit": "", "delta": "+12%"} |
choropleth |
{"values": {"SP": 120, "RJ": 90}, "region_level": "uf", "label": "cases"} |
spec accepts style overrides ({"color": "#4ade80"}, {"y_label": "cases"}) and
position controls ordering/width ({"order": 1, "width": "half"}).
Viewer tweak flow
A viewer types a request ("show only the Southeast after 2020") into the dashboard's chat box. EpiDash forwards it as an EpidBot chat turn with the dashboard context; the EpidBot agent uses its EpiDash MCP tools to update the charts; the viewer's browser picks up the change on its next poll.
Viewers can also ask questions ("why did cases spike in March?") via
the Ask button — the same chat pipeline, but the prompt instructs EpidBot
to answer from the dashboard data without modifying it. No authentication
is required for either; questions are recorded in the tweaks table with
kind='question' and rate-limited separately from tweaks.
Requires EPIDBOT_BASE_URL + EPIDBOT_INTERNAL_SECRET (EpidBot must accept
X-Internal-Request on its chat endpoint — see the EpidBot-side integration
docs) or an ek_ API key.
Deployment
Single container, single volume:
docker compose up -d --build
Production (KwarAI) deploys this via the Ansible epidash role behind
nginx at dash.kwar-ai.com.br — see the
infrastructure repo.
Development
uv sync # deps
uv run pytest -v # tests
uv run ty check epidash/ # type check
Browser end-to-end tests (headless chromium):
uv sync --group e2e
uv run playwright install chromium
uv run pytest tests_e2e -m e2e -v
License
MIT
Metadata
Release files for epidash 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| epidash-0.3.0.tar.gz | 1.8 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| epidash-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.6 MB
Release files / epidash-0.3.0.tar.gz
| Download URL | epidash-0.3.0.tar.gz |
|---|---|
| Size | 1.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0e106a4c250ff5c177920122ff6c82beb1fb5094b750983c50cbaff648105d65
|
|
BLAKE2b-256 checksum How to use checksums |
c429434b92f69352313ed8fc19559130d8889c78b7c6b13341a7fc20038a8059
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / epidash-0.3.0-py3-none-any.whl
| Download URL | epidash-0.3.0-py3-none-any.whl |
|---|---|
| Size | 1.7 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8fc0f3e0442e28429c07dd281cbc0eca5c5304c74087398c09efcfa40bdaee4e
|
|
BLAKE2b-256 checksum How to use checksums |
babb93308cd2d1ca0a12a00b0eee69ffaa0aaaf54d7f3a044402ba2cede5c223
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for epidash 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for epidash-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.