{"transport":"streamable-http","status":"tool_dispatch_ready","message":"Meridian Crystalline exposes a remote MCP JSON-RPC surface with development PKCE OAuth protecting tools/list and tools/call.","bootstrap":{"server_name":"meridian-mcp","codename":"Meridian Crystalline","transport_status":"tool_dispatch","tool_count":32,"oauth_status":"development_pkce","primary_surface":"remote"},"authorization_server":{"issuer":"https://meridian.coach","authorization_endpoint":"https://meridian.coach/api/oauth/authorize","token_endpoint":"https://meridian.coach/api/oauth/token","registration_endpoint":"https://meridian.coach/api/oauth/register","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none"],"scopes_supported":["mcp:read","mcp:write"],"service_documentation":"https://meridian.coach","response_modes_supported":["query"]},"protected_resource":{"resource":"https://meridian.coach/api/mcp","authorization_servers":["https://meridian.coach"],"scopes_supported":["mcp:read","mcp:write"],"bearer_methods_supported":["header"],"resource_documentation":"https://meridian.coach"},"tools":[{"name":"coach_context","description":"Return the persona-aware coaching starting context, anchored by the latest sync brief and current state.","inputSchema":{"type":"object","properties":{"personaId":{"type":"string"}},"additionalProperties":false}},{"name":"athlete_world","description":"Return the current athlete state, recent evidence, open actions, and sync summary.","inputSchema":{"type":"object","properties":{"evidenceLimit":{"type":"number","minimum":1,"maximum":50},"scanRunLimit":{"type":"number","minimum":1,"maximum":25},"stravaDataDir":{"type":"string"},"whoopDataDir":{"type":"string"},"weightFilePath":{"type":"string"}},"additionalProperties":false}},{"name":"get_evidence","description":"Fetch evidence rows by kind, time window, race, or source.","inputSchema":{"type":"object","properties":{"limit":{"type":"number","minimum":1,"maximum":100},"kind":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"source":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"after":{"type":"string"},"before":{"type":"string"},"query":{"type":"string"},"sourceRef":{"type":"string"},"evidenceId":{"type":"string"}},"additionalProperties":false}},{"name":"get_theses","description":"Read current-fitness, relationship, weekly-focus, and race-readiness theses.","inputSchema":{"type":"object","properties":{"limit":{"type":"number","minimum":1,"maximum":50},"kind":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"freshness":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"additionalProperties":false}},{"name":"list_actions","description":"List open sessions, nudges, check-ins, and race todos.","inputSchema":{"type":"object","properties":{"limit":{"type":"number","minimum":1,"maximum":100},"status":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"kind":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"includeDone":{"type":"boolean"}},"additionalProperties":false}},{"name":"list_races","description":"List upcoming and historical races for the athlete.","inputSchema":{"type":"object","properties":{"limit":{"type":"number","minimum":1,"maximum":100},"timeframe":{"type":"string","enum":["upcoming","past","all"]},"sport":{"type":"string"},"priority":{"type":"string"}},"additionalProperties":false}},{"name":"get_fitness_chart","description":"Return CTL, ATL, and TSB chart data for Claude rendering.","inputSchema":{"type":"object","properties":{"days":{"type":"number","minimum":1,"maximum":365}},"additionalProperties":false}},{"name":"get_activity_streams","description":"Return stream summaries for an activity evidence record.","inputSchema":{"type":"object","properties":{"evidenceId":{"type":"string"},"activityId":{"type":"string"},"streams":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"targetPoints":{"type":"number","minimum":1,"maximum":240}},"additionalProperties":false}},{"name":"get_weight_chart","description":"Return weight trend and body composition chart data.","inputSchema":{"type":"object","properties":{"days":{"type":"number","minimum":1,"maximum":365}},"additionalProperties":false}},{"name":"get_weight_trend","description":"Fit the body-weight trend over a trailing window: least-squares slope in kg/week with its 95% interval, the day-to-day noise band, the trend value today (not the last raw reading), and a verdict against the 0.45-0.9 kg/week band — under, within, over, or not-enough-data. Refuses to report a slope when the window is too short or too sparse, and says when it will be answerable.","inputSchema":{"type":"object","properties":{"windowDays":{"type":"number","minimum":7,"maximum":365,"description":"Trailing window to fit over (default 28)."},"targetKg":{"type":"number","exclusiveMinimum":0,"description":"Target weight. Defaults to the athlete profile's weight goal."},"targetDate":{"type":"string","description":"Date the target is wanted by, YYYY-MM-DD — e.g. a race date."},"timezone":{"type":"string","description":"Override the zone the local day is resolved in. Per-call only; never written to the profile."}},"additionalProperties":false}},{"name":"get_power_curve","description":"Best mean power over 1, 5, 10, 20 and 60 minutes — for one activity, or the rolling best across a trailing window with an optional same-length comparison window for year-on-year progression. Read from the curve stored at ingest, never recomputed. States plainly that a best 20-minute inside a non-maximal ride is not an FTP test.","inputSchema":{"type":"object","properties":{"days":{"type":"number","minimum":1,"maximum":3650,"description":"Trailing window for the rolling best (default 365)."},"activityId":{"type":"string","description":"Strava activity id — return that one ride's curve instead of a window."},"evidenceId":{"type":"string","description":"Evidence id — return that one ride's curve instead of a window."},"compare":{"type":"boolean","description":"Also report the window of the same length immediately before, with deltas."},"perActivity":{"type":"boolean","description":"List each contributing ride's own curve, newest first."},"limit":{"type":"number","minimum":1,"maximum":100,"description":"Cap on the per-activity list (default 20)."}},"additionalProperties":false}},{"name":"get_efficiency_trend","description":"Does the same effort now cost fewer beats? Fits the efficiency factor (normalised power per bpm for rides, metres per beat for runs) over a trailing window, per sport, and reports the change as heart rate at a reference output. Suppresses sessions where the measure is meaningless — under 30 minutes, incomplete HR, outside the aerobic band, or a ride without power — and says how many were excluded and why.","inputSchema":{"type":"object","properties":{"days":{"type":"number","minimum":21,"maximum":1825,"description":"Trailing window to fit over (default 120)."},"sport":{"type":"string","enum":["ride","run"],"description":"Restrict to one sport. The two are never mixed — the units differ."},"includeExcluded":{"type":"boolean","description":"List the sessions that did not qualify, and why (default true)."}},"additionalProperties":false}},{"name":"get_intake_summary","description":"Eating window per local day, overnight fast between adjacent logged days, and the two streaks — dry days and window hits — each with current, best-ever and a trailing count out of the days actually logged. Unlogged days count as unknown, never as failures. No calories, ever.","inputSchema":{"type":"object","properties":{"days":{"type":"number","minimum":1,"maximum":365,"description":"Trailing window in days (default 30)."},"targetWindowHours":{"type":"number","minimum":1,"maximum":24,"description":"Eating-window target a day must fit inside to count as a hit (default 8)."},"timezone":{"type":"string","description":"Override the zone the local day is resolved in. Per-call only; never written to the profile."}},"additionalProperties":false}},{"name":"get_ride_window","description":"Answer whether riding is on, and in which hours, from the hourly forecast at the athlete's derived location. Returns a verdict per day (ride / marginal / move / no), the best window, what closes it and when, the best day of the week, and the named rules behind each call. Location comes from the GPS start of the most recent activity unless overridden.","inputSchema":{"type":"object","properties":{"date":{"type":"string","description":"Day to focus on, YYYY-MM-DD. Defaults to tomorrow."},"days":{"type":"number","minimum":1,"maximum":16,"description":"How many days ahead to judge (default 7)."},"latitude":{"type":"number","description":"Override the derived location."},"longitude":{"type":"number","description":"Override the derived location."},"location":{"type":"string","description":"Place name to check instead of the derived location, e.g. \"Berlin\"."},"earliestHour":{"type":"string","description":"Earliest hour available to ride, HH:MM. Daylight is the default bound."},"latestHour":{"type":"string","description":"Latest hour available to ride, HH:MM."},"minimumWindowHours":{"type":"number","minimum":1,"maximum":12,"description":"Shortest run of hours that counts as a ride (default 2)."},"maxCacheAgeMinutes":{"type":"number","minimum":0,"maximum":1440,"description":"How stale a cached forecast may be before it is refetched (default 60)."},"refresh":{"type":"boolean","description":"Refetch from Open-Meteo even if the cache is fresh."},"timezone":{"type":"string","description":"Override the zone the local day is resolved in. Per-call only; never written to the profile."}},"additionalProperties":false}},{"name":"plan_week","description":"Plan the training week, and re-plan it any morning. Derives the week's load budget from current CTL, the safe ramp for the phase, and how far out the race is — not from a repeating template — then allocates sessions to the days that actually have a window, recording the weather, free time and readiness each allocation assumed. Re-run mid-week and it reconciles what happened, re-tests today's assumptions and re-spends only what is left. Capacity lost to illness or a bad night lowers the target; opportunity lost to weather or a meeting may move within the week. Nothing is ever carried into the next week. Proposes by default and writes nothing unless commit is set. Pass freeBusy to make the placements diary-aware; without it they are against daylight and the result says so.","inputSchema":{"type":"object","properties":{"weekOf":{"type":"string","description":"Any date in the week to plan, YYYY-MM-DD. Defaults to the week in progress, or on a Sunday to the week ahead."},"from":{"type":"string","description":"First day to (re)plan, YYYY-MM-DD. Defaults to today. Days before it are reconciled against what actually happened, never re-planned."},"freeBusy":{"type":"object","description":"Optional diary: { source, days: [{ date, busy: [{start,end,label}], free: [...], noBike, note }] }. Supplied means the plan is against real free hours; absent means it is against daylight and says so."},"sessionsPerWeek":{"type":"number","minimum":1,"maximum":7,"description":"Cap on sessions this week (default 5, floor 3)."},"blockStartDate":{"type":"string","description":"Override the block anchor, YYYY-MM-DD. Snapped to its Monday. Default is derived back from race week."},"blockWeeks":{"type":"number","minimum":1,"maximum":52,"description":"Override the default block length used to derive the anchor (default 8)."},"raceId":{"type":"string","description":"Which race the block is aimed at. Defaults to the next A race."},"commit":{"type":"boolean","description":"Write the proposed sessions as actions. Off by default — this capability proposes."},"timezone":{"type":"string","description":"Override the zone the local day is resolved in. Per-call only; never written to the profile."}},"additionalProperties":false}},{"name":"sync_data","description":"Sync connected Strava, WHOOP, or Withings data, then refresh coaching state and return a compact brief when new evidence lands.","inputSchema":{"type":"object","properties":{"service":{"type":"string","enum":["all","strava","whoop","withings"]},"limit":{"type":"number","minimum":1},"overlapDays":{"type":"number","minimum":0,"maximum":30},"refreshState":{"type":"boolean"},"refreshKinds":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"additionalProperties":false}},{"name":"record_weight","description":"Write a manual weight entry into evidence.","inputSchema":{"type":"object","required":["weightKg"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"weightKg":{"type":"number","exclusiveMinimum":0},"date":{"type":"string","description":"YYYY-MM-DD in the athlete's own timezone. Defaults to today, locally."},"time":{"type":"string","description":"Wall-clock time the reading was taken — HH:MM or HH:MM:SS, in the athlete's timezone. Body weight swings 1.0-1.5 kg across a day, so without this the reading cannot be compared with any other and is excluded from the trend. Defaults to now when no date is given; a bare date with no time records as 'time unknown'."},"context":{"type":"string","enum":["post-exercise"],"description":"Declare a band the clock cannot see. Pass 'post-exercise' for a reading taken after a session: it is kept out of the trend, because sweat loss is 1-2 kg and would flatter the slope, and it is filed under its own id so it does not overwrite the day's fasted weigh-in. Everything else is derived from the time of day and cannot be declared."},"notes":{"type":"string"},"fatRatio":{"type":"number","exclusiveMinimum":0}},"additionalProperties":false}},{"name":"record_intake","description":"Log one thing eaten or drunk, with coarse flags only — no calories, no macros. Sets whether it opens the eating window (caloric), and whether it carried alcohol, caffeine, liquid sugar or real protein. Returns the day's eating window recomputed after the write.","inputSchema":{"type":"object","required":["items"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string","description":"Zone this one entry's time is meant in. Per-call only; never written to the profile."},"items":{"type":"array","items":{"type":"string"},"description":"What was eaten or drunk. Never a quantity that was not given."},"at":{"type":"string","description":"HH:MM local, or an ISO datetime. Defaults to now."},"date":{"type":"string","description":"YYYY-MM-DD in the athlete's own timezone. Defaults to today, locally."},"caloric":{"type":"boolean","description":"Does this open or close the eating window? Default true. Black coffee, tea and water are false."},"alcohol":{"type":"boolean"},"caffeine":{"type":"boolean"},"liquidSugar":{"type":"boolean","description":"Juice, soft drinks, sweetened coffee."},"protein":{"type":"boolean","description":"Whether the entry carried real protein. Leave unset when it is not obvious."},"portion":{"type":"string","enum":["small","normal","large"],"description":"Only when obvious. Otherwise leave unset."},"place":{"type":"string"},"notes":{"type":"string"}},"additionalProperties":false}},{"name":"list_intake","description":"List logged intake entries with the evidence id needed to amend or delete each one, plus local time, items and coarse flags. Also rolls them up per day, so a correction can be judged against the eating window it moves.","inputSchema":{"type":"object","properties":{"timezone":{"type":"string","description":"Zone the local day is read in. Per-call only."},"date":{"type":"string","description":"Last local day of the window, YYYY-MM-DD. Defaults to today."},"days":{"type":"number","minimum":1,"description":"How many days back from date. Defaults to 1 — that day only."}},"additionalProperties":false}},{"name":"amend_intake","description":"Correct one logged intake entry by evidence id — items, time, date or any coarse flag. Changing the items or the time moves the entry rather than duplicating it, so exactly one row remains, and every changed field reaches the revision ledger. Returns the affected day's eating window.","inputSchema":{"type":"object","required":["evidenceId"],"properties":{"timezone":{"type":"string","description":"Zone this entry's time is meant in. Defaults to the zone it was logged in. Per-call only."},"evidenceId":{"type":"string","description":"The entry to change, as list_intake reports it."},"items":{"type":"array","items":{"type":"string"},"description":"Replaces what was eaten or drunk; moves the entry. Never a quantity that was not given."},"at":{"type":"string","description":"HH:MM local. Moves the entry."},"date":{"type":"string","description":"YYYY-MM-DD, athlete-local. Moves the entry to another day."},"caloric":{"type":"boolean","description":"Does this open the eating window? Black coffee, tea and water are false."},"alcohol":{"type":"boolean"},"caffeine":{"type":"boolean"},"liquidSugar":{"type":"boolean"},"protein":{"type":"boolean"},"portion":{"type":"string","enum":["small","normal","large"]},"place":{"type":"string"},"notes":{"type":"string"},"clear":{"type":"array","items":{"type":"string"},"description":"Fields to set back to unset: protein, portion, place, notes. An omitted field is left alone, so this is the only way to unset one."}},"additionalProperties":false}},{"name":"delete_intake","description":"Remove one logged intake entry by its evidence id. What it said is written field by field into the revision ledger first, and the day's eating window is recomputed from the remaining rows.","inputSchema":{"type":"object","required":["evidenceId"],"properties":{"evidenceId":{"type":"string","description":"The entry to remove, as list_intake reports it."}},"additionalProperties":false}},{"name":"record_note","description":"Persist a free-text coaching note or journal entry as evidence.","inputSchema":{"type":"object","required":["text"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"title":{"type":"string"},"text":{"type":"string"},"occurredAt":{"type":"string"},"source":{"type":"string"},"metadata":{"type":"object"}},"additionalProperties":false}},{"name":"record_constraint","description":"Persist travel, injury, schedule, or life constraints as evidence.","inputSchema":{"type":"object","required":["text"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"title":{"type":"string"},"text":{"type":"string"},"occurredAt":{"type":"string"},"source":{"type":"string"},"metadata":{"type":"object"}},"additionalProperties":false}},{"name":"record_session_feedback","description":"Persist qualitative post-session feedback as evidence.","inputSchema":{"type":"object","required":["text"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"title":{"type":"string"},"text":{"type":"string"},"occurredAt":{"type":"string"},"source":{"type":"string"},"metadata":{"type":"object"}},"additionalProperties":false}},{"name":"update_athlete_profile","description":"Update FTP, HR, location, and athlete preference fields.","inputSchema":{"type":"object","properties":{"displayName":{"type":"string"},"timezone":{"type":["string","null"]},"locationCity":{"type":["string","null"]},"locationCountry":{"type":["string","null"]},"ftp":{"type":["number","null"]},"maxHr":{"type":["number","null"]},"restingHr":{"type":["number","null"]},"heightCm":{"type":["number","null"]},"weightGoalKg":{"type":["number","null"]},"activePersonaId":{"type":"string"},"dietaryPrefs":{}},"additionalProperties":false}},{"name":"create_race","description":"Create a new race record.","inputSchema":{"type":"object","required":["name","date"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"name":{"type":"string"},"date":{"type":"string"},"endDate":{"type":["string","null"]},"priority":{"type":"string"},"sport":{"type":["string","null"]},"distanceKm":{"type":["number","null"]},"elevationM":{"type":["number","null"]},"location":{"type":["string","null"]},"strategy":{},"result":{}},"additionalProperties":false}},{"name":"update_race","description":"Update a race record or race metadata.","inputSchema":{"type":"object","required":["raceId"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"raceId":{"type":"string"},"name":{"type":"string"},"date":{"type":"string"},"endDate":{"type":["string","null"]},"priority":{"type":"string"},"sport":{"type":["string","null"]},"distanceKm":{"type":["number","null"]},"elevationM":{"type":["number","null"]},"location":{"type":["string","null"]},"strategy":{},"result":{}},"additionalProperties":false}},{"name":"update_race_strategy","description":"Update structured race strategy state.","inputSchema":{"type":"object","required":["raceId","strategy"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"raceId":{"type":"string"},"strategy":{},"merge":{"type":"boolean"}},"additionalProperties":false}},{"name":"create_action","description":"Create a coaching action such as a session or race todo.","inputSchema":{"type":"object","required":["kind","title"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"kind":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"priority":{"type":"string"},"dueAt":{"type":["string","null"]},"sourceEvidenceIds":{"type":["array","null"],"items":{"type":"string"}},"metadata":{}},"additionalProperties":false}},{"name":"update_action","description":"Update an existing action's title, status, or metadata.","inputSchema":{"type":"object","required":["actionId"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"actionId":{"type":"string"},"kind":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"priority":{"type":"string"},"dueAt":{"type":["string","null"]},"sourceEvidenceIds":{"type":["array","null"],"items":{"type":"string"}},"metadata":{},"completedAt":{"type":["string","null"]}},"additionalProperties":false}},{"name":"complete_action","description":"Mark an action as done.","inputSchema":{"type":"object","required":["actionId"],"properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"actionId":{"type":"string"},"completedAt":{"type":"string"},"status":{"type":"string"}},"additionalProperties":false}},{"name":"refresh_state","description":"Force thesis regeneration after meaningful evidence changes.","inputSchema":{"type":"object","properties":{"displayName":{"type":"string"},"timezone":{"type":"string"},"locationCity":{"type":"string"},"locationCountry":{"type":"string"},"kinds":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"additionalProperties":false}}],"name":"meridian","version":"0.3.0-alpha.1","description":"Meridian Crystalline remote MCP surface"}