cairn-mcp-server
MCP server exposing Cairn's local-first file-organization tools to Claude Code, Codex, Cursor, and any MCP client.
How to read this: tool names here are observed from a live tools/list handshake. The Risk label is a heuristic inferred from the tool name (write/destructive verbs), not from executing the tool — a conservative guess, not a verified capability. We never escalate risk from a description. Found one that's wrong? Tell us — we fix on report.
| Tool | Risk | Side effects | Approval |
|---|---|---|---|
| add_bill Record a bill the user paid and is owed for, splitting it across `people`.
`people` are the OTHERS on the bill — never the user. By default `total`
splits evenly across them plus the user; pass include_self=false when the
user was only fronting the money and owes no share. `shares` pins exact
amounts for specific people ({"alex": "52.30"}) and the rest split what
remains; rounding remainders fall on the user, never on a guest. `date` is
YYYY-MM-DD (default today).
| write | true | unknown |
| add_person_to_bill Add someone to an existing bill for an explicit amount.
Does not re-split the bill: the others have already been told what they owe.
| write | true | unknown |
| add_task Create a task (a `category: task` note) and return its record.
Files into `dir` (default `tasks/`) with a slug from `title`. `due` is
YYYY-MM-DD; `context` is a tag like "@errand"; `status` is
todo/doing/blocked/done. `priority` is p1/p2/p3. `blocked_by` is a
space/comma-separated list of task slugs (a task's slug is its filename stem)
that must finish before this one becomes actionable. `recur` makes it
recurring (daily/weekly/monthly/yearly/biweekly or `<N><d|w|m|y>`): completing
it rolls the task forward to its next due date instead of archiving it.
Colliding names get a numeric suffix, never clobber.
| write | true | unknown |
| add_waiting Start tracking something the user is waiting on `person` for (a delegated action).
`what` is the thing owed (a review, a reply, a deliverable); `person` is who
owes it. `since` defaults to today (when the wait began); `due` is an optional
"need it by" (YYYY-MM-DD) that makes it overdue past that date; `remind_every`
overrides the nudge cadence in days. The item ages from `since` and surfaces in
the daily digest once it's been outstanding a few days.
| write | true | unknown |
| attention Everything across the workspace that is waiting on the user right now.
The one reminder primitive spanning every lifecycle kind — tasks, bills,
papers, and any future kind — not one query per kind. Sweeps all OPEN items
and returns those that are `overdue`, `due_today`, `upcoming` (within
`upcoming_days`), or `stale` (open past their kind's staleness threshold
without a status change). `kinds` restricts to specific categories (e.g.
["task","bill"]); omit for all.
Returns {as_of, count, buckets, items}: `buckets` groups items by urgency
(most pressing first), each item carrying its `kind`, `due`, `age_days`, and
the `reasons` it was flagged. A `count` of 0 means nothing needs the user —
the signal to stay quiet rather than send an empty reminder.
| read | false | unknown |
| complete_task Complete a task (surgical frontmatter edit), stamping `completed` (today by default).
A one-off task is marked `done`; its result gains `unblocked` — the tasks it
was blocking that are now actionable. A **recurring** task (one with a `recur`
cadence) is instead rolled forward in place: status back to `todo`, `due`
advanced to the next occurrence, and the result carries
`recurred: {next_due, occurrences}`. The rest of the file is preserved.
| read | false | unknown |
| create_file Create a file under `path`. If `name` ends in .uni, wraps content as a .uni doc.
Pass `template` (e.g. "paper", "note" — see list_templates) with `fields` to
stamp out a document with frontmatter pre-filled; `content` is then ignored.
| write | true | unknown |
| create_folder Create a new folder `name` under `path`. | write | true | unknown |
| delete_item Delete a file or folder (recursive for folders). | destructive | true | true |
| digest_workspace Return a token-efficient map of the workspace: one entry (title, date,
tags, summary) per document, grouped by `folder`, `tag`, or a metadata field
like `status`/`project`. Read this before loading individual files. | read | false | unknown |
| drop_waiting Stop waiting on something — close the item as `dropped` (the user gave up on it). | destructive | true | true |
| find_by_meta Find documents whose metadata matches all `filters` (exact, not fuzzy).
Reads .uni JSON fields and .md YAML frontmatter alike. Scalar fields match
case-insensitively; `tags` (or any list field) matches when every requested
value is present. Example: {"status": "to-read", "project": "amazon"}.
| read | false | unknown |
| get_file_tags Return a document's tags — from a .uni's tags array or a text file's YAML frontmatter. | read | false | unknown |
| get_file_tree Return the nested file/folder tree under a directory (default: whole workspace). | read | false | unknown |
| get_tag_tree Return {tag: [file paths]} aggregated across the workspace. | read | false | unknown |
| grep Search file contents for `pattern`; returns {path, line, text} matches. | read | false | unknown |
| harvest_checklists Promote `- [ ]` checkbox lines in notes into canonical task files.
Scans text under `path` (a file or folder; default whole workspace), turning
each unchecked checkbox into a task and parsing inline `due:YYYY-MM-DD`,
`+project` and `@context` tokens. With `link_back` (default), each harvested
line is annotated so re-running never double-harvests. Returns {created, count}.
| read | false | unknown |
| import_document Convert a docx/pdf/pptx/csv/xlsx/md/text file into an editable .uni doc.
The original file is kept. Returns the new .uni item.
| read | false | unknown |
| import_folder Import every convertible (non-.uni) file under a folder into .uni docs.
Returns a summary with counts and per-file results (including skips).
| read | false | unknown |
| list_bills List shared bills (documents with `category: bill`), oldest first.
`status` defaults to "open" (someone is still unpaid); pass "settled" or
"all". `person` filters to bills naming that person. Each result carries the
per-person breakdown plus a derived `outstanding` balance and `age_days`.
Use this to find a specific bill; use `who_owes` for the reminder summary.
| read | false | unknown |
| list_dir List files and folders directly under a workspace-relative directory. | read | false | unknown |
| list_paper_projects List projects configured for paper recommendations (.cairn/paper_reco.json). | read | false | unknown |
| list_tasks List tasks (documents with `category: task`), sorted by due date.
A task is an ordinary frontmatter note with a lifecycle; this is the task
view over it. `status` defaults to the open set (todo/doing/blocked) — pass
"all" to include done, or a specific status / comma-separated list.
`project` and `context` (e.g. "@errand") are exact case-insensitive filters;
`due_before` (YYYY-MM-DD) keeps only tasks due before that date. Each result
carries derived `overdue` and `today` flags relative to the current date.
| read | false | unknown |
| list_templates List available document templates (built-ins + workspace .cairn/templates). | read | false | unknown |
| list_waiting List waiting-on items (things others owe the user), longest wait first.
A `waiting` note is one person owing one action. `state` is "open" (still
waiting, default), "closed" (received/dropped), or "all". `person` filters
case-insensitively. Each record carries {person, title, status, since,
age_days, due, overdue}. These also surface in `attention`/`reminder_digest`
once they've been waiting past the kind's staleness threshold.
| read | false | unknown |
| mark_received Close a waiting item because they delivered — stamps `received` (today by default). | unknown | unknown | unknown |
| move_item Move a file or folder into `target_dir`. | write | true | unknown |
| multi_edit Replace a single, unique occurrence of `old_string` with `new_string` in a file. | write | true | unknown |
| next_actions The actionable tasks right now — open, unblocked — best-first.
Answers "what should I work on next?": excludes done, manually blocked, and
anything still waiting on an open `blocked_by` dependency, then ranks by
urgency — overdue first, then priority (p1>p2>p3), then soonest due. Pass
`project` to scope it, `limit` to cap the list (0 = no cap).
| read | false | unknown |
| preview_paper_recommendations Preview the top unseen recommended papers for a project WITHOUT saving.
Ranks the project's candidate pool by relevance × citation impact and skips
anything already recommended or already filed. Use this to inspect the queue
or check a project's topic queries before committing notes. Set `refresh` to
re-pull the pool from Semantic Scholar instead of using the cached one.
| read | false | unknown |
| project_pulse Per-project momentum/health — the project-manager view of the workspace.
Where `attention` surfaces individual items, this steps up to the PROJECT:
for every `project:` value it derives open/overdue/in-progress/blocked task
counts, the next deadline, and days since anything in that project was last
touched (`days_since_activity`), flagging `stale` past `stale_after_days` and
`has_overdue`. Use it to answer "which projects are moving and which have
stalled?" without reading every note. Pass a `project` to get just that one.
Returns {as_of, stale_after_days, projects: [...]} sorted longest-idle first.
| read | false | unknown |
| read_detail Read a file. For .uni docs returns HTML content, flattened text, and tags;
for text/.md files returns raw text plus any YAML frontmatter tags & metadata. | read | false | unknown |
| recommend_papers Recommend + FILE the top unseen paper(s) for one project as to-read notes.
Picks the highest relevance×impact paper the project hasn't seen, writes it
into the papers folder as a `paper` note (status: to-read), and records it so
it never repeats. Returns the picks (title, why, citations, path). Run daily
for a steady walk through a field's canon, one paper per project per day.
Set `refresh` to re-pull the ranked pool instead of using the cached one.
| read | false | unknown |
| recommend_papers_all_projects Recommend + file the top unseen paper(s) for EVERY configured project.
The daily-driver: one call yields one new to-read paper note per project.
Set `refresh` to re-pull each ranked pool instead of using the cached one.
| read | false | unknown |
| refresh_project_dashboard Regenerate the computed Pulse block in a project's `projects/<name>.md`.
Writes momentum + open-task numbers into the dashboard between HTML-comment
markers, leaving every hand-written section untouched — so the "Last updated"
line stops going stale by hand. Pass a single `project`, or `all=true` to
refresh every existing `projects/*.md`. Never creates a dashboard: a missing
file is reported as `updated: false`, not an error.
Returns the per-project {project, path, updated, inserted, block}.
| read | false | unknown |
| reindex Build/refresh the embedding index (no-op if no embedder is configured). | unknown | unknown | unknown |
| reminder_digest The daily reminder to send: only what is DUE to be re-surfaced today.
Like `attention`, but cadence-filtered — an item reappears only when its own
reminder interval has elapsed since it was last sent (a per-note `remind_every`
frontmatter field, else the kind default of daily), with `overdue`/`due_today`
items escalated to daily whatever their cadence. This is the tool a reminder
cron calls; it records what it sent to a workspace ledger so the next run's
cadence is right (a WRITE, hence the annotation). Pass preview=true to see the
due set WITHOUT recording (leaves cadence untouched).
Returns {as_of, count, buckets, items}. A `count` of 0 means send nothing —
the signal to stay quiet rather than deliver an empty reminder.
| read | false | unknown |
| rename_item Rename a file or folder in place (bare name, not a path). | write | true | unknown |
| search_files Find files/folders whose name contains `query` (case-insensitive). | read | false | unknown |
| semantic_retrieve Return the documents most relevant to `query`.
Uses embeddings when an embedding endpoint is configured (env), else
lexical scoring.
| read | false | unknown |
| set_status Set a note's lifecycle status, stamping when it changed.
The kind-agnostic way to move any lifecycle note between states — mark a
paper `read`, a task `doing`, reopen a bill. Validates `status` against the
note's kind (rejecting a value the kind doesn't define) and records
`status_changed` so staleness is measured from this moment. For tasks and
bills the kind-specific tools (complete_task, settle_bill) carry extra domain
logic; prefer those when they fit, and this for everything else.
Returns {path, kind, status, state, status_changed}.
| write | true | unknown |
| settle_bill Stop tracking what `person` owes — because they paid, or the user waived it.
With `path` omitted this settles that person across EVERY open bill, which
is what "Alex paid me back" usually means; pass `path` to settle just one.
`state` is "paid" when the money actually arrived and "waived" when the user
has decided to stop chasing it — both end the reminders, but only "paid"
claims repayment, so do not substitute one for the other. A bill closes
itself once nobody on it is unpaid.
| unknown | unknown | unknown |
| stale_projects Just the stalled projects — a lean call for a daily-digest line.
Returns [{project, days_since_activity, tasks_open, tasks_overdue}] for every
project idle longer than `threshold_days`, longest-idle first. Empty list means
nothing has gone quiet — the signal to add no stale-projects line at all.
| read | false | unknown |
| update_file_tags Replace a document's tags — in a .uni's tags array or a text file's YAML frontmatter. | write | true | unknown |
| update_task Update one or more fields of a task in place (surgical frontmatter edit).
Pass only the fields to change; an empty argument leaves that field untouched.
`status` must be todo/doing/blocked/done; `due` is YYYY-MM-DD; `context` is a
tag like "@deep"; `priority` is p1/p2/p3; `blocked_by` replaces the dependency
list (space/comma-separated task slugs); `recur` sets the recurrence cadence.
| write | true | unknown |
| weekly_review The weekly review: accomplishments, project health, the week ahead, focus.
The once-a-week synthesis above the daily reminder digest — pure composition,
no side effects. Returns {as_of, since, horizon, closed, closed_count,
projects, stalled, due_next_week, focus}: `closed` is every lifecycle item
finished in the last `window_days` (tasks done, papers read, bills settled);
`projects`/`stalled` is the per-project pulse; `due_next_week` is dated items
needing attention within `horizon_days` (overdue + due-today + upcoming); and
`focus` is the top `focus_limit` next-actions to lead the week with. This is
the tool a weekly-review cron calls.
| read | false | unknown |
| who_owes Summarize who still owes the user money, across all open shared bills.
This is the reminder view: one row per person — {name, owes, bills,
oldest_days} — sorted so whoever has been owing longest comes first, plus a
grand `total` and `bill_count`. Returns an empty `people` list when
everything is settled, which is the signal to stay quiet rather than send an
empty reminder.
| read | false | unknown |
| who_owes_me Who owes the user an ACTION, rolled up by person — the delegated-work view.
The actions analog of `who_owes` (which is money): open `waiting` items —
things the user delegated or is blocked on (a review, a reply, a deliverable) —
grouped per person you've waited on longest first: {person, count,
oldest_age_days, items}. An empty list means nothing is outstanding — stay
quiet rather than nudge. Add items with `add_waiting`; close with `mark_received`.
| read | false | unknown |
The access this server can exercise, inferred from its verified tools — not a declared OAuth scope.
Add the “as seen on MCPExplorer” badge to your README.
This is one server. A loadout combines the right servers, governance, and proven plays for a whole job — assembled deliberately, not tool-dumped.
Explore loadouts →