logbook/Docs

MCP tool reference

Complete reference for the logbook MCP server, generated from source (app/api/mcp/route.ts, serverInfo.name = "logbook", version = "0.9.0"). Audience: coding agents first, humans second. Every tool name, parameter, default, and response field below is quoted verbatim from the tool registrations.

The server exposes 25 tools and 2 prompts. Every tool is scoped to a single project, resolved from your token and (for org keys) the project slug in the URL — see Connecting agents. Every write is attributed in the activity feed as claude (<your name>); created items are attributed to your human name (not the claude (…) label).

Conventions used by every tool

Refs

Item refs render as LOG-<number> on output regardless of project (formatRef always emits the LOG- prefix). On input, any tool that takes a query/to/epic string accepts a bare number (12), a LOG--prefixed ref (LOG-12), any other prefixed ref (GAP-88 — the prefix is stripped to the number), or free title text (fuzzy match).

Query resolution (findItemByQuery) returns one of:

  • exact number match, or
  • a single title/description match, or
  • a disambiguation message when >1 title matches: "<q>" matches N items — be more specific or use the ref: followed by the candidate refs, or
  • Nothing matching "<q>" — try list_backlog with a search term.

When a tool cannot resolve a query it returns that message as the tool result text (it does not throw).

Enums

Shared across tools (from lib/db/schema.ts):

EnumValues
status (ITEM_STATUSES)backlog, in_progress, done, stuck
priority (ITEM_PRIORITIES)low, medium, high, urgent
size (ITEM_SIZES)S, M, L, XL
kind (ITEM_KINDS)log, epic
link kind (LINK_KINDS)pr, branch, file, url

Link objects

Wherever a tool takes links / add_links, each entry is { kind: <pr|branch|file|url>, value: <string, min length 1> }.

  • pr — pull request URL or number
  • branch — git branch name
  • file — repo file path
  • url — anything else

On output, links serialize as "<kind>:<value>" strings; a pr link that matches a known PR is annotated with live state, e.g. "pr:<url> (merged)".

Errors surfaced as text

Domain errors (unknown label, unknown assignee email, epic invariant violations) are caught inside the tool body and returned as the tool result text prefixed with Error: — the call still "succeeds" at the protocol level. The two create tools additionally gate on billing before running; see upgrade_required.

Verbosity: the three output shapes

list_backlog, get_my_work, and (implicitly) the briefing snapshot serialize item lists at one of three verbosity levels. Default is index.

index (default)

One line per item, no descriptions or bodies — the always-safe-to-hold-in- context shape. Emails are dropped (assignee names only; emails remain in pull_item). Fields, in order:

{
  "ref": "LOG-42",
  "title": "…",
  "kind": "epic",              // present on epic rows only
  "epic": "LOG-12",            // parent ref, present on child logs only
  "summary": "…",              // authored summary, else first sentence of
                               //   description flattened + hard-cut at 140 chars;
                               //   omitted when there is no description
  "size": "M",                 // omitted when unsized
  "status": "in_progress",
  "priority": "high",
  "children": "2/5",           // done/total child logs, epic rows only
  "assignees": ["Ada"],        // names; omitted when none
  "tasks": "1/3",              // done/total subtasks; omitted when no tasks
  "labels": ["frontend"],      // omitted when none
  "updated": "2026-07-17T…Z"
}

refs

The leanest shape — for very large orgs or pure lookups. kind appears on epic rows only; child rows carry no parent ref here (refs is deliberately terse).

{ "ref": "LOG-42", "title": "…", "kind": "epic", "size": "M", "status": "…" }

full

Complete item bodies (the legacy serializeItem shape, with descriptions). A compatibility escape hatch for exceptional cases — not for surveys. For working an item, prefer pull_item/pull_items (which return an even richer payload including tasks, notes, related items, and live PR state).

Session / survey tools

briefing

