Local Agent Authoring
Local Agent Authoring lets Claude Code or Codex work on one encrypted zudo-text cloud workspace through the official stdio MCP server. A real coding agent runs on the user's machine and calls narrow workspace tools. The macOS desktop app also has a separate Terminal frame for local shell commands; it does not create a local workspace directory or grant the shell access to cloud documents.
The published MCP transport is the local stdio process from the versioned public @takazudo/zudotext-mcp npm package. This source adds an optional, unreleased restricted remote Kanban endpoint; there is no generic remote endpoint and no per-published-site MCP endpoint.
This page is the design and safety-policy authority. The MCP integration guide owns user workflow, while the package page owns distribution, configuration, exact inventory, and release details.
Relationship to the Terminal frame
The desktop Terminal frame starts a local login shell in the user's home folder or a selected local folder. It does not start an agent, configure MCP, or inject MCP credentials into a CLI process. Cloud workspace documents remain cloud documents; MCP is the explicit route by which a configured coding agent can read or write them.
The current opt-in Active Note Pointer is a separate read-only sidecar for local tools. It is distinct from the pre-pivot activeFile environment injection removed by the cloud-primary pivot.
System shape
Claude Code or Codex
│ local stdio MCP
▼
zudotext-mcp (one process, one workspace)
├── document tools ── HTTPS ── Automation API ── files / versions / changes
├── asset tools ───── HTTPS ── opaque /api/assets ── D1 + R2
└── ask_zudo_agent ── HTTPS ── agent-server ── Automation API toolsThe MCP process holds the configured workspace id, workspace-bound PAT, and workspace password. It translates plaintext, workspace-relative paths into the two encrypted wire contracts; the coding-agent harness never needs to implement zudo-text crypto itself.
One process, one authority boundary
A normal authoring process is intentionally narrower than an interactive app session:
| Grant | Shipped authority |
|---|---|
documents:read | List, read, and search documents; poll document changes; read versions and checkpoint projections. |
documents:write | Create, replace, soft-delete, move, restore document versions, and create authoring checkpoints. |
assets:read | List, inspect quota, and download assets; also fetch the encryption salt needed for local asset-key derivation. |
assets:write | Upload a new asset or create an empty asset folder. |
agent:invoke | Optional access to ask_zudo_agent. |
full | Explicit compatibility grant for the wider interactive/server surface; never part of the local-agent default. |
The default guided flow mints exactly the first four scopes and binds the PAT to the current workspace. Add agent:invoke only when cloud-agent delegation is required. One MCP process exposes one ZUDOTEXT_WORKSPACE_ID; workspace multiplexing is deliberately absent.
The server still treats a PAT request that omits scopes as full for pre-scoped compatibility. The app avoids that compatibility default: the generic token form preselects the four narrow authoring scopes and presents Full access as a mutually exclusive opt-in.
Documents and assets use different key paths
Documents: bounded server key session
The MCP process derives three raw 32-byte document subkeys locally with PBKDF2-SHA-256 at 600,000 iterations. It sends the transient bytes over TLS to the Automation API once to open a key session. The sync Worker keeps them only in Durable Object memory for at most 24 hours, never in D1, R2, logs, Durable Object storage, or an agent conversation. A restart or Durable Object eviction can end a session sooner.
The returned session id is bearer-equivalent access to decrypted documents and stays in process memory. Expiry or eviction triggers one re-derivation/reopen retry. MCP close, transport close, SIGINT, and SIGTERM start the same idempotent best-effort session deletion; abnormal death falls back to expiry.
Assets: local non-extractable keys
Asset tools do not open or use the document key session. The local process derives non-extractable Web Crypto keys and sends only deterministic encrypted path tokens and encrypted byte envelopes to the existing / family. Plaintext asset paths and bytes exist only in the MCP process and the explicit local source or destination files.
An assets:read PAT may fetch only encryptionSalt from workspace metadata without the document subscription gate. That exception still checks PAT workspace binding and ownership; it authorizes no keyed document route. assets:write alone does not grant the metadata read, which is why normal asset configuration includes both asset scopes.
Direct tools and cloud-agent turns
The default authoring MCP registry has exactly 21 tools. Direct document, history, change, and asset tools are deterministic operations and spend zero AI turns. ask_zudo_agent is the one delegation tool: use it for semantic, open-ended, or genuinely multi-step work, not to guess around a missing path or an omitted asset operation.
Every admitted ask_zudo_agent call spends one of the user's shared 100 AI turns per UTC day, before the model runs; a later failure still consumes that admission. Assistant-panel and inline-AI calls use the same pool. All Automation API routes, including the cloud agent's internal tool calls, also share one fixed 120 requests per user per minute budget. One regex search scans at most 2,000 candidate paths and reads content from at most 240 documents, so a truncated answer is incomplete rather than proof that no later match exists. The cloud agent's internal query_kanban can spend up to 85 of those shared requests in one bounded discovery/recovery walk.
The cloud agent can commit create_note directly because it is create-only and fails on a collision. Existing-note changes use propose_edit instead: ask_zudo_agent returns validated previews with newContent, baseVersion, diff, summary, and expiry, but applies none of them. A local caller reviews the candidate and, while it is valid, writes it with expectedVersion: baseVersion. Conversation history may retain plaintext excerpts from tool results; the document store's E2EE guarantee does not extend to that conversation record.
Document history, move, and undo
Document writes are whole-body writes with optimistic concurrency. Omitting expectedVersion means create-only; replacing, deleting, moving, and restoring requires a current version. Move preserves the stable file id and history, then appends an ordered old-path delete and new-path upsert; it does not add a move action or a content version.
what_changed is an ordered document-only polling cursor. It includes document upserts, tombstones, and both rows of a document move. It excludes assets and is not a WebSocket feed.
begin_authoring_session creates the checkpoint that marks an agent's document undo boundary. A checkpoint is a named change-log cursor, not a workspace snapshot. Per-file version restore is server-backed. Checkpoint rollback is a client-orchestrated manifest of restore, delete, and noop work:
fetch the restore manifest and its
expectedHeadCursor;page explicit
what_changed(afterCursor: 0)calls untilhasMoreis false, passing each page'schangeCursorinto the next call, then compare the final cursor;apply each normal optimistic mutation;
stop on the first conflict and report possible partial progress.
There is no one-call or atomic workspace rollback. HISTORY_COMPACTED is a typed failure, not an empty history. History, checkpoints, restore manifests, and change cursors cover documents only.
The sync server's nightly retention pass keeps every version from the most recent 24 hours, then thins unpinned history to hourly, daily, and weekly representatives. Current versions and versions pinned by a checkpoint are always retained. Pruned version rows hand off unreferenced encrypted R2 blobs to a queue with a further 24-hour grace period before deletion; this affects which old versions a local agent can read, but never deletes changes rows or changes HISTORY_COMPACTED semantics.
Narrow asset policy
The five asset tools are list, usage, local-path download, local-path upload, and empty-folder creation. Upload accepts only an explicit absolute path to a regular non-symlink file and enforces a 25 MiB plaintext limit. Download uses an explicit absolute destination, refuses to overwrite it, and removes only the partial file that call exclusively created after a failure. Asset bytes never enter MCP text or structured results.
Assets have no immutable revision, versions, history, checkpoint coverage, document change cursor, or undo. Consequently the narrow family has no move, rename, delete, recursive delete, replace, or delete-and-recreate operation. Those remain interactive/full; callers must not emulate them. Neither a document move nor an interactive asset rename rewrites Markdown references.
Committed asset mutations wake open filers with the content-free { type: "assets-changed" } notification. It only means “refetch listing and usage”; it is not ordered, replayable, or connected to the document cursor.
Resource policy
Direct whole-document writes are powerful enough to damage structured product files even when the version precondition succeeds:
Ordinary notes and
archives/documents are writable;archives/is an ordinary workspace-relative prefix and needs no special tool or scope.Preserve an existing YAML frontmatter block byte-for-byte unless the user explicitly requests a field change. A malformed raw rewrite can desynchronize indexed metadata.
zudo-text directive round-tripping through an agent write is unverified. Preserve directive text unless the user explicitly accepts the risk.
Use restricted Kanban tools for approved card operations, with the matching captured-base UI writer. Keep generic whole-board writes read-only. Todo, timeline, mindmap, spreadsheet, and slide use separate persistence paths whose agent-write safety remains unverified.
Treat
.zudotext.settings.json, frameset state, and pins as read-only. They are whole-document last-writer-wins state; concurrent app writes can clobber automation, while malformed settings can fall back to defaults.
The packaged Claude Code and Codex skills encode these defaults, including an owner-editable confirmation policy for destructive whole-note operations.
Local active-note pointer
The desktop app also has an optional local active-note pointer. It is off by default and is never enabled by PAT creation. When enabled, the app writes ~/ with the active workspace-relative note path, pane/view, and selected line/column range; note content is never included. It refreshes every 10 seconds and on note/pane/view changes. Readers treat data older than 30 seconds as stale. Disable, workspace switch, no active workspace note, and clean shutdown clear it; a crash may leave a stale file. This is read-only orientation metadata, not an MCP write-back channel.
Deliberately absent in v1
generic remote or per-published-site MCP endpoints;
patch/range edits, merge engines, and multi-file transactions;
an agent WebSocket feed (poll
what_changedfor documents);asset history, checkpoints, cursor entries, reference rewriting, or narrow destructive asset tools;
token self-service from a PAT, workspace multiplexing in one MCP process, or a redesign of cloud-agent conversation deletion/privacy;
compaction while checkpoints exist; nightly version/blob retention does not compact or delete the document change log, so its growth remains explicit.
Verification map
The MCP surface has several evidence layers. A green deterministic lane proves only the contract named in its row; it does not silently promote a fake, packaged, or in-process result into deployed authenticated evidence.
| Contract | Status | Deterministic evidence | What it proves | What it does not prove | Environment validity |
|---|---|---|---|---|---|
| Inventory and schema contract | Passing | server-inventory and tool-quality in packages/ (PR gate) | Every registered tool, description, input/output schema, and annotation matches the checked-in inventory and quality rules. | Family behavior, route parity, package installation, or a deployed account. | In-process MCP server with fake transport; deterministic PR evidence. |
| Per-family tool behavior | Passing | packages/ (fake transports, permutations, and failures) | Each direct tool family handles its expected success, conflict, malformed, and failure paths. | A real Hono backend, deployed service, real account, or release tarball. | Package Vitest fakes; deterministic PR evidence. |
| Backend route parity | Passing | agent-route-inventory plus feature-manifest (PR gate) | Agent-facing Automation and Assets routes are mapped to an MCP tool or an explicit v1 exemption. | That the mapped route works through a deployed service or that a fake covers every wire detail. | Checked-in snapshots and manifest diffed in tests; deterministic PR evidence. |
| Connected authoring journey | Passing | workers/ | The MCP source connects to the real Hono application and exercises encrypted documents and assets in Miniflare. | The deployed service, a published tarball, a real account, credentials, or production quotas. | In-process real Hono app with Miniflare D1/R2 and Durable Objects; deterministic PR evidence. |
| Packaged binary | Passing | release:verify in the PR gate and release workflow | The built tarball installs, starts, reports its version, exposes the exact inventory, and carries the expected skills and dependencies. | Deployed authentication, live workspace behavior, or agent capability. | Temporary-prefix install of the locally built artifact; deterministic package/release evidence. |
| Live authenticated journey | Outstanding | mcp-live-verify.yml (the scheduled live:verify lane) | Only a conforming credentialed run can establish deployed end-to-end behavior and its archived report. | Package fakes, Miniflare, a health check, a local smoke, or an unprovisioned/manual run. | Dedicated smoke workspace, narrow PAT, password source, deployed server, and published release; Outstanding until a conforming scheduled run is linked. |
| Agent scenarios | Outstanding | eval:agents | A manual run can measure whether an agent completes representative cards with the shipped skill and permitted tools. | Deterministic tool correctness, every possible prompt, or a PR-gate result. | Manual isolated harness run; archive each report per run on the epic; Outstanding until an owner run is linked. |
| Structured-document jobs | Profile-specific | MCP_FEATURES rows for Kanban, mindmap, todo, and timeline | Authoring covers schedule queries and mindmap/todo/timeline edits as raw Markdown text. The separate restricted profile maps eight format-aware Kanban commands. | Live approved-board authorization or deployed client acceptance. | Deterministic coverage checks for both profiles; restricted Kanban requires its own credential, grant and temporary unlock. |
See also
MCP integration — install, configure, add the official skills, and use all 21 tools.
Agent task recipes — the same surface organized by task, with the safety boundary restated per recipe.
Personal Access Tokens — UI defaults, scopes, workspace binding, and the one-time reveal.
Automation API — document wire routes, key sessions, rate limits, and typed errors.
Assets — the interactive filer.
MCP (manual) — the local agent's separate safety boundary, from the end-user's point of view.
Flue Agent Platform — cloud-agent ingress, edit previews, quota, and conversation retention.
Restricted Kanban connections (unreleased)
This capability requires a build from this source change and a configured matching Worker. It is not included in the currently published npm version shown above. A package release and deployment require separate authorization.
The default authoring profile has 21 tools and uses its workspace-bound authoring PAT, workspace ID, and password. It does not register Kanban tools. The separate ZUDOTEXT_MCP_PROFILE=kanban profile exposes exactly 8 tools listed below. It requires its own server-restricted Kanban credential, an active grant for selected boards, and an explicit temporary unlock in the first-party connection screen. A document/asset/agent PAT cannot call these routes; adding Kanban scopes to that PAT does not provision a grant. Use separate MCP client entries and credentials when both profiles are needed.
list_kanban_boards: approved boards only.get_kanban_board: ordered column IDs withcolumnLabels, observed revision and bounded cards; follownextOffsetuntil null.get_kanban_card: stable card identity or a revision-bound file reference.create_kanban_card: create a card and membership atomically.update_kanban_card: explicit title/body/labels/priority/due only; preserve other fields.move_kanban_card: required observed source column and revision; conditional claim has one winner.archive_kanban_card: archive with membership atomically.restore_kanban_card: restore into an existing column.
get_kanban_board returns ordered stable IDs in columns and an effective columnLabels map from each ID to its display label (configured label, or the ID as fallback). Match the user's intended label to this map, then send the returned ID in create/move/restore column and the observed ID in move expectedColumn. Never send a display label as an ID or assume labels are unique; resolve an ambiguous match before writing.
Every mutation requires the exact expectedRevision from a fresh read and a stable idempotencyKey. Retry a lost response with the identical request; a changed payload under the same key fails. On VERSION_CONFLICT, reread and reconsider intent. Never attach a fresh revision to a stale body. Reads never repair identities; missing identities use {fileId,version} until an explicit mutation initializes the target. Unknown format/source failures make zero writes.
Configure the restricted profile with ZUDOTEXT_SERVER_URL and an explicitly provisioned Kanban-only ZUDOTEXT_PAT. It does not read or prompt for a workspace password, create legacy key sessions, or register generic note, search, history, asset, local-path or AI inference tools. Keep credentials in the client/host secret facility, outside prompts and recipes. For Codex Cloud, a domain-scoped network secret can authorize the HTTPS adapter at POST / with the same JSON arguments; arbitrary hosted custom MCP availability is not assumed.
The first-party connection screen owns login, selected boards, and a separate explicit temporary unlock. Connections start locked. WORKSPACE_LOCKED or KEY_SESSION_EXPIRED means return there to unlock, not retry OAuth. Refresh never creates or extends the memory-only lease (at most 24 hours; eviction may end it sooner). Workspace-wide keys are temporarily held by the server; board restrictions are server authorization, not cryptographic board isolation. A dedicated agent workspace narrows key custody. Direct board editing is independent of built-in Workspace AI consent and never grants agent:invoke.
Remote HTTPS MCP exposes exactly these eight tools at /, with explicit OAuth deployment configuration and the same canonical audience intentionally accepted by the eight REST routes. Actual ChatGPT, Codex CLI, Claude Code and Codex Cloud account acceptance remains a separate live check; SDK/protocol tests do not establish those clients' readiness. Generic TODO, mindmap, timeline, schedule-query and asset operations retain their existing gaps.
Restricted client setup and acceptance
Kanban agent connections covers source-build setup for ChatGPT, Codex CLI/IDE, Claude Code and hosted environments, the separate optional tunnel, and outstanding live-client/native checks. The current npm 0.1.1 package does not contain this feature; the checkout's unchanged version is not release evidence. The portable routine in Agent task recipes (MCP) reads, conditionally claims, reports actual results and completes work without implying a scheduler.
Restricted TODO connections (unreleased)
The separate ZUDOTEXT_MCP_PROFILE=todo TODO profile exposes exactly 7 tools listed below, for existing zudo-text TODO documents (type: todo frontmatter, ## TODO / ## DONE sections, <!-- id: … --> item identities). It needs its own server-restricted TODO credential: a grant for exact selected documents (todo:read / todo:write), an explicit temporary unlock, and a TODO-only ZUDOTEXT_PAT. A Kanban credential cannot call these tools and a TODO credential cannot call Kanban, generic note, search, history, checkpoint, asset or agent routes. Moving, deleting or recreating an approved document ends the grant for it. The unlock carries workspace-wide keys; the per-document limit is server authorization, not separate document encryption.
list_todo_documents(): approved TODO documents only, with title and revision.get_todo_document(documentId, offset?, limit?): items (id, title, body, done, section, index, notify) and the whole-document revision; follownextOffsetuntil null.create_todo_item(documentId, expectedRevision, idempotencyKey, title, body?): append an open item with a new persisted identity.update_todo_item(documentId, expectedRevision, idempotencyKey, itemId, title?, body?): explicit title/body replacement only.set_todo_item_done(documentId, expectedRevision, idempotencyKey, itemId, done): complete or reopen; repeating the current state is a no-op.reorder_todo_item(documentId, expectedRevision, idempotencyKey, itemId, toIndex): move within the item's section.initialize_todo_item_ids(documentId, expectedRevision, idempotencyKey): append identities to items that have none.
Every mutation edits only the targeted item's bytes and keeps frontmatter, notifications, extra sections and every other item byte-for-byte; content the tool cannot edit that way is refused with zero writes (UNSUPPORTED_FORMAT), as are duplicate identities. Reads never normalize or assign identity: an item with id: null is reachable only after initialize_todo_item_ids. Retry a lost response with the identical request; a changed payload under the same key fails. On VERSION_CONFLICT (the whole document changed), reread and reconsider intent. There is no delete or archive tool.
Remote HTTPS MCP serves the same 7 tools at /, and the seven REST routes at / intentionally accept that same canonical audience. A remote client discovers it from / and authorizes on the same issuer with resource set to that URL and todo:read (add todo:write only when edits are needed). The resource alone selects the TODO family: a Kanban token, a TODO token on Kanban, a mixed kanban:*/todo:* request and a resource-less TODO request are all refused. Consent selects exact TODO documents, is read-only unless edit permission is chosen, and connects locked; unlock it in Settings → External Connections. Refresh can only narrow scopes and never unlocks, extends the lease or changes the documents. Actual client account acceptance remains a separate live check.
Captured document identity
Canonical notes have a permanent noteId UUID and display number; their paths can change. After read_note, retain the returned noteIdentity.noteId, noteIdentity.locationRevision, and body version. Supply these as noteId, expectedLocationRevision, and expectedVersion for update, delete, move, or restore, together with a caller-stable idempotencyKey. Do not substitute a newer version or the current occupant of an old path when retrying. Locate the same UUID and review the new body before authorizing a different edit. Reads and history reads accept noteId to reject a reused path.
Creating a Note Tray note requires a newly minted UUID in noteId and a stable idempotencyKey; omit expectedVersion. Keep the UUID, key, destination and exact body across an uncertain response or process restart. The server assigns the permanent display number. An ordinary root/internal document stays unregistered: its read returns a separate fileId, passed as expectedFileId with the captured version for mutations. Never pass an ordinary file ID as a note UUID or supply both identity fields. A workspace that still needs enrollment must be opened in the current app; there is no path-only creation fallback.
stash_log additionally requires the captured createdAt ISO UTC timestamp, noteId, and idempotencyKey. Reuse the entire tuple and original content on retry. It changes a filename suffix only after a definite collision; uncertain responses retain the original attempt. Read/write/stash results expose identity metadata in structuredContent as well as readable text.
External cloud-agent previews retain workspaceId, target, baseVersion, baseContent, and operation/admission metadata. A reviewed generic write_note must use that original target and body version; metadata does not grant external clients access to built-in Assistant admission or Apply routes. Missing or foreign-workspace preview identity is rejected.
Search results retain structuredContent.documents: the path, file ID, body version and optional canonical note identity captured for each matched body. Carry the matched noteId (or ordinary fileId as expectedFileId) into subsequent reads/history. A moved or reused path must never select a different result silently.