feat: add update_book and update_chapter tools - #20
Conversation
Books and chapters could only be created and deleted, not renamed or moved, via the MCP layer — the only way to rename one was to drop down to the raw BookStack REST API. update_chapter also closes the gap tracked in ttpears#12 (rename/move a chapter without reaching for curl). Both tools mirror the existing update_page/update_shelf pattern exactly: same write-gate guard, same enhance*Response wrapping, same Zod schema style. update_chapter additionally accepts book_id (move to a different book, matching update_page's book_id semantics) and priority (reorder within its book). Verified against a live BookStack instance: type-check/build/tests pass unchanged, and both tools were exercised end-to-end (rename, priority change, and a cross-book move) via the built server, with the result independently confirmed by an untouched second MCP session. Test objects were created and permanently removed as part of the verification. No new tests were added — this repo's existing suite only covers the concurrency limiter, and introducing an API-mocking harness for it felt like a separate decision to leave to the maintainer rather than bundle in here.
There was a problem hiding this comment.
Pull request overview
Adds missing BookStack “update” capabilities for books and chapters to the MCP server’s write-tool surface area, aligning these operations with existing update_page / update_shelf patterns so users can rename/move/reorder without dropping to raw REST calls.
Changes:
- Register new write tools:
update_bookandupdate_chapter(withbook_idmove +priorityreorder support). - Implement corresponding BookStackClient methods:
updateBook()andupdateChapter()using PUT endpoints. - Update README feature/tool listing to reflect the added tools and updated write-tool count.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/index.ts | Registers update_book and update_chapter write tools with Zod input schemas and tool handlers. |
| src/bookstack-client.ts | Adds updateBook and updateChapter client methods that call BookStack PUT endpoints and return enhanced responses. |
| README.md | Updates tool counts and write-tool table to include the new update tools. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
priority is an integer sort order; the schema previously accepted floats and negatives via z.coerce.number(), which BookStack would reject or sort unexpectedly. Constrain it at the tool boundary to a non-negative integer.
|
Addressed the Copilot note on |
update_page already supports moving content between books; update_chapter was missing the equivalent. book_id moves the chapter to a different book, priority reorders it within its book (matches upstream PR ttpears#20's proposal).
ttpears
left a comment
There was a problem hiding this comment.
build is green, and a trial merge onto main passes type-check/build/test locally (9/9).
docker-preview failing is not this PR — fork PRs get a read-only GITHUB_TOKEN, so the GHCR push returns denied: installation not allowed to Write organization package. Only build is required. Gating that job on same-repo PRs is a separate fix.
Test setup is fine as described — a PUT passthrough doesn't need a throwaway instance.
re: version — leave package.json alone, I'll cut the release.
update_page priority from #12 stays open as the follow-up.
Summary
Books and chapters could be created and deleted, but not renamed or moved,
via the MCP layer — the only way to rename one was to drop to the raw
BookStack REST API.
update_chaptercloses most of the gap tracked in #12(thanks @scocchiarella for the API research there — the field list below
matches your proposal).
update_book(id, {name?, description?, tags?})update_chapter(id, {name?, description?, book_id?, priority?, tags?})—
book_idmoves the chapter to another book,priorityreorders itwithin its book.
Scope notes:
prioritytoupdate_page. Leftout to keep this focused — happy to follow up.
update_bookintentionally exposes onlyname/description/tags,matching
update_shelf's scope.image_id(multipart upload, not aJSON PUT field) and
default_template_idwere left out deliberately;easy to add later if wanted.
update_page/update_shelfpattern exactly:same write-gate guard, same
enhance*Responsewrapping, same Zod schemastyle. No new patterns, no new permission logic.
Testing
type-check/build/testall pass unchanged (the existing 4-testsuite covers only the concurrency limiter).
Heads-up on the test setup — I did NOT use a throwaway instance as
CONTRIBUTING.md recommends. I verified end-to-end against an existing,
live BookStack instance with the built server (
BOOKSTACK_ENABLE_WRITE=true):renamed a book, renamed + reordered a chapter, and moved a chapter to a
different book — all via the new tools, independently confirmed via a
second untouched MCP session against the same instance. All test objects
were clearly marked (
PRTEST-…), created and then permanently deleted aspart of the run; recycle bin verified empty afterward. Nothing left behind.
No new automated tests were added: the project has no coverage of actual
BookStack API calls anywhere (per CONTRIBUTING.md), and introducing an
API-mocking harness felt like a separate decision for you rather than
something to bundle here. Not re-tested explicitly: the
ENABLE_WRITE=falsepath and invalid-id / non-existent-target-book error paths — both go
through the exact same shared code as
update_page/update_shelf.Version
Left
package.jsonuntouched — say the word for a bump (5.3.0 → 5.4.0)since that triggers your tag/release flow.