Session-start briefing in ONE call: unread inbox (marked read), items assigned to you with task progress, and the team scoreboard. Use this instead of separate check_inbox / get_my_work / team_summary calls.

Params: none.

Response:

{
  "inbox": ["<message>", …] ,           // or "empty — nothing new"
  "my_work": [ <index row>, … ],         // or "nothing assigned to you"
  "scoreboard": [
    { "who": "Ada", "active": 3, "done_30d": 7 }
  ]
}

Side effect: marks your inbox notifications as read (same as check_inbox).

backlog_stats

Call this FIRST for any survey or audit. The shape of the backlog in one small object, no item bodies.

Params: none.

Response:

{
  "total": 137,
  "by_status": { "backlog": 40, "in_progress": 12, "done": 80, "stuck": 5 },
  "by_size":   { "S": 20, "M": 30, "L": 10, "XL": 2 },
  "by_kind":   { "log": 130, "epic": 7 },
  "unsized":   35,                        // legacy items with no size — need cleanup
  "top_labels": [ { "label": "frontend", "count": 22 }, … ],
  "by_assignee": [ { "who": "Ada", "open": 4 }, … ],
  "oldest_in_progress": { "ref": "LOG-9", "updated": "2026-05-01T…Z" } // or null
}

Workflow: understand the shape here, THEN use list_backlog (filtered) and pull_items only for what you will actually work. Unsized legacy items never match a size filter — they surface only through unsized here.

list_backlog

Returns an INDEX (one line per item, no bodies). By default excludes done items. Use pull_item/pull_items to read or work an item.

ParamTypeRequiredDefaultNotes
statusenum ITEM_STATUSESno—Exact status filter.
assignee_emailemail stringno—Filter to this assignee.
labelstringno—Taxonomy label, e.g. frontend, bug.
sizearray of ITEM_SIZESno—OR semantics, e.g. ["S","M"]. Unsized items never match.
kindenum ITEM_KINDSno—Only log or only epic.
epicstring (ref)no—Returns that epic's child logs.
searchstringno—Free-text.
include_donebooleannofalseInclude done items.
verbosityrefs|index|fullnoindexSee verbosity.
limitint 1–200no50Max returned (capped at 200).
offsetint ≥0no0Skip this many matches; page with limit.

Response envelope:

{
  "total": 137,          // total matching BEFORE paging
  "returned": 50,        // rows in this page
  "offset": 0,
  "items": [ <row at requested verbosity>, … ],
  "note": "87 more match — narrow with status/label/assignee/search, or page with offset."
}

note is present only when total - offset - returned > 0.

get_my_work

Items currently assigned to you that are not done. Same INDEX rows as list_backlog.

ParamTypeRequiredDefaultNotes
sizearray of ITEM_SIZESno—OR semantics.
kindenum ITEM_KINDSno—Only log or epic.
epicstring (ref)no—Only your child logs under that epic.
verbosityrefs|index|fullnoindex—

Returns a bare array of rows, or the string "Nothing assigned to you right now." when empty.

team_summary

Per-person scoreboard: active item count and items completed in the last 30 days.

Params: none. Returns an array [{ who, active, done_30d }], or "No assigned items yet.".

check_inbox

Unread notifications for you (new assignments, notes on your items, status changes). Marks them read.

Params: none. Returns [{ at, message }], or "Inbox empty — nothing new.". (briefing folds this in.)

Reading items

pull_item

PREFERRED way to pick up ONE work item: everything in one call. Find by ref or title text; optionally claim it.

ParamTypeRequiredDefaultNotes
querystring (min 1)yes—Ref (GAP-88/LOG-12/12) or title text.
claimbooleanno—Also assign me and set in_progress.

claim=true runs an idempotent claim: adds you as assignee and moves backlog → in_progress (skipped if archived). The response then includes "claimed": true.

Response (serializeFullItem plus extras):

