# nightlife-mcp nightlife-mcp MCP server MCP server for nightlife event discovery backed by Supabase. ## Quick Start **Production endpoint:** `https://api.nightlife.dev/mcp` 1. Get a free API key at [nightlife.dev](https://nightlife.dev) 2. Add to Claude Desktop config (`claude_desktop_config.json`): ```json { "mcpServers": { "nightlife": { "url": "https://api.nightlife.dev/mcp", "headers": { "x-api-key": "YOUR_API_KEY" } } } } ``` For curl, TypeScript SDK, and other clients, see [`CLIENT_SETUP.md`](CLIENT_SETUP.md). ## Implemented (v0.3) - `search_events` - `get_tonight` - `get_event_details` - `search_venues` - `get_venue_info` - `search_performers` - `get_performer_info` - `log_unmet_request` - `create_vip_booking_request` - `get_vip_booking_status` - `get_vip_table_availability` - `get_vip_table_chart` - `get_recommendations` (v0.2, behind `MCP_ENABLE_RECOMMENDATIONS=true`) - Streamable HTTP endpoint with API-key middleware - Structured tool output schemas (`outputSchema`) - Deterministic tool error payloads with stable error codes - Runtime request/tool metrics exposed at `/health` ## Prerequisites - Node.js 18+ - Supabase project URL + service role key ## Setup ```bash cp .env.example .env npm install ``` Set env vars in `.env`: - `SUPABASE_URL` - `SUPABASE_SERVICE_ROLE_KEY` Optional: - `DEFAULT_CITY` (default: `tokyo`) - `MCP_TOP_LEVEL_CITIES` (default example: `tokyo,san-francisco`; controls `available_cities` in unsupported-city responses) - `DEFAULT_COUNTRY_CODE` (default: `JP`) - `NIGHTLIFE_BASE_URL` (default: `https://nightlifetokyo.com`) - `MCP_HTTP_REQUIRE_API_KEY` (default: `true`) - `MCP_HTTP_USE_DB_KEYS` (default: `true`) - `MCP_HTTP_ALLOW_ENV_KEY_FALLBACK` (default: `true`) - `MCP_HTTP_API_KEYS` (comma-separated legacy fallback keys) - `MCP_ENABLE_RECOMMENDATIONS` (default: `false`; enables `get_recommendations`) ## DB API Key Mode (Recommended) HTTP authentication can use persistent API keys from Supabase plus quota tracking. 1) Run SQL migration in your Supabase SQL editor: ```sql -- copy file contents from: -- supabase/migrations/20260219094000_mcp_api_keys.sql ``` 2) Ensure DB auth flags are enabled in `.env`: ```bash MCP_HTTP_REQUIRE_API_KEY=true MCP_HTTP_USE_DB_KEYS=true MCP_HTTP_ALLOW_ENV_KEY_FALLBACK=true ``` 3) Create an API key record: ```bash npm run key:create -- --name claude-desktop --tier starter --daily-quota 1000 --minute-quota 60 ``` This prints the raw `api_key` once. Save it securely and use it in MCP HTTP calls. If the DB RPC is unavailable, fallback to `MCP_HTTP_API_KEYS` works only when `MCP_HTTP_ALLOW_ENV_KEY_FALLBACK=true`. ## Concierge Unmet Request Backlog Public concierge flows can log unsupported user intents to Supabase. Run migration: ```sql -- copy file contents from: -- supabase/migrations/20260226_concierge_unmet_requests.sql ``` Then call MCP tool `log_unmet_request` when no good answer exists from available nightlife data. ## VIP Booking Phase 1 VIP table booking submission and status tracking are backed by Supabase. Run migration: ```sql -- copy file contents from: -- supabase/migrations/20260227143000_vip_phase1_requests_and_queue.sql -- supabase/migrations/20260228124500_add_vip_booking_enabled_to_venues.sql -- and if already deployed before 2026-02-28: -- supabase/migrations/20260228111000_vip_outward_language_defaults.sql -- supabase/migrations/20260301010000_vip_table_availability_chart.sql -- supabase/migrations/20260301114000_vip_table_chart_storage_bucket.sql -- supabase/migrations/20260303093000_vip_dashboard_admin_edits.sql ``` Then call MCP tools: - `create_vip_booking_request` - `get_vip_booking_status` - `get_vip_table_availability` (read per-day table availability by venue/date range) - `get_vip_table_chart` (read structured table chart with optional per-date status overlay and optional `layout_image_url`) Conversation policy for `create_vip_booking_request`: - Confirm booking date/time in venue local time before submitting. - Use dual-date confirmation wording, especially for late-night arrivals (`00:00`-`05:59`). - Required confirmation template: - `Just to confirm: you want a table for [Night Day] night ([Night Date]), arriving around [Time] on [Arrival Day], [Arrival Date] ([Timezone]). I'll submit that as [Night Day] night with [Time] arrival. Is that correct?` - If the user gives a time like `2am` without a day, ask: - `Do you mean 2:00 AM after Thursday night (Friday morning), or after Friday night (Saturday morning)?` - If the user changes day, regenerate confirmation before submission. Ops-tier sessions also have internal queue tools: - `list_vip_reservations` (all outstanding reservations; default statuses: `submitted`, `in_review`, `confirmed`) - `list_vip_requests_for_alerting` (due alerts only) - `mark_vip_request_alert_sent` - `claim_vip_request_after_ack` - `update_vip_booking_status` (set `confirmed`/`rejected`/`cancelled` with audit event) - `upsert_vip_venue_tables` (write venue table definitions + chart coordinates + optional table notes + optional `layout_image_url`) - `upsert_vip_table_availability` (write per-date table statuses) - `upload_vip_table_chart_image` (upload chart image to storage and attach `layout_image_url` to venue table metadata) To discover bookable venues first, use: - `search_venues` with `vip_booking_supported_only=true` - or `get_venue_info` and check `vip_booking_supported` For internal venue-booking workers, claim queue tasks via DB function: - `public.claim_next_vip_agent_task(p_agent_id text)` ### VIP Ops Dashboard Internal dashboard routes: - `GET /ops/login` - `POST /ops/login` - `POST /ops/logout` - `GET /ops/vip-dashboard` Internal admin API routes (cookie-session protected): - `GET /api/v1/admin/vip-bookings` - `GET /api/v1/admin/vip-bookings/:id` - `PATCH /api/v1/admin/vip-bookings/:id` Required env vars: - `VIP_DASHBOARD_ADMINS` (comma-separated `username:password` pairs) Optional env vars: - `VIP_DASHBOARD_SESSION_TTL_MINUTES` (default: `720`) - `VIP_DASHBOARD_SESSION_COOKIE_NAME` (default: `vip_dashboard_session`) ## Run Stdio (local desktop clients): ```bash npm run dev ``` Streamable HTTP: ```bash npm run dev:http ``` For production build: ```bash npm run build npm start ``` For HTTP in production: ```bash npm run start:http ``` Authenticated production smoke check: ```bash NLT_API_KEY=... npm run smoke:prod:auth ``` Optional env overrides: - `NLT_MCP_URL` (default: `https://api.nightlife.dev/mcp`) - `NLT_REST_BASE_URL` (default: `https://api.nightlife.dev/api/v1`) - `NLT_SMOKE_CITY` (default: `tokyo`) Debug web UI for recommendations: ```bash MCP_ENABLE_RECOMMENDATIONS=true npm run dev:http # open http://127.0.0.1:3000/debug/recommendations ``` ## Notes - Date handling supports `tonight`, `this_weekend`, `YYYY-MM-DD`, and `YYYY-MM-DD/YYYY-MM-DD`. - `get_recommendations` returns up to 10 diverse modal slots with dynamic city-aware fallback. - Venue and performer tools include upcoming events snapshots. - `log_unmet_request` writes unresolved user asks to `public.concierge_unmet_requests`. - VIP phase 1 writes booking submissions to `public.vip_booking_requests` and worker queue tasks to `public.vip_agent_tasks`. - VIP inventory writes table definitions to `public.vip_venue_tables` and date-specific statuses to `public.vip_table_availability`. - `search_venues` and `get_venue_info` include `vip_booking_supported` so clients can show exactly which venues accept VIP booking submissions. - `vip_booking_supported` is sourced from `public.venues.vip_booking_enabled` (separate from `guest_list_enabled`). - `create_vip_booking_request` only accepts venues where `vip_booking_supported=true`. - City handling is backed by `public.cities` (`slug`, timezone, and service-day cutoff). - Supported top-level cities are environment-configurable (for example `tokyo` and `san-francisco`) while Tokyo can remain the default. - Stdio transport: no API key check. - HTTP transport (`/mcp`): API key required by default (`MCP_HTTP_REQUIRE_API_KEY=true`). - API key headers: - `Authorization: Bearer ` - `x-api-key: ` - HTTP responses include key tier/source and rate-limit headers when DB-backed auth is active: - `X-API-Key-Tier` - `X-API-Key-Source` - `X-RateLimit-Daily-Limit` - `X-RateLimit-Daily-Remaining` - `X-RateLimit-Minute-Limit` - `X-RateLimit-Minute-Remaining` - Health endpoint: `/health`. - Debug page for manual tool testing: `/debug/recommendations`. - Tool errors are returned as JSON text payloads in `result.content[0].text`: - `INVALID_DATE_FILTER` - `INVALID_EVENT_ID` - `UNSUPPORTED_EVENT_ID` - `EVENT_NOT_FOUND` - `INVALID_VENUE_ID` - `VENUE_NOT_FOUND` - `INVALID_PERFORMER_ID` - `PERFORMER_NOT_FOUND` - `INVALID_BOOKING_REQUEST` - `BOOKING_REQUEST_NOT_FOUND` - `BOOKING_STATUS_UPDATE_FAILED` - `VIP_TASK_NOT_AVAILABLE` - `VIP_ALERT_UPDATE_FAILED` - `VIP_CLAIM_FAILED` - `INVALID_REQUEST` - `REQUEST_WRITE_FAILED` - `DB_QUERY_FAILED` - `INTERNAL_ERROR` ## Contributing & CI See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the workflow and rules. AI agents read [`AGENTS.md`](./AGENTS.md). `CLAUDE.md` is the architecture source of truth.