Anki MCP server agent

A Model Context Protocol (MCP) server that enables AI assistants to interact with Anki, the spaced repetition flashcard application.

by ankimcp·MIT license·★ 505 Stars on the repo·GitHub ↗

Files of Anki MCP server

ankimcp/main1 file
README.md
Show the full text1030 lines
anki-mcp-server/README.md1030 lines · 49.0 KB
Outline
RawView on GitHub

Anki MCP Server

Tests npm version

Anki + MCP Integration

Seamlessly integrate Anki with AI assistants through the Model Context Protocol

Beta - This project is in active development. APIs and features may change.

A Model Context Protocol (MCP) server that enables AI assistants to interact with Anki, the spaced repetition flashcard application.

Transform your Anki experience with natural language interaction - like having a private tutor. The AI assistant doesn't just present questions and answers; it can explain concepts, make the learning process more engaging and human-like, provide context, and adapt to your learning style. It can create and edit notes on the fly, turning your study sessions into dynamic conversations. More features coming soon!

Examples and Tutorials

For comprehensive guides, real-world examples, and step-by-step tutorials on using this MCP server with Claude Desktop, visit:

ankimcp.ai - Complete documentation with practical examples and use cases

See docs/ for supplementary documentation, including the reviewer setup guide and the sample Anki deck.

Example Use Cases

Three representative prompts showing the tool flows this server enables:

  1. "Help me review my Spanish deck." — The assistant offers to sync with AnkiWeb (sync), fetches due cards (get_due_cards with deck filter), presents each card (present_card), and records your rating (rate_card). Natural study conversation with explanations tailored to you.

  2. "Create 10 Arabic vocab cards with RTL styling." — The assistant lists note types (modelNames), creates a custom RTL model if needed (createModel + updateModelStyling for right-to-left CSS), then batch-creates the cards (addNotes).

  3. "Import this image from my Downloads folder into the front of the selected note." — The assistant uploads the local file (storeMediaFile with a file path), reads the currently-selected note from the browser (guiSelectedNotes + notesInfo), and updates the front field with an <img> tag (updateNoteFields).

Available Tools

The server exposes 53 MCP tools — 42 essential tools for everyday Anki operations and 11 GUI tools that drive the Anki desktop interface for note editing/creation workflows.

Essential Tools
Review & Study
  • sync - Sync with AnkiWeb to pull latest data and push changes
  • get_due_cards - Get cards that are due for review, optionally filtered by deck (answers omitted unless include_answer: true, default false)
  • get_cards - Get cards with flexible filtering by state (due, new, learning, suspended, buried) and deck (answers omitted unless include_answer: true, default false)
  • present_card - Show a card for review with its question/front side
  • rate_card - Rate card performance (Again, Hard, Good, Easy) and schedule the next review
  • forgetCards - Reset cards to new, discarding their scheduling without recording a review
  • setDueDate - Reschedule cards to become due in N days ("0", "3-7", "1!"), without recording a review
  • areSuspended - Check suspension state for one or more cards without changing anything
  • suspend - Suspend cards so they are skipped during review until unsuspended
  • unsuspend - Unsuspend cards so they return to normal review

Note: forgetCards and setDueDate change scheduling without logging a review, which is what separates them from rate_card. Reach for them when a card's schedule is wrong rather than the answer: rating a card Again to bury it deeper records a real lapse and drops its ease factor, permanently skewing both future scheduling and your statistics. forgetCards wipes the interval and starts the card over; setDueDate keeps the card's history and just moves the next review.

Note: Card front/back content is rendered per card from its own template (as Anki shows it), so reversed and cloze cards display the correct direction. Static text added by your card templates appears in the output as well.