{
  "ref": "LOG-42",
  "title": "…",
  "kind": "epic",                  // epic rows
  "epic": "LOG-12",                // child rows
  "status": "in_progress",
  "priority": "high",
  "assignees": ["Ada <ada@x.com>"], // full name <email> in pull payloads
  "children": "2/5",               // epic rows
  "labels": ["frontend"],
  "changed_files": ["lib/db/schema.ts", …], // distinct paths from merged PRs, capped 40
  "files_truncated": true,          // present when more than the cap
  "problem": "…",
  "resolution": "…",
  "description": "…",
  "created_by": "Ada",
  "archived": true,                 // present only when archived
  "links": ["pr:<url> (merged)", "branch:feat/x"],
  "tasks": [ { "title": "…", "done": false, "assignee": "Ada" } ],
  "notes": [ { "author": "claude (Ada)", "body": "…", "at": "…Z" } ],
  "documents": [ { "filename": "ux-spec.md", "size": 1234, "author": "…", "at": "…Z" } ],
  "related_items": ["LOG-9 [done] Title", …],
  "children": [ <index row>, … ],   // when the item IS an epic: its child logs
  "recent_activity": ["<event message>", …], // last 10 events
  "claimed": true                   // only when claim=true
}

Notes:

  • documents carries metadata only — fetch content with get_document.
  • Live PR state is joined in only when the item has a pr link.
  • An epic's children array holds index rows (no bodies); pull them individually to work them.

pull_items

Batch pull_item: fetch MANY items completely in ONE call. Select by refs/queries OR by filter. Per-item recent activity is omitted (pull one ref if you need it).

ParamTypeRequiredDefaultNotes
queriesarray of strings (1–50)no—Refs or title text. Omit to use filter mode.
statusenum ITEM_STATUSESno—Filter mode.
labelstringno—Filter mode.
sizearray of ITEM_SIZESno—Filter mode.
kindenum ITEM_KINDSno—Filter mode.
epicstring (ref)no—Filter mode — an epic's child logs.
assignee_emailemailno—Filter mode.
searchstringno—Filter mode.
limitint 1–100no25Filter mode only.
claimbooleanno—Assign me + set in_progress on every selected item.

Response:

{
  "count": 6,
  "items": [ <full pull_item payload>, … ],   // no recent_activity
  "not_found": ["GAP-99: Nothing matching …"],// only when some queries missed
  "claimed": 6                                 // only when claim=true (count attempted)
}

queries mode resolves each entry independently; misses go to not_found. Duplicate numbers are de-duplicated. Claims run sequentially so events land in deterministic order.

get_item

Get full detail for one item by number (not a query). Includes description and all notes. Simpler/legacy sibling of pull_item — no live PR state, no claim, no changed_files.

ParamTypeRequiredNotes
numberintyesItem number, e.g. 42 for LOG-42.

Returns the serializeItem fields plus problem, resolution, description, created_by, tasks (each with done_by when present), notes, and related_items. Returns No item LOG-<n> when absent.

history_for_files

Before modifying a file, ask what happened to it before — the items and resolutions that shaped these paths.

ParamTypeRequiredDefaultNotes
pathsarray of strings (1–20)yes—Repo-relative paths.
limitint 1–25no10Max items per path.

A trailing / makes a path a directory query (lib/db/); otherwise the match is exact and case-sensitive. Scoped to this project.

Response:

{
  "results": [
    {
      "path": "lib/db/schema.ts",
      "matched": 3,
      "items": [
        {
          "ref": "LOG-42", "title": "…", "summary": "…", "size": "M",
          "status": "done", "updated": "…Z",
          "resolution_excerpt": "…",       // when present
          "prs": [ … ]                       // when present
        }
      ]
    }
  ]
}

Creating items

create_item

Add ONE new item. Always state the problem. Description supports markdown. Labels must already exist. Gated by the billing quota (see upgrade_required).

