AnkiConnect Server
Headless AnkiConnect-compatible REST API server with AnkiWeb sync support and MCP server integration.
❓ Problem & Solution
Problem: Traditional AnkiConnect requires the Anki desktop app running, works only on localhost, and can't be deployed to servers.
Solution: This headless server provides direct collection access without Anki desktop, supports remote deployment, and includes automatic AnkiWeb sync.
✨ Features
- Server Deployment - Run on VPS, Raspberry Pi, or cloud without GUI
- AI Integration - MCP server for AI assistant access to your cards
- Full AnkiConnect API Compatibility - Version 6 API with all standard actions
- Headless Operation - No Qt/GUI required, perfect for servers and containers
- AnkiWeb Sync - Automatic synchronization with AnkiWeb (optional)
- MCP Server - Model Context Protocol integration for AI assistants
📋 Table of Contents
⚙️ Configuration
Set environment variables before running the server:
| Variable | Required | Default | Description |
|---|---|---|---|
ANKICONNECT_COLLECTION_PATH |
No | temp path | Path to your .anki2 collection file |
ANKICONNECT_PORT |
No | 8765 |
Server port |
ANKICONNECT_BIND |
No | 127.0.0.1 |
Bind address (use 0.0.0.0 for external access) |
ANKICONNECT_ANKIWEB_USER |
No | - | AnkiWeb username (required for sync) |
ANKICONNECT_ANKIWEB_PASS |
No | - | AnkiWeb password (required for sync) |
ANKICONNECT_ANKIWEB_URL |
No | - | Custom sync server URL (optional) |
Example .env File
# Required: Path to your Anki collection
ANKICONNECT_COLLECTION_PATH=/path/to/collection.anki2
# Optional: Server configuration
ANKICONNECT_PORT=8765
ANKICONNECT_BIND=127.0.0.1
# Optional: AnkiWeb sync credentials
ANKICONNECT_ANKIWEB_USER=your@email.com
ANKICONNECT_ANKIWEB_PASS=your_password
# Optional: Custom sync server
ANKICONNECT_ANKIWEB_URL=https://your-sync-server.com
🚀 Usage
Quick Start with uvx (No Installation)
# Run the API server
ANKICONNECT_COLLECTION_PATH=/path/to/collection.anki2 \
ANKICONNECT_ANKIWEB_USER=your@email.com \
ANKICONNECT_ANKIWEB_PASS=your_password \
uvx anki-connect-server api
# Run the MCP server
ANKICONNECT_COLLECTION_PATH=/path/to/collection.anki2 \
ANKICONNECT_ANKIWEB_USER=your@email.com \
ANKICONNECT_ANKIWEB_PASS=your_password \
uvx anki-connect-server mcp
📚 API Reference
The server exposes a single POST endpoint at /api that accepts AnkiConnect-style JSON requests.
Request Format
{
"action": "actionName",
"version": 6,
"params": {
"param1": "value1",
"param2": "value2"
}
}
Response Format
{
"result": <action_result>,
"error": null
}
On error:
{
"result": null,
"error": "Error message"
}
Examples
Get Deck Names
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{"action": "deckNames", "version": 6}'
Create a Deck
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{"action": "createDeck", "version": 6, "params": {"deck": "My New Deck"}}'
Add a Note
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{
"action": "addNote",
"version": 6,
"params": {
"note": {
"deckName": "Default",
"modelName": "Basic",
"fields": {
"Front": "Hello",
"Back": "World"
},
"tags": ["api"]
}
}
}'
Store Media from a URL (no base64 needed)
storeMediaFile accepts base64 data, a url to download, or a server-side path.
When several are given, data takes precedence over url, which takes precedence over path.
# Download audio from a dictionary on the server side (~100-byte request)
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{
"action": "storeMediaFile",
"version": 6,
"params": {
"filename": "sky_us.ogg",
"url": "https://commons.wikimedia.org/wiki/Special:FilePath/En-us-sky.ogg"
}
}'
# Copy a file already visible to the server (e.g. a mounted volume)
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{
"action": "storeMediaFile",
"version": 6,
"params": {"filename": "sky.mp3", "path": "/data/media-in/sky.mp3"}
}'
Then reference the stored file in a note field with [sound:sky_us.ogg].
Sync with AnkiWeb
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{"action": "sync", "version": 6}'
Batch Multiple Actions
curl -X POST http://localhost:8765/api \
-H "Content-Type: application/json" \
-d '{
"action": "multi",
"version": 6,
"params": {
"actions": [
{"action": "deckNames", "params": {}},
{"action": "modelNames", "params": {}}
]
}
}'
Supported Actions
Misc
version- Get API versionsync- Sync with AnkiWebsyncStatus- Get sync statussyncMedia- Sync media onlymulti- Execute multiple actionsimportPackage- Import.apkgfileexportPackage- Export deck to.apkg
Decks
deckNames- Get all deck namesdeckNamesAndIds- Get deck names with IDscreateDeck- Create a new deckdeleteDecks- Delete deckschangeDeck- Move cards to different deckgetDecks- Get decks for cardsgetDeckConfig- Get deck configurationsaveDeckConfig- Save deck configurationsetDeckConfigId- Assign config to deckscloneDeckConfigId- Clone deck configremoveDeckConfigId- Remove deck config
Models
modelNames- Get all model namesmodelNamesAndIds- Get model names with IDsmodelFieldNames- Get field names for modelmodelFieldsOnTemplates- Get fields used in templatescreateModel- Create a new note modelmodelTemplates- Get card templatesmodelStyling- Get CSS stylingupdateModelTemplates- Update templatesupdateModelStyling- Update CSS
Notes
addNote- Add a single noteaddNotes- Add multiple notescanAddNotes- Check if notes can be addedupdateNoteFields- Update note fieldsfindNotes- Search for notesnotesInfo- Get note detailsdeleteNotes- Delete notesaddTags- Add tags to notesremoveTags- Remove tags from notesgetTags- Get all tags
Cards
findCards- Search for cardscardsToNotes- Get note IDs from card IDscardsInfo- Get card detailssuspend- Suspend cardsunsuspend- Unsuspend cardsareSuspended- Check if cards are suspendedareDue- Check if cards are duegetIntervals- Get card intervals
Media
getMediaDirPath- Get media directory pathstoreMediaFile- Store a media file (base64data,url, or server-sidepath)retrieveMediaFile- Retrieve a media filedeleteMediaFile- Delete a media file
🤖 MCP Server
The server includes a Model Context Protocol (MCP) integration for AI assistants.
Starting the MCP Server
ANKICONNECT_COLLECTION_PATH=/path/to/collection.anki2 \
ANKICONNECT_ANKIWEB_USER=your@email.com \
ANKICONNECT_ANKIWEB_PASS=your_password \
uvx anki-connect-server mcp
Transports: stdio vs http
The MCP server supports two transports via --transport:
| Transport | Flag | Behavior |
|---|---|---|
stdio (default) |
--transport stdio |
One process per client connection. The MCP client (e.g. Claude Desktop, Hermes) spawns a new process for each session. |
http |
--transport http |
A single long-lived server holds the collection and serves all clients over HTTP. |
# stdio (default)
uvx anki-connect-server mcp
# http
uvx anki-connect-server mcp --transport http
⚠️ Concurrent MCP processes and the "already open" error
The Anki collection is a single-writer SQLite database: only one process can hold the lock at a time. The default stdio transport spawns a new process per client connection, so if your MCP client (Hermes, Claude Desktop, etc.) starts a new process before the previous one has exited, you will see:
anki.errors.DBError: Anki already open, or media currently syncing.
This is a SQLite lock contention error, not a media-sync issue — despite what the message says.
Workarounds:
-
Use HTTP transport (recommended for multi-client setups):
uvx anki-connect-server mcp --transport http # Server runs on http://127.0.0.1:8765/mcp
Configure your MCP client to connect to the HTTP endpoint instead of spawning a stdio process. Only one process holds the collection and serves all connections.
-
Leave
ANKICONNECT_COLLECTION_PATHunset. When not configured, each process gets a random temp collection (anki-connect-server-<random>.anki2), so concurrent processes don't contend for the same file. Note: each process will have its own empty collection — set the path explicitly if you need a shared collection. -
Ensure only one process is running. If you stick with stdio, make sure your MCP client kills the previous process before starting a new one.
Available MCP Tools
get_deck_names- Get all deck namesget_deck_names_and_ids- Get decks with IDscreate_deck- Create a new deckdelete_decks- Delete decksget_model_names- Get all model namesget_model_field_names- Get fields for a modelcreate_model- Create a note model (fields + card templates)model_add_field/model_remove_field/model_rename_field/model_reposition_field- Manage model fieldsupdate_note_fields- Update fields of an existing noteadd_note- Add a new notefind_notes- Search for notesget_notes_info- Get note detailsdelete_notes- Delete notesfind_cards- Search for cardsget_cards_info- Get card detailssuspend_cards/unsuspend_cards- Manage card suspensionare_suspended/are_due- Check card statusget_card_intervals- Get card intervalsget_all_tags- Get all tagsadd_tags/remove_tags- Manage tagsget_media_dir_path- Get media directorystore_media_file/retrieve_media_file/delete_media_file- Media operations (store_media_fileaccepts base64data,url, or server-sidepath)change_deck- Move cards between deckscards_to_notes- Convert card IDs to note IDsget_deck_config- Get deck configurationget_model_templates/get_model_styling- Model customizationupdate_model_templates/update_model_styling- Edit model templates/CSSget_api_version- Get API versionimport_package/export_package- Import/export deckssync/sync_media/get_sync_status- Sync operations
Adding to Claude Desktop
Option A — stdio (default, one process per session):
Add to your claude_desktop_config.json:
{
"mcpServers": {
"anki-connect-server": {
"command": "uvx",
"args": ["anki-connect-server", "mcp"],
"env": {
"ANKICONNECT_COLLECTION_PATH": "/path/to/collection.anki2",
"ANKICONNECT_ANKIWEB_USER": "your@email.com",
"ANKICONNECT_ANKIWEB_PASS": "your_password"
}
}
}
}
Option B — http (single long-lived server, avoids lock contention):
First start the HTTP server (e.g. as a systemd service or background process):
uvx anki-connect-server mcp --transport http
Then point your MCP client at http://127.0.0.1:8765/mcp.
🐳 Docker
Using Pre-built Image
docker run -d \
-p 8765:8765 \
-v /path/to/collection.anki2:/data/collection.anki2 \
-e ANKICONNECT_COLLECTION_PATH=/data/collection.anki2 \
-e ANKICONNECT_ANKIWEB_USER=your@email.com \
-e ANKICONNECT_ANKIWEB_PASS=your_password \
--name anki-connect-server \
glechic/anki-connect-server:latest
Docker Compose
version: '3.8'
services:
anki-connect-server:
image: glechic/anki-connect-server:latest
ports:
- "8765:8765"
volumes:
- ./collection.anki2:/data/collection.anki2
environment:
- ANKICONNECT_COLLECTION_PATH=/data/collection.anki2
- ANKICONNECT_ANKIWEB_USER=${ANKIWEB_USER}
- ANKICONNECT_ANKIWEB_PASS=${ANKIWEB_PASS}
restart: unless-stopped
Building from Source
docker build -t anki-connect-server .
Metadata
Release files for anki-connect-server 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| anki_connect_server-0.4.0.tar.gz | 44.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| anki_connect_server-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.6 kB
Release files / anki_connect_server-0.4.0.tar.gz
| Download URL | anki_connect_server-0.4.0.tar.gz |
|---|---|
| Size | 44.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e22734d870d119b0a2653ac3acdc132ecb2c46509ae1e2f39f6b0aeca53954eb
|
|
BLAKE2b-256 checksum How to use checksums |
5694040e1b855661ce0d8a295e9c06460b9aa0caa9adcf8d1995c01e3aa652c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.7.3
|
Release files / anki_connect_server-0.4.0-py3-none-any.whl
| Download URL | anki_connect_server-0.4.0-py3-none-any.whl |
|---|---|
| Size | 27.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d42c0fcd8bdc8e8da7f3a3ff543ff7bd013688e7358314e5dac933fb252127ce
|
|
BLAKE2b-256 checksum How to use checksums |
78048661035aecbe3976d9f0d24740f2b784a63a7c3ed99c9685b9e7860f2c97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.7.3
|