anki-mcp-server-integration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Anki MCP Server Integration

Anki MCP 服务器集成

Skill by ara.so — MCP Skills collection.
A Model Context Protocol (MCP) server that enables AI assistants to interact with Anki, the spaced repetition flashcard application. Transform study sessions into dynamic conversations where AI can create, edit, and review flashcards naturally, explain concepts, and adapt to your learning style.
ara.so开发的Skill —— MCP Skills 合集。
这是一个Model Context Protocol(MCP)服务器,可让AI助手与间隔重复闪卡应用Anki进行交互。将学习会话转变为动态对话,AI可以自然地创建、编辑和复习闪卡,解释概念并适应你的学习风格。

Installation

安装

Prerequisites

前提条件

For Claude Desktop (STDIO Mode)

适用于Claude Desktop(STDIO模式)

Option 1: MCPB Bundle (Recommended)
Download
.mcpb
file from releases, then in Claude Desktop:
  • Settings → Extensions → drag and drop the
    .mcpb
    file
  • Configure AnkiConnect URL if needed (default:
    http://localhost:8765
    )
  • Restart Claude Desktop
Option 2: NPM Configuration
Add to
claude_desktop_config.json
:
json
{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}
Location:
  • macOS:
    ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:
    %APPDATA%\Claude\claude_desktop_config.json
选项1:MCPB包(推荐)
发布页面下载
.mcpb
文件,然后在Claude Desktop中:
  • 设置 → 扩展 → 拖拽
    .mcpb
    文件
  • 如有需要,配置AnkiConnect URL(默认:
    http://localhost:8765
  • 重启Claude Desktop
选项2:NPM配置
添加到
claude_desktop_config.json
json
{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}
位置:
  • macOS:
    ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:
    %APPDATA%\Claude\claude_desktop_config.json

For Cursor IDE / Cline / Zed

适用于Cursor IDE / Cline / Zed

Add to MCP configuration file:
json
{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}
Configuration locations:
  • Cursor:
    ~/.cursor/mcp.json
  • Cline: VS Code settings UI
  • Zed: Extension marketplace
添加到MCP配置文件:
json
{
  "mcpServers": {
    "anki-mcp": {
      "command": "npx",
      "args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
      "env": {
        "ANKI_CONNECT_URL": "http://localhost:8765"
      }
    }
  }
}
配置位置:
  • Cursor:
    ~/.cursor/mcp.json
  • Cline: VS Code设置界面
  • Zed: 扩展市场

For Web-based AI (HTTP Mode with ngrok)

适用于基于Web的AI(使用ngrok的HTTP模式)

bash
undefined
bash
undefined

One-time setup

一次性设置

npm install -g @ankimcp/anki-mcp-server npm install -g ngrok ngrok config add-authtoken <YOUR_NGROK_TOKEN>
npm install -g @ankimcp/anki-mcp-server npm install -g ngrok ngrok config add-authtoken <YOUR_NGROK_TOKEN>

Start server with public tunnel

启动服务器并创建公开隧道

ankimcp --ngrok

Share the ngrok URL with ChatGPT or Claude.ai as an MCP server endpoint.
ankimcp --ngrok

将ngrok URL作为MCP服务器端点分享给ChatGPT或Claude.ai。

Configuration

配置

Environment Variables

环境变量

bash
undefined
bash
undefined

AnkiConnect URL (default: http://localhost:8765)

AnkiConnect URL(默认:http://localhost:8765)

ANKI_CONNECT_URL=http://localhost:8765
ANKI_CONNECT_URL=http://localhost:8765

Enable read-only mode (no write operations)

启用只读模式(禁止写入操作)

READ_ONLY=true
undefined
READ_ONLY=true
undefined

CLI Options

CLI选项

bash
ankimcp [options]

--stdio                  # STDIO mode for MCP clients
--port <port>            # HTTP port (default: 3000)
--host <host>            # HTTP host (default: 127.0.0.1)
--anki-connect <url>     # AnkiConnect URL
--ngrok                  # Start ngrok tunnel
--read-only              # Prevent modifications
bash
ankimcp [options]

--stdio                  # 为MCP客户端启用STDIO模式
--port <port>            # HTTP端口(默认:3000)
--host <host>            # HTTP主机(默认:127.0.0.1)
--anki-connect <url>     # AnkiConnect URL
--ngrok                  # 启动ngrok隧道
--read-only              # 阻止修改操作

Core Workflows

核心工作流

1. Interactive Review Session

1. 交互式复习会话

typescript
// User says: "Help me review my Spanish deck"

// AI workflow:
// 1. Sync with AnkiWeb
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "sync",
  arguments: {}
});

// 2. Get due cards from Spanish deck
const dueCards = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "get_due_cards",
  arguments: {
    deck: "Spanish"
  }
});

