{"_id":"bookstack-mcp-server","_rev":"4-8e20f6631aa1c3feeac5ff01ff9638a2","name":"bookstack-mcp-server","dist-tags":{"latest":"2.1.1"},"versions":{"1.0.0":{"name":"bookstack-mcp-server","version":"1.0.0","keywords":["mcp","bookstack","api","server","claude","context","documentation"],"author":{"name":"BookStack MCP Integration"},"license":"MIT","_id":"bookstack-mcp-server@1.0.0","maintainers":[{"name":"pnocera","email":"pnocera@gmail.com"}],"homepage":"https://github.com/pnocera/bookstack-mcp-server#readme","bugs":{"url":"https://github.com/pnocera/bookstack-mcp-server/issues"},"bin":{"bookstack-mcp-server":"dist/server.js"},"dist":{"shasum":"26fcf42f18a870fece7c254aa12369d80eaa6801","tarball":"https://registry.npmjs.org/bookstack-mcp-server/-/bookstack-mcp-server-1.0.0.tgz","fileCount":115,"integrity":"sha512-N77CUpEXHP/iBUpsoWxunj4Ad/3EENS05u/DDq5HplVInRu7+VcU+59tlGXRpw4LZGq8iut+ev2p9GBg3l6JNA==","signatures":[{"sig":"MEQCIGhdtxseoLia8fm+u5Y/TTb+yxpDaqHMXZtGk+Rp9cdpAiBhh4CQoX73nBrIbPosGiVt1WHL0fToEaqLUKAhzx/bQg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":450274},"jest":{"preset":"ts-jest","testMatch":["**/tests/**/*.test.ts"],"testEnvironment":"node","coverageDirectory":"coverage","coverageReporters":["text","lcov","html"],"collectCoverageFrom":["src/**/*.ts","!src/**/*.d.ts","!src/tests/**"]},"main":"dist/server.js","types":"./dist/server.d.ts","engines":{"npm":">=9.0.0","node":">=18.0.0"},"gitHead":"2dafc88ea606200841fdac444cdb2ffacc969eeb","scripts":{"dev":"ts-node src/server.ts","lint":"eslint src/**/*.ts","test":"jest","build":"npm run clean && tsc","clean":"rimraf dist","start":"node dist/server.js","watch":"nodemon --exec ts-node src/server.ts","format":"prettier --write src/**/*.ts","lint:fix":"eslint src/**/*.ts --fix","docker:run":"docker run -p 3000:3000 bookstack-mcp-server","test:watch":"jest --watch","docker:build":"docker build -t bookstack-mcp-server .","test:coverage":"jest --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"pnocera","actor":{"name":"pnocera","type":"user","email":"pnocera@gmail.com"},"email":"pnocera@gmail.com"},"repository":{"url":"git+https://github.com/pnocera/bookstack-mcp-server.git","type":"git"},"_npmVersion":"10.9.2","description":"MCP server providing comprehensive access to BookStack knowledge management system","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.22.4","cors":"^2.8.5","uuid":"^9.0.1","axios":"^1.6.0","dotenv":"^16.3.1","helmet":"^7.1.0","express":"^4.18.2","winston":"^3.10.0","compression":"^1.7.4","@modelcontextprotocol/sdk":"^1.0.0"},"preferGlobal":true,"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","rimraf":"^5.0.5","nodemon":"^3.0.1","ts-jest":"^29.1.1","ts-node":"^10.9.1","prettier":"^3.0.3","supertest":"^6.3.3","typescript":"^5.2.2","@types/cors":"^2.8.14","@types/jest":"^29.5.5","@types/node":"^20.8.0","@types/uuid":"^9.0.5","@types/express":"^4.17.17","@types/supertest":"^2.0.12","@types/compression":"^1.7.3","@typescript-eslint/parser":"^6.7.0","@typescript-eslint/eslint-plugin":"^6.7.0"},"_npmOperationalInternal":{"tmp":"tmp/bookstack-mcp-server_1.0.0_1751632190992_0.5705898871290915","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"bookstack-mcp-server","version":"2.0.0","keywords":["mcp","bookstack","api","server","claude","context","documentation","bun"],"author":{"name":"BookStack MCP Integration"},"license":"MIT","_id":"bookstack-mcp-server@2.0.0","maintainers":[{"name":"pnocera","email":"pnocera@gmail.com"}],"homepage":"https://github.com/pnocera/bookstack-mcp-server#readme","bugs":{"url":"https://github.com/pnocera/bookstack-mcp-server/issues"},"bin":{"bookstack-mcp-server":"src/server.ts"},"dist":{"shasum":"361a28f3062a52dac7f3f402b4b5a28afd5701dd","tarball":"https://registry.npmjs.org/bookstack-mcp-server/-/bookstack-mcp-server-2.0.0.tgz","fileCount":34,"integrity":"sha512-U5FqOyhSscWSvCpRyoMZCWdVjJwkkMk3XKhSLNpntE1Y92qx25Dc9jN4pR3vZDyykjFkKjB/oVRO9TQJRH5ETA==","signatures":[{"sig":"MEUCIGOx6pT1YquWJiioJDsYHAlIkz/9S8bq214UfEttXHhGAiEA9iGIpUtvqUbqkgsnafLew4ilklZlrXfbdhUUHRLutVo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/bookstack-mcp-server@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":534198},"main":"src/server.ts","type":"module","module":"src/server.ts","engines":{"bun":">=1.1.0"},"gitHead":"f9882b5696cf99591514d2312f79c4c351fef572","scripts":{"dev":"bun --watch src/server.ts","lint":"biome check .","test":"bun test","build":"bun build ./src/server.ts --target bun --outdir ./dist","clean":"rm -rf dist","start":"bun run src/server.ts","format":"biome format --write .","lint:fix":"biome check --write .","typecheck":"tsc --noEmit","docker:run":"docker run --rm -p 3000:3000 --env-file .env bookstack-mcp-server","test:watch":"bun test --watch","docker:build":"docker build -t bookstack-mcp-server .","test:coverage":"bun test --coverage","integration:up":"bun run scripts/integration-up.ts","prepublishOnly":"bun run typecheck","test:integration":"RUN_INTEGRATION=1 bun test tests/integration"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3ed86d09-fffd-4b10-aeb1-6fb61e9dc789"}},"repository":{"url":"git+https://github.com/pnocera/bookstack-mcp-server.git","type":"git"},"_npmVersion":"11.6.2","description":"MCP server providing comprehensive access to BookStack knowledge management system","directories":{},"_nodeVersion":"24.9.0","dependencies":{"zod":"4.4.3","axios":"1.18.1","dotenv":"17.4.2","express":"5.2.1","winston":"3.19.0","@modelcontextprotocol/sdk":"1.29.0"},"_hasShrinkwrap":false,"devDependencies":{"ajv":"8.20.0","@types/bun":"1.3.14","typescript":"7.0.2","@types/node":"26.1.1","@biomejs/biome":"2.5.4","@types/express":"5.0.6"},"_npmOperationalInternal":{"tmp":"tmp/bookstack-mcp-server_2.0.0_1784297675749_0.6230267904839397","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"bookstack-mcp-server","version":"2.1.0","keywords":["mcp","bookstack","api","server","claude","context","documentation","bun"],"author":{"name":"BookStack MCP Integration"},"license":"MIT","_id":"bookstack-mcp-server@2.1.0","maintainers":[{"name":"pnocera","email":"pnocera@gmail.com"}],"homepage":"https://github.com/pnocera/bookstack-mcp-server#readme","bugs":{"url":"https://github.com/pnocera/bookstack-mcp-server/issues"},"bin":{"bookstack-mcp-server":"src/server.ts"},"dist":{"shasum":"141b7a541c0ee568eddcbc0df34b65f94c08130b","tarball":"https://registry.npmjs.org/bookstack-mcp-server/-/bookstack-mcp-server-2.1.0.tgz","fileCount":35,"integrity":"sha512-d0ul8YGUXMybqKEaXawsE1ZAu/CSGYfbRwkX8y27KSlP6FH4cvp9DHK7nybBYKXvZpfiRNhcanT1UlaT63sqTg==","signatures":[{"sig":"MEQCIGVOfECphxS5XXPJ5ZUCwD0353U7tR0MiCiMKuFhplfnAiAPuLFNrrm6k0eddQZiw+AmezSVLTZrArd33IbkMW8d6Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/bookstack-mcp-server@2.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":589959},"main":"src/server.ts","type":"module","module":"src/server.ts","engines":{"bun":">=1.1.0"},"gitHead":"60bc5349d27cd57973d46cba782ecd4fa7c4cdb6","scripts":{"dev":"bun --watch src/server.ts","lint":"biome check .","test":"bun test","build":"bun build ./src/server.ts --target bun --outdir ./dist","clean":"rm -rf dist","start":"bun run src/server.ts","format":"biome format --write .","lint:fix":"biome check --write .","typecheck":"tsc --noEmit","docker:run":"docker run --rm -p 3000:3000 --env-file .env bookstack-mcp-server","test:watch":"bun test --watch","docker:build":"docker build -t bookstack-mcp-server .","test:coverage":"bun test --coverage","integration:up":"bun run scripts/integration-up.ts","prepublishOnly":"bun run typecheck","test:integration":"RUN_INTEGRATION=1 bun test tests/integration"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3ed86d09-fffd-4b10-aeb1-6fb61e9dc789"}},"repository":{"url":"git+https://github.com/pnocera/bookstack-mcp-server.git","type":"git"},"_npmVersion":"11.6.2","description":"MCP server providing comprehensive access to BookStack knowledge management system","directories":{},"_nodeVersion":"24.9.0","dependencies":{"zod":"4.4.3","axios":"1.18.1","dotenv":"17.4.2","express":"5.2.1","winston":"3.19.0","@modelcontextprotocol/sdk":"1.29.0"},"_hasShrinkwrap":false,"devDependencies":{"ajv":"8.20.0","@types/bun":"1.3.14","typescript":"7.0.2","@types/node":"26.1.1","@biomejs/biome":"2.5.4","@types/express":"5.0.6"},"_npmOperationalInternal":{"tmp":"tmp/bookstack-mcp-server_2.1.0_1787381918743_0.21001147643805074","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"_id":"bookstack-mcp-server@2.1.1","bin":{"bookstack-mcp-server":"src/server.ts"},"bugs":{"url":"https://github.com/pnocera/bookstack-mcp-server/issues"},"dist":{"shasum":"ffedd750e9aefa0c8b2fd70cdfc63586fc005768","tarball":"https://registry.npmjs.org/bookstack-mcp-server/-/bookstack-mcp-server-2.1.1.tgz","fileCount":35,"integrity":"sha512-yr4XFEuVS1Ta/gyNUznEEJw0723o+IqKi3kU3TDjFvTCLEtCpuW2ft8MWpXdSRvziAYtm8QWozOzSWiGB2PY9g==","signatures":[{"sig":"MEYCIQCBpPX3n1yYPsLkOzfeB3qQ7fsHVZvZXjBX40VH4VYgZAIhANrni0Uy4ZBhAJ+yNQT1XVkfbFW2mE96NFCOHGt5Ot/z","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDIs415pnAxdIi0Xu+95AflmPI82YkuJuGrJ70UjuXMBwIhAIt3zl8W1DXKDT++q9mavw+0w748e39iB0zYAeZj7qRx"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/bookstack-mcp-server@2.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":590167},"main":"src/server.ts","name":"bookstack-mcp-server","type":"module","author":{"name":"BookStack MCP Integration"},"module":"src/server.ts","engines":{"bun":">=1.1.0"},"gitHead":"ae27f476f08ec95fee750b1d111af814977e93f4","license":"MIT","scripts":{"dev":"bun --watch src/server.ts","lint":"biome check .","test":"bun test","build":"bun build ./src/server.ts --target bun --outdir ./dist","clean":"rm -rf dist","start":"bun run src/server.ts","format":"biome format --write .","lint:fix":"biome check --write .","typecheck":"tsc --noEmit","docker:run":"docker run --rm -p 3000:3000 --env-file .env bookstack-mcp-server","test:watch":"bun test --watch","docker:build":"docker build -t bookstack-mcp-server .","test:coverage":"bun test --coverage","integration:up":"bun run scripts/integration-up.ts","prepublishOnly":"bun run typecheck","test:integration":"RUN_INTEGRATION=1 bun test tests/integration"},"version":"2.1.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"3ed86d09-fffd-4b10-aeb1-6fb61e9dc789"}},"homepage":"https://github.com/pnocera/bookstack-mcp-server#readme","keywords":["mcp","bookstack","api","server","claude","context","documentation","bun"],"repository":{"url":"git+https://github.com/pnocera/bookstack-mcp-server.git","type":"git"},"_npmVersion":"11.6.2","description":"MCP server providing comprehensive access to BookStack knowledge management system","directories":{},"maintainers":[{"name":"pnocera","email":"pnocera@gmail.com"}],"_nodeVersion":"24.9.0","dependencies":{"zod":"4.4.3","axios":"1.18.1","dotenv":"17.4.2","express":"5.2.1","winston":"3.19.0","@modelcontextprotocol/sdk":"1.29.0"},"_hasShrinkwrap":false,"devDependencies":{"ajv":"8.20.0","@types/bun":"1.3.14","typescript":"7.0.2","@types/node":"26.1.1","@biomejs/biome":"2.5.4","@types/express":"5.0.6"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bookstack-mcp-server_2.1.1_1792953892092_0.22841125454771882"}}},"time":{"created":"2025-07-04T12:29:50.991Z","modified":"2026-10-25T18:44:52.538Z","1.0.0":"2025-07-04T12:29:51.185Z","2.0.0":"2026-07-17T14:14:35.881Z","2.1.0":"2026-08-22T06:58:38.889Z","2.1.1":"2026-10-25T18:44:52.193Z"},"bugs":{"url":"https://github.com/pnocera/bookstack-mcp-server/issues"},"author":{"name":"BookStack MCP Integration"},"license":"MIT","homepage":"https://github.com/pnocera/bookstack-mcp-server#readme","keywords":["mcp","bookstack","api","server","claude","context","documentation","bun"],"repository":{"url":"git+https://github.com/pnocera/bookstack-mcp-server.git","type":"git"},"description":"MCP server providing comprehensive access to BookStack knowledge management system","maintainers":[{"name":"pnocera","email":"pnocera@gmail.com"}],"readme":"# BookStack MCP Server\n\nConnect BookStack to Claude and other AI assistants through the Model Context Protocol (MCP). This server exposes 59 tools and 11 resources covering the supported subset of the BookStack API — books, pages, chapters, shelves, search, users, roles, permissions, attachments, images, the recycle bin, the audit log and system info.\n\nThis server supports two transport modes: **Streamable HTTP** (default) and **Stdio**.\n\n- **Streamable HTTP (default)**: A stateless HTTP transport. Authentication parameters can be overridden per-request using HTTP headers (`x-bookstack-url` and `x-bookstack-token`).\n- **Stdio Mode**: Standard input/output for local integration (e.g., with Claude Desktop). Set `MCP_TRANSPORT=stdio` to enable.\n\n> ⚠️ **Looking for the HTTP endpoint?** The MCP endpoint is `POST /message` — not `/`.\n> See [Transports](#-transports) and [HTTP endpoints](#http-endpoints) below.\n\n## ✨ What You Get\n\n- **BookStack Integration** - Access your books, pages, chapters, and content\n- **59 MCP Tools & 11 Resources** - CRUD, search and export across the supported endpoint families\n- **Search & Export** - Find content and export in multiple formats\n- **User Management** - Handle users, roles, and permissions\n- **Production Ready** - Rate limiting, validation, error handling, and logging\n\n## 🚀 Quick Start\n\n> ⚠️ **Requires [Bun](https://bun.sh) 1.1.0 or newer. Node.js is not supported.**\n> This package ships TypeScript source rather than a compiled bundle, and its\n> executable starts with `#!/usr/bin/env bun` — Bun must be installed on the\n> machine that runs it. `npx`/`npm install -g` will not work.\n\nConfigure first — the default HTTP transport **refuses to start** until both tokens\nbelow are set:\n\n```bash\n# 1. Configure\nexport BOOKSTACK_BASE_URL=\"https://your-bookstack.com/api\"\nexport BOOKSTACK_API_TOKEN=\"token_id:token_secret\"   # OUTBOUND: the credential this server spends\nexport MCP_AUTH_TOKEN=\"$(openssl rand -hex 32)\"      # INBOUND: who may make it spend that credential\n\n# 2. Run without installing (starts the HTTP server on port 3000)\nbunx bookstack-mcp-server\n\n# Or install globally, then run it by name\nbun add -g bookstack-mcp-server\nbookstack-mcp-server\n```\n\nThe two tokens are **not** interchangeable and must not be set to the same value:\n`BOOKSTACK_API_TOKEN` is what the server presents to BookStack; `MCP_AUTH_TOKEN` is\nwhat callers must present to `POST /message`, which dispatches all 59 tools with the\nauthority of the BookStack account behind `BOOKSTACK_API_TOKEN`. Skip `MCP_AUTH_TOKEN`\nonly for [stdio](#-transports), which has no network surface and ignores it.\n\n**Check it started:**\n\n```bash\ncurl http://localhost:3000/          # => {\"status\":\"running\", ...}\ncurl -i http://localhost:3000/health # => 200 healthy, or 503 if BookStack is unreachable\n```\n\n### Add to Claude\n\nTo use with Claude Desktop (requires Stdio mode):\n\n```bash\n# For Claude Code\nclaude mcp add bookstack bunx bookstack-mcp-server \\\n  --env BOOKSTACK_BASE_URL=https://your-bookstack.com/api \\\n  --env BOOKSTACK_API_TOKEN=token_id:token_secret \\\n  --env MCP_TRANSPORT=stdio\n```\n\n### Configuration\n\nSet these environment variables:\n\n```bash\nexport BOOKSTACK_BASE_URL=\"https://your-bookstack.com/api\"\nexport BOOKSTACK_API_TOKEN=\"token_id:token_secret\"\n\n# Required for the HTTP transport (the default); ignored by stdio.\nexport MCP_AUTH_TOKEN=\"$(openssl rand -hex 32)\"\n\n# Optional: transport mode — \"http\" (default) or \"stdio\"\nexport MCP_TRANSPORT=\"http\"\n```\n\n> 💡 **Token Format**: Combine your BookStack Token ID and Token Secret as `token_id:token_secret`\n\n#### Environment variables\n\n| Variable | Default | Description |\n| --- | --- | --- |\n| `MCP_TRANSPORT` | `http` | Transport mode. Only the exact value `stdio` selects stdio; **any other value (or unset) starts the HTTP server**. |\n| `MCP_AUTH_TOKEN` | _(none — **required** for HTTP)_ | **Inbound** secret callers must present as `Authorization: Bearer <value>` on `POST /message`. The HTTP transport **refuses to start** without it — there is no \"no auth\" mode. Unrelated to `BOOKSTACK_API_TOKEN`, and must not be the same value. Ignored in stdio mode. Generate with `openssl rand -hex 32`. |\n| `BOOKSTACK_BASE_URL` | `http://localhost:8080/api` | Full URL to the BookStack API. Must be a valid URL and include the `/api` suffix. |\n| `BOOKSTACK_API_TOKEN` | _(none — required)_ | **Outbound** BookStack API token as `token_id:token_secret`. This is the credential the server spends on every tool call. Startup fails if unset. |\n| `BOOKSTACK_TIMEOUT` | `30000` | BookStack request timeout in milliseconds. |\n| `SERVER_PORT` | `3000` | Port the HTTP transport listens on. Ignored in stdio mode. |\n| `HTTP_BODY_LIMIT` | `73400320` (70 MiB) | Maximum accepted `POST /message` body, in bytes. Sized for the largest inline base64 upload the image/attachment tools advertise (50,000 KB). Express's own default is ~100 KB, which would reject real uploads with a `413`. Lower it if untrusted callers can reach the port. |\n| `SERVER_NAME` | `bookstack-mcp-server` | Server name reported over MCP and by `GET /`. |\n| `SERVER_VERSION` | the package's own version | Version reported over MCP `initialize`, by `GET /`, and by `bookstack_server_info`. Defaults to `package.json#version`; leave it unset unless you deliberately want a different value. |\n| `RATE_LIMIT_REQUESTS_PER_MINUTE` | `60` | Outbound rate limit toward BookStack. |\n| `RATE_LIMIT_BURST_LIMIT` | `10` | Outbound burst allowance toward BookStack. |\n| `VALIDATION_ENABLED` | `true` | Input validation. Set to `false` to disable. |\n| `VALIDATION_STRICT_MODE` | `true` | Reject invalid tool params at the boundary. Set to `false` to log a warning and forward them to BookStack instead. |\n| `LOG_LEVEL` | `info` | One of `error`, `warn`, `info`, `debug`. |\n| `LOG_FORMAT` | `pretty` | One of `json`, `pretty`. |\n| `NODE_ENV` | `development` | One of `development`, `production`, `test`. |\n| `DEBUG` | `false` | Set to `true` for debug output. |\n\n> 💡 Need detailed setup? See the complete [Setup Guide](docs/setup-guide.md)\n\n## 🔌 Transports\n\nThe transport is chosen at startup from `MCP_TRANSPORT`:\n\n| `MCP_TRANSPORT` | Result |\n| --- | --- |\n| unset (**default**) | Streamable HTTP server on `SERVER_PORT` (default `3000`) |\n| `http` | Same as unset |\n| `stdio` | Stdio transport — reads MCP messages from stdin |\n\n**Stdio is opt-in.** If you do not set `MCP_TRANSPORT=stdio`, you get the HTTP server.\n\n### HTTP endpoints\n\nWhen running in HTTP mode the server exposes exactly three endpoints. Any other\npath returns a JSON `404` listing the valid ones.\n\n| Method & path | Purpose | Status codes |\n| --- | --- | --- |\n| `GET /` | Server info JSON (name, version, `status: \"running\"`, endpoint list) | `200` |\n| `GET /health` | Health check — verifies live connectivity to BookStack | `200` healthy, `503` unhealthy |\n| `POST /message` | **The MCP endpoint.** Send JSON-RPC MCP messages here | `200`, `401` without a valid bearer token, `500` on error |\n\n`GET /` and `GET /health` are unauthenticated. **`POST /message` requires an inbound\n`Authorization: Bearer <secret>` header** — it dispatches every tool, including\npermanent-delete and user/role operations, so the HTTP transport refuses to start\nwithout a secret configured. The startup error names the exact variable to set; the\n`stdio` transport has no network surface and needs none.\n\n**Check the server is up:**\n\n```bash\ncurl http://localhost:3000/\n```\n\n```json\n{\n  \"name\": \"bookstack-mcp-server\",\n  \"version\": \"1.0.0\",\n  \"status\": \"running\",\n  \"mcp\": true,\n  \"endpoints\": {\n    \"health\": \"/health\",\n    \"message\": \"/message (POST, requires an Authorization: Bearer header)\"\n  },\n  \"documentation\": \"Send MCP protocol messages to POST /message\"\n}\n```\n\n**Check health:**\n\n```bash\ncurl -i http://localhost:3000/health\n```\n\n`/health` verifies live connectivity to BookStack, so a **wrong** token returns `503`\nwith the failing check named:\n\n```json\n{\n  \"status\": \"unhealthy\",\n  \"checks\": [\n    { \"name\": \"bookstack_connection\", \"healthy\": false, \"message\": \"BookStack API connection\" },\n    { \"name\": \"tools_loaded\", \"healthy\": true, \"message\": \"59 tools loaded\" },\n    { \"name\": \"resources_loaded\", \"healthy\": true, \"message\": \"11 resources loaded\" }\n  ]\n}\n```\n\n> ⚠️ A **missing** `BOOKSTACK_API_TOKEN` behaves differently: config validation rejects\n> an empty token at startup, so the process exits with\n> `Configuration validation failed: bookstack.apiToken: BookStack API token is required`\n> before Express ever listens. There is no `/health` to call — `curl` gets a connection\n> refused, and under Docker the container restart-loops. A `503` therefore always means\n> the token is **present but not working**; a dead port means it is **absent**.\n\n**Call the MCP endpoint** — an `initialize` handshake. Both the `Content-Type`\nand `Accept` headers are required by the Streamable HTTP transport, and\n`Authorization` carries the same `MCP_AUTH_TOKEN` you exported in the quick start:\n\n```bash\n# Fail fast rather than sending an empty bearer header and puzzling over a 401.\n: \"${MCP_AUTH_TOKEN:?export MCP_AUTH_TOKEN first — the inbound secret this server was started with}\"\n\ncurl -X POST http://localhost:3000/message \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -H \"Authorization: Bearer $MCP_AUTH_TOKEN\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"initialize\",\n    \"params\": {\n      \"protocolVersion\": \"2024-11-05\",\n      \"capabilities\": {},\n      \"clientInfo\": { \"name\": \"curl\", \"version\": \"1.0.0\" }\n    }\n  }'\n```\n\nPer-request credential overrides are supported on `POST /message` via the\n`x-bookstack-url` and `x-bookstack-token` headers; both fall back to the\n`BOOKSTACK_BASE_URL` / `BOOKSTACK_API_TOKEN` environment variables.\n\n### Using with n8n\n\nPoint n8n's MCP client at the `/message` path — not the root URL:\n\n```\nhttp://<host>:3000/message\n```\n\nUse the host as n8n sees it: `http://localhost:3000/message` when n8n runs on\nthe same machine, or the container/service name (e.g. `http://mcp:3000/message`)\nwhen both run in Docker on a shared network.\n\n### Running stdio in Docker\n\nA stdio MCP server reads requests from **stdin** — so `docker run` without `-i`\ngives it no stdin, stdin hits EOF immediately, and the container exits on\nstartup. Pass `-i` to keep stdin attached:\n\n```bash\ndocker run -i --rm \\\n  -e MCP_TRANSPORT=stdio \\\n  -e BOOKSTACK_BASE_URL=https://your-bookstack.com/api \\\n  -e BOOKSTACK_API_TOKEN=token_id:token_secret \\\n  bookstack-mcp-server\n```\n\nUse `-i` alone, **not** `-it`: allocating a TTY breaks the JSON-RPC stream that\nMCP clients pipe over stdin/stdout. For stdio you also don't need `-p 3000:3000`\n— nothing listens on a port in stdio mode.\n\n> If your container \"starts then dies immediately\" with `MCP_TRANSPORT=stdio`,\n> a missing `-i` is almost always the cause.\n\n## 🛠️ Available Tools\n\n**59 tools across 13 categories:**\n\n- **📚 Books** (6) - Create, read, update, delete, and export books\n- **📄 Pages** (9) - Manage pages with HTML/Markdown content, including [partial editing](#-partial-page-editing)\n- **📑 Chapters** (6) - Organize pages within books\n- **📚 Shelves** (5) - Group books into collections\n- **🔍 Search** (1) - Search across content types\n- **👥 Users** (5) - User account management\n- **🎭 Roles** (5) - Roles and their permissions\n- **⚙️ System** (2) - Instance info and the audit log\n- **🔐 Permissions** (2) - Content access control\n- **🗑️ Recycle Bin** (3) - Deleted item recovery\n- **📎 Attachments** (5) - File attachments\n- **🖼️ Images** (5) - Image gallery\n- **🧭 Meta** (5) - Ask the server about its own tools and conventions\n\nNot exposed (no tools): comments, imports, tag-name listings, the image-gallery\n`data` endpoints, and `zip` export.\n\n> 📖 See the complete [Tools Overview](docs/tools-overview.md) for detailed documentation\n\n## ✂️ Partial page editing\n\nThe BookStack API has no PATCH for page content — `PUT /api/pages/{id}` takes a complete\n`html` or `markdown` body. Changing one paragraph of a long page therefore meant reading all\nof it, having the model reproduce it verbatim with the change applied, and sending it all\nback: the content crosses the model twice, and the whole page rides on it being copied\nbyte-perfectly.\n\nThree tools run that read-modify-write cycle inside the server instead, so a caller sends\nonly the fragment it wants changed:\n\n| Tool | What it does |\n|---|---|\n| `bookstack_pages_outline` | Heading structure with offsets and section sizes. No content transferred. |\n| `bookstack_pages_read` with `grep` | Matching excerpts from the **stored** source — paste one straight into `old_string`. |\n| `bookstack_pages_edit` | Literal find-and-replace. `old_string` must match exactly and be unique unless `replace_all` is set. |\n| `bookstack_pages_append` | Insert at the end of the page, the end of a named section, or right after a section heading. |\n\n```jsonc\n// 1. What sections exist, and how big are they?\n{ \"tool\": \"bookstack_pages_outline\", \"arguments\": { \"id\": 12 } }\n\n// 2. Get an exact anchor without loading the page\n{ \"tool\": \"bookstack_pages_read\",\n  \"arguments\": { \"id\": 12, \"grep\": \"retention period\", \"context\": 300 } }\n\n// 3. Rehearse: nothing is written\n{ \"tool\": \"bookstack_pages_edit\",\n  \"arguments\": { \"id\": 12, \"dry_run\": true,\n    \"edits\": [{ \"old_string\": \"retention period of 6 months\",\n                \"new_string\": \"retention period of 24 months\" }] } }\n\n// 4. Apply with a stale-page preflight\n{ \"tool\": \"bookstack_pages_edit\",\n  \"arguments\": { \"id\": 12, \"expected_updated_at\": \"2026-08-17T09:12:44.000000Z\",\n    \"edits\": [{ \"old_string\": \"retention period of 6 months\",\n                \"new_string\": \"retention period of 24 months\" }] } }\n```\n\n**Guards.** An ambiguous anchor is refused rather than applied to the wrong place, and the\nerror carries the first few matches with context. A missing anchor reports the same text found\nwith different whitespace, which is the usual near-miss. A result smaller than half the\noriginal is refused unless `allow_shrink` is set. After a write the page is re-read and the\nchange is looked for in normalised text — BookStack rewrites stored HTML on save (heading\nanchors, injected `id` attributes), so a byte comparison would call every success a failure.\nEvery write creates a BookStack revision, so an applied edit can be rolled back in the UI.\nNo response from these tools contains page content.\n`expected_updated_at` detects a page changed before this server reads it; BookStack's page\nAPI does not provide an atomic version condition, so it cannot prevent a write that races\nafter that check.\n\n**Two invariants**, if you touch this code (`src/utils/page-content.ts`): markdown pages are\npatched and written through `markdown`, because writing `html` to one switches the page's\neditor type; every other page is patched against `raw_html`, never the rendered `html` —\npatching the rendered output would write back expanded page-include tags and destroy the\nincludes permanently.\n\n`bookstack_pages_update` is unchanged and still replaces the whole content field; these tools\nare additive. `bookstack_pages_read` called without any of the new options returns exactly\nwhat it always did.\n\n## 📚 Documentation\n\nFind comprehensive guides in the `docs/` folder:\n\n- **[Setup Guide](docs/setup-guide.md)** - Complete installation and configuration\n- **[API Reference](docs/api-reference.md)** - Supported tools/endpoints with examples\n- **[Tools Overview](docs/tools-overview.md)** - Every tool explained\n- **[Resources Guide](docs/resources-guide.md)** - Resource access patterns\n- **[Examples & Workflows](docs/examples-and-workflows.md)** - Real-world usage\n- **[Integration Testing](docs/integration-testing.md)** - Running the live suite against a real BookStack\n- **[Releasing](docs/releasing.md)** - How versions are cut and published\n\n## ⚡ Quick Examples\n\n**List all books:**\n```javascript\nbookstack_books_list({ count: 10, sort: \"updated_at\" })\n```\n\n**Create a new page:**\n```javascript\nbookstack_pages_create({\n  name: \"Getting Started\",\n  book_id: 1,\n  markdown: \"# Welcome\\nYour content here...\"\n})\n```\n\n**Search for content:**\n```javascript\nbookstack_search({ query: \"API documentation\", count: 20 })\n```\n\n## 🛠️ Development\n\nThis project is Bun-native — Bun runs the TypeScript source directly, so there is\nno compile step.\n\n```bash\ngit clone <repository-url>\ncd bookstack-mcp-server\nbun install\nbun run dev          # hot reload; equivalent to: bun --watch src/server.ts\n```\n\n```bash\nbun run src/server.ts   # start the server\nbun test                # run tests\nbun run typecheck       # tsc --noEmit\nbun run lint            # biome check .\n```\n\n> 🔧 See the [Setup Guide](docs/setup-guide.md) for development, Docker, and production deployment\n\n## 🐳 Local testing with Docker Compose\n\nThe included `docker-compose.yml` spins up a full local stack — MariaDB, a real\nBookStack instance, and this MCP server (built from the Bun `Dockerfile`).\n\n1. **Start the backing services:**\n\n   ```bash\n   docker compose up -d db bookstack\n   ```\n\n2. **Wait for BookStack** to finish first-boot migrations, then open\n   <http://localhost:6875>. Default linuxserver credentials:\n\n   - Email: `admin@admin.com`\n   - Password: `password`\n\n3. **Create an API token** in the UI (Edit Profile → **API Tokens** → *Create\n   Token*). Combine the Token ID and Token Secret as `token_id:token_secret` and\n   put it in a `.env` file next to `docker-compose.yml`, together with an inbound\n   secret of your own:\n\n   ```bash\n   echo \"BOOKSTACK_API_TOKEN=token_id:token_secret\" > .env\n   echo \"MCP_AUTH_TOKEN=$(openssl rand -hex 32)\" >> .env\n   ```\n\n   > The token can only be created after BookStack is running, so it cannot be\n   > baked into the image — this manual step is required once.\n   >\n   > **Both** entries are required. `docker-compose.yml` passes `MCP_AUTH_TOKEN`\n   > through to the `mcp` service, and the HTTP transport refuses to start without\n   > it, so a `.env` carrying only `BOOKSTACK_API_TOKEN` leaves the container in a\n   > restart loop.\n\n4. **Start the MCP server** (it reads both tokens from `.env`):\n\n   ```bash\n   docker compose up -d mcp\n   ```\n\n5. **Check health** — returns `200` with `{\"status\":\"healthy\"}` once the server\n   can reach BookStack with your token:\n\n   ```bash\n   curl http://localhost:3000/health\n   ```\n\nUntil a **valid** `BOOKSTACK_API_TOKEN` is supplied the `mcp` container reports\n`unhealthy`, because `/health` verifies live connectivity to BookStack. If either token is\n**missing entirely** the container does not get that far and restart-loops instead of\nanswering `503` — check `docker compose logs mcp` for `Configuration validation failed`\n(no `BOOKSTACK_API_TOKEN`) or `MCP_AUTH_TOKEN is not set` (no inbound secret).\n\nThe compose file pins BookStack to `lscr.io/linuxserver/bookstack:version-v26.05.2` — the\nrelease this repo's tool contract was verified against — and ships a throwaway dev\n`APP_KEY`. Generate your own for anything beyond local testing, using the same pinned tag:\n\n```bash\ndocker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:version-v26.05.2 appkey\n```\n\nIt prints one `base64:…` line to paste into `APP_KEY`. The `--entrypoint` override is\nrequired: without it the image runs its normal init first, which halts with\n`The application key is missing, halting init!` — the very key you are trying to\ngenerate — and never reaches the `appkey` script.\n\n## 📝 License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## 🌟 Community\n\nThis project is part of the BookStack ecosystem! Check out other API-based tools and scripts in the [BookStack API Scripts](https://codeberg.org/bookstack/api-scripts) repository.\n\n## 🆘 Support\n\n- **📚 Documentation**: Complete guides in the [docs/](docs/) folder\n- **🐛 Issues**: [GitHub Issues](https://github.com/pnocera/bookstack-mcp-server/issues)\n- **💬 Discussions**: [GitHub Discussions](https://github.com/pnocera/bookstack-mcp-server/discussions)\n\n---\n\n**Built with ❤️ for the BookStack community**\n","readmeFilename":"README.md"}