Local development¶
Just want to run an instance for personal use on your laptop? Follow the quick-start instead — Docker handles everything. This page is for contributors hacking on the source code (changing the dashboard, adding MCP tools, debugging the schema, etc.) where you need a hot-reloading
npm run devsetup.
Setup for running the dashboard + MCP server on your laptop against a local Postgres.
Requirements¶
- Node.js 22+ (the Dockerfile uses 20; 22 is fine for local)
- Postgres 16 running somewhere you can reach (
localhost:5432works) - npm
1. Install deps¶
2. .env.local¶
cat > .env.local <<EOF
DATABASE_URL=postgres://dailyagent:dailyagent@localhost:5432/dailyagent
SELF_HOSTED_USER_ID=$(node -e "console.log(require('crypto').randomUUID())")
MCP_API_KEY=$(openssl rand -hex 32)
EOF
If you already have Postgres running but not the dailyagent user/database, create them:
createuser dailyagent
createdb -O dailyagent dailyagent
psql -d dailyagent -c "ALTER USER dailyagent WITH PASSWORD 'dailyagent';"
Or just run the compose postgres service and point DATABASE_URL at it:
3. Schema + profile¶
Then seed the single-user profile:
source .env.local
psql "$DATABASE_URL" <<EOF
INSERT INTO profiles (id, email, display_name, is_admin)
VALUES ('$SELF_HOSTED_USER_ID', 'you@example.com', 'You', true)
ON CONFLICT (id) DO NOTHING;
EOF
4. Run¶
- Dashboard: http://localhost:3000
- MCP: http://localhost:3000/api/mcp
- Hot reload via Turbopack
Scripts¶
| Command | What it does |
|---|---|
npm run dev |
Dev server (Turbopack, hot reload) |
npm run build |
Production build (standalone output, for Docker) |
npm start |
Start the built app |
npm run lint |
ESLint over everything |
npm test |
Run the Vitest suite once |
npm run test:watch |
Vitest in watch mode |
npm run db:generate |
Generate a new Drizzle migration from schema.ts diff |
npm run db:migrate |
Apply pending migrations |
npm run db:push |
Push schema directly (skip migrations — dev only) |
npm run db:studio |
Open Drizzle Studio (browser-based DB inspector) |
Drizzle Studio¶
Opens a local web UI at a URL it prints (typically https://local.drizzle.studio) where you can browse and edit tables. Useful for seeding test data, inspecting what a tool wrote, or sanity-checking CHECK constraints.
Testing against the MCP endpoint¶
With the dev server running:
source .env.local
curl -s -X POST \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' \
http://localhost:3000/api/mcp
You should see a JSON-RPC response listing the server's capabilities and 34 tools.
List tasks:
curl -s -X POST \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_tasks","arguments":{}}}' \
http://localhost:3000/api/mcp
Adding a new tool¶
- Open the relevant file under
src/lib/mcp/tools/(or create a new one and register it insrc/lib/mcp/server.ts). - Write the tool with
server.tool(name, description, inputSchema, handler). Use shared schemas fromsrc/lib/mcp/tools/validators.tswhere they apply. - If the handler writes to the DB, make sure the tool's Zod input schema matches every relevant CHECK constraint in
src/lib/db/schema.ts. Mismatched schemas are the #1 cause of "works in curl, fails in the agent" bugs. - Add a scope to
src/lib/oauth-scopes.tsif it's a new domain. - Update MCP reference and OpenClaw skill file.
npm test+npm run lint.
Changing the schema¶
# 1. Edit src/lib/db/schema.ts
# 2. Generate a migration:
npm run db:generate
# 3. Review drizzle/<timestamp>_*.sql
# 4. Apply:
npm run db:migrate
If you add or change a CHECK constraint, update the matching validator in src/lib/mcp/tools/validators.ts so the tool schema stays in sync with the DB.
Resetting the DB¶
Quickest path — drop and recreate the public schema:
psql "$DATABASE_URL" -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
npm run db:migrate
# re-seed profile
With the compose Postgres service:
docker compose down
docker volume rm mcp-dailyagent_dailyagent_pgdata
docker compose up -d postgres
npm run db:migrate
# re-seed profile
Running the production build locally¶
Useful for verifying the next build standalone output behaves the same as in Docker before pushing.
Docker compose, but local¶
If you want to run the full container stack on your laptop (useful for reproducing a deploy issue):
The container's entrypoint waits for Postgres, runs migrations, and seeds the profile row automatically. App lands at http://localhost:3000 (compose binds to 127.0.0.1:3000 by default).