// 3. Present first card
const presentation = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "present_card",
  arguments: {
    card_id: dueCards.cards[0].cardId
  }
});

// AI shows question, user answers, then AI rates:
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "rate_card",
  arguments: {
    card_id: dueCards.cards[0].cardId,
    ease: 3 // 1=Again, 2=Hard, 3=Good, 4=Easy
  }
});
typescript
// 用户说:"帮我复习我的西班牙语卡组"

// AI工作流:
// 1. 与AnkiWeb同步
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "sync",
  arguments: {}
});

// 2. 获取西班牙语卡组中的待复习卡片
const dueCards = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "get_due_cards",
  arguments: {
    deck: "Spanish"
  }
});

// 3. 展示第一张卡片
const presentation = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "present_card",
  arguments: {
    card_id: dueCards.cards[0].cardId
  }
});

// AI显示问题,用户回答后,AI进行评分:
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "rate_card",
  arguments: {
    card_id: dueCards.cards[0].cardId,
    ease: 3 // 1=重来, 2=困难, 3=良好, 4=简单
  }
});

2. Batch Card Creation

2. 批量创建卡片

typescript
// User says: "Create 10 Arabic vocab cards with RTL styling"

// 1. List available note types
const models = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "modelNames",
  arguments: {}
});

// 2. Get fields for Basic note type
const fields = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "modelFieldNames",
  arguments: {
    modelName: "Basic"
  }
});

// 3. Create notes in batch (up to 100 at once)
const result = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: {
    notes: [
      {
        deckName: "Arabic::Vocabulary",
        modelName: "Basic",
        fields: {
          Front: "مرحبا",
          Back: "Hello"
        },
        tags: ["arabic", "greetings"]
      },
      {
        deckName: "Arabic::Vocabulary",
        modelName: "Basic",
        fields: {
          Front: "شكرا",
          Back: "Thank you"
        },
        tags: ["arabic", "greetings"]
      }
      // ... up to 98 more notes
    ]
  }
});

// Result includes successful IDs and any errors
console.log(result.success); // [note_id1, note_id2, ...]
console.log(result.errors);  // Array of error objects if any failed
typescript
// 用户说:"创建10张带RTL样式的阿拉伯语词汇卡片"

// 1. 列出可用的笔记类型
const models = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "modelNames",
  arguments: {}
});

// 2. 获取Basic笔记类型的字段
const fields = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "modelFieldNames",
  arguments: {
    modelName: "Basic"
  }
});

// 3. 批量创建笔记(最多一次100条)
const result = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: {
    notes: [
      {
        deckName: "Arabic::Vocabulary",
        modelName: "Basic",
        fields: {
          Front: "مرحبا",
          Back: "Hello"
        },
        tags: ["arabic", "greetings"]
      },
      {
        deckName: "Arabic::Vocabulary",
        modelName: "Basic",
        fields: {
          Front: "شكرا",
          Back: "Thank you"
        },
        tags: ["arabic", "greetings"]
      }
      // ... 最多再添加98条笔记
    ]
  }
});

// 结果包含成功创建的ID和任何错误信息
console.log(result.success); // [note_id1, note_id2, ...]
console.log(result.errors);  // 若有失败则返回错误对象数组

3. Media Import from File

3. 从文件导入媒体

typescript
// User says: "Import this image from my Downloads folder into the selected note"

