Building envibe, Part 2: The Build

This is Part 2 of a 3-part series. Start with Part 1: The Idea if you haven't read it.

The Tech Stack

The design called for an MCP server, a tool that AI coding assistants like Claude Code could call directly. That decision narrowed the tech stack immediately.

The MCP SDK is first-class in TypeScript. The reference implementation, the examples, the type definitions: all TypeScript. I could have written it in Python, but MCP's TypeScript support was clearly ahead, and npm is where the users are. If someone is using Claude Code or another AI coding assistant, they're almost certainly in a JavaScript/TypeScript project with a package.json already. Installing from npm is one command. That's the bar.

I picked Bun as the runtime. Fast startup, native TypeScript support, built-in test runner. For an MCP server that gets spawned as a subprocess, startup time matters. The AI is waiting for the server to respond.

TypeScript + Bun + MCP SDK. That was the stack.

The DATABASE_URL Mistake

The first version of the auto-classification patterns marked DATABASE_URL as read-only. The logic seemed sound: the AI needs to know the database host and port to help debug connection issues.

But database URLs contain passwords. postgres://user:mypassword@localhost:5432/db has a credential embedded in the connection string. Giving the AI read access to "the URL" means giving it read access to the password.

The fix was simple: reclassify DATABASE_URL, REDIS_URL, MONGODB_URI, and CONNECTION_STRING as placeholder. The AI knows you have a database configured, knows it's Postgres, but doesn't see the actual connection string. If it needs to debug a connection issue, you can temporarily upgrade it to read-only in the manifest.

This was the moment I internalized that auto-classification needs to be paranoid. Every pattern defaults to the most restrictive reasonable level, not the most convenient one.

Building with Claude Code

I'm not going to say "AI built the whole thing" or "AI was useless and I did all the real work." Neither is true.

The actual workflow was a conversation. I'd describe what I wanted ("we need an MCP server that exposes five tools") and Claude Code would scaffold it. I'd review, push back on naming, ask for different error messages. It would iterate. We'd go back and forth until the code felt right.

The timeline was longer than a weekend hack:

Dec 2025Research begins
Jan 5Initial release: aienv590e55a
Jan 6Rename aienv to envibea7297e7
Jan 6Fix DB string classification22abdd2
Jan 6v0.2.0: DX polishe221958
Jan 31Fix env_blind_set access28393bb
Jan 31v0.2.6 shipped

Roughly two months from "I have an idea" to a stable, published package. Holidays and a day job in between.

What Claude Code was good at: scaffolding and grinding. The initial MCP server structure, the 142 unit tests, the 30+ classification patterns. That's a lot of repetitive but precise work. It also had a natural advantage building MCP integration since MCP is its own protocol. It understood the tool schema, the response format, the error conventions.

What I drove: the security philosophy. The decision that unclassified variables default to placeholder, not full. The decision that env_blind_set exists (a tool that lets the AI write a value the user provides without ever seeing it). The realization that DATABASE_URL needed to be placeholder. These weren't things the AI suggested. They came from thinking about threat models and developer experience.

There's a middle ground too, the stuff we figured out together. The env_check_required tool came from me describing a pain point ("I keep forgetting which env vars I haven't set up") and Claude Code proposing a solution that scans the manifest for required: true variables and reports which are missing. That tool ended up being the most useful one in the whole package.

The git log tells the story honestly. Every commit is co-authored:

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Every single commit. Not because I was being cute, but because it's accurate. This was pair programming. I just happened to be pairing with an AI.

aienv Becomes envibe

The name aienv lasted exactly one day. The problem wasn't just that it was confusable. It was fighting against years of virtualenv, pyenv, conda env muscle memory. Anything ending in env in the Python-adjacent world reads as "environment manager."

The naming journey was three stops: dotenv-guardaienvenvibe.

dotenv-guard was the first instinct, dropped to avoid brand confusion with the dotenv ecosystem. aienv was the second attempt, dropped because it read like a Python tool. envibe was the one that stuck: env + vibe. It was available on npm, it didn't collide with anything in the Python ecosystem, and it captured the spirit of the project. Permission-aware environment variables for the vibe coding era.

Claude Code handled the rename across every file in the repo, which was the easy part. Picking the name was the hard part.


Part 1: The Idea covers the research and design. Next up: Part 3: The Polish, shipping v0.2, finding a security bug, and what I learned.

envibe is open source: GitHub.