Deck Management
  • listDecks - List all decks, optionally with per-deck study-queue statistics
  • deckStats - Get comprehensive statistics for a single deck (study queue, true card-state counts, ease/interval distributions)
  • createDeck - Create a new empty deck (supports Parent::Child, max 2 levels)
  • changeDeck - Move cards to a different deck (created if it doesn't exist)

Note: Deck statistics come in two flavours. The counts block (and everything listDecks reports) mirrors Anki's deck browser: cards due today, capped by each deck's daily new/review limits, with suspended and buried cards excluded — so review is not "mature cards" and the other bucket is just the arithmetic remainder (mostly review cards not due today plus new cards over the daily limit). For true per-state totals use the states block on deckStats / collection_stats, which counts new, learning, review, suspended and buried via Anki searches, ignoring due dates and daily limits.

Note Management
  • addNote - Create a single note with specified fields and tags
  • addNotes - Batch-create up to 100 notes sharing a deck and model (partial success supported)
  • findNotes - Search for notes using Anki query syntax (deck:, tag:, is:due, etc.)
  • notesInfo - Get detailed information about notes (fields, tags, note type, card IDs; CSS comes from modelStyling)
  • updateNoteFields - Update existing note fields (CSS-aware, supports HTML content)
  • deleteNotes - Delete notes and all associated cards (destructive, requires confirmation)
Tag Management
  • getTags - Get all tags in the collection (use first to avoid duplication)
  • addTags - Add space-separated tags to specified notes
  • removeTags - Remove space-separated tags from specified notes
  • replaceTags - Rename a tag across specified notes
  • clearUnusedTags - Remove orphaned tags not used by any notes (destructive)
Media Management
  • getMediaFilesNames - List media files in collection.media, optionally filtered by pattern
  • retrieveMediaFile - Download a media file as base64 content
  • storeMediaFile - Upload media from base64 data, an absolute file path, or a URL
  • deleteMediaFile - Remove a media file from collection.media (destructive)

💡 Best Practice for Images:

  • ✅ Use file paths (e.g., /Users/you/image.png) - Fast and efficient
  • ✅ Use URLs (e.g., https://example.com/image.jpg) - Direct download
  • ❌ Avoid base64 - Extremely slow and token-inefficient

Just tell Claude where the image is, and it will handle the upload automatically using the most efficient method.

Model/Template Management
  • modelNames - List all available note types/models
  • modelFieldNames - Get field names for a specific note type
  • modelStyling - Get CSS styling information for a note type
  • modelTemplates - Get the card templates (Front and Back HTML) for a note type
  • createModel - Create a new note type with custom fields, card templates, and CSS (e.g., RTL models)
  • updateModelStyling - Update the CSS styling for an existing note type (applies to all its cards)
  • updateModelTemplates - Update the card templates (Front and Back HTML) for an existing note type (applies to all its cards)
  • addModelField - Add a new field to an existing note type (appended at the end or inserted at a specific position)
  • removeModelField - Remove a field from an existing note type (deletes its content from all notes; requires explicit confirmation)
  • renameModelField - Rename a field in an existing note type (Anki rewrites references to the old name in the card templates)
  • repositionModelField - Change the position of a field within an existing note type
Statistics
  • collection_stats - Aggregated statistics across all decks with per-deck breakdown and collection-wide card-state counts
  • review_stats - Review history analysis (temporal patterns, retention metrics, study streaks)
GUI Tools

Tools that drive the Anki desktop interface. Intended for note editing/creation and deck-management workflows, not for review sessions.

  • guiBrowse - Open the Card Browser and search for cards
  • guiSelectCard - Select a specific card in the Card Browser
  • guiSelectedNotes - Get IDs of notes currently selected in the Card Browser
  • guiAddCards - Open the Add Cards dialog with preset note details
  • guiEditNote - Open the note editor for a specific note
  • guiDeckOverview - Open the Deck Overview dialog for a specific deck
  • guiDeckBrowser - Open the Deck Browser dialog
  • guiCurrentCard - Get info about the current card in review mode (includes the answer; errors outside review mode)
  • guiShowQuestion - Show the question side of the current card
  • guiShowAnswer - Show the answer side of the current card
  • guiUndo - Undo the last action in Anki

Prerequisites

Installation

There are a few ways to get the server onto your machine. Once it's installed, head to Connecting an AI Client to wire it up to your AI assistant — locally or remotely.

npm (global or npx)

The general-purpose way to install the server, suitable for any MCP client that launches it directly.

Install it globally for clients that run the ankimcp command:

npm install -g @ankimcp/anki-mcp-server

Or run it on demand with no install required:

npx @ankimcp/anki-mcp-server

The easiest way to install this MCP server for Claude Desktop:

  1. Download the latest .mcpb bundle from the Releases page
  2. In Claude Desktop, install the extension:
    • Method 1: Go to Settings → Extensions, then drag and drop the .mcpb file
    • Method 2: Go to Settings → Developer → Extensions → Install Extension, then select the .mcpb file
  3. Configure AnkiConnect URL if needed (defaults to http://localhost:8765)
  4. Restart Claude Desktop

That's it! The bundle includes everything needed to run the server locally.

For Anthropic MCP Directory reviewers: a zero-to-integration walkthrough with a pre-populated sample deck lives in docs/reviewer-setup.md.

Install from Source (for development)

For development or advanced usage (running the test suite requires Node.js 24.9+ — the npm test scripts load the ESM-only NestJS 12 packages via require(esm), which Jest only supports there; the runtime requirement for using the server stays 22.12.0+):

npm install
npm run build

Connecting an AI Client

There are two ways an AI assistant can reach this server, depending on where the assistant runs:

  • Local — the server runs on the same machine as the AI client (Claude Desktop, Cursor, Cline, Zed, or a local browser session). Use STDIO for desktop MCP clients, HTTP for local web-based tools.
  • Remote — a hosted/remote AI (e.g. ChatGPT or Claude.ai in the cloud) needs to reach the Anki running on your local machine. Use the managed Tunnel (✅ recommended — authenticated) or, as a lighter-weight unauthenticated alternative, ngrok.
Local

The server runs on the same computer as your AI client and talks to AnkiConnect on localhost.

STDIO (primary local integration)

STDIO is the standard transport for local desktop MCP clients — Claude Desktop, Cursor IDE, Cline, Zed Editor, opencode, and others. The client launches the server as a subprocess and communicates over standard input/output.

Supported Clients:

For Claude Desktop, the MCPB bundle is the easiest path. For other clients, configure the npm package with the --stdio flag.

Configuration - Choose one method:

Method 1: Using npx (recommended - no installation needed)

{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

Method 2: Using global installation

First, install globally:

npm install -g @ankimcp/anki-mcp-server

Then configure:

{
  "mcpServers": {
    "anki-mcp": {
      "command": "ankimcp",
      "args": ["--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

opencode uses its own format — servers go under mcp, the command is a single array, and environment variables go under environment:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "anki-mcp": {
      "type": "local",
      "command": ["npx", "-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "enabled": true,
      "environment": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

Configuration file locations:

  • Cursor IDE: ~/.cursor/mcp.json (macOS/Linux) or %USERPROFILE%\.cursor\mcp.json (Windows)
  • Cline: Accessible via settings UI in VS Code
  • Zed Editor: Install as MCP extension through extension marketplace
  • opencode: opencode.json in your project root, or ~/.config/opencode/opencode.json globally

For client-specific features and troubleshooting, consult your MCP client's documentation. See also Connect to Claude Desktop for a config that points directly at a built dist/main-stdio.js.

HTTP (local web-based AI)

HTTP mode runs the server as a local web server speaking the MCP Streamable HTTP protocol. It's the transport a web-based AI tool talks to when pointed at your machine, and it's also what the Remote options expose to the outside world. On its own, HTTP mode binds to localhost only.

Binding beyond localhost? If you pass --host 0.0.0.0 (or run behind a reverse proxy/public domain), the server only accepts loopback Host headers by default for DNS-rebinding protection — set ALLOWED_HOSTS to the hostname(s) clients use. See HTTP Mode Configuration.

Setup - Choose one method:

Method 1: Using npx (recommended - no installation needed)

# Quick start
npx @ankimcp/anki-mcp-server

# With custom options
npx @ankimcp/anki-mcp-server --port 8080 --host 0.0.0.0
npx @ankimcp/anki-mcp-server --anki-connect http://localhost:8765

Method 2: Using global installation

# Install once
npm install -g @ankimcp/anki-mcp-server

# Run the server
ankimcp

# With custom options
ankimcp --port 8080 --host 0.0.0.0
ankimcp --anki-connect http://localhost:8765

Method 3: Install from source (for development)

npm install
npm run build
npm run start:prod:http

To make a local HTTP server reachable by a cloud-hosted AI, use one of the Remote options below.

Remote

A hosted/remote AI (such as ChatGPT or Claude.ai running in the cloud) can't reach localhost directly. These options expose your local Anki to the internet so a remote assistant can talk to it.

Recommended remote path — authenticated & secure. Unlike a raw public port, tunnel mode requires you to log in (OAuth 2.0 device flow), so the endpoint isn't open to anyone who guesses the URL.

Tunnel mode lets web-based AI assistants reach your local Anki without running your own tunnel. The server connects out to the managed AnkiMCP tunnel service (wss://tunnel.ankimcp.ai) over a WebSocket and is assigned a public URL. Authentication is built in — no ngrok account or separate tunnel process required, and you log in once.

Log in (OAuth device flow):

Tunnel mode uses the OAuth 2.0 Device Authorization Grant. Logging in opens your browser automatically to an approval page with the code already embedded in the URL — nothing to type, just approve. (If the browser can't open, the terminal prints a verification URL and code to enter manually as a fallback.) On success, credentials are saved to ~/.ankimcp/credentials.json (file permissions 0600).

# Pre-authenticate (optional — --tunnel will trigger this automatically if needed)
ankimcp --login
npx @ankimcp/anki-mcp-server --login

# Clear saved credentials
ankimcp --logout

Start the tunnel:

# Connect to the managed tunnel service (wss://tunnel.ankimcp.ai)
ankimcp --tunnel
npx @ankimcp/anki-mcp-server --tunnel

# Override the tunnel server URL (must be ws:// or wss://) — e.g. for self-hosting
ankimcp --tunnel wss://my-tunnel.example.com

If no credentials exist, --tunnel automatically starts the login flow first, then continues to the tunnel. This auto-login requires an interactive terminal — when stdout is not a TTY (systemd, headless Docker, CI), the server fast-fails and asks you to run ankimcp --login first. Once connected, the public tunnel URL is printed; press Ctrl+C to disconnect. Share that URL with your AI assistant.

Tunnel-mode environment variables:

Variable Description Default
TUNNEL_SERVER_URL Tunnel server WebSocket URL (the --tunnel/--login flag value overrides this) wss://tunnel.ankimcp.ai
TUNNEL_AUTH_CLIENT_ID OAuth client ID for the device flow. Advanced — only needed when pointing at a self-hosted tunnel/auth service. (built-in)

The device-flow auth endpoints (/auth/device, /auth/token) are derived from TUNNEL_SERVER_URL, so pointing --tunnel (or TUNNEL_SERVER_URL) at a different host also moves authentication to that host.

How it works: Tunnel mode runs the MCP server in-process behind an in-memory transport (TunnelTransport). That transport owns the MCP server and turns each relayed request body into a response, and TunnelClient bridges it to the remote tunnel service over a WebSocket — relaying MCP requests in and responses out. AnkiConnect is still only ever reached on your local machine.

Protocol revisions: Because the tunnel connects the MCP server in-process, tunnel mode serves the 2025 revision of the MCP protocol only, while STDIO and HTTP modes serve both 2025 and the newer 2026-07-28 revision. Every tool behaves the same either way — but a client that speaks only 2026-07-28 is turned away over the tunnel with a protocol-version error; run STDIO or HTTP mode for that client.

ngrok (unauthenticated alternative)

If you'd rather expose local HTTP mode publicly without an account on the managed tunnel, the built-in --ngrok flag launches an ngrok subprocess (src/services/ngrok.service.ts) and prints the public URL in the startup banner:

# One-time ngrok setup, then:
ankimcp --ngrok

This route is unauthenticated — anyone with the URL can reach your Anki, so it's less secure than Tunnel. Prefer Tunnel unless you have a specific reason to manage your own ngrok endpoint. (Requires a global ngrok install and authtoken.)

The --ngrok flag launches ngrok with --host-header=rewrite, so ngrok rewrites the upstream Host to localhost before forwarding. That keeps requests within the loopback Host allowlist (see DNS-rebinding protection) without you having to add the public *.ngrok domain to ALLOWED_HOSTS. If you instead run ngrok manually, use the same flag — ngrok http --host-header=rewrite 3000 — otherwise ngrok forwards the public ngrok hostname as Host and the server rejects it with 403.

CLI Options (all modes)
ankimcp [options]

Options:
  --stdio                        Run in STDIO mode (for MCP clients)
  --tunnel [url]                 Connect via the managed tunnel (authenticated)
  --login                        Authenticate for tunnel mode (OAuth device flow)
  --logout                       Clear saved tunnel credentials
  -p, --port <number>            Port to listen on (HTTP mode; default: 3000, or PORT env var)
  -h, --host <address>           Host to bind to (HTTP mode; default: 127.0.0.1, or HOST env var)
  -a, --anki-connect <url>       AnkiConnect URL (default: http://localhost:8765, or ANKI_CONNECT_URL env var)
  --ngrok                        Start ngrok tunnel (requires global ngrok installation)
  --read-only                    Run in read-only mode (blocks content changes and rescheduling; rating, suspend and sync still work)
  --help                         Show help message

Usage with npx (no installation needed):
  npx @ankimcp/anki-mcp-server                        # HTTP mode
  npx @ankimcp/anki-mcp-server --port 8080            # Custom port
  npx @ankimcp/anki-mcp-server --stdio                # STDIO mode
  npx @ankimcp/anki-mcp-server --tunnel               # Managed tunnel mode
  npx @ankimcp/anki-mcp-server --ngrok                # HTTP mode with ngrok tunnel
  npx @ankimcp/anki-mcp-server --read-only            # Read-only mode

Usage with global installation:
  npm install -g @ankimcp/anki-mcp-server             # Install once
  ankimcp                                             # HTTP mode
  ankimcp --port 8080                                 # Custom port
  ankimcp --stdio                                     # STDIO mode
  ankimcp --tunnel                                    # Managed tunnel mode
  ankimcp --ngrok                                     # HTTP mode with ngrok tunnel
  ankimcp --read-only                                 # Read-only mode
Read-Only Mode (all modes)

The --read-only flag (or READ_ONLY=true) blocks changes to note content, decks, tags, media and note types, and manual rescheduling of cards. When enabled:

  • All read operations work normally (browsing decks, viewing cards, searching notes)
  • These changes are still allowed: rating a card during review (rate_card), suspending/unsuspending cards (suspend, unsuspend), syncing with AnkiWeb (sync), and the GUI tools that navigate Anki's windows (guiBrowse, guiDeckOverview, …)
  • Content modifications are blocked (addNote, deleteNotes, createDeck, updateNoteFields, etc.); createDeck for a deck that already exists still succeeds, since nothing is written
  • Manual rescheduling is blocked: forgetCards (reset to new) and setDueDate
  • guiUndo is blocked, since an undo can revert a change to the collection
  • guiAddCards and guiEditNote are blocked, since a note added or saved in the dialog they open is written to the collection
  • Useful for exploring Anki data without accidental changes to its content
# HTTP mode with read-only
ankimcp --read-only

# STDIO mode with read-only
ankimcp --stdio --read-only

# Can combine with other flags
ankimcp --ngrok --read-only

You can also enable read-only mode via environment variable:

READ_ONLY=true ankimcp

Or in MCP client configuration:

{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio", "--read-only"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

Connect to Claude Desktop (Local Mode)

You can configure the server in Claude Desktop by either:

  • Going to: Settings → Developer → Edit Config
  • Or manually editing the config file
Configuration

Add the following to your Claude Desktop config:

{
  "mcpServers": {
    "anki-mcp": {
      "command": "node",
      "args": ["/path/to/anki-mcp-server/dist/main-stdio.js"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

Replace /path/to/anki-mcp-server with your actual project path.

Config File Locations
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

For more details, see the official MCP documentation.

Environment Variables (Optional)

Variable Description Default
ANKI_CONNECT_URL AnkiConnect URL http://localhost:8765
ANKI_CONNECT_API_VERSION API version 6
ANKI_CONNECT_API_KEY API key if configured in AnkiConnect -
ANKI_CONNECT_TIMEOUT Request timeout in ms 5000
READ_ONLY Enable read-only mode (true or 1) false
PORT HTTP mode: port to listen on (--port flag takes precedence) 3000
HOST HTTP mode: address to bind to (--host flag takes precedence) 127.0.0.1
ALLOWED_HOSTS HTTP mode: extra Host header values to accept beyond loopback (comma-separated hostnames). Required when binding to a LAN/public address or running behind a reverse proxy. See HTTP Mode Configuration. loopback only
ALLOWED_ORIGINS HTTP mode: comma-separated allowlist of browser Origin/Referer patterns (wildcards supported, e.g. https://*.ngrok.io). http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*
TUNNEL_SERVER_URL Tunnel server WebSocket URL (tunnel mode only) wss://tunnel.ankimcp.ai
MEDIA_ALLOWED_TYPES Extra MIME types to allow for file path imports (comma-separated, e.g., application/pdf) -
MEDIA_IMPORT_DIR Restrict file path imports to this directory -
MEDIA_ALLOWED_HOSTS Allow specific private network hosts for URL imports (comma-separated, e.g., 192.168.1.50,my-nas) -

Usage Examples

Searching and Updating Notes
# Search for notes in a specific deck
findNotes(query: "deck:Spanish")

# Get detailed information about notes
notesInfo(notes: [1234567890, 1234567891])

# Update a note's fields (HTML content supported)
updateNoteFields(note: {
  id: 1234567890,
  fields: {
    "Front": "<b>¿Cómo estás?</b>",
    "Back": "How are you?"
  }
})

# Delete notes (requires confirmation)
deleteNotes(notes: [1234567890], confirmDeletion: true)
Anki Query Syntax Examples

The findNotes tool supports Anki's powerful query syntax:

  • "deck:DeckName" - All notes in a specific deck
  • "tag:important" - Notes with the "important" tag
  • "is:due" - Cards that are due for review
  • "is:new" - New cards that haven't been studied
  • "added:7" - Notes added in the last 7 days
  • "front:hello" - Notes with "hello" in the front field
  • "flag:1" - Notes with red flag
  • "prop:due<=2" - Cards due within 2 days
  • "deck:Spanish tag:verb" - Spanish deck notes with verb tag (AND)
  • "deck:Spanish OR deck:French" - Notes from either deck
Important Notes
CSS and HTML Handling
  • The notesInfo tool returns each note's note type name; the CSS itself comes from modelStyling
  • The updateNoteFields tool supports HTML content in fields and preserves CSS styling
  • Each note model has its own CSS styling - use modelStyling to get model-specific CSS
Update Warning

⚠️ IMPORTANT: When using updateNoteFields, do NOT view the note in Anki's browser while updating, or the fields will not update properly. Close the browser or switch to a different note before updating. See Known Issues for more details.

Deletion Safety

The deleteNotes tool requires explicit confirmation (confirmDeletion: true) to prevent accidental deletions. Deleting a note removes ALL associated cards permanently.

Security

Media File Path and URL Validation

The media tools (storeMediaFile, retrieveMediaFile, deleteMediaFile) and updateNoteFields audio/picture fields include security validation to prevent misuse via prompt injection:

  • File path imports are restricted to media file types only (images, audio, video). Non-media files (e.g., SSH keys, credentials, shell configs) are rejected based on MIME type. Configure MEDIA_ALLOWED_TYPES to allow additional file types, or MEDIA_IMPORT_DIR to restrict imports to a specific directory.
  • URL imports are validated against SSRF attacks. Requests to private networks (10.x, 172.16.x, 192.168.x), loopback (127.x), link-local (169.254.x), and non-HTTP(S) schemes are blocked. Configure MEDIA_ALLOWED_HOSTS to allow specific private network hosts.
  • Filenames are sanitized to prevent path traversal (e.g., ../../ sequences are stripped).

These protections apply to storeMediaFile, retrieveMediaFile, deleteMediaFile, and updateNoteFields audio/picture fields.

Path traversal vulnerability reported by Hideaki Takahashi.

DNS-Rebinding Protection (HTTP transport)

When running in HTTP mode, the server validates the Host header on every request. By default only loopback hosts (localhost, 127.0.0.1, ::1) are accepted, regardless of port. Host is a browser-forbidden header, so a malicious web page cannot forge it — this closes the DNS-rebinding path where a rebound page reaches the local server with a spoofed Host and no Origin, and reaches the MCP tools. A disallowed Host is rejected with 403.

If you bind to 0.0.0.0, run behind a reverse proxy, or expose a public tunnel domain, set ALLOWED_HOSTS (comma-separated hostnames) to permit those hosts. When tunneling with ngrok the server uses --host-header=rewrite, so the upstream still sees a loopback Host. See HTTP Mode Configuration for the full list of options.

DNS-rebinding vulnerability reported by avishaigo-commits and yotampe-pluto.

Privacy Policy

This MCP server runs locally on your machine and collects no telemetry, analytics, or usage data.

Full policy: https://ankimcp.ai/privacy/

  • Data collection: The server collects nothing. It proxies requests between your AI assistant and your local AnkiConnect plugin.
  • Usage / storage: No server-side storage. All flashcard data stays in your Anki installation on your own device.
  • Third-party sharing: None. The server only talks to the AnkiConnect URL you configure (default: localhost). If you enable Anki's built-in AnkiWeb sync, that happens between your Anki install and AnkiWeb directly — outside this server's scope.
  • Retention: Not applicable — no data is retained server-side.
  • Contact: [email protected]

Known Issues

For a comprehensive list of known issues and limitations, please visit our documentation:

Known Issues Documentation

Critical Limitations
Note Updates Fail When Viewed in Browser

⚠️ IMPORTANT: When updating notes using updateNoteFields, the update will silently fail if the note is currently being viewed in Anki's browser window. This is an upstream AnkiConnect limitation.

Workaround: Always close the browser or navigate to a different note before updating.

For more details and other known issues, see the full documentation.

Troubleshooting

ERR_REQUIRE_ESM Error

If you see an error like:

Error [ERR_REQUIRE_ESM]: require() of ES Module not supported

This means your Node.js version is not supported. The server requires Node.js 22.12.0+.

Note: The minimum supported runtime is Node.js 22.12.0. Node.js 20 (Iron) reached end-of-life on 2026-04-30 and is no longer supported.

Check your version:

node --version

Solution: Update Node.js to version 22.12.0+. You can download it from nodejs.org or use a version manager like nvm.

Development

Transport Modes

This server supports three MCP transport modes via separate entry points:

STDIO Mode (Default)
  • For local MCP clients like Claude Desktop
  • Uses standard input/output for communication
  • Entry point: dist/main-stdio.js
  • Run: npm run start:prod:stdio or node dist/main-stdio.js
  • MCPB bundle: Uses STDIO mode
HTTP Mode (Streamable HTTP)
  • For remote MCP clients and web-based integrations
  • Uses MCP Streamable HTTP protocol
  • Entry point: dist/main-http.js
  • Run: npm run start:prod:http or node dist/main-http.js
  • Default port: 3000 (configurable via PORT env var)
  • Default host: 127.0.0.1 (configurable via HOST env var)
  • MCP endpoint: http://127.0.0.1:3000/ (root path)
Tunnel Mode (Managed WebSocket Tunnel)
  • For web-based AI assistants via the managed AnkiMCP tunnel service, with built-in authentication
  • The MCP server runs in-process behind an in-memory transport; TunnelTransport owns the MCP server and TunnelClient bridges it to the tunnel service over a WebSocket
  • Protocol: serves the 2025 MCP revision only (STDIO and HTTP also serve 2026-07-28)
  • Entry point: dist/main-tunnel.js
  • Run: node dist/main-tunnel.js --tunnel (or ankimcp --tunnel)
  • Auth: ankimcp --login / ankimcp --logout; credentials stored at ~/.ankimcp/credentials.json (0600)
  • Dev: npm run start:dev:tunnel (watch mode, runs --tunnel --debug)
Building
npm run build  # Builds once, creates dist/ with all three entry points

main-stdio.js, main-http.js, and main-tunnel.js are all built into the same dist/ directory. Choose which to run based on your needs.

HTTP Mode Configuration

Environment Variables:

  • PORT - HTTP server port (default: 3000)
  • HOST - Bind address (default: 127.0.0.1 for localhost-only)
  • ALLOWED_HOSTS - Comma-separated extra Host header values to accept beyond the built-in loopback set (localhost, 127.0.0.1, ::1). Hostname-only and port-agnostic. Default: loopback only.
  • ALLOWED_ORIGINS - Comma-separated allowlist of browser Origin/Referer patterns; wildcards supported (e.g. https://*.ngrok.io). Default: http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*.
  • LOG_LEVEL - Logging level (default: info)

Security:

  • Host header validation (DNS-rebinding protection) — every HTTP request must carry a Host header that matches the allowlist. By default only loopback hosts (localhost, 127.0.0.1, ::1) are accepted, regardless of port. Host is a browser-forbidden header, so a malicious web page cannot forge it — this closes the DNS-rebinding path where a rebound page reaches the server with a spoofed Host and no Origin. A disallowed Host is rejected with 403.
  • Origin header validation — browser requests with a present-but-disallowed Origin/Referer are rejected. Requests with no Origin (curl, Postman, MCP-over-HTTP clients) are allowed; Host validation is the defense against rebinding.
  • Binds to localhost (127.0.0.1) by default.
  • No authentication in current version (OAuth support planned).

Exposing HTTP mode beyond localhost — if you bind to a LAN/public address or put the server behind a reverse proxy or public domain, you must set ALLOWED_HOSTS to the hostname(s) clients will use, otherwise every non-loopback request is rejected with 403:

# Bind to all interfaces and accept the machine's LAN name + a public domain
ALLOWED_HOSTS=my-nas.local,anki.example.com PORT=8080 HOST=0.0.0.0 node dist/main-http.js

When you bind to 0.0.0.0/:: without ALLOWED_HOSTS, the server logs a startup warning that only loopback Host headers will be accepted.

Docker / reverse proxy / public domain: the same rule applies. In Docker, requests usually arrive with the container's published hostname or the proxy's Host, so set ALLOWED_HOSTS accordingly. A reverse proxy (nginx, Caddy, Traefik) should either forward the original Host and have that hostname listed in ALLOWED_HOSTS, or rewrite the upstream Host to localhost. The built-in --ngrok integration handles this automatically (see below).

Example: Running Modes

# Development - STDIO mode (watch mode with auto-rebuild)
npm run start:dev:stdio

# Development - HTTP mode (watch mode with auto-rebuild)
npm run start:dev:http

# Production - STDIO mode
npm run start:prod:stdio
# or
node dist/main-stdio.js

# Production - HTTP mode
npm run start:prod:http
# or
PORT=8080 HOST=0.0.0.0 node dist/main-http.js
Building an MCPB Bundle

To create a distributable MCPB bundle:

npm run mcpb:bundle

This command will:

  1. Sync version from package.json to manifest.json
  2. Remove old .mcpb files
  3. Build the TypeScript project
  4. Package dist/ and node_modules/ into an .mcpb file
  5. Run mcpb clean to remove devDependencies (optimizes bundle from ~47MB to ~10MB)

The output file will be named anki-mcp-server-X.X.X.mcpb and can be distributed for one-click installation.

What Gets Bundled

The MCPB bundle includes:

  • Compiled JavaScript (dist/ directory - includes all three entry points)
  • Production dependencies only (node_modules/ - devDependencies removed by mcpb clean)
  • Package metadata (package.json)
  • Manifest configuration (manifest.json - configured to use main-stdio.js)
  • Icon (icon.png)

Source files, tests, and development configs are automatically excluded via .mcpbignore.

Logging in Claude Desktop

When running as an MCPB extension in Claude Desktop, logs are written to:

Log Location: ~/Library/Logs/Claude/ (macOS)

The logs are split across multiple files:

  • main.log - General Claude Desktop application logs
  • mcp-server-Anki MCP Server.log - MCP protocol messages for this extension
  • mcp.log - Combined MCP logs from all servers

Note: The pino logger output (INFO, ERROR, WARN messages from the server code) goes to stderr and appears in the MCP-specific log files. Claude Desktop determines which log file receives which messages, but generally:

  • Application startup and MCP protocol communication → MCP-specific log
  • Server internal logging (pino) → Both MCP-specific log and sometimes main.log

To view logs in real-time:

tail -f ~/Library/Logs/Claude/mcp-server-Anki\ MCP\ Server.log
Debugging the MCP Server

You can debug the MCP server using the MCP Inspector and attaching a debugger from your IDE (WebStorm, VS Code, etc.).

Note for HTTP Mode: When testing HTTP mode (Streamable HTTP) with MCP Inspector, use "Connection Type: Via Proxy" to avoid CORS errors.

Step 1: Configure Debug Server in MCP Inspector

The mcp-inspector-config.json already includes a debug server configuration:

{
  "mcpServers": {
    "stdio-server-debug": {
      "type": "stdio",
      "command": "node",
      "args": ["--inspect-brk=9229", "dist/main-stdio.js"],
      "env": {
        "MCP_SERVER_NAME": "anki-mcp-stdio-debug",
        "MCP_SERVER_VERSION": "1.0.0",
        "LOG_LEVEL": "debug"
      },
      "note": "Anki MCP server with debugging enabled on port 9229"
    }
  }
}
Step 2: Start the Debug Server

Run the MCP Inspector with the debug server:

npm run inspector:debug

This will start the server with Node.js debugging enabled on port 9229 and pause execution at the first line.

Step 3: Attach Debugger from Your IDE
WebStorm
  1. Go to Run → Edit Configurations
  2. Add a new Attach to Node.js/Chrome configuration
  3. Set the port to 9229
  4. Click Debug to attach
VS Code
  1. Open the Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
  2. Select Debug MCP Server (Attach) configuration
  3. Press F5 to attach
Step 4: Set Breakpoints and Debug

Once attached, you can:

  • Set breakpoints in your TypeScript source files
  • Step through code execution
  • Inspect variables and call stack
  • Use the debug console for evaluating expressions

The debugger will work with source maps, allowing you to debug the original TypeScript code rather than the compiled JavaScript.

Debugging with Claude Desktop

You can also debug the MCP server while it runs inside Claude Desktop by enabling the Node.js debugger and attaching your IDE.

Step 1: Configure Claude Desktop for Debugging

Update your Claude Desktop config to enable debugging:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "anki-mcp": {
      "command": "node",
      "args": [
        "--inspect=9229",
        "<path_to_project>/anki-mcp-server/dist/main-stdio.js"
      ],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}

Key change: Add --inspect=9229 before the path to dist/main-stdio.js

Debug options:

  • --inspect=9229 - Start debugger immediately, doesn't block (recommended)
  • --inspect-brk=9229 - Pause execution until debugger attaches (for debugging startup issues)
Step 2: Restart Claude Desktop

After saving the config, restart Claude Desktop. The MCP server will now run with debugging enabled on port 9229.

Step 3: Attach Debugger from Your IDE
WebStorm
  1. Go to Run → Edit Configurations
  2. Click the + button and select Attach to Node.js/Chrome
  3. Configure:
    • Name: Attach to Anki MCP (Claude Desktop)
    • Host: localhost
    • Port: 9229
    • Attach to: Node.js < 8 or Chrome or Node.js > 6.3 (depending on WebStorm version)
  4. Click OK
  5. Click Debug (Shift+F9) to attach
VS Code
  1. Add to .vscode/launch.json:
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Attach to Anki MCP (Claude Desktop)",
      "port": 9229,
      "skipFiles": ["<node_internals>/**"],
      "sourceMaps": true,
      "outFiles": ["${workspaceFolder}/dist/**/*.js"]
    }
  ]
}
  1. Open the Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
  2. Select Attach to Anki MCP (Claude Desktop)
  3. Press F5 to attach
Step 4: Debug in Real-Time

Once attached, you can:

  • Set breakpoints in your TypeScript source files (e.g., src/mcp/primitives/essential/tools/create-model.tool.ts)
  • Use Claude Desktop normally - breakpoints will hit when tools are invoked
  • Step through code execution
  • Inspect variables and call stack
  • Use the debug console

Example: Set a breakpoint in create-model.tool.ts at line 119, then ask Claude to create a new model. The debugger will pause at your breakpoint!

Note: The debugger stays attached as long as Claude Desktop is running. You can detach/reattach anytime without restarting Claude Desktop.

Build Commands
npm run build              # Build the project (compile TypeScript to JavaScript)
npm run start:dev:stdio    # STDIO mode with watch (auto-rebuild)
npm run start:dev:http     # HTTP mode with watch (auto-rebuild)
npm run type-check         # Run TypeScript type checking
npm run lint               # Run ESLint
npm run mcpb:bundle        # Sync version, clean, build, and create MCPB bundle
NPM Package Testing (Local)

Test the npm package locally before publishing:

# 1. Create local package
npm run pack:local         # Builds and creates @ankimcp/anki-mcp-server-*.tgz

# 2. Install globally from local package
npm run install:local      # Installs from ./@ankimcp/anki-mcp-server-*.tgz

# 3. Test the command
ankimcp                    # Runs HTTP server on port 3000

# 4. Uninstall when done testing
npm run uninstall:local    # Removes global installation

How it works:

  • npm pack creates a .tgz file identical to what npm publish would create
  • Installing from .tgz simulates what users get from npm install -g ankimcp
  • This lets you test the full user experience before publishing to npm
Testing Commands
npm test              # Run all tests
npm run test:unit     # Run unit tests only
npm run test:tools    # Run tool-specific tests
npm run test:workflows # Run workflow integration tests
npm run test:e2e      # Run end-to-end tests
npm run test:cov      # Run tests with coverage report
npm run test:watch    # Run tests in watch mode
npm run test:debug    # Run tests with debugger
npm run test:ci       # Run tests for CI (silent, with coverage)
Test Coverage

The project maintains 70% minimum coverage thresholds for:

  • Branches
  • Functions
  • Lines
  • Statements

Coverage reports are generated in the coverage/ directory.

Versioning

This project follows Semantic Versioning with a pre-1.0 development approach:

  • 0.x.x - Beta/Development versions (current phase)

    • 0.1.x - Bug fixes and patches
    • 0.2.0+ - New features or minor improvements
    • Breaking changes are acceptable in 0.x versions
  • 1.0.0 - First stable release

    • Will be released when the API is stable and tested
    • Breaking changes will require major version bumps (2.0.0, etc.)

Current Status: 0.27.0 - Active beta development. Recent features include collection-wide review analysis (review_stats now aggregates across all decks when deck is omitted), model field management (addModelField, removeModelField, renameModelField, repositionModelField), batch note creation (addNotes), integrated ngrok tunneling (--ngrok flag), media file management, model/template management, and comprehensive deck statistics. APIs may change based on feedback and testing.

MCPB spec evolution

This project targets Anthropic's MCPB bundle specification, which is still evolving. We track the spec at https://github.com/modelcontextprotocol/mcpb and may introduce breaking changes to stay compliant. Breaking changes are permitted under the 0.x.x versioning scheme.

Similar Projects

If you're exploring Anki MCP integrations, here are other projects in this space:

scorzeth/anki-mcp-server
  • Status: Appears to be abandoned (no recent updates)
  • Early implementation of Anki MCP integration
nailuoGG/anki-mcp-server
  • Approach: Lightweight, single-file implementation
  • Architecture: Procedural code structure with all tools in one file
  • Good for: Simple use cases, minimal dependencies

Why this project differs:

  • Enterprise-grade architecture: Built on NestJS with dependency injection
  • Modular design: Each tool is a separate class with clear separation of concerns
  • Maintainability: Easy to extend with new features without touching existing code
  • Testing: Comprehensive test suite with 70% coverage requirement
  • Type safety: Strict TypeScript with Zod validation
  • Error handling: Robust error handling with helpful user feedback
  • Production-ready: Proper logging, progress reporting, and MCPB bundle support
  • Scalability: Can easily grow from basic tools to complex workflows

Use case: If you need a solid foundation for building advanced Anki integrations or plan to extend functionality significantly, this project's architectural approach makes it easier to maintain and scale over time.

License & Attribution

This project is licensed under the MIT License — see LICENSE for the full text.

Copyright © 2026 Anatoly Tarnavsky.

Third-Party Attributions
  • Anki® is a registered trademark of Ankitects Pty Ltd. This project is an unofficial third-party tool and is not affiliated with, endorsed by, or sponsored by Ankitects Pty Ltd. The Anki logo is used under the alternative license for referencing Anki with a link to https://apps.ankiweb.net. For the official Anki application, visit https://apps.ankiweb.net.

  • Model Context Protocol (MCP) is an open standard by Anthropic. The MCP logo is from the official MCP documentation repository and is used under the MIT License. For more information about MCP, visit https://modelcontextprotocol.io.

  • This is an independent project that bridges Anki and MCP technologies. All trademarks, service marks, trade names, product names, and logos are the property of their respective owners.

1# Anki MCP Server
2 
3[![Tests](https://github.com/ankimcp/anki-mcp-server/actions/workflows/test.yml/badge.svg)](https://github.com/ankimcp/anki-mcp-server/actions/workflows/test.yml)
4[![npm version](https://badge.fury.io/js/@ankimcp%2Fanki-mcp-server.svg)](https://www.npmjs.com/package/@ankimcp/anki-mcp-server)
5 
6<div align="center">
7 <img src="./docs/images/ankimcp.png" alt="Anki + MCP Integration" width="600" />
8 
9 <p><strong>Seamlessly integrate <a href="https://apps.ankiweb.net">Anki</a> with AI assistants through the <a href="https://modelcontextprotocol.io">Model Context Protocol</a></strong></p>
10</div>
11 
12**Beta** - This project is in active development. APIs and features may change.
13 
14A Model Context Protocol (MCP) server that enables AI assistants to interact with Anki, the spaced repetition flashcard application.
15 
16Transform your Anki experience with natural language interaction - like having a private tutor. The AI assistant doesn't just present questions and answers; it can explain concepts, make the learning process more engaging and human-like, provide context, and adapt to your learning style. It can create and edit notes on the fly, turning your study sessions into dynamic conversations. More features coming soon!
17 
18## Examples and Tutorials
19 
20For comprehensive guides, real-world examples, and step-by-step tutorials on using this MCP server with Claude Desktop, visit:
21 
22**[ankimcp.ai](https://ankimcp.ai)** - Complete documentation with practical examples and use cases
23 
24See [`docs/`](./docs/README.md) for supplementary documentation, including the [reviewer setup guide](./docs/reviewer-setup.md) and the sample Anki deck.
25 
26## Example Use Cases
27 
28Three representative prompts showing the tool flows this server enables:
29 
301. **"Help me review my Spanish deck."** — The assistant offers to sync with AnkiWeb (`sync`), fetches due cards (`get_due_cards` with deck filter), presents each card (`present_card`), and records your rating (`rate_card`). Natural study conversation with explanations tailored to you.
31 
322. **"Create 10 Arabic vocab cards with RTL styling."** — The assistant lists note types (`modelNames`), creates a custom RTL model if needed (`createModel` + `updateModelStyling` for right-to-left CSS), then batch-creates the cards (`addNotes`).
33 
343. **"Import this image from my Downloads folder into the front of the selected note."** — The assistant uploads the local file (`storeMediaFile` with a file path), reads the currently-selected note from the browser (`guiSelectedNotes` + `notesInfo`), and updates the front field with an `<img>` tag (`updateNoteFields`).
35 
36## Available Tools
37 
38The server exposes **53 MCP tools** — 42 essential tools for everyday Anki operations and 11 GUI tools that drive the Anki desktop interface for note editing/creation workflows.
39 
40### Essential Tools
41 
42#### Review & Study
43- `sync` - Sync with AnkiWeb to pull latest data and push changes
44- `get_due_cards` - Get cards that are due for review, optionally filtered by deck (answers omitted unless `include_answer: true`, default `false`)
45- `get_cards` - Get cards with flexible filtering by state (due, new, learning, suspended, buried) and deck (answers omitted unless `include_answer: true`, default `false`)
46- `present_card` - Show a card for review with its question/front side
47- `rate_card` - Rate card performance (Again, Hard, Good, Easy) and schedule the next review
48- `forgetCards` - Reset cards to new, discarding their scheduling without recording a review
49- `setDueDate` - Reschedule cards to become due in N days (`"0"`, `"3-7"`, `"1!"`), without recording a review
50- `areSuspended` - Check suspension state for one or more cards without changing anything
51- `suspend` - Suspend cards so they are skipped during review until unsuspended
52- `unsuspend` - Unsuspend cards so they return to normal review
53 
54> **Note:** `forgetCards` and `setDueDate` change scheduling *without* logging a review, which is what separates them from `rate_card`. Reach for them when a card's schedule is wrong rather than the answer: rating a card `Again` to bury it deeper records a real lapse and drops its ease factor, permanently skewing both future scheduling and your statistics. `forgetCards` wipes the interval and starts the card over; `setDueDate` keeps the card's history and just moves the next review.
55 
56> **Note:** Card `front`/`back` content is rendered per card from its own template (as Anki shows it), so reversed and cloze cards display the correct direction. Static text added by your card templates appears in the output as well.
57 
58#### Deck Management
59- `listDecks` - List all decks, optionally with per-deck study-queue statistics
60- `deckStats` - Get comprehensive statistics for a single deck (study queue, true card-state counts, ease/interval distributions)
61- `createDeck` - Create a new empty deck (supports `Parent::Child`, max 2 levels)
62- `changeDeck` - Move cards to a different deck (created if it doesn't exist)
63 
64> **Note:** Deck statistics come in two flavours. The `counts` block (and everything `listDecks` reports) mirrors Anki's deck browser: cards **due today**, capped by each deck's **daily new/review limits**, with suspended and buried cards excluded — so `review` is not "mature cards" and the `other` bucket is just the arithmetic remainder (mostly review cards not due today plus new cards over the daily limit). For true per-state totals use the `states` block on `deckStats` / `collection_stats`, which counts `new`, `learning`, `review`, `suspended` and `buried` via Anki searches, ignoring due dates and daily limits.
65 
66#### Note Management
67- `addNote` - Create a single note with specified fields and tags
68- `addNotes` - Batch-create up to 100 notes sharing a deck and model (partial success supported)
69- `findNotes` - Search for notes using Anki query syntax (`deck:`, `tag:`, `is:due`, etc.)
70- `notesInfo` - Get detailed information about notes (fields, tags, note type, card IDs; CSS comes from `modelStyling`)
71- `updateNoteFields` - Update existing note fields (CSS-aware, supports HTML content)
72- `deleteNotes` - Delete notes and all associated cards (destructive, requires confirmation)
73 
74#### Tag Management
75- `getTags` - Get all tags in the collection (use first to avoid duplication)
76- `addTags` - Add space-separated tags to specified notes
77- `removeTags` - Remove space-separated tags from specified notes
78- `replaceTags` - Rename a tag across specified notes
79- `clearUnusedTags` - Remove orphaned tags not used by any notes (destructive)
80 
81#### Media Management
82- `getMediaFilesNames` - List media files in `collection.media`, optionally filtered by pattern
83- `retrieveMediaFile` - Download a media file as base64 content
84- `storeMediaFile` - Upload media from base64 data, an absolute file path, or a URL
85- `deleteMediaFile` - Remove a media file from `collection.media` (destructive)
86 
87**💡 Best Practice for Images:**
88- ✅ **Use file paths** (e.g., `/Users/you/image.png`) - Fast and efficient
89- ✅ **Use URLs** (e.g., `https://example.com/image.jpg`) - Direct download
90- ❌ **Avoid base64** - Extremely slow and token-inefficient
91 
92Just tell Claude where the image is, and it will handle the upload automatically using the most efficient method.
93 
94#### Model/Template Management
95- `modelNames` - List all available note types/models
96- `modelFieldNames` - Get field names for a specific note type
97- `modelStyling` - Get CSS styling information for a note type
98- `modelTemplates` - Get the card templates (Front and Back HTML) for a note type
99- `createModel` - Create a new note type with custom fields, card templates, and CSS (e.g., RTL models)
100- `updateModelStyling` - Update the CSS styling for an existing note type (applies to all its cards)
101- `updateModelTemplates` - Update the card templates (Front and Back HTML) for an existing note type (applies to all its cards)
102- `addModelField` - Add a new field to an existing note type (appended at the end or inserted at a specific position)
103- `removeModelField` - Remove a field from an existing note type (deletes its content from all notes; requires explicit confirmation)
104- `renameModelField` - Rename a field in an existing note type (Anki rewrites references to the old name in the card templates)
105- `repositionModelField` - Change the position of a field within an existing note type
106 
107#### Statistics
108- `collection_stats` - Aggregated statistics across all decks with per-deck breakdown and collection-wide card-state counts
109- `review_stats` - Review history analysis (temporal patterns, retention metrics, study streaks)
110 
111### GUI Tools
112 
113Tools that drive the Anki desktop interface. Intended for note editing/creation and deck-management workflows, **not** for review sessions.
114 
115- `guiBrowse` - Open the Card Browser and search for cards
116- `guiSelectCard` - Select a specific card in the Card Browser
117- `guiSelectedNotes` - Get IDs of notes currently selected in the Card Browser
118- `guiAddCards` - Open the Add Cards dialog with preset note details
119- `guiEditNote` - Open the note editor for a specific note
120- `guiDeckOverview` - Open the Deck Overview dialog for a specific deck
121- `guiDeckBrowser` - Open the Deck Browser dialog
122- `guiCurrentCard` - Get info about the current card in review mode (includes the answer; errors outside review mode)
123- `guiShowQuestion` - Show the question side of the current card
124- `guiShowAnswer` - Show the answer side of the current card
125- `guiUndo` - Undo the last action in Anki
126 
127## Prerequisites
128 
129- [Anki](https://apps.ankiweb.net/) with [AnkiConnect](https://github.com/FooSoft/anki-connect) plugin installed
130- Node.js 22.12.0+
131 
132## Installation
133 
134There are a few ways to get the server onto your machine. Once it's installed, head to [Connecting an AI Client](#connecting-an-ai-client) to wire it up to your AI assistant — locally or remotely.
135 
136### npm (global or npx)
137 
138The general-purpose way to install the server, suitable for any MCP client that launches it directly.
139 
140Install it globally for clients that run the `ankimcp` command:
141 
142```bash
143npm install -g @ankimcp/anki-mcp-server
144```
145 
146Or run it on demand with no install required:
147 
148```bash
149npx @ankimcp/anki-mcp-server
150```
151 
152### MCPB Bundle (Recommended for Claude Desktop)
153 
154The easiest way to install this MCP server for Claude Desktop:
155 
1561. Download the latest `.mcpb` bundle from the [Releases](https://github.com/ankimcp/anki-mcp-server/releases) page
1572. In Claude Desktop, install the extension:
158 - **Method 1**: Go to Settings → Extensions, then drag and drop the `.mcpb` file
159 - **Method 2**: Go to Settings → Developer → Extensions → Install Extension, then select the `.mcpb` file
1603. Configure AnkiConnect URL if needed (defaults to `http://localhost:8765`)
1614. Restart Claude Desktop
162 
163That's it! The bundle includes everything needed to run the server locally.
164 
165> **For Anthropic MCP Directory reviewers:** a zero-to-integration walkthrough with a pre-populated sample deck lives in [`docs/reviewer-setup.md`](./docs/reviewer-setup.md).
166 
167### Install from Source (for development)
168 
169For development or advanced usage (running the test suite requires Node.js 24.9+ — the npm test scripts load the ESM-only NestJS 12 packages via `require(esm)`, which Jest only supports there; the runtime requirement for *using* the server stays 22.12.0+):
170 
171```bash
172npm install
173npm run build
174```
175 
176## Connecting an AI Client
177 
178There are two ways an AI assistant can reach this server, depending on where the assistant runs:
179 
180- **[Local](#local)** — the server runs on the same machine as the AI client (Claude Desktop, Cursor, Cline, Zed, or a local browser session). Use **STDIO** for desktop MCP clients, **HTTP** for local web-based tools.
181- **[Remote](#remote)** — a hosted/remote AI (e.g. ChatGPT or Claude.ai in the cloud) needs to reach the Anki running on your local machine. Use the managed **Tunnel** (✅ recommended — authenticated) or, as a lighter-weight unauthenticated alternative, **ngrok**.
182 
183### Local
184 
185The server runs on the same computer as your AI client and talks to AnkiConnect on `localhost`.
186 
187#### STDIO (primary local integration)
188 
189STDIO is the standard transport for local desktop MCP clients — **Claude Desktop**, **Cursor IDE**, **Cline**, **Zed Editor**, **opencode**, and others. The client launches the server as a subprocess and communicates over standard input/output.
190 
191**Supported Clients:**
192- [Claude Desktop](https://claude.ai/download)
193- [Cursor IDE](https://www.cursor.com/) - AI-powered code editor
194- [Cline](https://github.com/cline/cline) - VS Code extension for AI assistance
195- [Zed Editor](https://zed.dev/) - Fast, modern code editor
196- [opencode](https://opencode.ai) - Terminal AI coding agent
197- Other MCP clients that support STDIO transport
198 
199For Claude Desktop, the [MCPB bundle](#mcpb-bundle-recommended-for-claude-desktop) is the easiest path. For other clients, configure the npm package with the `--stdio` flag.
200 
201**Configuration - Choose one method:**
202 
203**Method 1: Using npx (recommended - no installation needed)**
204 
205```json
206{
207 "mcpServers": {
208 "anki-mcp": {
209 "command": "npx",
210 "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
211 "env": {
212 "ANKI_CONNECT_URL": "http://localhost:8765"
213 }
214 }
215 }
216}
217```
218 
219**Method 2: Using global installation**
220 
221First, install globally:
222```bash
223npm install -g @ankimcp/anki-mcp-server
224```
225 
226Then configure:
227```json
228{
229 "mcpServers": {
230 "anki-mcp": {
231 "command": "ankimcp",
232 "args": ["--stdio"],
233 "env": {
234 "ANKI_CONNECT_URL": "http://localhost:8765"
235 }
236 }
237 }
238}
239```
240 
241**opencode** uses its own format — servers go under `mcp`, the command is a single array, and environment variables go under `environment`:
242```json
243{
244 "$schema": "https://opencode.ai/config.json",
245 "mcp": {
246 "anki-mcp": {
247 "type": "local",
248 "command": ["npx", "-y", "@ankimcp/anki-mcp-server", "--stdio"],
249 "enabled": true,
250 "environment": {
251 "ANKI_CONNECT_URL": "http://localhost:8765"
252 }
253 }
254 }
255}
256```
257 
258**Configuration file locations:**
259- **Cursor IDE**: `~/.cursor/mcp.json` (macOS/Linux) or `%USERPROFILE%\.cursor\mcp.json` (Windows)
260- **Cline**: Accessible via settings UI in VS Code
261- **Zed Editor**: Install as MCP extension through extension marketplace
262- **opencode**: `opencode.json` in your project root, or `~/.config/opencode/opencode.json` globally
263 
264For client-specific features and troubleshooting, consult your MCP client's documentation. See also [Connect to Claude Desktop](#connect-to-claude-desktop-local-mode) for a config that points directly at a built `dist/main-stdio.js`.
265 
266#### HTTP (local web-based AI)
267 
268HTTP mode runs the server as a local web server speaking the MCP Streamable HTTP protocol. It's the transport a web-based AI tool talks to when pointed at your machine, and it's also what the [Remote](#remote) options expose to the outside world. On its own, HTTP mode binds to `localhost` only.
269 
270> **Binding beyond localhost?** If you pass `--host 0.0.0.0` (or run behind a reverse proxy/public domain), the server only accepts loopback `Host` headers by default for DNS-rebinding protection — set `ALLOWED_HOSTS` to the hostname(s) clients use. See [HTTP Mode Configuration](#http-mode-configuration).
271 
272**Setup - Choose one method:**
273 
274**Method 1: Using npx (recommended - no installation needed)**
275 
276```bash
277# Quick start
278npx @ankimcp/anki-mcp-server
279 
280# With custom options
281npx @ankimcp/anki-mcp-server --port 8080 --host 0.0.0.0
282npx @ankimcp/anki-mcp-server --anki-connect http://localhost:8765
283```
284 
285**Method 2: Using global installation**
286 
287```bash
288# Install once
289npm install -g @ankimcp/anki-mcp-server
290 
291# Run the server
292ankimcp
293 
294# With custom options
295ankimcp --port 8080 --host 0.0.0.0
296ankimcp --anki-connect http://localhost:8765
297```
298 
299**Method 3: Install from source (for development)**
300 
301```bash
302npm install
303npm run build
304npm run start:prod:http
305```
306 
307To make a local HTTP server reachable by a cloud-hosted AI, use one of the [Remote](#remote) options below.
308 
309### Remote
310 
311A hosted/remote AI (such as ChatGPT or Claude.ai running in the cloud) can't reach `localhost` directly. These options expose your **local** Anki to the internet so a remote assistant can talk to it.
312 
313#### Tunnel (✅ Recommended)
314 
315> **Recommended remote path — authenticated & secure.** Unlike a raw public port, tunnel mode requires you to log in (OAuth 2.0 device flow), so the endpoint isn't open to anyone who guesses the URL.
316 
317Tunnel mode lets web-based AI assistants reach your **local** Anki without running your own tunnel. The server connects out to the managed AnkiMCP tunnel service (`wss://tunnel.ankimcp.ai`) over a WebSocket and is assigned a public URL. Authentication is built in — no ngrok account or separate tunnel process required, and you log in once.
318 
319**Log in (OAuth device flow):**
320 
321Tunnel mode uses the OAuth 2.0 Device Authorization Grant. Logging in opens your browser automatically to an approval page with the code already embedded in the URL — nothing to type, just approve. (If the browser can't open, the terminal prints a verification URL and code to enter manually as a fallback.) On success, credentials are saved to `~/.ankimcp/credentials.json` (file permissions `0600`).
322 
323```bash
324# Pre-authenticate (optional — --tunnel will trigger this automatically if needed)
325ankimcp --login
326npx @ankimcp/anki-mcp-server --login
327 
328# Clear saved credentials
329ankimcp --logout
330```
331 
332**Start the tunnel:**
333 
334```bash
335# Connect to the managed tunnel service (wss://tunnel.ankimcp.ai)
336ankimcp --tunnel
337npx @ankimcp/anki-mcp-server --tunnel
338 
339# Override the tunnel server URL (must be ws:// or wss://) — e.g. for self-hosting
340ankimcp --tunnel wss://my-tunnel.example.com
341```
342 
343If no credentials exist, `--tunnel` automatically starts the login flow first, then continues to the tunnel. This auto-login requires an interactive terminal — when stdout is not a TTY (systemd, headless Docker, CI), the server fast-fails and asks you to run `ankimcp --login` first. Once connected, the public tunnel URL is printed; press Ctrl+C to disconnect. Share that URL with your AI assistant.
344 
345**Tunnel-mode environment variables:**
346 
347| Variable | Description | Default |
348|----------|-------------|---------|
349| `TUNNEL_SERVER_URL` | Tunnel server WebSocket URL (the `--tunnel`/`--login` flag value overrides this) | `wss://tunnel.ankimcp.ai` |
350| `TUNNEL_AUTH_CLIENT_ID` | OAuth client ID for the device flow. Advanced — only needed when pointing at a self-hosted tunnel/auth service. | (built-in) |
351 
352The device-flow auth endpoints (`/auth/device`, `/auth/token`) are derived from `TUNNEL_SERVER_URL`, so pointing `--tunnel` (or `TUNNEL_SERVER_URL`) at a different host also moves authentication to that host.
353 
354**How it works:** Tunnel mode runs the MCP server in-process behind an in-memory transport (`TunnelTransport`). That transport owns the MCP server and turns each relayed request body into a response, and `TunnelClient` bridges it to the remote tunnel service over a WebSocket — relaying MCP requests in and responses out. AnkiConnect is still only ever reached on your local machine.
355 
356**Protocol revisions:** Because the tunnel connects the MCP server in-process, tunnel mode serves the 2025 revision of the MCP protocol only, while STDIO and HTTP modes serve both 2025 and the newer 2026-07-28 revision. Every tool behaves the same either way — but a client that speaks only 2026-07-28 is turned away over the tunnel with a protocol-version error; run [STDIO](#stdio-primary-local-integration) or [HTTP](#http-local-web-based-ai) mode for that client.
357 
358#### ngrok (unauthenticated alternative)
359 
360If you'd rather expose [local HTTP mode](#http-local-web-based-ai) publicly without an account on the managed tunnel, the built-in `--ngrok` flag launches an [ngrok](https://ngrok.com/) subprocess (`src/services/ngrok.service.ts`) and prints the public URL in the startup banner:
361 
362```bash
363# One-time ngrok setup, then:
364ankimcp --ngrok
365```
366 
367This route is **unauthenticated** — anyone with the URL can reach your Anki, so it's less secure than [Tunnel](#tunnel--recommended). Prefer Tunnel unless you have a specific reason to manage your own ngrok endpoint. (Requires a global ngrok install and authtoken.)
368 
369The `--ngrok` flag launches ngrok with `--host-header=rewrite`, so ngrok rewrites the upstream `Host` to `localhost` before forwarding. That keeps requests within the loopback Host allowlist (see [DNS-rebinding protection](#http-mode-configuration)) without you having to add the public `*.ngrok` domain to `ALLOWED_HOSTS`. If you instead run ngrok manually, use the same flag — `ngrok http --host-header=rewrite 3000` — otherwise ngrok forwards the public ngrok hostname as `Host` and the server rejects it with `403`.
370 
371### CLI Options (all modes)
372 
373```bash
374ankimcp [options]
375 
376Options:
377 --stdio Run in STDIO mode (for MCP clients)
378 --tunnel [url] Connect via the managed tunnel (authenticated)
379 --login Authenticate for tunnel mode (OAuth device flow)
380 --logout Clear saved tunnel credentials
381 -p, --port <number> Port to listen on (HTTP mode; default: 3000, or PORT env var)
382 -h, --host <address> Host to bind to (HTTP mode; default: 127.0.0.1, or HOST env var)
383 -a, --anki-connect <url> AnkiConnect URL (default: http://localhost:8765, or ANKI_CONNECT_URL env var)
384 --ngrok Start ngrok tunnel (requires global ngrok installation)
385 --read-only Run in read-only mode (blocks content changes and rescheduling; rating, suspend and sync still work)
386 --help Show help message
387 
388Usage with npx (no installation needed):
389 npx @ankimcp/anki-mcp-server # HTTP mode
390 npx @ankimcp/anki-mcp-server --port 8080 # Custom port
391 npx @ankimcp/anki-mcp-server --stdio # STDIO mode
392 npx @ankimcp/anki-mcp-server --tunnel # Managed tunnel mode
393 npx @ankimcp/anki-mcp-server --ngrok # HTTP mode with ngrok tunnel
394 npx @ankimcp/anki-mcp-server --read-only # Read-only mode
395 
396Usage with global installation:
397 npm install -g @ankimcp/anki-mcp-server # Install once
398 ankimcp # HTTP mode
399 ankimcp --port 8080 # Custom port
400 ankimcp --stdio # STDIO mode
401 ankimcp --tunnel # Managed tunnel mode
402 ankimcp --ngrok # HTTP mode with ngrok tunnel
403 ankimcp --read-only # Read-only mode
404```
405 
406### Read-Only Mode (all modes)
407 
408The `--read-only` flag (or `READ_ONLY=true`) blocks changes to note content, decks, tags, media and note types, and manual rescheduling of cards. When enabled:
409- All read operations work normally (browsing decks, viewing cards, searching notes)
410- These changes are still allowed: rating a card during review (`rate_card`), suspending/unsuspending cards (`suspend`, `unsuspend`), syncing with AnkiWeb (`sync`), and the GUI tools that navigate Anki's windows (`guiBrowse`, `guiDeckOverview`, …)
411- Content modifications are blocked (addNote, deleteNotes, createDeck, updateNoteFields, etc.); `createDeck` for a deck that already exists still succeeds, since nothing is written
412- Manual rescheduling is blocked: `forgetCards` (reset to new) and `setDueDate`
413- `guiUndo` is blocked, since an undo can revert a change to the collection
414- `guiAddCards` and `guiEditNote` are blocked, since a note added or saved in the dialog they open is written to the collection
415- Useful for exploring Anki data without accidental changes to its content
416 
417```bash
418# HTTP mode with read-only
419ankimcp --read-only
420 
421# STDIO mode with read-only
422ankimcp --stdio --read-only
423 
424# Can combine with other flags
425ankimcp --ngrok --read-only
426```
427 
428You can also enable read-only mode via environment variable:
429```bash
430READ_ONLY=true ankimcp
431```
432 
433Or in MCP client configuration:
434```json
435{
436 "mcpServers": {
437 "anki-mcp": {
438 "command": "npx",
439 "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio", "--read-only"],
440 "env": {
441 "ANKI_CONNECT_URL": "http://localhost:8765"
442 }
443 }
444 }
445}
446```
447 
448## Connect to Claude Desktop (Local Mode)
449 
450You can configure the server in Claude Desktop by either:
451- Going to: Settings → Developer → Edit Config
452- Or manually editing the config file
453 
454### Configuration
455 
456Add the following to your Claude Desktop config:
457 
458```json
459{
460 "mcpServers": {
461 "anki-mcp": {
462 "command": "node",
463 "args": ["/path/to/anki-mcp-server/dist/main-stdio.js"],
464 "env": {
465 "ANKI_CONNECT_URL": "http://localhost:8765"
466 }
467 }
468 }
469}
470```
471 
472Replace `/path/to/anki-mcp-server` with your actual project path.
473 
474### Config File Locations
475 
476- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
477- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
478- **Linux**: `~/.config/Claude/claude_desktop_config.json`
479 
480For more details, see the [official MCP documentation](https://modelcontextprotocol.io/docs/develop/connect-local-servers).
481 
482## Environment Variables (Optional)
483 
484| Variable | Description | Default |
485|----------|-------------|---------|
486| `ANKI_CONNECT_URL` | AnkiConnect URL | `http://localhost:8765` |
487| `ANKI_CONNECT_API_VERSION` | API version | `6` |
488| `ANKI_CONNECT_API_KEY` | API key if configured in AnkiConnect | - |
489| `ANKI_CONNECT_TIMEOUT` | Request timeout in ms | `5000` |
490| `READ_ONLY` | Enable read-only mode (`true` or `1`) | `false` |
491| `PORT` | HTTP mode: port to listen on (`--port` flag takes precedence) | `3000` |
492| `HOST` | HTTP mode: address to bind to (`--host` flag takes precedence) | `127.0.0.1` |
493| `ALLOWED_HOSTS` | HTTP mode: extra `Host` header values to accept beyond loopback (comma-separated hostnames). Required when binding to a LAN/public address or running behind a reverse proxy. See [HTTP Mode Configuration](#http-mode-configuration). | loopback only |
494| `ALLOWED_ORIGINS` | HTTP mode: comma-separated allowlist of browser `Origin`/`Referer` patterns (wildcards supported, e.g. `https://*.ngrok.io`). | `http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*` |
495| `TUNNEL_SERVER_URL` | Tunnel server WebSocket URL (tunnel mode only) | `wss://tunnel.ankimcp.ai` |
496| `MEDIA_ALLOWED_TYPES` | Extra MIME types to allow for file path imports (comma-separated, e.g., `application/pdf`) | - |
497| `MEDIA_IMPORT_DIR` | Restrict file path imports to this directory | - |
498| `MEDIA_ALLOWED_HOSTS` | Allow specific private network hosts for URL imports (comma-separated, e.g., `192.168.1.50,my-nas`) | - |
499 
500## Usage Examples
501 
502### Searching and Updating Notes
503 
504```
505# Search for notes in a specific deck
506findNotes(query: "deck:Spanish")
507 
508# Get detailed information about notes
509notesInfo(notes: [1234567890, 1234567891])
510 
511# Update a note's fields (HTML content supported)
512updateNoteFields(note: {
513 id: 1234567890,
514 fields: {
515 "Front": "<b>¿Cómo estás?</b>",
516 "Back": "How are you?"
517 }
518})
519 
520# Delete notes (requires confirmation)
521deleteNotes(notes: [1234567890], confirmDeletion: true)
522```
523 
524### Anki Query Syntax Examples
525 
526The `findNotes` tool supports Anki's powerful query syntax:
527 
528- `"deck:DeckName"` - All notes in a specific deck
529- `"tag:important"` - Notes with the "important" tag
530- `"is:due"` - Cards that are due for review
531- `"is:new"` - New cards that haven't been studied
532- `"added:7"` - Notes added in the last 7 days
533- `"front:hello"` - Notes with "hello" in the front field
534- `"flag:1"` - Notes with red flag
535- `"prop:due<=2"` - Cards due within 2 days
536- `"deck:Spanish tag:verb"` - Spanish deck notes with verb tag (AND)
537- `"deck:Spanish OR deck:French"` - Notes from either deck
538 
539### Important Notes
540 
541#### CSS and HTML Handling
542- The `notesInfo` tool returns each note's note type name; the CSS itself comes from `modelStyling`
543- The `updateNoteFields` tool supports HTML content in fields and preserves CSS styling
544- Each note model has its own CSS styling - use `modelStyling` to get model-specific CSS
545 
546#### Update Warning
547⚠️ **IMPORTANT**: When using `updateNoteFields`, do NOT view the note in Anki's browser while updating, or the fields will not update properly. Close the browser or switch to a different note before updating. See [Known Issues](#known-issues) for more details.
548 
549#### Deletion Safety
550The `deleteNotes` tool requires explicit confirmation (`confirmDeletion: true`) to prevent accidental deletions. Deleting a note removes ALL associated cards permanently.
551 
552## Security
553 
554### Media File Path and URL Validation
555 
556The media tools (`storeMediaFile`, `retrieveMediaFile`, `deleteMediaFile`) and `updateNoteFields` audio/picture fields include security validation to prevent misuse via prompt injection:
557 
558- **File path imports** are restricted to media file types only (images, audio, video). Non-media files (e.g., SSH keys, credentials, shell configs) are rejected based on MIME type. Configure `MEDIA_ALLOWED_TYPES` to allow additional file types, or `MEDIA_IMPORT_DIR` to restrict imports to a specific directory.
559- **URL imports** are validated against SSRF attacks. Requests to private networks (10.x, 172.16.x, 192.168.x), loopback (127.x), link-local (169.254.x), and non-HTTP(S) schemes are blocked. Configure `MEDIA_ALLOWED_HOSTS` to allow specific private network hosts.
560- **Filenames** are sanitized to prevent path traversal (e.g., `../../` sequences are stripped).
561 
562These protections apply to `storeMediaFile`, `retrieveMediaFile`, `deleteMediaFile`, and `updateNoteFields` audio/picture fields.
563 
564> Path traversal vulnerability reported by [Hideaki Takahashi](https://github.com/Koukyosyumei).
565 
566### DNS-Rebinding Protection (HTTP transport)
567 
568When running in HTTP mode, the server validates the `Host` header on every request. By default only loopback hosts (`localhost`, `127.0.0.1`, `::1`) are accepted, regardless of port. `Host` is a browser-forbidden header, so a malicious web page cannot forge it — this closes the DNS-rebinding path where a rebound page reaches the local server with a spoofed `Host` and no `Origin`, and reaches the MCP tools. A disallowed `Host` is rejected with `403`.
569 
570If you bind to `0.0.0.0`, run behind a reverse proxy, or expose a public tunnel domain, set `ALLOWED_HOSTS` (comma-separated hostnames) to permit those hosts. When tunneling with ngrok the server uses `--host-header=rewrite`, so the upstream still sees a loopback `Host`. See [HTTP Mode Configuration](#http-mode-configuration) for the full list of options.
571 
572> DNS-rebinding vulnerability reported by [avishaigo-commits](https://github.com/avishaigo-commits) and [yotampe-pluto](https://github.com/yotampe-pluto).
573 
574## Privacy Policy
575 
576This MCP server runs locally on your machine and collects no telemetry, analytics, or usage data.
577 
578Full policy: **[https://ankimcp.ai/privacy/](https://ankimcp.ai/privacy/)**
579 
580- **Data collection**: The server collects nothing. It proxies requests between your AI assistant and your local AnkiConnect plugin.
581- **Usage / storage**: No server-side storage. All flashcard data stays in your Anki installation on your own device.
582- **Third-party sharing**: None. The server only talks to the AnkiConnect URL you configure (default: localhost). If you enable Anki's built-in AnkiWeb sync, that happens between your Anki install and AnkiWeb directly — outside this server's scope.
583- **Retention**: Not applicable — no data is retained server-side.
584- **Contact**: [email protected]
585 
586## Known Issues
587 
588For a comprehensive list of known issues and limitations, please visit our documentation:
589 
590**[Known Issues Documentation](https://ankimcp.ai/docs/known-issues/)**
591 
592### Critical Limitations
593 
594#### Note Updates Fail When Viewed in Browser
595⚠️ **IMPORTANT**: When updating notes using `updateNoteFields`, the update will silently fail if the note is currently being viewed in Anki's browser window. This is an upstream AnkiConnect limitation.
596 
597**Workaround**: Always close the browser or navigate to a different note before updating.
598 
599For more details and other known issues, see the [full documentation](https://ankimcp.ai/docs/known-issues/).
600 
601## Troubleshooting
602 
603### ERR_REQUIRE_ESM Error
604 
605If you see an error like:
606```
607Error [ERR_REQUIRE_ESM]: require() of ES Module not supported
608```
609 
610This means your Node.js version is not supported. The server requires **Node.js 22.12.0+**.
611 
612> **Note:** The minimum supported runtime is Node.js 22.12.0. Node.js 20 (Iron) reached end-of-life on 2026-04-30 and is no longer supported.
613 
614**Check your version:**
615```bash
616node --version
617```
618 
619**Solution:** Update Node.js to version 22.12.0+. You can download it from [nodejs.org](https://nodejs.org/) or use a version manager like [nvm](https://github.com/nvm-sh/nvm).
620 
621## Development
622 
623### Transport Modes
624 
625This server supports three MCP transport modes via **separate entry points**:
626 
627#### STDIO Mode (Default)
628- For local MCP clients like Claude Desktop
629- Uses standard input/output for communication
630- **Entry point**: `dist/main-stdio.js`
631- **Run**: `npm run start:prod:stdio` or `node dist/main-stdio.js`
632- **MCPB bundle**: Uses STDIO mode
633 
634#### HTTP Mode (Streamable HTTP)
635- For remote MCP clients and web-based integrations
636- Uses MCP Streamable HTTP protocol
637- **Entry point**: `dist/main-http.js`
638- **Run**: `npm run start:prod:http` or `node dist/main-http.js`
639- **Default port**: 3000 (configurable via `PORT` env var)
640- **Default host**: `127.0.0.1` (configurable via `HOST` env var)
641- **MCP endpoint**: `http://127.0.0.1:3000/` (root path)
642 
643#### Tunnel Mode (Managed WebSocket Tunnel)
644- For web-based AI assistants via the managed AnkiMCP tunnel service, with built-in authentication
645- The MCP server runs in-process behind an in-memory transport; `TunnelTransport` owns the MCP server and `TunnelClient` bridges it to the tunnel service over a WebSocket
646- **Protocol**: serves the 2025 MCP revision only (STDIO and HTTP also serve 2026-07-28)
647- **Entry point**: `dist/main-tunnel.js`
648- **Run**: `node dist/main-tunnel.js --tunnel` (or `ankimcp --tunnel`)
649- **Auth**: `ankimcp --login` / `ankimcp --logout`; credentials stored at `~/.ankimcp/credentials.json` (`0600`)
650- **Dev**: `npm run start:dev:tunnel` (watch mode, runs `--tunnel --debug`)
651 
652#### Building
653 
654```bash
655npm run build # Builds once, creates dist/ with all three entry points
656```
657 
658`main-stdio.js`, `main-http.js`, and `main-tunnel.js` are all built into the same `dist/` directory. Choose which to run based on your needs.
659 
660#### HTTP Mode Configuration
661 
662**Environment Variables:**
663- `PORT` - HTTP server port (default: 3000)
664- `HOST` - Bind address (default: 127.0.0.1 for localhost-only)
665- `ALLOWED_HOSTS` - Comma-separated extra `Host` header values to accept beyond the built-in loopback set (`localhost`, `127.0.0.1`, `::1`). Hostname-only and port-agnostic. Default: loopback only.
666- `ALLOWED_ORIGINS` - Comma-separated allowlist of browser `Origin`/`Referer` patterns; wildcards supported (e.g. `https://*.ngrok.io`). Default: `http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*`.
667- `LOG_LEVEL` - Logging level (default: info)
668 
669**Security:**
670- **Host header validation (DNS-rebinding protection)** — every HTTP request must carry a `Host` header that matches the allowlist. By default only loopback hosts (`localhost`, `127.0.0.1`, `::1`) are accepted, regardless of port. `Host` is a browser-forbidden header, so a malicious web page cannot forge it — this closes the [DNS-rebinding](https://github.com/ankimcp/anki-mcp-server/security/advisories) path where a rebound page reaches the server with a spoofed `Host` and no `Origin`. A disallowed `Host` is rejected with `403`.
671- **Origin header validation** — browser requests with a present-but-disallowed `Origin`/`Referer` are rejected. Requests with **no** `Origin` (curl, Postman, MCP-over-HTTP clients) are allowed; Host validation is the defense against rebinding.
672- Binds to localhost (127.0.0.1) by default.
673- No authentication in current version (OAuth support planned).
674 
675**Exposing HTTP mode beyond localhost** — if you bind to a LAN/public address or put the server behind a reverse proxy or public domain, you **must** set `ALLOWED_HOSTS` to the hostname(s) clients will use, otherwise every non-loopback request is rejected with `403`:
676 
677```bash
678# Bind to all interfaces and accept the machine's LAN name + a public domain
679ALLOWED_HOSTS=my-nas.local,anki.example.com PORT=8080 HOST=0.0.0.0 node dist/main-http.js
680```
681 
682When you bind to `0.0.0.0`/`::` without `ALLOWED_HOSTS`, the server logs a startup warning that only loopback `Host` headers will be accepted.
683 
684> **Docker / reverse proxy / public domain:** the same rule applies. In Docker, requests usually arrive with the container's published hostname or the proxy's `Host`, so set `ALLOWED_HOSTS` accordingly. A reverse proxy (nginx, Caddy, Traefik) should either forward the original `Host` and have that hostname listed in `ALLOWED_HOSTS`, or rewrite the upstream `Host` to `localhost`. The built-in `--ngrok` integration handles this automatically (see below).
685 
686**Example: Running Modes**
687```bash
688# Development - STDIO mode (watch mode with auto-rebuild)
689npm run start:dev:stdio
690 
691# Development - HTTP mode (watch mode with auto-rebuild)
692npm run start:dev:http
693 
694# Production - STDIO mode
695npm run start:prod:stdio
696# or
697node dist/main-stdio.js
698 
699# Production - HTTP mode
700npm run start:prod:http
701# or
702PORT=8080 HOST=0.0.0.0 node dist/main-http.js
703```
704 
705### Building an MCPB Bundle
706 
707To create a distributable MCPB bundle:
708 
709```bash
710npm run mcpb:bundle
711```
712 
713This command will:
7141. Sync version from `package.json` to `manifest.json`
7152. Remove old `.mcpb` files
7163. Build the TypeScript project
7174. Package `dist/` and `node_modules/` into an `.mcpb` file
7185. Run `mcpb clean` to remove devDependencies (optimizes bundle from ~47MB to ~10MB)
719 
720The output file will be named `anki-mcp-server-X.X.X.mcpb` and can be distributed for one-click installation.
721 
722#### What Gets Bundled
723 
724The MCPB bundle includes:
725- Compiled JavaScript (`dist/` directory - includes all three entry points)
726- Production dependencies only (`node_modules/` - devDependencies removed by `mcpb clean`)
727- Package metadata (`package.json`)
728- Manifest configuration (`manifest.json` - configured to use `main-stdio.js`)
729- Icon (`icon.png`)
730 
731Source files, tests, and development configs are automatically excluded via `.mcpbignore`.
732 
733### Logging in Claude Desktop
734 
735When running as an MCPB extension in Claude Desktop, logs are written to:
736 
737**Log Location**: `~/Library/Logs/Claude/` (macOS)
738 
739The logs are split across multiple files:
740- **main.log** - General Claude Desktop application logs
741- **mcp-server-Anki MCP Server.log** - MCP protocol messages for this extension
742- **mcp.log** - Combined MCP logs from all servers
743 
744**Note**: The pino logger output (INFO, ERROR, WARN messages from the server code) goes to stderr and appears in the MCP-specific log files. Claude Desktop determines which log file receives which messages, but generally:
745- Application startup and MCP protocol communication → MCP-specific log
746- Server internal logging (pino) → Both MCP-specific log and sometimes main.log
747 
748To view logs in real-time:
749```bash
750tail -f ~/Library/Logs/Claude/mcp-server-Anki\ MCP\ Server.log
751```
752 
753### Debugging the MCP Server
754 
755You can debug the MCP server using the MCP Inspector and attaching a debugger from your IDE (WebStorm, VS Code, etc.).
756 
757**Note for HTTP Mode:** When testing HTTP mode (Streamable HTTP) with MCP Inspector, use "Connection Type: Via Proxy" to avoid CORS errors.
758 
759#### Step 1: Configure Debug Server in MCP Inspector
760 
761The `mcp-inspector-config.json` already includes a debug server configuration:
762 
763```json
764{
765 "mcpServers": {
766 "stdio-server-debug": {
767 "type": "stdio",
768 "command": "node",
769 "args": ["--inspect-brk=9229", "dist/main-stdio.js"],
770 "env": {
771 "MCP_SERVER_NAME": "anki-mcp-stdio-debug",
772 "MCP_SERVER_VERSION": "1.0.0",
773 "LOG_LEVEL": "debug"
774 },
775 "note": "Anki MCP server with debugging enabled on port 9229"
776 }
777 }
778}
779```
780 
781#### Step 2: Start the Debug Server
782 
783Run the MCP Inspector with the debug server:
784 
785```bash
786npm run inspector:debug
787```
788 
789This will start the server with Node.js debugging enabled on port 9229 and pause execution at the first line.
790 
791#### Step 3: Attach Debugger from Your IDE
792 
793##### WebStorm
7941. Go to **Run → Edit Configurations**
7952. Add a new **Attach to Node.js/Chrome** configuration
7963. Set the port to `9229`
7974. Click **Debug** to attach
798 
799##### VS Code
8001. Open the Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
8012. Select **Debug MCP Server (Attach)** configuration
8023. Press F5 to attach
803 
804#### Step 4: Set Breakpoints and Debug
805 
806Once attached, you can:
807- Set breakpoints in your TypeScript source files
808- Step through code execution
809- Inspect variables and call stack
810- Use the debug console for evaluating expressions
811 
812The debugger will work with source maps, allowing you to debug the original TypeScript code rather than the compiled JavaScript.
813 
814### Debugging with Claude Desktop
815 
816You can also debug the MCP server while it runs inside Claude Desktop by enabling the Node.js debugger and attaching your IDE.
817 
818#### Step 1: Configure Claude Desktop for Debugging
819 
820Update your Claude Desktop config to enable debugging:
821 
822**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
823**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
824**Linux**: `~/.config/Claude/claude_desktop_config.json`
825 
826```json
827{
828 "mcpServers": {
829 "anki-mcp": {
830 "command": "node",
831 "args": [
832 "--inspect=9229",
833 "<path_to_project>/anki-mcp-server/dist/main-stdio.js"
834 ],
835 "env": {
836 "ANKI_CONNECT_URL": "http://localhost:8765"
837 }
838 }
839 }
840}
841```
842 
843**Key change**: Add `--inspect=9229` before the path to `dist/main-stdio.js`
844 
845**Debug options**:
846- `--inspect=9229` - Start debugger immediately, doesn't block (recommended)
847- `--inspect-brk=9229` - Pause execution until debugger attaches (for debugging startup issues)
848 
849#### Step 2: Restart Claude Desktop
850 
851After saving the config, restart Claude Desktop. The MCP server will now run with debugging enabled on port 9229.
852 
853#### Step 3: Attach Debugger from Your IDE
854 
855##### WebStorm
856 
8571. Go to **Run → Edit Configurations**
8582. Click the **+** button and select **Attach to Node.js/Chrome**
8593. Configure:
860 - **Name**: `Attach to Anki MCP (Claude Desktop)`
861 - **Host**: `localhost`
862 - **Port**: `9229`
863 - **Attach to**: `Node.js < 8` or `Chrome or Node.js > 6.3` (depending on WebStorm version)
8644. Click **OK**
8655. Click **Debug** (Shift+F9) to attach
866 
867##### VS Code
868 
8691. Add to `.vscode/launch.json`:
870 
871```json
872{
873 "version": "0.2.0",
874 "configurations": [
875 {
876 "type": "node",
877 "request": "attach",
878 "name": "Attach to Anki MCP (Claude Desktop)",
879 "port": 9229,
880 "skipFiles": ["<node_internals>/**"],
881 "sourceMaps": true,
882 "outFiles": ["${workspaceFolder}/dist/**/*.js"]
883 }
884 ]
885}
886```
887 
8882. Open the Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
8893. Select **Attach to Anki MCP (Claude Desktop)**
8904. Press F5 to attach
891 
892#### Step 4: Debug in Real-Time
893 
894Once attached, you can:
895- Set breakpoints in your TypeScript source files (e.g., `src/mcp/primitives/essential/tools/create-model.tool.ts`)
896- Use Claude Desktop normally - breakpoints will hit when tools are invoked
897- Step through code execution
898- Inspect variables and call stack
899- Use the debug console
900 
901**Example**: Set a breakpoint in `create-model.tool.ts` at line 119, then ask Claude to create a new model. The debugger will pause at your breakpoint!
902 
903**Note**: The debugger stays attached as long as Claude Desktop is running. You can detach/reattach anytime without restarting Claude Desktop.
904 
905### Build Commands
906 
907```bash
908npm run build # Build the project (compile TypeScript to JavaScript)
909npm run start:dev:stdio # STDIO mode with watch (auto-rebuild)
910npm run start:dev:http # HTTP mode with watch (auto-rebuild)
911npm run type-check # Run TypeScript type checking
912npm run lint # Run ESLint
913npm run mcpb:bundle # Sync version, clean, build, and create MCPB bundle
914```
915 
916### NPM Package Testing (Local)
917 
918Test the npm package locally before publishing:
919 
920```bash
921# 1. Create local package
922npm run pack:local # Builds and creates @ankimcp/anki-mcp-server-*.tgz
923 
924# 2. Install globally from local package
925npm run install:local # Installs from ./@ankimcp/anki-mcp-server-*.tgz
926 
927# 3. Test the command
928ankimcp # Runs HTTP server on port 3000
929 
930# 4. Uninstall when done testing
931npm run uninstall:local # Removes global installation
932```
933 
934**How it works:**
935- `npm pack` creates a `.tgz` file identical to what npm publish would create
936- Installing from `.tgz` simulates what users get from `npm install -g ankimcp`
937- This lets you test the full user experience before publishing to npm
938 
939### Testing Commands
940 
941```bash
942npm test # Run all tests
943npm run test:unit # Run unit tests only
944npm run test:tools # Run tool-specific tests
945npm run test:workflows # Run workflow integration tests
946npm run test:e2e # Run end-to-end tests
947npm run test:cov # Run tests with coverage report
948npm run test:watch # Run tests in watch mode
949npm run test:debug # Run tests with debugger
950npm run test:ci # Run tests for CI (silent, with coverage)
951```
952 
953### Test Coverage
954 
955The project maintains 70% minimum coverage thresholds for:
956- Branches
957- Functions
958- Lines
959- Statements
960 
961Coverage reports are generated in the `coverage/` directory.
962 
963## Versioning
964 
965This project follows [Semantic Versioning](https://semver.org/) with a pre-1.0 development approach:
966 
967- **0.x.x** - Beta/Development versions (current phase)
968 - **0.1.x** - Bug fixes and patches
969 - **0.2.0+** - New features or minor improvements
970 - **Breaking changes** are acceptable in 0.x versions
971 
972- **1.0.0** - First stable release
973 - Will be released when the API is stable and tested
974 - Breaking changes will require major version bumps (2.0.0, etc.)
975 
976**Current Status**: `0.27.0` - Active beta development. Recent features include collection-wide review analysis (`review_stats` now aggregates across all decks when `deck` is omitted), model field management (`addModelField`, `removeModelField`, `renameModelField`, `repositionModelField`), batch note creation (`addNotes`), integrated ngrok tunneling (`--ngrok` flag), media file management, model/template management, and comprehensive deck statistics. APIs may change based on feedback and testing.
977 
978### MCPB spec evolution
979 
980This project targets Anthropic's MCPB bundle specification, which is still evolving. We track the spec at [https://github.com/modelcontextprotocol/mcpb](https://github.com/modelcontextprotocol/mcpb) and may introduce breaking changes to stay compliant. Breaking changes are permitted under the 0.x.x versioning scheme.
981 
982## Similar Projects
983 
984If you're exploring Anki MCP integrations, here are other projects in this space:
985 
986### [scorzeth/anki-mcp-server](https://github.com/scorzeth/anki-mcp-server)
987- **Status**: Appears to be abandoned (no recent updates)
988- Early implementation of Anki MCP integration
989 
990### [nailuoGG/anki-mcp-server](https://github.com/nailuoGG/anki-mcp-server)
991- **Approach**: Lightweight, single-file implementation
992- **Architecture**: Procedural code structure with all tools in one file
993- **Good for**: Simple use cases, minimal dependencies
994 
995**Why this project differs:**
996- **Enterprise-grade architecture**: Built on NestJS with dependency injection
997- **Modular design**: Each tool is a separate class with clear separation of concerns
998- **Maintainability**: Easy to extend with new features without touching existing code
999- **Testing**: Comprehensive test suite with 70% coverage requirement
1000- **Type safety**: Strict TypeScript with Zod validation
1001- **Error handling**: Robust error handling with helpful user feedback
1002- **Production-ready**: Proper logging, progress reporting, and MCPB bundle support
1003- **Scalability**: Can easily grow from basic tools to complex workflows
1004 
1005**Use case**: If you need a solid foundation for building advanced Anki integrations or plan to extend functionality significantly, this project's architectural approach makes it easier to maintain and scale over time.
1006 
1007## Useful Links
1008 
1009- [Model Context Protocol Documentation](https://modelcontextprotocol.io/docs)
1010- [AnkiConnect API Documentation](https://git.sr.ht/~foosoft/anki-connect)
1011- [Claude Desktop Download](https://claude.ai/download)
1012- [Building Desktop Extensions (Anthropic Blog)](https://www.anthropic.com/engineering/desktop-extensions)
1013- [MCP Servers Repository](https://github.com/modelcontextprotocol/servers)
1014- [NestJS Documentation](https://docs.nestjs.com)
1015- [Anki Official Website](https://apps.ankiweb.net/)
1016 
1017## License & Attribution
1018 
1019This project is licensed under the MIT License — see [LICENSE](LICENSE) for the full text.
1020 
1021Copyright © 2026 Anatoly Tarnavsky.
1022 
1023### Third-Party Attributions
1024 
1025- **Anki®** is a registered trademark of Ankitects Pty Ltd. This project is an unofficial third-party tool and is not affiliated with, endorsed by, or sponsored by Ankitects Pty Ltd. The Anki logo is used under the alternative license for referencing Anki with a link to [https://apps.ankiweb.net](https://apps.ankiweb.net). For the official Anki application, visit [https://apps.ankiweb.net](https://apps.ankiweb.net).
1026 
1027- **Model Context Protocol (MCP)** is an open standard by Anthropic. The MCP logo is from the official [MCP documentation repository](https://github.com/modelcontextprotocol/docs) and is used under the MIT License. For more information about MCP, visit [https://modelcontextprotocol.io](https://modelcontextprotocol.io).
1028 
1029- This is an independent project that bridges Anki and MCP technologies. All trademarks, service marks, trade names, product names, and logos are the property of their respective owners.
1030 

Discussion

Alternatives