// 1. Get selected note from Anki browser
const selected = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "guiSelectedNotes",
  arguments: {}
});

// 2. Upload media file (auto-detects file path)
const media = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "diagram.png",
    data: "/Users/username/Downloads/diagram.png" // File path (fastest)
    // OR url: "https://example.com/image.jpg"   // URL download
    // OR data: "base64string..."                // Base64 (slowest, avoid)
  }
});

// 3. Get note details
const noteInfo = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: {
    notes: [selected.result[0]]
  }
});

// 4. Update front field with image
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: selected.result[0],
      fields: {
        Front: `<img src="${media.result}"> ${noteInfo.result[0].fields.Front.value}`
      }
    }
  }
});
typescript
// 用户说:"将我下载文件夹中的这张图片导入到选中的笔记中"

// 1. 从Anki浏览器获取选中的笔记
const selected = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "guiSelectedNotes",
  arguments: {}
});

// 2. 上传媒体文件(自动检测文件路径)
const media = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "diagram.png",
    data: "/Users/username/Downloads/diagram.png" // 文件路径(最快)
    // 或 url: "https://example.com/image.jpg"   // URL下载
    // 或 data: "base64string..."                // Base64(最慢,尽量避免)
  }
});

// 3. 获取笔记详情
const noteInfo = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: {
    notes: [selected.result[0]]
  }
});

// 4. 更新正面字段,添加图片
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: selected.result[0],
      fields: {
        Front: `<img src="${media.result}"> ${noteInfo.result[0].fields.Front.value}`
      }
    }
  }
});

4. Deck Management

4. 卡组管理

typescript
// List all decks with statistics
const decks = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "listDecks",
  arguments: {
    includeStats: true
  }
});

// Get detailed deck statistics
const stats = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deckStats",
  arguments: {
    decks: ["Spanish", "Spanish::Grammar"]
  }
});

// Create nested deck (max 2 levels: Parent::Child)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "createDeck",
  arguments: {
    deck: "French::Vocabulary"
  }
});

// Move cards to different deck
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "changeDeck",
  arguments: {
    cards: [1234567890, 9876543210],
    deck: "French::Advanced"
  }
});
typescript
// 列出所有卡组及统计信息
const decks = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "listDecks",
  arguments: {
    includeStats: true
  }
});

// 获取详细的卡组统计数据
const stats = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deckStats",
  arguments: {
    decks: ["Spanish", "Spanish::Grammar"]
  }
});

// 创建嵌套卡组(最多2级:父卡组::子卡组)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "createDeck",
  arguments: {
    deck: "French::Vocabulary"
  }
});

// 将卡片移动到其他卡组
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "changeDeck",
  arguments: {
    cards: [1234567890, 9876543210],
    deck: "French::Advanced"
  }
});

5. Note Search and Update

5. 笔记搜索与更新

typescript
// Search for notes using Anki query syntax
const notes = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "findNotes",
  arguments: {
    query: "deck:Spanish tag:verb" // Anki search syntax
  }
});

// Get detailed note information
const noteDetails = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: {
    notes: notes.result
  }
});

// Update note fields (CSS-aware, preserves formatting)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: notes.result[0],
      fields: {
        Front: "¿Cómo estás?",
        Back: "How are you? (informal)"
      }
    }
  }
});

// Delete notes and their cards
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deleteNotes",
  arguments: {
    notes: [1234567890, 9876543210]
  }
});
typescript
// 使用Anki查询语法搜索笔记
const notes = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "findNotes",
  arguments: {
    query: "deck:Spanish tag:verb" // Anki搜索语法
  }
});

// 获取详细的笔记信息
const noteDetails = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: {
    notes: notes.result
  }
});

// 更新笔记字段(支持CSS,保留格式)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: notes.result[0],
      fields: {
        Front: "¿Cómo estás?",
        Back: "How are you? (informal)"
      }
    }
  }
});

// 删除笔记及其对应的卡片
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deleteNotes",
  arguments: {
    notes: [1234567890, 9876543210]
  }
});

