From cef86cbcd95fb311304689ef5ea9e2538530dc4a Mon Sep 17 00:00:00 2001 From: jubnl Date: Fri, 10 Apr 2026 02:23:15 +0200 Subject: [PATCH] feat(mcp): add base server instructions for all MCP sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Injects a structured BASE_MCP_INSTRUCTIONS string into every session's initialize response so Claude has data model, workflow, and behavioral context without needing to infer it from tool names alone. Covers: data model hierarchy (trip→day→place→assignment), key discovery workflow (list_trips → get_trip_summary), correct place-to-itinerary flow (search_place → create_place → assign_place_to_day), accommodation creation order, access rules, date/time format, add-on feature list, and common pitfalls (e.g. don't skip search_place, confirm before bulk deletes). Static token deprecation notice is appended on top when applicable. --- server/src/mcp/index.ts | 84 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/server/src/mcp/index.ts b/server/src/mcp/index.ts index e362ffac..7fc74f90 100644 --- a/server/src/mcp/index.ts +++ b/server/src/mcp/index.ts @@ -13,6 +13,83 @@ import { McpSession, sessions, revokeUserSessions, revokeUserSessionsForClient } export { revokeUserSessions, revokeUserSessionsForClient }; +// --------------------------------------------------------------------------- +// Base instructions injected into every MCP session via the initialize response. +// Claude and other clients use these as system-level context before any tool call. +// Keep this actionable and concise — vague prose doesn't help the model. +// --------------------------------------------------------------------------- +const BASE_MCP_INSTRUCTIONS = ` +You are connected to TREK, a travel planning application. Below is a compact reference of the data model, key workflows, and behavioral rules you must follow. + +## Data model + +- **Trip** — top-level container. Has dates, currency, members (owner + collaborators), and optional add-ons. +- **Day** — one calendar day within a trip (YYYY-MM-DD). Days are generated automatically when a trip is created with start/end dates. +- **Place** — a point of interest (POI) stored in the trip's place pool. A place is NOT on the itinerary until it is assigned to a day. +- **Assignment** — links a Place to a Day (ordered, with optional start/end time). This is what builds the daily itinerary. +- **Accommodation** — a hotel or rental linked to a Place and a check-in/check-out day range. +- **Reservation** — a booking record (flight, train, restaurant, etc.) with confirmation details, linked to a day. +- **Day note** — a free-text annotation attached to a day (with optional time label and emoji icon). +- **Budget item** — an expense entry for a trip (amount, category, payer, split between members). +- **Packing item** — a checklist entry grouped into bags and categories. +- **Todo** — a task (not packing-specific) attached to a trip, ordered and togglable. +- **Tag** — a label that can be applied to places for filtering. +- **Collab note / poll / message** — shared notes, decision polls, and chat messages for group trips. +- **Atlas** — global travel journal: bucket list, visited countries and regions. +- **Vacay** — vacation-day planner that tracks leave across team members and years. + +## Key workflows + +**Discovering trips:** Always call \`list_trips\` first when no trip ID has been provided. Never assume a trip ID. + +**Loading trip context:** Before planning or modifying a trip, call \`get_trip_summary\` once. It returns all days (with assignments and notes), accommodations, budget, packing, reservations, collab notes, and todos in a single round-trip. Use this data to answer follow-up questions without extra tool calls. + +**Adding a place to the itinerary (correct order):** +1. \`search_place\` — find the real-world POI; note the \`osm_id\` and/or \`google_place_id\` in the result. +2. \`create_place\` — add it to the trip's place pool, passing the IDs from step 1 (enables opening hours, ratings, and map linking in the app). +3. \`assign_place_to_day\` — schedule it on the desired day using the dayId from \`get_trip_summary\`. + +**Creating an accommodation:** A place must exist in the trip first. Create the place (or reuse an existing one), then call \`create_accommodation\` with that \`place_id\` and the \`start_day_id\`/\`end_day_id\`. + +**Reordering:** Assignments, todos, packing items, and reservations all support positional reordering via dedicated reorder tools. Always read the current order from \`get_trip_summary\` before reordering. + +## Access rules + +- The authenticated user can only access trips they own or are a member of. Never guess at trip IDs. +- Only the trip owner can delete the trip, add members, or remove members. +- Deleting a place removes all of its day assignments as well — warn the user before doing this. +- Trips created via MCP are capped at 90 days. + +## Dates and times + +- All dates use ISO format: **YYYY-MM-DD**. +- Times are strings like **"09:00"** or **"14:30"** (24-hour). Pass \`null\` to clear a time. +- When displaying dates to users, use a friendly human-readable format (e.g. "Mon, Apr 14"). + +## Add-on features + +The following features are optional and may not be available on every TREK instance. Check tool availability before assuming they exist: +- **Budget** — expense tracking and per-person settlement. +- **Packing** — checklist with bags, categories, and templates. +- **Collab** — shared notes, polls, and chat messages for group trips. +- **Atlas** — bucket list and visited-country/region tracking. +- **Vacay** — team vacation-day planner with public holiday integration. + +## Behavioral rules + +- Prefer \`get_trip_summary\` over individual list tools when you need a full picture — it is one call instead of many. +- Use \`search_place\` before \`create_place\` so the app gets structured POI data (coordinates, address, opening hours). Do not skip this step. +- When the user asks to "add X to day Y", resolve both the place (search + create if needed) and the day ID before calling \`assign_place_to_day\`. +- Do not batch destructive operations (delete trip, delete day, delete place) without explicit user confirmation for each. +- Present budget amounts with the trip's currency. Use \`get_trip_summary\` to read the currency field. +- For group trips, always check member IDs via \`list_trip_members\` before calling tools that require a \`userId\` (e.g. budget splits, assignment participants). +`.trim(); + +const STATIC_TOKEN_DEPRECATION_NOTICE = + '\n\n⚠️ IMPORTANT: This MCP session is authenticated with a static API token (trek_…). ' + + 'Static token authentication is deprecated and will be removed in a future version of TREK. ' + + 'Please inform the user that they must migrate to OAuth 2.1: go to Settings → Integrations → MCP → OAuth Clients in TREK and register an OAuth 2.1 application for their MCP client.'; + const SESSION_TTL_MS = 60 * 60 * 1000; // 1 hour const sessionParsed = Number.parseInt(process.env.MCP_MAX_SESSION_PER_USER ?? ""); const MAX_SESSIONS_PER_USER = Number.isFinite(sessionParsed) && sessionParsed > 0 ? sessionParsed : 5; @@ -173,12 +250,7 @@ export async function mcpHandler(req: Request, res: Response): Promise { tools: { listChanged: true }, prompts: { listChanged: true }, }, - ...(isStaticToken && { - instructions: - '⚠️ IMPORTANT: This MCP session is authenticated with a static API token (trek_…). ' + - 'Static token authentication is deprecated and will be removed in a future version of TREK. ' + - 'Please inform the user that they must migrate to OAuth 2.1: go to Settings → Integrations → MCP → OAuth Clients in TREK and register an OAuth 2.1 application for their MCP client.', - }), + instructions: BASE_MCP_INSTRUCTIONS + (isStaticToken ? STATIC_TOKEN_DEPRECATION_NOTICE : ''), } ); registerResources(server, user.id, scopes);