MCP server for Anki collection management
Project description
Shrike
Sometimes you want to work on your Anki collection without opening Anki. That's what Shrike is for. It's a command-line client and an MCP server for your collection, so you can manage cards from a terminal or hand them to an LLM agent.
Shrike works directly on the collection's SQLite database, without using the Anki desktop app or relying on the AnkiConnect bridge. You get:
- a
shrikeCLI to browse, create, edit, and delete notes and note types - an MCP server exposing the same operations to agents
- semantic search over your notes, with embeddings computed locally
Requirements
- Python 3.12 or newer
- An Anki collection (a
collection.anki2file)
Getting started
Install Shrike from PyPI:
pipx install shrike-mcp # or: pip install shrike-mcp
The package is shrike-mcp; the command it installs is shrike.
Point Shrike at your collection and start the daemon:
shrike server start --collection ~/path/to/collection.anki2
That starts a background daemon with your collection open. The other shrike commands talk to it, so they don't repeat --collection:
shrike info
shrike note list --deck Default
And when you're done:
shrike server stop
Semantic search
shrike note search finds notes by meaning instead of keywords. It needs two things you supply yourself: a llama-server binary (from llama.cpp) to compute embeddings, and a GGUF embedding model for it to run.
Get llama.cpp by building it or installing it from a package manager; you want the llama-server binary it provides. For the model, any GGUF embedding model works. A small one like all-MiniLM-L6-v2 is a good default: it runs on CPU and is plenty for finding related cards.
Start Shrike with both:
shrike server start --collection ~/path/to/collection.anki2 \
--llama-server ~/llama.cpp/build/bin/llama-server \
--embedding-model ~/models/all-MiniLM-L6-v2-Q4_K_M.gguf
If --llama-server or --embedding-model isn't given on the command line or in your config file, Shrike falls back to LLAMA_SERVER_PATH and a llama-server on your PATH for the binary, and to SHRIKE_EMBEDDING_MODEL for the model.
Shrike builds an index of your notes in the background. A large collection takes a little while the first time; search will tell you if it's still indexing. Once it's ready:
shrike note search "electron transport chain"
shrike note search --similar-to 1700000000123
shrike note search also matches your query as exact text, so it finds notes that contain the words literally as well as ones that are semantically close; each result shows you which applied. The exact-text part works even without the embedding service running.
CLI
shrike info summarizes the collection: note types, decks, tags, and scheduling stats.
shrike info
shrike info --types --decks --stats
List and read notes:
shrike note list --deck "Organic Chemistry" --limit 20
shrike note show 1779749914797
Create a note by passing fields with -f:
shrike note create --deck "Japanese::Vocabulary" --type Basic \
-f Front="読む (よむ)" -f Back="to read"
Cloze notes work the same way; the deletions go in the field:
shrike note create --deck "Systems Design" --type Cloze \
-f Text="The {{c1::CAP theorem}} says a distributed system can guarantee at most two of {{c2::consistency}}, {{c3::availability}}, and {{c4::partition tolerance}}."
To add notes in bulk, pipe a JSON array to --json-input:
echo '[
{"deck": "Biochemistry", "note_type": "Basic",
"fields": {"Front": "What does ATP synthase do?",
"Back": "Builds ATP from ADP and phosphate, driven by the proton gradient across the inner mitochondrial membrane."},
"tags": ["metabolism", "mitochondria"]}
]' | shrike note create --json-input
Update fields, edit tags, or delete:
shrike note update 1779749914797 -f Back="to read; to interpret"
shrike note tag 1779749914797 --set verb,jlpt-n5
shrike note delete 1779749914797 --yes
shrike note tag edits a note's tags in one of three modes, and you pick one explicitly:
shrike note tag 1779749914797 --set verb,jlpt-n5 # replace all tags
shrike note tag 1779749914797 --add needs-review # add, leaving others
shrike note tag 1779749914797 --add jp --remove jp-verb # add and remove together
shrike note tag 1779749914797 --set "" # clear all tags
Fix text across many notes at once with shrike note replace. It needs a scope, previews the changes, and asks before applying:
shrike note replace "teh" "the" --deck "Biology" # preview, confirm, apply
shrike note replace "colou?r" "color" --regex --tags spelling --dry-run
For tags across the whole collection, shrike tag renames:
shrike tag rename history::ww2 history::wwii # rename everywhere it appears
shrike tag rename jp japanese --note 1779749914797 # only on these notes
Manage decks with shrike deck:
shrike deck create "Japanese::Vocabulary" # nested decks use ::
shrike deck rename "Misc::French" "French" # rename or reparent
shrike deck delete "Old Deck" --yes # delete (must be empty first)
Anywhere a command takes a deck (including --deck on note commands), you can pass the deck's name, its numeric ID, or #id instead.
Note types have their own commands:
shrike type list
shrike type show Basic
shrike type create --name Vocab --field Word --field Meaning \
--template 'Card 1:{{Word}}:{{FrontSide}}<hr>{{Meaning}}'
Every command takes --json for scriptable output. See the CLI reference for the full list of commands and flags.
Connect an MCP client
Start the server first (see Getting started). Clients connect to the running daemon at http://127.0.0.1:8372/mcp.
Claude Code connects over streamable HTTP directly:
claude mcp add --transport http shrike http://127.0.0.1:8372/mcp
Claude Desktop and claude.ai native URL connectors require OAuth, which Shrike doesn't implement yet, so connect through the mcp-remote stdio bridge instead. Add this to claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"shrike": {
"command": "npx",
"args": ["mcp-remote", "http://127.0.0.1:8372/mcp", "--allow-http", "--transport", "http-only"]
}
}
}
The endpoint is unauthenticated and bound to loopback by default. Before exposing it past 127.0.0.1, read the transport-security and remote-access options in the CLI reference.
MCP Tools
Shrike exposes these MCP tools:
- collection_info: collection structure, note types, decks, tags, and stats
- list_notes: filter and retrieve notes by deck, tags, type, IDs, or date
- search_notes: semantic similarity and exact-substring search over notes
- collection_query: find notes with a raw Anki search expression (
is:due,prop:, …) - find_replace_notes: bulk find and replace across note fields in a scoped set
- migrate_note_type: change notes' note type with a field/template map (preserves history)
- upsert_notes: create or update notes in bulk
- upsert_note_types: create or update note type definitions
- update_note_type_fields: add, remove, rename, or reposition a note type's fields (data-safe)
- update_note_type_templates: add, remove, rename, or reposition a note type's card templates (data-safe)
- find_replace_note_types: find and replace text in a note type's template HTML and CSS
- update_note_type_field_metadata: set a note type's per-field editor metadata (font, size, description)
- update_note_tags: set, add, or remove tags on a set of notes
- rename_tag: rename a tag collection-wide or on specific notes
- upsert_decks: create or rename/reparent decks in bulk
- delete_decks: delete decks by name, if empty
- delete_notes: permanently delete notes by ID
- delete_note_types: delete note types by ID, if no notes use them
- collection_prune: clean up unused tags, empty notes, and empty cards (previews by default)
The machine schema is whatever the running server advertises via tools/list. docs/mcp-tools.md is the human-readable companion.
Configuration
Shrike reads settings from a YAML file in the platform config directory: ~/.config/shrike/config.yml on Linux, ~/Library/Application Support/shrike/config.yml on macOS. The file is yours to manage; shrike server start never writes it on its own. To save the flags you started with so you don't have to repeat them, pass --save-config and Shrike writes the resolved settings to the file:
shrike server start --collection ~/path/to/collection.anki2 \
--embedding-model ~/models/all-MiniLM-L6-v2-Q4_K_M.gguf --save-config
Most settings also take an environment variable (SHRIKE_URL, SHRIKE_COLLECTION, SHRIKE_EMBEDDING_MODEL, and more) or a command-line flag. Command-line flags take precedence, then environment variables, then the file. The CLI reference has the full list.
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/unit -v # unit tests (no server)
pytest tests/integration -v -m integration # integration tests (real server subprocess)
ruff check src/shrike/
mypy src/shrike/
Contributing
CONTRIBUTING.md covers branching, versioning, releases, and how defects get tracked.
License
AGPL-3.0-or-later
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shrike_mcp-0.4.0.tar.gz.
File metadata
- Download URL: shrike_mcp-0.4.0.tar.gz
- Upload date:
- Size: 406.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
259f7855a3a7b319282ac2dc2edc6509ee69a8b3212c1641cb36f38dac4ca354
|
|
| MD5 |
6df7e8cc980a186ddc27aa3973483f33
|
|
| BLAKE2b-256 |
0074e2dbf358ee896c1fa8a041ea4f4143a3ee06f988f709809c3e804c57824e
|
Provenance
The following attestation bundles were made for shrike_mcp-0.4.0.tar.gz:
Publisher:
release.yml on lathrys-at/shrike
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shrike_mcp-0.4.0.tar.gz -
Subject digest:
259f7855a3a7b319282ac2dc2edc6509ee69a8b3212c1641cb36f38dac4ca354 - Sigstore transparency entry: 1741173346
- Sigstore integration time:
-
Permalink:
lathrys-at/shrike@8b1bf58bd26f5da658b208027e25bba4d54c0880 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/lathrys-at
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8b1bf58bd26f5da658b208027e25bba4d54c0880 -
Trigger Event:
push
-
Statement type:
File details
Details for the file shrike_mcp-0.4.0-py3-none-any.whl.
File metadata
- Download URL: shrike_mcp-0.4.0-py3-none-any.whl
- Upload date:
- Size: 167.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9def8959725bb27b5898f89d1ca65cf5baff8d1d40e16c61ba4d78cef4f7e98a
|
|
| MD5 |
b0ac2e26c30f02df3e3c291ac48ca9a9
|
|
| BLAKE2b-256 |
555266d6be14063e5e6e90d1b3fa38bb4b13e7b8f2fb393660ac6b27c0e18e18
|
Provenance
The following attestation bundles were made for shrike_mcp-0.4.0-py3-none-any.whl:
Publisher:
release.yml on lathrys-at/shrike
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shrike_mcp-0.4.0-py3-none-any.whl -
Subject digest:
9def8959725bb27b5898f89d1ca65cf5baff8d1d40e16c61ba4d78cef4f7e98a - Sigstore transparency entry: 1741173399
- Sigstore integration time:
-
Permalink:
lathrys-at/shrike@8b1bf58bd26f5da658b208027e25bba4d54c0880 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/lathrys-at
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8b1bf58bd26f5da658b208027e25bba4d54c0880 -
Trigger Event:
push
-
Statement type: