trackmcp
Back to directory
anki-mcp

anki-mcp-desktop

View on GitHub

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

15 stars TypeScriptAI & Machine Learning Updated Oct 15, 2025
aiai-learning-toolai-tutorankianki-addonanki-flashcards

Documentation

Anki MCP Server

Tests
npm version

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 syncs 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 `` tag (`updateNoteFields`).

Available Tools

The server exposes 50 MCP tools — 39 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

> 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, CSS styling)
  • `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 (card templates referencing the old name must be updated separately)
  • `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
  • `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:

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

Or run it on demand with no install required:

bash
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:

    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:

    bash
    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, 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)

    json
    {
      "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:

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

    Then configure:

    json
    {
      "mcpServers": {
        "anki-mcp": {
          "command": "ankimcp",
          "args": ["--stdio"],
          "env": {
            "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

    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)

    bash
    # 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

    bash
    # 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)

    bash
    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`).

    bash
    # 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:

    bash
    # 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:

    VariableDescriptionDefault
    `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:

    bash
    # 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)

    bash
    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             Port to listen on (HTTP mode; default: 3000, or PORT env var)
      -h, --host            Host to bind to (HTTP mode; default: 127.0.0.1, or HOST env var)
      -a, --anki-connect        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 all write operations)
      --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 prevents any modifications to your Anki collection. When enabled:

    • All read operations work normally (browsing decks, viewing cards, searching notes)
    • Review operations are allowed (sync, answerCards, suspend/unsuspend)
    • Content modifications are blocked (addNote, deleteNotes, createDeck, updateNoteFields, etc.)
    • Useful for safely exploring Anki data without risk of accidental changes
    bash
    # 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:

    bash
    READ_ONLY=true ankimcp

    Or in MCP client configuration:

    json
    {
      "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:

    json
    {
      "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)

    VariableDescriptionDefault
    `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

    code
    # 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": "¿Cómo estás?",
        "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 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: support@ankimcp.ai

    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:

    code
    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:

    bash
    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

    bash
    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`:

    bash
    # 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

    bash
    # 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:

    bash
    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:

    bash
    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:

    json
    {
      "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:

    bash
    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`

    json
    {
      "mcpServers": {
        "anki-mcp": {
          "command": "node",
          "args": [
            "--inspect=9229",
            "/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:

      4. Click OK

      5. Click Debug (Shift+F9) to attach

      VS Code

      1. Add to `.vscode/launch.json`:

      json
      {
        "version": "0.2.0",
        "configurations": [
          {
            "type": "node",
            "request": "attach",
            "name": "Attach to Anki MCP (Claude Desktop)",
            "port": 9229,
            "skipFiles": ["/**"],
            "sourceMaps": true,
            "outFiles": ["${workspaceFolder}/dist/**/*.js"]
          }
        ]
      }

      2. Open the Debug panel (Ctrl+Shift+D / Cmd+Shift+D)

      3. Select Attach to Anki MCP (Claude Desktop)

      4. 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

      bash
      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:

      bash
      # 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

      bash
      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)
        • 1.0.0 - First stable release

          Current Status: `0.22.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.
          • 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.

          Frequently asked questions

          What is anki-mcp-desktop?

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

          How do I install anki-mcp-desktop?

          Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

          Is anki-mcp-desktop open source?

          Yes — it is hosted on GitHub at https://github.com/anki-mcp/anki-mcp-desktop and has 15 stars.

          Related MCP tools

          Run your own MCP server? See who uses it and what to fix.

          Measure it with TrackMCP