Versions

#309 • 13 versions, newest first

Sep 19, 2026Claude CodeCurrent

MCP tool surface v2

Status: implemented and deployed (#313 to #317, switched in #317 on 2026-09-19; #310 done). It replaced the 27 tools of the old surface with eight, as one incompatible switch; the live list is 6,462 characters, and McpDiscoveryTests holds it to that budget. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changed only the tool surface. The measurements of #311 passed both gates; see Measurements. What connected agents have to change is in the release note #325.

Continue here

State on 2026-09-19. v2 is live on conderis.com with system guide v32. Recall and lists name memories as #N or memory:<id>. The Conderis Batch v2 schema and example are published under /schemas and /examples, and a test sends the published example through item_store. The owner's global CLAUDE.md uses the v2 names. Session summary: #329.

Next, in this order:

  1. #321 Memory visibility private or shared (Decision 1). It is the smaller change and the one agents notice: item_store already advertises only private, shared but the server still accepts public. Needs a migration (public to shared) and a move action for organization memories; nothing moves automatically.
  2. #320 Drop revisions (Decision 6). The large one: first move memory proposals off draft revisions, then put content on the item and one full-state history for every kind. It ends with deleting IItemHistory/RevisionItemHistory (src/Conderis/Services/Items/ItemHistory.cs), the internal mapping from version to revisions. The web history views must look as they do today.
  3. #323 Review setting per agent key (Decision 3, low priority). Small, independent, can go in any time.

Each one gets its own migration, its own system guide version and a short addition to the release note #325 when agents see a difference.

Rules when changing the tool surface:

  • A changed tool description must also change tests/Conderis.Tests/ToolSurface/V2Prototype.cs; BuiltV2ToolsMatchTheMeasuredSchemas compares them. Then update MaxToolListCharacters in McpDiscoveryTests to the measured size and add the step to the log comment there. Raising it is a decision; shorten other text first.
  • A changed system guide text (src/Conderis/Services/SystemGuide.cs) bumps SystemGuide.Version; recall announces it, so agents can tell their copy is stale. The server instructions live in Program.cs.
  • Web prompts that name tools: Components/Pages/Connect.razor and Guide.razor.
  • HTTP-level MCP tests: HttpTests, McpDiscoveryTests, ProposalHttpTests, InstructionPushTests. Direct tool tests: ItemReadToolsTests, ItemStoreToolsTests, ItemActionToolsTests, ProjectToolsTests. In direct tests use a fixed IHttpContextAccessor (FixedContext), because the default accessor keeps its context in a static AsyncLocal.

How to verify and ship: build, test and migrations are on #282 and #283; publishing and deploying on #282. Deploying is outward-facing: ask the owner first, check git status, and afterwards run a fresh headless session against production (claude -p with the conderis tools) that recalls, creates and completes a task and changes a memory.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeated the same ideas per kind: four tools wrote items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decided proposals. The same things were named and behaved differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete took both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also meant closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind named the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) did not fit into the budget as it stood.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could have been fixed on the 27 tools without consolidation; #311 measured that option as a control.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carried its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both were recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields or values.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear. Sending a task's current assignee back keeps it, even when that agent's connection has ended.
  • One version for every item; content changes and task and item actions check it; moves, relations and proposal actions do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happened with system guide version 31, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line in item_store, filters line in item_list, actions row in item_action, the server-side validator and tests. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review left MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

The tools live in src/Conderis/Services/Items (ProjectTools, ItemReadTools, ItemStoreTools, ItemActionTools, with ItemRef, ItemPage, TextPart and ItemHistory); the system guide is SystemGuide. The descriptions measured in #311 are in tests/Conderis.Tests/ToolSurface/V2Prototype.cs, and a test keeps the built tools equal to them.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped. Memories appear as #N in the recalled project, memory:<id> without a project, and with the long project reference only for an upstream's memory.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people by display name and agents, with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>. A project item is read by number.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • Tasks are the active ones (open, in progress, blocked) unless includeClosed. There is no status filter: in the #311 sessions agents asked for "open tasks" translated that into status: open every time and lost blocked and in-progress work; a description that explained the default did not change that.
  • filter is a JSON object per kind: task priority, assignee, parent, includeClosed; memory type, tags, visibility, includeArchived, includeExpired; page parent, includeArchived; proposal state, author, targetKind. An unknown filter is refused with the allowed ones.
  • Response per #304: short entries, total, hasMore, nextCursor. A task entry names its parent and assignee the way item_get does.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis). Published as Conderis Batch v2: schema /schemas/conderis-batch-v2.schema.json, example /examples/conderis-batch-v2.json.

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at the key of an earlier item of the same kind or at an existing number. The response maps keys to the assigned numbers. An item holds only key, kind and fields; anything else is refused.
  • Up to 100 items and 1 MB per batch; kinds may be mixed. Larger imports send several batches.
  • Each kind is stored atomically on its own (#315): tasks, pages and memories are written by separate services, so one transaction across them is not possible. A failing kind is reported and leaves the other kinds stored. Every kind records its result under the idempotency key plus its kind, so sending the same batch again returns the kinds already stored and retries only the one that failed.
  • Memories that need review become proposals; tasks are never reviewed; pages in a batch need moderation rights.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged as batch with kind: memory.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock, relate, unrelate; later reject
proposal approve, reject (both with a reason), withdraw
memory, page archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do. A task is closed with complete or by setting status canceled, never archived.

Version: task and item actions need the version from item_get; relate, unrelate and the proposal actions do not. Proposals are immutable, so their version only guarded against deciding twice, which the pending state already prevents. In the #311 sessions agents left out the version on approve in three of eight runs.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review", #323, open) and propose: true per call (implemented).
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, each kind atomic on its own with its own retry (changed on 2026-09-18 in #315 from "the whole batch atomic": there is no shared transaction across the services).
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; before v2 tasks and page moves checked it, memories and page text checked a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Until #320 a version of a memory, page or the instructions is the ordinal of its published revision along the base chain, counting privacy-locked revisions, so deleting one version keeps the numbers of the others.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.
  8. From #311 (2026-09-18): the task list has no status filter, only includeClosed; proposal actions need no version.
  9. No email addresses (2026-09-18): responses name people by a display name each person sets in the settings, prefilled with the part before the @.

Measurements (#311, 2026-09-18)

Method: the schemas are generated offline by the MCP SDK exactly as the server advertises them; the offline list of the old tools matched the live list to the character (19,014). Stubs and the measuring test live in tests/Conderis.Tests/ToolSurface, the session harness in tests/ToolSurfaceSession (da0842d, ae81f71). Tokens are counted with o200k as an approximation.

List size (final v2 descriptions after the session findings)

Series Tools Characters Tokens
Before v2 27 19,014 4,043
Consistency fixes only, descriptions cut to v2 length (plus task_block) 28 12,614 2,872
v2 (measured) 8 6,386 1,431
v2 (live after #317) 8 6,462 about 1,450

The live list is 76 characters above the measurement, because item_store and item_action descriptions were corrected during implementation (batch atomicity per kind, actions per kind). v2 is 66 % smaller than the old surface and half the size of the consistent 28 tools. Largest live v2 tools: item_store 1,362, item_list 983, item_action 977, item_get 909.

Growth by one kind (commits as the example): v2 +97 characters (+23 tokens), for the added lines in item_store, item_list and item_get. Consistent series +1,038 characters (+244 tokens) for three tools (commit_list, commit_link, commit_unlink).

Round trip for on-demand clients (create, list, complete a task; information only): before task_create + task_list + task_complete 505 tokens, v2 item_store + item_list + item_action 745 tokens. An agent that loads tools one by one pays about 240 tokens more per round trip in v2; clients that load the whole list save about 2,600 tokens per session.

Findability (information only): a keyword score over name, first sentence and the rest of the description found the intended v2 tool first for all six queries ("create task", "store memory", "read page", "approve proposal", "block task", "recall project"). Before, "read page" ranked item_get second and "block task" had no tool.

Retries in a real session. A stub server serves the measured list of one surface over stdio, answers from a small fixed project, checks calls the way the real server does and logs every call. Headless Claude Code sessions (claude -p, tools loaded on demand) did the same six things against each surface: create a subtask, change a memory's summary, block a task with a reason, list the open tasks, read a section of a page, approve a proposal.

Surface Runs Calls per run Refused calls What was refused
Before v2 3 10 0, 1, 1 memory_store with number but without projectId
v2, first design 5 9–11 0, 0, 1, 0, 1 approve without version
v2, first design 5 — — every run listed "open tasks" with status: open and missed the blocked task #14
v2, final design 3 8–9 0, 1, 0 status filter, answered with the allowed filters and corrected in the next call

Every refusal was corrected in the next call. The first design had a correctness problem that no refusal showed: status: open returned a plausible but incomplete list. Two design changes followed (Decision 8), and with them all runs listed the right tasks. The old surface recorded a block reason only by writing it into the task description; v2 recorded it as a reason in every run.

Gate: met. The list is 6,386 characters against 19,014, and the final v2 needed one refused call in three runs against two in three for the old surface.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility), #323 (review setting per key), #325 (release note), #329 (session summary).

Sep 19, 2026Claude Code

MCP tool surface v2

Status: implemented (#313 to #317, switched in #317 on 2026-09-19). It replaced the 27 tools of the old surface with eight, as one incompatible switch; the live list is 6,462 characters, and McpDiscoveryTests holds it to that budget. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface. The measurements of #311 passed both gates; see Measurements. What connected agents have to change is in the release note #325.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeated the same ideas per kind: four tools wrote items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decided proposals. The same things were named and behaved differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete took both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also meant closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind named the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) did not fit into the budget as it stood.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could have been fixed on the 27 tools without consolidation; #311 measured that option as a control.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carried its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both were recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields or values.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear. Sending a task's current assignee back keeps it, even when that agent's connection has ended.
  • One version for every item; content changes and task and item actions check it; moves, relations and proposal actions do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happened with system guide version 31, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line in item_store, filters line in item_list, actions row in item_action, the server-side validator and tests. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review left MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

The tools live in src/Conderis/Services/Items (ProjectTools, ItemReadTools, ItemStoreTools, ItemActionTools); the system guide is SystemGuide. The descriptions measured in #311 are in tests/Conderis.Tests/ToolSurface/V2Prototype.cs, and a test keeps the built tools equal to them.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people by display name and agents, with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • Tasks are the active ones (open, in progress, blocked) unless includeClosed. There is no status filter: in the #311 sessions agents asked for "open tasks" translated that into status: open every time and lost blocked and in-progress work; a description that explained the default did not change that.
  • filter is a JSON object per kind: task priority, assignee, parent, includeClosed; memory type, tags, visibility, includeArchived, includeExpired; page parent, includeArchived; proposal state, author, targetKind. An unknown filter is refused with the allowed ones.
  • Response per #304: short entries, total, hasMore, nextCursor. A task entry names its parent and assignee the way item_get does.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at the key of an earlier item of the same kind or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed. Larger imports send several batches.
  • Each kind is stored atomically on its own (#315): tasks, pages and memories are written by separate services, so one transaction across them is not possible. A failing kind is reported and leaves the other kinds stored. Every kind records its result under the idempotency key plus its kind, so sending the same batch again returns the kinds already stored and retries only the one that failed.
  • Memories that need review become proposals; tasks are never reviewed.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged as batch with kind: memory.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock, relate, unrelate; later reject
proposal approve, reject (both with a reason), withdraw
memory, page archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do. A task is closed with complete or by setting status canceled, never archived.

Version: task and item actions need the version from item_get; relate, unrelate and the proposal actions do not. Proposals are immutable, so their version only guarded against deciding twice, which the pending state already prevents. In the #311 sessions agents left out the version on approve in three of eight runs.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review", #323, open) and propose: true per call (implemented).
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, each kind atomic on its own with its own retry (changed on 2026-09-18 in #315 from "the whole batch atomic": there is no shared transaction across the services).
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; before v2 tasks and page moves checked it, memories and page text checked a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Until #320 a version of a memory, page or the instructions is the ordinal of its published revision along the base chain, counting privacy-locked revisions, so deleting one version keeps the numbers of the others.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.
  8. From #311 (2026-09-18): the task list has no status filter, only includeClosed; proposal actions need no version.
  9. No email addresses (2026-09-18): responses name people by a display name each person sets in the settings, prefilled with the part before the @.

Measurements (#311, 2026-09-18)

Method: the schemas are generated offline by the MCP SDK exactly as the server advertises them; the offline list of the old tools matched the live list to the character (19,014). Stubs and the measuring test live in tests/Conderis.Tests/ToolSurface, the session harness in tests/ToolSurfaceSession (da0842d, ae81f71). Tokens are counted with o200k as an approximation.

List size (final v2 descriptions after the session findings)

Series Tools Characters Tokens
Before v2 27 19,014 4,043
Consistency fixes only, descriptions cut to v2 length (plus task_block) 28 12,614 2,872
v2 (measured) 8 6,386 1,431
v2 (live after #317) 8 6,462 about 1,450

The live list is 76 characters above the measurement, because item_store and item_action descriptions were corrected during implementation (batch atomicity per kind, actions per kind). v2 is 66 % smaller than the old surface and half the size of the consistent 28 tools. Largest live v2 tools: item_store 1,362, item_list 983, item_action 977, item_get 909.

Growth by one kind (commits as the example): v2 +97 characters (+23 tokens), for the added lines in item_store, item_list and item_get. Consistent series +1,038 characters (+244 tokens) for three tools (commit_list, commit_link, commit_unlink).

Round trip for on-demand clients (create, list, complete a task; information only): before task_create + task_list + task_complete 505 tokens, v2 item_store + item_list + item_action 745 tokens. An agent that loads tools one by one pays about 240 tokens more per round trip in v2; clients that load the whole list save about 2,600 tokens per session.

Findability (information only): a keyword score over name, first sentence and the rest of the description found the intended v2 tool first for all six queries ("create task", "store memory", "read page", "approve proposal", "block task", "recall project"). Before, "read page" ranked item_get second and "block task" had no tool.

Retries in a real session. A stub server serves the measured list of one surface over stdio, answers from a small fixed project, checks calls the way the real server does and logs every call. Headless Claude Code sessions (claude -p, tools loaded on demand) did the same six things against each surface: create a subtask, change a memory's summary, block a task with a reason, list the open tasks, read a section of a page, approve a proposal.

Surface Runs Calls per run Refused calls What was refused
Before v2 3 10 0, 1, 1 memory_store with number but without projectId
v2, first design 5 9–11 0, 0, 1, 0, 1 approve without version
v2, first design 5 — — every run listed "open tasks" with status: open and missed the blocked task #14
v2, final design 3 8–9 0, 1, 0 status filter, answered with the allowed filters and corrected in the next call

Every refusal was corrected in the next call. The first design had a correctness problem that no refusal showed: status: open returned a plausible but incomplete list. Two design changes followed (Decision 8), and with them all runs listed the right tasks. The old surface recorded a block reason only by writing it into the task description; v2 recorded it as a reason in every run.

Gate: met. The list is 6,386 characters against 19,014, and the final v2 needed one refused call in three runs against two in three for the old surface.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility), #323 (review setting per key).

Sep 19, 2026Claude Code

MCP tool surface v2

Status: implemented (#313 to #317, switched in #317 on 2026-09-19). It replaced the 27 tools of the old surface with eight, as one incompatible switch; the live list is 6,462 characters, and McpDiscoveryTests holds it to that budget. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface. The measurements of #311 passed both gates; see Measurements. What connected agents have to change is in the release note docs/release-note-tool-surface-v2.md (local, not in Git).

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeated the same ideas per kind: four tools wrote items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decided proposals. The same things were named and behaved differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete took both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also meant closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind named the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) did not fit into the budget as it stood.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could have been fixed on the 27 tools without consolidation; #311 measured that option as a control.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carried its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both were recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields or values.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear. Sending a task's current assignee back keeps it, even when that agent's connection has ended.
  • One version for every item; content changes and task and item actions check it; moves, relations and proposal actions do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happened with system guide version 31, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line, filters line, actions row and validator. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review left MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

The tools live in src/Conderis/Services/Items (ProjectTools, ItemReadTools, ItemStoreTools, ItemActionTools); the system guide is SystemGuide. The descriptions measured in #311 are in tests/Conderis.Tests/ToolSurface/V2Prototype.cs, and a test keeps the built tools equal to them.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people by display name and agents, with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • Tasks are the active ones (open, in progress, blocked) unless includeClosed. There is no status filter: in the #311 sessions agents asked for "open tasks" translated that into status: open every time and lost blocked and in-progress work; a description that explained the default did not change that.
  • filter is a JSON object per kind: task priority, assignee, parent, includeClosed; memory type, tags, visibility, includeArchived, includeExpired; page parent, includeArchived; proposal state, author, targetKind. An unknown filter is refused with the allowed ones.
  • Response per #304: short entries, total, hasMore, nextCursor. A task entry names its parent and assignee the way item_get does.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at the key of an earlier item of the same kind or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed. Larger imports send several batches.
  • Each kind is stored atomically on its own (#315): tasks, pages and memories are written by separate services, so one transaction across them is not possible. A failing kind is reported and leaves the other kinds stored. Every kind records its result under the idempotency key plus its kind, so sending the same batch again returns the kinds already stored and retries only the one that failed.
  • Memories that need review become proposals; tasks are never reviewed.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged as batch with kind: memory.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock, relate, unrelate; later reject
proposal approve, reject (both with a reason), withdraw
memory, page archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do. A task is closed with complete or by setting status canceled, never archived.

Version: task and item actions need the version from item_get; relate, unrelate and the proposal actions do not. Proposals are immutable, so their version only guarded against deciding twice, which the pending state already prevents. In the #311 sessions agents left out the version on approve in three of eight runs.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review", #323, open) and propose: true per call (implemented).
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, each kind atomic on its own with its own retry (changed on 2026-09-18 in #315 from "the whole batch atomic": there is no shared transaction across the services).
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; before v2 tasks and page moves checked it, memories and page text checked a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Until #320 a version of a memory, page or the instructions is the ordinal of its published revision along the base chain, counting privacy-locked revisions, so deleting one version keeps the numbers of the others.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.
  8. From #311 (2026-09-18): the task list has no status filter, only includeClosed; proposal actions need no version.
  9. No email addresses (2026-09-18): responses name people by a display name each person sets in the settings, prefilled with the part before the @.

Measurements (#311, 2026-09-18)

Method: the schemas are generated offline by the MCP SDK exactly as the server advertises them; the offline list of the old tools matched the live list to the character (19,014). Stubs and the measuring test live in tests/Conderis.Tests/ToolSurface, the session harness in tests/ToolSurfaceSession (da0842d, ae81f71). Tokens are counted with o200k as an approximation.

List size (final v2 descriptions after the session findings)

Series Tools Characters Tokens
Before v2 27 19,014 4,043
Consistency fixes only, descriptions cut to v2 length (plus task_block) 28 12,614 2,872
v2 (measured) 8 6,386 1,431
v2 (live after #317) 8 6,462 about 1,450

The live list is 76 characters above the measurement, because item_store and item_action descriptions were corrected during implementation (batch atomicity per kind, actions per kind). v2 is 66 % smaller than the old surface and half the size of the consistent 28 tools. Largest live v2 tools: item_store 1,362, item_list 983, item_action 977, item_get 909.

Growth by one kind (commits as the example): v2 +97 characters (+23 tokens), for the added lines in item_store, item_list and item_get. Consistent series +1,038 characters (+244 tokens) for three tools (commit_list, commit_link, commit_unlink).

Round trip for on-demand clients (create, list, complete a task; information only): before task_create + task_list + task_complete 505 tokens, v2 item_store + item_list + item_action 745 tokens. An agent that loads tools one by one pays about 240 tokens more per round trip in v2; clients that load the whole list save about 2,600 tokens per session.

Findability (information only): a keyword score over name, first sentence and the rest of the description found the intended v2 tool first for all six queries ("create task", "store memory", "read page", "approve proposal", "block task", "recall project"). Before, "read page" ranked item_get second and "block task" had no tool.

Retries in a real session. A stub server serves the measured list of one surface over stdio, answers from a small fixed project, checks calls the way the real server does and logs every call. Headless Claude Code sessions (claude -p, tools loaded on demand) did the same six things against each surface: create a subtask, change a memory's summary, block a task with a reason, list the open tasks, read a section of a page, approve a proposal.

Surface Runs Calls per run Refused calls What was refused
Before v2 3 10 0, 1, 1 memory_store with number but without projectId
v2, first design 5 9–11 0, 0, 1, 0, 1 approve without version
v2, first design 5 — — every run listed "open tasks" with status: open and missed the blocked task #14
v2, final design 3 8–9 0, 1, 0 status filter, answered with the allowed filters and corrected in the next call

Every refusal was corrected in the next call. The first design had a correctness problem that no refusal showed: status: open returned a plausible but incomplete list. Two design changes followed (Decision 8), and with them all runs listed the right tasks. The old surface recorded a block reason only by writing it into the task description; v2 recorded it as a reason in every run.

Gate: met. The list is 6,386 characters against 19,014, and the final v2 needed one refused call in three runs against two in three for the old surface.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility), #323 (review setting per key).

Sep 18, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface. The measurements of #311 passed both gates; see Measurements.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could be fixed on the 27 tools without consolidation; #311 measured that option as a control.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carries its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both were recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields or values.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes and task and item actions check it; moves, relations and proposal actions do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line, filters line, actions row and validator. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

The descriptions measured in #311 are in tests/Conderis.Tests/ToolSurface/V2Prototype.cs and are the starting point for #313 to #316.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • Tasks are the active ones (open, in progress, blocked) unless includeClosed. There is no status filter: in the #311 sessions agents asked for "open tasks" translated that into status: open every time and lost blocked and in-progress work; a description that explained the default did not change that. task_list today has no status filter either.
  • filter is a JSON object per kind: task priority, assignee, parent, includeClosed; memory type, tags, visibility, includeArchived, includeExpired; page parent, includeArchived; proposal state, author, targetKind. An unknown filter is refused with the allowed ones.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

Version: task and item actions need the version from item_get; relate, unrelate and the proposal actions do not. Proposals are immutable, so their version only guarded against deciding twice, which the pending state already prevents. In the #311 sessions agents left out the version on approve in three of eight runs.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.
  8. From #311 (2026-09-18): the task list has no status filter, only includeClosed; proposal actions need no version.

Measurements (#311, 2026-09-18)

Method: the schemas are generated offline by the MCP SDK exactly as the server advertises them; the offline list of today's tools matches the live list to the character (19,014). Stubs and the measuring test live in tests/Conderis.Tests/ToolSurface, the session harness in tests/ToolSurfaceSession (da0842d, ae81f71). Tokens are counted with o200k as an approximation.

List size (final v2 descriptions after the session findings)

Series Tools Characters Tokens
Today 27 19,014 4,043
Consistency fixes only, descriptions cut to v2 length (plus task_block) 28 12,614 2,872
v2 8 6,386 1,431

v2 is 66 % smaller than today and half the size of the consistent 28 tools. Largest v2 tools: item_store 1,338 (against 6,615 for the four write tools today), item_list 983, item_action 925, item_get 909. The estimate for item_store held.

Growth by one kind (commits as the example): v2 +97 characters (+23 tokens), for the added lines in item_store, item_list and item_get. Consistent series +1,038 characters (+244 tokens) for three tools (commit_list, commit_link, commit_unlink).

Round trip for on-demand clients (create, list, complete a task; information only): today task_create + task_list + task_complete 505 tokens, v2 item_store + item_list + item_action 745 tokens. An agent that loads tools one by one pays about 240 tokens more per round trip in v2; clients that load the whole list save about 2,600 tokens per session.

Findability (information only): a keyword score over name, first sentence and the rest of the description found the intended v2 tool first for all six queries ("create task", "store memory", "read page", "approve proposal", "block task", "recall project"). Today "read page" ranks item_get second and "block task" has no tool.

Retries in a real session. A stub server serves the measured list of one surface over stdio, answers from a small fixed project, checks calls the way the real server does and logs every call. Headless Claude Code sessions (claude -p, tools loaded on demand) did the same six things against each surface: create a subtask, change a memory's summary, block a task with a reason, list the open tasks, read a section of a page, approve a proposal.

Surface Runs Calls per run Refused calls What was refused
Today 3 10 0, 1, 1 memory_store with number but without projectId
v2, first design 5 9–11 0, 0, 1, 0, 1 approve without version
v2, first design 5 — — every run listed "open tasks" with status: open and missed the blocked task #14
v2, final design 3 8–9 0, 1, 0 status filter, answered with the allowed filters and corrected in the next call

Every refusal was corrected in the next call. The first design had a correctness problem that no refusal showed: status: open returned a plausible but incomplete list. Two design changes followed (Decision 8), and with them all runs listed the right tasks. Today's surface records a block reason only by writing it into the task description; v2 recorded it as a reason in every run.

Gate: met. The list is 6,386 characters against 19,014, and the final v2 needed one refused call in three runs against two in three for today.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility).

Sep 18, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could be fixed on the 27 tools without consolidation; #311 measures that option as a control, mainly to show what it does not solve.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carries its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both are recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields; #311 counts the retries.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line, filters line, actions row and validator. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}. The allowed filters per kind are listed in #313 and in the description.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

#311 writes the eight schemas first and runs the budget test in two series: v2, and the 27 tools with only the consistency fixes, plus the cost of one more kind in each. It also records per-tool sizes for on-demand loading, whether the six standard queries find the right tool by keyword, and how many retries a real session needs on the stubs against today. The estimate for item_store is 1,200–1,500 characters against 6,615 today for the four write tools. Gate: the whole list lands well below the current 19,000 and retries do not rise once the error messages name the allowed fields. Round-trip size and findability are recorded, not gating.

Measurements (#311, 2026-09-18)

Method: the schemas are generated offline by the MCP SDK exactly as the server advertises them; the offline list of today's tools matches the live list to the character (19,014). Stubs and the measuring test live in tests/Conderis.Tests/ToolSurface (da0842d). Tokens are counted with o200k as an approximation.

List size

Series Tools Characters Tokens
Today 27 19,014 4,043
Consistency fixes only, descriptions cut to v2 length (plus task_block) 28 12,614 2,872
v2 8 6,295 1,410

v2 is 67 % smaller than today and half the size of the consistent 28 tools. Largest v2 tools: item_store 1,338 (against 6,615 for the four write tools today), item_list 916, item_get 909, item_action 901. The estimate for item_store held.

Growth by one kind (commits as the example): v2 +97 characters (+23 tokens), for the added lines in item_store, item_list and item_get. Consistent series +1,038 characters (+244 tokens) for three tools (commit_list, commit_link, commit_unlink).

Round trip for on-demand clients (create, list, complete a task; information only): today task_create + task_list + task_complete 2,445 characters (505 tokens), v2 item_store + item_list + item_action 3,145 characters (724 tokens). An agent that loads tools one by one pays about 220 tokens more per round trip in v2; clients that load the whole list save about 2,600 tokens per session.

Findability (information only): a keyword score over name, first sentence and the rest of the description found the intended v2 tool first for all six queries ("create task", "store memory", "read page", "approve proposal", "block task", "recall project"). Today "read page" ranks item_get second and "block task" has no tool.

Retries in a real session: open. It needs stubs that validate fields, filter and action like the server will, and an agent session against them and against today's tools.

Gate so far: the size criterion is met by a wide margin. The retry criterion is not yet measured.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could be fixed on the 27 tools without consolidation; #311 measures that option as a control, mainly to show what it does not solve.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carries its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both are recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields; #311 counts the retries.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line, filters line, actions row and validator. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}. The allowed filters per kind are listed in #313 and in the description.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public: public would contradict the project's own public/private badge in a private project. shared rather than project: project does not say who is included, and readers would ask whether members, the organization or the public are meant. shared states the rule: everyone who has access to the project, which in an open-source project is everyone in the world. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

#311 writes the eight schemas first and runs the budget test in two series: v2, and the 27 tools with only the consistency fixes, plus the cost of one more kind in each. It also records per-tool sizes for on-demand loading, whether the six standard queries find the right tool by keyword, and how many retries a real session needs on the stubs against today. The estimate for item_store is 1,200–1,500 characters against 6,615 today for the four write tools. Gate: the whole list lands well below the current 19,000 and retries do not rise once the error messages name the allowed fields. Round-trip size and findability are recorded, not gating.

Measurements

Filled in by #311.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

The kinds are not fixed. Memories, pages, tasks and proposals exist today; commits (#299) are next, and more will follow. Per-kind tools cost three to four tools per kind, so the list grows with the product. In v2 a new kind is one line in the descriptions of item_store, item_list and item_get, one row in item_action, and a server-side validator. The list stays the same size.

The inconsistencies alone could be fixed on the 27 tools without consolidation; #311 measures that option as a control, mainly to show what it does not solve.

Client context

Most clients (Cursor, Windsurf, Codex, Claude Desktop, Gemini CLI, Copilot) load the whole tool list into every session, so the list size is the primary cost. Claude Code is the exception: it loads MCP tools on demand through a keyword search over names and descriptions. For it, two other things matter:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds.
  • Findability. task_list carries its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Both are recorded by #311 for information; neither decides.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime, so every refusal names the allowed fields; #311 counts the retries.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.
  • A new kind adds no tool. It adds its fields line, filters line, actions row and validator. This is the property that keeps the list flat as the product grows.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal; later every further kind.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}. The allowed filters per kind are listed in #313 and in the description.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions; later every further kind
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public or project: public would contradict the project's own public/private badge in a private project, and project is no counterpart to private. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

#311 writes the eight schemas first and runs the budget test in two series: v2, and the 27 tools with only the consistency fixes, plus the cost of one more kind in each. It also records per-tool sizes for on-demand loading, whether the six standard queries find the right tool by keyword, and how many retries a real session needs on the stubs against today. The estimate for item_store is 1,200–1,500 characters against 6,615 today for the four write tools. Gate: the whole list lands well below the current 19,000 and retries do not rise once the error messages name the allowed fields. Round-trip size and findability are recorded, not gating.

Measurements

Filled in by #311.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below. Two of those decisions change the data model and run after the switch as #320 (no revisions) and #321 (visibility); v2 itself changes only the tool surface.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

The inconsistencies cost agents wrong calls regardless of the tool count. They could be fixed on the 27 tools without consolidation; #311 measures both options against each other.

Client context

How much the list costs depends on the client. Claude Code loads MCP tools on demand: only the tool names reach the context, and a tool's schema is fetched through a keyword search over names and descriptions when the agent needs it. For that client the list size is nearly free, and two other things matter instead:

  • Per-tool size. An agent that only creates a task loads item_store with the field lists of all four kinds. Consolidation can load more context for a single-kind session than today's task_create.
  • Findability. task_list carries its keyword in the name; item_store and item_action do not. Every v2 description therefore names its kinds in the first sentence.

Codex and Claude Desktop are believed to load the whole list; #311 verifies this and records it here. The switch is worth it only if v2 wins for both kinds of client.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion. Inside the write tools the server keeps checking per action; a client allowlist cannot separate task work from moderation in item_action (#314).
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each. Validation moves from the schema to runtime; #311 counts the retries this causes.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. Until #320 the server maps the version onto the published revision internally; no revision id appears in any v2 request or response.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}. The allowed filters per kind are listed in #313 and in the description.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared. Implemented after the switch as #321; until then item_store accepts today's values.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public or project: public would contradict the project's own public/private badge in a private project, and project is no counterpart to private. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories are listed for their owner with a move action into a project or the organization's instructions, nothing moves automatically. Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions. The tool contract (one version) is part of v2; the storage change is #320 after the switch.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226 for pages and instructions; memory creates still run on a draft revision (DraftRevisionId), which #320 moves to proposals first.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is computed at write time.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

#311 writes the eight schemas first and runs the budget test in two series: v2, and the 27 tools with only the consistency fixes. It also records per-tool sizes for on-demand loading, whether the six standard queries find the right tool by keyword, and how many retries a real session needs on the stubs against today. The estimate for item_store is 1,200–1,500 characters against 6,615 today for the four write tools; the switch is only worth it if the whole list lands well below the current 19,000, a single-kind round trip loads no more than today, and retries do not rise.

Measurements

Filled in by #311.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17), #320 (no revisions), #321 (visibility).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion.
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. There are no revisions: an item carries its content, and one version history keeps every earlier state (see Decisions).
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the version history as metadata (version, author, time, changed fields); version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public or project: public would contradict the project's own public/private badge in a private project, and project is no counterpart to private. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories need a rule before the switch (move into a project of the organization, or into its instructions, reviewed by the owner). Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item, and no revisions.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check a revision id instead. From now on every content change checks the version.
    • Revisions existed because a proposal used to be an unpublished revision next to the published one, so content lived in revision rows and the item only pointed at one. Proposals have been a separate system since #226, so that reason is gone.
    • The item carries its current content, as tasks already do.
    • One version history for every kind, storing full states: each content change appends item, version, the complete content, author and time; entries are never changed. Tasks switch from their field-change log to full states as well; the list of changed fields is derived by comparing neighbouring versions.
    • The history serves reading an earlier state (item_get with version), comparing a proposal with its baseVersion (the field already exists on proposals), and deleting a single version for privacy. Full states make all three trivial; a change log would have to be replayed and cannot lose a middle entry.
    • Migration: memory and page content moves from the published revision onto the item; existing revisions become history entries numbered in order of creation; proposals derive baseVersion from baseRevisionId; the task change log is converted by replaying it into full states. Where pending memory revisions are still created, those paths move to proposals.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

Write the eight schemas first and run the budget test (#311). The estimate for item_store is 1,200–1,500 characters against 6,615 today; the switch is only worth it if the whole list lands well below the current 19,000.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion.
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not. Stored states are addressed by item and version, never by a revision id (see Decisions).
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the history as metadata per version; version reads the item as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, visibility, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or of one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Visibility of a memory: private or shared.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • shared: everyone with access to the project, directly or through its organization, and everyone when the project is public. There is no separate public per memory: hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway. The web app states next to shared that it is visible to everyone in a public project.
    • shared rather than public or project: public would contradict the project's own public/private badge in a private project, and project is no counterpart to private. shared is already the term in the web app.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: public project memories become shared; organization memories need a rule before the switch (move into a project of the organization, or into its instructions, reviewed by the owner). Upstream recall of a fork takes the shared memories of a public upstream.
    • Visibility is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item replaces baseRevisionId; revisions have no id of their own.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check the revision instead. From now on every content change checks the version.
    • The stored text of each content version stays: history, the base a proposal is compared against, and deleting a single version for privacy all need it. It is identified by item and version ("#290 version 5"), not by a GUID. The word revision disappears from the tools.
    • Proposals record baseVersion (the field already exists) instead of baseRevisionId.
    • Migration: existing revisions receive their version number in order of creation; proposals derive baseVersion from baseRevisionId.
    • Pending memory revisions predate the general proposal system (#226). Where they are still created, those paths move to proposals, so stored versions are published states only.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

Write the eight schemas first and run the budget test (#311). The estimate for item_store is 1,200–1,500 characters against 6,615 today; the switch is only worth it if the whole list lands well below the current 19,000.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion.
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item; content changes check it, moves do not (see Decisions).
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. A memory returns its current revision (#306). Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the history as metadata per version; version reads the text as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get; required when content changes, not for a move
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, audience, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Audience of a memory: private or project.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • project: everyone with access to the project, directly or through its organization. Public when the project is public; there is no separate public per memory. Hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: shared and public project memories become project; organization memories need a rule before the switch (move into a project of the organization, or into its instructions, reviewed by the owner). Upstream recall of a fork takes the project memories of a public upstream.
    • Audience is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item replaces baseRevisionId.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check the revision instead. From now on every content change checks the version.
    • A matching version proves the published revision is the one the caller read, so the server records that revision as a proposal's base itself.
    • Revisions stay internal for history and review and store the version they created, so history reads "version 3, 5, 7" instead of ids.
  7. Moves are not version-checked. A change of only parent and/or position needs no version and does not increase it: whoever moves an item wants it at the new place, whatever happened before, so the last move wins. The server still checks at execution time that the target exists and is active, that no cycle forms and that the caller may move. A text change is therefore never refused because someone moved the item meanwhile. A position refers to the siblings the caller saw and may land one place off after a concurrent reorder, which is harmless. A store that changes content and structure together needs version.

Measure before building

Write the eight schemas first and run the budget test (#311). The estimate for item_store is 1,200–1,500 characters against 6,615 today; the switch is only worth it if the whole list lands well below the current 19,000.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch. The open questions were decided on 2026-09-17 (#312); see Decisions below.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. The same things are named and behave differently from tool to tool:

  • Addressing an item: number, taskNumber, sourceNumber/targetNumber, id next to number, proposalId, requestId, ref.
  • Concurrency: version for tasks, proposals and page moves, baseRevisionId for memories and page text; page_store and item_delete take both.
  • Changing: a JSON patch object for tasks, omitted-keeps plus flags such as clearExpiry for memories, and project_update replaced all three values (fixed in f996a12: omitted values are now kept).
  • Decisions: approve: bool with or without a reason, restore: bool turning archive into its opposite.
  • Reasons: explanation when storing, reason when reviewing.
  • Filters and paging: includeClosed in task_list against includeArchived in item_search, which also means closed tasks there; offset/limit in one tool, limit only in two, nothing in the rest.
  • kind names the relation type in the relation tools and the item kind everywhere else.

Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion.
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One version for every item and every change (see Decisions).
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall. Upstream knowledge of a fork is always included; includeUpstream is dropped.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, version, offset, take.

  • ref: instructions, memory:<id> for personal memories, proposal:<id>.
  • Returns the item, its direct relations as short entries and its version. A memory returns its current revision (#306). Deeper relations are followed with further calls; depth is dropped.
  • history: true returns the history as metadata per version; version reads the text as it was at that version.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal memories
number Change this item; without it, create
version The version read with item_get, required when changing
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose Ask for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, audience, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

The response names the outcome (published, proposed, denied with the missing capability), the number and the new version.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Up to 100 items and 1 MB per batch; kinds may be mixed, and pages and tasks may reference each other by key. Larger imports send several batches.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • If any item needs review, the whole batch becomes one proposal, so nothing arrives half.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.

item_action

projectId, number or ref, action, version, reason, target, relation.

Kind Actions
task claim, complete, block (reason required), unblock; later reject
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused, naming the actions that do.

item_delete

Requests permanent deletion of an item or one version and locks its content immediately. A person decides in the web app.

Decisions (2026-09-17, #312)

  1. Audience of a memory: private or project.
    • private: only the author, nobody else in the project or anywhere. Without projectId this is a personal memory across projects.
    • project: everyone with access to the project, directly or through its organization. Public when the project is public; there is no separate public per memory. Hiding a memory inside a public project would only be obscurity, and internal details do not belong in a public project anyway.
    • Organization memories are dropped. An organization is an access group that gives its members its projects. Organization-wide guidance stays in the organization's agent instructions.
    • Migration: shared and public project memories become project; organization memories need a rule before the switch (move into a project of the organization, or into its instructions, reviewed by the owner). Upstream recall of a fork takes the project memories of a public upstream.
    • Audience is fixed after creation; a change is refused.
  2. Blocking a task is an action with a required reason (block, unblock), so the reason is in the history and other agents see what the task waits for.
  3. Forcing a proposal works both ways: a setting on the agent key in the web app ("changes by this agent always need review") and propose: true per call.
  4. includeUpstream and relation depth are dropped.
  5. Batches: up to 100 items and 1 MB, mixed kinds, references by key across kinds, one proposal for the whole batch when any item needs review.
  6. One version per item replaces baseRevisionId.
    • Every item already has a version that grows with each change; today tasks and page moves check it, memories and page text check the revision instead. From now on every change checks the version.
    • A matching version proves the published revision is the one the caller read, so the server records that revision as a proposal's base itself.
    • Revisions stay internal for history and review and store the version they created, so history reads "version 3, 5, 7" instead of ids.
    • Pages lose their separate structure check: moving a page while someone edits its text makes the editor read again. Accepted for one mechanism instead of two.

Measure before building

Write the eight schemas first and run the budget test (#311). The estimate for item_store is 1,200–1,500 characters against 6,615 today; the switch is only worth it if the whole list lands well below the current 19,000.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks), #319 (decisions of 2026-09-17).

Sep 17, 2026Claude Code

MCP tool surface v2 (planned)

Status: planned. Nothing on this page is implemented. It replaces the 27 tools of the current surface (#279) with eight, as one incompatible switch.

Why

The advertised tool list costs every connected agent context in every session: 27 tools, about 19,000 characters, a few characters under the budget in McpDiscoveryTests. Most of it repeats the same ideas per kind: four tools write items (memory_store, page_store, task_create, task_patch) with 38 parameters between them, three read lists, three decide proposals. Growth planned in #302 (paging, partial reads) does not fit into the budget as it stands.

Rules

  • Read, write and irreversible stay separate tools. Clients grant permissions per tool name, and readOnlyHint/destructiveHint apply to a whole tool. A user must be able to allow reading without allowing writing, and writing without allowing deletion.
  • Per-kind data travels as one JSON object. The schema describes the envelope; the server deserializes, validates against the kind and refuses unknown fields, as task_patch already does. The tool description lists the fields per kind in one line each.
  • One patch rule for every kind: an omitted field is kept, null clears it. clearExpiry and similar flags disappear.
  • One concurrency token. item_get returns base; a change passes it back unchanged. Whether it stands for a task version or a revision id is the server's business.
  • Lists return short entries (#303, #306) and page with one contract (#304). Single items read in parts (#305).
  • No aliases for old names. They would fill the budget again. The switch happens with one system guide version, the server instructions, the web setup prompts and documentation together.

The eight tools

Tool Kind Replaces
recall read memory_recall; system_memory as guide: true
project_get read project_list (without projectId), task_assignee_list
project_store write project_create, project_update
item_get read item_get, memory_get, a single proposal, relation_list, item_history
item_list read task_list, item_search, the proposal queue
item_store write memory_store, page_store, task_create, task_patch
item_action write task_claim, task_complete, proposal_review, proposal_withdraw, item_archive, relation_create, relation_remove
item_delete irreversible item_delete (request only)

privacy_deletion_queue and privacy_deletion_review leave MCP. Deciding a permanent deletion is a person's job in the web app. Requesting one stays available to agents, because an agent that stored a secret by mistake must be able to lock it at once.

recall

projectId, focus, maxChars, guide. Unchanged behaviour (#215); guide: true returns the system guide instead of the recall.

project_get

Without projectId: the short list of accessible projects. With it:

  • Project: name, description, remote and whether it is verified, public or private, upstream.
  • Your permissions here: may write, may moderate. An agent knows before item_store whether a change publishes or becomes a proposal.
  • Assignees: people and agents with their immutable references, never email addresses.
  • Counts: open tasks, memories, pages, pending proposals. Later the hygiene score (#292) and recall mode (#296).

item_get

projectId, number or ref, history, revisionId, offset, take.

  • ref: instructions, memory:<id> for personal and organization memories, proposal:<id>.
  • Returns the item, its relations as short entries and base. A memory returns its current revision (#306).
  • history: true returns revisions or field changes instead, as metadata; revisionId reads one revision's text.
  • offset/take read long text in parts (#305).

item_list

kind, projectId, query, filter, sort, limit, cursor.

  • kind: task, memory, page, proposal.
  • query searches text, which replaces item_search.
  • filter is a JSON object per kind, for example {"status": "open"}, {"type": "decision"}, {"state": "pending", "author": "…"}.
  • Response per #304: short entries, total, hasMore, nextCursor.

item_store

Parameter Meaning
kind memory, page, task, instructions
projectId Missing for personal and organization memories
number Change this item; without it, create
base Token from item_get, required when changing
fields JSON object for the kind
reason Shown to the moderator when the change becomes a proposal
propose A moderator asks for review instead of publishing
batch Many items at once, instead of fields

Fields per kind:

  • memory: title, summary, content, tags, type, audience, expiresAt, later parent (#295) and weight (#294)
  • page: title, content, parent, position
  • task: title, content, status, priority, assignee, parent
  • instructions: content

Audience replaces scope, visibility and organizationId, which only form four meaningful combinations:

  • me: without projectId a personal memory across projects; with it a private note in that project
  • project: every project member; public when the project is public
  • organization:<id>

Audience is fixed after creation; a change is refused.

The response names the outcome (published, proposed, denied with the missing capability), the number and the new base.

Batches

One JSON object, used for moving memories between agents and for imports from other tools (task boards, wikis):

{
  "idempotencyKey": "import-2026-09-17",
  "source": "trello",
  "items": [
    { "key": "epic", "kind": "task", "fields": { "title": "Checkout" } },
    { "kind": "task", "fields": { "title": "Card form", "parent": "epic" } }
  ]
}
  • key names an item inside the batch; parent may point at a key or at an existing number. The response maps keys to the assigned numbers.
  • Atomic: all items are stored or none. A replay with the same idempotencyKey returns the first result.
  • Items that need review become one batch proposal, as memory batches do today.
  • The existing Conderis Memory Batch v1 file stays accepted unchanged.
  • A size limit per batch; larger imports send several batches.

item_action

projectId, number or ref, action, base, reason, target, relation.

Kind Actions
task claim, complete; later reject, block
proposal approve, reject, withdraw
any item archive, restore, relate, unrelate (with target and relation)

An action that does not apply to the item's kind is refused.

item_delete

Requests permanent deletion of an item or one revision and locks its content immediately. A person decides in the web app.

Open decisions

  1. Audience as described, including whether public per memory disappears in favour of the project's own visibility. Existing public memories in non-public projects need a migration rule.
  2. Blocking a task: a status set with item_store, or an action with a reason.
  3. propose per call, or as a setting on the agent key in the web app ("changes by this agent always need review"), or both.
  4. Batch limits: item count, total size, and whether pages and tasks in one batch may reference each other.
  5. Measure before building: write the eight schemas first and run the budget test. The estimate for item_store is 1,200–1,500 characters against 6,615 today; the switch is only worth it if the whole list lands well below the current 19,000.

Related: #302 (bounded responses), #219 and #304 (paging), #305 (partial reads), #291 (memory structure), #299 (commits on tasks).

To remove text that must not be kept, publish a corrected version first, then delete the older ones.

Connection interrupted or session ended. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.