6. Tag Management

6. 标签管理

typescript
// Get all existing tags (use first to avoid duplication)
const allTags = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getTags",
  arguments: {}
});

// Add tags to notes (space-separated)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addTags",
  arguments: {
    notes: [1234567890, 9876543210],
    tags: "important grammar advanced"
  }
});

// Remove tags from notes
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "removeTags",
  arguments: {
    notes: [1234567890],
    tags: "beginner"
  }
});

// Rename tag across all notes
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "replaceTags",
  arguments: {
    notes: [1234567890, 9876543210],
    tag_to_replace: "old-tag",
    replace_with_tag: "new-tag"
  }
});

// Clean up unused tags from collection
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "clearUnusedTags",
  arguments: {}
});
typescript
// 获取所有现有标签(优先使用现有标签避免重复)
const allTags = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getTags",
  arguments: {}
});

// 为笔记添加标签(空格分隔)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addTags",
  arguments: {
    notes: [1234567890, 9876543210],
    tags: "important grammar advanced"
  }
});

// 移除笔记的标签
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "removeTags",
  arguments: {
    notes: [1234567890],
    tags: "beginner"
  }
});

// 在所有笔记中重命名标签
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "replaceTags",
  arguments: {
    notes: [1234567890, 9876543210],
    tag_to_replace: "old-tag",
    replace_with_tag: "new-tag"
  }
});

// 清理集合中未使用的标签
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "clearUnusedTags",
  arguments: {}
});

7. Media Management

7. 媒体管理

typescript
// List media files with pattern filter
const mediaFiles = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getMediaFilesNames",
  arguments: {
    pattern: "*.png"
  }
});

// Download media as base64
const mediaData = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "retrieveMediaFile",
  arguments: {
    filename: "diagram.png"
  }
});

// Upload media (three methods)
// Method 1: File path (FASTEST - recommended)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "photo.jpg",
    data: "/Users/username/Pictures/photo.jpg"
  }
});

// Method 2: URL (FAST - auto-downloads)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "diagram.png",
    url: "https://example.com/diagram.png"
  }
});

// Method 3: Base64 (SLOW - avoid if possible)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "audio.mp3",
    data: "base64encodedstring..."
  }
});

// Delete media file
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deleteMediaFile",
  arguments: {
    filename: "old-diagram.png"
  }
});
typescript
// 按模式筛选列出媒体文件
const mediaFiles = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getMediaFilesNames",
  arguments: {
    pattern: "*.png"
  }
});

// 以base64格式下载媒体
const mediaData = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "retrieveMediaFile",
  arguments: {
    filename: "diagram.png"
  }
});

// 上传媒体(三种方式)
// 方式1:文件路径(最快 - 推荐)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "photo.jpg",
    data: "/Users/username/Pictures/photo.jpg"
  }
});

// 方式2:URL(快 - 自动下载)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "diagram.png",
    url: "https://example.com/diagram.png"
  }
});

// 方式3:Base64(慢 - 尽量避免)
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "storeMediaFile",
  arguments: {
    filename: "audio.mp3",
    data: "base64encodedstring..."
  }
});

// 删除媒体文件
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "deleteMediaFile",
  arguments: {
    filename: "old-diagram.png"
  }
});

Common Patterns

常见模式

Check Available Decks Before Creating

创建前检查可用卡组

typescript
// Always list decks first to avoid duplicates
const decks = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "listDecks",
  arguments: {}
});

if (!decks.result.includes("MyDeck")) {
  await use_mcp_tool({
    server_name: "anki-mcp",
    tool_name: "createDeck",
    arguments: { deck: "MyDeck" }
  });
}
typescript
// 先列出卡组以避免重复
const decks = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "listDecks",
  arguments: {}
});

if (!decks.result.includes("MyDeck")) {
  await use_mcp_tool({
    server_name: "anki-mcp",
    tool_name: "createDeck",
    arguments: { deck: "MyDeck" }
  });
}

Get Tags Before Adding

添加前获取标签

typescript
// Fetch existing tags to maintain consistency
const tags = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getTags",
  arguments: {}
});

