The day we gave shell-less agents hands on your data
Back when we first shipped the vibes-diy CLI, there was an asymmetry that quietly bugged us. A vibe is a tiny app with a live database behind it, and Claude Code could reach right into that database — it has a shell, so it could call the CLI directly. But the agents without a shell — Claude Desktop, Cowork — went blind the moment a vibe was deployed. They could write the app beautifully, then couldn't answer "how many signups today?", couldn't drop in a seed record to eyeball a UI state, couldn't touch the data they'd just built a home for. The data was right there; those agents just had no door.

The fix was almost boring in hindsight. A shell-less agent's one extension point is MCP: tools exposed over stdio JSON-RPC. So we took the exact data operations Claude Code was already shelling out to, and wrapped them as MCP tools. That's all vibes-diy mcp is — no new protocol, no parallel implementation, just the CLI's data surface handed to the one client that couldn't reach a shell.
The data path
What wiring it up looked like
Setup came out to three steps, and it still works exactly this way today.
Get posts like this in your inbox
One email field. Real updates. No algorithm required.
1. Log in once (on the same machine the agent runs on). This enrolls a device certificate — no password, no token to paste:
bashnpx vibes-diy login
2. Point your agent at the server. For Claude Desktop / Cowork, add one block to ~/Library/Application Support/Claude/claude_desktop_config.json:
json{
"mcpServers": {
"my-vibe": {
"command": "npx",
"args": ["vibes-diy", "mcp", "--app-slug", "APP", "--handle", "USER"]
}
}
}
For Claude Code, the same block goes in .mcp.json at your project root (project scope — checked into version control so your whole team gets the server), and you can drop the flags — --app-slug defaults from VIBES_APP_SLUG or the current directory:
json{ "mcpServers": { "my-vibe": { "command": "npx", "args": ["vibes-diy", "mcp"] } } }
3. Restart the agent. Ask it "What databases are in this vibe?" — it calls vibes_list_databases and answers. That's the whole setup. Want to poke at it first? npx @modelcontextprotocol/inspector npx vibes-diy mcp --app-slug APP opens the tools in the MCP inspector.
What it opened up
Six tools, all backed by the same CLI internals you'd use by hand:
| Tool | What it does |
|---|---|
vibes_list_apps |
Page through your vibes (limit, cursor, search) |
vibes_list_databases |
List the databases inside the session's vibe |
vibes_get |
Fetch one document by id |
vibes_put |
Create or update a document |
vibes_delete |
Remove a document by id |
vibes_query |
Query a database by field — key, prefix, range, limit, descending |
Read-only tools (list, get, query) are tagged readOnlyHint; put and delete carry destructiveHint, so a careful client can gate writes behind a confirmation. The agent sees the difference.
vibes_list_apps is paginated on purpose. A single MCP response has to fit in the agent's context, so dumping every vibe you've ever made doesn't scale. It defaults to 50 items (max 200), takes a cursor to page, and a search substring to narrow by title or slug. Ask "find my pickathon vibe" and the agent passes search: "pickathon" instead of scrolling a wall of JSON.
A worked example
You built pickathon-picker — a band-voting app for a festival. It's live, votes are coming in, and you want to make sense of them without opening a SQL console:
You: Which bands are leading in pickathon-picker, and add a test vote for "The Wedges" so I can check the tally UI.
Under the hood the agent chains tool calls:
vibes_list_databases→ finds thepickerdatabase.vibes_queryon thevotesfield → reads the current docs, tallies them, tells you the top three.vibes_put{ type: "vote", band: "The Wedges", ts: "…" }→ drops in your test record.- You refresh the app; the new vote is in the tally. When you're done: "delete that test vote" →
vibes_delete.
No export, no copy-paste, no leaving the chat. The agent treats your live vibe data as a first-class thing it can reason over and nudge.
The calls we made (and not a REST key)
The design decisions are what still hold up:
- One vibe per session, fixed at startup. No per-call
appSlugswitching means the agent can't wander into the wrong app's data mid-conversation. The blast radius of any tool call is one vibe you chose deliberately. - Device cert, not an API key in the config. Auth rides on the same
vibes-diy logincertificate the CLI already uses — the private key never gets pasted into the MCP config or the chat, and revoking the device kills MCP access with it. - Same internals as the CLI. The MCP server isn't a parallel implementation — it wraps the exact operations Claude Code shells out to. One behavior to reason about, two transports.
Looking back
It's one of those changes that felt small when it landed and turned out to matter more than we expected. The agent could already write the app; giving it hands on the running app's data — to read it, reason over it, and change it — is what made the build-and-inspect loop feel whole. And the reason it was a small change is the part worth remembering: the work wasn't a new protocol, it was exposing the data surface the CLI already had to the one client that couldn't reach a shell. It's all still there.
If you want to try it, full flags, config, and the inspector command live in the CLI reference — jump to the vibes-diy mcp section.
Build something with a database behind it
Make a vibe in a sentence, then let your agent operate its data.
Start building →