CLAUDE.md · ankimcp/anki-mcp-server
CLAUDE.md from ankimcp/anki-mcp-server: 319 lines covering Project Overview, Quick Reference, Architecture, Adding New Tools and more.
Template by ankimcp
Chalk Labs
Need help deploying CLAUDE.md · ankimcp/anki-mcp-server?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/ankimcp/anki-mcp-server/main/CLAUDE.md -o CLAUDE.md
Read it and adapt it to your project before use.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: documentation
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
A CLAUDE.md file: project memory Claude Code loads at the start of every session.
- For
- Claude Code
- Length
- 319 lines
- File
CLAUDE.md
Sections
- Project Overview
- Quick Reference
- Architecture
- Adding New Tools
- Testing
- Git Hooks (Husky)
- Release Process
- MCPB Bundle Notes
- Environment
First lines (shown under the repo's MIT license)
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
MCP server enabling AI assistants to interact with Anki via AnkiConnect. Built with NestJS and `@rekog/mcp-nest`.
- **Package**: `@ankimcp/anki-mcp-server` (npm)
- **License**: MIT
- **Status**: Beta (0.x.x) - breaking changes allowed
- **User-facing docs**: `README.md` covers installation, client setup (Claude Desktop/MCPB, HTTP, tunnel), and the full tool catalog — consult it for anything user-facing rather than reconstructing it here.
## Quick Reference
```bash
# Build & Run
npm run build # Build → dist/ (all three entry points)
npm run start:dev:stdio # STDIO mode with watch
npm run start:dev:http # HTTP mode with watch
npm run start:dev:tunnel # Tunnel mode with watch + debug
# Testing
npm test # All tests
npm test -- path/to/file.spec.ts # Single test file
npm run test:tools # Tool unit tests only
npm run test:workflows # Multi-tool workflow scenarios (mocked)
npm run test:cov # With coverage (70% threshold)
npm run e2e:full:local # One-shot E2E: up → test → down
npm run smoke:pack # Pack + install from tarball (no lockfile), verify initialize/tools-list
# Quality
npm run lint && npm run type-check # Pre-push checks (also runs via Husky)
# Debugging
npm run inspector:stdio # MCP Inspector UI for testing tools
npm run inspector:stdio:debug # With debugger on port 9229
npm run inspector:http # Inspector against HTTP transport
```
## Architecture
### Entry Points
Three entry points compiled in a single build:
| Mode | Entry | Use Case | Logging |
|------|-------|----------|---------|
| STDIO | `dist/main-stdio.js` | Claude Desktop, MCP clients | stderr |
| HTTP | `dist/main-http.js` | Web-based AI (ChatGPT, claude.ai) | stdout |
| Tunnel | `dist/main-tunnel.js` | Remote access via WebSocket tunnel | stderr |
### Core Files
```
src/
├── main-stdio.ts # STDIO bootstrap: NestFactory.createMicroservice() with the MCP strategy
├── main-http.ts # HTTP bootstrap: NestFactory.create() + guards + connectMicroservice()
├── main-tunnel.ts # Tunnel bootstrap: auth commands + WebSocket tunnel
├── app.module.ts # Root module with forStdio()/forHttp()/forTunnel() factories
├── bootstrap.ts # Shared logger setup (pino → NestJS LoggerService) + createMcpStrategy()
├── version.ts # Package version constant (read from package.json at build)
├── cli/ # CLI layer (Commander parsing + user-facing output)
│ ├── args.ts # Commander entrypoint: option parsing, subcommand dispatch
│ ├── cli-output.ts # User-facing print helpers (cli.success/error/info/box) — separate from args.ts so non-CLI code can use it without pulling in Commander
│ ├── spinner.ts # Terminal spinner for long-running CLI operations
│ └── index.ts # Barrel re-export for the cli module
├── app-config.service.ts # IAnkiConfig implementation (reads from validated AppConfig)
├── config/ # Zod-validated config system (schema, factory, APP_CONFIG token)
├── services/ngrok.service.ts # Optional ngrok subprocess for HTTP-mode public tunneling
├── http/guards/ # HTTP-only guards: origin-validation + host-validation (DNS-rebinding protection)
├── http/mcp-http.factory.ts # StreamableHttpTransport + the Nest controller that owns the MCP route
├── tunnel/ # Tunnel mode: WebSocket client, OAuth device flow, credentials
│ ├── tunnel.client.ts # WebSocket client for tunnel server
│ ├── tunnel.transport.ts # McpTransport for tunnel mode: owns the bound MCP server + handleRequest()
│ ├── in-memory.transport.ts # SDK Transport that pairs a relayed request with the server's response
│ ├── tunnel.protocol.ts # WS message type definitions (request/response/ping/error)
│ ├── device-flow.service.ts # OAuth device flow authentication
│ ├── credentials.service.ts # Persistent credential storage
│ └── commands/ # CLI command handlers (login, logout, tunnel)
└── mcp/
├── clients/anki-connect.client.ts # HTTP client using ky (retries, error handling, read-only guard)
├── config/anki-config.interface.ts # ANKI_CONFIG injection token + IAnkiConfig interface
├── types/anki.types.ts # Shared Anki types (cards, notes, ratings)
├── utils/ # Shared utilities (anki.utils, date.utils, markdown.utils, stats.utils, media-validation.utils, card-states.utils, deck-hierarchy.utils, card-validation.utils, card-suspension.utils)
├── primitives/essential/ # Core tools, prompts, resources
└── primitives/gui/ # GUI-specific tools (require user approval)
```
### Module System
```
AppModule.forStdio()/forHttp()/forTunnel()
→ MCP_STRATEGY provider # the McpStrategy the entry point built
→ McpPrimitivesAnkiEssentialModule.forRoot()
→ McpPrimitivesAnkiGuiModule.forRoot()
```
Each entry point builds exactly one `McpStrategy` (`new McpStrategy({ name, version, icons, instructions, transports })` — see `createMcpStrategy()` in `bootstrap.ts`) and hands the same instance to both `AppModule.forX()` (as `MCP_STRATEGY`) and the NestJS microservice connection: `createMicroservice()` for STDIO/tunnel, `connectMicroservice()` + `startAllMicroservices()` for HTTP. Transports are instances, not enum values — `new StdioTransport()`, `new StreamableHttpTransport()`, `new TunnelTransport()`.
All tools/prompts/resources are `@McpController()` classes listed in each primitive module's `controllers` array (see `ESSENTIAL_MCP_TOOLS` and `GUI_MCP_TOOLS`). NestJS scans every module's controllers for the `@MessagePattern` handlers that `@Tool`/`@Prompt`/`@Resource` compile to, so `AppModule` does not re-list them.
**HTTP mode** does not let the transport self-mount its route. `createMcpHttpServer()` (`src/http/mcp-http.factory.ts`) builds the MCP endpoint as a controller via the `McpHttpControllerFor(transport)` mixin, so requests run through the Nest pipeline and the Origin/Host `APP_GUARD`s apply. A self-mounted route would register straight on the HTTP adapter and bypass them.
**Tunnel mode** uses `TunnelTransport` (`src/tunnel/tunnel.transport.ts`): it gets a fully-wired server from `ctx.createBoundServer(session)`, connects it to an `InMemoryTransport` channel, and exposes `handleRequest(body)`. `TunnelClient` relays request bodies from the tunnel server over WebSocket into that method. Bootstrap lives in `src/tunnel/commands/tunnel.command.ts`. The bound server is created with `era: "legacy"`, so tunnel mode serves only the 2025 protocol revision — STDIO and HTTP also negotiate the 2026-07-28 revision.
### Key Patterns
**Tool response format**: Success paths return raw objects matching the tool's `outputSchema`. The mcp-nest handler validates and wraps them automatically. Error paths use `createErrorResponse(error, context)` from `anki.utils.ts` which returns `CallToolResult` with `isError: true` and bypasses outputSchema validation. Two distinct `success: false` shapes exist: with `error` + `isError: true` (from `createErrorResponse`) the call failed; without them (see `add-notes.tool.ts`, `suspend`/`unsuspend`) the call ran and the payload describes a partial outcome — routing those through `createErrorResponse` would drop `structuredContent`.
**Action helper pattern**: The former aggregate tools (`deckActions`, `tagActions`, `mediaActions`) were split into single-purpose tools (`list-decks.tool.ts`, `deck-stats.tool.ts`, `create-deck.tool.ts`, `change-deck.tool.ts`, `store-media-file.tool.ts`, `replace-tags.tool.ts`, …). Their directories now hold only `actions/*.action.ts` — pure functions taking `(params, ankiClient)` that the split tools import. The tool class stays thin: log, call the helper, wrap failures in `createErrorResponse`.
**Read-only mode**: `AnkiConnectClient` enforces read-only mode by checking actions against a `WRITE_ACTIONS` set before sending requests. Throws `ReadOnlyModeError`. The set covers content, deck, tag, media and note-type writes, the manual rescheduling actions (`forgetCards`, `setDueDate`), `guiUndo`, and the Add/Edit dialogs (`guiAddCards`, `guiEditNote`). Still allowed: `answerCards` (rating during review), `suspend`/`unsuspend`, `sync`, and the GUI navigation actions. `createErrorResponse` replaces the caller's hint with a read-only hint for a `ReadOnlyModeError`, so tools need no special handling.
**Config system**: Two injection tokens:
- `APP_CONFIG` — validated `AppConfig` object (Zod schema in `src/config/config.schema.ts`). Provided as `useValue` after parsing env + CLI overrides.
- `ANKI_CONFIG` — AnkiConnect-specific config interface. Provided via `useClass: AppConfigService` in each module's `forRoot()`. Modules can swap the config provider for testing.
**Environment Configuration**: Config `process.env.*` reads go through `buildConfigInput()` in `src/config/config.factory.ts`. CLI args override env vars in memory (no `process.env` mutation). Services inject `AppConfigService` for type-safe access. Two deliberate exceptions sit outside the config system: `media-validation.utils.ts` (`MEDIA_*`) and `system-info.resource.ts` read `process.env` directly.
### Upstream AnkiConnect Quirks
These are upstream behaviors that shape tool design — surface them in tool descriptions, stated as facts about the tool (see "Description style" below), so the AI can avoid them:
- **`updateNoteFields` silently fails** if the target note is open in Anki's Browse window. The request returns 200 but fields don't persist. The tool description states this.
- **Model CSS is per-note-type, not per-note.** Use `modelStyling` to fetch CSS for a model; `notesInfo` tells you which model each note uses. `updateNoteFields` should preserve inline styles.
- **`sync` relies on the desktop app being logged into AnkiWeb.** There's no API path to authenticate — surface a helpful error hint.
- **`deleteNotes` permanently removes the notes and cascades to all their cards.** Anki's undo history (Edit > Undo, or `guiUndo`, which reverts the newest step) can restore them until that history is cleared, for example by a change Anki does not record for undo such as `updateNoteFields`; after that only a backup restores them. The tool requires explicit `confirmDeletion: true`.
### Build & Tooling Notes
- **NestJS CLI** builds the project (`nest build`). Asset copying is configured in `nest-cli.json` — all `**/*.md` files in `src/` are copied to `dist/`. This matters for prompt templates that reference markdown files.
- **`prebuild` hook** runs `scripts/generate-icon.mjs` before every `npm run build` (via npm's `pre*` lifecycle) — that's why an icon-generation step fires on build. It's expected, not a stray command.
- **ESLint flat config** (`eslint.config.mjs`) — not legacy `.eslintrc`. Uses `typescript-eslint` + Prettier integration.
- **TypeScript**: `strict: true`, target ES2023, `nodenext` module resolution. Path aliases are resolved by both `tsconfig.json` and Jest's `moduleNameMapper`.
- **Zod 4** (`^4.4.3`) — not Zod 3. Some patterns like `z.preprocess` in `config.schema.ts` are Zod 3 holdovers that still work but may need migration.
### Path Aliases
- `@/*` → `src/*`
- `@test/*` → `test/*`
### Key Dependencies
- **Zod v4** (`zod@^4.x`) — NOT v3. Zod 4 has different APIs (e.g., `z.interface()`, changed error handling). Don't use v3 patterns.
- **`@rekog/mcp-nest` v2** (`^2.0.0`) — NOT v1. No `McpModule.forRoot()`: the server is a NestJS microservice `CustomTransportStrategy` (`McpStrategy`), capability classes are `@McpController()`s, and handler methods take `@Payload()`.
- **`@modelcontextprotocol/{core,node,server}` v2** — the MCP SDK. The old single `@modelcontextprotocol/sdk` package is gone. Don't bump without testing MCP protocol compatibility.
- **`@nestjs/microservices`** — required by mcp-nest v2 (`CustomStrategy`, `@Payload()`), not optional.
- **TypeScript** — `strict: true`, `module: "nodenext"`, target `ES2023`. Path aliases (`@/`, `@test/`) handle most imports.
- **ESLint** — Flat config (`eslint.config.mjs`), not legacy `.eslintrc`.
### Logging Guidelines
**Two types of output - don't mix them:**
1. **CLI Output** (user-facing, clean, no timestamps):
```typescript
import { cli } from '@/cli/cli-output';Signals
- Stars
- 531
- Forks
- 38
- Open issues
- 1
- Contributors
- 6
- Last commit (this folder)
- Oct 4, 2026 (26 days ago)
- Last release
- Oct 4, 2026 (26 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/ankimcp/anki-mcp-server
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get CLAUDE.md · ankimcp/anki-mcp-server running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
CLAUDE.md · ankimcp/anki-mcp-server. Agent Mart. https://agentmart.dev/templates/ankimcp-anki-mcp-server-claude-md (last updated 2026-10-24)