Baseline
Baseline is a Next.js + Supabase app for daily operations, triage, implementations, and secure calendar-derived briefing signals.
Local Setup
- Install dependencies:
npm install
- Create
.env.local with your Supabase keys and optional provider keys (see Environment Variables below).
- Run the app:
npm run dev
Environment Variables
Required for the app: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY. Optional: DEFAULT_USER_ID, LLM_ADMIN_USER_ID, and the settings below.
If you want MCP access outside the browser app, or you still maintain the legacy ChatGPT custom GPT fallback, also configure:
MISSION_CONTROL_API_KEY for legacy or internal machine-auth MCP callers
MISSION_CONTROL_ACTIONS_API_KEY only if you still use the private ChatGPT custom GPT Actions fallback
MISSION_CONTROL_FAMCAL_API_KEY only if the FamCal Sunday brief reads your personal tasks
MISSION_CONTROL_USER_ID for the user that machine-auth requests run as
DEPLOYMENT_ROLE=main|mcp to split the protected main app from the public MCP deployment
MCP_CANONICAL_APP_URL for stable user-facing URLs returned from search / fetch
MCP_UPSTREAM_API_URL for the public MCP deployment to call the protected main app upstream routes
Calendar settings
CALENDAR_SOURCE=local|ical|none
local: load events from local ICS file path (CALENDAR_LOCAL_ICS_PATH)
ical: load events from WORK_ICAL_URL
none: skip ingest and only serve already-stored sanitized rows
WORK_ICAL_URL= optional remote Outlook/M365 ICS feed URL
CALENDAR_LOCAL_ICS_PATH=data/calendar/work-calendar.ics
CALENDAR_RETENTION_DAYS=183
CALENDAR_FUTURE_HORIZON_DAYS=30
CALENDAR_BODY_MAX_CHARS=4000
CALENDAR_STORE_BODY=false (default privacy mode)
ENABLE_CALENDAR_ENRICHMENT=false (reserved)
Planner settings
PLANNER_ENABLE_CRITICAL_EXCEPTION=false
PLANNER_CRITICAL_EXCEPTION_THRESHOLD=90
NEXT_PUBLIC_SPRINT_HOLIDAYS=2026-01-01,2026-05-25,2026-07-03 optional comma-separated YYYY-MM-DD list used by Today sprint pacing; excluded from workday counts in addition to weekends
Machine auth settings
MISSION_CONTROL_API_KEY: backend key used by legacy or internal machine-auth MCP callers and other existing external callers
MISSION_CONTROL_ACTIONS_API_KEY: optional backend key used only by the legacy private ChatGPT custom GPT Actions integration
MISSION_CONTROL_USER_ID: required when either machine key is enabled
MISSION_CONTROL_ACTIONS_API_KEY is a Mission Control backend key, not an OpenAI API key
- Use a distinct value for
MISSION_CONTROL_ACTIONS_API_KEY so /api/mcp remains reserved for the primary MCP machine-auth key
- The actions key is restricted to the allowlisted non-delete routes used by the legacy ChatGPT Actions surface
- Delete operations are intentionally excluded from the ChatGPT Actions v1 fallback surface
MISSION_CONTROL_FAMCAL_API_KEY: optional backend key for the FamCal Sunday brief (FamCal stores the same value as BASELINE_API_KEY). Give it its own value. It sees only personal to-dos: tasks that carry the personal tag themselves, have no sprint, and are either standalone (no project, application or section) or in a project that is itself tagged personal. Anything else, e.g. a personal task inside a work or build project, is left out of the list and is a plain 404:
- Routes:
GET /api/tasks (always filtered to tag=personal; only tag, limit, offset, status, owner, needs_review, due_soon, include_done, include_parked and include_missed are accepted, each at most once; any other parameter is a 400, even when empty), GET|PATCH /api/tasks/{id}, GET|POST /api/tasks/{id}/comments, POST /api/tasks/park/{id}. Everything else, and every DELETE, is 403.
- Any other task is a 404 on the per-task routes. Dependency rows in the list keep
unresolved but their title ("Another task" / "A commitment"), status and linked ids are redacted.
- Tasks come back in a fixed FamCal shape: the task's own fields (not
source_url), recurrence as its scheduling fields only (never the template's id), its project and section only when the project is itself tagged personal, and never an application or sprint (those are null), so a personal task filed under a work project shows nothing about it.
- PATCH accepts only
status (Done, Parked, or back to an open status from Done/Parked), and (a date; clearing is a 400); any other field is a 400. Writes re-check the tag at write time; a status change that races another change is a 409.
Remote MCP deployment settings
DEPLOYMENT_ROLE=main on the normal app deployment
DEPLOYMENT_ROLE=mcp on the public MCP-only deployment
MCP_CANONICAL_APP_URL=https://your-main-app.example.com on both deployments
MCP_UPSTREAM_API_URL=https://your-main-app.example.com on the public mcp deployment
- The public
mcp deployment serves only /api/mcp, /oauth/*, /.well-known/*, /login, and /auth/callback
- The protected main deployment exposes
/api/mcp-upstream and /api/mcp-upstream/* for bearer-token upstream calls only
Focus directive API
GET /api/focus: returns current active focus directive (active) and optional fallback note
POST /api/focus: creates a focus directive (supports implementation, stakeholder, task_type, query scopes)
PATCH /api/focus/:id: updates an existing directive (including activation/deactivation)
POST /api/focus/clear: clears active directive(s) for the current user
Calendar Privacy Model
- Calendar exports in
data/calendar/ are ignored by git.
- Runtime calendar ingestion reads explicit source config only (no XML sharing URL discovery at runtime).
- Stored data is sanitized and scoped by user with Supabase RLS.
/api/calendar returns only approved fields for brief generation and UI cards.
- User-authored meeting context is stored separately (
calendar_event_context) and merged into /api/calendar for planner use.
PATCH /api/calendar supports saving or clearing per-event meeting_context notes.
Authentication
- App API routes require an authenticated Supabase session.
- Use
/login to request a magic-link sign-in email.
- Callback route (
/auth/callback) exchanges the auth code and returns to app routes.
- MCP is the primary LLM interface for both ChatGPT and Claude.
- Internal or legacy MCP callers can still use
/api/mcp with MISSION_CONTROL_API_KEY.
- The private ChatGPT custom GPT Actions flow remains available only as a fallback and sends
X-Mission-Control-Key: <MISSION_CONTROL_ACTIONS_API_KEY>.
- Public remote MCP clients use OAuth authorization code + PKCE with dynamic client registration.
- Public remote MCP auth is limited to
/api/mcp and /api/mcp-upstream/*; general app APIs do not accept MCP OAuth bearer tokens.
Remote MCP
Mission Control now supports a public remote MCP deployment for hosted MCP-compatible LLM products.
ChatGPT and Claude should both prefer this MCP path unless you have a specific reason to keep the legacy ChatGPT Actions fallback.
- Public MCP endpoint:
https://<your-mcp-domain>/api/mcp
- OAuth authorize endpoint:
https://<your-mcp-domain>/oauth/authorize
- OAuth token endpoint:
https://<your-mcp-domain>/oauth/token
- Dynamic client registration endpoint:
https://<your-mcp-domain>/oauth/register
- Authorization server metadata:
https://<your-mcp-domain>/.well-known/oauth-authorization-server
- Protected resource metadata:
https://<your-mcp-domain>/.well-known/oauth-protected-resource/api/mcp
Scope model:
mcp.read: read/list/search/fetch/briefing/calendar style operations
mcp.write: non-destructive mutations
mcp.delete: destructive deletes only; request this separately from the default read/write consent
Tool behavior:
- Existing domain-specific tools remain available.
- Generic
search(query) and fetch(id) are additive retrieval tools.
search returns stable URLs pointing to MCP_CANONICAL_APP_URL.
Deployment outline:
- Deploy the existing app normally with
DEPLOYMENT_ROLE=main.
- Deploy the same repo a second time with
DEPLOYMENT_ROLE=mcp.
- Point
MCP_UPSTREAM_API_URL on the public mcp deployment at the protected main app domain.
- Keep
MISSION_CONTROL_API_KEY and MISSION_CONTROL_ACTIONS_API_KEY only on main while migrating legacy clients.
ChatGPT setup:
- Open ChatGPT Developer Mode or MCP app setup.
- Add the public MCP server URL:
https://<your-mcp-domain>/api/mcp.
- Let ChatGPT dynamically register as a public OAuth client.
- Approve the default
mcp.read mcp.write consent in the browser.
- Re-run consent with
mcp.delete only if you explicitly want destructive tools enabled.
Product note:
- As of March 8, 2026, OpenAI Help Center documentation says full write-capable MCP in ChatGPT beta is for Business and Enterprise/Edu, while Pro is read/fetch only in developer mode.
- This is a product-availability constraint, not a code limitation in this repo.
Legacy ChatGPT Custom GPT Fallback
Prefer MCP for both ChatGPT and Claude. Use this section only if you still want the older private custom GPT plus Actions integration as a fallback.
You do not need an OpenAI API key for normal use inside ChatGPT. The only extra credential for this integration is MISSION_CONTROL_ACTIONS_API_KEY.
- Open the GPT builder in ChatGPT and create a private GPT.
- Paste your adapted Mission Control instructions into the GPT Instructions field.
- Import the tracked OpenAPI schema from
/openapi/chatgpt-actions-v1.yaml.
- Configure the action authentication header as
X-Mission-Control-Key with MISSION_CONTROL_ACTIONS_API_KEY.
- Add prompt starters such as
morning brief, midday brief, eod brief, weekly review, monthly review, recommend my today list but do not sync yet, review these meeting notes and suggest updates, and apply these meeting notes to Mission Control.
Notes:
Database
Apply Supabase SQL migrations from supabase/migrations/:
001_initial_schema.sql
002_calendar_schema.sql
003_task_comments_dependencies.sql
004_add_task_type_task.sql
005_focus_planner_brain.sql
006_calendar_event_context.sql
007_llm_model_eval.sql — LLM model catalog, user preferences, and usage telemetry
008_task_status_workflow.sql — Renames task status enum values (Next->Backlog, Scheduled->Planned, Waiting->Blocked/Waiting) and adds In Progress
009_add_quick_capture_feature.sql — Adds quick_capture to LLM feature CHECK constraints
010_missing_rls_policies.sql — Adds missing RLS policies for inbox_items and status_updates
027_add_mcp_oauth.sql — Adds OAuth client, authorization code, access token, and refresh token storage for public remote MCP
028_add_project_focus_scope.sql — Adds project-scoped focus directives
029_add_task_tags.sql — Adds tags for tasks
030_add_project_review_history.sql — Adds durable project status history and persisted weekly/monthly review snapshots
Task status values
After migration 008, the valid task_status enum values are:
Backlog | Planned | In Progress | Blocked/Waiting | Done
The old values (Next, Scheduled, Waiting) no longer exist.
LLM features
Supported feature names for model preferences and usage tracking:
briefing_narrative | intake_extraction | quick_capture
Validation
Run checks:
npm run lint
npm run build
npm run test:planner-scoring
npm run test:calendar-sanitize
npm run test:mcp-oauth
Calendar API contract check (requires running app at http://localhost:3000 by default):
npm run dev
# in another terminal
npm run test:calendar-api
Optional env for contract test:
CALENDAR_API_BASE_URL (default http://localhost:3000)
CALENDAR_TEST_BEARER_TOKEN (if you want to provide an existing token)