anki-mcp-http 0.8.0 → 0.8.2
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/LICENSE +5 -19
- package/README.md +74 -35
- package/dist/anki-config.service.d.ts +2 -2
- package/dist/anki-config.service.js +4 -4
- package/dist/app.module.d.ts +1 -1
- package/dist/app.module.js +7 -7
- package/dist/app.module.js.map +1 -1
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.js +4 -4
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/http/guards/origin-validation.guard.d.ts +1 -1
- package/dist/http/guards/origin-validation.guard.js +5 -5
- package/dist/main-http.js +3 -3
- package/dist/main-http.js.map +1 -1
- package/dist/main-stdio.js +2 -2
- package/dist/mcp/clients/__mocks__/anki-connect.client.js +1 -1
- package/dist/mcp/clients/anki-connect.client.d.ts +1 -1
- package/dist/mcp/clients/anki-connect.client.js +7 -7
- package/dist/mcp/config/anki-config.interface.js +1 -1
- package/dist/mcp/primitives/essential/index.d.ts +26 -26
- package/dist/mcp/primitives/essential/index.js +1 -4
- package/dist/mcp/primitives/essential/index.js.map +1 -1
- package/dist/mcp/primitives/essential/prompts/review-session.prompt.js +5 -5
- package/dist/mcp/primitives/essential/prompts/review-session.prompt.js.map +1 -1
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/index.js +10 -8
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/index.js.map +1 -1
- package/dist/mcp/primitives/essential/resources/system-info.resource.js +12 -12
- package/dist/mcp/primitives/essential/tools/add-note.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/add-note.tool.js +27 -30
- package/dist/mcp/primitives/essential/tools/add-note.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/create-deck.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js +15 -15
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/create-model.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/create-model.tool.js +20 -18
- package/dist/mcp/primitives/essential/tools/create-model.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.js +22 -22
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/find-notes.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/find-notes.tool.js +14 -14
- package/dist/mcp/primitives/essential/tools/find-notes.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.js +17 -15
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/list-decks.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js +10 -10
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/deleteMediaFile.action.d.ts +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/deleteMediaFile.action.js +3 -3
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.d.ts +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.js +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/retrieveMediaFile.action.d.ts +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/retrieveMediaFile.action.js +3 -3
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.d.ts +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.js +7 -7
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/mediaActions/index.d.ts +5 -5
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.js +23 -17
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.js +20 -17
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/model-names.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/model-names.tool.js +13 -11
- package/dist/mcp/primitives/essential/tools/model-names.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/model-styling.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/model-styling.tool.js +11 -11
- package/dist/mcp/primitives/essential/tools/model-styling.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/notes-info.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/notes-info.tool.js +19 -19
- package/dist/mcp/primitives/essential/tools/notes-info.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/present-card.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/present-card.tool.js +11 -11
- package/dist/mcp/primitives/essential/tools/present-card.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/rate-card.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/rate-card.tool.js +19 -14
- package/dist/mcp/primitives/essential/tools/rate-card.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/sync.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/sync.tool.js +8 -8
- package/dist/mcp/primitives/essential/tools/sync.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.d.ts +2 -2
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.js +19 -19
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.js.map +1 -1
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.d.ts +3 -3
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.js +32 -32
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/index.d.ts +17 -17
- package/dist/mcp/primitives/gui/index.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.d.ts +2 -2
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.js +22 -22
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.js +15 -14
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.js +13 -13
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.js +12 -12
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.d.ts +2 -2
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.js +15 -12
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.d.ts +2 -2
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.js +13 -12
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.d.ts +2 -2
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.js +18 -15
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.js +15 -14
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.js +15 -15
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.js +15 -15
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.js.map +1 -1
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.d.ts +3 -3
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.js +15 -15
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.js.map +1 -1
- package/dist/mcp/types/anki.types.d.ts +2 -2
- package/dist/mcp/utils/anki.utils.d.ts +3 -3
- package/dist/mcp/utils/anki.utils.js +42 -38
- package/dist/mcp/utils/anki.utils.js.map +1 -1
- package/dist/mcp/utils/markdown.utils.js +16 -12
- package/dist/mcp/utils/markdown.utils.js.map +1 -1
- package/dist/mcp/utils/mcpb-workarounds.js +2 -2
- package/dist/mcp/utils/mcpb-workarounds.js.map +1 -1
- package/dist/services/ngrok.service.js +30 -30
- package/dist/services/ngrok.service.js.map +1 -1
- package/dist/test-fixtures/mock-data.d.ts +2 -2
- package/dist/test-fixtures/mock-data.js +46 -46
- package/dist/test-fixtures/mock-data.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +12 -7
package/LICENSE
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
Anki MCP Server is licensed under the GNU Affero General Public License,
|
|
2
|
+
version 3 or later.
|
|
2
3
|
|
|
3
|
-
Copyright (
|
|
4
|
+
Copyright (C) 2025 Anatoly Tarnavsky
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
6
|
+
For the full text of the AGPL-3.0 license, see:
|
|
7
|
+
https://www.gnu.org/licenses/agpl-3.0.txt
|
package/README.md
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
# Anki MCP
|
|
1
|
+
# Anki MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ankimcp/anki-mcp-server/actions/workflows/test.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@ankimcp/anki-mcp-server)
|
|
5
|
+
|
|
6
|
+
<div align="center">
|
|
7
|
+
<img src="./docs/images/ankimcp.png" alt="Anki + MCP Integration" width="600" />
|
|
8
|
+
|
|
9
|
+
<p><strong>Seamlessly integrate <a href="https://apps.ankiweb.net">Anki</a> with AI assistants through the <a href="https://modelcontextprotocol.io">Model Context Protocol</a></strong></p>
|
|
10
|
+
</div>
|
|
2
11
|
|
|
3
12
|
**Beta** - This project is in active development. APIs and features may change.
|
|
4
13
|
|
|
@@ -38,6 +47,13 @@ For comprehensive guides, real-world examples, and step-by-step tutorials on usi
|
|
|
38
47
|
- `getMediaFilesNames` - List media files with optional pattern filtering
|
|
39
48
|
- `deleteMediaFile` - Remove media files
|
|
40
49
|
|
|
50
|
+
**💡 Best Practice for Images:**
|
|
51
|
+
- ✅ **Use file paths** (e.g., `/Users/you/image.png`) - Fast and efficient
|
|
52
|
+
- ✅ **Use URLs** (e.g., `https://example.com/image.jpg`) - Direct download
|
|
53
|
+
- ❌ **Avoid base64** - Extremely slow and token-inefficient
|
|
54
|
+
|
|
55
|
+
Just tell Claude where the image is, and it will handle the upload automatically using the most efficient method.
|
|
56
|
+
|
|
41
57
|
### Model/Template Management
|
|
42
58
|
- `modelNames` - List note types
|
|
43
59
|
- `modelFieldNames` - Get fields for a note type
|
|
@@ -59,7 +75,7 @@ This server works in two modes:
|
|
|
59
75
|
|
|
60
76
|
The easiest way to install this MCP server for Claude Desktop:
|
|
61
77
|
|
|
62
|
-
1. Download the latest `.mcpb` bundle from the [Releases](https://github.com/
|
|
78
|
+
1. Download the latest `.mcpb` bundle from the [Releases](https://github.com/ankimcp/anki-mcp-server/releases) page
|
|
63
79
|
2. In Claude Desktop, install the extension:
|
|
64
80
|
- **Method 1**: Go to Settings → Extensions, then drag and drop the `.mcpb` file
|
|
65
81
|
- **Method 2**: Go to Settings → Developer → Extensions → Install Extension, then select the `.mcpb` file
|
|
@@ -87,7 +103,7 @@ Want to use Anki with MCP clients like **Cursor IDE**, **Cline**, or **Zed Edito
|
|
|
87
103
|
"mcpServers": {
|
|
88
104
|
"anki-mcp": {
|
|
89
105
|
"command": "npx",
|
|
90
|
-
"args": ["-y", "anki-mcp-
|
|
106
|
+
"args": ["-y", "@ankimcp/anki-mcp-server", "--stdio"],
|
|
91
107
|
"env": {
|
|
92
108
|
"ANKI_CONNECT_URL": "http://localhost:8765"
|
|
93
109
|
}
|
|
@@ -100,7 +116,7 @@ Want to use Anki with MCP clients like **Cursor IDE**, **Cline**, or **Zed Edito
|
|
|
100
116
|
|
|
101
117
|
First, install globally:
|
|
102
118
|
```bash
|
|
103
|
-
npm install -g anki-mcp-
|
|
119
|
+
npm install -g @ankimcp/anki-mcp-server
|
|
104
120
|
```
|
|
105
121
|
|
|
106
122
|
Then configure:
|
|
@@ -108,7 +124,7 @@ Then configure:
|
|
|
108
124
|
{
|
|
109
125
|
"mcpServers": {
|
|
110
126
|
"anki-mcp": {
|
|
111
|
-
"command": "anki-mcp-
|
|
127
|
+
"command": "@ankimcp/anki-mcp-server",
|
|
112
128
|
"args": ["--stdio"],
|
|
113
129
|
"env": {
|
|
114
130
|
"ANKI_CONNECT_URL": "http://localhost:8765"
|
|
@@ -143,31 +159,31 @@ Want to use Anki with ChatGPT or Claude.ai in your browser? This mode lets you c
|
|
|
143
159
|
|
|
144
160
|
```bash
|
|
145
161
|
# Quick start
|
|
146
|
-
npx anki-mcp-
|
|
162
|
+
npx @ankimcp/anki-mcp-server
|
|
147
163
|
|
|
148
164
|
# With ngrok tunnel (recommended for web-based AI)
|
|
149
|
-
npx anki-mcp-
|
|
165
|
+
npx @ankimcp/anki-mcp-server --ngrok
|
|
150
166
|
|
|
151
167
|
# With custom options
|
|
152
|
-
npx anki-mcp-
|
|
153
|
-
npx anki-mcp-
|
|
168
|
+
npx @ankimcp/anki-mcp-server --port 8080 --host 0.0.0.0
|
|
169
|
+
npx @ankimcp/anki-mcp-server --anki-connect http://localhost:8765
|
|
154
170
|
```
|
|
155
171
|
|
|
156
172
|
**Method 2: Using global installation**
|
|
157
173
|
|
|
158
174
|
```bash
|
|
159
175
|
# Install once
|
|
160
|
-
npm install -g anki-mcp-
|
|
176
|
+
npm install -g @ankimcp/anki-mcp-server
|
|
161
177
|
|
|
162
178
|
# Run the server
|
|
163
|
-
anki-mcp-
|
|
179
|
+
@ankimcp/anki-mcp-server
|
|
164
180
|
|
|
165
181
|
# With ngrok tunnel (recommended for web-based AI)
|
|
166
|
-
anki-mcp-
|
|
182
|
+
@ankimcp/anki-mcp-server --ngrok
|
|
167
183
|
|
|
168
184
|
# With custom options
|
|
169
|
-
anki-mcp-
|
|
170
|
-
anki-mcp-
|
|
185
|
+
@ankimcp/anki-mcp-server --port 8080 --host 0.0.0.0
|
|
186
|
+
@ankimcp/anki-mcp-server --anki-connect http://localhost:8765
|
|
171
187
|
```
|
|
172
188
|
|
|
173
189
|
**Method 3: Install from source (for development)**
|
|
@@ -181,7 +197,7 @@ npm run start:prod:http
|
|
|
181
197
|
**CLI Options:**
|
|
182
198
|
|
|
183
199
|
```bash
|
|
184
|
-
anki-mcp-
|
|
200
|
+
@ankimcp/anki-mcp-server [options]
|
|
185
201
|
|
|
186
202
|
Options:
|
|
187
203
|
--stdio Run in STDIO mode (for MCP clients)
|
|
@@ -192,17 +208,17 @@ Options:
|
|
|
192
208
|
--help Show help message
|
|
193
209
|
|
|
194
210
|
Usage with npx (no installation needed):
|
|
195
|
-
npx anki-mcp-
|
|
196
|
-
npx anki-mcp-
|
|
197
|
-
npx anki-mcp-
|
|
198
|
-
npx anki-mcp-
|
|
211
|
+
npx @ankimcp/anki-mcp-server # HTTP mode
|
|
212
|
+
npx @ankimcp/anki-mcp-server --port 8080 # Custom port
|
|
213
|
+
npx @ankimcp/anki-mcp-server --stdio # STDIO mode
|
|
214
|
+
npx @ankimcp/anki-mcp-server --ngrok # HTTP mode with ngrok tunnel
|
|
199
215
|
|
|
200
216
|
Usage with global installation:
|
|
201
|
-
npm install -g anki-mcp-
|
|
202
|
-
anki-mcp-
|
|
203
|
-
anki-mcp-
|
|
204
|
-
anki-mcp-
|
|
205
|
-
anki-mcp-
|
|
217
|
+
npm install -g @ankimcp/anki-mcp-server # Install once
|
|
218
|
+
@ankimcp/anki-mcp-server # HTTP mode
|
|
219
|
+
@ankimcp/anki-mcp-server --port 8080 # Custom port
|
|
220
|
+
@ankimcp/anki-mcp-server --stdio # STDIO mode
|
|
221
|
+
@ankimcp/anki-mcp-server --ngrok # HTTP mode with ngrok tunnel
|
|
206
222
|
```
|
|
207
223
|
|
|
208
224
|
**Using with ngrok:**
|
|
@@ -215,7 +231,7 @@ npm install -g ngrok
|
|
|
215
231
|
ngrok config add-authtoken <your-token> # Get token from https://dashboard.ngrok.com
|
|
216
232
|
|
|
217
233
|
# Start server with ngrok tunnel in one command:
|
|
218
|
-
anki-mcp-
|
|
234
|
+
@ankimcp/anki-mcp-server --ngrok
|
|
219
235
|
|
|
220
236
|
# The tunnel URL will be displayed in the startup banner
|
|
221
237
|
# Example output:
|
|
@@ -226,7 +242,7 @@ anki-mcp-http --ngrok
|
|
|
226
242
|
|
|
227
243
|
```bash
|
|
228
244
|
# Terminal 1: Start the server
|
|
229
|
-
anki-mcp-
|
|
245
|
+
@ankimcp/anki-mcp-server
|
|
230
246
|
|
|
231
247
|
# Terminal 2: Create tunnel
|
|
232
248
|
ngrok http 3000
|
|
@@ -239,7 +255,7 @@ ngrok http 3000
|
|
|
239
255
|
- ✅ One command instead of two terminals
|
|
240
256
|
- ✅ Automatic cleanup when you press Ctrl+C
|
|
241
257
|
- ✅ URL displayed directly in the startup banner
|
|
242
|
-
- ✅ Works with custom ports:
|
|
258
|
+
- ✅ Works with custom ports: `@ankimcp/anki-mcp-server --port 8080 --ngrok`
|
|
243
259
|
|
|
244
260
|
**Security note:** Anyone with your ngrok URL can access your Anki, so keep that URL private!
|
|
245
261
|
|
|
@@ -267,7 +283,7 @@ Add the following to your Claude Desktop config:
|
|
|
267
283
|
"mcpServers": {
|
|
268
284
|
"anki-mcp": {
|
|
269
285
|
"command": "node",
|
|
270
|
-
"args": ["/path/to/anki-mcp-
|
|
286
|
+
"args": ["/path/to/anki-mcp-server/dist/main-stdio.js"],
|
|
271
287
|
"env": {
|
|
272
288
|
"ANKI_CONNECT_URL": "http://localhost:8765"
|
|
273
289
|
}
|
|
@@ -276,7 +292,7 @@ Add the following to your Claude Desktop config:
|
|
|
276
292
|
}
|
|
277
293
|
```
|
|
278
294
|
|
|
279
|
-
Replace `/path/to/anki-mcp-
|
|
295
|
+
Replace `/path/to/anki-mcp-server` with your actual project path.
|
|
280
296
|
|
|
281
297
|
### Config File Locations
|
|
282
298
|
|
|
@@ -423,7 +439,7 @@ This command will:
|
|
|
423
439
|
3. Build the TypeScript project
|
|
424
440
|
4. Package `dist/` and `node_modules/` into an `.mcpb` file
|
|
425
441
|
|
|
426
|
-
The output file will be named based on the current version (e.g.,
|
|
442
|
+
The output file will be named based on the current version (e.g., `@ankimcp/anki-mcp-server-0.5.0.mcpb`) and can be distributed for one-click installation.
|
|
427
443
|
|
|
428
444
|
#### What Gets Bundled
|
|
429
445
|
|
|
@@ -536,7 +552,7 @@ Update your Claude Desktop config to enable debugging:
|
|
|
536
552
|
"command": "node",
|
|
537
553
|
"args": [
|
|
538
554
|
"--inspect=9229",
|
|
539
|
-
"
|
|
555
|
+
"<path_to_project>/anki-mcp-server/dist/main-stdio.js"
|
|
540
556
|
],
|
|
541
557
|
"env": {
|
|
542
558
|
"ANKI_CONNECT_URL": "http://localhost:8765"
|
|
@@ -625,10 +641,10 @@ Test the npm package locally before publishing:
|
|
|
625
641
|
|
|
626
642
|
```bash
|
|
627
643
|
# 1. Create local package
|
|
628
|
-
npm run pack:local # Builds and creates anki-mcp-
|
|
644
|
+
npm run pack:local # Builds and creates @ankimcp/anki-mcp-server-*.tgz
|
|
629
645
|
|
|
630
646
|
# 2. Install globally from local package
|
|
631
|
-
npm run install:local # Installs from
|
|
647
|
+
npm run install:local # Installs from ./@ankimcp/anki-mcp-server-*.tgz
|
|
632
648
|
|
|
633
649
|
# 3. Test the command
|
|
634
650
|
ankimcp # Runs HTTP server on port 3000
|
|
@@ -694,7 +710,7 @@ If you're exploring Anki MCP integrations, here are other projects in this space
|
|
|
694
710
|
- **Architecture**: Procedural code structure with all tools in one file
|
|
695
711
|
- **Good for**: Simple use cases, minimal dependencies
|
|
696
712
|
|
|
697
|
-
**Why this project
|
|
713
|
+
**Why this project differs:**
|
|
698
714
|
- **Enterprise-grade architecture**: Built on NestJS with dependency injection
|
|
699
715
|
- **Modular design**: Each tool is a separate class with clear separation of concerns
|
|
700
716
|
- **Maintainability**: Easy to extend with new features without touching existing code
|
|
@@ -714,4 +730,27 @@ If you're exploring Anki MCP integrations, here are other projects in this space
|
|
|
714
730
|
- [Building Desktop Extensions (Anthropic Blog)](https://www.anthropic.com/engineering/desktop-extensions)
|
|
715
731
|
- [MCP Servers Repository](https://github.com/modelcontextprotocol/servers)
|
|
716
732
|
- [NestJS Documentation](https://docs.nestjs.com)
|
|
717
|
-
- [Anki Official Website](https://apps.ankiweb.net/)
|
|
733
|
+
- [Anki Official Website](https://apps.ankiweb.net/)
|
|
734
|
+
|
|
735
|
+
## License & Attribution
|
|
736
|
+
|
|
737
|
+
This project is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).
|
|
738
|
+
|
|
739
|
+
### Why AGPL-3.0?
|
|
740
|
+
|
|
741
|
+
This license was chosen to maintain compatibility with Anki's AGPL-3.0 license for potential future integration scenarios.
|
|
742
|
+
|
|
743
|
+
**What this means:**
|
|
744
|
+
- **Personal use**: Use the software freely
|
|
745
|
+
- **Running as a service for others**: You must provide source code access (AGPL Section 13)
|
|
746
|
+
- **Modifying and distributing**: Share your improvements under AGPL-3.0-or-later
|
|
747
|
+
|
|
748
|
+
For complete license terms, see the [LICENSE](LICENSE) file.
|
|
749
|
+
|
|
750
|
+
### Third-Party Attributions
|
|
751
|
+
|
|
752
|
+
- **Anki®** is a registered trademark of Ankitects Pty Ltd. This project is an unofficial third-party tool and is not affiliated with, endorsed by, or sponsored by Ankitects Pty Ltd. The Anki logo is used under the alternative license for referencing Anki with a link to [https://apps.ankiweb.net](https://apps.ankiweb.net). For the official Anki application, visit [https://apps.ankiweb.net](https://apps.ankiweb.net).
|
|
753
|
+
|
|
754
|
+
- **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).
|
|
755
|
+
|
|
756
|
+
- 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.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ConfigService } from
|
|
2
|
-
import { IAnkiConfig } from
|
|
1
|
+
import { ConfigService } from "@nestjs/config";
|
|
2
|
+
import { IAnkiConfig } from "./mcp/config/anki-config.interface";
|
|
3
3
|
export declare class AnkiConfigService implements IAnkiConfig {
|
|
4
4
|
private configService;
|
|
5
5
|
constructor(configService: ConfigService);
|
|
@@ -19,18 +19,18 @@ let AnkiConfigService = class AnkiConfigService {
|
|
|
19
19
|
this.configService = configService;
|
|
20
20
|
}
|
|
21
21
|
get ankiConnectUrl() {
|
|
22
|
-
return this.configService.get(
|
|
22
|
+
return this.configService.get("ANKI_CONNECT_URL", "http://localhost:8765");
|
|
23
23
|
}
|
|
24
24
|
get ankiConnectApiVersion() {
|
|
25
|
-
const version = this.configService.get(
|
|
25
|
+
const version = this.configService.get("ANKI_CONNECT_API_VERSION", "6");
|
|
26
26
|
return parseInt(version, 10);
|
|
27
27
|
}
|
|
28
28
|
get ankiConnectApiKey() {
|
|
29
|
-
const apiKey = this.configService.get(
|
|
29
|
+
const apiKey = this.configService.get("ANKI_CONNECT_API_KEY");
|
|
30
30
|
return (0, mcpb_workarounds_1.sanitizeMcpbConfigValue)(apiKey);
|
|
31
31
|
}
|
|
32
32
|
get ankiConnectTimeout() {
|
|
33
|
-
const timeout = this.configService.get(
|
|
33
|
+
const timeout = this.configService.get("ANKI_CONNECT_TIMEOUT", "5000");
|
|
34
34
|
return parseInt(timeout, 10);
|
|
35
35
|
}
|
|
36
36
|
};
|
package/dist/app.module.d.ts
CHANGED
package/dist/app.module.js
CHANGED
|
@@ -22,11 +22,11 @@ let AppModule = AppModule_1 = class AppModule {
|
|
|
22
22
|
config_1.ConfigModule.forRoot({
|
|
23
23
|
isGlobal: true,
|
|
24
24
|
cache: true,
|
|
25
|
-
envFilePath: [
|
|
25
|
+
envFilePath: [".env.local", ".env"],
|
|
26
26
|
}),
|
|
27
27
|
mcp_nest_1.McpModule.forRoot({
|
|
28
|
-
name: process.env.MCP_SERVER_NAME ||
|
|
29
|
-
version: process.env.MCP_SERVER_VERSION ||
|
|
28
|
+
name: process.env.MCP_SERVER_NAME || "anki-mcp-server",
|
|
29
|
+
version: process.env.MCP_SERVER_VERSION || "1.0.0",
|
|
30
30
|
transport: mcp_nest_1.McpTransportType.STDIO,
|
|
31
31
|
}),
|
|
32
32
|
essential_1.McpPrimitivesAnkiEssentialModule.forRoot({
|
|
@@ -52,13 +52,13 @@ let AppModule = AppModule_1 = class AppModule {
|
|
|
52
52
|
config_1.ConfigModule.forRoot({
|
|
53
53
|
isGlobal: true,
|
|
54
54
|
cache: true,
|
|
55
|
-
envFilePath: [
|
|
55
|
+
envFilePath: [".env.local", ".env"],
|
|
56
56
|
}),
|
|
57
57
|
mcp_nest_1.McpModule.forRoot({
|
|
58
|
-
name: process.env.MCP_SERVER_NAME ||
|
|
59
|
-
version: process.env.MCP_SERVER_VERSION ||
|
|
58
|
+
name: process.env.MCP_SERVER_NAME || "anki-mcp-server",
|
|
59
|
+
version: process.env.MCP_SERVER_VERSION || "1.0.0",
|
|
60
60
|
transport: mcp_nest_1.McpTransportType.STREAMABLE_HTTP,
|
|
61
|
-
mcpEndpoint:
|
|
61
|
+
mcpEndpoint: "/",
|
|
62
62
|
}),
|
|
63
63
|
essential_1.McpPrimitivesAnkiEssentialModule.forRoot({
|
|
64
64
|
ankiConfigProvider: {
|
package/dist/app.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,2CAA8C;AAC9C,8CAA8D;AAC9D,0DAGoC;AACpC,8CAAkE;AAClE,+DAA0D;AAGnD,IAAM,SAAS,iBAAf,MAAM,SAAS;IAIpB,MAAM,CAAC,QAAQ;QACb,OAAO;YACL,MAAM,EAAE,WAAS;YACjB,OAAO,EAAE;gBAEP,qBAAY,CAAC,OAAO,CAAC;oBACnB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,IAAI;oBACX,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;iBACpC,CAAC;gBAGF,oBAAS,CAAC,OAAO,CAAC;oBAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,
|
|
1
|
+
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,2CAA8C;AAC9C,8CAA8D;AAC9D,0DAGoC;AACpC,8CAAkE;AAClE,+DAA0D;AAGnD,IAAM,SAAS,iBAAf,MAAM,SAAS;IAIpB,MAAM,CAAC,QAAQ;QACb,OAAO;YACL,MAAM,EAAE,WAAS;YACjB,OAAO,EAAE;gBAEP,qBAAY,CAAC,OAAO,CAAC;oBACnB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,IAAI;oBACX,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;iBACpC,CAAC;gBAGF,oBAAS,CAAC,OAAO,CAAC;oBAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,iBAAiB;oBACtD,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO;oBAClD,SAAS,EAAE,2BAAgB,CAAC,KAAK;iBAClC,CAAC;gBAGF,4CAAgC,CAAC,OAAO,CAAC;oBACvC,kBAAkB,EAAE;wBAClB,OAAO,EAAE,uBAAW;wBACpB,QAAQ,EAAE,uCAAiB;qBAC5B;iBACF,CAAC;gBAGF,gCAA0B,CAAC,OAAO,CAAC;oBACjC,kBAAkB,EAAE;wBAClB,OAAO,EAAE,uBAAW;wBACpB,QAAQ,EAAE,uCAAiB;qBAC5B;iBACF,CAAC;aACH;YACD,SAAS,EAAE,CAAC,uCAAiB,CAAC;SAC/B,CAAC;IACJ,CAAC;IAKD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,MAAM,EAAE,WAAS;YACjB,OAAO,EAAE;gBAEP,qBAAY,CAAC,OAAO,CAAC;oBACnB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,IAAI;oBACX,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;iBACpC,CAAC;gBAGF,oBAAS,CAAC,OAAO,CAAC;oBAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,iBAAiB;oBACtD,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO;oBAClD,SAAS,EAAE,2BAAgB,CAAC,eAAe;oBAC3C,WAAW,EAAE,GAAG;iBACjB,CAAC;gBAGF,4CAAgC,CAAC,OAAO,CAAC;oBACvC,kBAAkB,EAAE;wBAClB,OAAO,EAAE,uBAAW;wBACpB,QAAQ,EAAE,uCAAiB;qBAC5B;iBACF,CAAC;gBAGF,gCAA0B,CAAC,OAAO,CAAC;oBACjC,kBAAkB,EAAE;wBAClB,OAAO,EAAE,uBAAW;wBACpB,QAAQ,EAAE,uCAAiB;qBAC5B;iBACF,CAAC;aACH;YACD,SAAS,EAAE,CAAC,uCAAiB,CAAC;SAC/B,CAAC;IACJ,CAAC;CACF,CAAA;AAnFY,8BAAS;oBAAT,SAAS;IADrB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,SAAS,CAmFrB"}
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { LoggerService } from
|
|
1
|
+
import { LoggerService } from "@nestjs/common";
|
|
2
2
|
export declare function createPinoLogger(destination: 1 | 2): import("pino").Logger<never, boolean>;
|
|
3
3
|
export declare function createLoggerService(pinoLogger: any): LoggerService;
|
package/dist/bootstrap.js
CHANGED
|
@@ -5,14 +5,14 @@ exports.createLoggerService = createLoggerService;
|
|
|
5
5
|
const pino_1 = require("pino");
|
|
6
6
|
function createPinoLogger(destination) {
|
|
7
7
|
return (0, pino_1.pino)({
|
|
8
|
-
level: process.env.LOG_LEVEL ||
|
|
8
|
+
level: process.env.LOG_LEVEL || "info",
|
|
9
9
|
transport: {
|
|
10
|
-
target:
|
|
10
|
+
target: "pino-pretty",
|
|
11
11
|
options: {
|
|
12
12
|
destination,
|
|
13
13
|
colorize: true,
|
|
14
|
-
translateTime:
|
|
15
|
-
ignore:
|
|
14
|
+
translateTime: "HH:MM:ss Z",
|
|
15
|
+
ignore: "pid,hostname",
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
});
|
package/dist/cli.js
CHANGED
|
@@ -12,10 +12,10 @@ const path_1 = require("path");
|
|
|
12
12
|
const update_notifier_1 = __importDefault(require("update-notifier"));
|
|
13
13
|
function getPackageJson() {
|
|
14
14
|
try {
|
|
15
|
-
return JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname,
|
|
15
|
+
return JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, "../package.json"), "utf-8"));
|
|
16
16
|
}
|
|
17
17
|
catch {
|
|
18
|
-
return { version:
|
|
18
|
+
return { version: "0.0.0", name: "anki-mcp-http" };
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
function getVersion() {
|
|
@@ -27,15 +27,15 @@ function checkForUpdates() {
|
|
|
27
27
|
function parseCliArgs() {
|
|
28
28
|
const program = new commander_1.Command();
|
|
29
29
|
program
|
|
30
|
-
.name(
|
|
31
|
-
.description(
|
|
30
|
+
.name("ankimcp")
|
|
31
|
+
.description("AnkiMCP Server - Model Context Protocol server for Anki")
|
|
32
32
|
.version(getVersion())
|
|
33
|
-
.option(
|
|
34
|
-
.option(
|
|
35
|
-
.option(
|
|
36
|
-
.option(
|
|
37
|
-
.option(
|
|
38
|
-
.addHelpText(
|
|
33
|
+
.option("--stdio", "Run in STDIO mode (for MCP clients like Cursor, Cline, Zed)")
|
|
34
|
+
.option("-p, --port <number>", "Port to listen on (HTTP mode)", "3000")
|
|
35
|
+
.option("-h, --host <address>", "Host to bind to (HTTP mode)", "127.0.0.1")
|
|
36
|
+
.option("-a, --anki-connect <url>", "AnkiConnect URL", "http://localhost:8765")
|
|
37
|
+
.option("--ngrok", "Start ngrok tunnel (requires global ngrok installation)")
|
|
38
|
+
.addHelpText("after", `
|
|
39
39
|
Transport Modes:
|
|
40
40
|
HTTP Mode (default): For web-based AI assistants (ChatGPT, Claude.ai)
|
|
41
41
|
STDIO Mode: For desktop MCP clients (Cursor, Cline, Zed)
|
|
@@ -83,20 +83,20 @@ function displayStartupBanner(options, ngrokUrl) {
|
|
|
83
83
|
const version = getVersion();
|
|
84
84
|
const title = `AnkiMCP HTTP Server v${version}`;
|
|
85
85
|
const padding = Math.floor((64 - title.length) / 2);
|
|
86
|
-
const paddedTitle =
|
|
86
|
+
const paddedTitle = " ".repeat(padding) + title + " ".repeat(64 - padding - title.length);
|
|
87
87
|
console.log(`
|
|
88
88
|
╔════════════════════════════════════════════════════════════════╗
|
|
89
89
|
║${paddedTitle}║
|
|
90
90
|
╚════════════════════════════════════════════════════════════════╝
|
|
91
91
|
|
|
92
92
|
🚀 Server running on: http://${options.host}:${options.port}
|
|
93
|
-
🔌 AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` :
|
|
93
|
+
🔌 AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` : ""}
|
|
94
94
|
|
|
95
95
|
Configuration:
|
|
96
96
|
• Port: ${options.port} (override: --port 8080)
|
|
97
97
|
• Host: ${options.host} (override: --host 0.0.0.0)
|
|
98
98
|
• AnkiConnect: ${options.ankiConnect}
|
|
99
|
-
(override: --anki-connect http://localhost:8765)${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` :
|
|
99
|
+
(override: --anki-connect http://localhost:8765)${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` : ""}
|
|
100
100
|
${!ngrokUrl
|
|
101
101
|
? `
|
|
102
102
|
Usage with ngrok:
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AA0BA,0CAEC;AAED,oCAuEC;AAED,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AA0BA,0CAEC;AAED,oCAuEC;AAED,oDAsCC;AA7ID,yCAAoC;AACpC,2BAAkC;AAClC,+BAA4B;AAC5B,sEAA6C;AAS7C,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CACf,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC1D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,cAAc,EAAE,CAAC,OAAO,CAAC;AAClC,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAA,yBAAc,EAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,SAAgB,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,yDAAyD,CAAC;SACtE,OAAO,CAAC,UAAU,EAAE,CAAC;SACrB,MAAM,CACL,SAAS,EACT,6DAA6D,CAC9D;SACA,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,CAAC;SACtE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,EAAE,WAAW,CAAC;SAC1E,MAAM,CACL,0BAA0B,EAC1B,iBAAiB,EACjB,uBAAuB,CACxB;SACA,MAAM,CACL,SAAS,EACT,yDAAyD,CAC1D;SACA,WAAW,CACV,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCL,CACI,CAAC;IAEJ,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAc,CAAC;IAE3C,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC3C,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;KAC9B,CAAC;AACJ,CAAC;AAED,SAAgB,oBAAoB,CAClC,OAAmB,EACnB,QAAiB;IAEjB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,wBAAwB,OAAO,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,WAAW,GACf,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAExE,OAAO,CAAC,GAAG,CAAC;;GAEX,WAAW;;;+BAGiB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI;wBACnC,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;;;0BAGzE,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,WAAW;0EAC6B,QAAQ,CAAC,CAAC,CAAC,6BAA6B,QAAQ,iDAAiD,CAAC,CAAC,CAAC,EAAE;EAE9K,CAAC,QAAQ;QACP,CAAC,CAAC;;;;;CAKL;QACG,CAAC,CAAC;;IAEF,QAAQ;CAEZ;;CAEC,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -14,9 +14,9 @@ const common_1 = require("@nestjs/common");
|
|
|
14
14
|
let OriginValidationGuard = class OriginValidationGuard {
|
|
15
15
|
allowedOrigins;
|
|
16
16
|
constructor() {
|
|
17
|
-
const defaultOrigins =
|
|
17
|
+
const defaultOrigins = "http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*";
|
|
18
18
|
const originsEnv = process.env.ALLOWED_ORIGINS || defaultOrigins;
|
|
19
|
-
this.allowedOrigins = originsEnv.split(
|
|
19
|
+
this.allowedOrigins = originsEnv.split(",").map((o) => o.trim());
|
|
20
20
|
}
|
|
21
21
|
canActivate(context) {
|
|
22
22
|
const request = context.switchToHttp().getRequest();
|
|
@@ -34,10 +34,10 @@ let OriginValidationGuard = class OriginValidationGuard {
|
|
|
34
34
|
if (origin === pattern) {
|
|
35
35
|
return true;
|
|
36
36
|
}
|
|
37
|
-
if (pattern.includes(
|
|
37
|
+
if (pattern.includes("*")) {
|
|
38
38
|
const regexPattern = pattern
|
|
39
|
-
.replace(/\./g,
|
|
40
|
-
.replace(/\*/g,
|
|
39
|
+
.replace(/\./g, "\\.")
|
|
40
|
+
.replace(/\*/g, ".*");
|
|
41
41
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
42
42
|
return regex.test(origin);
|
|
43
43
|
}
|
package/dist/main-http.js
CHANGED
|
@@ -28,15 +28,15 @@ async function bootstrap() {
|
|
|
28
28
|
ngrokUrl = tunnelInfo.publicUrl;
|
|
29
29
|
}
|
|
30
30
|
catch (err) {
|
|
31
|
-
console.error(
|
|
31
|
+
console.error("\n❌ Failed to start ngrok:");
|
|
32
32
|
console.error(err instanceof Error ? err.message : String(err));
|
|
33
|
-
console.error(
|
|
33
|
+
console.error("\nServer is still running locally without tunnel.\n");
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
(0, cli_1.displayStartupBanner)(options, ngrokUrl);
|
|
37
37
|
}
|
|
38
38
|
bootstrap().catch((err) => {
|
|
39
|
-
console.error(
|
|
39
|
+
console.error("Failed to start MCP HTTP server:", err);
|
|
40
40
|
process.exit(1);
|
|
41
41
|
});
|
|
42
42
|
//# sourceMappingURL=main-http.js.map
|
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,+
|
|
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;IAGnD,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
|
@@ -10,11 +10,11 @@ async function bootstrap() {
|
|
|
10
10
|
logger: loggerService,
|
|
11
11
|
bufferLogs: true,
|
|
12
12
|
});
|
|
13
|
-
pinoLogger.info(
|
|
13
|
+
pinoLogger.info("MCP STDIO server started successfully");
|
|
14
14
|
await new Promise(() => { });
|
|
15
15
|
}
|
|
16
16
|
bootstrap().catch((err) => {
|
|
17
|
-
console.error(
|
|
17
|
+
console.error("Failed to start MCP STDIO server:", err);
|
|
18
18
|
process.exit(1);
|
|
19
19
|
});
|
|
20
20
|
//# sourceMappingURL=main-stdio.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAnkiConfig } from
|
|
1
|
+
import type { IAnkiConfig } from "../config/anki-config.interface";
|
|
2
2
|
export declare class AnkiConnectError extends Error {
|
|
3
3
|
readonly action?: string | undefined;
|
|
4
4
|
readonly originalError?: string | undefined;
|
|
@@ -57,7 +57,7 @@ class AnkiConnectError extends Error {
|
|
|
57
57
|
super(message);
|
|
58
58
|
this.action = action;
|
|
59
59
|
this.originalError = originalError;
|
|
60
|
-
this.name =
|
|
60
|
+
this.name = "AnkiConnectError";
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
exports.AnkiConnectError = AnkiConnectError;
|
|
@@ -75,11 +75,11 @@ let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
|
75
75
|
prefixUrl: config.ankiConnectUrl,
|
|
76
76
|
timeout: config.ankiConnectTimeout,
|
|
77
77
|
headers: {
|
|
78
|
-
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
79
|
},
|
|
80
80
|
retry: {
|
|
81
81
|
limit: 2,
|
|
82
|
-
methods: [
|
|
82
|
+
methods: ["POST"],
|
|
83
83
|
statusCodes: [408, 413, 429, 500, 502, 503, 504],
|
|
84
84
|
backoffLimit: 3000,
|
|
85
85
|
},
|
|
@@ -109,7 +109,7 @@ let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
|
109
109
|
try {
|
|
110
110
|
this.logger.log(`Invoking AnkiConnect action: ${action}`);
|
|
111
111
|
const response = await this.client
|
|
112
|
-
.post(
|
|
112
|
+
.post("", {
|
|
113
113
|
json: request,
|
|
114
114
|
})
|
|
115
115
|
.json();
|
|
@@ -122,12 +122,12 @@ let AnkiConnectClient = AnkiConnectClient_1 = class AnkiConnectClient {
|
|
|
122
122
|
catch (error) {
|
|
123
123
|
if (error instanceof ky_1.HTTPError) {
|
|
124
124
|
if (error.response.status === 403) {
|
|
125
|
-
throw new AnkiConnectError(
|
|
125
|
+
throw new AnkiConnectError("Permission denied. Please check AnkiConnect configuration and API key.", action);
|
|
126
126
|
}
|
|
127
127
|
throw new AnkiConnectError(`HTTP error ${error.response.status}: ${error.message}`, action);
|
|
128
128
|
}
|
|
129
|
-
if (error instanceof Error && error.message.includes(
|
|
130
|
-
throw new AnkiConnectError(
|
|
129
|
+
if (error instanceof Error && error.message.includes("fetch")) {
|
|
130
|
+
throw new AnkiConnectError("Cannot connect to Anki. Please ensure Anki is running and AnkiConnect plugin is installed.", action);
|
|
131
131
|
}
|
|
132
132
|
if (error instanceof AnkiConnectError) {
|
|
133
133
|
throw error;
|