A recursive, self-aware dashboard built on the Cursor SDK and FastAPI.
The app reads its own source code, queries its own data, and edits itself when you ask it to.
from fastapi import FastAPI
import cursorfy
app = FastAPI()
cursorfy.mount(app)
<script defer src="/cursorfy/static/chat.js"></script>
An [ ASK ]
button appears in lower right corner. Users ask questions about the data, ask how the app works, or ask for changes. The agent reads app.py
, queries the data, edits the file, and tells the user to refresh.
Ask for a redesign and the agent edits app.py
. Refresh, the dashboard changes.
uv add cursorfy # or: pip install cursorfy
export CURSOR_API_KEY=crsr_... # https://cursor.com/dashboard/integrations
.env
in the project root is loaded automatically.
Point it at any folder of data and get a working dashboard:
$ ls
yellow_taxi_2026_03.parquet taxi_zones.csv
$ cursorfy init
[cursorfy] scaffolding dashboard in . (model=composer-2.5)…
[cursorfy] done in 42.4s
[cursorfy] agent: Built dashboard: Manhattan accounts for 86% of yellow
taxi pickups in March 2026; airport runs earn 3× the
street fare — 2 charts + sortable zone leaderboard
from yellow_taxi_2026_03.parquet (5,000 trips) joined
to taxi_zones.csv (265 zones).
$ uvicorn app:app --port 8086
The scaffolder reads every file, joins fact tables to dimension tables when they coexist, computes real aggregates, and picks the strongest finding before writing a line of code. The page leads with the finding, four KPI tiles, two or three Tufte-correct charts, and a sortable leaderboard. Theme inherits the cursorfy CSS variables. The agent then has full context for follow-up edits.
Iterate from the chat: "add a histogram of tip_pct", "color the bars by
borough", "drop the last chart". The agent edits app.py
; you refresh.
Six dashboards ship in examples/
, each scaffolded by the same cursorfy init
prompt over a different real, public dataset. Click a tile to jump to its source.
git clone https://github.com/crowdcent/cursorfy
cd cursorfy
uv pip install -e ".[examples]"
export CURSOR_API_KEY=crsr_...
| |
Top GitHub reposThe 30 most-starred OSS repos, via the live GitHub Search API.
S&P 500Five years of daily close plus the top SPY holdings.
Our World in Data, 2000–2024. China overtook the US in 2006.
Olympic medalsRio 2016, Tokyo 2020, Paris 2024. US and China tied at 40 gold.
MovieLensFour CSVs joined in polars. 30,000 ratings, 610 users.
Each example boots on its own port (see the docstring at the top of its
app.py
). Each ships an AGENTS.md
with the data schema, the findings worth referencing, common questions, and the chart conventions. The chat agent reads that file on every session.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import cursorfy
app = FastAPI()
cursorfy.mount(app) # POST /api/chat + /cursorfy/static/*
@app.get("/", response_class=HTMLResponse)
def index():
return """<!DOCTYPE html>
<html><body>
<h1>My dashboard</h1>
<script defer src="/cursorfy/static/chat.js"></script>
</body></html>"""
uvicorn app:app --reload
mount()
adds one POST route and one static-files mount. It does not touch your existing routes, middleware, or templates. The agent's working directory defaults to the caller's file directory.
Three ways to wire it up:
cursorfy.mount(app)
@cursorfy.enable
def create_app(): return FastAPI()
app = cursorfy.attach(FastAPI())
All take the same kwargs (cwd
, model
, valid_models
, preamble
,
api_key
, prefix
, static_path
). See AGENTS.md for defaults and descriptions.
- The full project directory via standard Cursor tools (
read_file
,grep
,terminal
,edit_file
, etc). Format doesn't matter — CSV, parquet, JSON, sqlite, Excel. For SQL across mixed formats the agent uses DuckDB. It also seesapp.py
itself, so it can explain how anything on the page was built. - Any SQL database whose URL you export as
CURSORFY_DB_<NAME>
in.env
(Postgres, MySQL, Snowflake, BigQuery, etc.). Cursorfy lists each one in the agent's preamble; the agent picks the driver (uv add sqlalchemy psycopg[binary]
) and queries it with sqlalchemy + polars or DuckDB ATTACH. Passwords are masked in the preamble. Read-only by default. - A screenshot of the page the user is looking at, captured client-side and
attached as an image. The agent sees the actual dashboard, not just the
text prompt. Opt out with
data-screenshot="false"
on the script tag. AGENTS.md
at the project root, appended to the system prompt in full. Put project-specific rules, data descriptions, and chart conventions here. The scaffolder writes one for you.
CURSOR_API_KEY=crsr_...
CURSORFY_DB_USERS=postgresql://user:pass@localhost:5432/users
CURSORFY_DB_WAREHOUSE=snowflake://user:pass@account/analytics
<script defer src="/cursorfy/static/chat.js"
data-endpoint="/api/chat"
data-title="Ask the data"
data-model="composer-2.5"
data-models="auto,composer-2.5,claude-opus-4-7"
data-theme="auto"
data-screenshot="true"
data-starters='["What columns are here?", "Plot Y over time"]'></script>
data-theme
: auto
(default, follows OS), dark
, or light
.
data-screenshot
: true
by default; "false"
skips the capture.
Built-in light and dark modes — follows prefers-color-scheme
automatically
and the floating T
toggle lets users flip on demand. Override the eight
--cursorfy-*
CSS variables in your own stylesheet to retheme everything;
every tint, border, and hover state is derived via color-mix()
, so changing one accent color cascades. See AGENTS.md for the variable list.
/api/chat
is unauthenticated by default. The agent it spawns has full shell + read/write access to the project directory. Treat the endpoint as privileged: bind uvicorn to--host 127.0.0.1
for local dev, and put it behind your own auth (reverse proxy, FastAPI dependency, etc.) before exposing it publicly.- The agent has read/write access to your project directory. Don't deploy it next to secrets. Use a service account that owns just the dashboard.
- Set
CURSOR_API_KEY
via your platform's secret manager. Never commit it. - First message in a new session takes 2-5s (agent cold start). Follow-ups resume the agent and are faster.
- Each session holds one agent process. Default cap is 50. Override with
cursorfy.mount(app, max_sessions=...)
.
MIT. See LICENSE.
Built on the Cursor SDK by Cursor, at CrowdCent.
If you think this README could be better, cursorfy init
in this repo and ask the agent to rewrite it.