Skip to main content
Glama
README.md
# komga-mcp

A small, **read-only** [Model Context Protocol](https://modelcontextprotocol.io)
(MCP) server that exposes your [Komga](https://komga.org) reading data to any
MCP-compatible client over **Streamable HTTP**.

It is intentionally minimal and safe: it can only *read* from Komga. There is no
tool that writes progress, edits metadata, deletes, downloads, scans, or issues
arbitrary API calls.

## Tools

The server advertises **exactly four** tools:

| Tool | Description |
|------|-------------|
| `search_comics` | Search a series or a book by title. |
| `get_unread_series` | List series that contain unread books. |
| `get_reading_progress` | List books in progress with page/percentage. |
| `continue_reading` | Books in progress first, then on-deck (next unread), separated. |

Every response is a normalized JSON payload — never a raw Komga dump, never file
paths or device identifiers.

## HTTP endpoints

| Path | Method | Auth | Purpose |
|------|--------|------|---------|
| `/mcp` | POST | Bearer | MCP Streamable HTTP endpoint |
| `/health` | GET | none | Liveness (does not call Komga) |
| `/ready` | GET | Bearer | Readiness (checks Komga connectivity) |

Inbound requests to `/mcp` and `/ready` require
`Authorization: Bearer <MCP_BEARER_TOKEN>`.

## Configuration

Set via environment variables (see [`.env.example`](./.env.example)):

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PORT` | no | `8000` | Listen port |
| `KOMGA_URL` | yes | — | Base URL of Komga (e.g. `http://komga:25600`) |
| `KOMGA_API_KEY` | yes | — | Sent to Komga as `X-API-Key` |
| `MCP_BEARER_TOKEN` | yes | — | Required bearer for inbound MCP clients |
| `KOMGA_REQUEST_TIMEOUT_MS` | no | `10000` | Per-request timeout (1000..60000) |
| `LOG_LEVEL` | no | `info` | `error` / `warn` / `info` / `debug` |

Generate a strong bearer token, for example: `openssl rand -hex 32`.

## Run with Docker

```bash
cp .env.example .env   # then edit .env with real values
docker run --rm --env-file .env -p 8101:8000 ghcr.io/pierrec18/komga-mcp:0.1.0
```

Or with Compose — see [`compose.example.yaml`](./compose.example.yaml). The image
runs as a non-root user and is compatible with a read-only root filesystem.

## Run from source

```bash
npm ci
npm run build
KOMGA_URL=http://localhost:25600 \
KOMGA_API_KEY=... \
MCP_BEARER_TOKEN=... \
npm start
```

## Connecting an MCP client

Point any MCP client at the Streamable HTTP endpoint and pass the bearer token.
Example with the official TypeScript SDK:

```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:8101/mcp"),
  { requestInit: { headers: { Authorization: `Bearer ${process.env.MCP_BEARER_TOKEN}` } } },
);
const client = new Client({ name: "my-client", version: "1.0.0" });
await client.connect(transport);
console.log(await client.listTools());
```

This server is **not** tied to any particular client or agent framework.

## Development

```bash
npm ci
npm run lint       # ESLint
npm run typecheck  # tsc --noEmit
npm test           # vitest (runs against a fake Komga; never your real instance)
npm run build      # emit dist/
```

## Security

See [SECURITY.md](./SECURITY.md). In short: read-only by design, bearer-protected,
secrets never logged, upstream errors sanitized, timeouts and bounded limits on
everything.

## License

MIT