Skip to content

Releases: ankimcp/anki-mcp-server-addon

v0.26.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 18:53

What's Changed

  • feat: event-driven request dispatch replaces 25ms polling QTimer by @anatoly314 in #65

Full Changelog: v0.25.2...v0.26.0

v0.25.2

Choose a tag to compare

@github-actions github-actions released this 16 Jul 11:06

What's Changed

  • feat: hosted_mode for unattended (self-hosted) tunnel connections by @anatoly314 in #64

Full Changelog: v0.25.1...v0.25.2

v0.25.1

Choose a tag to compare

@github-actions github-actions released this 09 Jul 04:09
bb0a8eb

What's New

Deck-scoped tag listing with get_tags

The tag_management tool's get_tags action gains an optional deck parameter. Pass a deck name to get just the distinct tags on notes with at least one card in that deck (subdecks included), instead of every tag in the collection. This lets an AI client discover which tags actually apply to a given deck in one lean call — the natural discovery step before suggesting tags — rather than paging through cards_stats and de-duping by hand. Omit the parameter for the original all-collection behavior.

Tags are computed collection-side using the same deck: scoping cards_stats uses, so the two agree: subdecks are included, prefix-similar sibling decks (e.g. Algebra vs Algebra - extra) stay separate, and results come back sorted. An unknown or empty deck returns an empty list rather than erroring. (#63)

Install

  • AnkiWeb: Tools → Add-ons → Get Add-ons… → code 124672614
  • From this release: download the .ankiaddon below and double-click (or Tools → Add-ons → Install from file).

Full Changelog: v0.25.0...v0.25.1

v0.25.0

Choose a tag to compare

@github-actions github-actions released this 08 Jul 07:31

What's New

Bulk card scheduling stats with cards_stats

New read-only cards_stats tool returns compact per-card scheduling metrics — type, queue, interval (days), the note's tags, and a computed dueToday flag — for a whole deck (subdecks included), paginated. It's FSRS-independent (works whether or not FSRS is enabled), so a single lean call can replace a find_notes + notes_info + get_card_memory_state sequence when you only need scheduling data. No note fields, no HTML — just the scheduling metrics, for bulk analytics. (#62)

Asynchronous sync

The sync tool moves to a non-blocking job model: sync() starts a sync and returns a job id, sync(job_id) polls its status, and sync(job_id, resolve=...) resolves a full-sync conflict — so a long sync no longer blocks the MCP call. (#62)

Install

  • AnkiWeb: Tools → Add-ons → Get Add-ons… → code 124672614
  • From this release: download the .ankiaddon below and double-click (or Tools → Add-ons → Install from file).

Full Changelog: v0.24.0...v0.25.0

v0.24.0

Choose a tag to compare

@github-actions github-actions released this 30 Jun 07:09

What's New

Preview bulk edits with dry_run on update_notes

update_notes gains a dry_run=true option that runs the full per-entry validation pass (field names, note existence, ID validity) and reports which notes would be updated — without writing anything. Returns would_update/would_fail counts plus per-note status: "would_update" entries, mirroring the dry run delete_notes already had. Handy for safely previewing a large bulk edit before committing it.

delete_notes respects max_notes_per_batch

delete_notes no longer caps at a hardcoded 100 notes — it now honors the max_notes_per_batch config option like update_notes, add_notes, and notes_info. Raising that limit in your add-on config now applies to deletes too, and the error hint points at the config option.

(both above: #59 — thanks @klfk for your first contribution!)

Fix: startup crash when another add-on bundles an old packaging

On Anki 26.05+ (Python 3.13), if another add-on (for example AMBOSS) had already loaded an older packaging (≤ 20.9) into Anki's shared Python process, AnkiMCP's first-run pydantic_core download failed with No module named 'distutils' — even though AnkiMCP's own bundled packaging was perfectly fine and just never got used.

AnkiMCP now forces its own vendored packaging when selecting the download wheel, regardless of what other add-ons have loaded, and restores the other add-on's copy afterward (so nothing else breaks). Already-affected installs self-heal on the next launch. (#61)

Docs: connecting via Opencode

Added setup instructions for the Opencode terminal AI agent. (#60 — thanks @Marley-Mulvin-Broome!)

Install

  • AnkiWeb: Tools → Add-ons → Get Add-ons… → code 124672614
  • From this release: download the .ankiaddon below and double-click (or Tools → Add-ons → Install from file).

New Contributors

Full Changelog: v0.23.0...v0.24.0

v0.23.0

Choose a tag to compare

@github-actions github-actions released this 28 Jun 06:31

What's New

Manage note-type fields — new model_fields tool

AI clients can now reshape an existing note type's fields without opening Anki's Fields dialog by hand. The tool exposes three actions:

  • add — add a new field, optionally at a specific 0-based position (defaults to appended at the end).
  • rename — rename a field, preserving its content.
  • reposition — move a field to a new position.

A fourth action, remove, permanently deletes a field and all of its content. Because that's irreversible bulk data loss, it's treated as destructive: hidden from MCP clients by default and revealed only when the operator opts in via enabled_destructive_tools (e.g. "model_fields:remove").

Operator caveats baked into the tool:

  • Every action changes the note-type schema, so each result warns that the next sync will be a one-way full sync — overwriting your collection on AnkiWeb and other devices. Sync your other devices first if they hold unsynced changes.
  • Rename does not rewrite card templates: any {{OldFieldName}} reference renders blank until you fix it with update_model_templates. The tool flags this in its response.

Closes #57.

Install

  • AnkiWeb: Tools → Add-ons → Get Add-ons… → code 124672614
  • From this release: download the .ankiaddon below and double-click (or Tools → Add-ons → Install from file).

Full Changelog: v0.22.0...v0.23.0

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 25 Jun 18:03

What's New in v0.22.0

🩺 Diagnostic file logging

New opt-in log_to_file setting writes a rotating log to user_files/ankimcp.log (~1 MB × 3 backups) with secrets — the http_api_key, OAuth tokens, and any Bearer token — redacted before anything reaches disk. The settings dialog gains Open log folder and Copy diagnostics buttons. When the add-on can't start (for example a blocked pydantic_core download), you can now capture and share the real cause instead of a cryptic error. Off by default.

🔁 More resilient first-run dependency download

The runtime pydantic_core download is hardened: it retries transient native-library file locks, uses an atomic cache swap so a flaky re-download never destroys a working install, and reports the real failure reason (network, DNS, SSL, …) instead of a generic "not available".

🔎 Clearer dependency-conflict diagnostics

When another add-on has already loaded a shared library, the startup warning now reports the actual version and file path of the conflicting copy — making cross-add-on issues far easier to pin down.

Under the hood

  • Packaging: exclude the runtime _cache/ directory from the .ankiaddon, so a local build can never ship a stray platform-specific native binary.
  • CI: per-tag concurrency guard on the release workflow.

Validated on macOS (arm64), Windows 11 (arm64), Ubuntu/snap (aarch64), and NixOS.

Full Changelog: v0.21.2...v0.22.0

v0.21.2

Choose a tag to compare

@github-actions github-actions released this 20 Jun 16:39

What's Changed

  • fix: ensure rpds at runtime (cross-platform), + cross-platform & Anki-compat CI (#54) by @anatoly314 in #55

Full Changelog: v0.21.1...v0.21.2

v0.21.1

Choose a tag to compare

@github-actions github-actions released this 18 Jun 13:03

v0.21.1 — Apple Silicon startup crash fix

Fixed

  • Wrong pydantic_core wheel on Apple Silicon (#52). On Macs where Anki runs a universal2 framework Python, the add-on downloaded the x86_64 pydantic_core wheel instead of the arm64 one, so the MCP server crashed on startup with:
    ImportError: dlopen(..._pydantic_core...darwin.so):
    mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')
    
    Root cause: architecture was inferred from sysconfig.get_platform(), which a universal2 (fat) build reports as macosx-…-universal2 — a string containing neither arm64 nor aarch64, so Apple Silicon was misread as x86_64. Detection now also consults platform.machine(), which reflects the architecture the process is actually running as (and stays correct under Rosetta).

Who was affected

  • Apple Silicon Macs whose Anki uses a universal2 framework Python. Macs that run a native-arm64 Python (e.g. Anki's newer uv-based launcher), Intel Macs, Linux, and Windows were never affected. Source / Nix installs never hit this code path.

Upgrading

  • Already-broken installs self-heal on the next launch — the stale x86_64 binary fails to load, which transparently triggers a re-download of the correct arm64 wheel. No manual cache wipe needed.

Notes

  • Bugfix-only patch release; no API or config changes.
  • Added unit regression coverage for wheel selection across universal2 / native-arm64 / Intel / Rosetta / Linux.

Thanks to @ksuenaga — and his AI agent, which filed a remarkably precise report that pinpointed both the root cause and the fix. Filed by one AI agent, fixed by another. 🙂

Full Changelog: v0.21.0...v0.21.1

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 18 Jun 12:00

v0.21.0 — Security: DNS-rebinding protection + optional API key

Security

  • DNS-rebinding protection is now enabled by default on the local HTTP transport (GHSA-j9xx-59ph-wmr6). The server validates the Host and Origin of every request against a loopback allowlist (127.0.0.1, localhost, [::1]). A forged Host is rejected with 421, a forged Origin with 403. This closes a vector where a malicious web page could DNS-rebind to your local server and invoke tools. The tunnel (WebSocket) transport is unaffected.

Added

  • http_allowed_hosts / http_allowed_origins — extend the allowlist to expose the HTTP server through a tunnel or reverse proxy, instead of disabling protection. Host entries are scheme-less (myapp.ngrok.io); origin entries include the scheme (https://myapp.example).
  • http_api_key — optional shared-secret auth (AnkiConnect-style). When set, every HTTP request must send Authorization: Bearer <key> (constant-time compared); failures return 403. Empty by default (disabled).

Backward compatibility

  • Local-only use is unaffected. If you reached the HTTP server under a non-loopback name (bound to 0.0.0.0, or behind ngrok / Cloudflare / a reverse proxy), those requests now return 421 Invalid Host header until you add the host to http_allowed_hosts (and the origin to http_allowed_origins for browser clients), or run ngrok http --host-header=rewrite 3141.

Thanks to @avishaigo-commits for the responsible disclosure.