Content
# bookstack-mcp-server-dotnet
[](https://github.com/MarkZither/bookstack-mcp-server-dotnet/actions/workflows/ci.yml)
[](https://marketplace.visualstudio.com/items?itemName=MarkZither.bookstack-mcp-server)
[](https://hub.docker.com/r/markzither/bookstack-mcp-server)
[](https://hub.docker.com/r/markzither/bookstack-mcp-server)
A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server providing full access to BookStack's knowledge management API in .NET 10. Gives AI assistants (GitHub Copilot, Claude, and others) direct access to your BookStack instance.
Inspired by <https://github.com/pnocera/bookstack-mcp-server>.
---
## Installation
### VS Code Extension (recommended — no .NET required)
Install from the VS Code Marketplace — the server binary is bundled:
[](https://marketplace.visualstudio.com/items?itemName=MarkZither.bookstack-mcp-server)
Then set three settings (`Ctrl+,` → search `bookstack`):
| Setting | Value |
|---------|-------|
| `bookstack.url` | `https://your-bookstack/` |
| `bookstack.tokenId` | Your BookStack API token ID |
| `bookstack.tokenSecret` | Your BookStack API token secret |
See [vscode-extension/README.md](vscode-extension/README.md) for full configuration reference.
### Docker (Streamable HTTP transport)
Run the server as a container — ideal for self-hosted AI assistants, Claude Desktop, or any client that supports Streamable HTTP MCP.
```bash
docker run -d \
-e BOOKSTACK_BASE_URL=https://your-bookstack/ \
-e BOOKSTACK_TOKEN_SECRET=your-token-id:your-token-secret \
-e BOOKSTACK_MCP_TRANSPORT=http \
-e BOOKSTACK_MCP_HTTP_AUTH_TOKEN=change-me \
-p 3000:3000 \
ghcr.io/markzither/bookstack-mcp-server:latest
```
Health check: `GET http://localhost:3000/health`
MCP endpoint: `http://localhost:3000/mcp` (Bearer token required if `BOOKSTACK_MCP_HTTP_AUTH_TOKEN` is set)
#### Docker Compose
Sample compose files are provided for common configurations:
| File | Description |
|------|-------------|
| [`docker-compose.yml`](docker-compose.yml) | HTTP transport, no vector search (simplest) |
| [`docker-compose.sqlite.yml`](docker-compose.sqlite.yml) | SQLite vector search + Ollama embeddings |
| [`docker-compose.postgres.yml`](docker-compose.postgres.yml) | PostgreSQL + pgvector + Ollama (all in containers) |
| [`docker-compose.postgres-external.yml`](docker-compose.postgres-external.yml) | PostgreSQL external connection string + Ollama |
Copy and edit the relevant file, then:
```bash
cp docker-compose.yml docker-compose.override.yml
# edit docker-compose.override.yml with your values
docker compose up -d
```
#### Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `BOOKSTACK_BASE_URL` | ✅ | Full URL of your BookStack instance, e.g. `https://demo.bookstackapp.com/` |
| `BOOKSTACK_TOKEN_SECRET` | ✅ | `tokenId:tokenSecret` from BookStack → Settings → API Tokens |
| `BOOKSTACK_MCP_TRANSPORT` | — | `stdio` (default) \| `http` \| `both` |
| `BOOKSTACK_MCP_HTTP_PORT` | — | HTTP listen port (default: `3000`) |
| `BOOKSTACK_MCP_HTTP_AUTH_TOKEN` | — | Bearer token for HTTP endpoint auth (recommended for HTTP transport) |
| `BOOKSTACK_SCOPED_BOOKS` | — | Comma-separated book IDs or slugs to restrict access |
| `BOOKSTACK_SCOPED_SHELVES` | — | Comma-separated shelf IDs or slugs to restrict access |
| `ConnectionStrings__VectorDb` | — | Connection string for vector database (default: `Data Source=bookstack-vectors.db`) |
| `VectorSearch__Enabled` | — | `true` to enable semantic search (default: `false`) |
| `VectorSearch__Database` | — | `Sqlite` (default) \| `Postgres` |
| `VectorSearch__EmbeddingProvider` | — | `Ollama` (default) \| `AzureOpenAI` |
| `VectorSearch__Ollama__BaseUrl` | — | Ollama base URL (default: `http://localhost:11434`) |
| `VectorSearch__Ollama__Model` | — | Ollama embedding model (default: `qllama/bge-large-en-v1.5`) — see [Embedding Model Selection](#embedding-model-selection) |
| `VectorSearch__Ollama__QueryPrefix` | — | Prefix prepended to query strings before embedding. Empty string for symmetric models (`qllama/bge-large-en-v1.5`, `nomic-embed-text`); set to `"Represent this sentence for searching relevant passages: "` when using `mxbai-embed-large`. |
| `VectorSearch__AzureOpenAI__Endpoint` | — | Azure OpenAI endpoint (when using AzureOpenAI provider) |
| `VectorSearch__AzureOpenAI__DeploymentName` | — | Azure OpenAI deployment name |
| `VectorSearch__AzureOpenAI__ApiKey` | — | Azure OpenAI API key |
| `VectorSearch__Sync__IntervalHours` | — | How often to sync embeddings (default: `24`) |
| `VectorSearch__Sync__BatchSize` | — | Pages per sync batch (default: `50`) |
### Embedding Model Selection
The embedding model determines vector quality and dimension. 21 configurations (3 models × 7 chunk sizes) were benchmarked against the v2 golden dataset (30 queries, ASP.NET Core / .NET developer knowledge) at the optimal chunk config (ChunkSize=256, ChunkOverlap=64):
| Model | Dimensions | Recall@1 | Recall@3 | MRR | p50 latency |
|-------|-----------|---------|---------|-----|-------------|
| `qllama/bge-large-en-v1.5` | 1024 | **0.6667** | **0.9000** | **0.7856** | 228ms |
| `mxbai-embed-large` | 1024 | 0.5667 | 0.9333 | 0.7556 | 288ms |
| `nomic-embed-text` | 768 | 0.4333 | 0.8000 | 0.6067 | 93ms |
**Default**: `qllama/bge-large-en-v1.5` — best Recall@1 (+10 pp over mxbai) and MRR. No query prefix required (symmetric model). `nomic-embed-text` is ~3× faster but noticeably lower quality.
Pull the default model before starting the server:
```bash
ollama pull qllama/bge-large-en-v1.5
```
#### Changing the embedding model
Switching to a model with a **different vector dimension** (e.g. 768 → 1024) requires:
1. Update `VectorSearch__Ollama__Model` (or `BOOKSTACK_VECTOR_OLLAMA_MODEL`) to the new model name.
2. Delete or replace the existing vector database file (`Data Source=bookstack-vectors.db`) — the schema is incompatible.
3. Restart the server; it will re-index all pages automatically.
> **Note:** The vector dimension is compiled into the schema. Changing between models with the **same** dimension (e.g. two 768-dim models) only requires updating the model name and a full re-index; no schema change is needed.
### Local Development (self-hosted BookStack + F5 debugging)
Run a full local stack — BookStack, MariaDB, and the MCP server — with no external dependencies.
**Prerequisites**: Docker Engine 20.10+, .NET 10 SDK, VS Code
```bash
git clone https://github.com/MarkZither/bookstack-mcp-server-dotnet.git
cd bookstack-mcp-server-dotnet
# Required: create your local env file (gitignored — never committed)
cp .env.dev.example .env.dev
# Required: generate a BookStack app key and append it to .env.dev
docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:latest appkey \
| sed 's/^/APP_KEY=/' >> .env.dev
```
Then press **F5** in VS Code. The pre-launch task starts BookStack and MariaDB, waits until they are healthy, and then launches the MCP server with the debugger attached.
On first run, open <http://localhost:6875>, log in with `admin@admin.com` / `password`, generate an API token (Profile → API Tokens → Add Token), and paste the token ID and secret into `.env.dev`.
See [docs/developer-guide.md](docs/developer-guide.md) for the full setup walkthrough including Ollama integration and the data seed script.
### Build from Source
**Prerequisites**: [.NET 10 SDK](https://dotnet.microsoft.com/download)
```bash
git clone https://github.com/MarkZither/bookstack-mcp-server-dotnet.git
cd bookstack-mcp-server-dotnet
dotnet build
dotnet test
```
---
## Available MCP Tools
### Content
| Category | Operations |
|----------|-----------|
| **Books** | list, create, read, update, delete, export |
| **Chapters** | list, create, read, update, delete |
| **Pages** | list, create, read, update, delete, export |
| **Shelves** | list, create, read, update, delete |
### Search
| Tool | Description |
|------|-------------|
| **Search** | Full-text search across all BookStack content |
| **Semantic Search** | Natural language vector search using AI embeddings (requires `VectorSearch__Enabled=true`) |
### Administration
| Category | Operations |
|----------|-----------|
| **Users** | list, create, read, update, delete |
| **Roles** | list, create, read, update, delete |
| **Attachments** | list, create, read, update, delete |
| **Images** | list, read, update, delete |
| **Permissions** | read and update content-level permissions |
| **Audit Logs** | list audit log entries |
| **Recycle Bin** | list, restore, and permanently delete items |
| **Server Info** | read BookStack server version and instance information |
---
## Vector Search
Semantic search lets AI assistants find pages by meaning rather than exact keywords. It is disabled by default and requires:
1. An embedding provider (Ollama with `qllama/bge-large-en-v1.5`, or Azure OpenAI)
2. A vector database (SQLite with sqlite-vec, or PostgreSQL with pgvector)
The server syncs page content to the vector database on a configurable schedule (`VectorSearch__Sync__IntervalHours`).
See the [docker-compose.sqlite.yml](docker-compose.sqlite.yml) and [docker-compose.postgres.yml](docker-compose.postgres.yml) files for ready-to-run examples.
### SQLite database file location
When using the SQLite provider the database is created at a predictable per-user location:
| Platform | Path |
|----------|------|
| Linux / macOS | `~/.local/share/bookstack-mcp/bookstack-vectors.db` |
| Windows | `%LOCALAPPDATA%\bookstack-mcp\bookstack-vectors.db` |
The VS Code Admin Panel (BookStack status bar → click) shows the resolved path and lets you reveal it in your file manager.
### Browsing the SQLite database
The `page_vectors` table is a **sqlite-vec virtual table** (`vec0` module). Generic SQLite browsers (DB Browser for SQLite, DBeaver, etc.) fail to open the file unless the sqlite-vec native extension is loaded first — the file itself is a standard SQLite database.
**What you can read without the extension:**
The `sync_metadata` table is a plain EF Core table and opens in any browser:
```bash
sqlite3 ~/.local/share/bookstack-mcp/bookstack-vectors.db "SELECT * FROM sync_metadata;"
```
**DB Browser for SQLite — loading the extension:**
1. Find the sqlite-vec native library alongside the server binary (e.g. in `publish-local/` or next to the extension binary):
```bash
find ~/.local/share/bookstack-mcp -name "*.so" 2>/dev/null
find "$(dirname $(which bookstack-mcp-server))" -name "*vec*" 2>/dev/null
```
2. In DB Browser for SQLite: **Edit → Preferences → Extensions → Add**, paste the `.so` / `.dll` path, click OK.
3. Re-open the database — the `vec0` virtual table will be recognised.
You can also use **Tools → Load Extension** (available in most builds) to load it on the fly for a single session.
---
## Scoped Access
Restrict the MCP server to specific books or shelves to limit what the AI can see and modify:
```bash
# Via environment variable (comma-separated IDs or slugs)
BOOKSTACK_SCOPED_BOOKS=architecture-decisions,runbooks
BOOKSTACK_SCOPED_SHELVES=engineering
```
```json
// Via VS Code settings
{
"bookstack.scopedBooks": ["architecture-decisions", "runbooks"],
"bookstack.scopedShelves": ["engineering"]
}
```
---
## Transports
| Transport | Use case |
|-----------|----------|
| `stdio` | VS Code extension, Claude Desktop (default) |
| `http` | Docker / self-hosted, remote AI clients |
| `both` | Run both simultaneously |
HTTP transport exposes a Streamable HTTP MCP endpoint at `/mcp` and a health check at `/health`. Protect it with `BOOKSTACK_MCP_HTTP_AUTH_TOKEN` (Bearer token).
---
## Development
```bash
dotnet build # also auto-wires the pre-commit hook (first run)
dotnet test --configuration Release
dotnet format # fix any formatting issues before committing
```
### Pre-commit hook
Formatting is enforced by [Husky.Net](https://alirezanet.github.io/Husky.Net/) — a cross-platform .NET git hooks tool.
It runs `dotnet format --verify-no-changes` via the task runner on every `git commit` and blocks the commit if issues are found.
The hook is **automatically installed** the first time you run `dotnet build` or `dotnet restore`.
[`Directory.Build.targets`](Directory.Build.targets) runs `dotnet tool restore && dotnet husky install` via MSBuild — no manual setup required.
| Platform | Requirement |
|----------|-------------|
| Linux / macOS | Works out of the box |
| Windows | Requires [Git for Windows](https://git-scm.com/download/win) — Git uses the bundled `sh.exe` to execute hook scripts |
To skip hooks temporarily (e.g. WIP commit): set `HUSKY=0` before committing.
If the hook blocks your commit, run `dotnet format`, re-stage the changed files, then commit again.
See [docs/](docs/) for architecture decisions, feature specs, and migration guides.
## Contributing
- [GitHub Issues](https://github.com/MarkZither/bookstack-mcp-server-dotnet/issues)
- Pull requests welcome — please open an issue first for significant changes.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available