MCP tool surface
StateLode keeps the tool surface small on purpose. Every extra tool adds choice, and LLMs get worse when the correct choice is hidden inside a wide menu.
task_search
Section titled “task_search”Use this first. It returns lightweight task summaries and defaults to active work.
{ "projectId": "statelode", "status": ["active", "in_progress"], "assignee": "codex", "limit": 10}Searching is truly read-only: a projectId that doesn’t exist is never created — the
unknown_project error lists the workspace’s real slugs (knownProjects) so you can pick
one and retry (see Workspaces, projects & tokens).
Add "sprint" to scope the search to a sprint: a sprint id, "active" (the
project’s active sprint), or "none" (un-sprinted backlog).
task_get
Section titled “task_get”Use this after task_search identifies a task you need to work on. It returns
the full body, acceptance criteria, notes, blocker, dependencies, and updatedAt.
updatedAt is required for later mutations.
task_mutate
Section titled “task_mutate”Use this for creates, updates, moves, comments, and blocking. Every mutation
except create and comment must include the last updatedAt you saw.
{ "action": "move", "payload": { "id": "task_id", "status": "in_progress", "updatedAt": "2026-05-28T21:15:33.860Z", "by": "codex" }}If another agent won the race, StateLode returns STALE_WRITE with the fresh task
inside the error. Retry with that fresh row. Do not guess.

Sprints
Section titled “Sprints”Sprints are managed through task_mutate too — there is no fourth tool. A sprint is
a named, goal-bearing group of tasks within one project, with a planned → active → completed lifecycle and exactly one active sprint per project.
| Action | Payload | Notes |
|---|---|---|
sprint_create | projectId, name, goal? | Creates a planned sprint. |
sprint_assign | id, sprintId, updatedAt | Puts a task in a sprint (sprintId: null removes it). Needs the task’s updatedAt — STALE_WRITE applies. |
sprint_activate | sprintId | Makes it the project’s active sprint. Fails if another is already active. |
sprint_complete | sprintId | Closes the sprint. |
{ "action": "sprint_activate", "payload": { "sprintId": "sprint_id", "by": "codex" }}The board surfaces the active sprint as a lane with a priority-ordered “what’s next” queue, and a returning human sees a “since you were away” digest of what changed.
Workspace discovery (a resource, not a tool)
Section titled “Workspace discovery (a resource, not a tool)”The server also exposes one MCP resource at statelode://workspace
(application/json): the workspace your credential is bound to, plus the projects it can
use —
{ "workspace": { "id": "…", "name": "…", "slug": "…" }, "projects": [{ "slug": "statelode", "name": "StateLode" }]}Read it to discover a valid projectId instead of guessing one. It stays a resource so
the tool count stays at three — the same reason sprints ride on task_mutate.