Vibes DIY
Vibes DIY / Blog
From the build log

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 vibes-diy MCP server — list, get, put, query over your vibe's data
The fix turned out to be small: one login, one config block, six tools.

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

Claude DesktopCowork · Cursorvibes-diy mcpstdio MCP serveryour vibeFirefly databasestdioJSON-RPCdevicecert
Your agent calls tools over stdio; the server authenticates as your device and reaches your vibe's database — the private key never enters the config or the chat.

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:

  1. vibes_list_databases → finds the picker database.
  2. vibes_query on the votes field → reads the current docs, tallies them, tells you the top three.
  3. vibes_put { type: "vote", band: "The Wedges", ts: "…" } → drops in your test record.
  4. 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 appSlug switching 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 login certificate 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 →

Enjoyed this? Get the next post by email

One email field. Real updates. No algorithm required.

Prefer a feed? RSS · Atom