// Use existing tags when adding notes
const newTags = tags.result.filter(t => t.includes("spanish"));
typescript
// 获取现有标签以保持一致性
const tags = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "getTags",
  arguments: {}
});

// 添加笔记时使用现有标签
const newTags = tags.result.filter(t => t.includes("spanish"));

Batch Operations for Efficiency

批量操作提升效率

typescript
// Instead of adding notes one by one, batch them
const notes = [];
for (let i = 0; i < 50; i++) {
  notes.push({
    deckName: "Vocabulary",
    modelName: "Basic",
    fields: { Front: `Word ${i}`, Back: `Definition ${i}` },
    tags: ["batch-import"]
  });
}

// Single batch operation
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: { notes }
});
typescript
// 不要逐个添加笔记,批量处理
const notes = [];
for (let i = 0; i < 50; i++) {
  notes.push({
    deckName: "Vocabulary",
    modelName: "Basic",
    fields: { Front: `Word ${i}`, Back: `Definition ${i}` },
    tags: ["batch-import"]
  });
}

// 单次批量操作
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: { notes }
});

Safe Note Updates

安全更新笔记

typescript
// Always get note info before updating to preserve content
const info = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: { notes: [noteId] }
});

const currentFields = info.result[0].fields;

// Update only specific fields, preserve others
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: noteId,
      fields: {
        Front: currentFields.Front.value, // Keep existing
        Back: "Updated back content"      // Change this
      }
    }
  }
});
typescript
// 更新前先获取笔记信息以保留内容
const info = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "notesInfo",
  arguments: { notes: [noteId] }
});

const currentFields = info.result[0].fields;

// 仅更新特定字段,保留其他内容
await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "updateNoteFields",
  arguments: {
    note: {
      id: noteId,
      fields: {
        Front: currentFields.Front.value, // 保留现有内容
        Back: "Updated back content"      // 修改此字段
      }
    }
  }
});

Anki Search Query Syntax

Anki搜索查询语法

When using
findNotes
, use Anki's search syntax:
typescript
// Examples of valid queries
"deck:Spanish"                    // All notes in Spanish deck
"tag:verb"                        // Notes tagged with 'verb'
"deck:Spanish tag:verb"           // Spanish deck AND verb tag
"is:due"                          // Due for review
"added:7"                         // Added in last 7 days
"Front:*hola*"                    // Front field contains 'hola'
"deck:Spanish -tag:mastered"      // Spanish deck WITHOUT mastered tag
使用
findNotes
时,需使用Anki的搜索语法:
typescript
// 有效查询示例
"deck:Spanish"                    // 西班牙语卡组中的所有笔记
"tag:verb"                        // 带有'verb'标签的笔记
"deck:Spanish tag:verb"           // 西班牙语卡组且带有verb标签的笔记
"is:due"                          // 待复习的笔记
"added:7"                         // 最近7天添加的笔记
"Front:*hola*"                    // 正面字段包含'hola'的笔记
"deck:Spanish -tag:mastered"      // 西班牙语卡组中不带mastered标签的笔记

Troubleshooting

故障排除

AnkiConnect Not Responding

AnkiConnect无响应

bash
undefined
bash
undefined

Verify AnkiConnect is installed

验证AnkiConnect已安装

In Anki: Tools → Add-ons → Check for "AnkiConnect"

在Anki中:工具 → 插件 → 检查是否有"AnkiConnect"

Check if Anki is running

检查Anki是否在运行

Try custom port

尝试自定义端口

export ANKI_CONNECT_URL=http://localhost:8766 ankimcp --stdio
undefined
export ANKI_CONNECT_URL=http://localhost:8766 ankimcp --stdio
undefined

Permission Errors

权限错误

AnkiConnect requires explicit permission for external access. If operations fail:
  1. Open Anki
  2. Tools → Add-ons → AnkiConnect → Config
  3. Add your application to
    webCorsOriginList
    if using HTTP mode
