Skip to content

Feat(mcp): list_tasks says which status column each task is in, as zn mcp does - #943

Merged
adibhanna merged 1 commit into
v2.70.0from
feat/mcp-task-status
Oct 14, 2026
Merged

adibhanna merged 1 commit into
v2.70.0from
feat/mcp-task-status

Conversation

@adibhanna

Copy link
Copy Markdown
Contributor

For #941, and the desktop half of ZenNotes/tui#10: an assistant reading tasks through the desktop's MCP server now sees which status column each task is in, as it does through zn mcp.

What changes

  • list_tasks returns status on every task note (its frontmatter status:, open when it has none) and on every checkbox line that has one (its first @status:, or the status: of its note's frontmatter), lower-cased. A line without one has no status key.
  • Content and ids do not change: this grammar keeps @status: in content, as it keeps every field.
  • inlineTaskField, the app's own field reader, is exported from packages/shared-domain/src/tasks.ts and imported by vault-ops.ts instead of being copied again.
  • The tool description names the field, and the status argument says it filters by state (open, done, waiting), not by that column, in the same words as zn mcp.
  • The legacy TypeScript CLI shares this scan, so its task list --json gains status too, matching the Go zn.

Verification

  • npx vitest run src/mcp/ (63 tests, 1 new) and shared-domain (1,994) pass; desktop and shared-domain typecheck clean.
  • The new test checks every line's status against shared-domain's parseTasksFromBody, so the MCP server and the app cannot drift apart.
  • Built out/main/mcp.js, ran it with ELECTRON_RUN_AS_NODE=1 on a scratch vault and called list_tasks over stdio: the same ids, content and statuses as zn mcp from Feat(tasks): zn task watch prints each task status change as it happens tui#10, and tools/list shows the new descriptions.

How to test locally

cd apps/desktop && npx electron-vite build
mkdir -p /tmp/mcp-status/vault/inbox /tmp/mcp-status/cfg
printf -- '---\ntags: [task]\nstatus: backlog\n---\nBuild it.\n' > /tmp/mcp-status/vault/inbox/Parser.md
printf -- '- [ ] Write docs @status:Review\n- [ ] Plain task\n' > /tmp/mcp-status/vault/inbox/Sprint.md
printf '{"workspaceMode":"local","vaultRoot":"/tmp/mcp-status/vault"}\n' > /tmp/mcp-status/cfg/zennotes.config.json
ZENNOTES_CONFIG_DIR=/tmp/mcp-status/cfg ELECTRON_RUN_AS_NODE=1 npx electron out/main/mcp.js <<'EOF' 2>/dev/null | tail -1 | jq -r '.result.content[0].text' | jq -c '.[] | {content, status}'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_tasks","arguments":{"status":"all"}}}
EOF

Before this change no task has a status. After it: Parser is backlog, Write docs @status:Review is review, and Plain task has none. zn mcp from ZenNotes/tui#10 gives the same answer for the same vault.

… mcp does

An assistant reading tasks through the MCP server saw a task's due date,
priority and tags, but not the column it sits in on a board grouped by
status: a task note's `status:`, a checkbox line's `@status:`, or the
status a line takes from its note's frontmatter. zn mcp reports it with
ZenNotes/tui#10 (#941), so the two servers would have
answered the same question differently.

list_tasks now carries `status` on every task note (`open` when it has
none) and on every line that has one, read the way the app reads it: the
first `@status:` on a line wins and the value is lower-cased. The token
stays in `content`, as every field does in this grammar, so ids and
content do not change. The app's own field reader, inlineTaskField, is
now exported from shared-domain and imported here rather than copied
again. The legacy TypeScript CLI shares this scan, so its `task list
--json` gains the field too.

The tool's description names the field, and its `status` argument says
it filters by state (open, done, waiting), not by that column, in the
same words as zn mcp.

A new test reads a task note with and without a status, a line that
inherits its note's status, and one with two `@status:` tokens, and
checks every line against shared-domain's parser. With the built server
over stdio, list_tasks on a scratch vault returned the same ids, content
and statuses as zn mcp, and tools/list showed the new descriptions.
@adibhanna
adibhanna changed the base branch from main to v2.70.0 October 14, 2026 16:53
@adibhanna
adibhanna merged commit ee57932 into v2.70.0 Oct 14, 2026
10 checks passed
@adibhanna
adibhanna deleted the feat/mcp-task-status branch October 14, 2026 16:55
@adibhanna adibhanna mentioned this pull request Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant