An MCP server that lets an AI assistant (Claude Code, Claude Desktop, …) search and read your OpenArchiver email archive.
⚠️ Unofficial. Independent, community-maintained project — not affiliated with, endorsed by, or supported by OpenArchiver / LogicLabs. The "OpenArchiver" name is used only to describe compatibility. Please report issues here, not to the OpenArchiver project.
It is a thin, typed wrapper around the OpenArchiver REST API and exposes four tools:
| Tool | What it does |
|---|---|
search_archive |
Full-text search across archived emails (subject, body, and extracted attachment text). Returns compact hits, each with an id. |
get_email |
Fetch one email by id: metadata, full body, and an attachment list (each with a storagePath). |
check_integrity |
Re-compute the SHA-256 hashes of an archived email and its attachments and compare them against the hashes stored at archival time — proof that a message has not been altered or corrupted in storage. |
get_attachment |
Download an attachment by its storagePath. Default returns it inline (text → text, images → image content, other binaries → base64). With mode: "file" it saves the bytes to a server-managed temp file and returns only the path — handy for large binaries to keep base64 out of the context (path only usable when the server runs locally; the server picks the path, callers can't). |
All operations are read-only.
- Node.js ≥ 26
- An OpenArchiver instance and an API key with the permissions
search:archiveandread:archive(create one in OpenArchiver under Settings → API keys, or viaPOST /api/v1/api-keys).
OpenArchiver compatibility: developed and tested against OpenArchiver 0.6.0 (REST API
v1), and tested in CI against 0.6.0, 0.5.2 and the current upstreammainsnapshot. OpenArchiver has no stable public OpenAPI spec yet, so other versions may differ; 0.5.2 or newer is recommended.
The published package runs directly with npx — no checkout or build needed:
npx -y openarchiver-mcp(It's normally launched by your MCP client, not by hand — see below.)
Two flags work without an MCP client, which is handy when you need to tell someone which build you are actually running:
npx -y openarchiver-mcp --version # prints e.g. 1.3.0
npx -y openarchiver-mcp --help # usage, environment variables, tool listThe same version is reported to the client in the MCP initialize handshake (serverInfo), so it
also shows up in e.g. Claude Code's /mcp view and the MCP Inspector.
npm install
npm run build # compiles TypeScript to dist/
npm test # builds and runs the unit tests
npm run smoke # launches the built server and verifies it speaks MCP (tools/list)
npm run test:integration # full end-to-end test against a real OpenArchiver (needs Docker)
npm run smokeneeds no OpenArchiver instance: it uses dummy credentials and only lists tools, which never calls the API.
npm run test:integration (scripts/integration.mjs) is a full end-to-end check. It spins up a
throwaway OpenArchiver stack via docker compose (test/integration/), auto-creates an admin and
API key, imports a small fixture mbox through the local-file (mbox) connector, then drives the built
MCP server over stdio against that live instance and asserts search_archive, get_email,
check_integrity and get_attachment all work. The stack is torn down (docker compose down -v) afterwards, so no state
persists and no real secrets are involved.
Requires Docker and
docker composev2. It pulls the OpenArchiver stack images and waits for the asynchronous import/indexing, so a run takes a few minutes. It is not part of the fast unit CI — it runs nightly / on demand via theIntegrationworkflow.
By default it tests against OpenArchiver v0.6.0. To test another version, set OA_IMAGE_TAG
(or pass it as an argument):
OA_IMAGE_TAG=v0.5.2 npm run test:integration
npm run test:integration -- v0.5.2 # equivalent
# test several versions in sequence:
for v in v0.6.0 v0.5.2; do OA_IMAGE_TAG=$v npm run test:integration || break; doneOnly OSS semver tags (
v0.1.1…v0.6.0) are freely pullable. Thev1.xreleases are published only as-enterpriseimages (e.g.OA_IMAGE_TAG=v1.4.2-enterprise), which require a license and may bootstrap differently. The CIIntegrationworkflow runs a matrix overv0.6.0,v0.5.2and one pre-release snapshot of upstreammain(non-blocking: it reports but cannot fail the workflow). The versions live in.github/oa-snapshot.json; the weeklySnapshot checkworkflow re-pins the snapshot by PR when upstream publishes a newer one. Each run uses host port 3000, so local runs must be sequential (the loop above), not parallel.
The server reads two environment variables:
| Variable | Required | Example |
|---|---|---|
OPENARCHIVER_BASE_URL |
yes | https://openarchiver.example.com (the /api/v1 suffix is added automatically) |
OPENARCHIVER_API_KEY |
yes | your API key |
OPENARCHIVER_TIMEOUT_MS |
no | request timeout, default 30000 |
For local runs you can keep these in a .env file (see .env.example) and start the server with
Node's built-in env-file support — no extra dependency required:
cp .env.example .env # then fill in your values
node --env-file=.env dist/index.jsThe
.envfile is git-ignored. Never commit your API key.
claude mcp add openarchiver \
--env OPENARCHIVER_BASE_URL=https://openarchiver.example.com \
--env OPENARCHIVER_API_KEY=your-api-key \
-- npx -y openarchiver-mcpkiro-cli mcp add \
--name openarchiver \
--command npx \
--args "-y openarchiver-mcp" \
--env OPENARCHIVER_BASE_URL=https://openarchiver.example.com \
--env OPENARCHIVER_API_KEY=your-api-keyAdd to claude_desktop_config.json:
{
"mcpServers": {
"openarchiver": {
"command": "npx",
"args": ["-y", "openarchiver-mcp"],
"env": {
"OPENARCHIVER_BASE_URL": "https://openarchiver.example.com",
"OPENARCHIVER_API_KEY": "your-api-key"
}
}
}
}Running from a local checkout instead? Replace the command with
node /absolute/path/to/openarchiver-mcp/dist/index.js.
Run the official MCP Inspector against the built server:
npx @modelcontextprotocol/inspector node --env-file=.env dist/index.jssrc/config.ts— reads and validates env vars; normalizes the base URL to…/api/v1.src/client.ts—OpenArchiverClient(search,getEmail,checkIntegrity,download);fetchis injectable for testing; maps HTTP errors (401/403/404/…) to actionable messages.src/format.ts— pure functions that render API responses into compact text for the model.src/index.ts— registers the four MCP tools and serves them over stdio.get_emailparses the raw RFC822 message (returned by the API) withmailparserto produce a readable body.
Tests live next to the code (*.test.ts) and run on Node's built-in test runner (node:test) — no
network access required (the HTTP layer is mocked).
- OpenArchiver does not publish an official client SDK or a hosted OpenAPI spec, so this server talks to the REST endpoints directly.
- The storage download endpoint returns
application/octet-streamfor attachments, soget_attachmentreturns most binaries (e.g. PDFs) as base64. For reading PDF/Office content, prefersearch_archive/get_email, which expose the server-side extracted text. - Meilisearch caps
totalat 1000 by default; usepage/limitto paginate. - Since OpenArchiver 0.6.0, permissions are enforced per record: the download endpoint checks that
the requested path belongs to an email the API key's role actually covers, and answers
404(not403) when it does not, so a path cannot be probed. With a scoped key,get_attachmentcan therefore report "not found" for a path that exists. A key with unrestrictedread:archiveis unaffected.