An MCP (Model Context Protocol) server that exposes Readur's document management and OCR API as tools for AI assistants. This server enables AI clients such as Claude Code, Claude Desktop, and other MCP-compatible applications to list, search, read, upload, and organize documents stored in a Readur instance.
MCP is an open protocol that standardizes how AI applications interact with external tools and data sources. By running this server, you give your AI assistant direct access to your Readur document library through 18 purpose-built tools.
Document Management -- List, inspect, upload, delete, and reprocess documents with full metadata access including OCR-extracted text.
Full-Text Search -- Search across document contents with relevance scoring, text snippets, filtering by MIME type and status, and query suggestions.
Label Organization -- Create labels and assign them to documents for categorization and retrieval.
System Monitoring -- Check authenticated user info, application settings, OCR language availability, and processing queue statistics.
| Category | Tools |
|---|---|
| Documents | list_documents, get_document, get_document_ocr_text, upload_document, delete_document, retry_document_ocr |
| Search | search_documents, enhanced_search, get_search_facets |
| Labels | list_labels, create_label, get_document_labels, add_document_label, remove_document_label |
| Status | whoami, get_settings, get_queue_stats, get_ocr_languages |
- Node.js >= 24.0.0
- A running Readur instance with API access
- Authentication credentials: either a JWT token or a username/password pair
Clone the repository and build from source:
git clone <repository-url>
cd readur-mcp
npm install
npm run buildAfter building, the compiled server is located at dist/index.js.
The server accepts configuration from three sources, applied in the following priority order (highest to lowest):
- CLI arguments
- Environment variables
- Config file (
~/.config/readur-mcp/config.json)
A value set via CLI argument overrides the same value set via environment variable, which in turn overrides the config file.
| Variable | Required | Description |
|---|---|---|
READUR_URL |
Yes | Base URL of your Readur instance (no trailing slash) |
READUR_TOKEN |
No* | Pre-obtained JWT authentication token |
READUR_USERNAME |
No* | Username for login-based authentication |
READUR_PASSWORD |
No* | Password for login-based authentication |
READUR_TRANSPORT |
No | Transport type: stdio (default) or sse |
READUR_PORT |
No | Port for SSE transport (default: 3000) |
READUR_HOST |
No | Host/bind address for SSE transport (default: 127.0.0.1) |
*You must provide either READUR_TOKEN or both READUR_USERNAME and READUR_PASSWORD.
--url <url> Readur server URL (required, no trailing slash)
--token <token> Pre-obtained JWT authentication token
--username <username> Username for login-based authentication
--password <password> Password for login-based authentication
--transport <type> Transport type: stdio or sse (default: stdio)
--port <port> Port for SSE transport (default: 3000)
--host <host> Host/bind address for SSE transport (default: 127.0.0.1)
-h, --help Show help message
Create ~/.config/readur-mcp/config.json with any of the supported options:
{
"url": "https://readur.example.com",
"token": "your-jwt-token",
"transport": "stdio"
}Or with username/password credentials:
{
"url": "https://readur.example.com",
"username": "admin",
"password": "your-password",
"transport": "stdio"
}The config file is silently ignored if it does not exist. A warning is printed if the file exists but contains invalid JSON.
The server supports two authentication modes:
Token-based authentication -- Provide a pre-obtained JWT token via --token, READUR_TOKEN, or the config file. The token is sent as a Bearer token with every API request. This mode is straightforward but requires you to manage token expiration externally.
Credential-based authentication -- Provide a username and password via --username/--password, READUR_USERNAME/READUR_PASSWORD, or the config file. The server automatically calls POST /api/auth/login to obtain a JWT token on the first API request. If a subsequent request returns HTTP 401, the server transparently re-authenticates and retries the request once, so sessions recover automatically from token expiration.
Register the MCP server using claude mcp add. You can provide configuration through environment variables or CLI arguments.
With a JWT token (environment variables):
claude mcp add readur-mcp \
-e READUR_URL=https://readur.example.com \
-e READUR_TOKEN=your-jwt-token \
-- node /absolute/path/to/readur-mcp/dist/index.jsWith a JWT token (CLI arguments):
claude mcp add readur-mcp \
-- node /absolute/path/to/readur-mcp/dist/index.js \
--url https://readur.example.com \
--token your-jwt-tokenWith username/password credentials:
claude mcp add readur-mcp \
-e READUR_URL=https://readur.example.com \
-e READUR_USERNAME=admin \
-e READUR_PASSWORD=secret \
-- node /absolute/path/to/readur-mcp/dist/index.jsTo verify the server is registered:
claude mcp listAdd the server to your claude_desktop_config.json file.
With a JWT token:
{
"mcpServers": {
"readur": {
"command": "node",
"args": ["/absolute/path/to/readur-mcp/dist/index.js"],
"env": {
"READUR_URL": "https://readur.example.com",
"READUR_TOKEN": "your-jwt-token"
}
}
}
}With username/password credentials:
{
"mcpServers": {
"readur": {
"command": "node",
"args": ["/absolute/path/to/readur-mcp/dist/index.js"],
"env": {
"READUR_URL": "https://readur.example.com",
"READUR_USERNAME": "admin",
"READUR_PASSWORD": "your-password"
}
}
}
}The config file location depends on your operating system:
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Any MCP-compatible client can use this server. The server supports two transports:
stdio (default) -- The client spawns the server process and exchanges JSON-RPC messages over stdin/stdout.
- Run the command:
node /absolute/path/to/readur-mcp/dist/index.js - Pass configuration via environment variables (
READUR_URL,READUR_TOKEN, etc.) or CLI arguments (--url,--token, etc.) - Connect using the stdio transport
SSE -- The server runs as an HTTP server exposing SSE (Server-Sent Events) endpoints. Clients connect over HTTP instead of spawning a subprocess.
- Start the server with
--transport sse:node /absolute/path/to/readur-mcp/dist/index.js \ --url https://readur.example.com \ --token your-jwt-token \ --transport sse \ --port 3000
- The server exposes two endpoints:
GET /sse-- Establishes the SSE stream (returns the message endpoint URL with a session ID)POST /messages?sessionId=...-- Receives JSON-RPC messages from the client
- Point your MCP client at
http://<host>:<port>/sseusing the SSE transport type
The MCP Inspector provides an interactive web UI for testing tools without an AI client. Run it with:
npm run inspectorThis launches the @modelcontextprotocol/inspector and connects it to the server. You can browse available tools, invoke them with custom parameters, and inspect the responses. Set the required environment variables before running the command, or rely on your config file.
| Tool | Description | Parameters | Mode |
|---|---|---|---|
list_documents |
List documents with optional filtering, sorting, and pagination. Returns IDs, titles, MIME types, statuses, and file sizes. | page? (int), pageSize? (int, 1-100), sortBy? (string), sortOrder? ("asc" or "desc"), status? ("pending", "processing", "completed", "failed"), mimeType? (string) |
Read-only |
get_document |
Get full metadata for a single document by ID. Returns title, filename, MIME type, status, page count, file size, hash, and timestamps. | id (int) |
Read-only |
get_document_ocr_text |
Get the OCR-extracted text content of a document. Returns the full text and per-page text with confidence scores. | id (int) |
Read-only |
upload_document |
Upload a new document to Readur. Supports PDF, TIFF, PNG, JPG, and other image formats. | filename (string), content (base64 string), mimeType (string) |
Mutating |
delete_document |
Permanently delete a document by ID. This action cannot be undone. | id (int) |
Destructive |
retry_document_ocr |
Retry OCR processing for a document that previously failed or needs reprocessing. | id (int) |
Mutating |
| Tool | Description | Parameters | Mode |
|---|---|---|---|
search_documents |
Search documents by text query with optional filtering by MIME type and status. Returns matching documents with relevance scores and text snippets. | query (string), page? (int), pageSize? (int, 1-100), mimeType? (string), status? ("pending", "processing", "completed", "failed") |
Read-only |
enhanced_search |
Perform an enhanced search with relevance scores, text snippets, and query suggestions for refining searches. Supports filtering by MIME type and tag. | query (string), page? (int), pageSize? (int, 1-100), mimeType? (string), tag? (string) |
Read-only |
get_search_facets |
Get available search facets for filtering. Returns MIME types and tags present in the collection along with document counts. | (none) | Read-only |
| Tool | Description | Parameters | Mode |
|---|---|---|---|
list_labels |
List all available labels. Returns each label with its ID, name, color, and description. | (none) | Read-only |
create_label |
Create a new label that can be assigned to documents. | name (string), color? (string, e.g. "#ff0000"), description? (string) |
Mutating |
get_document_labels |
Get all labels assigned to a specific document. | documentId (int) |
Read-only |
add_document_label |
Assign a label to a document. | documentId (int), labelId (int) |
Mutating |
remove_document_label |
Remove a label assignment from a document. | documentId (int), labelId (int) |
Destructive |
| Tool | Description | Parameters | Mode |
|---|---|---|---|
whoami |
Get the currently authenticated user's ID, username, email, and role. | (none) | Read-only |
get_settings |
Get the current Readur application settings as key-value pairs. | (none) | Read-only |
get_queue_stats |
Get processing queue statistics: pending, processing, completed, failed, and total job counts. | (none) | Read-only |
get_ocr_languages |
Get available OCR languages with their code, name, and installation status. | (none) | Read-only |
npm run build # Compile TypeScript to dist/
npm run dev # Watch mode -- recompiles on file changes
npm run start # Run the compiled server (dist/index.js)
npm run typecheck # Type-check without emitting files
npm run inspector # Launch MCP Inspector for interactive testingreadur-mcp/
package.json
tsconfig.json
src/
index.ts # Entry point -- loads config and starts server
config.ts # Configuration loading (CLI, env, config file)
server.ts # MCP server setup and tool registration
client/
readur.ts # HTTP client for the Readur REST API
errors/
index.ts # Error formatting utilities
tools/
index.ts # Aggregates all tools and handlers
schemas.ts # Tool definition helper (defineTool)
validators.ts # Shared Zod schemas (pagination, IDs, filters)
documents.ts # Document management tools (6 tools)
search.ts # Search tools (3 tools)
labels.ts # Label management tools (5 tools)
status.ts # System status tools (4 tools)
types/
api.ts # TypeScript type definitions for API responses
"Error: Readur server URL is required" -- The server could not find a URL from any configuration source. Set READUR_URL as an environment variable, pass --url as a CLI argument, or add "url" to your config file.
"Error: Authentication is required" -- Provide either a JWT token (READUR_TOKEN / --token) or both a username and password (READUR_USERNAME + READUR_PASSWORD / --username + --password).
"Login failed" errors -- Verify that your username and password are correct and that the Readur instance is reachable at the configured URL. Check that the /api/auth/login endpoint is accessible.
401 errors during operation -- If using token-based auth, the token may have expired. Obtain a fresh token and update your configuration. If using credential-based auth, the server retries authentication automatically on 401 responses; persistent 401 errors indicate invalid credentials.
Tools not appearing in Claude Code -- Run claude mcp list to confirm the server is registered. If the server is listed but tools are missing, check the server logs for startup errors. Ensure the dist/ directory exists by running npm run build.
SSE server won't start -- Ensure the configured port is not already in use. Check --port / READUR_PORT (default 3000). If binding to 0.0.0.0, be aware this exposes the server on all network interfaces.
MIT