- Go 99.2%
- Makefile 0.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- internal/wikijs: new uploadLimit(ctx) probes the site config once per client and falls back to DefaultMaxUploadBytes (5 MiB) when the API key lacks the manage:system permission - truncate() is now rune-safe: it decodes the last rune instead of splitting UTF-8 sequences at byte boundaries - internal/server: validateAssetFolderPath delegates to wikijs.AssetFolderPathSegments, removing the duplicated slug regex - tests: cover the upload limit probe, empty-file rejection, and the renamed contract between handler validation and the resolver |
||
| .github/workflows | ||
| cmd/wikijs-mcp | ||
| internal | ||
| .gitignore | ||
| AGENTS.md | ||
| docker-compose.yml | ||
| env.example | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
WikiJS MCP Server
A Model Context Protocol server for WikiJS. It lets MCP clients such as Claude Code, Claude Desktop, Cursor, or any custom agent search and read a WikiJS knowledge base, and optionally create or edit pages.
This is a Go rewrite of cahaseler/wikijs-mcp. It has no Node.js runtime, no npm install step, and no GraphQL code generation; the result is a single static binary.
Features
search_pages,get_page_by_id,get_page_by_path, andget_all_pagesfor reading a wiki.list_assetsandlist_asset_foldersto browse uploaded files, andupload_asset/create_asset_folderwhen editing is explicitly enabled.- Every asset result includes an absolute URL and a ready-to-use markdown reference.
- Absolute page URLs in every result.
- stdio transport for local MCP clients.
- Streamable HTTP transport with bearer-token authentication required for non-loopback binds.
- Structured MCP output (
structuredContent) plus JSON text for older clients. - Configuration through environment variables or an optional
.envfile; secrets never appear in flags.
Requirements
- Go 1.25 or newer to build. The result is a single binary with no runtime dependencies.
- A WikiJS instance and an API key. In WikiJS, open Administration → API
Access, enable the API, create a key, and copy it. Read access is enough for
the read tools;
create_page,edit_page, and asset uploads need write access (write:assetspermission for uploads).
Build
From the repository root:
make build
The binary is written to bin/wikijs-mcp. You can also install it directly:
go install ./cmd/wikijs-mcp
Set the required environment variables:
export WIKIJS_URL=https://wiki.example.com
export WIKIJS_API_KEY=your-api-key
WIKIJS_URL is the base URL of the wiki. A trailing slash or a /graphql
suffix is accepted.
Using a .env file
The server loads .env from the current working directory automatically when
the file exists. Variables that are already exported take precedence, so a
shell environment can always override the file:
cp env.example .env
# edit .env
./bin/wikijs-mcp
To use a different file, pass --env-file or set ENV_FILE:
./bin/wikijs-mcp --env-file /etc/wikijs-mcp.env
# or:
ENV_FILE=/etc/wikijs-mcp.env ./bin/wikijs-mcp
Precedence for selecting the file: --env-file > ENV_FILE > ./.env.
Values from the process environment always win over values from any env file.
A missing default .env is ignored; an explicitly selected file must exist and
be readable. The file supports comments, export KEY=value, and quoted values.
Use with the stdio transport
The default transport is stdio. The MCP client starts the binary and communicates over stdin/stdout.
Claude Code
claude mcp add wikijs \
-e WIKIJS_URL=https://wiki.example.com \
-e WIKIJS_API_KEY=your-api-key \
-- /absolute/path/to/bin/wikijs-mcp
Add -e WIKIJS_ENABLE_EDIT=true to expose the page-editing tools and asset
uploads.
Claude Desktop and Cursor
Add an entry to the client's MCP configuration:
{
"mcpServers": {
"wikijs": {
"command": "/absolute/path/to/bin/wikijs-mcp",
"env": {
"WIKIJS_URL": "https://wiki.example.com",
"WIKIJS_API_KEY": "your-api-key",
"WIKIJS_ENABLE_EDIT": "false"
}
}
}
}
Typical configuration paths:
- Claude Desktop (Linux):
~/.config/Claude/claude_desktop_config.json - Claude Desktop (macOS):
~/Library/Application Support/Claude/claude_desktop_config.json - Cursor:
~/.cursor/mcp.json
Generic stdio client
Run the binary with the environment above. It logs to stderr and speaks MCP JSON-RPC on stdout, so it must be launched by an MCP client rather than used interactively.
Use as an HTTP server
Start the server with the Streamable HTTP transport:
export WIKIJS_URL=https://wiki.example.com
export WIKIJS_API_KEY=your-api-key
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
./bin/wikijs-mcp --transport http --addr :8080 --path /mcp
The MCP endpoint is http://localhost:8080/mcp. A liveness endpoint is
available at GET /healthz; it is always reachable without a token.
For local-only use, the default address is 127.0.0.1:8080, so no token is
needed:
./bin/wikijs-mcp --transport http
If MCP_AUTH_TOKEN is set, every MCP request must send:
Authorization: Bearer <token>
Without MCP_AUTH_TOKEN, the server refuses to start when --addr/MCP_ADDR
is not a loopback address (:8080, 0.0.0.0:8080, or a LAN hostname). This
prevents accidentally exposing the wiki to the network.
Connect an MCP client:
claude mcp add --transport http wikijs http://localhost:8080/mcp \
--header "Authorization: Bearer $MCP_AUTH_TOKEN"
You can also explore the server with the MCP Inspector:
npx @modelcontextprotocol/inspector --url http://localhost:8080/mcp \
--header "Authorization: Bearer $MCP_AUTH_TOKEN"
Flags
| Flag | Environment | Default | Description |
|---|---|---|---|
--transport |
MCP_TRANSPORT |
stdio |
stdio or http |
--addr |
MCP_ADDR |
127.0.0.1:8080 |
Listen address for HTTP |
--path |
MCP_PATH |
/mcp |
Path of the MCP endpoint |
--stateless |
MCP_STATELESS |
false |
Disable MCP sessions (useful behind load balancers) |
--log-level |
LOG_LEVEL |
info |
debug, info, warn, or error |
--env-file |
ENV_FILE |
.env |
Env file to load (existing environment wins) |
Notes for deployments:
- By default the server uses stateful MCP sessions with a 30-minute idle
timeout. Use
--statelessif requests may hit different replicas. - A reverse proxy in front of the server must support streaming responses and must not buffer them (SSE).
- The default listen address is loopback-only. To listen on all interfaces, set
--addr :8080and provideMCP_AUTH_TOKEN. - HTTP transport does not terminate TLS. Put it behind a TLS reverse proxy for remote access.
Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
WIKIJS_URL |
yes | – | Base URL of the WikiJS instance |
WIKIJS_API_KEY |
yes | – | WikiJS API key, sent as a bearer token |
WIKIJS_ENABLE_EDIT |
no | false |
Expose create_page, edit_page, upload_asset, and create_asset_folder |
WIKIJS_TIMEOUT |
no | 30s |
Timeout for one GraphQL request |
MCP_TRANSPORT |
no | stdio |
stdio or http |
MCP_ADDR |
no | 127.0.0.1:8080 |
HTTP listen address |
MCP_PATH |
no | /mcp |
HTTP endpoint path |
MCP_STATELESS |
no | false |
Disable HTTP sessions |
MCP_AUTH_TOKEN |
no | – | Bearer token for HTTP; required when MCP_ADDR is not loopback |
LOG_LEVEL |
no | info |
Log verbosity; logs always go to stderr |
ENV_FILE |
no | .env |
Alternative env file; --env-file overrides it |
Tools
The four read tools are always available. Editing is disabled by default; set
WIKIJS_ENABLE_EDIT=true to also expose the write tools.
| Tool | Arguments | Description |
|---|---|---|
search_pages |
query (required), path, locale |
Full-text search. Returns results, suggestions, and totalHits. |
get_page_by_id |
id (required) |
Full page by numeric ID, or null. |
get_page_by_path |
path, locale (both required) |
Full page by path and locale, or null. |
get_all_pages |
limit, locale, tags |
List pages with optional filters. Returns {"pages": [...]}. |
list_asset_folders |
parentFolder |
List the child folders of an asset folder (empty = root). |
list_assets |
folder, kind |
List the files in an asset folder. kind filters by IMAGE or BINARY. Each asset includes its absolute url and a ready-to-use markdown reference. |
upload_asset |
filename, content or contentB64, folder, mimeType |
Upload a file to an asset folder (empty = root). Returns the absolute url and a markdown reference to paste into a page. |
create_page |
title, content, description, path (required); editor, isPublished, isPrivate, locale, tags |
Create a page. |
edit_page |
id, title, content, description, path (required); editor, isPublished, isPrivate, locale, tags |
Update a page. |
create_asset_folder |
name (required), parentFolder |
Create a new asset folder; the slug is derived from the name. |
Defaults for the write tools: editor=markdown, isPublished=true,
isPrivate=false, locale=en, tags=[].
Locales use the form en or de-DE (two lowercase letters, optionally followed
by a hyphen and two uppercase letters). Every page result includes a url built
from WIKIJS_URL; the locale is part of the URL for non-English pages.
Local WikiJS for development
docker compose up -d
WikiJS is then available at http://localhost:3000. Complete the setup wizard,
create an API key under Administration → API Access, set WIKIJS_URL and
WIKIJS_API_KEY, and run the server.
Development
make test # unit tests
make test-race # unit tests with the race detector
make vet # go vet
make check # vet + race tests
make run-http # run the HTTP transport on 127.0.0.1:8080
Tests cover the GraphQL client against an httptest server, all tool handlers
through an in-memory MCP session, and the HTTP transport including bearer-token
authentication. See AGENTS.md for the code layout and conventions.
Security
- Keep
WIKIJS_API_KEYandMCP_AUTH_TOKENout of version control. Prefer the environment or a secrets manager over committing.env; keep the file mode restrictive (chmod 600 .env). - Editing is disabled by default. Enable it only for clients and environments you trust.
- The HTTP transport refuses non-loopback addresses unless
MCP_AUTH_TOKENis set. Even with a token, use TLS (for example via a reverse proxy) before exposing it to untrusted networks. - The server never logs API keys, bearer tokens, or request bodies.
License
GPL-3.0-or-later. See LICENSE.