From d2858537536fa8ccb0e308c9466b97c21a66945e Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 20:51:38 +0000 Subject: [PATCH 1/6] docs: problem statement, feedback/obtain/contribute info, contribution requirements (OpenSSF Bronze) Addresses four OpenSSF Bronze "Basic project website content" criteria: - MUST succinctly describe what the software does: replace generic tagline with a problem-statement-focused opening paragraph - MUST provide info on how to obtain, provide feedback, and contribute: add "Obtaining the software" and "Feedback and contributing" sections near the top - Contribution info MUST explain the contribution process (URL): CONTRIBUTING.md already covers PRs and DCO; link made prominent in new top-level section - Contribution info SHOULD include requirements for acceptable contributions (URL): add explicit "Requirements for acceptable contributions" section to CONTRIBUTING.md listing DCO, type-check, build, test, audit, and style gates Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- CONTRIBUTING.md | 11 +++++++++++ README.md | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c74fc3..84d05b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,6 +65,17 @@ npm test Please do **not** open a public issue for security vulnerabilities. Use the process described in [SECURITY.md](SECURITY.md). +## Requirements for acceptable contributions + +All pull requests must meet these requirements before they will be merged: + +1. **DCO sign-off** — every commit must have a `Signed-off-by` line (see above) +2. **Type-check passes** — `npm run type-check` must succeed with no errors +3. **Build passes** — `npm run build` must succeed +4. **Tests pass** — `npm test` must pass (unit and fuzz tests run without credentials; functional tests require a live BookStack instance) +5. **No new HIGH/CRITICAL vulnerabilities** — `npm audit --audit-level=high` must pass +6. **Code style** — follow the conventions below + ## Code style - TypeScript strict mode diff --git a/README.md b/README.md index df25eab..f097351 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,21 @@ [![OpenSSF Baseline](https://www.bestpractices.dev/projects/12116/baseline)](https://www.bestpractices.dev/projects/12116) -A modern Model Context Protocol (MCP) server for BookStack, providing AI assistants with full access to your BookStack documentation. +BookStack stores your team's knowledge — but AI assistants can't access it without an integration. BookStack MCP Server bridges that gap, connecting AI assistants (Claude Desktop, LibreChat, and any MCP-compatible client) directly to your BookStack instance so they can search, read, and manage your documentation through natural language. + +## Obtaining the software + +- **Docker (recommended):** `docker pull ghcr.io/paradoxbound/bookstack-mcp:latest` +- **npm:** `npx bookstack-mcp` (no installation required) +- **Source:** Clone the repository and run `npm install && npm run build` + +Full setup instructions are in the [Quick Start](#quick-start) section below. + +## Feedback and contributing + +- **Bug reports and feature requests:** [Open an issue](https://github.com/paradoxbound/bookstack-mcp/issues) +- **Security vulnerabilities:** Follow the process in [SECURITY.md](SECURITY.md) — do not open a public issue +- **Contributing code or docs:** See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution process, DCO sign-off requirement, and code style requirements ## Features From f8a3b2efc7ce9620905bc47f1ca6d2a86ecae3f2 Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 20:55:45 +0000 Subject: [PATCH 2/6] docs: link to LICENSE file with SPDX identifier and OSI reference (OpenSSF Bronze) Addresses OpenSSF Bronze FLOSS license criteria: - Software MUST be released as FLOSS: MIT (OSI-approved) - License MUST be posted in a standard location: LICENSE at repo root (unchanged) - Update README License section to hyperlink the LICENSE file directly and note the SPDX identifier and OSI approval, satisfying the URL requirement Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f097351..4a84b4a 100644 --- a/README.md +++ b/README.md @@ -401,7 +401,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on making changes, running ## License -MIT License - see LICENSE file for details +This project is released under the [MIT License](LICENSE) (SPDX: `MIT`), an [OSI-approved](https://opensource.org/license/mit) permissive free and open-source software license. ## Links From a10b85679f9dafeb246246849f7cfe2486b8af7a Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 21:37:29 +0000 Subject: [PATCH 3/6] docs: add tool reference documentation (OpenSSF Bronze) Creates docs/reference.md with complete input/output documentation for all 45 MCP tools. Covers common patterns (pagination, sorting, filters, response enhancement) then documents each tool with an input parameter table (name/type/required/constraints/description) and output description. Updates the README "Available Tools" section to link to the reference doc rather than duplicating the full tool list inline. Satisfies OpenSSF Bronze "reference documentation for external interface" criterion. URL: docs/reference.md Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- README.md | 63 +--- docs/reference.md | 748 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 753 insertions(+), 58 deletions(-) create mode 100644 docs/reference.md diff --git a/README.md b/README.md index 4a84b4a..d4807fe 100644 --- a/README.md +++ b/README.md @@ -111,19 +111,7 @@ mcpServers: # BOOKSTACK_ENABLE_WRITE: "false" # Optional ``` -Or use local build: - -```yaml -mcpServers: - bookstack: - command: node - args: - - /path/to/bookstack-mcp/packages/stdio/dist/index.js - env: - BOOKSTACK_BASE_URL: "https://your-bookstack.com" - BOOKSTACK_TOKEN_ID: "your-token-id" - BOOKSTACK_TOKEN_SECRET: "your-token-secret" -``` +See [docs/librechat-integration.md](docs/librechat-integration.md) for advanced options, troubleshooting, and local build configuration. ### Docker @@ -154,56 +142,15 @@ docker run --rm \ ## Available Tools +45 tools across two categories. See [docs/reference.md](docs/reference.md) for complete input/output documentation for every tool. + ### Read Operations (Always Available) -1. **get_capabilities** - Show current server capabilities -2. **search_content** - Advanced search with filtering and pagination -3. **search_pages** - Search specifically for pages with book filtering -4. **get_books** - List books with advanced filtering and sorting -5. **get_book** - Get detailed information about a specific book -6. **get_pages** - List pages with previews and context -7. **get_page** - Get full content of a specific page -8. **get_chapters** - List chapters with filtering -9. **get_chapter** - Get details of a specific chapter -10. **get_shelves** - List book shelves (collections) -11. **get_shelf** - Get shelf details with all books -12. **get_attachments** - List attachments with filtering -13. **get_attachment** - Get attachment details -14. **export_page** - Export pages in various formats -15. **export_book** - Export entire books -16. **export_chapter** - Export chapters -17. **get_recent_changes** - Get recently updated content -18. **get_comments** - List comments with optional page filtering -19. **get_comment** - Get comment details including replies -20. **get_audit_log** - List audit log (system activity trail) -21. **get_system_info** - BookStack version and instance info -22. **get_users** - List users (read-only) -23. **get_user** - Get user details (read-only) -24. **get_recycle_bin** - List soft-deleted items -25. **get_image_gallery** - List gallery images (read-only) -26. **get_image** - Get gallery image details (read-only) +`get_capabilities`, `search_content`, `search_pages`, `get_books`, `get_book`, `get_pages`, `get_page`, `get_chapters`, `get_chapter`, `get_shelves`, `get_shelf`, `get_attachments`, `get_attachment`, `export_page`, `export_book`, `export_chapter`, `get_recent_changes`, `get_comments`, `get_comment`, `get_audit_log`, `get_system_info`, `get_users`, `get_user`, `get_recycle_bin`, `get_image_gallery`, `get_image` ### Write Operations (Requires BOOKSTACK_ENABLE_WRITE=true) -27. **create_book** - Create new books -28. **update_book** - Update existing books -29. **delete_book** - Delete books -30. **create_chapter** - Create new chapters -31. **update_chapter** - Update existing chapters -32. **delete_chapter** - Delete chapters -33. **create_page** - Create new pages -34. **update_page** - Update existing pages -35. **delete_page** - Delete pages -36. **create_shelf** - Create new shelves -37. **update_shelf** - Update existing shelves -38. **delete_shelf** - Delete shelves -39. **create_attachment** - Create link attachments -40. **upload_attachment** - Upload file attachments from local filesystem -41. **update_attachment** - Update attachments -42. **delete_attachment** - Delete attachments -43. **create_comment** - Create comments on pages (with reply support) -44. **update_comment** - Update comment content or archive status -45. **delete_comment** - Delete comments +`create_book`, `update_book`, `delete_book`, `create_chapter`, `update_chapter`, `delete_chapter`, `create_page`, `update_page`, `delete_page`, `create_shelf`, `update_shelf`, `delete_shelf`, `create_attachment`, `upload_attachment`, `update_attachment`, `delete_attachment`, `create_comment`, `update_comment`, `delete_comment` ## BookStack API Setup diff --git a/docs/reference.md b/docs/reference.md new file mode 100644 index 0000000..c69baa5 --- /dev/null +++ b/docs/reference.md @@ -0,0 +1,748 @@ +# BookStack MCP — Tool Reference + +> For installation and quick start, see the [README](../README.md). + +This reference documents all 45 tools exposed by BookStack MCP Server. Tools are invoked by MCP-compatible clients (Claude Desktop, LibreChat, etc.) using their registered names. + +## Common patterns + +### Pagination + +All list tools accept `offset` (default: `0`) and `count` (default: `50`, max: `500`) for pagination. + +### Sorting + +The `sort` parameter accepts a field name for ascending order, or `-field_name` (with a leading dash) for descending order. Examples: `name`, `-created_at`, `updated_at`. + +### Filters + +The `filter` parameter accepts an object of key/value pairs applied as BookStack API filter criteria. Example: `{"name": "API Guide"}`. + +### Response enhancement + +All responses from BookStack are enhanced with additional fields before being returned to the MCP client: + +| Added field | Description | +|---|---| +| `url` | Direct link to the item in BookStack | +| `direct_link` | Markdown-formatted `[Name](url)` link | +| `friendly_date` | Human-readable timestamp, e.g. "2 hours ago" | +| `content_preview` | 150–200 character excerpt of text content | +| `word_count` | Word count (pages only) | +| `location` | Contextual info: parent book name, chapter name (pages and chapters) | + +--- + +## Read tools + +These 26 tools are always available regardless of the `BOOKSTACK_ENABLE_WRITE` setting. + +--- + +### get_capabilities + +Returns a summary of the server's current configuration and available tools. + +**Input:** none + +**Output:** JSON object with fields: `server_name`, `version`, `write_operations_enabled` (boolean), `available_tools` (array of tool names), `security_note`. + +--- + +### search_content + +Performs a full-text search across all BookStack content types. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `query` | string | yes | | Search query. Supports BookStack advanced syntax, e.g. `{type:page}`, `{book_id:5}` | +| `type` | string | no | `"book"` \| `"page"` \| `"chapter"` \| `"bookshelf"` | Filter results to a specific content type | +| `count` | number | no | 1–500 | Number of results to return | +| `offset` | number | no | ≥0 | Pagination offset | + +**Output:** JSON object with `search_query`, `search_url` (link to BookStack search UI), `summary`, and `results` array. Each result includes `type`, `name`, `url`, `direct_link`, `content_preview`, `location_info`, and standard entity fields. + +--- + +### search_pages + +Searches specifically within pages, with optional book scoping. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `query` | string | yes | | Search query | +| `book_id` | number | no | ≥1 | Restrict search to a specific book | +| `count` | number | no | 1–500 | Number of results | +| `offset` | number | no | ≥0 | Pagination offset | + +**Output:** JSON with page search results and content previews. + +--- + +### get_books + +Lists all books with pagination, sorting, and filtering. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field, e.g. `name`, `-created_at` | +| `filter` | object | no | | Filter criteria | + +**Output:** JSON with `data` (array of enhanced book objects) and `total` (integer). Each book includes `id`, `name`, `slug`, `description`, `url`, `direct_link`, `friendly_date`, `content_preview`, and timestamps. + +--- + +### get_book + +Gets detailed information about a single book. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Book ID | + +**Output:** Enhanced book object with `id`, `name`, `slug`, `description`, `url`, `direct_link`, `friendly_date`, `created_at`, `updated_at`. + +--- + +### get_pages + +Lists pages with optional book and chapter filtering. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `book_id` | number | no | ≥1 | Filter by book ID | +| `chapter_id` | number | no | ≥1 | Filter by chapter ID | +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | +| `filter` | object | no | | Additional filters | + +**Output:** JSON with `data` (enhanced page objects) and `total`. Each page includes `id`, `name`, `slug`, `book_id`, `chapter_id`, `url`, `direct_link`, `content_preview`, `word_count`, `location`, and timestamps. + +--- + +### get_page + +Gets the full content of a single page. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Page ID | + +**Output:** Enhanced page object including `html`, `markdown`, `text` (plain text), `word_count`, `url`, `direct_link`, `location`, `friendly_date`, and all standard page fields. + +--- + +### get_chapters + +Lists chapters with optional book filtering. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `book_id` | number | no | ≥1 | Filter by book ID | +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | + +**Output:** JSON with `data` (enhanced chapter objects) and `total`. + +--- + +### get_chapter + +Gets details of a single chapter. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Chapter ID | + +**Output:** Enhanced chapter object with `id`, `name`, `slug`, `book_id`, `description`, `url`, `direct_link`, `friendly_date`, location info, and timestamps. + +--- + +### get_shelves + +Lists book shelves (collections of books). + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | +| `filter` | object | no | | Filter criteria | + +**Output:** JSON with `data` (enhanced shelf objects) and `total`. Each shelf includes `book_count`, an array of books, `tags_summary`, `url`, `direct_link`, and timestamps. + +--- + +### get_shelf + +Gets details of a single shelf including all its books. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Shelf ID | + +**Output:** Enhanced shelf object with full `books` array, `tags`, `description`, `url`, `direct_link`, `friendly_date`, and timestamps. + +--- + +### get_attachments + +Lists file and link attachments. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | +| `filter` | object | no | | Filter criteria | + +**Output:** JSON with `data` (enhanced attachment objects) and `total`. Each includes `page_url` (link to the page the attachment belongs to) and `direct_link`. + +--- + +### get_attachment + +Gets details of a single attachment. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Attachment ID | + +**Output:** Attachment object with `id`, `name`, `extension`, `uploaded_to` (page ID), `external` (boolean), `page_url`, `direct_link`, `download_url`, and timestamps. + +--- + +### export_page + +Exports a page in the specified format. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Page ID | +| `format` | string | yes | `"html"` \| `"pdf"` \| `"markdown"` \| `"plaintext"` \| `"zip"` | Export format | + +**Output:** +- For `html`, `markdown`, `plaintext`: raw content string +- For `pdf`, `zip`: JSON object with `download_url`, `page_name`, `book_name`, `filename`, `note` + +--- + +### export_book + +Exports an entire book in the specified format. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Book ID | +| `format` | string | yes | `"html"` \| `"pdf"` \| `"markdown"` \| `"plaintext"` \| `"zip"` | Export format | + +**Output:** +- For `html`, `markdown`, `plaintext`: raw content string +- For `pdf`, `zip`: JSON object with `download_url`, `book_name`, `filename` + +--- + +### export_chapter + +Exports a chapter in the specified format. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Chapter ID | +| `format` | string | yes | `"html"` \| `"pdf"` \| `"markdown"` \| `"plaintext"` \| `"zip"` | Export format | + +**Output:** +- For `html`, `markdown`, `plaintext`: raw content string +- For `pdf`, `zip`: JSON object with `download_url`, `chapter_name`, `book_name`, `filename` + +--- + +### get_recent_changes + +Lists recently updated content across all content types. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `type` | string | no | `"all"` \| `"page"` \| `"book"` \| `"chapter"`, default `"all"` | Filter by content type | +| `limit` | number | no | 1–100, default 20 | Number of items | +| `days` | number | no | 1–365, default 30 | How many days back to search | + +**Output:** JSON with `results` array. Each result includes `url`, `direct_link`, `content_preview`, `contextual_info`, `last_updated` (friendly date), `change_summary`. + +--- + +### get_comments + +Lists comments, optionally filtered to a specific page. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `page_id` | number | no | ≥1 | Filter comments to a specific page | +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | e.g. `created_at`, `-created_at` | Sort field | + +**Output:** JSON with `data` (comment objects) and `total`. Each comment includes `id`, `html`, `created_by`, `local_id`, `parent_id`, and timestamps. + +--- + +### get_comment + +Gets a single comment with its replies. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Comment ID | + +**Output:** Comment object with `id`, `html`, `replies` (array), creator info, timestamps, and `content_ref`. + +--- + +### get_audit_log + +Lists the system audit log. Requires admin permissions in BookStack. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | e.g. `-created_at` | Sort field | + +**Output:** JSON with `data` (audit log entries) and `total`. Each entry includes `id`, `type`, `detail`, `user_id`, `loggable_type`, `loggable_id`, `ip`, `created_at`, `user`. + +--- + +### get_system_info + +Returns BookStack instance information. + +**Input:** none + +**Output:** JSON with `version`, `instance_id`, `app_name`, `app_logo` (optional), `base_url`. + +--- + +### get_users + +Lists BookStack users (read-only). Requires admin permissions in BookStack. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | + +**Output:** JSON with `data` (user objects) and `total`. Each user includes `id`, `name`, `email`, `slug`, and timestamps. + +--- + +### get_user + +Gets details of a single user (read-only). Requires admin permissions in BookStack. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | User ID | + +**Output:** User object with `id`, `name`, `email`, `slug`, `profile_url`, `edit_url`, `avatar_url`, `last_activity_at`, and timestamps. + +--- + +### get_recycle_bin + +Lists soft-deleted items in the recycle bin. Requires admin permissions in BookStack. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | + +**Output:** JSON with `data` (recycle bin entries) and `total`. Each entry includes `id`, `deleted_by`, `deletable_type`, `deletable_id`, and the full `deletable` object (the deleted book, page, etc.). + +--- + +### get_image_gallery + +Lists images in the BookStack image gallery (read-only). + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `offset` | number | no | ≥0, default 0 | Pagination offset | +| `count` | number | no | 1–500, default 50 | Results per page | +| `sort` | string | no | | Sort field | + +**Output:** JSON with `data` (image gallery entries) and `total`. Each entry includes `id`, `name`, `url`, `path`, `type`, `uploaded_to`, creator info, and timestamps. + +--- + +### get_image + +Gets details of a single gallery image (read-only). + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Image ID | + +**Output:** Image gallery entry with `id`, `name`, `url`, `path`, `type`, `uploaded_to`, creator/updater info, and timestamps. + +--- + +## Write tools + +These 19 tools are only registered when the server is started with `BOOKSTACK_ENABLE_WRITE=true`. Calling them without that setting enabled returns an error. + +--- + +### create_book + +Creates a new book. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `name` | string | yes | 1–255 chars | Book name | +| `description` | string | no | ≤1000 chars | Book description | +| `tags` | array | no | Array of `{name, value}` objects | Tags to apply | + +**Output:** Enhanced book object (same shape as `get_book`). + +--- + +### update_book + +Updates an existing book. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Book ID | +| `name` | string | no | 1–255 chars | New name | +| `description` | string | no | ≤1000 chars | New description | +| `tags` | array | no | Array of `{name, value}` objects | Tags (replaces all existing tags) | + +**Output:** Enhanced book object. + +--- + +### delete_book + +Deletes a book and all its contents. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Book ID | + +**Output:** Deletion result object. + +--- + +### create_chapter + +Creates a new chapter inside a book. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `book_id` | number | yes | ≥1 | Parent book ID | +| `name` | string | yes | 1–255 chars | Chapter name | +| `description` | string | no | ≤1000 chars | Chapter description | +| `tags` | array | no | Array of `{name, value}` objects | Tags | + +**Output:** Enhanced chapter object. + +--- + +### update_chapter + +Updates an existing chapter. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Chapter ID | +| `name` | string | no | 1–255 chars | New name | +| `description` | string | no | ≤1000 chars | New description | +| `book_id` | number | no | ≥1 | Move chapter to a different book | +| `tags` | array | no | Array of `{name, value}` objects | Tags (replaces all existing tags) | + +**Output:** Enhanced chapter object. + +--- + +### delete_chapter + +Deletes a chapter. Its pages are moved up to the parent book. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Chapter ID | + +**Output:** Deletion result object. + +--- + +### create_page + +Creates a new page in a book or chapter. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `name` | string | yes | 1–255 chars | Page name | +| `book_id` | number | yes | ≥1 | Parent book ID | +| `chapter_id` | number | no | ≥1 | Place the page inside a specific chapter | +| `html` | string | no | ≤1,000,000 chars | Page content as HTML | +| `markdown` | string | no | ≤1,000,000 chars | Page content as Markdown | + +**Output:** Enhanced page object. + +--- + +### update_page + +Updates an existing page. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Page ID | +| `name` | string | no | 1–255 chars | New name | +| `html` | string | no | ≤1,000,000 chars | New content as HTML | +| `markdown` | string | no | ≤1,000,000 chars | New content as Markdown | + +**Output:** Enhanced page object. + +--- + +### delete_page + +Deletes a page (moves it to the recycle bin). + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Page ID | + +**Output:** Deletion result object. + +--- + +### create_shelf + +Creates a new book shelf. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `name` | string | yes | 1–255 chars | Shelf name | +| `description` | string | no | ≤1000 chars | Shelf description | +| `books` | array | no | Array of book ID numbers | Books to include on the shelf | +| `tags` | array | no | Array of `{name, value}` objects | Tags | + +**Output:** Enhanced shelf object. + +--- + +### update_shelf + +Updates an existing shelf. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Shelf ID | +| `name` | string | no | 1–255 chars | New name | +| `description` | string | no | ≤1000 chars | New description | +| `books` | array | no | Array of book ID numbers | Books on the shelf (replaces existing list) | +| `tags` | array | no | Array of `{name, value}` objects | Tags (replaces all existing tags) | + +**Output:** Enhanced shelf object. + +--- + +### delete_shelf + +Deletes a shelf. Books on the shelf are not deleted. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Shelf ID | + +**Output:** Deletion result object. + +--- + +### create_attachment + +Creates a link attachment on a page. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `name` | string | yes | 1–255 chars | Attachment display name | +| `uploaded_to` | number | yes | ≥1 | Page ID to attach to | +| `link` | string | yes | HTTP/HTTPS URL, ≤2000 chars | URL for the link attachment | + +**Output:** Attachment object with `id`, `name`, `page_url`, `direct_link`. + +--- + +### upload_attachment + +Uploads a local file as an attachment on a page. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `file_path` | string | yes | Absolute path | Path to the local file to upload. Supports `~` expansion. | +| `uploaded_to` | number | yes | ≥1 | Page ID to attach to | +| `name` | string | no | 1–255 chars | Attachment name (defaults to the filename) | + +**Output:** Attachment object with `id`, `name`, `extension`, `page_url`, `direct_link`. + +--- + +### update_attachment + +Updates an existing attachment. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Attachment ID | +| `name` | string | no | 1–255 chars | New display name | +| `link` | string | no | HTTP/HTTPS URL, ≤2000 chars | New URL (for link attachments only) | +| `uploaded_to` | number | no | ≥1 | Move attachment to a different page | + +**Output:** Updated attachment object. + +--- + +### delete_attachment + +Deletes an attachment. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Attachment ID | + +**Output:** Deletion result object. + +--- + +### create_comment + +Creates a comment on a page, with optional reply support. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `page_id` | number | yes | ≥1 | Page to comment on | +| `html` | string | yes | 1–10,000 chars | Comment content as HTML | +| `reply_to` | number | no | ≥1 | `local_id` of the parent comment (for replies) | +| `content_ref` | string | no | | Content reference for inline comments | + +**Output:** Comment object with `id`, `html`, `local_id`, `parent_id`, creator info, and timestamps. + +--- + +### update_comment + +Updates an existing comment. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Comment ID | +| `html` | string | no | 1–10,000 chars | New comment content | +| `archived` | boolean | no | | Archive or unarchive the comment (top-level comments only) | + +**Output:** Updated comment object. + +--- + +### delete_comment + +Deletes a comment. + +**Input:** + +| Parameter | Type | Required | Constraints | Description | +|---|---|---|---|---| +| `id` | number | yes | ≥1 | Comment ID | + +**Output:** Deletion result object. From 527b39afbfaaabe5e1ef1ff4bd17344ce225e934 Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 21:37:43 +0000 Subject: [PATCH 4/6] docs: move architecture, CI/CD, and LibreChat docs into docs/ folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates docs/ folder with flat structure (no subdirectories needed at this scale). Moves content out of CLAUDE.md and the repo root: - docs/architecture.md — monorepo structure, data flow, components, types, design decisions, migration history - docs/ci-cd.md — pipeline job sequences, version tagging, branch protection rules, SLSA, Trivy caching - docs/librechat-integration.md — moved from LIBRECHAT_INTEGRATION.md, updated internal links to docs/ paths Slims CLAUDE.md: replaces Architecture, CI/CD Pipeline, and Migration sections with one-line pointers to the new docs/ files. Claude Code specific content (build commands, key implementation details, common workflows, debugging, future plans) stays in CLAUDE.md. Deletes LIBRECHAT_INTEGRATION.md from the repo root. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- CLAUDE.md | 120 +---------- docs/architecture.md | 194 ++++++++++++++++++ docs/ci-cd.md | 129 ++++++++++++ .../librechat-integration.md | 13 +- 4 files changed, 332 insertions(+), 124 deletions(-) create mode 100644 docs/architecture.md create mode 100644 docs/ci-cd.md rename LIBRECHAT_INTEGRATION.md => docs/librechat-integration.md (91%) diff --git a/CLAUDE.md b/CLAUDE.md index ec3e0ff..46ad4d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,41 +28,7 @@ npm start # Run stdio server (node packages/stdio/dist/index.js) ## Architecture -### Monorepo (v2.5.0) - -- **packages/core** (`@bookstack-mcp/core`) – Shared BookStack API client and types. Uses **native `fetch`** only (no axios). Entry: `packages/core/src/bookstack-client.ts` and `packages/core/src/types.ts`. -- **packages/stdio** (`bookstack-mcp-stdio`) – MCP server with stdio transport. Imports `BookStackClient` and `BookStackConfig` from `@bookstack-mcp/core`. Entry: `packages/stdio/src/index.ts`. - -**Key Design Decisions:** -- **Native fetch** – Core client uses only `fetch`; API errors set `error.status` and `error.response` for tool error handling. -- **Stdio only** – Single transport for local, LibreChat, Claude Desktop. -- **Workspaces** – Root `package.json` has `"workspaces": ["packages/core", "packages/stdio"]`; build/test at root run in workspaces. - -### Data Flow - -``` -MCP Client (LibreChat/Claude Desktop) - ↓ -Stdio Transport (packages/stdio) - ↓ -McpServer - ListTools/CallTool - ↓ -Tool handlers - call BookStackClient from @bookstack-mcp/core - ↓ -BookStackClient (native fetch) - API calls, response enhancement - ↓ -Enhanced JSON with URLs, previews, metadata -``` - -### Core Components - -**packages/core** -- `src/bookstack-client.ts` – Fetch-based HTTP client, token auth, timeouts, `request()` / `requestForm()`; all entity methods; response enhancement (URLs, previews, dates). -- `src/types.ts` – Shared types (BookStackConfig, Book, Page, Chapter, Shelf, etc.). -- `tests/` – Functional tests (global-setup, read-tools, write-tools, write-gate); import `@bookstack-mcp/core`. - -**packages/stdio** -- `src/index.ts` – Env validation, McpServer, tool registration (read + write when enabled), stdio transport. Error handling uses `error.status` / `error.response` (no axios). +See [docs/architecture.md](docs/architecture.md) for the full architecture reference (monorepo structure, data flow, components, types, design decisions). ## Configuration @@ -233,23 +199,6 @@ npm test # runs packages/core tests - **packages/core** – No runtime deps; uses native `fetch`. Dev: typescript, vitest. - **packages/stdio** – `@bookstack-mcp/core`, `@modelcontextprotocol/sdk`, `zod`. Dev: typescript, tsx, @types/node. -## Migration from v1.0 - -### What Changed - -1. **Single entry point** - `src/index.ts` replaces multiple files -2. **Modern API** - `McpServer` + `registerTool()` instead of manual handlers -3. **Removed complexity** - No SSE, no supergateway, no separate transport layer -4. **Stdio only** - Universal transport works everywhere -5. **Zod schemas** - Type-safe input validation -6. **Simpler deployment** - Just works with LibreChat and Claude Desktop - -### v2.5.0 Monorepo - -- **packages/core** – BookStack client + types; native `fetch` only (no axios). -- **packages/stdio** – MCP server entry; depends on `@bookstack-mcp/core`. -- Root `src/` removed; tests live in `packages/core/tests`. Docker and CI build from root; image runs `node packages/stdio/dist/index.js`. - ## Debugging ### Check Server Output @@ -280,72 +229,7 @@ curl -H "Authorization: Token $BOOKSTACK_TOKEN_ID:$BOOKSTACK_TOKEN_SECRET" \ ## CI/CD Pipeline -### Workflow Overview - -| Workflow | Trigger | Purpose | -|---|---|---| -| `functional-tests.yml` | PR + push to main | Build, type-check, run functional tests | -| `docker-publish.yml` | PR + push to main | PR: Dockerfile validate + full CD pre-check. Post-merge: build, verify, merge manifest, tag, clean up | -| `auto-tag.yml` | _(retired — no trigger)_ | Kept as documentation only; logic moved into docker-publish.yml | - -### PR Job Sequence (docker-publish.yml) - -``` -pull_request → main (same-repo only) - ↓ -build-and-push (matrix: amd64 + arm64) fail-fast=true - │ build only — validates Dockerfile compiles cleanly (no push) - ↓ both must succeed -pre-merge-cd-check - ├── build + push :pr-{n}-amd64 and :pr-{n}-arm64 to GHCR - ├── verify both PR arch images exist in registry - ├── create + verify test manifest :pr-{n} - ├── assert version not already tagged in registry - └── clean up all :pr-{n}-* images (always, even on failure) -``` - -### Post-merge Job Sequence (docker-publish.yml) - -``` -push to main - ↓ -build-and-push (matrix: amd64 + arm64) fail-fast=true - ↓ both must succeed -verify — inspect both digests in GHCR - ↓ either missing → cleanup job runs, workflow fails -merge - ├── read version from packages/stdio/package.json - ├── assert version tag not already in registry - ├── create multi-arch manifest (:latest, :2.5.0, :2.5, :2) - ├── verify manifest is pullable - ├── create git tag (idempotent) - └── delete staging tags (:latest-amd64, :latest-arm64) via GHCR REST API - ↓ any step fails → cleanup job runs -cleanup (runs on verify or merge failure) - └── delete :latest-amd64 and :latest-arm64 from GHCR via REST API -``` - -### Required GitHub Branch Protection Rules - -These settings **must** be configured in GitHub → Settings → Branches → main to enforce the PR gate. They cannot be set in workflow files. - -- **Require status checks to pass before merging** - - Required checks: `test` (functional-tests.yml), `build-and-push` (docker-publish.yml), and `pre-merge-cd-check` (docker-publish.yml) -- **Require branches to be up to date before merging** — enabled -- **Restrict who can push to matching branches** — block direct pushes to main -- **Do not allow bypassing the above settings** — enabled - -Without these rules, GitHub will allow the merge button regardless of workflow results. - -> **Note:** `pre-merge-cd-check` only runs on same-repo PRs (not forks). Fork PRs cannot push to GHCR and will not have this check required. - -### Version Tagging Convention - -- Version is always read from `packages/stdio/package.json` (the published package). -- The root `package.json` is `private: true` and is **not** the version source. -- Bumping `packages/stdio/package.json` version and merging to main triggers a full release. -- If the version tag already exists in GHCR, the pipeline fails early to prevent overwriting a released image. -- Git tag (`vX.Y.Z`) is created **after** the registry manifest is verified — never before. +See [docs/ci-cd.md](docs/ci-cd.md) for the full pipeline reference (workflow overview, job sequences, version tagging, branch protection rules). ## Future Plans diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..750c1b3 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,194 @@ +# BookStack MCP — Architecture + +## Overview + +BookStack MCP Server is a monorepo with two packages: + +- **`packages/core`** (`@bookstack-mcp/core`) — BookStack API client and shared types. Uses native `fetch` only (no axios). No runtime dependencies. +- **`packages/stdio`** (`bookstack-mcp-stdio`) — MCP server with stdio transport. Imports `BookStackClient` and `BookStackConfig` from `@bookstack-mcp/core`. + +## Repository structure + +``` +packages/ +├── core/ # @bookstack-mcp/core — shared client & types +│ ├── src/ +│ │ ├── bookstack-client.ts # BookStack API client (native fetch) +│ │ └── types.ts # Shared TypeScript types +│ ├── tests/ # Unit, fuzz, and functional tests +│ └── dist/ # Compiled output +└── stdio/ # bookstack-mcp-stdio — MCP server + ├── src/ + │ └── index.ts # MCP tools + stdio transport + └── dist/ + └── index.js # Entry point (npm start / Docker) +``` + +## Data flow + +``` +MCP Client (Claude Desktop / LibreChat) + │ stdio: tool list request / tool call + ▼ +packages/stdio — McpServer + │ Zod input validation → registered tool handler + ▼ +packages/core — BookStackClient (native fetch) + │ HTTPS + Token auth → BookStack REST API + ▼ +BookStack instance + │ JSON response + ▼ +BookStackClient — response enhancement (URLs, previews, dates, word counts) + ▼ +packages/stdio — JSON serialised as MCP tool result + ▼ +MCP Client +``` + +## Actors + +| Actor | Role | +|---|---| +| **MCP Client** (Claude Desktop, LibreChat, etc.) | Sends tool-call requests over stdio; receives structured JSON responses | +| **bookstack-mcp server** (`packages/stdio`) | Validates inputs with Zod, dispatches to the BookStack API client, formats responses | +| **BookStack API client** (`packages/core`) | Authenticates with the BookStack instance, makes HTTP requests, enhances responses | +| **BookStack instance** | Stores and serves documentation content via its REST API | +| **Operator** | Configures environment variables (`BOOKSTACK_BASE_URL`, tokens, `BOOKSTACK_ENABLE_WRITE`) | + +## Key design decisions + +- **Native fetch** — no axios; HTTP errors surface as `error.status` / `error.response` +- **Stdio transport** — single universal transport for local, LibreChat, and Claude Desktop use +- **Zod schemas** — all MCP tool inputs validated before the API is called +- **Write-gate** — write tools are only registered when `BOOKSTACK_ENABLE_WRITE=true` +- **Monorepo** — `packages/core` has no runtime dependencies; `packages/stdio` depends on core and the MCP SDK + +## Core components + +### `packages/core/src/bookstack-client.ts` + +Fetch-based HTTP client with: +- Token authentication via `Authorization: Token {id}:{secret}` header +- 30-second default timeout (120s for file uploads) +- 429 rate-limit retry — 3 retries, honours `Retry-After` header, defaults to 10s backoff +- `request(method, path, params?, body?, options?)` — core HTTP method +- `requestForm(path, formData, options?)` — FormData POST for file uploads +- Private enhancement methods that augment API responses before they are returned: + - `enhanceBookResponse()` — adds URL, direct_link, friendly dates, content preview + - `enhancePageResponse()` — adds URL, direct_link, word_count, content preview, location + - `enhanceChapterResponse()` — adds URL, direct_link, friendly dates, location + - `enhanceShelfResponse()` — adds URL, direct_link, book_count, tags_summary + - `enhanceSearchResults()` — adds URL, direct_link, content_preview, location_info + +### `packages/core/src/types.ts` + +Shared TypeScript interfaces: + +| Type | Description | +|---|---| +| `BookStackConfig` | `{baseUrl, tokenId, tokenSecret, enableWrite?}` | +| `Book` | id, name, slug, description, timestamps, owned_by | +| `Page` | id, book_id, chapter_id?, name, slug, html, markdown, text, timestamps | +| `Chapter` | id, book_id, name, slug, description, timestamps | +| `Shelf` | id, name, slug, description, books[], tags[], timestamps | +| `Attachment` | id, name, extension, uploaded_to, external, timestamps | +| `Comment` | id, commentable_id, html, local_id, parent_id, archived, replies?, timestamps | +| `SearchResult` | type, id, name, slug, preview_content?, timestamps | +| `ListResponse` | `{data: T[], total: number}` | +| `AuditLogEntry` | id, type, detail, user_id, loggable_*, ip, created_at | +| `SystemInfo` | version, instance_id, app_name, base_url | +| `User` | id, name, email, slug, profile_url, timestamps | +| `RecycleBinEntry` | id, deleted_by, deletable_type, deletable_id, deletable | +| `ImageGalleryEntry` | id, name, url, path, type, uploaded_to, timestamps | +| `ApiError` | extends Error with `status?` and `response?` | + +### `packages/stdio/src/index.ts` + +MCP server entry point: +- Reads and validates environment variables at startup; exits with a clear error message if required vars are missing or `BOOKSTACK_BASE_URL` is not HTTPS +- Creates a `BookStackClient` instance +- Registers read tools unconditionally (26 tools) +- Registers write tools inside `if (config.enableWrite)` (19 tools) +- Starts the stdio transport + +## Response enhancement + +All API responses are enhanced with additional fields before being returned to the MCP client. The enrichment adds human-friendly context that raw BookStack API responses do not include: + +| Field | Description | +|---|---| +| `url` | Full URL to the item in BookStack, using slugs where available (`{baseUrl}/books/{slug}/page/{slug}`) | +| `direct_link` | Markdown-formatted link: `[Name](url)` | +| `friendly_date` | Relative timestamp ("2 hours ago", "3 days ago") | +| `content_preview` | 150–200 character excerpt of text content | +| `word_count` | Word count (pages only) | +| `location` | Parent book name and chapter name (pages and chapters) | + +## URL generation + +All URLs use slugs instead of IDs where available: +- Format: `{baseUrl}/books/{book.slug}/page/{page.slug}` +- Falls back to ID-based URLs if slugs are unavailable + +## Export handling + +Binary formats (PDF, ZIP) cannot be returned as inline content, so the server returns a metadata object containing a direct download URL: + +```json +{ + "download_url": "https://bookstack.example.com/...", + "page_name": "API Guide", + "book_name": "Developer Docs", + "filename": "api-guide.pdf", + "note": "Click download_url to retrieve the file" +} +``` + +Text formats (`html`, `markdown`, `plaintext`) return the raw content string directly. + +## Write operation security + +Write tools are only registered when `BOOKSTACK_ENABLE_WRITE=true` is explicitly set: + +```typescript +if (config.enableWrite) { + server.registerTool("create_page", ...); + server.registerTool("update_page", ...); + // ... other write tools +} +``` + +If a write tool is called without the flag set, the client receives a clear error message. Write operations are disabled by default; the server exposes only read tools in the default configuration. + +## HTTPS enforcement + +The server validates `BOOKSTACK_BASE_URL` at startup and exits immediately if it does not begin with `https://`. This prevents API tokens and content from being transmitted over plain HTTP. + +## Dependency structure + +| Package | Runtime deps | Dev deps | +|---|---|---| +| `packages/core` | none | typescript, vitest, fast-check, @fast-check/vitest | +| `packages/stdio` | @bookstack-mcp/core, @modelcontextprotocol/sdk, zod | typescript, tsx, @types/node | + +## Migration history + +### v1.0 → v2.0 + +| Before | After | +|---|---| +| Multiple entry files | Single `src/index.ts` | +| Manual MCP protocol handlers | `McpServer` + `registerTool()` API | +| SSE transport + supergateway | Stdio only | +| Separate transport layer | Universal transport | +| No input validation | Zod schemas on all inputs | +| Complex Docker setup for LibreChat | Add to `librechat.yaml` and restart | + +### v2.0 → v2.5.0 Monorepo + +- Root `src/` removed; split into `packages/core` and `packages/stdio` +- `packages/core` has zero runtime dependencies (native `fetch` only, no axios) +- Tests moved to `packages/core/tests` +- Docker and CI build from repo root; image runs `node packages/stdio/dist/index.js` +- Version source is `packages/stdio/package.json` (root `package.json` is `private: true`) diff --git a/docs/ci-cd.md b/docs/ci-cd.md new file mode 100644 index 0000000..0c58e57 --- /dev/null +++ b/docs/ci-cd.md @@ -0,0 +1,129 @@ +# BookStack MCP — CI/CD Pipeline + +## Workflow overview + +| Workflow | Trigger | Purpose | +|---|---|---| +| `functional-tests.yml` | PR + push to main | Build, type-check, npm audit, OSV scan, run tests | +| `docker-publish.yml` | PR + push to main | PR: Dockerfile validate + full CD pre-check. Post-merge: build, verify, merge manifest, tag, clean up | +| `codeql.yml` | PR + push to main + weekly | SAST scanning; blocks merge on HIGH/CRITICAL findings | +| `scorecard.yml` | Push to main + weekly | OpenSSF Scorecard assessment | +| `dependency-review.yml` | PR | Block new vulnerable/malicious dependencies | + +## PR job sequence (`docker-publish.yml`) + +``` +pull_request → main (same-repo PRs only) + ↓ +build-and-push (matrix: amd64 + arm64) fail-fast=true + │ build only — validates Dockerfile compiles cleanly (no push) + ↓ both must succeed +pre-merge-cd-check + ├── build + push :pr-{n}-amd64 and :pr-{n}-arm64 to GHCR + ├── verify both PR arch images exist in registry + ├── create + verify test manifest :pr-{n} + ├── assert version not already tagged in registry + └── clean up all :pr-{n}-* images (always, even on failure) +``` + +`pre-merge-cd-check` only runs on same-repo PRs. Fork PRs cannot push to GHCR and will not have this check required. + +## Post-merge job sequence (`docker-publish.yml`) + +``` +push to main + ↓ +check-version + ├── read version from packages/stdio/package.json + └── check if git tag vX.Y.Z already exists → if yes, skip release jobs + ↓ new version only +build-and-push (matrix: amd64 + arm64) fail-fast=true + ├── push :latest-amd64 and :latest-arm64 staging tags to GHCR + └── generate SLSA Level 2 provenance attestation via actions/attest-build-provenance + ↓ both must succeed +verify + └── inspect both digests in GHCR via imagetools inspect + ↓ either missing → cleanup job runs, workflow fails +merge + ├── assert version tag not already in registry + ├── create multi-arch manifest (:latest, :X.Y.Z, :X.Y, :X) + ├── verify manifest is pullable + ├── create git tag vX.Y.Z (idempotent) + └── delete staging tags (:latest-amd64, :latest-arm64) via GHCR REST API + ↓ any step fails → cleanup job runs +cleanup (runs on verify or merge failure) + └── delete :latest-amd64 and :latest-arm64 from GHCR via REST API +``` + +## Version tagging convention + +- Version is always read from `packages/stdio/package.json` (the published npm package). +- The root `package.json` is `private: true` and is **not** the version source. +- Bumping `packages/stdio/package.json` version and merging to main triggers a full release. +- If the version tag already exists in GHCR, the `check-version` job gates all release jobs — pipeline-only merges (no version bump) skip release steps cleanly. +- The git tag (`vX.Y.Z`) is created **after** the registry manifest is verified — never before. +- Multi-arch manifest tags created: `:latest`, `:X.Y.Z`, `:X.Y`, `:X` + +## GHCR tag deletion pattern + +The pipeline deletes staging tags via the GHCR REST API: + +```bash +# 1. Find the version ID by tag name +GET {PKG_API}/versions + +# 2. Delete by version ID +DELETE {PKG_API}/versions/{VERSION_ID} +``` + +Owner-type routing (handles personal vs organisation repos): + +```bash +OWNER_TYPE=$(curl -s -H "Authorization: Bearer $TOKEN" \ + "https://api.github.com/users/${OWNER}" | jq -r '.type') +if [ "$OWNER_TYPE" = "Organization" ]; then + PKG_API="https://api.github.com/orgs/${OWNER}/packages/container/${PKG}" +else + PKG_API="https://api.github.com/user/packages/container/${PKG}" +fi +``` + +## Required branch protection rules + +These settings must be configured in GitHub → Settings → Branches → main. They cannot be enforced from workflow files. + +| Setting | Value | +|---|---| +| Require status checks to pass before merging | Enabled | +| Required checks | `test`, `build-and-push (amd64, linux/amd64)`, `build-and-push (arm64, linux/arm64)`, `pre-merge-cd-check`, `CodeQL` | +| Require branches to be up to date before merging | Enabled | +| Restrict who can push to matching branches | Enabled (block direct pushes to main) | +| Do not allow bypassing the above settings | Enabled | + +Without these rules, GitHub allows the merge button regardless of workflow outcomes. + +## Security scanning + +| Tool | Scope | Failure threshold | +|---|---|---| +| `npm audit --audit-level=high` | Full npm dependency tree (including dev deps) | HIGH and CRITICAL vulnerabilities | +| OSV Scanner | Full recursive dependency tree | Any OSV advisory match | +| Trivy | Docker image | CRITICAL vulnerabilities in shipped image | +| GitHub Dependency Review | Dependency diff on each PR | New vulnerable or malicious dependencies | +| CodeQL | TypeScript/JavaScript source | `error`-level (HIGH/CRITICAL) SAST findings | + +A VEX document (`vex.json` at repo root) records confirmed non-applicable CVEs. Trivy reads it automatically via `--vex vex.json` to suppress those findings from gate failures. + +## SLSA provenance + +Every post-merge Docker image build generates a SLSA Level 2 provenance attestation via `actions/attest-build-provenance`. To verify: + +```bash +gh attestation verify \ + oci://ghcr.io/paradoxbound/bookstack-mcp:2.6.1 \ + --owner paradoxbound +``` + +## Trivy DB caching + +The Trivy vulnerability database is cached using `actions/cache@v4` with a weekly cache key (`date +%Y-%U`) to avoid redundant downloads on every run. diff --git a/LIBRECHAT_INTEGRATION.md b/docs/librechat-integration.md similarity index 91% rename from LIBRECHAT_INTEGRATION.md rename to docs/librechat-integration.md index 864a86e..35b1dea 100644 --- a/LIBRECHAT_INTEGRATION.md +++ b/docs/librechat-integration.md @@ -36,7 +36,7 @@ mcpServers: bookstack: command: node args: - - /path/to/bookstack-mcp/dist/index.js + - /path/to/bookstack-mcp/packages/stdio/dist/index.js env: BOOKSTACK_BASE_URL: "https://your-bookstack.com" BOOKSTACK_TOKEN_ID: "your-token-id" @@ -138,7 +138,7 @@ mcpServers: # ... env vars ``` -## What's Different from Old Integration? +## What's Different from the Old Integration? **Old way (v1.0):** - Required `Dockerfile.mcp-bookstack` @@ -147,7 +147,7 @@ mcpServers: - Complex Docker networking - Separate service container -**New way (v2.0):** +**New way (v2.0+):** - Just add to `librechat.yaml` - No Docker changes needed - Direct stdio communication @@ -168,6 +168,7 @@ The AI will automatically use the BookStack MCP tools to answer your questions. ## Next Steps -- See [README.md](./README.md) for complete tool list -- Check [CLAUDE.md](./CLAUDE.md) for architecture details -- Visit [BookStack docs](https://www.bookstackapp.com/docs/) for API info +- See [README](../README.md) for complete tool list and quick start +- See [Tool Reference](reference.md) for detailed input/output documentation +- See [Architecture](architecture.md) for how the server works internally +- Visit [BookStack docs](https://www.bookstackapp.com/docs/) for BookStack API info From 9668372b6d73fb0192cb6862cff4c7a3f76356e3 Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 21:43:36 +0000 Subject: [PATCH 5/6] docs: update repository structure diagrams to include docs/ folder Both docs/architecture.md and README.md "Project Structure" sections showed only the packages/ subtree. Update both diagrams to reflect the full repository layout including the new docs/ directory and root files. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- README.md | 28 ++++++++++++++++------------ docs/architecture.md | 37 +++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d4807fe..904e352 100644 --- a/README.md +++ b/README.md @@ -203,18 +203,22 @@ Tests are self-seeding: they create all required data on the instance and clean ## Project Structure ``` -packages/ -├── core/ # @bookstack-mcp/core – shared client & types -│ ├── src/ -│ │ ├── bookstack-client.ts # BookStack API (native fetch) -│ │ └── types.ts # Shared types -│ ├── tests/ # Functional tests -│ └── dist/ -└── stdio/ # bookstack-mcp-stdio – MCP server - ├── src/ - │ └── index.ts # MCP tools + stdio transport - └── dist/ - └── index.js # Entry point (npm start / Docker) +bookstack-mcp/ +├── docs/ # Project documentation +│ ├── reference.md # Tool reference (all 45 tools, input/output) +│ ├── architecture.md # Architecture and data flow +│ ├── ci-cd.md # CI/CD pipeline reference +│ └── librechat-integration.md +├── packages/ +│ ├── core/ # @bookstack-mcp/core – shared client & types +│ │ ├── src/ +│ │ │ ├── bookstack-client.ts # BookStack API (native fetch) +│ │ │ └── types.ts # Shared types +│ │ └── tests/ # Unit, fuzz, and functional tests +│ └── stdio/ # bookstack-mcp-stdio – MCP server +│ └── src/ +│ └── index.ts # MCP tools + stdio transport +└── package.json # Root workspace (private: true) ``` ## Response Enhancements diff --git a/docs/architecture.md b/docs/architecture.md index 750c1b3..c67dee9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -10,18 +10,31 @@ BookStack MCP Server is a monorepo with two packages: ## Repository structure ``` -packages/ -├── core/ # @bookstack-mcp/core — shared client & types -│ ├── src/ -│ │ ├── bookstack-client.ts # BookStack API client (native fetch) -│ │ └── types.ts # Shared TypeScript types -│ ├── tests/ # Unit, fuzz, and functional tests -│ └── dist/ # Compiled output -└── stdio/ # bookstack-mcp-stdio — MCP server - ├── src/ - │ └── index.ts # MCP tools + stdio transport - └── dist/ - └── index.js # Entry point (npm start / Docker) +bookstack-mcp/ +├── docs/ # Project documentation +│ ├── reference.md # Tool reference (all 45 tools, input/output) +│ ├── architecture.md # This file +│ ├── ci-cd.md # CI/CD pipeline reference +│ └── librechat-integration.md # LibreChat setup guide +├── packages/ +│ ├── core/ # @bookstack-mcp/core — shared client & types +│ │ ├── src/ +│ │ │ ├── bookstack-client.ts # BookStack API client (native fetch) +│ │ │ └── types.ts # Shared TypeScript types +│ │ ├── tests/ # Unit, fuzz, and functional tests +│ │ └── dist/ # Compiled output +│ └── stdio/ # bookstack-mcp-stdio — MCP server +│ ├── src/ +│ │ └── index.ts # MCP tools + stdio transport +│ └── dist/ +│ └── index.js # Entry point (npm start / Docker) +├── .github/ +│ └── workflows/ # CI/CD workflow definitions +├── CLAUDE.md # Claude Code project instructions +├── CONTRIBUTING.md # Contribution guidelines +├── SECURITY.md # Security policy and threat model +├── CHANGELOG.md # Version history +└── package.json # Root workspace (private: true) ``` ## Data flow From 4629504ee27c4e5f37781d40a38c1bb9a217997c Mon Sep 17 00:00:00 2001 From: Paradoxbound Date: Mon, 9 Mar 2026 21:56:03 +0000 Subject: [PATCH 6/6] docs: add OpenSSF Best Practices badge to README Shows progress towards the CII Best Practices (Bronze/Silver/Gold) badge alongside the existing OpenSSF Baseline Level 3 badge. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Paradoxbound --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 904e352..20b7337 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BookStack MCP Server -[![OpenSSF Baseline](https://www.bestpractices.dev/projects/12116/baseline)](https://www.bestpractices.dev/projects/12116) +[![OpenSSF Baseline](https://www.bestpractices.dev/projects/12116/baseline)](https://www.bestpractices.dev/projects/12116) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12116/badge)](https://www.bestpractices.dev/projects/12116) BookStack stores your team's knowledge — but AI assistants can't access it without an integration. BookStack MCP Server bridges that gap, connecting AI assistants (Claude Desktop, LibreChat, and any MCP-compatible client) directly to your BookStack instance so they can search, read, and manage your documentation through natural language.