vibedasher-mcp
An MCP server that exposes the Vibedasher data engine — datasets, CSV upload + ETL, headless SQL query, and dashboard (viz) management — as tools a customer's AI (Claude Code, Cursor, ...) can drive directly.
This is the control plane (VD-601, PIVOT/PLAN.md EP-6). It wraps the published
vibedasher Python SDK and calls only the public, metered /api/v1/*
API.
Install: pip install 'vibedasher-mcp>=0.6.0', then vibedasher login — no API
key to mint. Live on PyPI since 2026-08-09, as are
vibedasher (PyPI) and @vibedasher/client (npm).
The eject tools (VD-602) ship in this package, not separately: eject_viz,
eject_viz_files, eject_instructions and get_viz_files are registered by
register_eject_tools() in eject.py. They pull a viz's code plus an SDK-wiring
manifest so your AI can recreate the dashboard natively in your stack.
Tools
| Tool | What it does |
|---|---|
list_datasets |
List datasets the key can read (id, name, cleanSQLName, status). |
get_dataset(dataset_id) |
One dataset's metadata + column schema. |
upload_dataset(name, csv_content, ...) |
Create a CSV dataset, upload, load, poll to READY. |
run_query(sql, dataset_ids, params, type) |
Explore. Inline alias-only SQL across your datasets (plural, joins allowed) → typed columns + rows in the response. type="wasm" returns a plan of presigned Parquet URLs instead of rows — the credential-free data lane for a standalone build, and the only call that builds a dataset's wasm extract. |
run_query_to_file(sql, output_path, dataset_ids, params, format, overwrite) |
Extract. Same query, but the rows are written to a local CSV/JSONL file and never enter the response — you get back path, rowCount, columns, bytes, truncated. stdio only; absolute paths only; never clobbers without overwrite=True. |
list_vizzes(include_unpublished=False) |
List dashboards. |
get_viz(viz_id) |
One viz's metadata. |
create_viz(name, dataset_ids, seed_id/dashboard_config, ...) |
Create a viz (AI dashboard-build entry). |
build_viz(viz_id, branch) |
Compile a viz branch into a renderable bundle. |
eject_viz(viz_id, branch, out_dir, overwrite, max_chars) |
(eject) The viz's runnable source tree + package.json + Tailwind config + wiring manifest. Pass out_dir — see Delivery. |
eject_viz_files(viz_id, paths, branch, out_dir, ...) |
(eject) Ejected file contents on demand — the follow-up call when eject_viz came back paged. |
eject_instructions() |
(eject) How to recreate an ejected dashboard in your app — the query contract, auth modes, region sharding, and the host-bundler traps. |
get_viz_files(viz_id, branch, out_dir, ...) |
(eject) Raw file tree for a viz branch. Same delivery budget as eject_viz. |
Delivery: an ejected tree does not fit in one response
Measured against prod on 2026-08-18: eject_viz(1275) is 44 files / 517,351
characters (~129k tokens) and eject_viz(1282) is 45 files / 556,363
characters (~139k tokens). MCP clients cap a tool result, so the single-shot
payload was either dropped outright by a strict client or ate the agent's entire
context. Every eject response now carries a delivery block, and the three file
tools take the same three parameters.
| Mode | How | Cost |
|---|---|---|
| disk (preferred) | out_dir=<ABSOLUTE path> |
~30k characters regardless of tree size — measured 32,308 for viz 1275 |
| paged (default) | omit out_dir; files fills to max_chars (60,000), the rest land in delivery.pendingPaths |
10 calls for viz 1275, none over budget |
| unbounded | max_chars=0 |
the pre-0.2.0 payload, on request only |
eject_viz(viz_id=1275, out_dir="/abs/path/to/app/dashboard") # one call, whole tree on disk
out_dir resolves on the machine running the MCP server; with the standard
stdio launch (command: vibedasher-mcp) that is your machine, because the client
spawns the server as a local subprocess. Relative paths are refused, existing
files are refused unless overwrite=True, and any path escaping out_dir aborts
the write before a byte lands.
Paging is the fallback, not the goal: round trips do not reduce total tokens (the
content still crosses the model), they only keep each individual response
deliverable. Hand delivery.pendingPaths straight back to
eject_viz_files(viz_id, paths=...) and repeat until it is empty. A response
with a non-empty pendingPaths is a page, not a tree.
Override the budget per call with max_chars or globally with
VIBEDASHER_MCP_MAX_RESPONSE_CHARS.
Three data lanes
Pick by what you will do with the rows:
| Lane | Call | What comes back | Use it for |
|---|---|---|---|
| Explore | run_query(sql, ...) |
the rows, inline | aggregates, samples, schema checks — anything the agent will read |
| Extract | run_query_to_file(sql, output_path=<absolute>, format="csv"|"jsonl") |
{path, rowCount, columns, bytes, truncated} — never the rows |
anything loaded, charted or shipped from disk; thousands of rows and up |
| Static asset | run_query(sql, type="wasm") |
a plan of presigned Parquet URLs, no rows | a dashboard you host yourself, querying in-browser with DuckDB-WASM and no key in the bundle |
run_query_to_file writes on the machine running the MCP server, which with the
standard stdio launch is the agent's own machine (the client spawns the server as a
local subprocess). It is refused over --http, where the path would resolve on the
server's disk. Both lanes that return counts honour the backend's 100,000-row cap:
truncated: true means the rows — in the response or in the file — are a prefix.
Parquet output is deliberately not offered: the wasm lane already returns Parquet
the server built, and pyarrow is a 40 MB wheel to install for one option.
run_query reuses the SDK's hand-written query() transport (VD-301): the caller
never sees inline-vs-presigned delivery, MessagePack, or retries — one call in,
typed rows out. Each dataset id resolves server-side to that dataset's
cleanSQLName alias under the VD-203 RLS/alias-rewrite; SQL references only aliases.
Auth
Two lanes. The first one is the default because it asks nothing of you.
Sign in through your browser (first run)
pip install 'vibedasher-mcp>=0.6.0' # `login` does not exist below 0.6.0
vibedasher login # opens a browser; approve; done
vibedasher login opens your browser, captures the authorization code on a
loopback listener bound to 127.0.0.1, and stores a short-lived refresh token in
your OS keyring (with a 0600 file fallback where no keyring is available).
There is no key to mint, copy, paste or rotate, and nothing lands in a config
file or your shell history. Later runs are silent until the session lapses, at
which point login asks again.
Two companions: vibedasher whoami prints the account, user, region and the date
you will be asked to sign in again; vibedasher logout revokes this machine's
session and forgets it.
The MCP client block then carries no credential at all:
{
"mcpServers": {
"vibedasher": {
"command": "vibedasher-mcp",
"env": { "VIBEDASHER_REGION": "eu-central-1" }
}
}
}
VIBEDASHER_REGION is optional here — login records the region the account
actually lives in — but pinning it costs nothing and makes the shard explicit.
API key (CI, servers, headless agents)
Anywhere a browser cannot open, an API key is still the answer. Mint one in the
console (Settings → API keys) or via POST /v1/api-keys, and give it to the
server as an environment variable:
export VIBEDASHER_API_KEY=... # X-Api-Key, handled by the SDK's create_client
export VIBEDASHER_REGION=eu-central-1 # or us-east-1
# export VIBEDASHER_BASE_URL=... # optional override (on-prem/staging)
{
"mcpServers": {
"vibedasher": {
"command": "vibedasher-mcp",
"env": {
"VIBEDASHER_API_KEY": "...",
"VIBEDASHER_REGION": "eu-central-1"
}
}
}
}
An API key authenticates as the user who created it, has no expiry unless one is set, and is a bearer secret in plaintext on whatever disk holds that config. That is a fine trade for a build server and a poor one for your laptop — which is why it is the second lane and not the first.
Either way, the server checks before it serves
vibedasher-mcp resolves a credential — stored session first, then
VIBEDASHER_API_KEY — at startup. With neither, it refuses to start and prints
what to do on stderr, so a credential-less server never connects and never
advertises tools it cannot serve. Neither credential is ever logged or echoed in
a tool result.
Metering
Every tool hits the public, metered endpoints (create_dataset is credit-gated;
run_query is metered via the usage/credits event). The MCP adds no side channel
and bypasses no metering.
Run
pip install 'vibedasher-mcp>=0.6.0' # (monorepo dev: also make `vibedasher` importable)
vibedasher login # sign in once, in a browser
vibedasher-mcp # stdio MCP server
# or: python -m vibedasher_mcp
Wiring Claude Code in one line, on a machine that has signed in:
claude mcp add vibedasher --env VIBEDASHER_REGION=eu-central-1 -- vibedasher-mcp
Then ask your agent for something. It has 40 tools; a good first prompt is
"run whoami, then list_datasets, and build me a dashboard from the most
interesting one — verify with get_build_status and probe_viz before telling
me it's done."
For the JSON mcpServers form of both lanes, see Auth.
Development
The package depends on the sibling SDK at ../sdk/py. Tests wire that path
automatically (tests/conftest.py), so from packages/mcp:
python -m pytest tests/ -q
Note:
fastmcpis a client-side dependency of this standalone package; it is not bundled into the API Lambdas, so theexport_requirements.pystep in the rootCLAUDE.mddoes not apply here.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vibedasher_mcp-0.6.1.tar.gz.
File metadata
- Download URL: vibedasher_mcp-0.6.1.tar.gz
- Upload date:
- Size: 403.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0ed5bb690b5b81585c787276a228ad81ef8e4d47580e4a6906e1a9c1f928c03
|
|
| MD5 |
bd312976ccd90b08c31b3402e8a48843
|
|
| BLAKE2b-256 |
0afe7d5cd8ad1da5b325bc166226ab59e3a31a10f60478857be9dc010c0a54bb
|
File details
Details for the file vibedasher_mcp-0.6.1-py3-none-any.whl.
File metadata
- Download URL: vibedasher_mcp-0.6.1-py3-none-any.whl
- Upload date:
- Size: 235.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6377c8a0df7f6b3994fc8b50431771bd2ca448a884c17b00c269f0737b0b83a7
|
|
| MD5 |
0b0aa9b4fa0162e8ff205374100e71b6
|
|
| BLAKE2b-256 |
81e66b835ee615a35d9be22683392ecc0731c5085fa95ab9b3f50b24759ec1ca
|