butterbase-ai/contributing
Use when contributing to the Butterbase codebase, adding new MCP tools, creating API routes, writing migrations, or understanding the monorepo architecture
npx skills add https://github.com/butterbase-ai/butterbase-skills --skill contributing
Contributor guide for the Butterbase monorepo. Covers architecture, how to add MCP tools, API routes, database migrations, and coding conventions.
| Directory | Package | Purpose |
|-----------|---------|---------|
| packages/cli | @butterbase/cli (v0.1.3) | Published CLI tool (Commander.js). Commands: init, apps, schema, functions, storage, deploy, data, env, keys, realtime, status, open |
| packages/sdk | @butterbase/sdk (v1.2.1) | Published TypeScript SDK. Modules: auth, storage, functions, AI, billing, realtime, admin |
| packages/shared | @butterbase/shared | Internal shared types, constants, schema DSL, error types |
| packages/plugin | @butterbase/plugin | Claude Code plugin (this package — skills for AI agents) |
| services/control-api | @butterbase/control-api | Fastify API server — the brain. Routes, plugins, services. Port 4000 |
| services/mcp-server | @butterbase/mcp-server | MCP server with ~28 tools (consolidated manage_* action-based tools + a few standalone ones like init_app, deploy_function, select_rows). Runs via stdio or HTTP (served by control-api at /mcp) |
| services/deno-runtime | — | Serverless function executor. Deno-based worker isolation. Port 7133 |
| services/cron-scheduler | @butterbase/cron-scheduler | Cron job runner using node-cron + cron-parser |
| services/dashboard | — | React management UI (Vite + Radix UI) |
| services/dashboard-api | — | Dashboard backend proxy. Port 4100 |
| services/docs | @butterbase/docs | Astro/Starlight documentation site |
| services/storage-indexer | — | Cloudflare Worker for S3 event indexing |
| db/control-plane | — | SQL migrations (sequential numbering, 001_ upward). Control plane database schema |
| db/data-plane | — | Per-app database initialization scripts |
services/mcp-server/src/tools/my-new-tool.tsDecide whether the new capability is a standalone tool (single, self-contained operation like init_app) or another action on an existing umbrella tool (manage_schema, manage_function, etc). Most new operations should be added as actions on an existing manage_* tool — this keeps the surface area small for AI agents.
For a brand-new standalone tool, follow the pattern from init-app.ts:
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { apiPost } from '../api-client.js';
interface MyResponse {
// response shape
}
export function registerMyNewTool(server: McpServer) {
server.tool(
'my_new_tool', // snake_case name
`Tool description. // Multi-line description with examples
Example:
Input: { ... }
Output: { ... }
Common errors:
- ERROR_CODE: Description`,
{
// Zod schema for parameters
app_id: z.string().describe('The app ID'),
param: z.string().describe('Parameter description'),
},
async ({ app_id, param }) => {
const result = await apiPost<MyResponse>(`/v1/${app_id}/my-endpoint`, { param });
return {
content: [{
type: 'text' as const,
text: JSON.stringify(result, null, 2),
}],
};
}
);
}
API client functions available: apiGet, apiPost, apiPatch, apiDelete (from ../api-client.js).
services/mcp-server/src/create-server.tsimport { registerMyNewTool } from './tools/my-new-tool.js';
// ...
registerMyNewTool(server);
services/control-api/src/routes/services/control-api/src/index.tsservices/mcp-server/src/docs/user-documentation.tsSECTIONS objectscripts/migrate.ts or scripts/backfill-migrations.ts, NEVER raw psqldb/control-plane/NNN_description.sql (sequential numbering, starting at 001_initial_schema.sql)npx tsx scripts/migrate.ts| Convention | Example |
|-----------|---------|
| MCP tool names | snake_case. Two flavours: standalone (init_app, deploy_function, select_rows) and manage_* umbrella tools that take an action enum (manage_schema, manage_rls, manage_function, manage_frontend, etc.) |
| App IDs | app_ prefix: app_abc123 |
| Service keys | bb_sk_ prefix: bb_sk_a1b2c3... |
| Environment variables | BUTTERBASE_ prefix: BUTTERBASE_API_KEY |
| Response metadata | _meta.next_actions (suggested next tool calls), _meta.resource_info (quota/state) |
| Error codes | UPPERCASE_WITH_UNDERSCORES: AUTH_RLS_POLICY_VIOLATION, QUOTA_TABLE_LIMIT |
| Domain | butterbase.ai (never "nira") |
docker-compose -f docker-compose.local.yml up
| Service | Port | URL |
|---------|------|-----|
| Control API | 4000 | http://localhost:4000 |
| Dashboard API | 4100 | http://localhost:4100 |
| Deno Runtime | 7133 | http://localhost:7133 |
| Control Plane DB | 5433 | postgres://localhost:5433 |
| Data Plane DB | 5435 | postgres://localhost:5435 |
| PgBouncer | 6432 | postgres://localhost:6432 |
| LocalStack (S3) | 4566 | http://localhost:4566 |
cd services/control-api && npm test__tests__/ directory or co-located *.test.tsnpm run build (from repo root)npx tsc --noEmit in each workspaceTake butterbase-ai/contributing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npx.
Without those the skill loads but fails at the first command.