AnkiConnect需要明确的外部访问权限。若操作失败:
  1. 打开Anki
  2. 工具 → 插件 → AnkiConnect → 配置
  3. 若使用HTTP模式,将你的应用添加到
    webCorsOriginList

Media Upload Fails

媒体上传失败

typescript
// Prefer file paths over base64
// ✅ GOOD
storeMediaFile({ filename: "img.png", data: "/path/to/img.png" })

// ✅ GOOD
storeMediaFile({ filename: "img.png", url: "https://example.com/img.png" })

// ❌ SLOW (avoid unless necessary)
storeMediaFile({ filename: "img.png", data: "base64..." })
typescript
// 优先使用文件路径而非base64
// ✅ 推荐
storeMediaFile({ filename: "img.png", data: "/path/to/img.png" })

// ✅ 推荐
storeMediaFile({ filename: "img.png", url: "https://example.com/img.png" })

// ❌ 缓慢(除非必要否则避免)
storeMediaFile({ filename: "img.png", data: "base64..." })

Read-Only Mode Not Working

只读模式不生效

bash
undefined
bash
undefined

Verify environment variable

验证环境变量

echo $READ_ONLY
echo $READ_ONLY

Or use CLI flag explicitly

或显式使用CLI标志

ankimcp --stdio --read-only
undefined
ankimcp --stdio --read-only
undefined

Batch Operation Partial Failures

批量操作部分失败

typescript
// addNotes supports partial success
const result = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: { notes: [...] }
});

// Check which notes succeeded and which failed
result.success.forEach((id, idx) => {
  if (id) console.log(`Note ${idx}: Success - ID ${id}`);
});

result.errors.forEach((err, idx) => {
  if (err) console.log(`Note ${idx}: Failed - ${err.error}`);
});
typescript
// addNotes支持部分成功
const result = await use_mcp_tool({
  server_name: "anki-mcp",
  tool_name: "addNotes",
  arguments: { notes: [...] }
});

// 检查哪些笔记成功,哪些失败
result.success.forEach((id, idx) => {
  if (id) console.log(`Note ${idx}: Success - ID ${id}`);
});

result.errors.forEach((err, idx) => {
  if (err) console.log(`Note ${idx}: Failed - ${err.error}`);
});

Deck Creation Limits

卡组创建限制

typescript
// Only 2 levels supported: Parent::Child
// ✅ Valid
createDeck({ deck: "Languages::Spanish" })

// ❌ Invalid (too many levels)
createDeck({ deck: "Languages::Spanish::Verbs" })
typescript
// 仅支持2级:父卡组::子卡组
// ✅ 有效
createDeck({ deck: "Languages::Spanish" })

// ❌ 无效(层级过多)
createDeck({ deck: "Languages::Spanish::Verbs" })

Best Practices

最佳实践

  1. Always sync before review sessions: Use
    sync
    tool at start of review workflow
  2. Use file paths for media: Avoid base64 when possible for performance
  3. Batch note creation: Use
    addNotes
    instead of multiple
    addNote
    calls
  4. Check existing tags: Call
    getTags
    before adding to avoid duplication
  5. Preserve note content: Get
    notesInfo
    before
    updateNoteFields
    to avoid data loss
  6. Use read-only mode for exploration: Enable
    --read-only
    when testing queries
  7. Handle partial failures: Check
    errors
    array in
    addNotes
    response
  8. Verify decks exist: Use
    listDecks
    before creating or moving cards
  1. 复习会话前务必同步:在复习工作流开始时使用
    sync
    工具
  2. 使用文件路径处理媒体:尽量避免使用base64以提升性能
  3. 批量创建笔记:使用
    addNotes
    而非多次调用
    addNote
  4. 检查现有标签:添加标签前调用
    getTags
    避免重复
  5. 保留笔记内容:更新
    updateNoteFields
    前先获取
    notesInfo
    避免数据丢失
  6. 探索时使用只读模式:测试查询时启用
    --read-only
  7. 处理部分失败:检查
    addNotes
    响应中的
    errors
    数组
  8. 验证卡组存在:创建或移动卡片前使用
    listDecks

Resources

资源