@ankimcp/anki-mcp-server 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -5
- package/bin/ankimcp.js +16 -0
- package/dist/anki-config.service.d.ts +1 -0
- package/dist/anki-config.service.js +4 -0
- package/dist/anki-config.service.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +12 -3
- package/dist/cli.js.map +1 -1
- package/dist/main-http.js +1 -0
- package/dist/main-http.js.map +1 -1
- package/dist/main-stdio.js +28 -1
- package/dist/main-stdio.js.map +1 -1
- package/dist/mcp/clients/anki-connect.client.d.ts +5 -0
- package/dist/mcp/clients/anki-connect.client.js +35 -1
- package/dist/mcp/clients/anki-connect.client.js.map +1 -1
- package/dist/mcp/config/anki-config.interface.d.ts +1 -0
- package/dist/mcp/config/anki-config.interface.js.map +1 -1
- package/dist/mcp/primitives/essential/index.d.ts +4 -8
- package/dist/mcp/primitives/essential/index.js +6 -13
- package/dist/mcp/primitives/essential/index.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/add-note.tool.js +22 -9
- package/dist/mcp/primitives/essential/tools/add-note.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/deckActions/actions/changeDeck.action.d.ts +12 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/changeDeck.action.js +24 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/changeDeck.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/createDeck.action.d.ts +15 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/createDeck.action.js +44 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/createDeck.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/{deck-stats/deck-stats.types.d.ts → deckActions/actions/deckStats.action.d.ts} +5 -2
- package/dist/mcp/primitives/essential/tools/deckActions/actions/deckStats.action.js +78 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/deckStats.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/listDecks.action.d.ts +18 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/listDecks.action.js +63 -0
- package/dist/mcp/primitives/essential/tools/deckActions/actions/listDecks.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/{deck-stats/deck-stats.tool.d.ts → deckActions/deckActions.tool.d.ts} +8 -4
- package/dist/mcp/primitives/essential/tools/deckActions/deckActions.tool.js +138 -0
- package/dist/mcp/primitives/essential/tools/deckActions/deckActions.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/deckActions/index.d.ts +5 -0
- package/dist/mcp/primitives/essential/tools/deckActions/index.js +6 -0
- package/dist/mcp/primitives/essential/tools/deckActions/index.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/dist/mcp/primitives/essential/tools/create-deck.tool.d.ts +0 -105
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js +0 -121
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js.map +0 -1
- package/dist/mcp/primitives/essential/tools/deck-stats/deck-stats.tool.js +0 -165
- package/dist/mcp/primitives/essential/tools/deck-stats/deck-stats.tool.js.map +0 -1
- package/dist/mcp/primitives/essential/tools/deck-stats/deck-stats.types.js +0 -3
- package/dist/mcp/primitives/essential/tools/deck-stats/deck-stats.types.js.map +0 -1
- package/dist/mcp/primitives/essential/tools/deck-stats/index.d.ts +0 -2
- package/dist/mcp/primitives/essential/tools/deck-stats/index.js +0 -6
- package/dist/mcp/primitives/essential/tools/deck-stats/index.js.map +0 -1
- package/dist/mcp/primitives/essential/tools/list-decks.tool.d.ts +0 -105
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js +0 -117
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js.map +0 -1
package/README.md
CHANGED
|
@@ -41,8 +41,11 @@ For comprehensive guides, real-world examples, and step-by-step tutorials on usi
|
|
|
41
41
|
- `rate_card` - Rate card performance
|
|
42
42
|
|
|
43
43
|
### Deck Management
|
|
44
|
-
- `
|
|
45
|
-
- `
|
|
44
|
+
- `deckActions` - Unified deck operations:
|
|
45
|
+
- `listDecks` - List all decks with optional statistics
|
|
46
|
+
- `createDeck` - Create new decks (max 2 levels: "Parent::Child")
|
|
47
|
+
- `deckStats` - Get comprehensive deck statistics
|
|
48
|
+
- `changeDeck` - Move cards to a different deck
|
|
46
49
|
|
|
47
50
|
### Note Management
|
|
48
51
|
- `addNote` - Create new notes
|
|
@@ -216,6 +219,7 @@ Options:
|
|
|
216
219
|
-h, --host <host> Host to bind to (HTTP mode, default: 127.0.0.1)
|
|
217
220
|
-a, --anki-connect <url> AnkiConnect URL (default: http://localhost:8765)
|
|
218
221
|
--ngrok Start ngrok tunnel (requires global ngrok installation)
|
|
222
|
+
--read-only Run in read-only mode (blocks all write operations)
|
|
219
223
|
--help Show help message
|
|
220
224
|
|
|
221
225
|
Usage with npx (no installation needed):
|
|
@@ -223,6 +227,7 @@ Usage with npx (no installation needed):
|
|
|
223
227
|
npx @ankimcp/anki-mcp-server --port 8080 # Custom port
|
|
224
228
|
npx @ankimcp/anki-mcp-server --stdio # STDIO mode
|
|
225
229
|
npx @ankimcp/anki-mcp-server --ngrok # HTTP mode with ngrok tunnel
|
|
230
|
+
npx @ankimcp/anki-mcp-server --read-only # Read-only mode
|
|
226
231
|
|
|
227
232
|
Usage with global installation:
|
|
228
233
|
npm install -g @ankimcp/anki-mcp-server # Install once
|
|
@@ -230,6 +235,46 @@ Usage with global installation:
|
|
|
230
235
|
ankimcp --port 8080 # Custom port
|
|
231
236
|
ankimcp --stdio # STDIO mode
|
|
232
237
|
ankimcp --ngrok # HTTP mode with ngrok tunnel
|
|
238
|
+
ankimcp --read-only # Read-only mode
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Read-Only Mode:**
|
|
242
|
+
|
|
243
|
+
The `--read-only` flag prevents any modifications to your Anki collection. When enabled:
|
|
244
|
+
- All read operations work normally (browsing decks, viewing cards, searching notes)
|
|
245
|
+
- Review operations are allowed (sync, answerCards, suspend/unsuspend)
|
|
246
|
+
- Content modifications are blocked (addNote, deleteNotes, createDeck, updateNoteFields, etc.)
|
|
247
|
+
- Useful for safely exploring Anki data without risk of accidental changes
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# HTTP mode with read-only
|
|
251
|
+
ankimcp --read-only
|
|
252
|
+
|
|
253
|
+
# STDIO mode with read-only
|
|
254
|
+
ankimcp --stdio --read-only
|
|
255
|
+
|
|
256
|
+
# Can combine with other flags
|
|
257
|
+
ankimcp --ngrok --read-only
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
You can also enable read-only mode via environment variable:
|
|
261
|
+
```bash
|
|
262
|
+
READ_ONLY=true ankimcp
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Or in MCP client configuration:
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"mcpServers": {
|
|
269
|
+
"anki-mcp": {
|
|
270
|
+
"command": "npx",
|
|
271
|
+
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio", "--read-only"],
|
|
272
|
+
"env": {
|
|
273
|
+
"ANKI_CONNECT_URL": "http://localhost:8765"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
233
278
|
```
|
|
234
279
|
|
|
235
280
|
**Using with ngrok:**
|
|
@@ -321,6 +366,7 @@ For more details, see the [official MCP documentation](https://modelcontextproto
|
|
|
321
366
|
| `ANKI_CONNECT_API_VERSION` | API version | `6` |
|
|
322
367
|
| `ANKI_CONNECT_API_KEY` | API key if configured in AnkiConnect | - |
|
|
323
368
|
| `ANKI_CONNECT_TIMEOUT` | Request timeout in ms | `5000` |
|
|
369
|
+
| `READ_ONLY` | Enable read-only mode (`true` or `1`) | `false` |
|
|
324
370
|
|
|
325
371
|
## Usage Examples
|
|
326
372
|
|
|
@@ -398,14 +444,16 @@ If you see an error like:
|
|
|
398
444
|
Error [ERR_REQUIRE_ESM]: require() of ES Module not supported
|
|
399
445
|
```
|
|
400
446
|
|
|
401
|
-
This means your Node.js version is
|
|
447
|
+
This means your Node.js version is not supported. The server requires **Node.js 20.19.0+ or 22.12.0+**.
|
|
448
|
+
|
|
449
|
+
> **Note:** Node.js 21.x is not supported. The `require(esm)` feature was added in Node 22 and backported to Node 20.17+, but was never available in Node 21. See [#16](https://github.com/ankimcp/anki-mcp-server/issues/16) for details.
|
|
402
450
|
|
|
403
451
|
**Check your version:**
|
|
404
452
|
```bash
|
|
405
453
|
node --version
|
|
406
454
|
```
|
|
407
455
|
|
|
408
|
-
**Solution:** Update Node.js to version 20.19.0 or
|
|
456
|
+
**Solution:** Update Node.js to version 20.19.0+ or 22.12.0+. You can download it from [nodejs.org](https://nodejs.org/) or use a version manager like [nvm](https://github.com/nvm-sh/nvm).
|
|
409
457
|
|
|
410
458
|
## Development
|
|
411
459
|
|
|
@@ -798,4 +846,4 @@ For complete license terms, see the [LICENSE](LICENSE) file.
|
|
|
798
846
|
|
|
799
847
|
- **Model Context Protocol (MCP)** is an open standard by Anthropic. The MCP logo is from the official [MCP documentation repository](https://github.com/modelcontextprotocol/docs) and is used under the MIT License. For more information about MCP, visit [https://modelcontextprotocol.io](https://modelcontextprotocol.io).
|
|
800
848
|
|
|
801
|
-
- This is an independent project that bridges Anki and MCP technologies. All trademarks, service marks, trade names, product names, and logos are the property of their respective owners.
|
|
849
|
+
- This is an independent project that bridges Anki and MCP technologies. All trademarks, service marks, trade names, product names, and logos are the property of their respective owners.
|
package/bin/ankimcp.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Runtime Node.js version check (require(esm) needs Node 20.19+ or 22.12+)
|
|
4
|
+
const [major, minor] = process.versions.node.split('.').map(Number);
|
|
5
|
+
if (
|
|
6
|
+
(major < 20) ||
|
|
7
|
+
(major === 21) ||
|
|
8
|
+
(major === 20 && minor < 19) ||
|
|
9
|
+
(major === 22 && minor < 12)
|
|
10
|
+
) {
|
|
11
|
+
console.error(
|
|
12
|
+
`Error: Node.js ${process.versions.node} is not supported.\n` +
|
|
13
|
+
'This package requires Node.js 20.19.0+ or 22.12.0+ (Node 21.x is not supported).\n' +
|
|
14
|
+
'Download: https://nodejs.org/'
|
|
15
|
+
);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
// Check if --stdio flag is present
|
|
4
20
|
const isStdioMode = process.argv.includes('--stdio');
|
|
5
21
|
|
|
@@ -33,6 +33,10 @@ let AnkiConfigService = class AnkiConfigService {
|
|
|
33
33
|
const timeout = this.configService.get("ANKI_CONNECT_TIMEOUT", "5000");
|
|
34
34
|
return parseInt(timeout, 10);
|
|
35
35
|
}
|
|
36
|
+
get readOnly() {
|
|
37
|
+
const readOnly = this.configService.get("READ_ONLY", "false");
|
|
38
|
+
return readOnly === "true" || readOnly === "1";
|
|
39
|
+
}
|
|
36
40
|
};
|
|
37
41
|
exports.AnkiConfigService = AnkiConfigService;
|
|
38
42
|
exports.AnkiConfigService = AnkiConfigService = __decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anki-config.service.js","sourceRoot":"","sources":["../src/anki-config.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAA+C;AAE/C,mEAAuE;AAMhE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACR;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAC3B,kBAAkB,EAClB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,IAAI,qBAAqB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,0BAA0B,EAC1B,GAAG,CACJ,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,iBAAiB;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,sBAAsB,CAAC,CAAC;QACtE,OAAO,IAAA,0CAAuB,EAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,kBAAkB;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,sBAAsB,EACtB,MAAM,CACP,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;CACF,CAAA;
|
|
1
|
+
{"version":3,"file":"anki-config.service.js","sourceRoot":"","sources":["../src/anki-config.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAA+C;AAE/C,mEAAuE;AAMhE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACR;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAC3B,kBAAkB,EAClB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,IAAI,qBAAqB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,0BAA0B,EAC1B,GAAG,CACJ,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,iBAAiB;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,sBAAsB,CAAC,CAAC;QACtE,OAAO,IAAA,0CAAuB,EAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,kBAAkB;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,sBAAsB,EACtB,MAAM,CACP,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,QAAQ;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,EAAE,OAAO,CAAC,CAAC;QACtE,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,GAAG,CAAC;IACjD,CAAC;CACF,CAAA;AAnCY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEwB,sBAAa;GADrC,iBAAiB,CAmC7B"}
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -35,6 +35,7 @@ function parseCliArgs() {
|
|
|
35
35
|
.option("-h, --host <address>", "Host to bind to (HTTP mode)", "127.0.0.1")
|
|
36
36
|
.option("-a, --anki-connect <url>", "AnkiConnect URL", "http://localhost:8765")
|
|
37
37
|
.option("--ngrok", "Start ngrok tunnel (requires global ngrok installation)")
|
|
38
|
+
.option("--read-only", "Run in read-only mode (blocks all write operations)")
|
|
38
39
|
.addHelpText("after", `
|
|
39
40
|
Transport Modes:
|
|
40
41
|
HTTP Mode (default): For web-based AI assistants (ChatGPT, Claude.ai)
|
|
@@ -64,6 +65,10 @@ Examples - STDIO Mode:
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
Examples - Read-Only Mode:
|
|
69
|
+
$ ankimcp --read-only # HTTP mode, read-only
|
|
70
|
+
$ ankimcp --stdio --read-only # STDIO mode, read-only
|
|
71
|
+
|
|
67
72
|
Ngrok Setup (one-time):
|
|
68
73
|
1. Install: npm install -g ngrok
|
|
69
74
|
2. Get auth token from: https://dashboard.ngrok.com/get-started/your-authtoken
|
|
@@ -77,6 +82,7 @@ Ngrok Setup (one-time):
|
|
|
77
82
|
host: options.host,
|
|
78
83
|
ankiConnect: options.ankiConnect,
|
|
79
84
|
ngrok: options.ngrok || false,
|
|
85
|
+
readOnly: options.readOnly || false,
|
|
80
86
|
};
|
|
81
87
|
}
|
|
82
88
|
function displayStartupBanner(options, ngrokUrl) {
|
|
@@ -84,19 +90,22 @@ function displayStartupBanner(options, ngrokUrl) {
|
|
|
84
90
|
const title = `AnkiMCP HTTP Server v${version}`;
|
|
85
91
|
const padding = Math.floor((64 - title.length) / 2);
|
|
86
92
|
const paddedTitle = " ".repeat(padding) + title + " ".repeat(64 - padding - title.length);
|
|
93
|
+
const readOnlyWarning = options.readOnly
|
|
94
|
+
? "\n\n** READ-ONLY MODE ENABLED **\nContent modifications (addNote, deleteNotes, createDeck, etc.) are blocked.\nReview operations (sync, answerCards, suspend) remain available."
|
|
95
|
+
: "";
|
|
87
96
|
console.log(`
|
|
88
97
|
╔════════════════════════════════════════════════════════════════╗
|
|
89
98
|
║${paddedTitle}║
|
|
90
|
-
|
|
99
|
+
╚════════════════════════════════════════════════════════════════╝${readOnlyWarning}
|
|
91
100
|
|
|
92
101
|
🚀 Server running on: http://${options.host}:${options.port}
|
|
93
|
-
🔌 AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` : ""}
|
|
102
|
+
🔌 AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` : ""}${options.readOnly ? "\n🔒 Mode: Read-only" : ""}
|
|
94
103
|
|
|
95
104
|
Configuration:
|
|
96
105
|
• Port: ${options.port} (override: --port 8080)
|
|
97
106
|
• Host: ${options.host} (override: --host 0.0.0.0)
|
|
98
107
|
• AnkiConnect: ${options.ankiConnect}
|
|
99
|
-
(override: --anki-connect http://localhost:8765)${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` : ""}
|
|
108
|
+
(override: --anki-connect http://localhost:8765)${options.readOnly ? "\n • Read-only: Yes (write operations blocked)" : ""}${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` : ""}
|
|
100
109
|
${!ngrokUrl
|
|
101
110
|
? `
|
|
102
111
|
Usage with ngrok:
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/main-http.js
CHANGED
|
@@ -12,6 +12,7 @@ async function bootstrap() {
|
|
|
12
12
|
process.env.PORT = options.port.toString();
|
|
13
13
|
process.env.HOST = options.host;
|
|
14
14
|
process.env.ANKI_CONNECT_URL = options.ankiConnect;
|
|
15
|
+
process.env.READ_ONLY = options.readOnly ? "true" : "false";
|
|
15
16
|
const pinoLogger = (0, bootstrap_1.createPinoLogger)(1);
|
|
16
17
|
const loggerService = (0, bootstrap_1.createLoggerService)(pinoLogger);
|
|
17
18
|
const app = await core_1.NestFactory.create(app_module_1.AppModule.forHttp(), {
|
package/dist/main-http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-http.js","sourceRoot":"","sources":["../src/main-http.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AACpE,mFAA8E;AAC9E,+BAA4E;AAC5E,4DAAwD;AAExD,KAAK,UAAU,SAAS;IAEtB,IAAA,qBAAe,GAAE,CAAC;IAElB,MAAM,OAAO,GAAG,IAAA,kBAAY,GAAE,CAAC;IAG/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"main-http.js","sourceRoot":"","sources":["../src/main-http.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AACpE,mFAA8E;AAC9E,+BAA4E;AAC5E,4DAAwD;AAExD,KAAK,UAAU,SAAS;IAEtB,IAAA,qBAAe,GAAE,CAAC;IAElB,MAAM,OAAO,GAAG,IAAA,kBAAY,GAAE,CAAC;IAG/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAG5D,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,CAAC,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,UAAU,CAAC,CAAC;IAGtD,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,IAAI,+CAAqB,EAAE,CAAC,CAAC;IAEjD,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7C,IAAI,QAA4B,CAAC;IACjC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,4BAAY,EAAE,CAAC;YACxC,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1D,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAC5C,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAGD,IAAA,0BAAoB,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/main-stdio.js
CHANGED
|
@@ -3,14 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const core_1 = require("@nestjs/core");
|
|
4
4
|
const app_module_1 = require("./app.module");
|
|
5
5
|
const bootstrap_1 = require("./bootstrap");
|
|
6
|
+
function parseStdioArgs() {
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
let readOnly = false;
|
|
9
|
+
let ankiConnect;
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
if (args[i] === "--read-only") {
|
|
12
|
+
readOnly = true;
|
|
13
|
+
}
|
|
14
|
+
else if (args[i] === "--anki-connect" || args[i] === "-a") {
|
|
15
|
+
ankiConnect = args[i + 1];
|
|
16
|
+
i++;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { readOnly, ankiConnect };
|
|
20
|
+
}
|
|
6
21
|
async function bootstrap() {
|
|
22
|
+
const cliOptions = parseStdioArgs();
|
|
23
|
+
if (cliOptions.readOnly) {
|
|
24
|
+
process.env.READ_ONLY = "true";
|
|
25
|
+
}
|
|
26
|
+
if (cliOptions.ankiConnect) {
|
|
27
|
+
process.env.ANKI_CONNECT_URL = cliOptions.ankiConnect;
|
|
28
|
+
}
|
|
7
29
|
const pinoLogger = (0, bootstrap_1.createPinoLogger)(2);
|
|
8
30
|
const loggerService = (0, bootstrap_1.createLoggerService)(pinoLogger);
|
|
9
31
|
await core_1.NestFactory.createApplicationContext(app_module_1.AppModule.forStdio(), {
|
|
10
32
|
logger: loggerService,
|
|
11
33
|
bufferLogs: true,
|
|
12
34
|
});
|
|
13
|
-
|
|
35
|
+
if (cliOptions.readOnly) {
|
|
36
|
+
pinoLogger.info("MCP STDIO server started in READ-ONLY mode");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
pinoLogger.info("MCP STDIO server started successfully");
|
|
40
|
+
}
|
|
14
41
|
await new Promise(() => { });
|
|
15
42
|
}
|
|
16
43
|
bootstrap().catch((err) => {
|
package/dist/main-stdio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-stdio.js","sourceRoot":"","sources":["../src/main-stdio.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;
|
|
1
|
+
{"version":3,"file":"main-stdio.js","sourceRoot":"","sources":["../src/main-stdio.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AAMpE,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAA+B,CAAC;IAEpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,EAAE,CAAC;YAC9B,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,gBAAgB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5D,WAAW,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,SAAS;IAEtB,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IAGpC,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC;IACjC,CAAC;IACD,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,UAAU,CAAC,WAAW,CAAC;IACxD,CAAC;IAGD,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,CAAC,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,UAAU,CAAC,CAAC;IAGtD,MAAM,kBAAW,CAAC,wBAAwB,CAAC,sBAAS,CAAC,QAAQ,EAAE,EAAE;QAC/D,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAEH,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QACxB,UAAU,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAGD,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -4,11 +4,16 @@ export declare class AnkiConnectError extends Error {
|
|
|
4
4
|
readonly originalError?: string | undefined;
|
|
5
5
|
constructor(message: string, action?: string | undefined, originalError?: string | undefined);
|
|
6
6
|
}
|
|
7
|
+
export declare class ReadOnlyModeError extends Error {
|
|
8
|
+
readonly action: string;
|
|
9
|
+
constructor(action: string);
|
|
10
|
+
}
|
|
7
11
|
export declare class AnkiConnectClient {
|
|
8
12
|
private readonly config;
|
|
9
13
|
private readonly client;
|
|
10
14
|
private readonly apiVersion;
|
|
11
15
|
private readonly apiKey?;
|
|
16
|
+
private readonly readOnly;
|
|
12
17
|
private readonly logger;
|
|
13
18
|
constructor(config: IAnkiConfig);
|
|
14
19
|
invoke<T = any>(action: string, params?: Record<string, any>): Promise<T>;
|
|
@@ -46,10 +46,25 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
46
46
|
};
|
|
47
47
|
var AnkiConnectClient_1;
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
-
exports.AnkiConnectClient = exports.AnkiConnectError = void 0;
|
|
49
|
+
exports.AnkiConnectClient = exports.ReadOnlyModeError = exports.AnkiConnectError = void 0;
|
|
50
50
|
const common_1 = require("@nestjs/common");
|
|
51
51
|
const ky_1 = __importStar(require("ky"));
|
|
52
52
|
const anki_config_interface_1 = require("../config/anki-config.interface");
|
|
53
|
+
const WRITE_ACTIONS = new Set([
|
|
54
|
+
"addNote",
|
|
55
|
+
"updateNoteFields",
|
|
56
|
+
"deleteNotes",
|
|
57
|
+
"createDeck",
|
|
58
|
+
"changeDeck",
|
|
59
|
+
"addTags",
|
|
60
|
+
"removeTags",
|
|
61
|
+
"clearUnusedTags",
|
|
62
|
+
"replaceTags",
|
|
63
|
+
"storeMediaFile",
|
|
64
|
+
"deleteMediaFile",
|
|
65
|
+
"createModel",
|
|
66
|
+
"updateModelStyling",
|
|
67
|
+
]);
|
|
53
68
|
class AnkiConnectError extends Error {
|
|
54
69
|
action;
|
|
55
70
|
originalError;
|
|
@@ -61,16 +76,28 @@ class AnkiConnectError extends Error {
|
|
|
61
76
|
}
|
|
62
77
|
}
|
|
63
78
|
exports.AnkiConnectError = AnkiConnectError;
|
|
79
|
+
class ReadOnlyModeError extends Error {
|
|
80
|
+
action;
|
|
81
|
+
constructor(action) {
|
|
82
|
+
super(`Action "${action}" is blocked: server is running in read-only mode. ` +
|
|
83
|
+
`Write operations are disabled. Remove the --read-only flag to enable writes.`);
|
|
84
|
+
this.action = action;
|
|
85
|
+
this.name = "ReadOnlyModeError";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.ReadOnlyModeError = ReadOnlyModeError;
|
|
64
89
|
let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
65
90
|
config;
|
|
66
91
|
client;
|
|
67
92
|
apiVersion;
|
|
68
93
|
apiKey;
|
|
94
|
+
readOnly;
|
|
69
95
|
logger = new common_1.Logger(AnkiConnectClient_1.name);
|
|
70
96
|
constructor(config) {
|
|
71
97
|
this.config = config;
|
|
72
98
|
this.apiVersion = config.ankiConnectApiVersion;
|
|
73
99
|
this.apiKey = config.ankiConnectApiKey;
|
|
100
|
+
this.readOnly = config.readOnly ?? false;
|
|
74
101
|
this.client = ky_1.default.create({
|
|
75
102
|
prefixUrl: config.ankiConnectUrl,
|
|
76
103
|
timeout: config.ankiConnectTimeout,
|
|
@@ -98,6 +125,10 @@ let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
|
98
125
|
});
|
|
99
126
|
}
|
|
100
127
|
async invoke(action, params) {
|
|
128
|
+
if (this.readOnly && WRITE_ACTIONS.has(action)) {
|
|
129
|
+
this.logger.warn(`Blocked write action "${action}" in read-only mode`);
|
|
130
|
+
throw new ReadOnlyModeError(action);
|
|
131
|
+
}
|
|
101
132
|
const request = {
|
|
102
133
|
action,
|
|
103
134
|
version: this.apiVersion,
|
|
@@ -120,6 +151,9 @@ let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
|
120
151
|
return response.result;
|
|
121
152
|
}
|
|
122
153
|
catch (error) {
|
|
154
|
+
if (error instanceof ReadOnlyModeError) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
123
157
|
if (error instanceof ky_1.HTTPError) {
|
|
124
158
|
if (error.response.status === 403) {
|
|
125
159
|
throw new AnkiConnectError("Permission denied. Please check AnkiConnect configuration and API key.", action);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anki-connect.client.js","sourceRoot":"","sources":["../../../src/mcp/clients/anki-connect.client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,yCAA+C;AAC/C,2EAA8D;
|
|
1
|
+
{"version":3,"file":"anki-connect.client.js","sourceRoot":"","sources":["../../../src/mcp/clients/anki-connect.client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anki-config.interface.js","sourceRoot":"","sources":["../../../src/mcp/config/anki-config.interface.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"anki-config.interface.js","sourceRoot":"","sources":["../../../src/mcp/config/anki-config.interface.ts"],"names":[],"mappings":";;;AAsCa,QAAA,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -2,10 +2,8 @@ export { ANKI_CONFIG } from "../../config/anki-config.interface";
|
|
|
2
2
|
export type { IAnkiConfig } from "../../config/anki-config.interface";
|
|
3
3
|
export * from "../../types/anki.types";
|
|
4
4
|
export * from "../../utils/anki.utils";
|
|
5
|
-
export { AnkiConnectClient, AnkiConnectError, } from "../../clients/anki-connect.client";
|
|
5
|
+
export { AnkiConnectClient, AnkiConnectError, ReadOnlyModeError, } from "../../clients/anki-connect.client";
|
|
6
6
|
export { SyncTool } from "./tools/sync.tool";
|
|
7
|
-
export { ListDecksTool } from "./tools/list-decks.tool";
|
|
8
|
-
export { CreateDeckTool } from "./tools/create-deck.tool";
|
|
9
7
|
export { GetDueCardsTool } from "./tools/get-due-cards.tool";
|
|
10
8
|
export { GetCardsTool } from "./tools/get-cards.tool";
|
|
11
9
|
export { PresentCardTool } from "./tools/present-card.tool";
|
|
@@ -23,7 +21,7 @@ export { DeleteNotesTool } from "./tools/delete-notes.tool";
|
|
|
23
21
|
export { MediaActionsTool } from "./tools/mediaActions";
|
|
24
22
|
export { GetTagsTool } from "./tools/get-tags.tool";
|
|
25
23
|
export { TagActionsTool } from "./tools/tagActions";
|
|
26
|
-
export {
|
|
24
|
+
export { DeckActionsTool } from "./tools/deckActions";
|
|
27
25
|
export { CollectionStatsTool } from "./tools/collection-stats";
|
|
28
26
|
export { ReviewStatsTool } from "./tools/review-stats";
|
|
29
27
|
export { ReviewSessionPrompt } from "./prompts/review-session.prompt";
|
|
@@ -31,8 +29,6 @@ export { TwentyRulesPrompt } from "./prompts/twenty-rules.prompt";
|
|
|
31
29
|
export { SystemInfoResource } from "./resources/system-info.resource";
|
|
32
30
|
import { DynamicModule, Provider } from "@nestjs/common";
|
|
33
31
|
import { SyncTool } from "./tools/sync.tool";
|
|
34
|
-
import { ListDecksTool } from "./tools/list-decks.tool";
|
|
35
|
-
import { CreateDeckTool } from "./tools/create-deck.tool";
|
|
36
32
|
import { GetDueCardsTool } from "./tools/get-due-cards.tool";
|
|
37
33
|
import { GetCardsTool } from "./tools/get-cards.tool";
|
|
38
34
|
import { PresentCardTool } from "./tools/present-card.tool";
|
|
@@ -50,13 +46,13 @@ import { DeleteNotesTool } from "./tools/delete-notes.tool";
|
|
|
50
46
|
import { MediaActionsTool } from "./tools/mediaActions";
|
|
51
47
|
import { GetTagsTool } from "./tools/get-tags.tool";
|
|
52
48
|
import { TagActionsTool } from "./tools/tagActions";
|
|
53
|
-
import {
|
|
49
|
+
import { DeckActionsTool } from "./tools/deckActions";
|
|
54
50
|
import { CollectionStatsTool } from "./tools/collection-stats";
|
|
55
51
|
import { ReviewStatsTool } from "./tools/review-stats";
|
|
56
52
|
import { ReviewSessionPrompt } from "./prompts/review-session.prompt";
|
|
57
53
|
import { TwentyRulesPrompt } from "./prompts/twenty-rules.prompt";
|
|
58
54
|
import { SystemInfoResource } from "./resources/system-info.resource";
|
|
59
|
-
export declare const ESSENTIAL_MCP_TOOLS: (typeof SyncTool | typeof
|
|
55
|
+
export declare const ESSENTIAL_MCP_TOOLS: (typeof SyncTool | typeof GetDueCardsTool | typeof GetCardsTool | typeof PresentCardTool | typeof RateCardTool | typeof ModelNamesTool | typeof ModelFieldNamesTool | typeof ModelStylingTool | typeof CreateModelTool | typeof UpdateModelStylingTool | typeof AddNoteTool | typeof FindNotesTool | typeof NotesInfoTool | typeof UpdateNoteFieldsTool | typeof DeleteNotesTool | typeof MediaActionsTool | typeof GetTagsTool | typeof TagActionsTool | typeof DeckActionsTool | typeof CollectionStatsTool | typeof ReviewStatsTool | typeof ReviewSessionPrompt | typeof TwentyRulesPrompt | typeof SystemInfoResource)[];
|
|
60
56
|
export interface McpPrimitivesAnkiEssentialModuleOptions {
|
|
61
57
|
ankiConfigProvider: Provider;
|
|
62
58
|
}
|
|
@@ -21,7 +21,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
21
21
|
};
|
|
22
22
|
var McpPrimitivesAnkiEssentialModule_1;
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.McpPrimitivesAnkiEssentialModule = exports.ESSENTIAL_MCP_TOOLS = exports.SystemInfoResource = exports.TwentyRulesPrompt = exports.ReviewSessionPrompt = exports.ReviewStatsTool = exports.CollectionStatsTool = exports.
|
|
24
|
+
exports.McpPrimitivesAnkiEssentialModule = exports.ESSENTIAL_MCP_TOOLS = exports.SystemInfoResource = exports.TwentyRulesPrompt = exports.ReviewSessionPrompt = exports.ReviewStatsTool = exports.CollectionStatsTool = exports.DeckActionsTool = exports.TagActionsTool = exports.GetTagsTool = exports.MediaActionsTool = exports.DeleteNotesTool = exports.UpdateNoteFieldsTool = exports.NotesInfoTool = exports.FindNotesTool = exports.AddNoteTool = exports.UpdateModelStylingTool = exports.CreateModelTool = exports.ModelStylingTool = exports.ModelFieldNamesTool = exports.ModelNamesTool = exports.RateCardTool = exports.PresentCardTool = exports.GetCardsTool = exports.GetDueCardsTool = exports.SyncTool = exports.ReadOnlyModeError = exports.AnkiConnectError = exports.AnkiConnectClient = exports.ANKI_CONFIG = void 0;
|
|
25
25
|
var anki_config_interface_1 = require("../../config/anki-config.interface");
|
|
26
26
|
Object.defineProperty(exports, "ANKI_CONFIG", { enumerable: true, get: function () { return anki_config_interface_1.ANKI_CONFIG; } });
|
|
27
27
|
__exportStar(require("../../types/anki.types"), exports);
|
|
@@ -29,12 +29,9 @@ __exportStar(require("../../utils/anki.utils"), exports);
|
|
|
29
29
|
var anki_connect_client_1 = require("../../clients/anki-connect.client");
|
|
30
30
|
Object.defineProperty(exports, "AnkiConnectClient", { enumerable: true, get: function () { return anki_connect_client_1.AnkiConnectClient; } });
|
|
31
31
|
Object.defineProperty(exports, "AnkiConnectError", { enumerable: true, get: function () { return anki_connect_client_1.AnkiConnectError; } });
|
|
32
|
+
Object.defineProperty(exports, "ReadOnlyModeError", { enumerable: true, get: function () { return anki_connect_client_1.ReadOnlyModeError; } });
|
|
32
33
|
var sync_tool_1 = require("./tools/sync.tool");
|
|
33
34
|
Object.defineProperty(exports, "SyncTool", { enumerable: true, get: function () { return sync_tool_1.SyncTool; } });
|
|
34
|
-
var list_decks_tool_1 = require("./tools/list-decks.tool");
|
|
35
|
-
Object.defineProperty(exports, "ListDecksTool", { enumerable: true, get: function () { return list_decks_tool_1.ListDecksTool; } });
|
|
36
|
-
var create_deck_tool_1 = require("./tools/create-deck.tool");
|
|
37
|
-
Object.defineProperty(exports, "CreateDeckTool", { enumerable: true, get: function () { return create_deck_tool_1.CreateDeckTool; } });
|
|
38
35
|
var get_due_cards_tool_1 = require("./tools/get-due-cards.tool");
|
|
39
36
|
Object.defineProperty(exports, "GetDueCardsTool", { enumerable: true, get: function () { return get_due_cards_tool_1.GetDueCardsTool; } });
|
|
40
37
|
var get_cards_tool_1 = require("./tools/get-cards.tool");
|
|
@@ -69,8 +66,8 @@ var get_tags_tool_1 = require("./tools/get-tags.tool");
|
|
|
69
66
|
Object.defineProperty(exports, "GetTagsTool", { enumerable: true, get: function () { return get_tags_tool_1.GetTagsTool; } });
|
|
70
67
|
var tagActions_1 = require("./tools/tagActions");
|
|
71
68
|
Object.defineProperty(exports, "TagActionsTool", { enumerable: true, get: function () { return tagActions_1.TagActionsTool; } });
|
|
72
|
-
var
|
|
73
|
-
Object.defineProperty(exports, "
|
|
69
|
+
var deckActions_1 = require("./tools/deckActions");
|
|
70
|
+
Object.defineProperty(exports, "DeckActionsTool", { enumerable: true, get: function () { return deckActions_1.DeckActionsTool; } });
|
|
74
71
|
var collection_stats_1 = require("./tools/collection-stats");
|
|
75
72
|
Object.defineProperty(exports, "CollectionStatsTool", { enumerable: true, get: function () { return collection_stats_1.CollectionStatsTool; } });
|
|
76
73
|
var review_stats_1 = require("./tools/review-stats");
|
|
@@ -84,8 +81,6 @@ Object.defineProperty(exports, "SystemInfoResource", { enumerable: true, get: fu
|
|
|
84
81
|
const common_1 = require("@nestjs/common");
|
|
85
82
|
const anki_connect_client_2 = require("../../clients/anki-connect.client");
|
|
86
83
|
const sync_tool_2 = require("./tools/sync.tool");
|
|
87
|
-
const list_decks_tool_2 = require("./tools/list-decks.tool");
|
|
88
|
-
const create_deck_tool_2 = require("./tools/create-deck.tool");
|
|
89
84
|
const get_due_cards_tool_2 = require("./tools/get-due-cards.tool");
|
|
90
85
|
const get_cards_tool_2 = require("./tools/get-cards.tool");
|
|
91
86
|
const present_card_tool_2 = require("./tools/present-card.tool");
|
|
@@ -103,7 +98,7 @@ const delete_notes_tool_2 = require("./tools/delete-notes.tool");
|
|
|
103
98
|
const mediaActions_2 = require("./tools/mediaActions");
|
|
104
99
|
const get_tags_tool_2 = require("./tools/get-tags.tool");
|
|
105
100
|
const tagActions_2 = require("./tools/tagActions");
|
|
106
|
-
const
|
|
101
|
+
const deckActions_2 = require("./tools/deckActions");
|
|
107
102
|
const collection_stats_2 = require("./tools/collection-stats");
|
|
108
103
|
const review_stats_2 = require("./tools/review-stats");
|
|
109
104
|
const review_session_prompt_2 = require("./prompts/review-session.prompt");
|
|
@@ -111,8 +106,6 @@ const twenty_rules_prompt_2 = require("./prompts/twenty-rules.prompt");
|
|
|
111
106
|
const system_info_resource_2 = require("./resources/system-info.resource");
|
|
112
107
|
exports.ESSENTIAL_MCP_TOOLS = [
|
|
113
108
|
sync_tool_2.SyncTool,
|
|
114
|
-
list_decks_tool_2.ListDecksTool,
|
|
115
|
-
create_deck_tool_2.CreateDeckTool,
|
|
116
109
|
get_due_cards_tool_2.GetDueCardsTool,
|
|
117
110
|
get_cards_tool_2.GetCardsTool,
|
|
118
111
|
present_card_tool_2.PresentCardTool,
|
|
@@ -130,7 +123,7 @@ exports.ESSENTIAL_MCP_TOOLS = [
|
|
|
130
123
|
mediaActions_2.MediaActionsTool,
|
|
131
124
|
get_tags_tool_2.GetTagsTool,
|
|
132
125
|
tagActions_2.TagActionsTool,
|
|
133
|
-
|
|
126
|
+
deckActions_2.DeckActionsTool,
|
|
134
127
|
collection_stats_2.CollectionStatsTool,
|
|
135
128
|
review_stats_2.ReviewStatsTool,
|
|
136
129
|
review_session_prompt_2.ReviewSessionPrompt,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp/primitives/essential/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AACA,4EAAiE;AAAxD,oHAAA,WAAW,OAAA;AAIpB,yDAAuC;AAGvC,yDAAuC;AAGvC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp/primitives/essential/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,16 +24,29 @@ let AddNoteTool = AddNoteTool_1 = class AddNoteTool {
|
|
|
24
24
|
}
|
|
25
25
|
async addNote({ deckName, modelName, fields, tags, allowDuplicate, duplicateScope, duplicateScopeOptions, }, context) {
|
|
26
26
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
this.logger.log(`Adding note to deck "${deckName}" with model "${modelName}"`);
|
|
28
|
+
await context.reportProgress({ progress: 20, total: 100 });
|
|
29
|
+
const fieldNames = await this.ankiClient.invoke("modelFieldNames", {
|
|
30
|
+
modelName: modelName,
|
|
31
|
+
});
|
|
32
|
+
if (!fieldNames || fieldNames.length === 0) {
|
|
33
|
+
return (0, anki_utils_1.createErrorResponse)(new Error(`Model "${modelName}" not found or has no fields`), {
|
|
31
34
|
modelName,
|
|
32
|
-
|
|
35
|
+
hint: "Use modelNames tool to see available models",
|
|
33
36
|
});
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
await context.reportProgress({ progress: 35, total: 100 });
|
|
39
|
+
const sortField = fieldNames[0];
|
|
40
|
+
const sortFieldValue = fields[sortField];
|
|
41
|
+
if (!sortFieldValue || sortFieldValue.trim() === "") {
|
|
42
|
+
return (0, anki_utils_1.createErrorResponse)(new Error(`The first field "${sortField}" cannot be empty. Anki requires the sort field to have content.`), {
|
|
43
|
+
modelName,
|
|
44
|
+
sortField,
|
|
45
|
+
providedFields: Object.keys(fields),
|
|
46
|
+
hint: `The first field "${sortField}" is the sort field and must contain non-empty content.`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
await context.reportProgress({ progress: 50, total: 100 });
|
|
37
50
|
const noteParams = {
|
|
38
51
|
deckName: deckName,
|
|
39
52
|
modelName: modelName,
|
|
@@ -59,11 +72,11 @@ let AddNoteTool = AddNoteTool_1 = class AddNoteTool {
|
|
|
59
72
|
if (hasOptions) {
|
|
60
73
|
noteParams.options = options;
|
|
61
74
|
}
|
|
62
|
-
await context.reportProgress({ progress:
|
|
75
|
+
await context.reportProgress({ progress: 60, total: 100 });
|
|
63
76
|
const noteId = await this.ankiClient.invoke("addNote", {
|
|
64
77
|
note: noteParams,
|
|
65
78
|
});
|
|
66
|
-
await context.reportProgress({ progress:
|
|
79
|
+
await context.reportProgress({ progress: 80, total: 100 });
|
|
67
80
|
if (!noteId) {
|
|
68
81
|
this.logger.warn("Note creation failed - possibly a duplicate");
|
|
69
82
|
await context.reportProgress({ progress: 100, total: 100 });
|