Skip to content

BookStack MCP Server — 69 tools via DADL

The BookStack DADL turns BookStack's API into an MCP server that Claude, GPT or any MCP-compatible agent can consume directly. One YAML file declares all 69 tools — book, chapter, image, page, role, attachment, and more — and ToolMesh serves them at runtime. No Python boilerplate, no per-endpoint code, no separate MCP server process.

Below: the endpoint coverage matrix, a two-block ToolMesh setup, the full tool reference grouped by BookStack feature area, required credential scopes.

Source: BookStack REST API

Credits: Dunkel Cloud GmbH -- maintainer Updated: 2026-06-12

Which BookStack endpoints are covered?

98% (69 of ~77 endpoints)

Focus: BookStack full API: books (CRUD, exports), pages (CRUD, HTML, markdown, exports), chapters (CRUD, exports), shelves (CRUD, book assignment), attachments (links, uploads), images (gallery, drawio, file data), comments (threads, replies, archiving), search (cross-entity), users (CRUD, invites), roles (permissions, MFA), recycle-bin (restore, purge), content-permissions (role overrides, fallback), audit-log, tags, ZIP imports.

Missing: Cover-image upload on book/shelf create/update and file replacement on attachment/image update (multipart facets of covered endpoints); the _method=PUT form workaround. All 77 documented endpoints are otherwise reachable -- the 15 export endpoints are collapsed into 6 parameterized tools.

Last reviewed: 2026-06-12

How do you configure the BookStack DADL?

  1. Log in to your BookStack instance as the user the API should act as (its roles define what the API can see and do)
  2. Ensure one of the user's roles grants the 'Access System API' permission: Settings > Roles > (role) > System Permissions
  3. Open the user's profile (avatar top-right > 'Edit Profile') and scroll to the 'API Tokens' section
  4. Click 'Create Token', enter a name and expiry date, then Save -- the Token ID and Token Secret are shown
  5. Join both values with a colon to form the credential: '<token_id>:<token_secret>' -- store this single string as bookstack_token

Environment variable: CREDENTIAL_BOOKSTACK_TOKEN

Authentication docs ↗

The backends.yaml url MUST include the /api suffix. The header sent is "Authorization: Token <token_id>:<token_secret>" -- the credential value is the colon-joined pair, ToolMesh prepends the "Token " prefix. Content visibility mirrors the token user's roles exactly; for read-mostly agents prefer a dedicated low-privilege user. Default rate limit is 180 requests/min per user (env API_REQUESTS_PER_MIN).

How do you install the BookStack MCP server with ToolMesh?

Add to your backends.yaml:

- name: bookstack
  transport: rest
  dadl: bookstack.dadl
  url: "https://wiki.example.com/api"

Set the credential:

CREDENTIAL_BOOKSTACK_TOKEN=your-token-here

What 69 tools does the BookStack DADL expose?

