Doramagic 项目包 · 项目说明书
anki-mcp-server 项目
生成时间:2026-05-31 19:46:39 UTC
项目介绍
Anki MCP Server 是一个基于 Model Context Protocol (MCP) 的 Anki 间隔重复记忆应用服务端。该项目使 AI 助手能够与 Anki 进行自然语言交互,实现学习辅助、卡片管理、复习调度等功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Anki MCP Server 是一个基于 Model Context Protocol (MCP) 的 Anki 间隔重复记忆应用服务端。该项目使 AI 助手能够与 Anki 进行自然语言交互,实现学习辅助、卡片管理、复习调度等功能。
资料来源:manifest.json:3-6
核心功能
主要能力
| 功能类别 | 具体功能 |
|---|---|
| 复习管理 | 获取到期卡片、呈现卡片、评分卡片、同步 |
| 笔记操作 | 添加笔记、批量添加笔记、编辑笔记、删除笔记 |
| 模型管理 | 创建笔记类型、获取字段名称、获取样式、更新样式 |
| 牌组管理 | 获取牌组列表、管理牌组 |
| 媒体文件 | 存储媒体文件、获取媒体文件 |
| 统计信息 | 集合级统计、复习统计 |
资料来源:manifest.json:25-48
技术架构
该项目采用 NestJS 框架构建,基于 MCP 协议实现与 AI 助手的通信,通过 AnkiConnect 插件与 Anki 桌面应用交互。
graph TD
A[AI 助手] -->|MCP 协议| B[Anki MCP Server]
B -->|HTTP API| C[AnkiConnect 插件]
C --> D[Anki 桌面应用]
B --> E[STDIO 传输]
B --> F[Streamable HTTP 传输]
B --> G[ngrok 隧道]资料来源:CLAUDE.md:1-20
项目结构
anki-mcp-server/
├── src/
│ ├── mcp/
│ │ ├── clients/ # AnkiConnect HTTP 客户端
│ │ ├── config/ # 配置接口
│ │ ├── types/ # TypeScript 类型定义
│ │ ├── utils/ # 工具函数
│ │ └── primitives/
│ │ ├── essential/ # 核心工具
│ │ └── gui/ # GUI 交互工具
│ └── main.ts # 应用入口
├── bin/ # CLI 入口脚本
├── dist/ # 编译输出
├── package.json
└── server.json # MCP 服务器配置
资料来源:CLAUDE.md:1-15
模块系统
项目采用 NestJS 的模块化设计,通过依赖注入组织各个功能模块:
graph LR
A[AppModule] --> B[McpModule]
A --> C[McpPrimitivesAnkiEssentialModule]
A --> D[McpPrimitivesAnkiGuiModule]
B --> E[STDIO / HTTP 传输]
C --> F[核心工具集]
D --> G[GUI 交互工具]核心模块
| 模块 | 职责 |
|---|---|
McpModule | MCP 协议传输层,支持 STDIO 和 Streamable HTTP |
McpPrimitivesAnkiEssentialModule | 核心工具:复习、笔记、牌组管理等 |
McpPrimitivesAnkiGuiModule | GUI 工具:浏览窗口、卡片选择等 |
资料来源:CLAUDE.md:18-25
工具系统
工具定义模式
所有 MCP 工具遵循统一的实现模式:
// 1. Zod schema 用于输入验证
// 2. @Injectable() 类注入 AnkiConnectClient
// 3. @Tool() 装饰器定义工具元数据
// 4. execute() 方法调用 ankiClient.invoke()
// 5. 成功返回匹配 outputSchema 的原始对象
// 6. 错误返回 createErrorResponse()
资料来源:CLAUDE.md:42-52
工具注解
每个工具在 @Tool 装饰器中声明注解:
| 注解 | 说明 |
|---|---|
readOnlyHint | 标识是否为只读操作 |
destructiveHint | 标识是否为破坏性操作 |
idempotentHint | 标识是否为幂等操作 |
资料来源:CLAUDE.md:54-55
配置选项
IAnkiConfig 接口
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ankiConnectUrl | string | 'http://localhost:8765' | AnkiConnect 服务地址 |
ankiConnectApiVersion | number | 6 | API 版本 |
ankiConnectApiKey | string | - | 认证密钥(可选) |
ankiConnectTimeout | number | 5000 | 请求超时(毫秒) |
readOnly | boolean | false | 只读模式,阻止所有写入操作 |
资料来源:src/mcp/config/anki-config.interface.ts:5-30
只读模式
只读模式通过 readOnly 配置项启用,可阻止所有写入操作,防止意外修改 Anki 牌组。此功能响应社区需求(#18),为需要限制 MCP 服务器操作权限的用户提供解决方案。
资料来源:src/mcp/config/anki-config.interface.ts:25-26
传输方式
支持的传输协议
| 传输方式 | 说明 | 使用场景 |
|---|---|---|
| STDIO | 标准输入输出 | Claude Desktop、本地 CLI |
| Streamable HTTP | HTTP 长连接 | 远程访问、Web 集成 |
| ngrok 隧道 | 公共 URL 访问 | 无公网 IP 的远程访问 |
资料来源:CLAUDE.md:8-10
启动方式
# STDIO 模式(默认)
npx -y @ankimcp/anki-mcp-server --stdio
# HTTP 模式
npx -y @ankimcp/anki-mcp-server --http
# ngrok 隧道模式
npx -y @ankimcp/anki-mcp-server --ngrok
数据类型
核心类型定义
interface AddNoteParams {
deckName: string; // 目标牌组
modelName: string; // 笔记类型(Basic、Cloze 等)
fields: Record<string, string>; // 字段键值对
tags?: string[]; // 标签数组
options?: NoteOptions; // 重复检查选项
}
interface Model {
name: string; // 模型名称
id: number; // 唯一标识符
css: string; // CSS 样式
fields: string[]; // 字段名称数组
}
interface NoteInfo {
noteId: number; // 笔记 ID
modelName: string; // 使用的模型
tags: string[]; // 标签
fields: Record<string, { value: string; order: number }>;
cards: number[]; // 关联的卡片 ID
}
资料来源:src/mcp/types/anki.types.ts:25-50
依赖项
核心依赖
| 依赖 | 版本 | 用途 |
|---|---|---|
@nestjs/core | ^11.x | 核心框架 |
@modelcontextprotocol/sdk | 1.29.0 | MCP 协议实现 |
ky | ^1.14.3 | HTTP 客户端 |
zod | ^4.x | 数据验证 |
pino | ^10.x | 日志记录 |
commander | ^14.x | CLI 解析 |
已知兼容性问题
Node.js 版本要求:项目需要 Node.js 20.19+ 或更高版本。由于 ky 是纯 ESM 包,不支持 Node.js 21.x(该版本未包含 require(esm) 功能)。
资料来源:package.json:1-50
安全特性
媒体操作安全
v0.15.1 版本引入了多层安全验证:
- 路径遍历防护:防止通过
storeMediaFile路径参数读取任意文件 - SSRF 防护:防止通过 URL 参数发起服务端请求伪造攻击
- MIME 类型白名单:验证上传文件的实际类型
此安全加固由 Hideaki Takahashi 报告并修复。
资料来源:CHANGELOG.md:1-10
版本历史
| 版本 | 主要变更 |
|---|---|
| 0.18.x | 最新稳定版,支持隧道功能 |
| 0.15.x | 媒体安全加固 |
| 0.14.x | 批量添加笔记、牌组统计 |
| 0.8.x | 早期版本,存在 ESM 兼容性问题 |
资料来源:CHANGELOG.md:1-25
快速开始
前置要求
- 安装 Anki 桌面应用
- 安装 AnkiConnect 插件(插件编号 2055492159)
- 确保 Node.js >= 20.19
安装步骤
# 通过 npx 运行
npx -y @ankimcp/anki-mcp-server --stdio
# 或全局安装
npm install -g @ankimcp/anki-mcp-server
Claude Desktop 配置
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"]
}
}
}
社区反馈
高优先级功能请求
| Issue | 功能 | 状态 |
|---|---|---|
| #22 | 创建卡片超时问题 | 建议使用 addNotes 批量操作 |
| #21 | 批量创建卡片性能 | addNotes 工具已可用 |
| #19 | changeDeck 和 reposition 工具 | 规划中 |
| #14 | 牌组和集合级统计 | 部分可用 |
资料来源:社区上下文
相关资源
- 官网:https://ankimcp.ai
- 文档:https://github.com/ankimcp/anki-mcp-server#readme
- 问题反馈:https://github.com/ankimcp/anki-mcp-server/issues
- 隐私政策:https://ankimcp.ai/privacy/
资料来源:manifest.json:10-15
资料来源:manifest.json:3-6
安装与配置
Anki MCP Server 是一个基于 Model Context Protocol (MCP) 的服务器,为 Anki 间隔重复记忆应用提供 AI 交互能力。本页面详细说明该服务器的安装前置条件、多种安装方式、配置选项以及各客户端集成方法。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
系统要求
运行时环境
| 组件 | 最低版本要求 | 推荐版本 | 备注 |
|---|---|---|---|
| Node.js | >=20.19.0 | 20.x LTS | 禁止使用 Node 21.x,该版本存在 ERR_REQUIRE_ESM 兼容性问题 |
| npm | 最新稳定版 | 最新稳定版 | 用于全局安装或本地依赖管理 |
| Anki | 2.1.x | 2.1.50+ | 桌面版,必须安装 AnkiConnect 插件 |
| AnkiConnect | 最新版本 | 最新版本 | Anki 插件,版本号 6.0.0+ |
重要提示:Node.js 21.x 版本不支持 ESM 模块的 CommonJS 导入方式,会导致 ky 依赖库加载失败,产生 ERR_REQUIRE_ESM 错误。如果当前系统使用 Node 21.x,请降级至 Node 20.19+ 或升级至 Node 22+。资料来源:package.json:dependencies 和 issues/#17
AnkiConnect 插件安装
- 启动 Anki 桌面应用
- 进入 Tools → Add-ons → Get Add-ons
- 在代码框中输入插件编号:
2055492159 - 点击 OK 安装插件
- 重启 Anki 确保插件生效
插件安装后,AnkiConnect 默认监听 http://127.0.0.1:8765 端口。资料来源:manifest.json
安装方式
方式一:全局安装 (npm)
npm install -g @ankimcp/anki-mcp-server
安装完成后,使用以下命令启动:
# STDIO 模式(适用于 Claude Desktop 等 MCP 客户端)
anki-mcp --stdio
# HTTP 模式(适用于 Web 集成)
anki-mcp --http
# Ngrok 隧道模式(适用于远程访问)
anki-mcp --ngrok
资料来源:package.json:bin
方式二:npx 临时运行
无需预先安装,直接通过 npx 运行:
# STDIO 模式
npx -y @ankimcp/anki-mcp-server --stdio
注意:使用 npx 时,首次运行会临时下载并执行,可能存在网络延迟。资料来源:issues/#16
方式三:Docker 部署
对于需要隔离环境的用户,可通过 Docker 运行:
FROM node:20-slim
RUN npm install -g @ankimcp/anki-mcp-server
CMD ["anki-mcp", "--stdio"]
运行容器时需将 AnkiConnect 端口映射到宿主机:
docker run -p 8765:8765 anki-mcp-container
传输模式
Anki MCP Server 支持三种传输模式,适用于不同的使用场景:
graph TD
A[Anki MCP Server] --> B[STDIO 模式]
A --> C[HTTP 模式]
A --> D[Ngrok 隧道模式]
B --> B1[Claude Desktop]
B --> B2[Cursor]
B --> B3[其他 MCP 客户端]
C --> C1[Web 应用集成]
C --> C2[自定义 HTTP 客户端]
D --> D1[ngrok 隧道]
D --> D2[远程访问]
style B fill:#90EE90
style C fill:#87CEEB
style D fill:#FFB6C1STDIO 模式
标准输入/输出模式,适合本地 MCP 客户端集成,如 Claude Desktop、VS Code 等。
anki-mcp --stdio
配置文件示例 (~/.claude/desktops/claude_desktop_config.json):
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
"env": {
"ANKI_CONNECT_URL": "http://127.0.0.1:8765"
}
}
}
}
资料来源:server.json
HTTP 模式
HTTP 服务器模式,适合需要远程访问或 Web 集成的场景。
anki-mcp --http --port 8080
| 参数 | 默认值 | 说明 |
|---|---|---|
--port | 8080 | HTTP 服务监听端口 |
--host | 127.0.0.1 | 绑定地址(建议仅本地使用) |
Ngrok 隧道模式
通过 ngrok 创建公网可访问的隧道,适合需要远程访问 Anki 的场景。
anki-mcp --ngrok --api-key YOUR_NGROK_API_KEY
前提条件:
- 已安装 ngrok 并配置 API Key
- ngrok 版本 >= 3.0
已知问题:部分 ngrok 3.24.x 版本可能存在启动问题,如遇此情况可降级至 7.0 版本。资料来源:issues/#9
环境变量配置
| 环境变量 | 默认值 | 说明 |
|---|---|---|
ANKI_CONNECT_URL | http://127.0.0.1:8765 | AnkiConnect API 地址 |
ANKI_CONNECT_TIMEOUT | 30000 | 请求超时时间(毫秒) |
ANKI_CONNECT_RETRIES | 3 | 失败重试次数 |
DEBUG | false | 启用调试日志输出 |
AnkiConnect 连接配置
AnkiConnect 默认配置位于 Anki 插件设置中,主要参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| API Key | 空 | 可选的安全认证密钥 |
| 允许来源 | ["127.0.0.1", "localhost"] | CORS 允许的来源地址 |
| 最大并发请求 | 6 | 同时处理的最大请求数 |
客户端集成
Claude Desktop 配置
在 Claude Desktop 配置文件中添加以下内容:
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
"env": {
"ANKI_CONNECT_URL": "http://127.0.0.1:8765",
"ANKI_CONNECT_TIMEOUT": "30000"
}
}
}
}
配置文件位置:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
已知问题:某些 Claude Desktop 版本可能存在 MCP 服务器配置兼容性问题,如安装后无法启动,请确保使用最新版本的 Claude Desktop。资料来源:issues/#10
VS Code MCP 扩展配置
使用 VS Code 的 MCP 扩展时,在 VS Code 设置中添加:
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"]
}
}
}
Cursor IDE 配置
在 Cursor 的 MCP 设置中配置:
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"]
}
}
}
架构概览
graph LR
A[MCP 客户端<br/>Claude/VS Code] --> B[Anki MCP Server]
B --> C[AnkiConnect Client<br/>ky HTTP]
C --> D[AnkiConnect API]
D --> E[Anki 应用]
B --> F[NestJS IoC 容器]
F --> G[MCP Tools]
F --> H[MCP Resources]
F --> I[MCP Prompts]
style C fill:#FFD700
style E fill:#87CEEB常见问题排查
ERR_REQUIRE_ESM 错误
症状:启动时报 Error [ERR_REQUIRE_ESM]: require() of ES Module 错误
原因:Node.js 版本不兼容,当前使用 Node 21.x
解决方案:
# 检查 Node 版本
node --version
# 切换到 Node 20.x
nvm install 20
nvm use 20
资料来源:issues/#12
AnkiConnect 连接超时
症状:创建卡片时频繁超时
原因:AnkiConnect 单线程 Qt 事件循环无法处理高并发请求
解决方案:
- 批量操作时使用
addNotes而非多次addNote - 降低请求频率
- 确保 Anki 窗口未最小化或被遮挡
资料来源:issues/#22
Ngrok 启动失败
症状:failed to start ngrok 错误
解决方案:
- 确认 ngrok 已正确安装并配置 API Key
- 尝试降级 ngrok 版本:
npx [email protected]
资料来源:issues/#9
安全注意事项
- 网络隔离:默认情况下,AnkiConnect 仅允许本地连接(127.0.0.1)
- API Key:在公共网络环境下使用 HTTP 模式,建议配置 API Key
- 媒体文件:服务器包含媒体路径遍历和 SSRF 攻击防护机制
- 只读模式:生产环境中可考虑使用只读模式限制写操作
资料来源:v0.15.1 release notes 和 issues/#18
资料来源:package.json:bin
系统架构
anki-mcp-server 是一个基于 Model Context Protocol (MCP) 的 Anki 间隔重复闪卡应用服务器。它允许 AI 助手(如 Claude)与 Anki 进行自然语言交互,实现复习会话、卡组管理、笔记创建与编辑等功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
anki-mcp-server 是一个基于 Model Context Protocol (MCP) 的 Anki 间隔重复闪卡应用服务器。它允许 AI 助手(如 Claude)与 Anki 进行自然语言交互,实现复习会话、卡组管理、笔记创建与编辑等功能。
核心架构采用 NestJS 框架构建,通过 AnkiConnect 插件与 Anki 桌面应用通信,支持 STDIO 和 HTTP 两种传输模式。
技术栈
| 层级 | 技术/框架 | 版本要求 |
|---|---|---|
| 运行时 | Node.js | ≥20.19.0(明确排除 21.x) |
| 框架 | NestJS | ^11.x |
| MCP 协议 | @modelcontextprotocol/sdk | 精确版本 1.29.0 |
| HTTP 客户端 | ky | ^1.14.3(ESM 专有) |
| 数据验证 | Zod | ^4.3.6(非 v3) |
| 日志 | pino | ^10.3.1 |
注意:Node 21.x 不被支持,因为require(esm)功能在 Node 21 中不存在(该功能在 Node 22 和 Node 20.17+ 中才被引入)。用户报告在 Node 21 环境下会遇到ERR_REQUIRE_ESM错误。资料来源:package.json
整体架构图
graph TB
subgraph "MCP 客户端 (AI Agent)"
A[Claude Desktop / VS Code / 其他 MCP 客户端]
end
subgraph "anki-mcp-server"
B[入口层<br/>bootstrap.ts]
C[传输层<br/>STDIO / HTTP]
D[核心模块<br/>AppModule]
E[MCP 协议层<br/>@rekog/mcp-nest]
F[业务层<br/>Primitives Modules]
G[通信层<br/>AnkiConnectClient]
end
subgraph "外部服务"
H[AnkiConnect 插件]
I[Anki 桌面应用]
end
A -->|MCP 协议| B
B --> C
C --> D
D --> E
E --> F
F --> G
G -->|HTTP| H
H --> I模块系统
核心模块结构
项目采用 NestJS 的模块化架构,核心模块包括:
AppModule.forStdio() // STDIO 传输模式
AppModule.forHttp() // HTTP 传输模式
模块依赖关系
graph LR
subgraph "传输层"
A[STDIO 传输]
B[HTTP 传输]
end
subgraph "核心层"
C[McpModule]
D[McpPrimitivesAnkiEssentialModule]
E[McpPrimitivesAnkiGuiModule]
end
subgraph "工具层"
F[Review 工具]
G[Deck 工具]
H[Note 工具]
I[Media 工具]
J[GUI 工具]
end
A --> C
B --> C
C --> D
C --> E
D --> F
D --> G
D --> H
D --> I
E --> J工具分类
项目中的 MCP 工具分为两大类:
| 类别 | 模块 | 说明 | 权限级别 |
|---|---|---|---|
| Essential | McpPrimitivesAnkiEssentialModule | 核心复习、笔记操作 | 普通权限 |
| GUI | McpPrimitivesAnkiGuiModule | 需要用户确认的界面操作 | 需用户批准 |
Essential 工具包括:
sync- 与 AnkiWeb 同步get_due_cards- 获取待复习卡片get_cards- 按状态获取卡片present_card- 展示卡片问题面rate_card- 评分卡片addNote/addNotes- 创建笔记modelNames/modelFieldNames- 获取笔记类型信息getTags- 获取所有标签modelStyling/updateModelStyling- 样式管理createModel- 创建笔记类型
GUI 工具包括:
guiBrowse- 打开卡片浏览器guiSelectCard- 选择卡片guiSelectedNotes- 获取选中笔记
用户请求功能:工具权限控制(只读模式)是一个被关注的功能需求,详见 Issue #18
核心组件
1. AnkiConnectClient
AnkiConnectClient 是与 Anki 通信的核心 HTTP 客户端:
| 特性 | 说明 |
|---|---|
| 协议 | HTTP POST |
| 默认地址 | http://127.0.0.1:8765 |
| 请求库 | ky(ESM) |
| 重试机制 | 内置重试逻辑 |
| 只读守卫 | 可配置的 read-only guard |
关键文件:src/mcp/clients/anki-connect.client.ts
// 核心调用模式
class AnkiConnectClient {
invoke<T>(action: AnkiAction, params?: Record<string, unknown>): Promise<T>
// 支持重试和错误处理
}
2. 配置系统
配置系统采用分层设计:
graph TB
A[命令行参数 / 环境变量]
B[AnkiConfigService]
C[IAnkiConfig 接口]
D[注入令牌 ANKI_CONFIG]
A --> B
B --> C
C --> DIAnkiConfig 接口 定义:
| 配置项 | 类型 | 说明 | 默认值 |
|---|---|---|---|
host | string | AnkiConnect 主机地址 | 127.0.0.1 |
port | number | AnkiConnect 端口 | 8765 |
readOnly | boolean | 只读模式 | false |
retries | number | 重试次数 | 3 |
关键文件:
3. 工具模式 (Tool Pattern)
所有 MCP 工具遵循统一的实现模式:
graph LR
A[Zod 输入验证] --> B[Tool 装饰器]
B --> C[execute 方法]
C --> D[调用 AnkiConnectClient]
D --> E[返回结果或错误]标准工具结构:
// 1. Zod schema 用于输入验证
// 2. @Injectable() 类注入 AnkiConnectClient
// 3. @Tool 装饰器定义元数据
// 4. execute() 方法执行业务逻辑
关键文件:src/mcp/primitives/essential/tools/get-tags.tool.ts
工具响应格式:
| 场景 | 返回方式 | 说明 |
|---|---|---|
| 成功路径 | 返回原始对象 | 匹配 outputSchema,handler 自动包装 |
| 错误路径 | createErrorResponse() | 返回 isError: true,绕过 schema 验证 |
4. 类型系统
核心类型定义位于 anki.types.ts:
| 类型 | 用途 |
|---|---|
AddNoteParams | 添加笔记参数 |
Model | 笔记类型信息 |
NoteInfo | 笔记详细信息 |
AnkiCard | 卡片信息 |
CardRating | 评分枚举 |
关键文件:src/mcp/types/anki.types.ts
传输模式
STDIO 模式
适用于命令行和桌面 AI 客户端(如 Claude Desktop):
npx -y @ankimcp/anki-mcp-server --stdio
HTTP 模式
支持远程访问和 ngrok 隧道:
# 本地 HTTP
npx -y @ankimcp/anki-mcp-server --http
# 带 ngrok 隧道
npx -y @ankimcp/anki-mcp-server --tunnel
注意:用户报告 ngrok 在某些版本下可能启动失败(Issue #9)
数据流
笔记创建流程
sequenceDiagram
participant AI as AI Agent
participant MCP as anki-mcp-server
participant ACC as AnkiConnectClient
participant AK as AnkiConnect
participant AN as Anki
AI->>MCP: addNote / addNotes
MCP->>ACC: invoke("addNotes")
ACC->>AK: HTTP POST
AK->>AN: 创建笔记
AN-->>AK: 响应
AK-->>ACC: 结果
ACC-->>MCP: 格式化响应
MCP-->>AI: 成功/错误批量操作:建议使用addNotes而非多个addNote调用,因为 AnkiConnect 基于 Qt 单线程事件循环,并行调用可能导致超时(Issue #22、Issue #21)
复习流程
graph LR
A[获取待复习卡片] --> B[展示问题]
B --> C[用户响应]
C --> D{评分}
D -->|Again| E[标记失败]
D -->|Hard| F[标记困难]
D -->|Good| G[标记成功]
D -->|Easy| H[标记简单]
E --> I[安排下次复习]
F --> I
G --> I
H --> I入口与引导
bootstrap.ts
启动流程:
// 1. 创建 NestJS 应用
// 2. 读取配置
// 3. 选择传输模式 (STDIO/HTTP)
// 4. 初始化 MCP 模块
// 5. 启动监听
关键文件:src/bootstrap.ts
AppModule 结构
@Module({
imports: [
// MCP 核心模块
McpModule.forRoot(), // STDIO 或 STREAMABLE_HTTP
McpPrimitivesAnkiEssentialModule.forRoot(),
McpPrimitivesAnkiGuiModule.forRoot(),
],
providers: [
// 显式声明的工具列表
// ESSENTIAL_MCP_TOOLS
// GUI_MCP_TOOLS
],
})
export class AppModule {}
MCP-Nest 1.9.0+ 要求工具必须同时在 AppModule.providers 中声明。资料来源:CLAUDE.md
安全机制
媒体操作安全
v0.15.1 版本引入了多层安全验证:
| 安全措施 | 说明 |
|---|---|
| MIME 类型白名单 | 验证文件类型 |
| 路径遍历防护 | 防止通过路径参数读取任意文件 |
| SSRF 防护 | 防止通过 URL 参数发起请求 |
此修复由 Hideaki Takahashi 报告,详见 Release v0.15.1
只读模式
可通过配置启用只读模式,限制所有写操作:
| 配置项 | 效果 |
|---|---|
readOnly: true | 禁止创建、修改、删除笔记 |
AnkiConnect 已知特性
项目中记录了以下 AnkiConnect 上游行为:
| 行为 | 影响 |
|---|---|
updateNoteFields 在笔记打开时静默失败 | 请求返回 200 但字段未保存 |
| Model CSS 是按笔记类型的 | 使用 modelStyling 获取 |
sync 需要桌面应用登录 AnkiWeb | 无 API 路径可认证 |
deleteNotes 不可逆且级联删除 | 需显式 confirmDeletion: true |
资料来源:CLAUDE.md
依赖关系
关键生产依赖
| 依赖 | 版本 | 用途 |
|---|---|---|
| @nestjs/core | ^11.x | NestJS 框架核心 |
| @modelcontextprotocol/sdk | 1.29.0 | MCP 协议实现 |
| @rekog/mcp-nest | - | MCP-Nest 集成 |
| ky | ^1.14.3 | HTTP 客户端(ESM) |
| zod | ^4.3.6 | 数据验证 |
| pino | ^10.3.1 | 日志框架 |
| passport | ^0.7.0 | 认证中间件 |
开发依赖
| 依赖 | 用途 |
|---|---|
| @anthropic-ai/mcpb | MCPB 配置 |
| @nestjs/cli | NestJS CLI |
| @nestjs/testing | 测试框架 |
| typescript | TypeScript 编译器 |
项目结构
anki-mcp-server/
├── src/
│ ├── app.module.ts # 主应用模块
│ ├── bootstrap.ts # 启动入口
│ ├── anki-config.service.ts # 配置服务
│ └── mcp/
│ ├── clients/
│ │ └── anki-connect.client.ts # AnkiConnect 通信客户端
│ ├── config/
│ │ └── anki-config.interface.ts # 配置接口
│ ├── types/
│ │ └── anki.types.ts # 类型定义
│ ├── utils/
│ │ ├── anki.utils.ts # 工具函数
│ │ └── markdown.utils.ts # Markdown 处理
│ └── primitives/
│ ├── essential/ # 核心工具
│ │ ├── tools/ # 工具实现
│ │ └── prompts/ # 提示词模板
│ └── gui/ # GUI 工具
├── package.json
└── tsconfig.json
版本信息
| 版本 | 说明 |
|---|---|
| 当前版本 | 0.18.5 |
| 最新测试版本 | v0.18.5-tunnel.2 |
| 语义化版本 | 主版本 0(Beta 阶段) |
详见 CHANGELOG.md
资料来源:CLAUDE.md
传输模式
Anki MCP Server 支持两种主要的传输模式,用于 MCP 客户端与服务器之间的通信。正确选择传输模式对于集成、部署和安全性都有重要影响。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
| 传输模式 | 用途 | 适用场景 | 安全级别 |
|---|---|---|---|
| STDIO | 标准输入输出 | Claude Desktop、本地集成 | 高(本地通信) |
| HTTP + Ngrok | HTTP 远程访问 | 远程客户端、跨网络访问 | 中(需 Ngrok 配置) |
STDIO 模式
STDIO(标准输入/输出)是 MCP 协议的默认传输方式,适用于本地进程间通信。
工作原理
graph LR
A["MCP 客户端<br/>(Claude Desktop)"] -->|stdin| B["Anki MCP Server<br/>(子进程)"]
B -->|stdout| A
B --> C["AnkiConnect API<br/>(localhost:8765)"]
C --> D["Anki 应用"]启动命令
# 通过 npx 直接运行
npx -y @ankimcp/anki-mcp-server --stdio
# 或指定特定版本
npx -y @ankimcp/anki-mcp-server@latest --stdio
源码实现
入口文件:src/main-stdio.ts
STDIO 模式使用 NestJS 框架的模块系统初始化服务:
// 伪代码示例
AppModule.forStdio()
.then(() => bootstrap())
配置方式
在 Claude Desktop 配置文件中添加 STDIO 传输:
{
"mcpServers": {
"anki-mcp": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
"env": {
"ANKI_HOST": "localhost",
"ANKI_PORT": "8765"
}
}
}
}
HTTP 模式
HTTP 传输模式提供基于 HTTP 的远程访问能力,支持 MCP Streamable HTTP 规范。
工作原理
graph TD
A["MCP 客户端"] -->|HTTPS| B["Ngrok Tunnel"]
B -->|HTTP| C["Anki MCP Server"]
C --> D["AnkiConnect API"]
D --> E["Anki 应用"]
F["本地客户端"] -->|HTTP| C启动命令
# 基本 HTTP 模式
npx -y @ankimcp/anki-mcp-server --http
# 带 Ngrok 隧道的远程访问
npx -y @ankimcp/anki-mcp-server --http --ngrok
# 指定端口
npx -y @ankimcp/anki-mcp-server --http --port 8080
# 带认证令牌
npx -y @ankimcp/anki-mcp-server --http --auth-token <TOKEN>
源码实现
入口文件:src/main-http.ts
HTTP 模式使用 AppModule.forHttp() 初始化,提供完整的 HTTP 服务器支持:
// 伪代码示例
AppModule.forHttp({
port: 8080,
ngrok: process.env.USE_NGROK === 'true',
authToken: process.env.AUTH_TOKEN
})
HTTP 服务配置参数
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
| 端口 | PORT | 8080 | HTTP 服务器监听端口 |
| Host | HOST | 0.0.0.0 | 绑定地址 |
| Ngrok | USE_NGROK | false | 是否启用 Ngrok 隧道 |
| 认证令牌 | AUTH_TOKEN | 无 | HTTP 认证令牌 |
| Anki 地址 | ANKI_HOST | localhost | AnkiConnect 服务地址 |
| Anki 端口 | ANKI_PORT | 8765 | AnkiConnect 服务端口 |
Ngrok 隧道服务
Ngrok 提供安全的公共 URL 用于远程访问本地 MCP 服务器。
服务实现
// 伪代码示例 - NgrokService
class NgrokService {
async startTunnel(port: number): Promise<string> {
// 1. 启动 ngrok 进程
// 2. 轮询 ngrok API 获取公网 URL
// 3. 返回 HTTPS 端点 URL
return ngrokUrl;
}
}
资料来源:src/services/ngrok.service.ts:1-100
Ngrok 配置要求
- 安装 Ngrok:需要预先安装 ngrok CLI 工具
- 认证:配置 ngrok auth token(用于持久化隧道)
- 版本注意:推荐使用 ngrok 3.x 版本
已知问题
社区反馈 #9 指出 Ngrok 在某些版本上可能启动失败:
"ngrok fails to start with latest version... I was able to work around this by using version 7.0"
如遇问题,可尝试降级或使用 npx [email protected]。
安全机制
来源验证
HTTP 模式包含来源验证保护,防止 DNS 重绑定攻击:
@Injectable()
export class OriginValidationGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// 验证请求来源
// 防止 DNS rebinding 攻击
}
}
资料来源:src/http/guards/origin-validation.guard.ts:1-50
认证机制
HTTP 模式支持令牌认证:
# 设置认证令牌
export AUTH_TOKEN="your-secret-token"
# 启动服务
npx -y @ankimcp/anki-mcp-server --http
客户端连接时需要在请求头中包含令牌:
Authorization: Bearer <your-secret-token>
只读模式
社区功能请求 #18 建议添加只读模式,可限制服务器仅执行读取操作:
npx -y @ankimcp/anki-mcp-server --stdio --read-only
该功能可以防止意外修改 Anki 数据,适合学习和浏览场景。
传输模式选择指南
graph TD
A["选择传输模式"] --> B{使用场景}
B -->|Claude Desktop| C["STDIO 模式"]
B -->|VS Code| C
B -->|远程访问| D["HTTP + Ngrok"]
B -->|本地开发| E{"需要 Ngrok?"}
E -->|是| F["HTTP + Ngrok"]
E -->|否| G["HTTP 模式"]
C --> H["高安全性<br/>本地通信"]
F --> I["中等安全性<br/>公网访问"]
G --> J["中等安全性<br/>内网访问"]性能考虑
| 模式 | 延迟 | 并发支持 | 适用卡组规模 |
|---|---|---|---|
| STDIO | 极低 | 单一客户端 | 任何规模 |
| HTTP | 低 | 多客户端 | 任何规模 |
| HTTP + Ngrok | 中等(网络延迟) | 多客户端 | 建议配合 addNotes 批量操作 |
社区问题 #22 报告了大量创建卡片时的超时问题,建议在批量操作时使用 addNotes 工具而非多次调用 addNote。
模块系统
根据 CLAUDE.md 的模块架构,两种传输模式共享核心功能:
AppModule.forStdio() / forHttp()
├─ McpModule.forRoot() # 传输层
├─ McpPrimitivesAnkiEssentialModule.forRoot() # 核心工具
└─ McpPrimitivesAnkiGuiModule.forRoot() # GUI工具
资料来源:CLAUDE.md 模块系统章节
命令行参数参考
| 参数 | 模式 | 说明 |
|---|---|---|
--stdio | STDIO | 启用标准输入输出传输 |
--http | HTTP | 启用 HTTP 传输 |
--port <num> | HTTP | 指定 HTTP 端口 |
--ngrok | HTTP | 启用 Ngrok 隧道 |
--auth-token <token> | HTTP | 设置认证令牌 |
--read-only | 通用 | 限制为只读操作 |
故障排除
STDIO 常见问题
| 问题 | 解决方案 |
|---|---|
ERR_REQUIRE_ESM | 确认 Node.js 版本 >= 20.19(项目已排除 21.x) |
| 连接超时 | 确保 AnkiConnect 插件已安装并运行 |
HTTP 常见问题
| 问题 | 解决方案 |
|---|---|
| Ngrok 启动失败 | 检查 ngrok 安装和认证状态 |
| 连接被拒绝 | 确认端口未被占用,检查防火墙设置 |
Node.js 版本要求
项目要求 Node.js >= 20.19,明确排除 21.x 版本(社区问题 #17):
"users on Node 21.x getERR_REQUIRE_ESMbecause therequire(esm)feature doesn't exist in Node 21"
核心工具参考
Anki MCP Server 的核心工具是实现 AI 助手与 Anki 闪卡应用交互的基础组件。所有工具均通过 AnkiConnectClient 与 AnkiConnect 插件通信,利用 NestJS 依赖注入和 @rekog/mcp-nest 框架自动发现和注册。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Anki MCP Server 的核心工具是实现 AI 助手与 Anki 闪卡应用交互的基础组件。所有工具均通过 AnkiConnectClient 与 AnkiConnect 插件通信,利用 NestJS 依赖注入和 @rekog/mcp-nest 框架自动发现和注册。
核心工具按照功能分为以下几类:
| 类别 | 工具数量 | 主要用途 |
|---|---|---|
| 笔记管理 | 4 | 创建、查询、更新、删除笔记 |
| 复习管理 | 5 | 获取待复习卡片、显示卡片、评分 |
| 卡组管理 | 2 | 获取卡组列表、卡组统计 |
| 模型管理 | 4 | 列表字段、样式、创建模型 |
| 媒体管理 | 2 | 存储媒体文件、检索笔记媒体 |
| 标签管理 | 1 | 获取所有标签 |
| 同步管理 | 1 | AnkiWeb 同步 |
资料来源:manifest.json:manifest.json.tools
工具架构
工具实现模式
所有核心工具遵循统一的实现模式:
graph TD
A[MCP Client 调用工具] --> B[@Tool 装饰器]
B --> C[Zod 参数校验]
C --> D[execute 方法执行]
D --> E[AnkiConnectClient.invoke]
E --> F{成功?}
F -->|是| G[返回原始对象]
F -->|否| H[createErrorResponse]
G --> I[mcp-nest 自动包装]
H --> J[返回错误结果]工具类结构如下:
@Injectable()
export class SomeTool {
constructor(private readonly ankiClient: AnkiConnectClient) {}
@Tool({
name: "toolName",
description: "工具描述",
parameters: z.object({ /* 参数 schema */ }),
outputSchema: z.object({ /* 输出 schema */ }),
annotations: {
readOnlyHint: boolean,
destructiveHint: boolean,
idempotentHint?: boolean
}
})
async execute(params: SomeParams, context: Context) {
// 实现逻辑
}
}
资料来源:CLAUDE.md
错误处理机制
工具错误处理使用 createErrorResponse 函数,返回 CallToolResult 类型,绕过 outputSchema 验证:
return createErrorResponse(error, {
hint: "错误提示信息"
});
资料来源:src/mcp/utils/anki.utils.ts:anki.utils.ts.createErrorResponse
笔记管理工具
addNote - 创建单条笔记
创建单条笔记到指定卡组,使用前需要确保理解卡片创建的最佳实践。
参数定义:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| deckName | string | 是 | 目标卡组名称 |
| modelName | string | 是 | 笔记类型(如 "Basic", "Cloze") |
| fields | Record<string, string> | 是 | 字段名到值的映射 |
| tags | string[] | 否 | 标签数组 |
| options | NoteOptions | 否 | 重复检查等选项 |
重要提示:
- 批量创建时推荐使用
addNotes工具,单独调用addNote会产生多次 HTTP 请求 - 如果需要创建多条卡片,应使用
addNotes批量操作以提高性能
重复检查选项:
interface NoteOptions {
checkDuplicate: boolean; // 是否检查重复
duplicateScope: string; // 检查范围:"deck" | "deckAndNotes"
duplicateScopeDeckId?: number;
duplicateScopeOptions?: {
checkAllModels?: boolean;
};
}
资料来源:src/mcp/primitives/essential/tools/add-note.tool.ts 资料来源:src/mcp/types/anki.types.ts:AddNoteParams
社区反馈: 用户报告创建卡片时遇到 AnkiConnect 超时问题,建议批量操作使用 addNotes 而非多次调用 addNote。参见 Issue #22。
addNotes - 批量创建笔记
批量创建多条笔记,显著提高大量卡片创建效率。
设计背景: Claude Desktop 等 MCP 客户端会并行触发多个 addNote 调用,这会压垮 AnkiConnect 的单线程 Qt 事件循环。addNotes 工具专门为此场景设计。
参数结构:
interface AddNotesParams {
notes: Array<{
deckName: string;
modelName: string;
fields: Record<string, string>;
tags?: string[];
options?: NoteOptions;
}>;
}
返回结果:
{
success: boolean;
results: Array<{
index: number;
success: boolean;
error?: string;
noteId?: number;
}>;
added: number;
failed: number;
}
资料来源:src/mcp/primitives/essential/tools/add-notes.tool.ts 资料来源:Issue #23
findNotes - 搜索笔记
通过查询条件搜索笔记。
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| query | string | Anki 查询语法字符串 |
| limit | number | 最大返回数量(默认 100) |
Anki 查询语法示例:
deck:FDNY-CAPTAIN tag:10-codes
"Code 1" // 全文搜索
is:new // 新卡片
is:due // 到期卡片
资料来源:src/mcp/primitives/essential/tools/find-notes.tool.ts
updateNoteFields - 更新笔记字段
更新已有笔记的字段内容。
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| note | number | 是 | 笔记 ID |
| fields | Record<string, string> | 是 | 要更新的字段 |
注意事项: 如果目标笔记在 Anki 浏览窗口中打开,updateNoteFields 会静默失败(请求返回 200 但字段未保存)。这是上游 AnkiConnect 的行为。
资料来源:CLAUDE.md - Upstream AnkiConnect Quirks
deleteNotes - 删除笔记
永久删除指定笔记及其所有关联卡片。
参数:
{
notes: number[]; // 笔记 ID 数组
confirmDeletion: true; // 必须设置为 true
}
安全提示:
- 此操作不可逆,会级联删除笔记的所有卡片
- 工具注释标记
destructiveHint: true
资料来源:manifest.json:deleteNotes
复习管理工具
getDueCards - 获取到期卡片
获取需要复习的卡片。
{
deck?: string; // 可选卡组过滤
}
返回卡片结构包含:卡片 ID、笔记 ID、卡片类型、到期时间、间隔、难度等。
资料来源:src/mcp/types/anki.types.ts:AnkiCard
getCards - 灵活卡片筛选
按状态和卡组筛选卡片。
状态选项:
| 状态 | 说明 |
|---|---|
| due | 到期需要复习 |
| new | 新卡片 |
| learning | 学习中的卡片 |
| suspended | 暂停的卡片 |
| buried | 隐藏的卡片 |
presentCard - 显示卡片问题
显示卡片正面内容供复习使用。
rateCard - 评分卡片
对复习的卡片进行评分并安排下次复习时间。
评分选项:
| 评分 | 说明 |
|---|---|
| Again | 完全忘记,需要重新学习 |
| Hard | 记得但很困难 |
| Good | 正常回忆 |
| Easy | 太简单了 |
卡组管理工具
deckNames - 列出所有卡组
返回 Anki 中所有卡组的名称列表。
deckStats - 卡组统计信息
获取指定卡组的详细统计数据。
参数:
{
deck: string; // 卡组名称
includeRevLogs?: boolean; // 包含复习日志
days?: number; // 统计天数(默认 30)
}
返回数据:
{
deck: string;
date: string;
cards: {
total: number;
new: number;
learning: number;
mature: number;
};
reviews: number;
averageTime: number;
retention?: number;
breakdown?: {
again: number;
hard: number;
good: number;
easy: number;
};
}
功能特性:
- 支持子卡组统计(默认包含子卡组数据)
- 可选包含复习日志用于详细分析
- 支持自定义统计时间范围
资料来源:src/mcp/primitives/essential/tools/deck-stats.tool.ts
模型管理工具
modelNames - 列出笔记类型
返回所有可用的笔记类型/模型名称列表。
modelFieldNames - 获取字段名
获取指定笔记类型的字段名称列表。
{
modelName: string; // 模型名称
}
modelStyling - 获取 CSS 样式
获取笔记类型的 CSS 样式信息。
用途:
- 查看当前样式
- 为 RTL(从右到左)语言(希伯来语、阿拉伯语等)添加样式支持
- 自定义卡片外观
createModel - 创建笔记类型
创建自定义笔记类型,包含字段、卡片模板和 CSS 样式。
{
model: {
name: string;
inOrderFields: string[]; // 字段列表
css: string; // CSS 样式
isCloze: boolean; // 是否为填空模板
cardTemplates?: Array<{
Name: string;
Question: string; // 正面模板
Answer: string; // 反面模板
}>;
};
}
注意: Model CSS 是按笔记类型共享的,不是按笔记单独设置。使用 notesInfo 可获取笔记使用的模型。
资料来源:CLAUDE.md - Upstream AnkiConnect Quirks
标签管理工具
getTags - 获取所有标签
获取 Anki 集合中的所有标签列表。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| pattern | string | 可选的过滤模式(大小写不敏感) |
返回结构:
{
success: boolean;
tags: string[]; // 标签列表
total: number; // 总数
message: string;
filtered?: boolean; // 是否被过滤
totalUnfiltered?: number; // 过滤前的总数
}
使用场景:
- AI 代理在创建笔记前发现现有标签
- 防止标签重复(如 "roman-empire" vs "roman_empire")
- 维护标签命名一致性
社区需求: 此工具是为了解决 Issue #13 中用户反馈的问题:AI 代理无法看到现有标签,导致标签不一致。
资料来源:src/mcp/primitives/essential/tools/get-tags.tool.ts 资料来源:Issue #13
媒体管理工具
storeMediaFile - 存储媒体文件
将媒体文件存储到 Anki 集合中。
安全特性(v0.15.1+):
- MIME 类型白名单验证
- 路径遍历攻击防护
- SSRF 风险防护
参数:
{
path: string; // 文件路径或 URL
name: string; // 目标文件名
source: "path" | "url"; // 来源类型
}
支持的 MIME 类型: 图像、音频、视频等常见格式。
安全修复: v0.15.1 版本修复了路径遍历漏洞,该漏洞可能允许通过 storeMediaFile 路径参数进行任意文件读取,以及通过 URL 参数的 SSRF 风险。
资料来源:src/mcp/primitives/essential/tools/store-media-file.tool.ts 资料来源:v0.15.1 Release Notes
同步管理工具
sync - AnkiWeb 同步
与 AnkiWeb 同步以获取最新数据并推送更改。
前置条件: 桌面版 Anki 必须已登录 AnkiWeb 账户。MCP 服务器没有 API 路径来进行身份验证。
错误处理: 如果未登录,会返回提示建议检查 Anki 连接设置。
资料来源:src/mcp/primitives/essential/tools/sync.tool.ts 资料来源:CLAUDE.md - Upstream AnkiConnect Quirks
工具权限控制
只读模式
可通过 --read-only 全局标志限制所有写操作,防止意外修改 Anki 卡组。
受影响的工具:
- addNote / addNotes
- updateNoteFields
- deleteNotes
- createModel
- updateModelStyling
- sync(需要写权限)
社区需求: 参见 Issue #18:用户希望配置工具权限以防止 AI 意外修改卡组。
常用工作流
创建批量卡片
graph TD
A[开始] --> B[获取可用标签<br>getTags]
B --> C[确认标签列表]
C --> D[获取卡组名称<br>deckNames]
D --> E[确认目标卡组]
E --> F[获取模型字段<br>modelFieldNames]
F --> G[构建笔记数据]
G --> H[批量创建笔记<br>addNotes]
H --> I{成功?}
I -->|是| J[完成]
I -->|否| K[检查错误]
K --> G复习工作流
graph TD
A[开始复习] --> B[获取到期卡片<br>getDueCards]
B --> C{有到期卡片?}
C -->|否| Z[完成]
C -->|是| D[获取卡片详情]
D --> E[显示问题<br>presentCard]
E --> F[用户回忆]
F --> G[显示答案]
G --> H[用户评分]
H --> I[rateCard]
I --> J{继续复习?}
J -->|是| D
J -->|否| K[可选: sync]
K --> Z类型定义参考
AnkiCard - 卡片类型
interface AnkiCard {
cardId: number; // 卡片 ID
fields: Record<string, { value: string; order: number }>;
buttons: string[]; // 评分按钮 ["Again", "Hard", "Good", "Easy"]
css: string;
front: string; // 清理后的正面内容
back: string; // 清理后的背面内容
cardType: CardType; // "new" | "learning" | "review" | "relearning"
interval: number; // 间隔(天)
due: number; // Unix 时间戳
dueString: string;
lapses: number; // 遗忘次数
reps: number; // 复习次数
leftToday: number;
newToday: number;
reviewedToday: number;
remaining: number;
}
资料来源:src/mcp/types/anki.types.ts:AnkiCard
CardRating - 评分枚举
enum CardRating {
Again = 1, // 重新学习
Hard = 2, // 困难
Good = 3, // 良好
Easy = 4 // 简单
}
最佳实践
卡片创建
- 使用批量工具:创建多条卡片时使用
addNotes而非多次调用addNote - 预先验证:使用
getTags和modelFieldNames确认目标结构 - 标签一致性:建立统一的标签命名规范,避免重复
复习流程
- 定期同步:复习后使用
sync保持与 AnkiWeb 同步 - 批量评分:连续复习时批量使用
rateCard - 状态检查:使用
getCards检查不同状态的卡片
性能优化
| 场景 | 建议 |
|---|---|
| 大量卡片创建 | 使用 addNotes 批量创建 |
| 标签发现 | 使用 getTags 一次性获取,避免重复查询 |
| 卡组统计 | 设置合理的 days 参数避免返回过多数据 |
| 媒体上传 | 验证文件类型,使用 source: "path" 避免网络请求 |
相关文档
- CLAUDE.md - 开发指南和架构说明
- CHANGELOG.md - 版本变更记录
- Issue #21 - 批量创建功能讨论
- Issue #22 - 卡片创建超时问题
- Issue #23 - addNotes 批量操作引导
资料来源:manifest.json:manifest.json.tools
GUI 工具集
GUI 工具集是 anki-mcp-server 中用于与 Anki 桌面应用程序图形界面进行交互的工具模块。与核心复习工具不同,GUI 工具需要用户的显式操作批准,因为它们会打开或操作 Anki 的图形界面组件。这些工具主要用于笔记编辑和创建工作流,而非复习会话。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
GUI 工具集是 anki-mcp-server 中用于与 Anki 桌面应用程序图形界面进行交互的工具模块。与核心复习工具不同,GUI 工具需要用户的显式操作批准,因为它们会打开或操作 Anki 的图形界面组件。这些工具主要用于笔记编辑和创建工作流,而非复习会话。
根据 CLAUDE.md 的规范,GUI 工具必须包含双重警告:
- "IMPORTANT: Only use when user explicitly requests..."
- "This tool is for note editing/creation workflows, NOT for review sessions"
资料来源:CLAUDE.md
架构设计
模块结构
GUI 工具集采用 NestJS 模块化架构,通过 McpPrimitivesAnkiGuiModule 统一管理所有 GUI 相关工具。
graph TD
A[McpPrimitivesAnkiGuiModule] --> B[Browser Tools]
A --> C[Dialog Tools]
A --> D[Utility Tools]
B --> B1[GuiBrowseTool]
B --> B2[GuiSelectCardTool]
B --> B3[GuiSelectedNotesTool]
C --> C1[GuiAddCardsTool]
C --> C2[GuiEditNoteTool]
C --> C3[GuiDeckOverviewTool]
C --> C4[GuiDeckBrowserTool]
D --> D1[GuiCurrentCardTool]
D --> D2[GuiShowQuestionTool]
D --> D3[GuiShowAnswerTool]
D --> D4[GuiUndoTool]工具分类
| 分类 | 用途 | 典型场景 |
|---|---|---|
| Browser Tools | 卡片浏览器操作 | 搜索、选择笔记 |
| Dialog Tools | 对话框交互 | 添加卡片、编辑笔记、查看牌组 |
| Utility Tools | 界面状态查询 | 查看当前卡片、显示问答、撤销操作 |
资料来源:src/mcp/primitives/gui/index.ts:1-50
浏览器工具
GuiBrowseTool
用于打开 Anki 的卡片浏览器并搜索指定的卡片。此工具不执行复习会话相关的操作。
@Tool({
name: "guiBrowse",
description: "Open Anki Card Browser and search for cards...",
annotations: {
title: "Browse Cards",
readOnlyHint: false,
destructiveHint: false
}
})
输入参数:
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| query | string | 是 | Anki 搜索查询语法 |
使用限制:
- 仅用于笔记编辑/创建工作流
- 不适用于复习会话
资料来源:src/mcp/primitives/gui/tools/gui-browse.tool.ts
GuiSelectCardTool
在卡片浏览器中选中指定 ID 的卡片,用于笔记编辑流程中的卡片定位。
GuiSelectedNotesTool
获取卡片浏览器中当前选中的笔记 ID 列表,返回结果为笔记 ID 数组。
输出结构:
outputSchema: z.object({
noteIds: z.array(z.number())
})
资料来源:src/mcp/primitives/gui/tools/gui-selected-notes.tool.ts
对话框工具
GuiAddCardsTool
打开 Anki 的添加卡片对话框,允许用户通过图形界面添加新卡片。
GuiEditNoteTool
打开笔记编辑器,允许用户编辑现有笔记的内容和标签。
关键约束: 根据 CLAUDE.md 的说明,updateNoteFields 在目标笔记已在 Anki 浏览窗口中打开时会静默失败,请求返回 200 但字段不会保存。
GuiDeckOverviewTool
打开指定牌组的概览界面。
GuiDeckBrowserTool
打开牌组浏览器界面。
实用工具
GuiCurrentCardTool
获取当前在 Anki 界面中显示的卡片信息。
sequenceDiagram
Client->>GuiCurrentCardTool: 请求当前卡片
GuiCurrentCardTool->>AnkiConnectClient: invoke('guiCurrentCard')
AnkiConnectClient->>Anki: 获取当前卡片
Anki-->>AnkiConnectClient: 卡片数据
AnkiConnectClient-->>GuiCurrentCardTool: CardInfo
GuiCurrentCardTool-->>Client: { cardId, front, back, ... }资料来源:src/mcp/primitives/gui/tools/gui-current-card.tool.ts
GuiShowQuestionTool
显示卡片的问答面(问题/正面),在复习流程中呈现问题给用户。
GuiShowAnswerTool
显示卡片的答案面(回答/背面),在复习流程中呈现答案给用户。
@Tool({
name: "guiShowAnswer",
description: "Show the answer (back) of a card...",
annotations: {
title: "Show Answer",
readOnlyHint: true,
destructiveHint: false
}
})
资料来源:src/mcp/primitives/gui/tools/gui-show-answer.tool.ts
GuiUndoTool
撤销 Anki 中的最后一次操作。
工具注解
所有 GUI 工具都必须在 @Tool 装饰器中声明 annotations,包含以下属性:
| 注解 | 类型 | 说明 |
|---|---|---|
title | string | 工具的可读标题 |
readOnlyHint | boolean | 是否为只读操作 |
destructiveHint | boolean | 是否为破坏性操作 |
idempotentHint | boolean | (可选) 操作是否幂等 |
资料来源:CLAUDE.md
安全考虑
用户批准机制
GUI 工具通过 AnkiConnect API 与 Anki 桌面应用交互,用户在 Anki 中的操作(如打开编辑器、选择卡片)需要用户显式批准。这提供了一层保护,防止 AI 代理在用户不知情的情况下修改笔记。
读/写模式
根据 issue #18 的功能请求,用户希望配置可配置的工具有限权限(只读模式)。GUI 工具集中的写操作工具(如 GuiEditNoteTool)在只读模式下应被禁用。
区分复习和工作流
文档明确区分了两种使用场景:
| 场景 | 适用工具 | 工具描述 |
|---|---|---|
| 复习会话 | presentCard, rate_card | 核心复习工具 |
| 笔记编辑/创建 | GUI 工具 | 需要用户界面交互 |
资料来源:manifest.json, issue #18
与核心工具的区别
| 特性 | 核心复习工具 | GUI 工具集 |
|---|---|---|
| 传输方式 | STDIO/Streamable HTTP | 需要 Anki 桌面应用运行 |
| 用户批准 | 自动执行 | 需要用户界面操作 |
| 适用场景 | 自动化复习 | 笔记编辑/创建工作流 |
| 依赖 | AnkiConnect API | AnkiConnect API + GUI 交互 |
错误处理
GUI 工具使用统一的错误响应格式:
return createErrorResponse(error, {
context: "额外错误上下文信息",
hint: "用户可执行的修复建议"
});
错误处理会捕获常见的失败场景,如:
- Anki 未运行
- 指定的卡片/笔记不存在
- 网络连接问题
资料来源:src/mcp/utils/anki.utils.ts
模块导出
src/mcp/primitives/gui/index.ts 统一导出所有 GUI 相关的工具和类型:
// Tools - Browser
export { GuiBrowseTool } from "./tools/gui-browse.tool";
export { GuiSelectCardTool } from "./tools/gui-select-card.tool";
export { GuiSelectedNotesTool } from "./tools/gui-selected-notes.tool";
// Tools - Dialog
export { GuiAddCardsTool } from "./tools/gui-add-cards.tool";
export { GuiEditNoteTool } from "./tools/gui-edit-note.tool";
export { GuiDeckOverviewTool } from "./tools/gui-deck-overview.tool";
export { GuiDeckBrowserTool } from "./tools/gui-deck-browser.tool";
// Tools - Utility
export { GuiCurrentCardTool } from "./tools/gui-current-card.tool";
export { GuiShowQuestionTool } from "./tools/gui-show-question.tool";
export { GuiShowAnswerTool } from "./tools/gui-show-answer.tool";
export { GuiUndoTool } from "./tools/gui-undo.tool";
资料来源:CLAUDE.md
故障排除
本页面汇总了 anki-mcp-server 使用过程中常见的错误场景、根本原因分析以及解决方案。所有错误处理逻辑均基于源代码中的实际实现。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
常见错误与解决方案
1. ERR_REQUIRE_ESM 启动崩溃
错误信息:
Error [ERR_REQUIRE_ESM]: require() of ES Module...
影响版本: v0.8.5 及更早版本 根本原因: ky 是一个纯 ESM 模块,在 Node.js 21.x 环境下无法通过 CommonJS 的 require() 加载。
解决方案:
| 操作 | 说明 |
|---|---|
| 升级 Node.js | 升级到 Node.js 22.x 或 Node.js 20.17+ |
| 检查版本 | 运行 node --version 确认版本 |
项目 package.json 中已明确声明 engines 字段要求 Node.js >= 20.19.0:
"engines": {
"node": ">=20.19.0"
}
资料来源:package.json:38
2. AnkiConnect 超时错误
错误信息:
AnkiConnect request timeout
社区反馈: Issue #22 报告在批量创建卡片时频繁出现超时。 根本原因: AnkiConnect 基于 Qt 单线程事件循环,高并发请求会导致响应延迟。
解决方案:
- 使用批量操作工具 - 优先使用
addNotes而非多次调用addNote:
// 正确方式:批量添加
addNotes({
notes: [
{ deckName: "MyDeck", modelName: "Basic", fields: {...} },
{ deckName: "MyDeck", modelName: "Basic", fields: {...} }
]
})
// 避免:连续多次调用
for (const note of notes) {
addNote(note) // 每次调用都产生网络往返
}
- 检查 Anki 运行状态 - 确保 Anki 应用程序正在运行且 AnkiConnect 插件已启用
- 增加超时配置 - 在 AnkiConnect 设置中调整超时时间
资料来源:CLAUDE.md (Upstream AnkiConnect Quirks 部分)
3. Ngrok 隧道启动失败
错误信息:
failed to start ngrok
社区反馈: Issue #9 报告 ngrok 3.24.0-msix 版本与服务器不兼容。 解决方案: 使用 ngrok v7.0 或更高版本:
npx [email protected]
4. 卡片创建时字段为空被拒绝
社区反馈: Issue #20 报告部分笔记类型的字段是可选的,但工具拒绝空字段。
当前行为: 系统会验证必填字段,阻止创建包含空值的笔记。
建议方案:
- 检查笔记类型定义,确认哪些字段是可选的
- 如需支持空字段,可考虑使用
updateNoteFields事后清空
错误处理架构
错误响应生成流程
graph TD
A[AnkiConnect 请求] --> B{请求成功?}
B -->|是| C[返回原始数据]
B -->|否| D[捕获 AnkiConnectError]
D --> E{错误类型识别}
E -->|模型未找到| F[生成错误提示<br/>使用 modelNames 获取可用模型]
E -->|笔记未找到| G[生成错误提示<br/>检查笔记ID]
E -->|其他错误| H[通用错误信息]
F --> I[createErrorResponse]
G --> I
H --> I
I --> J[返回 CallToolResult<br/>isError: true]createErrorResponse 函数负责将各种错误场景转换为统一的 MCP 错误响应格式:
// src/mcp/utils/anki.utils.ts
export function createErrorResponse(
error: AnkiConnectError,
context?: Record<string, string>
): CallToolResult {
return {
content: [
{
type: "text",
text: formatAnkiError(error, context),
},
],
isError: true,
};
}
资料来源:src/mcp/utils/anki.utils.ts
AnkiConnect 客户端重试机制
graph LR
A[MCP Tool 调用] --> B[ky HTTP Client]
B --> C{请求成功?}
C -->|是| D[返回结果]
C -->|否| E{重试次数 < 3?}
E -->|是| F[等待后重试]
F --> B
E -->|否| G[抛出 AnkiConnectError]客户端实现了自动重试机制,默认最多重试 3 次:
// src/mcp/clients/anki-connect.client.ts
export class AnkiConnectClient {
async invoke<T>(action: string, params?: Record<string, unknown>): Promise<T> {
// ... 重试逻辑实现
}
}
资料来源:src/mcp/clients/anki-connect.client.ts
AnkiConnect 上游特性与限制
以下行为属于 AnkiConnect 本身的特性,anki-mcp-server 无法绕过:
| 行为 | 描述 | 规避方案 |
|---|---|---|
updateNoteFields 在笔记打开时静默失败 | 在 Anki 浏览器中打开笔记后更新不会生效 | 更新前关闭浏览器或切换笔记 |
| Model CSS 是按笔记类型共享的 | 所有使用同一模型的卡片共享 CSS | 使用 modelStyling 获取,updateNoteFields 保留内联样式 |
sync 需要桌面应用登录 | 无法通过 API 路径认证 | 确保 Anki 已登录 AnkiWeb |
deleteNotes 不可逆 | 删除笔记会级联删除所有关联卡片 | 必须设置 confirmDeletion: true |
资料来源:CLAUDE.md
安全验证错误
媒体文件安全
v0.15.1 版本后添加了多层安全验证:
| 验证类型 | 攻击场景 | 处理方式 |
|---|---|---|
| MIME 类型检查 | 上传恶意文件绕过扩展名 | 白名单验证 |
| 路径遍历检查 | 通过 ../ 读取任意文件 | 规范化路径后验证 |
| SSRF 检查 | 通过 URL 参数访问内网资源 | URL 源验证 |
验证失败时会返回安全错误:
// src/mcp/utils/media-validation.utils.ts
export function validateMediaPath(path: string): ValidationResult {
// 路径遍历检测
// MIME 类型白名单验证
// 返回验证结果
}
资料来源:src/mcp/utils/media-validation.utils.ts
调试步骤
1. 环境检查
# 检查 Node.js 版本
node --version
# 检查 anki-mcp-server 版本
npx @ankimcp/anki-mcp-server --version
# 验证 Anki 运行状态
curl http://localhost:8765 -d '{"action": "version", "version": 6}'
2. 启用调试日志
项目使用 Pino 进行日志记录:
# 启动服务并查看详细日志
DEBUG=pino:* npx @ankimcp/anki-mcp-server --stdio
3. MCP Inspector 调试
使用 MCP Inspector 检查工具调用:
npx @modelcontextprotocol/inspector \
npx @ankimcp/anki-mcp-server --stdio
版本历史问题
| 版本 | 问题 | 状态 |
|---|---|---|
| v0.8.5 | ERR_REQUIRE_ESM 在 Node 21.x | 已修复 - Node >= 20.19.0 |
| v0.15.1 | 媒体路径遍历漏洞 | 已修复 - 安全验证 |
| v0.14.0 | 批量添加工具缺失 | 已修复 - addNotes 工具 |
| v0.18.5-tunnel.2 | ngrok 兼容性问题 | 持续改进 |
资料来源:CHANGELOG.md
获取帮助
如果以上方案无法解决您的问题:
- 检查 GitHub Issues - 搜索类似问题的解决方案
- 提交新 Issue - 请包含以下信息:
- 操作系统和 Node.js 版本
- anki-mcp-server 版本
- Anki 版本和 AnkiConnect 插件版本
- 完整的错误日志
- 重现步骤
资料来源:package.json:38
命令行参考
Anki MCP Server 提供多种命令行启动方式,支持 STDIO 传输和 HTTP 隧道两种主要模式。本页面详细说明所有可用的命令行选项、配置参数和使用场景。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Anki MCP Server 是一个基于 Model Context Protocol (MCP) 的 Anki 闪卡应用程序服务器。它通过 AnkiConnect 插件与 Anki 通信,使 AI 助手能够与用户的 Anki 集合进行交互。
支持的运行模式:
| 模式 | 传输协议 | 典型用途 |
|---|---|---|
| STDIO | 标准输入输出 | Claude Desktop、VS Code 等 MCP 客户端 |
| HTTP + Ngrok | Streamable HTTP + 隧道 | 远程访问、跨网络连接 |
安装与前置条件
前置要求
- Node.js:
>=20.19.0(Node 21.x 不支持,详见 Issue #17) - Anki: 已安装并运行
- AnkiConnect 插件: 在 Anki 中安装(插件 ID:
2055492159)
安装方式
# 全局安装
npm install -g @ankimcp/anki-mcp-server
# 或使用 npx 直接运行
npx -y @ankimcp/anki-mcp-server --stdio
STDIO 模式
STDIO 模式是 MCP 客户端(如 Claude Desktop)的标准连接方式。
基本语法
anki-mcp --stdio
选项参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--stdio | boolean | false | 启用 STDIO 传输模式 |
--anki-port | number | 8765 | AnkiConnect HTTP 服务端口 |
--anki-host | string | "127.0.0.1" | AnkiConnect 主机地址 |
--read-only | boolean | false | 启用只读模式,禁用所有写操作 |
--timeout | number | 30 | 请求超时时间(秒) |
--log-level | string | "info" | 日志级别 (trace, debug, info, warn, error) |
--verbose | boolean | false | 启用详细日志输出 |
完整示例
# 标准启动
anki-mcp --stdio
# 自定义 AnkiConnect 连接
anki-mcp --stdio --anki-port 8765 --anki-host 127.0.0.1
# 只读模式(社区功能请求 - Issue #18)
anki-mcp --stdio --read-only
# 启用调试日志
anki-mcp --stdio --log-level debug --verbose
Claude Desktop 配置
在 claude_desktop_config.json 中配置:
{
"mcpServers": {
"anki": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
"env": {}
}
}
}
HTTP 隧道模式
HTTP 隧道模式通过 Ngrok 创建公共 URL,支持远程访问 Anki MCP Server。
基本语法
anki-mcp --tunnel
隧道模式选项
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--tunnel | boolean | false | 启用 Ngrok 隧道模式 |
--tunnel-port | number | 8080 | 本地 HTTP 服务端口 |
--auth-token | string | - | Ngrok 认证令牌 |
--domain | string | - | Ngrok 自定义域名 |
Ngrok 认证配置
首次使用隧道模式需要配置 Ngrok 认证:
- 注册 Ngrok 账号
- 获取 Authtoken
- 配置 Ngrok:
ngrok config add-authtoken <YOUR_AUTH_TOKEN>
完整示例
# 基本隧道启动
anki-mcp --tunnel
# 自定义本地端口
anki-mcp --tunnel --tunnel-port 9090
# 使用 Ngrok authtoken
anki-mcp --tunnel --auth-token <YOUR_TOKEN>
注意: Ngrok 模式曾报告启动问题(见 Issue #9),如遇 "failed to start ngrok" 错误,建议检查 Ngrok 版本。
只读模式
只读模式是社区请求的功能(Issue #18),用于限制 MCP 服务器的操作权限,防止意外修改 Anki 集合。
启用方式
anki-mcp --stdio --read-only
只读模式的限制
启用后,以下操作将被阻止:
| 操作类型 | 受影响工具 |
|---|---|
| 创建笔记 | addNote, addNotes |
| 更新笔记 | updateNoteFields |
| 删除笔记 | deleteNotes |
| 管理牌组 | createDeck, deleteDeck |
| 管理模板 | createModel, updateModelStyling |
| 复习操作 | rateCard |
| 媒体操作 | storeMediaFile |
允许的操作(只读)
get_due_cards- 获取到期卡片get_cards- 获取卡片信息present_card- 展示卡片modelNames- 列出模板modelFieldNames- 获取字段名getTags- 获取标签列表guiBrowse- 浏览窗口- 统计工具
环境变量
除命令行参数外,部分配置也可通过环境变量设置:
| 变量名 | 说明 | 对应参数 |
|---|---|---|
ANKI_PORT | AnkiConnect 端口 | --anki-port |
ANKI_HOST | AnkiConnect 主机 | --anki-host |
ANKI_TIMEOUT | 请求超时(秒) | --timeout |
LOG_LEVEL | 日志级别 | --log-level |
READ_ONLY | 只读模式 | --read-only |
连接架构
graph TD
subgraph "MCP 客户端"
A[Claude Desktop / VS Code]
end
subgraph "Anki MCP Server"
B[CLI 解析层]
C[传输层]
D[业务逻辑层]
end
subgraph "AnkiConnect"
E[HTTP 服务]
F[Anki 应用]
end
A -->|MCP 协议| B
B --> C
C -->|STDIO 或 HTTP| D
D -->|JSON-RPC| E
E -->|API| F
style A fill:#e1f5fe
style F fill:#c8e6c9
style D fill:#fff3e0常见问题与故障排除
ERR_REQUIRE_ESM 错误
问题: Node.js 21.x 用户遇到启动崩溃(Issue #12、Issue #16)
原因: 项目依赖的 ky 库是纯 ESM 模块,Node.js 21.x 不支持此加载方式
解决方案:
- 降级至 Node.js 20.19+ 或升级至 Node.js 22+
- 或使用 npx 自动获取兼容版本
AnkiConnect 超时
问题: 创建卡片时频繁超时(Issue #22)
原因: AnkiConnect 基于 Qt 单线程事件循环,高并发请求会导致排队
解决方案:
- 使用
addNotes批量创建而非多次addNote调用 - 增加
--timeout参数值 - 检查 Anki 是否正在执行同步操作
Ngrok 启动失败
问题: Ngrok 隧道模式启动失败(Issue #9)
解决方案:
- 验证 Ngrok authtoken 配置正确
- 检查 Ngrok 版本(建议 3.x)
- 确认防火墙允许 Ngrok 出站连接
退出码
| 退出码 | 含义 |
|---|---|
| 0 | 正常退出 |
| 1 | 一般错误(参数错误、连接失败等) |
| 2 | AnkiConnect 连接失败 |
| 130 | 收到 SIGINT (Ctrl+C) 中断信号 |
相关文档
来源:https://github.com/ankimcp/anki-mcp-server / 项目说明书
安全特性
Anki MCP Server 实现了多层次的安全机制,用于保护用户数据免受恶意请求、路径遍历攻击、服务器端请求伪造(SSRF)等安全威胁。本页面详细说明服务器的安全架构、实现机制和配置选项。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
架构概览
Anki MCP Server 的安全体系由以下几个核心模块组成:
graph TD
A[客户端请求] --> B{传输层}
B --> C[STDIO 传输]
B --> D[HTTP 传输]
D --> E[来源验证守卫]
E --> F[API Key 认证]
D --> G[媒体文件操作]
G --> H[媒体安全验证]
H --> I{MIME 类型检查}
H --> J{路径规范化检查}
H --> K{URL 参数验证}
I --> L[允许列表验证]
J --> M[禁止遍历序列]
K --> N[DNS 重绑定防护]
style A fill:#e1f5fe
style L fill:#c8e6c9
style M fill:#c8e6c9
style N fill:#c8e6c9媒体安全验证
概述
v0.15.1 版本引入了媒体操作的安全加固,针对 storeMediaFile 工具的路径遍历漏洞和 URL 参数的 SSRF 风险实施了多层验证。该安全更新由安全研究员 Hideaki Takahashi 报告。
资料来源:SECURITY.md
MIME 类型白名单
媒体文件上传必须通过 MIME 类型白名单验证。服务器仅允许预定义的安全文件类型:
| 类型分类 | 允许的 MIME 类型 |
|---|---|
| 图片 | image/jpeg, image/png, image/gif, image/webp, image/svg+xml |
| 音频 | audio/mpeg, audio/wav, audio/ogg, audio/flac |
| 视频 | video/mp4, video/webm, video/ogg |
| 文档 | application/pdf |
// src/mcp/utils/media-validation.utils.ts
export const ALLOWED_MIME_TYPES = [
// Images
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/svg+xml",
// Audio
"audio/mpeg",
"audio/wav",
"audio/ogg",
"audio/flac",
// Video
"video/mp4",
"video/webm",
"video/ogg",
// Documents
"application/pdf",
] as const;
资料来源:src/mcp/utils/media-validation.utils.ts
路径遍历防护
storeMediaFile 工具实现了路径规范化检查,防止攻击者通过 ../ 等序列访问用户目录之外的敏感文件:
export function isPathSafe(filePath: string): boolean {
const normalized = path.normalize(filePath);
return (
!normalized.includes("..") &&
path.isAbsolute(filePath) === false &&
!normalized.startsWith("/")
);
}
关键检查规则:
- 禁止路径中包含
..遍历序列 - 不允许绝对路径
- 禁止以
/开头的路径
资料来源:src/mcp/utils/media-validation.utils.ts
SSRF 防护
对于通过 URL 参数传入的媒体文件,服务器实施以下防护措施:
- URL 协议验证:仅允许
http://和https://协议 - DNS 重绑定保护:验证
Host头部,防止私有 IP 地址范围访问 - 重定向限制:验证最终请求目标,防止重定向到内部网络
// src/mcp/utils/media-validation.utils.ts
export function validateUrlSafety(url: string): boolean {
try {
const parsed = new URL(url);
if (!["http:", "https:"].includes(parsed.protocol)) {
return false;
}
// Additional DNS rebinding protection
return !isPrivateIpRange(parsed.hostname);
} catch {
return false;
}
}
资料来源:src/mcp/utils/media-validation.utils.ts
HTTP 传输安全
来源验证守卫
HTTP 传输模式使用来源验证守卫(Origin Validation Guard)防止跨站请求攻击:
// src/http/guards/origin-validation.guard.ts
@Injectable()
export class OriginValidationGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const origin = request.headers.origin;
// Validate against allowed origins
const allowedOrigins = this.configService.getAllowedOrigins();
if (!allowedOrigins.includes(origin)) {
throw new ForbiddenException('Invalid origin');
}
return true;
}
}
资料来源:src/http/guards/origin-validation.guard.ts
API Key 认证
HTTP 端点使用 JWT API Key 进行身份验证:
// src/http/strategies/api-key.strategy.ts
@Injectable()
export class ApiKeyStrategy extends PassportStrategy(Strategy, 'api-key') {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
secretOrKey: configService.getApiKeySecret(),
});
}
}
资料来源:src/http/strategies/api-key.strategy.ts
只读模式
用户可以通过命令行参数启用只读模式,防止 MCP 服务器执行任何写入操作:
npx @ankimcp/anki-mcp-server --read-only --stdio
资料来源:src/config/anki-config.interface.ts
配置接口
export interface IAnkiConfig {
readonly?: boolean;
ankiHost?: string;
ankiPort?: number;
// ... other config options
}
只读模式会阻止以下操作:
- 创建笔记 (
addNote,addNotes) - 更新笔记字段 (
updateNoteFields) - 删除笔记 (
deleteNotes) - 存储媒体文件 (
storeMediaFile) - 创建卡牌模板 (
createModel)
资料来源:src/mcp/clients/anki-connect.client.ts
工具注解与权限控制
每个 MCP 工具都通过注解(annotations)声明其安全属性:
| 注解类型 | 说明 | 示例 |
|---|---|---|
readOnlyHint | 标识只读操作 | true 或 false |
destructiveHint | 标识破坏性操作 | true 或 false |
idempotentHint | 标识幂等操作 | true 或 false |
// 示例:storeMediaFile 工具注解
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
},
资料来源:src/mcp/clients/anki-connect.client.ts
安全报告
Anki MCP Server 遵循负责任的安全披露原则。如需报告安全漏洞,请参考 SECURITY.md。
已知限制
| 问题编号 | 描述 | 当前状态 |
|---|---|---|
| #18 | 缺少细粒度工具权限配置(per-tool control) | 功能请求中 |
| #22 | 批量创建卡片时可能触发 AnkiConnect 超时 | 建议使用 addNotes 批量工具 |
资料来源:SECURITY.md
开发指南
本指南面向希望为 anki-mcp-server 项目贡献代码、扩展功能或深入了解项目架构的开发者。anki-mcp-server 是一个基于 Model Context Protocol (MCP) 的 Anki 间隔重复记忆应用服务器,使 AI 助手能够与 Anki 进行自然语言交互。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
环境要求与项目设置
Node.js 版本要求
项目明确要求 Node.js >= 20.19.0 且 < 21.0.0。Node 21.x 由于缺少 require(esm) 特性支持会导致 ERR_REQUIRE_ESM 错误。
"engines": {
"node": ">=20.19.0 <21.0.0"
}
资料来源:package.json:7-9
核心依赖
| 依赖包 | 版本 | 用途 |
|---|---|---|
@modelcontextprotocol/sdk | 精确版本 1.29.0 | MCP 协议实现 |
zod | ^4.3.6 | 数据验证(注意:使用 v4 而非 v3) |
ky | ^1.14.3 | HTTP 客户端(ESM 模块) |
@rekog/mcp-nest | - | MCP-Nest 框架 |
@nestjs/common | - | NestJS 核心 |
资料来源:package.json:31-62
开发脚本
# 构建项目
npm run build
# STDIO 模式开发(热重载)
npm run start:dev:stdio
# HTTP 模式开发(热重载)
npm run start:dev:http
# 生产环境启动
npm run start:prod:stdio
npm run start:prod:http
# 代码检查与格式化
npm run lint
npm run format
# 测试
npm run test
资料来源:package.json:17-28
项目架构概览
anki-mcp-server/
├── src/
│ ├── main-stdio.ts # STDIO 传输入口
│ ├── main-http.ts # HTTP 传输入口
│ ├── main-tunnel.ts # Ngrok 隧道入口
│ ├── app.module.ts # 根模块
│ ├── config/ # 配置相关
│ │ ├── anki-config.interface.ts # 配置接口
│ │ └── anki-config.loader.ts # 配置加载器
│ ├── services/ # 服务层
│ │ └── ngrok.service.ts # Ngrok 隧道管理
│ └── mcp/ # MCP 核心模块
│ ├── clients/ # AnkiConnect HTTP 客户端
│ ├── config/ # MCP 配置
│ ├── types/ # 共享类型定义
│ ├── utils/ # 工具函数
│ └── primitives/ # 工具、提示词、资源
│ ├── essential/ # 核心工具
│ └── gui/ # GUI 交互工具
├── test/ # 测试文件
├── scripts/ # 构建脚本
└── bin/ # CLI 入口
资料来源:CLAUDE.md:1-22
传输模式架构
项目支持三种传输模式:
graph TD
A[客户端] --> B{传输模式}
B -->|stdio| C[main-stdio.ts]
B -->|http| D[main-http.ts]
B -->|tunnel| E[main-tunnel.ts + Ngrok]
C --> F[McpModule STDIO]
D --> G[McpModule STREAMABLE_HTTP]
E --> G
E --> H[NgrokService]
F --> I[AnkiConnect Client]
G --> I
H --> J[AnkiConnect]
I --> K[Anki 桌面应用]
J --> K模块系统
应用模块结构
AppModule.forStdio() / forHttp()
→ McpModule.forRoot() // STDIO 或 STREAMABLE_HTTP 传输
→ McpPrimitivesAnkiEssentialModule.forRoot() // 核心工具
→ McpPrimitivesAnkiGuiModule.forRoot() // GUI 工具
资料来源:CLAUDE.md:24-31
模块发现机制
所有工具、提示词和资源通过 @rekog/mcp-nest 自动发现。MCP-Nest 1.9.0+ 要求工具同时在 AppModule.providers 中声明(参见 ESSENTIAL_MCP_TOOLS 和 GUI_MCP_TOOLS 数组)。
工具开发规范
标准工具结构
每个 MCP 工具应遵循以下模式:
// 1. Zod schema 用于输入验证
// 2. @Injectable() 类注入 AnkiConnectClient
// 3. @Tool 装饰器定义元数据
// 4. execute() 方法调用 AnkiConnectClient.invoke()
// 5. 成功返回匹配 outputSchema 的原始对象
// 6. 错误返回 createErrorResponse()
资料来源:CLAUDE.md:67-75
最小工具示例
import { Injectable, Logger } from "@nestjs/common";
import { Tool } from "@rekog/mcp-nest";
import { z } from "zod";
import { AnkiConnectClient } from "@/mcp/clients/anki-connect.client";
import { createErrorResponse } from "@/mcp/utils/anki.utils";
@Injectable()
export class SyncTool {
private readonly logger = new Logger(SyncTool.name);
constructor(private readonly ankiClient: AnkiConnectClient) {}
@Tool({
name: "sync",
description: "Sync with AnkiWeb to get latest data and push changes",
parameters: z.object({}),
outputSchema: z.object({
success: z.boolean(),
message: z.string(),
}),
annotations: {
title: "Sync Collection",
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
},
})
async execute() {
try {
const result = await this.ankiClient.invoke("sync", {});
return result;
} catch (error) {
return createErrorResponse(error, {
hint: "Make sure Anki is running and logged into AnkiWeb.",
});
}
}
}
资料来源:src/mcp/primitives/essential/tools/sync.tool.ts
@Tool 装饰器参数
| 参数 | 类型 | 说明 |
|---|---|---|
name | string | 工具唯一名称 |
description | string | 工具描述,用于 AI 理解功能 |
parameters | ZodSchema | 输入参数验证 schema |
outputSchema | ZodSchema | 输出数据结构 schema |
annotations | object | 包含 readOnlyHint、destructiveHint、idempotentHint |
outputSchema 与错误处理
- 成功路径:返回匹配
outputSchema的原始对象,MCP-Nest handler 自动验证并包装 - 错误路径:使用
createErrorResponse()返回,跳过 outputSchema 验证
// 错误响应示例
return createErrorResponse(error, {
modelName,
hint: "Model not found. Use modelNames tool to see available models.",
});
资料来源:CLAUDE.md:53-62
GUI 工具特殊要求
GUI 工具位于 src/mcp/primitives/gui/,必须包含双重警告:
- "IMPORTANT: Only use when user explicitly requests..."
- "This tool is for note editing/creation workflows, NOT for review sessions"
数据类型定义
核心类型
// 添加笔记参数
interface AddNoteParams {
deckName: string; // 目标牌组
modelName: string; // 笔记类型
fields: Record<string, string>; // 字段键值对
tags?: string[]; // 可选标签
options?: NoteOptions; // 重复检查选项
}
// 笔记类型/模型信息
interface Model {
name: string; // 模型名称
id: number; // 唯一标识符
css: string; // CSS 样式
fields: string[]; // 字段名称数组
}
// 笔记详细信息
interface NoteInfo {
noteId: number;
modelName: string;
tags: string[];
fields: Record<string, { value: string; order: number }>;
cards: number[];
mod: number;
}
资料来源:src/mcp/types/anki.types.ts
工具函数
错误响应创建
import { createErrorResponse } from "@/mcp/utils/anki.utils";
// 返回 CallToolResult,isError: true,绕过 outputSchema 验证
return createErrorResponse(error, {
field: "value",
hint: "Helpful error message",
});
资料来源:src/mcp/utils/anki.utils.ts:1-10
HTML 清理
import { cleanHtml } from "@/mcp/utils/anki.utils";
// 移除 HTML 标签并解码 HTML 实体
const cleanText = cleanHtml(html);
Markdown 解析
import { parseMarkdownSections } from "@/mcp/utils/markdown.utils";
// 按 H1 标题分割 Markdown 内容
const sections = parseMarkdownSections(markdown);
// 返回: { "Description": "...", "Content": "..." }
路径别名配置
项目配置了 TypeScript 路径别名:
| 别名 | 指向 |
|---|---|
@/* | src/* |
@test/* | test/* |
// 示例导入
import { AnkiConnectClient } from "@/mcp/clients/anki-connect.client";
import { Something } from "@test/helpers";
资料来源:CLAUDE.md:88-91
测试策略
项目采用三层测试策略,根据变更类型选择合适的测试级别:
graph LR
A[变更类型] --> B{选择测试}
B -->|工具逻辑| C[单元测试]
B -->|集成流程| D[集成测试]
B -->|端到端| E[E2E 测试]测试目录结构
test/
├── unit/ # 单元测试
├── integration/ # 集成测试
└── e2e/ # 端到端测试
常见陷阱与限制
AnkiConnect 上游行为
项目中标注了以下上游行为,开发时需要注意:
| 行为 | 说明 | 处理方式 |
|---|---|---|
updateNoteFields 静默失败 | 目标笔记在浏览窗口打开时不保存 | 在工具描述中警告用户 |
| Model CSS 是按笔记类型而非按笔记 | 获取 CSS 用 modelStyling,判断模型用 notesInfo | 更新笔记时保留内联样式 |
sync 依赖桌面应用登录状态 | 无 API 路径进行身份验证 | 返回提示性错误信息 |
deleteNotes 不可逆且级联删除 | 所有相关卡片一并删除 | 要求 confirmDeletion: true |
资料来源:CLAUDE.md:40-50
Zod 版本注意
项目使用 Zod v4(zod@^4.x),不是 v3。Zod 4 有不同的 API:
- 使用
z.interface()而非z.object()(在某些场景) - 错误处理方式不同
// Zod v4 正确用法
const schema = z.object({
name: z.string(),
age: z.number(),
});
Claude Desktop 集成
安装配置
在 Claude Desktop 配置文件 (~/.config/Claude/claude_desktop_config.json) 中添加:
{
"mcpServers": {
"anki-mcp-server": {
"command": "npx",
"args": ["-y", "@ankimcp/anki-mcp-server"],
"env": {
"ANKI_HOST": "127.0.0.1",
"ANKI_PORT": "8765"
}
}
}
}
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
ANKI_HOST | 127.0.0.1 | AnkiConnect 主机地址 |
ANKI_PORT | 8765 | AnkiConnect 端口 |
ANKI_READ_ONLY | false | 只读模式标志 |
社区已知问题
开发时需注意以下社区反馈的问题:
| Issue | 状态 | 说明 |
|---|---|---|
| #22: 创建卡片超时 | 已知 | AnkiConnect 单线程 Qt 事件循环在高并发下超时 |
| #21: 批量创建性能 | 功能请求 | addNotes 批量工具已提供,建议优先使用 |
| #20: 空字段处理 | 已知 | 当前对空字段有检查,可选字段场景受限 |
| #16: ERR_REQUIRE_ESM | 已修复 | Node 21.x 不支持,已排除在 engines 范围外 |
资料来源:community_context
代码规范
ESLint 配置
项目使用 ESLint 进行代码检查,配置文件位于 eslint.config.mjs。
Prettier 格式化
# 格式化所有源码
npm run format
TypeScript 配置
strict: true- 严格类型检查module: "nodenext"- Node.js ESM 支持target: "ES2023"- 编译目标
发布流程
项目使用语义化版本控制,当前处于 0.x.x beta 阶段,允许破坏性变更。
发布通过 GitHub Actions 自动完成,发布说明由合并的 PR 自动生成。
# 构建并发布
npm run build
npm publish
资料来源:CHANGELOG.md
扩展开发清单
新增工具时的检查清单:
- [ ] 创建
*.tool.ts文件于src/mcp/primitives/essential/或src/mcp/primitives/gui/ - [ ] 定义 Zod 输入/输出 schema
- [ ] 使用
@Injectable()和@Tool()装饰器 - [ ] 在
ESSENTIAL_MCP_TOOLS或GUI_MCP_TOOLS数组中注册 - [ ] 添加单元测试
- [ ] 更新
manifest.json工具列表(如需要) - [ ] 更新本开发指南文档
资料来源:package.json:7-9
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
可能增加新用户试用和生产接入成本。
可能阻塞安装或首次运行。
可能增加新用户试用和生产接入成本。
可能增加新用户试用和生产接入成本。
Pitfall Log / 踩坑日志
项目:ankimcp/anki-mcp-server
摘要:发现 14 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:安装坑 - 来源证据:Anki mcp for mcp clients fails with 'ERR_REQUIRE_ESM'。
1. 安装坑 · 来源证据:Anki mcp for mcp clients fails with 'ERR_REQUIRE_ESM'
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:Anki mcp for mcp clients fails with 'ERR_REQUIRE_ESM'
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_7f29acec242e48c9b9a28ff2585734d3 | https://github.com/ankimcp/anki-mcp-server/issues/12 | 来源讨论提到 node 相关条件,需在安装/试用前复核。
2. 安装坑 · 来源证据:Crash on startup: ERR_REQUIRE_ESM when requiring 'ky'
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:Crash on startup: ERR_REQUIRE_ESM when requiring 'ky'
- 对用户的影响:可能阻塞安装或首次运行。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_66dcd7ebe4a842228360620571d902f5 | https://github.com/ankimcp/anki-mcp-server/issues/16 | 来源讨论提到 node 相关条件,需在安装/试用前复核。
3. 安装坑 · 来源证据:Feature Request: Deck and Collection Level Statistics
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:Feature Request: Deck and Collection Level Statistics
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_e49f272712124771bc55365bbe3ad1cc | https://github.com/ankimcp/anki-mcp-server/issues/14 | 来源讨论提到 npm 相关条件,需在安装/试用前复核。
4. 安装坑 · 来源证据:Not possible to install Anki MCP 0.8.4 to latest Claude Desktop Claude 1.0.1307 (1ed883)
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:Not possible to install Anki MCP 0.8.4 to latest Claude Desktop Claude 1.0.1307 (1ed883)
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_82a572d18c7b4eac9facb5ff06652bc5 | https://github.com/ankimcp/anki-mcp-server/issues/10 | 来源类型 github_issue 暴露的待验证使用条件。
5. 安装坑 · 来源证据:ngrok fails to start with latest version
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:ngrok fails to start with latest version
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_bd4f26c6db6b4db8adc1436a457ff69a | https://github.com/ankimcp/anki-mcp-server/issues/9 | 来源讨论提到 npm 相关条件,需在安装/试用前复核。
6. 配置坑 · 来源证据:Explicitly exclude Node 21.x in engines field
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个配置相关的待验证问题:Explicitly exclude Node 21.x in engines field
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_0a42c043dbad4d93a4731734d006fa08 | https://github.com/ankimcp/anki-mcp-server/issues/17 | 来源讨论提到 node 相关条件,需在安装/试用前复核。
7. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 建议检查:将假设转成下游验证清单。
- 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
- 证据:capability.assumptions | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | README/documentation is current enough for a first validation pass.
8. 运行坑 · 来源证据:getting lots of timeouts creating cards
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个运行相关的待验证问题:getting lots of timeouts creating cards
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_92310c7e5cea46d48a7643da19e2d5a6 | https://github.com/ankimcp/anki-mcp-server/issues/22 | 来源类型 github_issue 暴露的待验证使用条件。
9. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
- 证据:evidence.maintainer_signals | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | last_activity_observed missing
10. 安全/权限坑 · 下游验证发现风险项
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:下游已经要求复核,不能在页面中弱化。
- 建议检查:进入安全/权限治理复核队列。
- 防护动作:下游风险存在时必须保持 review/recommendation 降级。
- 证据:downstream_validation.risk_items | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | no_demo; severity=medium
11. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 建议检查:把风险写入边界卡,并确认是否需要人工复核。
- 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
- 证据:risks.scoring_risks | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | no_demo; severity=medium
12. 安全/权限坑 · 来源证据:Feature: Configurable tool permissions (read-only mode)
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题:Feature: Configurable tool permissions (read-only mode)
- 对用户的影响:可能阻塞安装或首次运行。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_65de282533484baf95eaa214c669167b | https://github.com/ankimcp/anki-mcp-server/issues/18 | 来源类型 github_issue 暴露的待验证使用条件。
13. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
- 防护动作:issue/PR 响应未知时,必须提示维护风险。
- 证据:evidence.maintainer_signals | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | issue_or_pr_quality=unknown
14. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
- 证据:evidence.maintainer_signals | mcp_registry:ai.ankimcp/anki-mcp-server:0.18.5 | https://registry.modelcontextprotocol.io/v0.1/servers/ai.ankimcp%2Fanki-mcp-server/versions/0.18.5 | release_recency=unknown
来源:Doramagic 发现、验证与编译记录