Skip to content

wip: support parented MCP block writes - #17

Merged
e-zz merged 1 commit into
privfrom
fix/issue-6-mcp-parent-wip
Oct 10, 2026
Merged

e-zz merged 1 commit into
privfrom
fix/issue-6-mcp-parent-wip

Conversation

@e-zz

@e-zz e-zz commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

Draft WIP for #6 — writing parented block
trees in a single MCP upsertNodes batch.

upsertNodes block adds now accept an optional :parent-id, which may be:

  • the unique temporary :id of another block add in the same batch
    (child-before-parent and multi-level nesting both work), or
  • the stable uuid string of an existing ordinary visible block on the same page.

Same-page, same-call nesting is encoded through the importer's :build/children
recursion; existing parents use an explicit uuid lookup ref. Missing, duplicate,
cross-page, hidden, recycled, tag/property/pseudochild, self-referential and
cyclic parents are rejected before any transaction. dry-run performs no
transaction and returns planned uuids. The verified receipt ({:receipt true})
resolves temporary parent ids to generated block uuids and checks the actually
persisted parent via worker DB readback.

Inherited dependency scope (declared, not new in this branch)

This branch was isolated from the dirty priv checkout where issue6 was
developed. The parent-id slice is not separable from the following inherited
slices, which are carried here verbatim and not claimed as new work:

  • issue8 read slice (tools.cljs / cli.cljs / worker handler / api export):
    get-block + read-upsert-blocks (receipt readback), entity serialization
    helpers, recycled-page gates and full-tree get-page-data
    (include-children? / max-blocks), plus their cli/tools tests.
  • issue5 receipt base: opt-in :receipt option on upsert-nodes,
    mcp-upsert.cljs adapter, receipt tests in cli_test.cljs, and the
    db_core test-api registry lines.

Search is explicitly out of scope: the main checkout's in-flight
scoped-search change (search options on logseq.app.search) is NOT included.
The scoped tests below do not establish app-bridge search behavior; search verification remains outside this PR.

Tests (isolated worktree, recompiled from this branch)

clojure -M:test compile db-worker-node test
LOGSEQ_STABLE_IDENTS=1 node static/tests.js -n logseq.api.db-based.issue6-test
LOGSEQ_STABLE_IDENTS=1 node static/tests.js -n logseq.api.db-based.tools-test
LOGSEQ_STABLE_IDENTS=1 node static/tests.js -n logseq.api.db-based.cli-test
LOGSEQ_STABLE_IDENTS=1 node static/tests.js -n electron.mcp-server-test
clojure -M:clj-kondo --lint <issue6 files>
Suite Result
logseq.api.db-based.issue6-test 10 tests / 41 assertions, 0 failures, 0 errors
logseq.api.db-based.tools-test 35 tests / 183 assertions, 0 failures, 0 errors
logseq.api.db-based.cli-test 18 tests / 67 assertions, 0 failures, 0 errors
electron.mcp-server-test 1 test / 3 assertions, 0 failures, 0 errors
clj-kondo (issue6 source + test files) 0 errors / 0 warnings

git diff --check clean. Compile: 0 warnings.

Unverified surfaces (Draft stays WIP)

  • Desktop → MCP HTTP runtime chain (Electron main process → /mcp →
    logseq.cli.upsertNodes → worker)
  • SQLite persistence after graph close/reopen
  • Packaged CLI E2E
  • Full repository test suite

Refs #6

Adds :parent-id to upsertNodes block adds so a batch can write a nested
block tree in a single call. A parent is either the unique temporary :id
of another block add in the same batch, or the stable uuid of an existing
ordinary visible block on the same page. Cross-page, hidden, recycled,
tag/property, pseudochild, self-referential and cyclic parents are
rejected before any transaction; dry-run still performs no transaction.
The verified receipt resolves temporary parent ids to the generated block
uuids and checks the actual persisted parent via worker DB readback.

Inherited slices carried from the dirty priv WIP because the parent-id
work is built on top of them (declared here, not new in this branch):
- issue8 read slice in tools/cli/worker/api: get-block + read-upsert-blocks
  (receipt readback), entity serialization helpers, page-recycled gates
  and full-tree get-page-data (include-children?/max-blocks) in
  get-page-data, plus their cli/tools tests.
- issue5 receipt base: opt-in :receipt option on upsert-nodes (cli.cljs,
  api export, worker handler, mcp-upsert.cljs adapter, receipt tests in
  cli_test) and the db_core test-api registry lines.
- search: the dirty main checkout carries an in-flight scoped-search
  change (logseq.app.search options); it is NOT included in this branch.
  The searchBlocks test app-search-converts-page-uuid-and-limit-options
  in cli_test.cljs tests that app-bridge conversion and will fail until
  the scoped-search slice lands separately.

Tests (isolated worktree, recompiled from this branch):
  logseq.api.db-based.issue6-test 10 tests / 41 assertions, 0 failures
  logseq.api.db-based.tools-test  35 tests / 183 assertions, 0 failures
  logseq.api.db-based.cli-test    18 tests / 67 assertions, 0 failures
  electron.mcp-server-test         1 test / 3 assertions, 0 failures
  clj-kondo (issue6 files) 0 errors / 0 warnings
Unverified: Desktop -> MCP HTTP runtime chain, SQLite persistence after
graph close/reopen, packaged CLI E2E, full repo test suite.

Refs #6
@e-zz
e-zz merged commit 76c7a22 into priv Oct 10, 2026
1 check failed
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