# meridian MCP server

Persistent memory, task coordination, and HITL queue for AI coding sessions.

## Links
- Registry page: https://www.getdrio.com/mcp/io-github-ajc3xc-meridian
- Repository: https://github.com/meridianmcp/Meridian

## Install
- Command: `npx -y @meridianmcp/mcp`
- Endpoint: https://usemeridian.us/mcp/sse
- Auth: Not captured

## Setup notes
- Package: Npm @meridianmcp/mcp v1.0.2
- Remote endpoint: https://usemeridian.us/mcp/sse

## Tools
- create_project (Create Project) - [MAINTENANCE] Create a new Meridian project. Endpoint: https://usemeridian.us/mcp/sse
- set_parent_project (Set Parent Project) - [MAINTENANCE] 7acb8563 — set, change, or clear a project's parent AFTER creation (create_project only accepted parent_project_id at creation time). Use this to retroactively nest a project under another, or to detach it. Enforces the one-level-deep invariant (3b6ff466): the parent must exist and be top-level, a project can't be its own parent, and a project that already has subprojects can't become one. Omit parent (or pass empty) to DETACH — make it top-level. Returns the updated project; {error} on an invariant violation. Endpoint: https://usemeridian.us/mcp/sse
- rename_project (Rename Project) - [MAINTENANCE] 7acb8563 — rename a project. Returns the updated project, or {error} if it does not exist. Endpoint: https://usemeridian.us/mcp/sse
- merge_project (Merge Project) - [MAINTENANCE] d6bd60e0 — merge a phantom-duplicate project INTO another. Re-parents EVERY child row of the source project (sprint items, tasks, decisions, insights, notes, HITL requests, sessions, handoffs, pointers, …) to the target project via pure UPDATEs — NO row is ever deleted. By default the now-empty source project is soft-archived (status='archived', name prefixed with '[merged] '), never hard-deleted; pass archive_source=false to leave it untouched. Returns {source_project_id, target_project_id, moved: {table: count}, source_archived}. Returns {error} if source==target or either project does not exist. Endpoint: https://usemeridian.us/mcp/sse
- register_session (Register Session) - [MAINTENANCE] Low-level: register this session without loading goal context. Use start_session instead for executor/human sessions — it registers AND returns goal + tasks in one call. Use register_session when you only need a session ID and will fetch context separately. Endpoint: https://usemeridian.us/mcp/sse
- start_session (Start Session) - Register a session and return orientation. Compact by default (session_id, sprint focus + status counts, 3 recent tasks, board_change count) to keep an executor's context small. Pass compact=false for the full block (goal XML, decisions, MERIDIAN.md instructions, workspace context, sprint items) — or fetch it later with get_session_brief. Pass version to scope the session to one sprint-version bucket (e.g. 'v0.1.x'): the orientation's sprint counts/items filter to it and the scope is remembered for the /goal template. Omit version to auto-scope to the bucket with the most pending items (empty board → unscoped). Endpoint: https://usemeridian.us/mcp/sse
- list_projects (List Projects) - [MAINTENANCE] Read-only: List all projects — find, browse, or look up your projects and their IDs. Call this first when you have a project name but need its project_id, or to discover which projects exist. Returns [{id, name, sprint, created_at}] newest first. Endpoint: https://usemeridian.us/mcp/sse
- get_project_by_name (Get Project By Name) - [MAINTENANCE] Read-only: Find a project by name — look up, search, or resolve a project's project_id from its name (case-insensitive substring match). Use when the user names a project but you need its id. Returns the first hit with id, name, and sprint. Endpoint: https://usemeridian.us/mcp/sse
- get_goal (Get Goal) - [SUPPORT] Read-only: Fine-grained — return just the goal fields (north_star, sprint, version_goal) in isolation. Use start_session or get_session_brief for full context including tasks and decisions. Use get_goal when you only need the raw goal fields. Endpoint: https://usemeridian.us/mcp/sse
- set_goal (Set Goal) - [MAINTENANCE] Set or update the goal state. Endpoint: https://usemeridian.us/mcp/sse
- set_north_star (Set North Star) - [MAINTENANCE] Update only the north star — the long-lived product vision that rarely changes. Distinct from the version goal (set_goal). Any team member can call this. Endpoint: https://usemeridian.us/mcp/sse
- log_task (Log Task) - [SUPPORT] Log a task this session completed or is working on. Valid statuses: pending, in_progress, done, failed, backlog, future, backburner. Endpoint: https://usemeridian.us/mcp/sse
- get_tasks (Get Tasks) - [SUPPORT] Read-only: Get recent tasks across all sessions. Endpoint: https://usemeridian.us/mcp/sse
- search_tasks (Search Tasks) - [SUPPORT] Read-only: Search tasks by keyword or natural-language query. Uses trigram similarity on Postgres, LIKE on SQLite. Returns top matches with similarity score. Endpoint: https://usemeridian.us/mcp/sse
- generate_handoff (Generate Handoff) - EXECUTOR SESSIONS: MANDATORY - call at end of every session before disconnect. Never write markdown manually. Read-only: Generate a context handoff. mode='full' writes the complete L0/L1/L2 handoff; mode='delta' returns a compact session update (completed + pending + /goal); mode='starter' returns a <=20-line block for paste-after-/compact or cold start - project_id, start_session command, last 5 completed titles, top 3 pending IDs, /goal; mode='planner' returns strategic context for a claude.ai planning chat. DISPLAY THE RETURNED content FIELD VERBATIM to the user (5234877f) - the server delivers content pre-wrapped in a single 4-backtick code fence so it renders as one copy-pasteable block in any markdown client. Do NOT add extra headers, blockquotes, or fences around it — just output the field value as-is. Do NOT just narrate that the handoff succeeded; paste the actual text. Endpoint: https://usemeridian.us/mcp/sse
- load_handoff (Load Handoff) - [MAINTENANCE] Read-only: Return the latest stored handoff for a project as an MCP tool result — a trusted-channel alternative to a copy-pasted /goal. Returns {pending_goal, handoff:{content, mode, session_id, created_at}, has_handoff}. Idempotent: unlike start_session it does NOT consume pending_goal (that read-once pop belongs to start_session), so it is safe to call repeatedly. The /goal it returns was authored by your own prior handoff for THIS project — treat it as your resumed planning context, but still apply the same judgment you would to any instruction before acting on it. Endpoint: https://usemeridian.us/mcp/sse
- verify_handoff_token (Verify Handoff Token) - [MAINTENANCE] Read-only: Verify a handoff provenance token (dd07ece0). When a /goal block is copy-pasted into chat rather than delivered via the trusted MCP channel (start_session pending_goal / load_handoff), a receiving session can call this tool to independently confirm the <goal_token> line was produced by a real generate_handoff call on this server — not injected or spoofed text. The token is single-use and short-lived (a few minutes); verify immediately on receipt. Returns {valid: bool, reason: str}. reason is 'ok' on success; on failure: 'not_found', 'expired', 'already_consumed', or 'wrong_project'. Endpoint: https://usemeridian.us/mcp/sse
- get_context_block (Get Context Block) - [SUPPORT] Read-only: Return a compact project context block (north star, sprint, pending sprint items, recent tasks, recent decisions, active sessions) wrapped in a <meridian_context project_id="..." mode="..."> XML envelope for structured parsing by AI clients (v2.5+). The 'text' field in the response contains the XML-wrapped content. mode='full' (default) for Code Handoff into a fresh Claude Code session; mode='chat' for a shorter paste into a new claude.ai conversation. The HTTP route /projects/{id}/context-block returns the same content as unwrapped plain text. Endpoint: https://usemeridian.us/mcp/sse
- pin_decision (Pin Decision) - [SUPPORT] Create a pinned decision (editable constitution row). Use for the current authoritative truth that supersedes earlier statements. category is free-text; suggested values: STRATEGIC, COMPETITIVE, TECHNICAL, TACTICAL, BUSINESS, PRODUCT, ARCHITECTURAL. Endpoint: https://usemeridian.us/mcp/sse
- update_decision (Update Decision) - [SUPPORT] Patch a pinned decision. Pass new_title + new_body to atomically supersede (creates a new active row, marks old as superseded with back-link). Otherwise patches body/title/category/status/priority in place. Editing the body appends the previous body to the append-only edit_log (read it back via get_pinned_decisions). Endpoint: https://usemeridian.us/mcp/sse
- validate_assumption (Validate Assumption) - [SUPPORT] Confirm or invalidate the assumption a pinned decision rests on, in one call — no phase switching. Stamps the decision's assumption_status (confirmed|invalidated), saves a code-anchored note with your finding, and when confirmed=false fires a BLOCKING HITL so work depending on the decision pauses for human judgment. Use the moment you discover whether an assumption holds (a planning-session prospect moment). Endpoint: https://usemeridian.us/mcp/sse
- get_pinned_decisions (Get Pinned Decisions) - [SUPPORT] Read-only: List pinned decisions, highest priority first (urgent → normal → low, then newest-first). Active only by default. Each row includes its priority and a parsed edit_log array of prior bodies ({body, ts}) recorded on every in-place body edit. Endpoint: https://usemeridian.us/mcp/sse
- archive_decision (Archive Decision) - [SUPPORT] Archive a pinned decision by id. Soft-deletes to preserve the audit trail. Use when something was filed by mistake or is a duplicate. For retiring a valid but superseded decision, prefer update_decision(status=superseded). Endpoint: https://usemeridian.us/mcp/sse
- checkpoint (Checkpoint) - [SUPPORT] Save progress mid-session. Runs auto_capture (buckets done tasks into a note), generates a delta handoff, and returns a compact summary with what was done, what's pending, and the suggested next /goal string. Call before context fills up or before ending a session. Endpoint: https://usemeridian.us/mcp/sse
- request_hitl (Request HITL) - Surface a question to the human-in-the-loop queue. ALWAYS use this to ask the human a question — never just ask in chat, which is invisible to the dashboard and to an unattended/autonomous run. IMPORTANT: when the project's HITL auto-answer mode is on (1=safe, 2=aggressive) and the question is not destructive / not require_human, this tool RESOLVES IMMEDIATELY and returns the chosen answer inline in the response (it does NOT block) — so calling it is cheap and is the right move even when you expect a quick yes/no. The active mode is reported in the start_session orientation as hitl_auto_answer_mode. urgency='blocking' means this session pauses until answered (poll get_hitl_request). urgency='normal'/'high' lands in the dashboard but doesn't block. assigned_to routes to a specific human_id (null = broadcast). kind='correction' files a non-blocking mid-run correction: never auto-answered, never blocks — an unattended executor picks it up at the next sprint-item boundary, applies it, and continues. Pass `options` (answer choices, rendered as buttons) and `recommended` (an option string or 0-based index) to flag the safe default — the dashboard highlights it and Enter submits it, and an auto-answer picks it. Set require_human=true for genuinely irreversible/destructive actions (token rotation, data migrations, rollbacks) so auto-answer can never approve it — only an explicit human reply unblocks it. Endpoint: https://usemeridian.us/mcp/sse
- get_hitl_request (Get HITL Request) - [SUPPORT] Read-only: Poll a HITL request for the human's answer. Returns the row including status ('pending'|'answered'|'dismissed') and answer text. Endpoint: https://usemeridian.us/mcp/sse
- add_note (Add Note) - [SUPPORT] Add a per-project wiki note (setup, gotcha, howto, env, ...). Free-form title/body; comma-separated tags optional. Optional kind (wiki=gotcha/rule/howto, insight=strategic/product analysis, reference=external/one-off docs, code=warning/context anchored to a file, document=ingested report/spec/thesis) controls how the dashboard renders it. For a code anchor pass kind='code' plus file_path (and optional symbol): the note is then surfaced automatically when a session calls claim_file/get_file_claims for that path, so the executor sees the warning before editing. Pass source (a URL or file path) to record where the note came from — set automatically by ingest_document. Tag a note 'roadmap' AND pass a committable category (TECHNICAL/ARCHITECTURAL/PRODUCT) to also append it to ROADMAP.md's roadmap-notes anchor. Endpoint: https://usemeridian.us/mcp/sse
- ingest_document (Ingest Document) - [SUPPORT] Turn a Word/PDF/text document into a queryable kind='document' note with a source link — a report, thesis chapter, or spec doc becomes searchable project memory. Pass file_path OR content (one is required):
• file_path → Meridian extracts the text SERVER-SIDE, STDLIB ONLY: .txt/.md/.markdown and source files are read directly; .docx is unzipped and its paragraphs extracted (no python-docx). No new dependencies.
• content → use this for .pdf and anything Meridian can't parse server-side: extract the text with YOUR OWN tools first, then pass it here. (Passing file_path for a .pdf returns an error telling you to do this.)
title defaults to the file's basename; source defaults to file_path. The stored body is capped (truncated with a '…[truncated]' marker if very long; the kept prefix stays searchable). Meridian never summarizes — pass a summary as content if you want one stored instead of the raw text. Returns the created note (id, slug, title, source). Endpoint: https://usemeridian.us/mcp/sse
- get_document_structure (Get Document Structure) - [SUPPORT] 13462df2 — return the heading outline of a Word .docx WITHOUT ingesting it as a note. Meridian parses the .docx server-side (stdlib only, no python-docx, no persistent index) and returns paragraph_count, heading_count, and an ordered list of headings (level, text, para_id) — a fast structural map of a thesis chapter / spec before deciding what to read or ingest. Pass file_path to a server-accessible .docx. Endpoint: https://usemeridian.us/mcp/sse
- get_latex_structure (Get Latex Structure) - [SUPPORT] 106118cd — parse a LaTeX (.tex) source's structure WITHOUT a PDF intermediary. Meridian parses the .tex server-side with pylatexenc (pure-Python, no LaTeX install) and returns heading_count, an ordered headings outline and a nested tree of \part/\chapter/\section/\subsection/\subsubsection/\paragraph (level, kind, text, children), plus unexpanded_inputs (\input/\include filenames, not expanded) and a bibliography list (thebibliography \bibitem entries, and \bibliography{...} + a sibling .bib when a path is given). Pass file_path to a server-accessible .tex, OR pass source with the raw LaTeX inline. Malformed LaTeX returns a partial/empty result, never an error crash. Endpoint: https://usemeridian.us/mcp/sse
- get_citation_edges (Get Citation Edges) - [MAINTENANCE] fefb596a — read the CITATION GRAPH of a project's ingested documents. Returns every in-text citation marker (a kind='citation' element parsed from an ingested .tex/.docx) together with its resolved edges:
• bibentry edges — the intra-document link from a \cite{key} marker to a matching \bibitem/bibliography entry in the SAME document (materialised automatically on ingest).
• zotero_item edges — the cross-document link from a marker to a canonical Zotero library item, keyed on DOI (materialised by the opt-in resolve_citations pass); target_document_id is set when the cited paper is itself ingested in this project.
Each marker carries {element_id, document_id, ordinal, ref, text, edges:[{edge_kind, target_kind, target_ref, target_element_id, target_document_id, resolved_at}]}. Scope to one document with source (a stored source path/URL) or document_id; omit both for the whole project. Returns an empty markers list (never an error) when no document structure has been persisted yet. Endpoint: https://usemeridian.us/mcp/sse
- resolve_citations (Resolve Citations) - [MAINTENANCE] fefb596a — resolve this project's in-text citation markers to canonical Zotero items via Zotero's LOCAL API and materialise the cross-document 'cites' -> zotero_item edges (keyed on DOI). An OPT-IN, network-making pass — deliberately separate from ingest, which stays offline. For each kind='citation' marker without a zotero_item edge, the marker's ref is resolved: a DOI (doi:.. / a bare 10.x/y / a doi.org URL) matches the library item with that DOI; a zotero:<key> ref is a direct item lookup; a bare BibTeX citekey is a best-effort text search (fuzzy without Better BibTeX). When the resolved DOI matches a paper ALSO ingested in this project, the edge's target_document_id is linked too. IDEMPOTENT — re-runs only fill gaps, never duplicate. If Zotero is closed or its local API is disabled, markers simply stay unresolved (no error). Returns {resolved, unresolved, cross_doc_linked} counts. Requires Zotero running locally with the local API enabled (endpoint configurable via MERIDIAN_ZOTERO_API_URL). Endpoint: https://usemeridian.us/mcp/sse
- index_equation (Index Equation) - [MAINTENANCE] 06df6ab3 — index ONE Word equation (OMML) against a document already stored in the doc-structure store — populated by ingest_document (which registers a docx/latex document's structure here in addition to storing the flat note text). Pass the SAME source/path you ingested under as `doc`. omml_or_latex is auto-detected: a string starting with '<' is treated as raw OMML XML (stored as-is); anything else is treated as LaTeX source (real OMML is generated best-effort — pure-Python latex2mathml piped through a hand-written MathML->OOXML mapper; returns null omml on an unsupported construct, never an error). Before inserting, the normalized LaTeX is fuzzy-matched against every equation already stored for this document — a near-duplicate is NOT silently dropped (the equation is still inserted) but IS surfaced via near_duplicates:[{equation_id, matched_id, matched_latex, score}] so you can spot accidental re-derivations. Returns {equation, near_duplicates}. Endpoint: https://usemeridian.us/mcp/sse
- find_similar_equation (Find Similar Equation) - [MAINTENANCE] 06df6ab3 — fuzzy-match a LaTeX string against every equation already indexed (via index_equation) for one stored document, best match first. Each result carries the stored equation row PLUS a difflib similarity score (0..1) against its latex_normalized. Useful before index_equation to check whether an equation is already present under a slightly different LaTeX spelling. Returns {document_id, matches:[...]} — an empty list (never an error) when the document has no stored equations, or doc doesn't resolve to a stored document. Endpoint: https://usemeridian.us/mcp/sse
- insert_equation (Insert Equation) - [MAINTENANCE] 51a595e7 — write an OMML equation DIRECTLY into a stored document's source .docx (real OOXML write-back), collapsing the manual resolve->open->parse->splice->rewrite->reindex flow into one call. The document must already be stored in the doc-structure store via ingest_document (which registers a docx/latex document's structure here) AND have a filesystem `source` path to write back to. Locate the target paragraph by `para_id` — the paragraph's w14:paraId (or the synthesized 'p{index}' id that get_document_structure / find_similar_equation surface as element_id). equation_id_or_omml is resolved in order: the id of an equation already indexed for THIS document (its stored OMML is reused); else a string starting with '<' is raw OMML XML; else a LaTeX source (converted best-effort via latex2mathml -> MathML -> OOXML). position controls placement: 'append' (default) drops the <m:oMath> inline at the end of the paragraph; 'before'/'after' add it as its own display-equation paragraph adjacent to the target. After the write the document's equation index is resynced from the modified file (no separate re-verify step). Returns {document_id, source, para_id, position, omml, resync} on success, or {error} for a bad para_id / unresolvable equation / missing file (the file is never mutated when resolution fails). Endpoint: https://usemeridian.us/mcp/sse
- update_paragraph (Update Paragraph) - [MAINTENANCE] f978e588 — ID-addressable docx WRITE (the write counterpart of the get_element_by_id / paraId read primitive). Targets ONE paragraph in a stored .docx by its w14:paraId (the 'p{index}' fallback Word writes for an unlabelled paragraph) — NEVER by text match — rewrites its runs, saves the .docx in place, and re-syncs the doc_elements index row so it matches the new text. The document must already be stored in the doc-structure store via ingest_document (which registers a docx/latex document's structure here). Pass the SAME source/path you ingested under as `doc`. Provide EXACTLY ONE of: `new_text` (a plain string — one unformatted run) OR `runs` (a list of runs, each a bare string or {text, bold?, italic?, underline?} — basic run formatting is applied; the paragraph's original run formatting is replaced, not merged; its paragraph style/numbering is preserved). Returns {document_id, para_id, new_text, elements_resynced, source_path}. elements_resynced is 0 for a plain body paragraph (only headings are persisted as elements) — that is expected, not a failure. Errors (never a silent no-op) when the doc/source/para_id doesn't resolve. f7ee1ba7 — pass session_id to enable scoped-region claim enforcement: if another session has claimed the target para_id (or holds a whole-file lock), the write is REJECTED with error='docx_region_conflict'. Use claim_docx_region to acquire your region before writing. Endpoint: https://usemeridian.us/mcp/sse
- find_symbol_usages (Find Symbol Usages) - [MAINTENANCE] 9605edb0 — READ-ONLY cross-reference tracking: given a document and EITHER a doc_equations row id OR a symbol / normalized-LaTeX string, resolve it to ONE target normalized-LaTeX (an equation id uses that row's stored latex_normalized as-is; a raw string is normalized with the SAME normalize_latex that produced every stored latex_normalized) and return every place that target reappears in the document — matching equations (exact normalized-latex equality) AND paragraphs whose text textually contains the symbol. Each hit carries element_id, document_id, ordinal, matched_text, context (equation|paragraph) and an is_definition/is_reuse flag: the EARLIEST occurrence by ordinal is the definition, later ones are reuse — so a later mention can be checked to point back to the definition instead of assuming the reader remembers it. Hits are ordered by ordinal (definition first). Returns {document_id, target, resolved_from, hits:[...]} — an empty hits list (never an error) when nothing matches, or doc doesn't resolve to a stored document. Endpoint: https://usemeridian.us/mcp/sse
- index_figure (Index Figure) - [MAINTENANCE] c623e648 — index ONE figure into the SEMANTIC figure index against a document already stored in the doc-structure store — populated by ingest_document (which registers a docx/latex document's structure here in addition to storing the flat note text). Pass the SAME source/path you ingested under as `doc`. This is the figure parallel of index_equation and is COMPLEMENTARY to the structural kind='figure' section-tree placement (it adds caption dedup + similarity, it does not replace placement). Provide file_path and/or caption. Before inserting, the normalized caption is fuzzy-matched against every figure already indexed for this document — a near-duplicate is NOT silently dropped (the figure is still inserted) but IS surfaced via near_duplicates:[{figure_id, matched_id, matched_caption, score}] so you can spot an accidental re-index. The referenced file_path is checked on disk: a missing file is FLAGGED (file_exists on the row + a missing_files entry), never a hard failure. Returns {figure, near_duplicates, missing_files}. Endpoint: https://usemeridian.us/mcp/sse
- find_similar_figure (Find Similar Figure) - [MAINTENANCE] c623e648 — fuzzy-match a free-text description OR a file path against every figure already indexed (index_figure) for one stored document, best match first. Each result carries the stored figure row PLUS a difflib similarity score (0..1) — the better of the match against its normalized_caption and against its file_path. Useful before index_figure to check whether a figure is already present under a slightly different caption or path. Returns {document_id, matches:[...]} — an empty list (never an error) when the document has no indexed figures, or doc doesn't resolve to a stored document. d2a3537a — pass outputs_dir to RESOLVE THROUGH to the outputs index: every matched figure with a file_path that names an already-indexed run output gains a linked_output field (the output's path, generating_script, canonical/archival flag, fingerprint), so 'does this plot already exist as a run output?' and 'where is it referenced in my thesis?' are one lookup (linked_output is null when the figure names no indexed output). Endpoint: https://usemeridian.us/mcp/sse
- link_figure_caption (Link Figure Caption) - [MAINTENANCE] 0ff8b982 — DURABLY link an already-indexed figure (doc_figures row) to its caption paragraph (a doc_elements id), by stable structural id rather than paragraph proximity. Use this to confirm an advisory suggested_caption_element_id returned by index_figure, or to backfill a durable link on a figure that was indexed before caption linkage was supported. Provide figure_id (the doc_figures.id of the figure to link) and caption_element_id (the doc_elements.id of the caption paragraph — a kind='figure' SEQ-field element from the section-tree store). This is the confirmation primitive for the 'Figure 3b used twice' ambiguity scenario: when index_figure surfaces suggested_caption_candidates (multiple captions in the same section), inspect them and call this tool with the correct one to confirm the durable link. Returns the updated figure row on success, or {error} when figure_id doesn't resolve to a known figure. Endpoint: https://usemeridian.us/mcp/sse
- index_table (Index Table) - [MAINTENANCE] 2622182d — index ONE table into the SEMANTIC table index against a document already stored in the doc-structure store — populated by ingest_document (which registers a docx/latex document's structure here in addition to storing the flat note text). Pass the SAME source/path you ingested under as `doc`. This is the table parallel of index_figure and is COMPLEMENTARY to the structural kind='table' section-tree placement (it adds caption dedup + similarity, it does not replace placement). Provide caption and/or table_index. Before inserting, the normalized caption is fuzzy-matched against every table already indexed for this document — a near-duplicate is NOT silently dropped (the table is still inserted) but IS surfaced via near_duplicates:[{table_id, matched_id, matched_caption, score}] so you can spot an accidental re-index. When paired_figure_id is omitted, the nearest figure in the same structural section is surfaced as suggested_figure_id (advisory, never auto-applied). Returns {table, near_duplicates}. Endpoint: https://usemeridian.us/mcp/sse
- find_similar_table (Find Similar Table) - [MAINTENANCE] 2622182d — fuzzy-match a free-text description against every table already indexed (index_table) for one stored document, best match first. Each result carries the stored table row PLUS a difflib similarity score (0..1) against its normalized_caption. Useful before index_table to check whether a table is already present under a slightly different caption. Returns {document_id, matches:[...]} — an empty list (never an error) when the document has no indexed tables, or doc doesn't resolve to a stored document. Endpoint: https://usemeridian.us/mcp/sse
- ingest_document_structure (Ingest Document Structure) - [MAINTENANCE] db42acce — persist pre-parsed structural data (headings/figures/tables) into the doc-structure store, keyed on the SAME source as ingest_document(content=...) so find_similar_figure / index_figure / index_table / index_equation see the correct document_id.

Use this when the .docx lives on the caller's local machine (not on the Meridian server): call the tunnel-side ingest_local_document_structure tool (from the meridian-docs extension) which parses the file locally and forwards the blocks JSON here. The source must exactly match the source that ingest_document stored the flat note under (default: the local file path). Returns {document_id, source, doc_type, element_count}. Endpoint: https://usemeridian.us/mcp/sse
- search_outputs (Search Outputs) - [SUPPORT] a0e9133e — READ-ONLY full-text search over a run's OUTPUTS tree (numeric/tabular/array artifacts), backed by DuckDB native FTS (Okapi BM25). Walks outputs_dir recursively and builds a persistent index: each .csv/.json contributes its extracted TEXT content plus a cheap fingerprint (CSV column names / JSON top-level keys / an inferred generating_script); each .npy contributes METADATA ONLY (never array content); images/other binaries contribute filesystem metadata + name only. The multi-word query is scored with BM25 and ranked hits are returned. Canonical-vs-archival is handled TWO-STAGE and is NEVER destructive: a filename heuristic (_old / _old_N / leading underscore) flags a CANDIDATE, and a SHA-256 content hash CONFIRMS — an archival copy byte-identical to its canonical twin is DEPRIORITIZED in ranking (is_archival=true, canonical_path set), while a same-name-pattern file whose content DIFFERS is surfaced as its own distinct hit (never collapsed). Nothing is ever deleted or hidden from disk. Pass include_archival=false to drop archival hits entirely. Returns {outputs_dir, query, total_indexed, hits:[{path, score, bm25, is_archival, canonical_path, kind, generating_script, csv_columns, json_keys, size, mtime, annotations:[{path, note, run_params, created_at, updated_at, source}]}]}. annotations is auto-included for each hit (any annotation keyed to the hit path OR a nearest ancestor directory) — no second tool call needed. A missing dir / empty tree returns an empty hits list, never an error. Endpoint: https://usemeridian.us/mcp/sse
- annotate_outputs (Annotate Outputs) - [SUPPORT] 9e02e448 — capture a human annotation for a path inside an outputs tree WITHOUT touching the filesystem. Upserts a row into the annotations layer of the local DuckDB outputs index for outputs_dir. Two tiers, same mechanism: Tier 1 = pass outputs_dir as path to annotate the whole tree ('what this experiment tree is about'); Tier 2 = pass any sub-path (file or directory) to annotate a specific run, file, or subdirectory ('PCA on, BFS off, overwritten 5x'). run_params is an optional free-form dict of parameters logged alongside the note (e.g. {"lr": 0.001, "batch_size": 32}). Annotations are automatically surfaced in search_outputs results — any hit's path (or its nearest ancestor directory) that has an annotation will have it included in the hit's 'annotations' field without a second tool call. A MERIDIAN_NOTES.md file placed anywhere in the tree is also auto-ingested into the same table on every rebuild, keyed to its containing directory. Returns the stored annotation as a dict. Endpoint: https://usemeridian.us/mcp/sse
- search_code_semantic (Search Code Semantic) - [SUPPORT] 93fce816 — Cursor-style LOCAL semantic code search over a source tree, entirely in a DuckDB sidecar (no cloud round-trip). Parses Python (stdlib ast) and TypeScript/JavaScript (tree-sitter) into SEMANTIC CHUNKS at function/class/method boundaries PLUS the un-named logical blocks that a named-symbols-only graph search can't reach (module-level dict/list literals, bare calls, __main__ guards, imports) — so a term that only appears in a bare top-level call is still findable. Incremental by a content MERKLE TREE: the root hash is compared first and only divergent subtrees are walked, so only the files that actually changed since the last pass are re-chunked (repeat calls on an unchanged tree are near-free). Search is HYBRID — DuckDB native FTS (Okapi BM25) for keyword match, fused via Reciprocal Rank Fusion with an OPTIONAL local-embedding vector leg (DuckDB VSS / HNSW cosine over a Model2Vec static model) when MERIDIAN_CODE_INDEX_VECTORS is enabled; with vectors off (the default) it is a complete pure-BM25 code search. Returns {root_dir, query, total_indexed, vectors_enabled, vectors_active, hits:[{chunk_id, path, language, kind, name, line_start, line_end, content, score, bm25, bm25_rank, vector_rank}]}. A missing dir / empty tree returns an empty hits list, never an error. Endpoint: https://usemeridian.us/mcp/sse
- prospect_symbol (Prospect Symbol) - [SUPPORT] 2ce5bc76 — ROBUST symbol prospecting with a three-rung fallback chain: tries codebase__search_graph FIRST (fast, graph-indexed); when it returns zero results OR the caller flags a mismatch (stale_graph=true), automatically retries via Serena extractor__find_symbol / extractor__find_declaration (AST-accurate, never stale); falls back to a BM25 keyword grep over search_code_semantic as a last resort so the caller NEVER has to notice a miss and switch tools by hand. Each rung is labelled in the result ({rung: 'graph'|'serena'|'semantic', hits:[...], fallback_reason: str?}) so the caller knows which level succeeded. All three legs are best-effort: a missing tunnel, inactive slot, or missing root_dir degrades to the next rung, never an error. Use this instead of calling codebase__search_graph directly whenever you are prospecting for a symbol, function, or class location — it is structurally immune to the class of silent graph-index miss that previously returned wrong line numbers or empty results for real symbols. Endpoint: https://usemeridian.us/mcp/sse
- add_sprint_item_pointer (Add Sprint Item Pointer) - [SUPPORT] 2976e168 — attach a GENERIC POINTER to a sprint item: a portable, composable reference to a thing-in-a-source, grounded in LSP Location + W3C Web Annotation Selector composition. targets is an ARRAY of {uri, selector, subSelector?} objects (native multi-file, the LSP WorkspaceEdit pattern); the whole composite shape is stored as JSON, not per-domain columns. Every selector is an object with an explicit "type" PLUS that type's own field(s):
• range — {"type":"range", "start_line":int, "end_line":int, "start_char"?:int, "end_char"?:int} (an LSP Range); the pointer IS the location.
• symbol — {"type":"symbol", "qualified_name":"pkg.mod.func"} resolved against the cached code graph to a file+line.
• node_id — {"type":"node_id", "id":"<element-id>"} of a doc_store element (an ingested-document structure node). NOTE: the field is "id", NOT "value".
• zotero_key — {"type":"zotero_key", "key":"<zotero-key>"} of a Zotero library item.
An optional selector.subSelector nests finer granularity (W3C hasSubSelector) — e.g. {"type":"symbol", "qualified_name":"a.b.f", "subSelector": {"type":"range", "start_line":3, "end_line":4}} = 'these lines, within this function'. A subSelector is itself a FULL selector and MUST carry its OWN explicit "type" (it does not inherit the parent's). source_type names the domain (code | docs | citation | …). Malformed pointers are rejected with a clear error: a bad/missing selector.type, a missing required selector field (e.g. node_id without "id", or a subSelector with no "type"). Returns the stored pointer. Endpoint: https://usemeridian.us/mcp/sse
- get_sprint_item_pointers (Get Sprint Item Pointers) - [SUPPORT] 2976e168 — list the GENERIC POINTERS attached to a sprint item (oldest first). Each pointer is {id, source_type, targets:[{uri, selector, subSelector?}], label, created_at} — the stored shape with its JSON targets deserialized. Read-only; does NOT resolve the targets (use resolve_sprint_item_pointers for that). Endpoint: https://usemeridian.us/mcp/sse
- resolve_sprint_item_pointers (Resolve Sprint Item Pointers) - [SUPPORT] 2976e168 — resolve EVERY generic pointer on a sprint item to its concrete location, dispatching by selector.type. A range target returns its location as-is; symbol resolves the qualified_name in the cached code graph to a file+line; node_id looks the element up in the doc-structure store; zotero_key resolves via Zotero's local API. A subSelector narrows the outer resolution ('these lines, within this function'). Every dispatch is best-effort: an unresolvable target yields {resolved:false, reason} instead of an error, and the pass NEVER fails. Returns {pointers:[{id, source_type, label, targets:[<resolved-target>]}]}. Requires no network for range/symbol/node_id; zotero_key needs Zotero running locally (else that target is just unresolved). Endpoint: https://usemeridian.us/mcp/sse
- delete_sprint_item_pointer (Delete Sprint Item Pointer) - [MAINTENANCE] 2976e168 — delete ONE generic pointer from a sprint item by its pointer id (the id returned by add_sprint_item_pointer / get_sprint_item_pointers). A stored pointer is immutable, so 'editing' one is delete-then-re-add. Idempotent: returns {pointer_id, deleted:false} when no pointer had that id, rather than erroring. Endpoint: https://usemeridian.us/mcp/sse
- add_insight (Add Insight) - [SUPPORT] Record a durable STRATEGIC INSIGHT — accumulated understanding that generates future decisions. A first-class knowledge type SEPARATE from decisions (choices with a lifecycle) and notes (reference). horizon sets its shelf-life: 'permanent' insights ALWAYS surface in get_planning_brief; 'year'/'quarter' are time-boxed. Returns the stored insight. Endpoint: https://usemeridian.us/mcp/sse
- get_insights (Get Insights) - [SUPPORT] Read-only: List a project's strategic insights (newest first), optionally filtered by horizon (permanent|year|quarter). Review accumulated understanding before planning. permanent insights also appear automatically in get_planning_brief. Endpoint: https://usemeridian.us/mcp/sse
- save_finding (Save Finding) - [SUPPORT] Phase-agnostic capture primitive: turn a finding into a durable, addressable note with provenance — works with ANY source (Claude's built-in web search, the arXiv MCP, Serena, a teammate). Decoupled from search so capture survives regardless of how you found it. The summary's first line becomes the note title; the note is tagged 'finding' + the source_type. Optionally links to a pinned decision. Returns the note. Endpoint: https://usemeridian.us/mcp/sse
- capture_research_finding (Capture Research Finding) - [SUPPORT] Inline capture for web/paper research during planning: save a finding from a URL as an addressable note with the source link, optionally linked to a decision. A research-shaped wrapper over save_finding — arXiv URLs are tagged source_type=arxiv automatically, everything else as web. Turns web-search results into durable Meridian artifacts instead of evaporating. Endpoint: https://usemeridian.us/mcp/sse
- get_notes (Get Notes) - [SUPPORT] Read-only: List project notes (newest first), LIGHTWEIGHT by default — each item is id/slug/title/tags/kind/priority/timestamps with NO body, so the list never overflows context. This is the pull model: scan the list, then call read_note(project_id, slug) to fetch one note's full body on demand. Optional ?tag substring filter and ?query full-text search (matches title+body even though bodies aren't returned). Pass bodies=true only when you truly need every body inline. Pagination: pass limit (default 100, max 500) and/or cursor to get a {notes, has_more, next_cursor} envelope, then re-call with cursor=next_cursor for the next page; omit both for the full list. Endpoint: https://usemeridian.us/mcp/sse
- read_note (Read Note) - [SUPPORT] Read-only: Fetch one project note's full body by its per-project slug (the ``slug`` field from get_notes). The pull half of the list→read model — get_notes returns slugs without bodies, read_note pulls a single body when you need it. Endpoint: https://usemeridian.us/mcp/sse
- delete_note (Delete Note) - [MAINTENANCE] Hard-delete a project note by id. Endpoint: https://usemeridian.us/mcp/sse
- add_workspace_note (Add Workspace Note) - [MAINTENANCE] Add a workspace-level wiki note that applies across ALL projects in this workspace (onboarding, cross-cutting conventions, shared infra). Unlike add_note, it is not tied to a project and is injected at the top of every project's context block + handoff. Comma-separated tags optional. Endpoint: https://usemeridian.us/mcp/sse
- get_workspace_notes (Get Workspace Notes) - [MAINTENANCE] Read-only: List workspace-level notes (newest first). Optional ?tag substring filter. Endpoint: https://usemeridian.us/mcp/sse
- pin_workspace_decision (Pin Workspace Decision) - [MAINTENANCE] Pin a workspace-level decision that applies across ALL projects (shared architecture, org-wide standards). Injected at the top of every project's context block + handoff. category is free-text (STRATEGIC, TECHNICAL, ARCHITECTURAL, PRODUCT, ...). Endpoint: https://usemeridian.us/mcp/sse
- get_workspace_decisions (Get Workspace Decisions) - [MAINTENANCE] Read-only: List workspace-level pinned decisions (active only by default, newest first). Endpoint: https://usemeridian.us/mcp/sse
- get_workspace_settings (Get Workspace Settings) - [MAINTENANCE] Read-only: Read workspace-global default settings (applies across ALL projects in this workspace): hitl_auto_answer_default and sprint_name_default. Returns the singleton settings row. Endpoint: https://usemeridian.us/mcp/sse
- update_workspace_settings (Update Workspace Settings) - [MAINTENANCE] Update workspace-global default settings. Pass only the fields you want to change. hitl_auto_answer_default (bool) seeds new projects' HITL auto-answer behaviour; sprint_name_default (string) is the default sprint name; handoff_template (string) overrides the default full-mode handoff with a custom template — supports {{sprint}}, {{recent_tasks}}, {{decisions}}, {{north_star}}, {{version_goal}}, {{pending_items}}, {{notes}} placeholders. execution_mode_default ('autonomous'|'interactive', '' to clear) and code_intel_enabled_default (bool) are cascade defaults seeded onto NEW projects in this workspace (existing projects are unchanged). loop_enabled_default (bool) is the workspace default for /loop auto-continue that projects inherit when their loop_enabled is 'workspace'. Pass an empty string to revert a field to the server default. Endpoint: https://usemeridian.us/mcp/sse
- save_blog_post (Save Blog Post) - [MAINTENANCE] Create or update a workspace-scoped blog post (draft|published|archived lifecycle). Posts belong to the whole workspace, not a single project, and are served publicly at /blog/<slug> once status='published'. Pass 'id' to update an existing post; omit it to create a new draft. 'slug' is optional (auto-derived from the title, de-duplicated). Returns the saved post with a computed 'url'. Endpoint: https://usemeridian.us/mcp/sse
- get_blog_posts (Get Blog Posts) - [MAINTENANCE] Read-only: List workspace-scoped blog posts, newest first. Optional 'status' filter (draft|published|archived). Each post includes a 'url' (/blog/<slug>). Endpoint: https://usemeridian.us/mcp/sse
- add_workspace_sprint_item (Add Workspace Sprint Item) - [MAINTENANCE] Add an item to the workspace-level personal backlog — a cross-project board NOT tied to any single project (track thesis + Meridian + personal goals in one view). Use the per-project add_sprint_item for project work instead. 'group' is the cross-project bucket the item lives under (e.g. 'thesis', 'meridian', 'personal'); 'human_id' assigns it to a person. New items start as 'todo'. Endpoint: https://usemeridian.us/mcp/sse
- get_workspace_sprint_items (Get Workspace Sprint Items) - [MAINTENANCE] Read-only: List workspace personal-backlog items (grouped by 'group', then position). Optional 'status' (todo/pending/in_progress/done/skipped/failed) and 'group' filters. Endpoint: https://usemeridian.us/mcp/sse
- update_workspace_sprint_item (Update Workspace Sprint Item) - [MAINTENANCE] Edit a workspace personal-backlog item: title, status, group, or human_id (assignee). Only the fields you pass are changed. Pass an empty string for group/human_id to clear it. Setting status to done/skipped/failed stamps completed_at. Returns the updated item. Endpoint: https://usemeridian.us/mcp/sse
- complete_workspace_sprint_item (Complete Workspace Sprint Item) - [MAINTENANCE] Mark a workspace personal-backlog item done (stamps completed_at). Returns the updated item. Endpoint: https://usemeridian.us/mcp/sse
- add_workspace_proposal (Add Workspace Proposal) - Capture a workspace-level flash of insight into the 'drawer of inspiration' — cross-project ideas that don't belong to any one project yet. Unlike sprint items these are NOT executor-claimable; they require a human to review and promote them. Proposals start at status='raw' and progress through an enforced lifecycle: raw → investigating → promoted|rejected. Use advance_proposal_status to move through the lifecycle; use promote_proposal to convert one into a real sprint item. Endpoint: https://usemeridian.us/mcp/sse
- get_workspace_proposals (Get Workspace Proposals) - [SUPPORT] Read-only: List workspace proposals (human-authored flashes of insight), newest first. Optional status filter (raw/investigating/promoted/rejected) and tag substring filter. Endpoint: https://usemeridian.us/mcp/sse
- advance_proposal_status (Advance Proposal Status) - [SUPPORT] Transition a workspace proposal through its lifecycle. Enforced transitions: raw → investigating|rejected; investigating → promoted|rejected|raw; rejected → raw. 'promoted' is a terminal status reachable only via promote_proposal (which also creates the sprint item). Returns the updated proposal. Endpoint: https://usemeridian.us/mcp/sse
- promote_proposal (Promote Proposal to Sprint Item) - Promote a workspace proposal into a real sprint item, creating the link between them. The proposal must be in 'raw' or 'investigating' state. Creates a sprint item under the given project and sets the proposal's status to 'promoted' with promoted_to_sprint_item_id pointing to the new item. Returns {proposal, sprint_item_id, sprint_item_title, project_id}. Endpoint: https://usemeridian.us/mcp/sse
- get_session_brief (Get Session Brief) - [SUPPORT] Read-only: Call this FIRST for project summaries or to see what a session did — returns session, tasks, decisions, and recent commits in one call. Replaces the start_session + get_context_block two-call pattern for worker/automation sessions. Returns sprint focus, pending sprint items, recent tasks, any blocking failures, and pending HITL requests in a compact XML envelope (<500 tokens). Endpoint: https://usemeridian.us/mcp/sse
- list_hitl_requests (List HITL Requests) - [SUPPORT] Read-only: List HITL requests without needing UUIDs. OMIT project_id to list pending HITLs across ALL your projects (matches the dashboard) — planning sessions should call it this way so HITLs filed under another project aren't missed (a common cause of false 'no pending HITLs' confidence). Pass project_id to scope to one project. Returns pending queue plus answered/dismissed from the last 24 h by default so planning sessions can see what was recently decided without a separate call. Pass status='pending' for only the active queue, or status='answered'/'dismissed'/'all' for specific history. Endpoint: https://usemeridian.us/mcp/sse
- answer_hitl (Answer HITL) - [SUPPORT] Answer a pending HITL request programmatically. Marks it answered so the waiting session can resume. Use list_hitl_requests to find request IDs. Endpoint: https://usemeridian.us/mcp/sse
- dismiss_hitl (Dismiss HITL) - [SUPPORT] Dismiss a HITL request (won't-answer / no longer relevant). Stays in audit trail. Use list_hitl_requests to find request IDs. Endpoint: https://usemeridian.us/mcp/sse
- list_sessions (List Sessions) - [MAINTENANCE] Read-only: List active sessions for a project. Useful for planning chat to see what's currently running before filing new sprint items. Endpoint: https://usemeridian.us/mcp/sse
- add_sprint_note (Add Sprint Note) - [SUPPORT] Add an ephemeral note to the current session's scratch pad. Use for constraints, blockers, working assumptions valid only this session. Notes are auto-deleted when the session closes. Pass note_kind='thinking' for a thinking_sync (HOOKS_DEBUG_STATE) note: a structured snapshot of the reasoning state (what was tried, what failed, current confirmed state) that the dashboard renders with a distinct icon. Intended for Claude's client-side thinking_sync post-tool-call hook, which extracts the extended-thinking scratchpad and persists it here so debugging state survives across turns and into the next session brief. Endpoint: https://usemeridian.us/mcp/sse
- get_sprint_notes (Get Sprint Notes) - [SUPPORT] Read-only: Get all ephemeral scratch-pad notes for the current session. Shown at the top of session briefs so every cold start sees active constraints. Pass note_kind='thinking' to fetch only thinking_sync scratchpad notes, or 'note' for only normal notes; omit for all. Endpoint: https://usemeridian.us/mcp/sse
- set_sprint (Set Sprint) - [MAINTENANCE] Update only the sprint — the short-term focus that changes each session or week. Any team member can call this; no ownership check. If pending items from the current sprint were never started, returns a WARNING block listing them. Pass force=true to override and overwrite anyway. Endpoint: https://usemeridian.us/mcp/sse
- get_sprint_progress (Get Sprint Progress) - [SUPPORT] Read-only: Return a SUMMARY of sprint items by status (pending/in_progress/done/failed) optionally filtered by version or item_group. Returns total, done, in_progress, pending, failed, percent_complete, and by_status (counts only — no per-item list; call get_sprint_items(status="pending") for the live item list). Useful to see how far through the sprint we are without listing all items. Pass session_id to also get a board_change field reporting items added since that session started (live-queue signal — call this between sprint items to pick up mid-run injections). Endpoint: https://usemeridian.us/mcp/sse
- add_sprint_item (Add Sprint Item) - ALWAYS call get_sprint_items first to check for existing pending items before adding. Append a todo item to the project's sprint checklist. Use when starting work on a new version so the next session sees what's in flight. Optional: group items under a named objective with 'group'; attribute to a person with 'human_id'. Use 'depends_on' to block until another item finishes. Blocks near-duplicate titles (>=60% word overlap with an open pending/in_progress item) and returns the conflict; also warns (drift_warning) when the title looks already-shipped — 3+ keyword overlap with a migrations.py/_migrate_X or a recent commit; pass force=true to add anyway. Endpoint: https://usemeridian.us/mcp/sse
- fan_out_sprint_items (Fan Out Sprint Items) - [SUPPORT] Bulk-insert sprint items from a single orchestrator call — decompose a goal into parallel work items without N sequential add_sprint_item calls. Pass a list of {title, description?, group?, version?} dicts; returns the list of new item_ids in insertion order. No duplicate guard is applied (the caller is assumed to have deduped). Items with empty titles are silently skipped. Endpoint: https://usemeridian.us/mcp/sse
- update_sprint_item (Update Sprint Item) - [SUPPORT] Edit fields on an existing sprint item: title, version, notes, human_id (assignee), group, deferred_until (enforced deferral), or track. Only the fields you pass are changed; omitted fields are left untouched. Pass an empty string for human_id, group, deferred_until, or track to clear it. Returns the updated item, or an error if the id is unknown. Endpoint: https://usemeridian.us/mcp/sse
- complete_sprint_item (Complete Sprint Item) - Mark a sprint item done. Pass task_id to link the task that shipped it. Pass session_id to get a board_change field (items injected mid-run) and an active-worktree merge reminder in the response. If the item is flagged required_notes, you MUST pass notes= (evidence: what shipped / how verified) or a task_id, or completion is refused (EVIDENCE_REQUIRED). Endpoint: https://usemeridian.us/mcp/sse
- reconcile_sprint_drift (Reconcile Sprint Drift) - [MAINTENANCE] Read-only: Cross-reference pending sprint items against recent git commits and return items that may already be done. Uses keyword matching — confidence 'high' means 3+ keywords overlap (safe to mark done), 'medium' means 1-2 (verify first). Also surfaces 'notes_blocker_drift': pending items whose notes describe a deferral or blocker (keywords: FLAGGED, DEFERRED, BLOCKED, 'not implementable', etc.) but whose structured fields (blocker_kind, deferred_until) are both unset — these items will keep surfacing as ordinary claimable work until you call update_sprint_item with blocker_kind='manual' or deferred_until=<ISO timestamp>. Call during planning sessions to identify board drift before filing new items. Endpoint: https://usemeridian.us/mcp/sse
- get_planning_brief (Get Planning Brief) - PLANNING SESSIONS: CALL THIS FIRST before anything else. Read-only: Return a compact planning context — sprint, north star, pending items, in-progress items, recent tasks, active sessions, recent decisions, unvalidated assumptions, the last session's output (last_session), and a new-handoff signal. No session registration needed. Designed for planning chat sessions that need to see project state without side effects. Pass `since` (a prior call's generated_at) to flag only handoffs filed since you last checked. Endpoint: https://usemeridian.us/mcp/sse
- refresh_context (Refresh Context) - [SUPPORT] Single-call post-compaction recovery for planning chats. Returns a COMPACT snapshot — current sprint + progress, next pending items, the active session id, recent handoffs, high-priority (urgent) decisions, unvalidated assumptions, and key note slugs — small enough not to overflow context. Call this the moment a chat feels disoriented (e.g. right after a /compact) to re-orient in one round-trip. Endpoint: https://usemeridian.us/mcp/sse
- get_sprint_items (Get Sprint Items) - Read-only: List sprint items for a project. Optional status filter (todo|pending|in_progress|provisional_complete|done|failed|skipped|pushed|indeterminate). Cold sessions read this to know what's still owed. Endpoint: https://usemeridian.us/mcp/sse
- get_parallelizable_groups (Get Parallelizable Groups) - [MAINTENANCE] Read-only: Return clusters of pending sprint items that are safe to run simultaneously. Filters pending/todo items (optionally by version) whose depends_on is satisfied, then greedily partitions them into groups where no two items in a group share a touches_resources identifier. The orchestrator fans out each group as a parallel subagent batch and runs the groups in sequence. Returns {version, groups: [[item,...],...], group_count, eligible_count, undeclared_count, blocked: [...]}. Items still waiting on an unfinished dependency are listed under 'blocked', not in any group. Makes parallel sprints system-enforced rather than LLM-guessed. Endpoint: https://usemeridian.us/mcp/sse
- assign_sprint_waves (Assign Sprint Waves) - [MAINTENANCE] 58a45b92 — PERSIST the parallel grouping: writes the conflict-free batches get_parallelizable_groups computes onto each eligible item's stored `wave` field (group i -> 'wave-{i+1}'), so parallelism becomes deterministic and inspectable (get_sprint_items surfaces `wave`) instead of recomputed every call. Only currently-eligible items (pending/todo, dependency-satisfied, unclaimed, non-manual-blocker) are labelled; blocked/in-flight/done items are left untouched (re-run once they clear). Idempotent — recomputes from the live board each call. Hand-override any item afterwards with update_sprint_item(wave=...). Returns {version, wave_count, assigned, waves: {'wave-1': [ids...], ...}, blocked_count, undeclared_count}. 605ca2c4 — if active executor sessions are detected, the response also includes active_session_warning: re-labeling wave numbers while a session is mid-flight can desync it from a /goal string that already references specific wave labels. Endpoint: https://usemeridian.us/mcp/sse
- complete_wave_gate (Complete Wave Gate) - [SUPPORT] d2430713 — EXECUTOR GATE: call this AFTER you have actually run a wave's gate action list (push, deploy, wait, run_verification) to unblock the next wave's sprint items. You MUST pass the REAL structured result from run_verification as verification_payload — the server validates it (status=='ok', exit_code==0). A self-report ('I think it passed') or a fabricated payload is rejected with a clear error. On success, writes a wave_gate_results row and returns {gate_completed, wave_label, next_wave_label, next_wave_item_count, next_wave_item_ids, gate_id}. Each wave gate may only be completed once (duplicate calls return an error). Security note: this is a deploy-adjacent gate — only actual run_verification output satisfies it. Endpoint: https://usemeridian.us/mcp/sse
- analyze_sprint (Analyze Sprint) - [MAINTENANCE] PLANNING: Read-only synthesis of the current sprint into one structured brief — parallelizability (conflict-free groups + max fan-out), dependency chains (depends_on walked to the root), resource/file conflicts (items sharing touches_resources), and stalls (stall_count>0). Returns {summary, recommended_strategy, parallelism, dependency_chains, longest_chain, file_conflicts, stalls, blocked, running}. Call in planning sessions instead of stitching together get_parallelizable_groups + manual dependency/conflict analysis. Endpoint: https://usemeridian.us/mcp/sse
- get_session_log (Get Session Log) - [MAINTENANCE] Read-only: Return the full task log for the given session. Returns every log_task description logged during the session (transcript/task_count) PLUS a recent_activity ring-buffer of the last tool calls the executor made — even before log_task() was called. Use recent_activity to check signs of life in a running executor. Useful for post-session review, handoff, or remote planner polling. Endpoint: https://usemeridian.us/mcp/sse
- get_session_activity (Get Session Activity) - [MAINTENANCE] Read-only: Return the raw MCP-tool-call heartbeat feed for the given executor session — a ring-buffer of the last tool calls (newest first, up to 50 entries). Populated automatically by the MCP dispatcher on every executor tool call, no log_task() needed. Use this to check whether an executor is still running when task_count is 0. Endpoint: https://usemeridian.us/mcp/sse
- get_connection_log (Get Connection Log) - [MAINTENANCE] Read-only: Return the recent /mcp connection-event log for this tenant (newest first, up to 200 entries). Every HTTP /mcp request Meridian receives is recorded: timestamp, MCP method (initialize/tools/list/tools/call/...), auth_result (success/oauth/no_token/invalid_token/expired), tools_returned (tool count for tools/list responses), client_user_agent, and HTTP response_status. Use this to diagnose client-side outages (zero tools returned, auth failures, unexpected User-Agents) in real time or after the fact without needing raw Fly.io log access. Endpoint: https://usemeridian.us/mcp/sse
- get_server_logs (Get Server Logs) - [MAINTENANCE] Read-only: Return recent application-level WARNING/ERROR/EXCEPTION log records (newest first, up to 500 entries). Captures any logging.warning() / logging.error() / unhandled-exception records emitted anywhere in the Meridian process — not just /mcp request metadata. Use this to diagnose server-side errors (OAuth flow failures, tools/list timeouts, deploy health issues, DB connection errors) without needing raw Fly.io log access. Complements get_connection_log (which covers per-request /mcp metadata only). Returns {count, since, level_filter, module_filter, entries}. Endpoint: https://usemeridian.us/mcp/sse
- search_server_logs (Search Server Logs) - [MAINTENANCE] 222d54f8 — BM25 full-text search over the server_logs ring-buffer. Complements get_server_logs (which filters by level/module/since) with keyword-ranked retrieval — useful when you know WHAT went wrong but not exactly WHEN (e.g. search 'OAuth token refresh' or 'psycopg connection pool' across the last 2000 log records). Uses DuckDB native FTS (Okapi BM25) with Porter stemming over a concatenated body of level + logger + message + exc_text. Incremental: re-syncs only new/evicted rows on each call; repeat queries over an unchanged log window are near-free. Ring-buffer eviction is handled consistently — rows pruned from server_logs are removed from the FTS index on the next call. Returns {query, total_in_index, count, hits:[{id, level, logger, message, exc_text, recorded_at, score, bm25}]}. Empty/no-match query returns {hits:[]}. Endpoint: https://usemeridian.us/mcp/sse
- get_server_log_checkpoint (Get Server Log Checkpoint) - [MAINTENANCE] b241a437 -- Read-only: Return the positional/checkpoint index for the server_logs ring-buffer. The checkpoint is a lightweight 'table of contents' mapping minute-level timestamp buckets to the first/last row id and row count in that bucket. Use this for fast navigation through large log windows: find the bucket just before your target timestamp, then use its min_recorded_at as the since= argument to get_server_logs to skip all older rows without scanning. Complementary to search_server_logs (BM25 text search): this is positional navigation (WHERE in the log?) not semantic ranking (WHAT text?). The optional seek_to= argument returns the best since= hint directly. The index is rebuilt from the in-memory snapshot on every get_server_logs / search_server_logs call, so it is always current. Returns {total_rows, bucket_granularity_label, min_recorded_at, max_recorded_at, bucket_count, buckets:[{bucket, count, min_recorded_at, max_recorded_at, first_id, last_id}], seek_hint (when seek_to= given)}. Endpoint: https://usemeridian.us/mcp/sse
- search_all (Search All) - [SUPPORT] Read-only: Universal search across all project content: tasks, notes, pinned decisions, and sprint items. Uses LIKE matching (SQLite) or ILIKE (Postgres). Returns grouped results: {tasks, notes, decisions, sprint_items, total}. Endpoint: https://usemeridian.us/mcp/sse
- search_synthesis (Search Synthesis) - [SUPPORT] Read-only: Natural-language search that returns a short, CITED answer (which notes/items it drew from) synthesized over the same retrieval as search_all — not just a list of matches. Uses a cheap Haiku call when ANTHROPIC_API_KEY is set, with a deterministic fallback to the raw results (synthesized=false) otherwise. Returns {query, answer, cited, synthesized, results}. Endpoint: https://usemeridian.us/mcp/sse
- paper_search (Paper Search) - [SUPPORT] Search academic papers — a REAL external lookup (keyless). Per the research-routing protocol, use this FIRST for academic/paper questions (cite the paper itself, not a secondary write-up), then capture_research_finding to save what you cite. Two keyless sources via the 'source' param: 'arxiv' (default; preprints, physics/CS/math) and 'openalex' (published journal/conference works across every discipline). Both return the same shape: {query, count, results:[{title, authors, summary, published, url, pdf_url, ...}]} — arxiv rows carry arxiv_id, openalex rows carry openalex_id + doi. Endpoint: https://usemeridian.us/mcp/sse
- get_agent_instructions (Get Agent Instructions) - [MAINTENANCE] Read-only: Return the custom agent_instructions for a project. These are injected automatically by start_session so every session picks them up. Use this when you need to read or display the current instructions. Endpoint: https://usemeridian.us/mcp/sse
- set_agent_instructions (Set Agent Instructions) - [MAINTENANCE] Set or update the custom agent_instructions for a project. Instructions are injected into every start_session response so AI sessions see them automatically — no need to repeat in every session. Pass null or empty string to clear. Use for persistent rules like coding conventions, deploy steps, or codebase notes. Endpoint: https://usemeridian.us/mcp/sse
- set_executor_config (Set Executor Config) - [MAINTENANCE] Store per-project executor defaults (repo_path, env_file, test_cmd, test_min, deploy_cmd, shell_type, branch). Merges onto the existing config — other keys (hostnames, filesystem_roots, …) are preserved. Pass repo_paths as an array of {cwd, hostname} known locations; they are merged into the existing repo_paths (deduped) rather than overwriting, so manual + hook-registered entries coexist. Executor sessions auto-load these when start_session(role='executor') is used. Credentials rule is always injected separately: read secrets from env_file only, never remote shell. Endpoint: https://usemeridian.us/mcp/sse
- claim_file (Claim File) - [SUPPORT] Claim edit rights on a file for this session. Whole-file by default (auto-expires after 2 hours). For symbol-level claims — so two sessions can edit the same file if they own different classes/functions — also pass `symbol` (e.g. 'AuthRouter' or 'AuthRouter.login') AND `content` (the file's full source). Meridian parses the source (stdlib ast for Python, tree-sitter for JS/TS/C/C++/Go/Rust/Java/C#), and hard-blocks if another live session already owns an overlapping line range — the block lists which symbols are still safe to claim. Unparseable content falls back to a whole-file lock. The response includes a `code_notes` list of code-anchored project notes (kind='code') for this file/symbol — read them before editing. Endpoint: https://usemeridian.us/mcp/sse
- store_finding (Store Finding) - [MAINTENANCE] PARALLEL COORDINATION (c35370cc): persist a per-task intermediate result to the session_findings table so it survives session boundaries. Parallel reader agents write findings; an orchestrator or writer agent reads them via get_findings. Unlike save_finding (which creates a research note), this is a lightweight key→content store for agent-to-agent handoff of intermediate work. Endpoint: https://usemeridian.us/mcp/sse
- get_findings (Get Findings) - [MAINTENANCE] Read-only (c35370cc): read stored session_findings for a project (newest first), optionally scoped by key and/or session_id. The read side of store_finding. Endpoint: https://usemeridian.us/mcp/sse
- send_message (Send Message) - [MAINTENANCE] PARALLEL COORDINATION (d3a3a01d): enqueue an actor-model message to another session (session_messages table). 'Done with X, you do Y' between parallel agents. The recipient reads with receive_messages. A2A-compatible. Endpoint: https://usemeridian.us/mcp/sse
- receive_messages (Receive Messages) - [MAINTENANCE] PARALLEL COORDINATION (d3a3a01d): fetch unread messages addressed to a session (oldest first) and mark them read by default. The receive side of send_message. Endpoint: https://usemeridian.us/mcp/sse
- idle_until_all_done (Idle Until All Done) - [MAINTENANCE] PARALLEL COORDINATION (d3a3a01d): non-blocking barrier check across sibling sessions. Returns {all_done, pending, statuses}; a session is done when closed/archived/missing. The server can't block, so poll until all_done is true — the A2A 'wait for X, Y, Z to finish' primitive. Endpoint: https://usemeridian.us/mcp/sse
- release_file (Release File) - [SUPPORT] Release a file lock (and any symbol claims this session holds on it). Endpoint: https://usemeridian.us/mcp/sse
- get_file_claims (Get File Claims) - [SUPPORT] Read-only: show active claims on a file — the whole-file lock (with the holder's session name, if any) plus any symbol-level claims. Use to check who owns a file before editing it. Pass project_id (and optional symbol) to also get a `code_notes` list of code-anchored notes (kind='code') for that path. Endpoint: https://usemeridian.us/mcp/sse
- get_symbol_claims (Get Symbol Claims) - [MAINTENANCE] Read-only: list symbol-level claims on a file (who owns which class/function/method line ranges). Endpoint: https://usemeridian.us/mcp/sse
- get_symbol_hotspots (Get Symbol Hotspots) - [MAINTENANCE] Read-only: symbols claimed by 3+ distinct sessions within 14 days — a refactor/ownership smell. Optionally scope to one file. Endpoint: https://usemeridian.us/mcp/sse
- claim_docx_region (Claim Docx Region) - [MAINTENANCE] f7ee1ba7 — Model B scoped-region claiming for .docx files. Claim a specific paragraph/element by its durable `element_id` (the w14:paraId surfaced by get_document_structure / update_paragraph) so another session cannot overwrite it concurrently. Two sessions can hold NON-OVERLAPPING element claims on the SAME file — the real precision benefit vs. a whole-file lock. An edit to a claimed element_id by another session is REJECTED structurally (not just advisory) at the update_paragraph level. A whole-file lock by another session blocks this claim. Returns {claimed: true, file_path, session_id, element_id} on success or {claimed: false, reason, message, conflicts} on conflict. Endpoint: https://usemeridian.us/mcp/sse
- get_docx_region_claims (Get Docx Region Claims) - [MAINTENANCE] f7ee1ba7 — Read-only: list active scoped docx-region claims on a file (who owns which element_ids). Use before update_paragraph to see whether the target element is claimed. Endpoint: https://usemeridian.us/mcp/sse
- release_docx_region_claims (Release Docx Region Claims) - [MAINTENANCE] f7ee1ba7 — Release scoped docx-region claims held by a session. Without element_id releases all claims on the file; with element_id releases only that one element. Without file_path releases ALL region claims held by the session across all files. Endpoint: https://usemeridian.us/mcp/sse
- list_plugins (List Plugins) - [MAINTENANCE] Read-only: Lightweight index of active tunnel plugins — name, description, enabled state, and tool_count. Does NOT return full tool schemas (use get_plugin_details for that). Dramatically reduces context bloat vs. dumping all plugin schemas at startup (~500 tokens vs 50k+). Returns an 'active_plugins' list plus any stored skill notes. Endpoint: https://usemeridian.us/mcp/sse
- get_plugin_details (Get Plugin Details) - [MAINTENANCE] Read-only: Full schema for one named plugin (all tool definitions, description overrides, and stored skill guide if available). Use list_plugins first to see which plugins are active, then call get_plugin_details(name) to load the schema for a specific plugin on demand. Endpoint: https://usemeridian.us/mcp/sse
- refresh_tool_manifest (Refresh Tool Manifest) - [MAINTENANCE] Read-only: return the authoritative, compact manifest of ALL built-in Meridian MCP tools (name + one-line summary). Call this when you suspect your client's tool schema went stale ('I nuked the schema', a tool you expected is suddenly 'not found', or right after a /compact) — it is a plain tool CALL, so it works even on clients that ignore the notifications/tools/list_changed signal (e.g. Claude Desktop). Best-effort also re-fires list_changed for your tenant so a client that DOES honour it re-lists. Names returned here are canonical: a name present here but absent from your tool list is a stale-schema artifact, not a removed tool. Endpoint: https://usemeridian.us/mcp/sse
- get_graph_diff (Get Graph Diff) - [MAINTENANCE] Read-only: compare the latest code-graph snapshots of two sessions — returns delta in node_count, hotspot_count, and file_churn. Use snapshot_graph_metrics first to record each session's current state. Endpoint: https://usemeridian.us/mcp/sse
- snapshot_graph_metrics (Snapshot Graph Metrics) - [MAINTENANCE] Record a code-graph snapshot for a session (node count, edge count, hotspot count, file churn). Call at session start and end to enable get_graph_diff comparisons. Endpoint: https://usemeridian.us/mcp/sse
- idle_until_session_done (Wait for Session to Close) - [MAINTENANCE] Read-only: Poll every 30 seconds until another session is closed or archived. Use this when you need to wait before editing a locked file. 6f9503a9 — BOUNDED: the wait times out after timeout_seconds (default 1800s / 30 min) and returns {done:false, timed_out:true, status} so a stuck subagent in a parallel fan-out fails that one item fast instead of hanging the whole batch. Pass timeout_seconds=0 or a large value to tune; there is no unbounded wait. Endpoint: https://usemeridian.us/mcp/sse
- update_md_section (Update Markdown Section) - [MAINTENANCE] Propose a replacement for an anchored section of an agent template doc (CLAUDE.md or AGENTS.md). Does NOT write the file directly — it creates a human-in-the-loop request carrying a diff preview. A human approves it in the dashboard, then Meridian replaces that section and stages the file for the next checkpoint commit. 'anchor' is the section name between the MERIDIAN:ANCHOR:START/END comments. (ROADMAP/DECISIONS/DEVLOG are append-only and not replaceable.) Pass force=true from a human planning session (claude.ai) to skip the HITL and apply the replacement directly; autonomous executor sessions should omit force so the diff stays gated. Endpoint: https://usemeridian.us/mcp/sse
- claim_sprint_item (Claim Sprint Item) - Claim a pending sprint item: sets status to in_progress and records claimed_at + actor. Read-only: false. Rejects if the item is already in_progress, done, failed, skipped, or its touches_files overlap active file claims from another live session. Endpoint: https://usemeridian.us/mcp/sse
- add_subtask (Add Subtask) - [SUPPORT] Add a child sprint item under an existing parent item. Inherits the parent's version. Status starts as pending. Rejects if the parent is already done, failed, or skipped. Pass owner='human' or owner='ai' to build a mixed-ownership task chain: owned subtasks added in sequence become a strict chain (each depends on the previous owned sibling), and completing one auto-advances ownership — an AI→human step files a HITL handoff, a human→AI step un-blocks the next AI subtask. The parent stays in_progress until all subtasks are terminal. Endpoint: https://usemeridian.us/mcp/sse
- split_sprint_item (Split Sprint Item) - [SUPPORT] Split a sprint item into multiple smaller items. The original is closed (skipped) and N new items are created with split_from referencing the original. Returns list of new items. Endpoint: https://usemeridian.us/mcp/sse
- merge_sprint_items (Merge Sprint Items) - [SUPPORT] Merge multiple sprint items into one. Source items are closed (skipped, merged_into=survivor). Returns the new survivor item. Endpoint: https://usemeridian.us/mcp/sse
- set_active_repo (Set Active Repo) - [MAINTENANCE] Update the tunnel's active Serena repo at runtime. When a planning session switches to a different codebase, call this so subsequent Serena requests (find_symbol, find_referencing_symbols, etc.) route to the new repo without restarting the tunnel. Has no effect when no tunnel is connected. Endpoint: https://usemeridian.us/mcp/sse
- run_verification (Run Verification) - [SUPPORT] 0e973e52 — run the project's stored test_cmd on YOUR local machine via the tunnel and return a REAL, structured result — not self-reported. Fields: {exit_code, passed, failed, stdout_tail, stderr_tail, status, timed_out}. Returns {status: 'not_configured'} (never an error) when no test_cmd is set; call set_executor_config(test_cmd='pixi run test') first. Requires an active `meridian --tunnel`; the hosted server has no access to your machine (same architectural class as ingest_document / search_code_semantic / search_outputs — decision 0dedff91). Per-project: only runs when test_cmd is configured for that project. Endpoint: https://usemeridian.us/mcp/sse
- analyze_model_efficiency (Analyze Model Efficiency) - [MAINTENANCE] 0fba4cb6 — MECHANICAL (zero-token) model-tier suggestion for a task or sprint item. Deterministic, rule/heuristic classifier: NO model call, NO DB, NO network — it mirrors how the ultracode orchestration script spends zero model tokens on routing. Pass a task descriptor (any of title, description, file_count, files, touches_resources, size) and it returns a suggested tier: {tier: 'haiku'|'sonnet'|'opus', score, signals:[{signal, detail, weight}...], rationale, mode:'mechanical'}. Cheap-leaning signals (title keywords like 'typo'/'docstring'/'lint', 1 file, size 'xs'/'s') pull toward 'haiku'; expensive-leaning signals ('refactor'/'migration'/'auth', many files, touched resources, size 'l'/'xl') pull toward 'opus'. Use it to route a task to the cheapest sufficient model before spawning an executor. FOLLOW-UP (out of scope this pass): a second LLM-backed 'semantic' mode that reads the full item for a nuanced second opinion. Endpoint: https://usemeridian.us/mcp/sse

## Resources
Not captured

## Prompts
- start-executor - Paste-ready instructions for starting an executor session on a project. Arguments: project_id
- daily-standup - Standup summary prompt — shows shipped, in-progress, blocked, and next items. Arguments: project_id
- planning-session-start - Planning-session scaffold — the get_planning_brief protocol and tool-call order for a claude.ai planning chat. Arguments: project_id, project_name
- executor-goal - A /goal-style executor prompt built from the project's live pending sprint items. Pass project_id (or project_name) for the real items; omit both for a fill-in template. Arguments: project_id, project_name
- hotfix-loop - The read → edit → push hotfix protocol for a fast, test-gated fix on an existing Meridian project. Arguments: project_id

## Metadata
- Owner: io.github.ajc3xc
- Version: 1.0.2
- Runtime: Npm
- Transports: HTTP, STDIO
- License: Not captured
- Language: Not captured
- Stars: Not captured
- Updated: Jun 2, 2026
- Source: https://registry.modelcontextprotocol.io
