1. Overview
Contributor guide for the Butterbase monorepo. Covers architecture, how to add MCP tools, API routes, database migrations, and coding conventions.
2. Monorepo Map
| 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 |
3. Adding a New MCP Tool (4 Steps)
Step 1: Create tool file at services/mcp-server/src/tools/my-new-tool.ts
Decide 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).
Step 2: Register in services/mcp-server/src/create-server.ts
import { registerMyNewTool } from './tools/my-new-tool.js';
// ...
registerMyNewTool(server);
Step 3: Create the backing API route in services/control-api/src/routes/
- Fastify route handler matching the endpoint your tool calls
- Register in
services/control-api/src/index.ts
Step 4: Update documentation in services/mcp-server/src/docs/user-documentation.ts
- Add tool to the relevant section's table in the
SECTIONS object
4. Adding a Database Migration
- IMPORTANT: Use
scripts/migrate.ts or scripts/backfill-migrations.ts, NEVER raw psql
- Migration files:
db/control-plane/NNN_description.sql (sequential numbering, starting at 001_initial_schema.sql)
- Pick the next free three-digit prefix; never edit a committed migration
- Run migrations:
npx tsx scripts/migrate.ts
5. Coding Conventions
| 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") |
6. Running Locally
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 |
7. Testing
- Framework: Vitest
- Run tests per workspace:
cd services/control-api && npm test
- Test files:
__tests__/ directory or co-located *.test.ts
- Build all workspaces:
npm run build (from repo root)
- Type check:
npx tsc --noEmit in each workspace
1---2name: contributing3description: Use when contributing to the Butterbase codebase, adding new MCP tools, creating API routes, writing migrations, or understanding the monorepo architecture4---5
6## 1. Overview
7
8Contributor guide for the Butterbase monorepo. Covers architecture, how to add MCP tools, API routes, database migrations, and coding conventions.
9
10---
11
12## 2. Monorepo Map
13
14| Directory | Package | Purpose |
15|-----------|---------|---------|
16| `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 |
17| `packages/sdk` | `@butterbase/sdk` (v1.2.1) | Published TypeScript SDK. Modules: auth, storage, functions, AI, billing, realtime, admin |
18| `packages/shared` | `@butterbase/shared` | Internal shared types, constants, schema DSL, error types |
19| `packages/plugin` | `@butterbase/plugin` | Claude Code plugin (this package — skills for AI agents) |
20| `services/control-api` | `@butterbase/control-api` | Fastify API server — the brain. Routes, plugins, services. Port 4000 |
21| `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`) |
22| `services/deno-runtime` | — | Serverless function executor. Deno-based worker isolation. Port 7133 |
23| `services/cron-scheduler` | `@butterbase/cron-scheduler` | Cron job runner using node-cron + cron-parser |
24| `services/dashboard` | — | React management UI (Vite + Radix UI) |
25| `services/dashboard-api` | — | Dashboard backend proxy. Port 4100 |
26| `services/docs` | `@butterbase/docs` | Astro/Starlight documentation site |
27| `services/storage-indexer` | — | Cloudflare Worker for S3 event indexing |
28| `db/control-plane` | — | SQL migrations (sequential numbering, `001_` upward). Control plane database schema |
29| `db/data-plane` | — | Per-app database initialization scripts |
30
31---
32
33## 3. Adding a New MCP Tool (4 Steps)
34
35### Step 1: Create tool file at `services/mcp-server/src/tools/my-new-tool.ts`
36
37Decide 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.
38
39For a brand-new standalone tool, follow the pattern from `init-app.ts`:
40
41```typescript
42import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
43import { z } from 'zod';
44import { apiPost } from '../api-client.js';
45
46interface MyResponse {
47 // response shape
48}
49
50export function registerMyNewTool(server: McpServer) {
51 server.tool(
52 'my_new_tool', // snake_case name
53 `Tool description. // Multi-line description with examples
54
55Example:
56 Input: { ... }
57 Output: { ... }
58
59Common errors:
60 - ERROR_CODE: Description`,
61 {
62 // Zod schema for parameters
63 app_id: z.string().describe('The app ID'),
64 param: z.string().describe('Parameter description'),
65 },
66 async ({ app_id, param }) => {
67 const result = await apiPost<MyResponse>(`/v1/${app_id}/my-endpoint`, { param });
68 return {
69 content: [{
70 type: 'text' as const,
71 text: JSON.stringify(result, null, 2),
72 }],
73 };
74 }
75 );
76}
77```
78
79API client functions available: `apiGet`, `apiPost`, `apiPatch`, `apiDelete` (from `../api-client.js`).
80
81### Step 2: Register in `services/mcp-server/src/create-server.ts`
82
83```typescript
84import { registerMyNewTool } from './tools/my-new-tool.js';
85// ...
86registerMyNewTool(server);
87```
88
89### Step 3: Create the backing API route in `services/control-api/src/routes/`
90
91- Fastify route handler matching the endpoint your tool calls
92- Register in `services/control-api/src/index.ts`
93
94### Step 4: Update documentation in `services/mcp-server/src/docs/user-documentation.ts`
95
96- Add tool to the relevant section's table in the `SECTIONS` object
97
98---
99
100## 4. Adding a Database Migration
101
102- **IMPORTANT**: Use `scripts/migrate.ts` or `scripts/backfill-migrations.ts`, NEVER raw `psql`
103- Migration files: `db/control-plane/NNN_description.sql` (sequential numbering, starting at `001_initial_schema.sql`)
104- Pick the next free three-digit prefix; never edit a committed migration
105- Run migrations: `npx tsx scripts/migrate.ts`
106
107---
108
109## 5. Coding Conventions
110
111| Convention | Example |
112|-----------|---------|
113| 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.) |
114| App IDs | `app_` prefix: `app_abc123` |
115| Service keys | `bb_sk_` prefix: `bb_sk_a1b2c3...` |
116| Environment variables | `BUTTERBASE_` prefix: `BUTTERBASE_API_KEY` |
117| Response metadata | `_meta.next_actions` (suggested next tool calls), `_meta.resource_info` (quota/state) |
118| Error codes | `UPPERCASE_WITH_UNDERSCORES`: `AUTH_RLS_POLICY_VIOLATION`, `QUOTA_TABLE_LIMIT` |
119| Domain | `butterbase.ai` (never "nira") |
120
121---
122
123## 6. Running Locally
124
125```bash
126docker-compose -f docker-compose.local.yml up
127```
128
129| Service | Port | URL |
130|---------|------|-----|
131| Control API | 4000 | `http://localhost:4000` |
132| Dashboard API | 4100 | `http://localhost:4100` |
133| Deno Runtime | 7133 | `http://localhost:7133` |
134| Control Plane DB | 5433 | `postgres://localhost:5433` |
135| Data Plane DB | 5435 | `postgres://localhost:5435` |
136| PgBouncer | 6432 | `postgres://localhost:6432` |
137| LocalStack (S3) | 4566 | `http://localhost:4566` |
138
139---
140
141## 7. Testing
142
143- Framework: Vitest
144- Run tests per workspace: `cd services/control-api && npm test`
145- Test files: `__tests__/` directory or co-located `*.test.ts`
146- Build all workspaces: `npm run build` (from repo root)
147- Type check: `npx tsc --noEmit` in each workspace