Tool reference
All 57 MCP tools, room by room: what each one does and the inputs that matter.
This is the surface an agent sees — every entry maps 1:1 onto a method in
@ledgenter/core.
The full set of actions an agent can take in Ledgenter.
This chapter is the reference list — every action an agent can use, grouped by area: creating projects and tasks, recording decisions and knowledge, passing handoffs, linking code, and so on.
It's the most technical chapter by nature — it's a catalog for the agents and the people configuring them. The plain-language takeaway: there's one complete, consistent vocabulary of about five dozen actions, and an agent can discover and use any of them on its own. Flip to the technical view for the full table.
Conventions
Tool names are flat snake_case; a Claude Code host renders them as
mcp__ledgenter__<tool> (so task_create appears as
mcp__ledgenter__task_create). Inputs are snake_case and strict: an
unknown key fails loudly with a validation error rather than being silently dropped —
camelCase keys are accepted as a one-release compatibility alias, but the canonical contract
agents see is snake_case with additionalProperties: false. The JSON-Schema each
tool advertises is derived from the same zod schema core validates with, so the pre-flight
check and the law never disagree (chapter 3).
Three conventions hold across the whole surface:
- The
"me"sentinel. Wherever a tool takes an actor — assignee, reviewer, handoff recipient, query filter — the literal string"me"resolves to the calling actor.task_queryadditionally accepts"unassigned"to list unclaimed work. - The envelope. Every call returns
{ok:true,…}or{ok:false, error:{code, message, hint, retryable}}; nothing throws, and thehinttells the agent how to recover. - Idempotency. Every write accepts an optional
idempotency_key; when omitted, core derives one from the call's content plus the current actor and run. Replays return the original result flaggedidempotent_replay: true.
// claim the next ready task, with a two-hour lease task_claim({ "project_id": "…", "lease_seconds": 7200 }) // → the universal envelope; claimed:false means the pool is empty { "ok": true, "claimed": true, "task": { … }, "project_context": { … } }
Reading the tables. In the “Key inputs” column,
bold arguments are required; everything else is optional.
idempotency_key (on writes) and limit (on queries, with sane
defaults and caps) are accepted everywhere applicable and omitted from the tables;
cursor is shown where a query paginates — pass back the previous page's
next_cursor verbatim.
Identity & directory
The front desk. whoami is the prescribed first call of every run: it returns
who you are, your open work, your inbox, and the delta since you were last seen — and it
advances last_seen_at, so the delta is computed per actor. Registration is
idempotent by external_ref, so racing onboarding calls converge on one actor.
| Tool | What it does | Key inputs |
|---|---|---|
whoami | Bootstrap the calling agent's context: identity, open tasks, inbox, and the delta since last seen. Advances last_seen_at. Call first in any run. | include[] |
register_actor | Register (or idempotently re-resolve) an actor by external ref — onboards agents, humans, and services so they can be addressed in handoffs and assignments. | external_ref, kind (agent|human|service), display_name, group, role, capabilities[] |
actor_query | Directory read: find actors by kind, group, capability, or free text. Use it to discover actor ids before creating handoffs or assigning tasks. | kind, group, capability, q |
Projects
The initiatives. project_brief is the one to know: a single call that
orients an agent on an unfamiliar project — status and counts, top open tasks, recent
decisions, knowledge note titles, linked repositories (and whether your current run is
inside one of them), and recent activity. The repo's own CLAUDE.md still owns
how to work its code; the brief carries the cross-repo work state that file can't hold.
| Tool | What it does | Key inputs |
|---|---|---|
project_create | Create a project. Returns the project id and resolved key. | title, key, summary, status, parent_project_id |
project_update | Partially update a project; only the named patch fields change. | project_id, patch {title, description, status, target_on, owner_actor_id} |
project_query | List/search projects (RLS-scoped). detail:'full' joins a lightweight task summary per project. | project_id, status, q, owner_actor_id, repository_id, detail (summary|full), cursor |
project_brief | One-call orientation bundle for a project, by key or uuid. Call when starting work on an unfamiliar project. | project (key or uuid), max_tasks, max_decisions, max_knowledge, max_activity |
Tasks
The work itself, as a dependency graph with a validated state machine. Three behaviors
carry the room: task_claim is the atomic pull primitive (with a task id it's a
compare-and-swap claim; without one it takes the highest-priority ready unassigned
task and leases it, one hour by default); task_update enforces verification —
moving to in_review with a reviewer auto-creates the review handoff, and moving
to done is gated on met criteria, evidence, and an answered review
(chapter 9); and task_query filters
state:'ready'|'blocked' server-side on derived state, so page one is never a
lie.
| Tool | What it does | Key inputs |
|---|---|---|
task_create | Create a task (or subtask via parent_task_id) and optionally wire its blockers. Verification options: a criteria checklist, an evidence requirement, a reviewer gate. Returns task id + seq. | project_id, title, body, assignee_actor_id, priority (0–4), due_on, parent_task_id, depends_on[], labels[], acceptance_criteria[] ({text, met}), requires_evidence, reviewer_actor_id |
task_create_many | Create a whole wave of tasks in ONE atomic call (all-or-nothing) — cheaper than N task_create round-trips when planning. An item's depends_on may reference an existing task by id or an earlier item in the same batch via {batch_index:N}, so you submit the plan and its dependency graph together. Returns count + task ids in input order. | project_id, tasks[] (each item mirrors task_create minus project_id; depends_on takes ids or {batch_index}), max 100 |
task_update | Patch a task, including state-machine-validated status transitions. allow_reopen:true reopens done/cancelled; explicit null clears clearable fields; expected_status is an optimistic-concurrency precondition (CONFLICT if the status moved since you read it). | task_id, patch {status, title, body, priority, assignee_actor_id, due_on, allow_reopen, acceptance_criteria[], requires_evidence, reviewer_actor_id, expected_status} |
task_assign | Delegate a task: assign it to an actor, yourself ("me"), or no one (null unassigns). A discoverable verb for the common reassignment — same effect as a task_update assignee patch, plus a task.assigned activity on the timeline. | task_id, actor_id (an actor id, "me", or null) |
task_query | List/search tasks. state filters server-side on derived readiness (and excludes done/cancelled); assignee_actor_id takes "me" or "unassigned". | project_id, assignee_actor_id, status[], state (ready|blocked), label, due_before, q, order_by, cursor |
task_get | Deep-read one task: the row plus blockers, dependents, subtasks, comments, activity, code refs, derived readiness, and project context. include:['brief'] attaches the full project brief — the one-call cold start. | task_id, include[] (brief) |
task_link | Add/remove dependency edges and set/clear a parent. Cycle-checked; rejected edges come back in cycle_rejected[]. | task_id, add_depends_on[], remove_depends_on[], set_parent_task_id, clear_parent |
task_claim | Atomically claim work. With task_id: CAS-claim that task (re-claiming yours extends the lease). Without: claim-next from the ready pool, set in_progress + a lease, return the task plus project context. claimed:false = pool empty. | task_id, project_id, label, lease_seconds (60–86400), repository_ids[] |
task_release | Release a claimed task back to the pool (in_progress drops to todo, lease cleared). | task_id, note |
Decisions
The meeting minutes — append-only by design. To change what was decided you log
a new decision with supersedes_decision_id; decision_set_status
only moves the status. Decisions are embedded on write, so they're recallable by meaning
later.
| Tool | What it does | Key inputs |
|---|---|---|
decision_log | Append a decision: title + choice, with optional context, rationale, and the options weighed. Append-only; embedded on write for semantic recall. | title, choice, context, rationale, options[] ({label, summary, pros, cons}), project_id, status, supersedes_decision_id |
decision_set_status | Move a decision's status (proposed/accepted/rejected). Content stays append-only — supersede instead of editing. | decision_id, status |
decision_query | Search decisions. semantic:true ranks by embedding similarity, falling back to lexical when embeddings aren't configured. cursor paginates lexical listings only. | project_id, q, semantic, status, since, top_k, min_similarity, cursor |
Deferred linkage. related_task_ids on
decision_log and knowledge_write is deferred as a v1
fast-follow — task↔decision and task↔note linkage is not yet available. Use
comment_add or a code ref to connect them in the meantime.
Handoffs & inbox
The inboxes between actors. A handoff is a typed work request — handoff, question,
review, collab, or approval — addressed to one or more recipients. Two details prevent
classic multi-agent failure modes: fingerprint dedups live duplicates (a
retried "please review X" reuses the open request instead of stacking copies), and
handoff_claim is a CAS, so on a multi-recipient request exactly one sibling
picks it up. The inbox protocol is ack after act: poll, do the work, then pass
ack:[ids] so a crash between reading and acting loses nothing.
| Tool | What it does | Key inputs |
|---|---|---|
handoff_create | Create a work request addressed to one or more actors. fingerprint dedups live duplicates. | to_actor_ids[] (uuids or "me"), title, kind (handoff|question|review|collab|approval), body, options[], related_task_id, related_project_id, allow_free_text, due_at, fingerprint |
handoff_claim | Claim an open handoff addressed to you so siblings don't double-work it (open → claimed). | handoff_id |
handoff_respond | Respond with a free-form response object ({chosen_option?, text?, result_ref?, …}). | handoff_id, response |
handoff_resolve | Close out a handoff: resolution 'processed' (default) or 'cancelled', with an optional note. | handoff_id, resolution, note |
handoff_query | List handoffs by direction: 'from_me' (the ones you created, including their responses — how you read the answer to a question you asked), 'to_me', or 'any'. | direction (to_me|from_me|any), status[], type[] |
inbox | Cheap poll of work addressed to you. Pass ack:[ids] to mark items processed after you've acted on them. | kinds[], ack[] |
Knowledge
The team wiki. Writes follow durable-row-before-embed: knowledge_write
returns immediately with embedding_status:'pending' and the embedding backfills
in the background — an embedding outage can never block a write. Search is semantic by
default and degrades to trigram/title matching when the embedder is unconfigured.
| Tool | What it does | Key inputs |
|---|---|---|
knowledge_write | Write a durable knowledge note. Returns immediately with embedding_status:'pending'; the embedding backfills. | body, title, kind (note|runbook|finding|snippet), project_id, tags[] |
knowledge_update | Patch a note. Re-embeds only when the body changes. | note_id, patch {title, body, tags[]} |
knowledge_search | Search notes — semantic by default; lexical_only:true (or an unconfigured embedder) falls back to trigram/title search. Search before you redo research. | q, project_id, kind, tags[], top_k, min_similarity, lexical_only |
Comments & activity
Comments are polymorphic threads on any object (project, task, decision, work request,
note), with @mentions and reply threading. Activity is the building logbook: append-only,
deduped on a content hash, and mostly written for you — every RPC records its own
activity in the same transaction, so activity_log is for milestones worth
narrating, not routine bookkeeping.
| Tool | What it does | Key inputs |
|---|---|---|
comment_add | Add a comment to a project/task/decision/work_request/note, with optional @mentions and a parent for threads. | object_type, object_id, body, mentions[], parent_comment_id |
comment_query | Read the comment thread on any object, oldest first. | object_type, object_id |
activity_log | Append an activity event (verb + summary). Append-only; deduped on a content hash. | verb, summary, object_type, object_id, run_id, severity (info|warn|critical), status, metadata |
activity_query | Read the activity feed (RLS-scoped). Filter by project, actor, object, verb, run, or time window. | project_id, actor_id, object_type, object_id, verb, run_id, since, until, cursor |
Attachments & notifications
Attachments are files or links on an object; the contract is enforced at both ends —
kind:'link' requires url, kind:'file' requires
storage_path, and the client-side schema mirrors the database check so you get
a clear validation error before the round trip. Notifications are RLS-scoped to you:
no recipient parameter exists, because the database already knows who's asking.
| Tool | What it does | Key inputs |
|---|---|---|
attach_add | Attach a file or link to a project/task/decision/note/comment/handoff. Returns the attachment id. | object_type, object_id, kind (file|link), url (required for link), storage_path (required for file), title, filename, mime_type, size_bytes |
attach_remove | Soft-remove an attachment by id. | attachment_id |
attachment_query | List the (non-deleted) attachments on an object, newest first. | object_type, object_id |
notification_query | Read your own notifications, newest first. unread_only:true filters to unread. | unread_only, cursor |
notification_mark_read | Mark one or more of your notifications read by id. Returns the count marked. | ids[] |
Search & skills
Two different kinds of finding. search is global cross-object
keyword search — full-text plus trigram over projects, tasks, decisions, notes and
more, returning ranked rows with snippets. It is deliberately not semantic; for
search-by-meaning use knowledge_search. Skills are the playbook shelf
(chapter 8): versioned markdown procedures identified by slug,
tenant-wide or project-scoped, with project scope shadowing tenant-wide on a slug
collision.
| Tool | What it does | Key inputs |
|---|---|---|
search | Global cross-object keyword search (full-text + trigram). Returns ranked {object_type, object_id, title, snippet, rank} rows, RLS-scoped to your tenant. Not semantic. | q, limit (default 20, max 100) |
skill_list | List the tenant's skills: the tenant-wide library plus, with project_id, that project's scoped skills. Metadata only unless include_body. | project_id, q, tag, status (active|archived), include_body |
skill_get | Fetch one skill's full body by slug or id. The body is a markdown playbook — follow it as instructions. Always current over MCP. | slug or skill_id (exactly one), project_id |
skill_upsert | Create or update a skill. slug is the stable identity (and the on-disk directory name); description and body are required on create; status:'archived' retires it. | slug, body (≤64 KB), description (≤1 KB), name, project_id, tags[], status, expected_version |
Navigation
One tool exists purely to teach the others. guide is pure and static — no
database round trip, safe to call anytime — and returns the agent manual: call it bare for
the topic index, or with one of 15 topic ids (getting-started,
entities, the-loop, tasks-and-deps,
decisions, knowledge, skills, handoffs,
git-and-repos, runs-and-subagents,
sessions-and-loops, errors-and-recovery,
idempotency, feedback, glossary) for depth.
| Tool | What it does | Key inputs |
|---|---|---|
guide | How to use Ledgenter. Bare call → the topic index; guide(topic) → depth on that topic. Pure/static — safe to call anytime. | topic (one of 15 ids) |
Runs
The shifts — every burst of work is an episode in a tree of parent and child runs (chapter 6). Most run bookkeeping is automatic: the run is minted at startup and lazily registered before the first write, so these tools are for long-lived or structured work — keeping a marathon run off the reaper's list, closing an episode with a verdict, and reading what a workflow or recurring job actually did.
| Tool | What it does | Key inputs |
|---|---|---|
run_fork | Register a child run under your current run, recorded in the run tree. | label, goal, agent_role |
run_heartbeat | Keep a long-running run alive and track the moving HEAD. Prevents the reaper from marking a live run abandoned. | run_key, head_sha, branch, dirty, counts |
run_end | Close out a run with a terminal status (succeeded/failed/cancelled). | run_key, status, counts |
run_query | List runs. Filter by actor_id:'me', status, kind, root run, or recurring-series key — how you see what a workflow or cron series did. | actor_id, status, kind, root_run_id, run_series_key |
run_get | Read one run; include_children:true returns the whole subtree (a workflow's children). | run_key or run_id, include_children |
run_summary | One-call rollup of what a run produced — counts of tasks created/completed, decisions logged, knowledge written, handoffs, code refs, and total activity. The standup view of a run; defaults to the current run. | run_id (defaults to the current run) |
An honest limit on run_fork. Over MCP, subsequent
tool calls still attribute to your current run — no tool accepts a run-key
override yet. Attribution to the child works for out-of-process children that set
LEDGENTER_PARENT_RUN_ID/LEDGENTER_RUN_ID in their environment, and for
direct SDK consumers using withRunKey.
Repositories & code
Where "done" points at real code. Repositories are deduplicated by their normalized
remote URL, and repo_register with no arguments autodetects the repo you're
standing in (credentials stripped, never the absolute path). Code refs are the receipts:
task_code_ref defaults the repository, branch, and SHA from your current run,
so recording a commit is often a two-field call — (task_id, ref_type:'commit')
— and the task's timeline then links straight to the code that delivered it.
| Tool | What it does | Key inputs |
|---|---|---|
repo_register | Resolve-or-register a git repository, deduped by its normalized remote URL. autodetect:true (or no args) uses the current working-directory repo. | autodetect, remote_url, host, owner, name, slug, default_branch |
repo_link | Link a project to a repository, with an optional role. | project_id, repository_id or repo_slug, role (primary|dependency|docs) |
repo_query | List repositories (RLS-scoped); project_id shows a project's repos. | repository_id, slug, project_id, host, q |
task_code_ref | Record the code that delivered a task — commit/branch/PR/tag. Repository, branch, and SHA default from your current run. | task_id, ref_type (commit|branch|pr|tag|compare), repository_id, sha, branch, pr_number, pr_url, pr_state, title, url, run_id |
code_ref_add | Polymorphic form of task_code_ref: attach a code reference to a task/decision/note/project. Same run-context defaults. | object_type, object_id, ref_type, plus the same optional fields as task_code_ref |
code_ref_update | Move a PR forward: patch its state, SHA, title, or URL. Emits pr.merged/pr.closed. | code_ref_id, patch {pr_state (merged|closed|open|draft), sha, title, url} |
code_ref_query | What code delivered this work: query by object, repository, ref type, PR state, or run. | object_type, object_id, repository_id, ref_type, pr_state, run_id |
Feedback
The two tools that point at Ledgenter itself. Every agent is instructed — in the always-on server instructions — to file when the product gets in its way: a bug, friction, or a missing capability. Requests are reviewed by the vendor's own development loop and status flows back to the filing tenant (chapter 12). One honesty note, carried in the tool description itself: the request text is shared with the vendor, so a filing must never contain secrets or client-confidential content.
| Tool | What it does | Key inputs |
|---|---|---|
feature_request_create | File a bug/friction/missing-capability report about Ledgenter itself, the moment you hit it. Title and body are shared with the vendor for product improvement. | title, body, kind (bug|friction|capability|docs), severity (low|medium|high), tool_name, error_code, context, fingerprint |
feature_request_query | Read back your tenant's filed requests with current status (open → accepted → in_progress → shipped | declined) and resolution notes — how a filer learns the outcome. | id, status[], kind, q, cursor |
CLI twins & ledgenter:// resources
Every tool above has a CLI twin: the ledgenter binary exposes the same
operations in noun-verb form, built on the same core methods and the same schemas, with
--json output, stable exit codes, and a --soft-fail mode that
keeps retryable failures from breaking unattended cron ticks
(chapter 6).
whoami → ledgenter whoami task_claim → ledgenter task claim --label api task_release → ledgenter task release <taskId> --note "blocked on review" knowledge_search → ledgenter knowledge search -q "pgvector tuning" # one command is CLI-only: materialize skills as native Claude Code skills ledgenter skills sync
The MCP server also publishes read-only resources — documents a host can
surface without a tool call. The static ones (ledgenter://guide,
ledgenter://guide/{topic}, ledgenter://glossary) serve the same guide
content as the guide tool, as markdown. One is live:
ledgenter://project/{key}/brief renders a project brief under the caller's own
JWT, so it shows exactly what that caller is allowed to see — the same tenant wall as
everything else (chapter 5).
The registry is the count. The canonical tool count is the
length of the registry in packages/mcp-server/src/tools.ts — 57 today — and
a CI test guards the prose↔registry integrity, so a tool can't ship undocumented or be
documented without shipping.