ParamTypeRequiredNotes
titlestring (min 1)yes—
problemstringnoWhat problem is this solving.
descriptionstringnoMarkdown.
summarystring (max 120)noOne line, imperative — shown in every index.
sizeenum ITEM_SIZESconditionalRequired for logs; omit for epics.
kindenum ITEM_KINDSnolog (default) or epic.
epicstring (ref)noAttach this log to an existing epic. Logs only.
priorityenum ITEM_PRIORITIESno—
labelsarray of stringsnoMust already exist.
linksarray of link objectsno—
tasksarray of stringsnoInitial subtask checklist.
assignee_emailsarray of emailsnoAssign team members by logbook email.

Create-time rules (enforced by validateKindFields, all-or-nothing — a violation throws and nothing is created):

  • size is required for logs (kind omitted or log). Missing → `size` is required for logs (S/M/L/XL). If this decomposes into shippable pieces, pass kind:'epic' and omit size.
  • size is rejected for epics → Epics carry no size — omit size for kind:'epic' (containers aren't sized).
  • An epic cannot have a parent → passing epic (or epic_index) on an epic → Epics cannot have a parent — `epic`/`epic_index` are for child logs only (no nesting in v1).
  • summary max length is 120 chars (schema-enforced).
  • Unknown labels, unknown assignee_emails, or an epic ref that resolves to a log (not an epic) each abort with a descriptive Error: ….

Sizing guidance (from the schema): S one focused change, single session; M a feature, several files, still one sitting; L cross-cutting / multi-session, needs a plan; XL usually should be an epic instead.

Kind decision test: if one PR would close it, it is a log (even with a long task checklist); if it decomposes into independently shippable pieces different sessions could claim, it is an epic with sized children.

Response: Created LOG-<n>: <title> (epics: Created LOG-<n> (epic): <title>).

create_items

Batch create_item: file MANY structured items in ONE call. Each entry takes the same fields as create_item, plus epic_index.

ParamTypeRequiredNotes
itemsarray (1–50) of entry objectsyesEach = a create_item payload plus epic_index.

Entry-only field:

FieldTypeNotes
epic_indexint ≥0Link this child log to an epic entry in THIS batch by its 0-based index. Use instead of epic. Logs only.

Parent linking: a child links a parent either by epic (an existing epic ref) OR epic_index (a 0-based index of an epic entry in this batch), never both. Epics in the batch are inserted first, so an in-batch child resolves to its just-created parent regardless of array order.

Validation is all-or-nothing — every check runs before any insert. Any of these aborts with nothing was created and leaves the backlog untouched:

  • Per-entry kind/size/epic interplay (same rules as create_item), reported as item <i>: <message>.
  • Unknown labels across the whole batch: Unknown label(s): <list> — nothing was created. Use list_labels / create_label first, then retry.
  • epic_index out of range: item <i>: epic_index <idx> is out of range (0..N-1) — nothing was created.
  • epic_index pointing at itself: item <i>: epic_index cannot point at itself — nothing was created.
  • epic_index pointing at a non-epic entry: item <i>: epic_index <idx> points at a non-epic entry — nothing was created.
  • An epic ref that does not resolve: item <i>: <resolve message> — nothing was created.
  • An epic ref that resolves to a log: item <i>: LOG-<n> is a log, not an epic — nothing was created.
  • A bad assignee_emails entry throws naming the email before anything inserts.

Response:

{ "created": 3, "items": [ { "ref": "LOG-42", "title": "…", "kind": "epic" }, … ] }

kind appears in each result row only for epics; results are in original input order.

Updating items

update_item

Update ONE item. Change status (done to cross off, stuck to flag blocked work), priority, title, description, labels, size, or reassign.

ParamTypeRequiredNotes
numberintyesItem number.
statusenum ITEM_STATUSESno—
priorityenum ITEM_PRIORITIESno—
titlestringno—
problemstringno—
descriptionstringno—
summarystring (max 120)no—
sizeenum ITEM_SIZESnoReclassify, or backfill an unsized legacy item.
resolutionstringnoWhat was done — write one at completion; becomes documentation.
epicstring (ref) or nullnoAttach to an epic by ref, or null to detach. Rejected on epics.
labelsarray of stringsnoReplaces the full label set; must already exist.
add_linksarray of link objectsnoAppends links.
assignee_emailsarray of emails or nullnoReplaces the full assignee set; null or [] unassigns everyone.

Semantics and rejections:

  • kind is immutable after creation — there is no way to change it.
  • epic on an item that is itself an epic → LOG-<n> is an epic — epics can't be attached to a parent.
  • epic referencing a log (not an epic) → LOG-<n> is a log, not an epic — children only attach to epics.
  • epic referencing a non-existent id → That epic does not exist in this project.
  • Unknown labels → Unknown label(s): <list>. Available: <list>. Create labels deliberately (Settings, or the create_label tool).
  • labels replaces the whole set; add_links appends; assignee_emails replaces (with null/[] meaning unassign-all).

Response: Updated LOG-<n> — status: <status>, priority: <priority>, or No item LOG-<n> when absent.

update_items

Batch update_item: apply updates to MANY items in ONE call. Each entry takes the same fields as update_item. Items are processed independently — one failure does NOT stop the rest.

ParamTypeRequiredNotes
updatesarray (1–50) of update entriesyesEach = an update_item payload (with number).

Response:

{
  "updated": 4,
  "failed": 1,
  "results": [
    { "ref": "LOG-42", "ok": true, "status": "done", "priority": "high" },
    { "ref": "LOG-9",  "ok": false, "error": "no such item" }
  ]
}

Per-item errors carry the same messages as update_item (epic rejections, unknown labels, etc.).

Tasks

add_task

Add a subtask to an item's checklist, optionally assigned.

ParamTypeRequiredNotes
numberintyes—
titlestring (min 1)yes—
assignee_emailemailno—

Returns Task added to LOG-<n>: "<title>", or No item LOG-<n>.

complete_task

Check off a subtask, identified by its title (exact or unique substring).

ParamTypeRequiredNotes
numberintyes—
taskstring (min 1)yesTask title, exact or unique substring.

Matching: exact (case-insensitive) matches win; otherwise substring matches.

  • No match → No open task matching "<task>" on LOG-<n>. Open tasks: "…", "…"
  • Ambiguous (>1) → "<task>" is ambiguous — matches: "…", "…". Be more specific.
  • Success → Checked off "<title>" on LOG-<n> — <k> task(s) remaining

Notes

add_note

Leave a markdown note on an item. The assignee is notified.

ParamTypeRequiredNotes
numberintyes—
bodystring (min 1)yesMarkdown.

Returns Note added to LOG-<n>, or No item LOG-<n>.

Labels

list_labels

The managed label taxonomy, with item counts. Items can only use labels from this set.

Params: none. Returns the label rows ({ label, count, … }), or No labels defined yet — use create_label..

create_label

Add a new label to the managed taxonomy. Check list_labels first — reuse over near-duplicates (frontend vs front-end).

ParamTypeRequiredNotes
namestring (min 1)yesLowercase slug, e.g. front-end. Normalized on write.

Returns Label created: <slug>.

Documents

Attachments live outside the item body, so they never bloat list_backlog/pull_item. Agents see only the filename metadata until they call get_document.

attach_document

Attach a markdown document to an item — for long-form specs, design docs, RFCs.

ParamTypeRequiredNotes
numberintyes—
filenamestring (min 1)yese.g. ux-spec.md. Include the .md extension.
contentstring (min 1)yesThe full markdown document.

Stored as text/markdown. Returns Attached "<filename>" (<size> bytes) to LOG-<n>, or No item LOG-<n>.

get_document

Fetch the full markdown content of an attached document. List filenames first via pull_item/get_item.

ParamTypeRequiredNotes
numberintyes—
filenamestring (min 1)yesAttached name, exact or unique substring.

Response:

{ "filename": "ux-spec.md", "size": 1234, "author": "claude (Ada)", "at": "…Z", "content": "# …" }

Returns No item LOG-<n> when the item is absent, or the finder's message when the filename does not match / is ambiguous.

Relations

link_items

Link two related items (bidirectional). Linked items appear in each other's pull_item as related_items. Link items that share a root cause, a PR cluster, or block each other.

ParamTypeRequiredNotes
numberintyesOne item's number.
tostring (min 1)yesThe other item: ref or title text.

Returns Linked LOG-<a> ↔ LOG-<b>, or LOG-<a> and LOG-<b> were already linked.

Team

list_team

List logbook team members (for assigning work by email).

Params: none. Returns [{ name, email }].

The roster is the project's org members plus anyone attributed in its items — never every user in the database (that would leak names across tenants). This same roster backs assignee_email resolution: an email that is not on it is rejected with No logbook user with email <email>. Known users: <list>.

Repos

link_repo

Register a GitHub repo with this project so its PR webhook events are recorded here. Used during project setup.

ParamTypeRequiredNotes
repostring (min 1)yesowner/name, e.g. groovy-os/logbook-v2.

Returns Linked <owner/name> to this project. PR webhook events for this repo will now be recorded here. After linking, create a webhook on the repo pointing at the logbook webhook URL (see the project's setup instructions and Troubleshooting).

Maintenance

archive_done_items

Sweep all done items into the archive (they leave the board but keep their history and still count toward done stats).

Params: none. Returns Archived <n> done item(s), or No done items to archive.

Prompts

Two planning prompts ship WITH the server (registered via registerPrompt), so they are advertised on both /api/mcp and /api/mcp/<slug>. Claude Code surfaces each as a slash command: /mcp__logbook__plan-work and /mcp__logbook__enrich-log. Each returns a single user message whose text orchestrates the existing tools; both keep a human in the loop (draft → approve → file). The prompt text does not restate tool docs — it references tools by their exact registered names.

plan-work

Title: Plan work. Turns a rough idea into well-formed backlog items, filing nothing until the human approves.

ArgTypeRequiredNotes
ideastringnoOptional one-liner seeding the interview.

Orchestrates: (1) an interview (problem, done, out-of-scope, constraints, how work splits); (2) prior-art check via backlog_stats, then list_backlog (status/label/search), history_for_files on named paths, and list_labels (adopt existing taxonomy, do not invent labels); (3) the log-vs-epic decision test with a stated verdict; (4) a full plan drafted as text for approval; (5) on approval, a single create_items call (epic first, children linked by epic_index), then attach_document for any flagged specs, reporting the created refs.

enrich-log

Title: Enrich a log. Audits an existing item against the quality bar, asks only about the gaps, then fixes it (or proposes a split) on approval.

ArgTypeRequiredNotes
refstringyesThe item to enrich — LOG-42 or search text.

Orchestrates: (1) pull_item to read the whole item; (2) audit for a missing size, missing summary, no problem statement, no acceptance criteria, an XL log that should be an epic, several deliverables under one title, or a stale status — note only real gaps; (3) ask the human only about those gaps; (4) apply fixes in a single update_item call (check list_labels, do not invent labels); (5) if it is really several deliverables, propose a split via create_items + re-parenting (epic_index or epic ref), or close the original as superseded with a resolution — only on the human's approval.

Connection model (brief)

Agents authenticate with an lgbk_… bearer token. Two token kinds exist:

  • Org key — the only kind Settings mints now. Valid only on /api/mcp/<project-slug>; the slug picks the project, the org must own it.
  • Legacy project token — fixed to one project. Valid on the bare /api/mcp, and on /api/mcp/<slug> only when the slug is its own.

serverInfo reports { name: "logbook", version: "0.9.0" }. maxDuration is 60s. For the full token/URL model, minting tokens, and setup, see Connecting agents; for auth and validation errors see Troubleshooting.