GET search_content Search across all content types (shelves, books, chapters, pages) using the same query syntax as the BookStack UI search bar. Returns {data, total}; each result has a 'type' property (bookshelf, book, chapter, page), tags, parent references (book/chapter), and a 'preview_html' object with highlighted name/content snippets. Uses its own page/count paging -- sort and filter params are NOT supported here.
GET list_shelves List all shelves visible to the API user. Returns id, name, slug, description, created_at, updated_at, created_by, updated_by, owned_by per shelf in {data, total}.
GET get_shelf Get a single shelf with tags, cover image info, description_html and the visible books on it (id, name, slug each).
POST create_shelf Create a new shelf. Optionally assign books by ID -- they appear on the shelf in the order given. Provide description (plain text) or description_html, not both.
PUT update_shelf Update a shelf. Providing 'books' REPLACES the entire book assignment with the given list -- fetch current books via get_shelf first when only adding/removing one.
DELETE delete_shelf Delete a shelf (sent to the recycle bin; books on it are NOT deleted). Returns 204.
GET list_books List all books visible to the API user. Returns id, name, slug, description, created_at, updated_at, created_by, updated_by, owned_by plus cover image info per book in {data, total}.
GET get_book Get a single book with tags, cover, description_html, shelves it is on, and a 'contents' tree listing its direct chapters and pages in display order -- each entry has a 'type' (chapter/page); chapters nest their pages. Use this to navigate a book's structure before reading pages.
POST create_book Create a new book. Provide description (plain text) or description_html. Cover-image upload requires multipart and is not supported here.
PUT update_book Update a book's details. Same writable fields as create_book.
DELETE delete_book Delete a book including its chapters and pages (sent to the recycle bin). Returns 204.
GET export_book Export a whole book (all chapters and pages compiled) as text. The result is the raw exported content -- HTML document, Markdown, or plain text. For PDF/ZIP use export_book_file.
GET export_book_file Export a whole book as a binary file -- 'pdf' or 'zip' (portable BookStack ZIP). Returns a download URL via the ToolMesh file broker.
GET list_chapters List all chapters visible to the API user. Returns id, book_id, name, slug, description, priority, created_at, updated_at, created_by, updated_by, owned_by per chapter in {data, total}.
GET get_chapter Get a single chapter with tags, description_html, book_slug and its visible pages (listing-level fields each).
POST create_chapter Create a new chapter inside a book.
PUT update_chapter Update a chapter. Passing a different book_id MOVES the chapter (and its pages) into that book -- requires delete permission on the chapter.
DELETE delete_chapter Delete a chapter including its pages (sent to the recycle bin). Returns 204.
GET export_chapter Export a chapter (all its pages compiled) as text -- HTML document, Markdown, or plain text. For PDF/ZIP use export_chapter_file.
GET export_chapter_file Export a chapter as a binary file -- 'pdf' or 'zip' (portable BookStack ZIP). Returns a download URL via the ToolMesh file broker.
GET list_pages List all pages visible to the API user. Returns id, book_id, chapter_id (0 when directly in a book), name, slug, priority, draft, template, created_at, updated_at, created_by, updated_by, owned_by per page in {data, total}. Content is NOT included -- call get_page for that.
GET get_page Get a single page with full content: 'html' (rendered output, includes resolved), 'raw_html' (stored source -- use as editing basis), 'markdown' (only if last saved with the Markdown editor), tags, and a 'comments' object with 'active' and 'archived' comment trees.
POST create_page Create a new page. Exactly ONE of book_id (page directly in book) or chapter_id (page inside chapter) is required, and exactly ONE of html or markdown content. Keep HTML to a single-block depth of plain elements for editor compatibility; base64 data-URI images are extracted into the image gallery automatically.
PUT update_page Update a page's details or content. Passing book_id or chapter_id MOVES the page to that parent (requires delete permission on the page). When updating content, base your edit on 'raw_html' from get_page and send it via 'html' -- the rendered 'html' field has include-tags resolved and round-trips badly.
DELETE delete_page Delete a page (sent to the recycle bin). Returns 204.
GET export_page Export a single page as text -- HTML document, Markdown, or plain text. For PDF/ZIP use export_page_file. Note: get_page already returns html/markdown content; exports add standalone document wrapping.
GET export_page_file Export a single page as a binary file -- 'pdf' or 'zip' (portable BookStack ZIP). Returns a download URL via the ToolMesh file broker.
GET list_attachments List attachments visible to the API user. Returns id, name, extension, uploaded_to (page ID), external (true = link attachment, false = file upload), order, created_at, updated_at, created_by, updated_by in {data, total}.
GET get_attachment Get details and content of an attachment. 'content' holds the link URL for external attachments, or the FULL base64-encoded file data for uploads -- which can be very large; strip it with a jq override when you only need metadata. 'links' provides ready-made HTML and Markdown embed snippets.
POST create_attachment Attach an external LINK to a page. For uploading a file as attachment use upload_attachment instead.
POST upload_attachment Upload a FILE as attachment to a page (multipart request). The file is fetched from the given URL by ToolMesh and uploaded to BookStack.
PUT update_attachment Update an attachment's name, target link, or move it to another page. Replacing an uploaded FILE requires a multipart PUT which is not modeled -- delete and re-create via upload_attachment instead.
DELETE delete_attachment Permanently delete an attachment (no recycle bin). Returns 204.
GET list_images List images in the system (page gallery images and drawio diagrams). Returns id, name, url, path, type (gallery|drawio), uploaded_to (page ID), created_by, updated_by, created_at, updated_at in {data, total}. Visibility requires access to the page each image was uploaded to.
GET get_image Get details of a single image: url, 'thumbs' (scaled variants) and 'content' with ready-made HTML/Markdown embed snippets as BookStack would insert them. Image file data is NOT included -- use get_image_data or the 'url' property.
POST create_image Upload a new image to the gallery of a page (multipart request). Use type 'gallery' for normal page images; 'drawio' ONLY for PNG files with embedded diagrams.net data. If name is omitted the filename is used.
PUT update_image Update an image's name. Replacing the image FILE requires a multipart PUT which is not modeled -- upload a new image instead.
DELETE delete_image Permanently delete an image and its thumbnails (no recycle bin). Usage is NOT checked -- pages referencing the image will show broken references. Returns 204.
GET get_image_data Download the raw image file data for an image ID. Returns a download URL via the ToolMesh file broker instead of JSON.
GET get_image_data_by_url Download raw image file data identified by its public BookStack image URL (as found in page content src attributes) instead of an ID. Returns a download URL via the ToolMesh file broker.
GET list_comments List comments visible to the API user. Returns id, commentable_id (page ID), commentable_type, parent_id, local_id, content_ref, created_by, updated_by, created_at, updated_at in {data, total}. Comment HTML is NOT included -- use get_comment. For a page's full comment tree prefer get_page (comments property).
GET get_comment Get a single comment (with safe HTML content) plus its direct replies. Note: 'local_id' is page-scoped; 'parent_id' of replies refers to the parent's local_id, not the global id.
POST create_comment Create a comment on a page. To reply to an existing comment set reply_to to the parent comment's LOCAL_ID (page-scoped), not its global id.
PUT update_comment Update a comment's content and/or archive state. Only provide 'archived' when actively changing it; only top-level comments (non-replies) can be archived/unarchived.
DELETE delete_comment Permanently delete a comment (no recycle bin). Returns 204.
GET list_users List all users. Requires 'Manage users' permission. Returns id, name, slug, email, external_auth_id, created_at, updated_at, last_activity_at plus profile/avatar URLs in {data, total}.
GET get_user Get a single user including their assigned roles (id + display_name). Requires 'Manage users' permission.
POST create_user Create a new user. Requires 'Manage users' permission. Either set a password or send_invite=true so the user sets their own via email.
PUT update_user Update a user. Requires 'Manage users' permission. 'roles' replaces the full role assignment.
DELETE delete_user Permanently delete a user. Requires 'Manage users' permission. Pass migrate_ownership_id to transfer ownership of their content to another user first. Returns 204.
GET list_roles List all roles. Requires 'Manage roles' permission. Returns display_name, description, mfa_enforced, external_auth_id, timestamps plus permissions_count and users_count in {data, total}.
GET get_role Get a single role including its granted permission name strings and a high-level list of assigned users. Requires 'Manage roles' permission.
POST create_role Create a new role. Permissions are given as an array of permission name strings (e.g. 'access-api', 'page-create-all', 'book-view-own') -- inspect an existing role via get_role for the full vocabulary.
PUT update_role Update a role. CAUTION: 'permissions' replaces ALL granted permissions and an empty array clears them -- fetch current permissions via get_role and send the modified full set.
DELETE delete_role Permanently delete a role (users keep their other roles). Requires 'Manage roles' permission. Returns 204.
GET list_recycle_bin List items in the recycle bin. Requires permission to manage both system settings and permissions. Each entry has a deletion id (use for restore/purge), deleted_by, deletable_type (page/chapter/book/ bookshelf), deletable_id and a 'deletable' object with child counts (books/chapters) or parent info (chapters/pages).
PUT restore_recycle_bin_item Restore a deletion from the recycle bin, including all child content. Takes the DELETION id from list_recycle_bin, not the content's own ID. Returns {restore_count}.
DELETE purge_recycle_bin_item PERMANENTLY destroy a deletion from the recycle bin including all child content -- this cannot be undone. Takes the DELETION id from list_recycle_bin. Returns {delete_count}.
GET get_content_permissions Read the content-level permission OVERRIDES for one item: owner, role_permissions (per-role view/create/update/delete flags) and fallback_permissions ('inheriting': true means no override; its flag values are null then). Shows only overrides on this item -- not evaluated/inherited permissions.
PUT update_content_permissions Update content-level permission overrides for one item. OMIT owner_id / role_permissions / fallback_permissions entirely to leave that category unchanged. CAUTION: an empty role_permissions array CLEARS all configured role overrides -- read existing permissions first and send the merged result.
GET list_audit_log List audit log events. Requires permission to manage both users and system settings. Returns id, type (e.g. page_create, auth_login, permissions_update), detail, user_id (plus user object), loggable_id, loggable_type, ip, created_at in {data, total}.
GET list_tag_names List distinct tag NAMES used across visible content, with usage totals: name, values (count of distinct values), usages, page_count, chapter_count, book_count, shelf_count. Only 'name' is filterable. Requires BookStack >= v26.05 -- older instances return a 404 HTML page.
GET list_tag_values List the distinct VALUES set for one tag name across visible content, with usage totals per value. Only 'value' is filterable. Requires BookStack >= v26.05 -- older instances return a 404 HTML page.
GET list_imports List pending ZIP imports visible to the user. Requires 'Import content' permission. Returns id, name, size, type (book/chapter/page), created_by, created_at, updated_at in {data, total}.
POST create_import Upload and validate a BookStack portable ZIP file (from export_*_file zip exports) as a pending import. Does NOT import yet -- call run_import afterwards. Requires 'Import content' permission.
GET get_import Read a pending ZIP import including a 'details' property with metadata about the ZIP content (structure varies by import type).
POST run_import Execute a pending ZIP import. parent_type + parent_id are REQUIRED when the import's type is 'chapter' or 'page' (check via get_import); book imports need no parent. Returns the imported item on success.
DELETE delete_import Delete a pending ZIP import (the staged upload, not imported content). Returns 204.
GET get_system_info Read instance details: version, instance_id, app_name, app_logo (may be null), base_url. Useful for verifying connectivity and version-gated features.