anki-connect-server 0.1.0__tar.gz
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.
- anki_connect_server-0.1.0/PKG-INFO +375 -0
- anki_connect_server-0.1.0/README.md +361 -0
- anki_connect_server-0.1.0/pyproject.toml +40 -0
- anki_connect_server-0.1.0/setup.cfg +4 -0
- anki_connect_server-0.1.0/src/anki_connect_server/__init__.py +1 -0
- anki_connect_server-0.1.0/src/anki_connect_server/anki_wrapper.py +485 -0
- anki_connect_server-0.1.0/src/anki_connect_server/api.py +58 -0
- anki_connect_server-0.1.0/src/anki_connect_server/config.py +27 -0
- anki_connect_server-0.1.0/src/anki_connect_server/handlers.py +368 -0
- anki_connect_server-0.1.0/src/anki_connect_server/mcp_server.py +256 -0
- anki_connect_server-0.1.0/src/anki_connect_server/wrapper.py +23 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/PKG-INFO +375 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/SOURCES.txt +21 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/dependency_links.txt +1 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/entry_points.txt +3 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/requires.txt +7 -0
- anki_connect_server-0.1.0/src/anki_connect_server.egg-info/top_level.txt +1 -0
- anki_connect_server-0.1.0/tests/test_api.py +316 -0
- anki_connect_server-0.1.0/tests/test_config.py +71 -0
- anki_connect_server-0.1.0/tests/test_handlers.py +417 -0
- anki_connect_server-0.1.0/tests/test_integration.py +122 -0
- anki_connect_server-0.1.0/tests/test_mcp_server.py +218 -0
- anki_connect_server-0.1.0/tests/test_sync_server.py +189 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anki-connect-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Headless AnkiConnect-compatible REST API server with AnkiWeb sync
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: fastapi>=0.100.0
|
|
8
|
+
Requires-Dist: uvicorn[standard]>=0.23.0
|
|
9
|
+
Requires-Dist: anki>=24.6
|
|
10
|
+
Requires-Dist: python-multipart>=0.0.6
|
|
11
|
+
Requires-Dist: pydantic>=2.0
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
13
|
+
Requires-Dist: fastmcp>=3.1.0
|
|
14
|
+
|
|
15
|
+
# AnkiConnect Server
|
|
16
|
+
|
|
17
|
+
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
18
|
+
[](https://www.python.org/downloads/)
|
|
19
|
+
[](https://fastapi.tiangolo.com/)
|
|
20
|
+
|
|
21
|
+
Headless AnkiConnect-compatible REST API server with AnkiWeb sync support and MCP server integration.
|
|
22
|
+
|
|
23
|
+
## ❓ Problem & Solution
|
|
24
|
+
|
|
25
|
+
**Problem:** Traditional AnkiConnect requires the Anki desktop app running, works only on localhost, and can't be deployed to servers.
|
|
26
|
+
|
|
27
|
+
**Solution:** This headless server provides direct collection access without Anki desktop, supports remote deployment, and includes automatic AnkiWeb sync.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## ✨ Features
|
|
31
|
+
|
|
32
|
+
- **Server Deployment** - Run on VPS, Raspberry Pi, or cloud without GUI
|
|
33
|
+
- **AI Integration** - MCP server for AI assistant access to your cards
|
|
34
|
+
- **Full AnkiConnect API Compatibility** - Version 6 API with all standard actions
|
|
35
|
+
- **Headless Operation** - No Qt/GUI required, perfect for servers and containers
|
|
36
|
+
- **AnkiWeb Sync** - Automatic synchronization with AnkiWeb (optional)
|
|
37
|
+
- **Custom Sync Server** - Support for self-hosted Anki sync servers
|
|
38
|
+
- **MCP Server** - Model Context Protocol integration for AI assistants
|
|
39
|
+
|
|
40
|
+
## 📋 Table of Contents
|
|
41
|
+
|
|
42
|
+
- [Requirements](#-requirements)
|
|
43
|
+
- [Installation](#-installation)
|
|
44
|
+
- [Configuration](#-configuration)
|
|
45
|
+
- [Usage](#-usage)
|
|
46
|
+
- [API Reference](#-api-reference)
|
|
47
|
+
- [MCP Server](#-mcp-server)
|
|
48
|
+
- [Docker](#-docker)
|
|
49
|
+
|
|
50
|
+
## 🔧 Requirements
|
|
51
|
+
|
|
52
|
+
- Python 3.12+
|
|
53
|
+
- [uv](https://github.com/astral-sh/uv) package manager
|
|
54
|
+
- Anki collection file (`.anki21`)
|
|
55
|
+
|
|
56
|
+
## 📦 Installation
|
|
57
|
+
|
|
58
|
+
### Quick Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Clone the repository
|
|
62
|
+
git clone https://github.com/glechic/anki-connect-server.git
|
|
63
|
+
cd anki-connect-server
|
|
64
|
+
|
|
65
|
+
# Install with uv
|
|
66
|
+
uv pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### From PyPI (coming soon)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install anki-connect-server
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## ⚙️ Configuration
|
|
76
|
+
|
|
77
|
+
Set environment variables before running the server:
|
|
78
|
+
|
|
79
|
+
| Variable | Required | Default | Description |
|
|
80
|
+
|----------|----------|---------|-------------|
|
|
81
|
+
| `ANKI_COLLECTION_PATH` | **Yes** | - | Path to your `.anki21` collection file |
|
|
82
|
+
| `ANKICONNECT_PORT` | No | `8765` | Server port |
|
|
83
|
+
| `ANKICONNECT_BIND` | No | `127.0.0.1` | Bind address (use `0.0.0.0` for external access) |
|
|
84
|
+
| `ANKICONNECT_ANKIWEB_USER` | No | - | AnkiWeb username (required for sync) |
|
|
85
|
+
| `ANKICONNECT_ANKIWEB_PASS` | No | - | AnkiWeb password (required for sync) |
|
|
86
|
+
| `ANKIWEB_URL` | No | - | Custom sync server URL (optional) |
|
|
87
|
+
| `ANKICONNECT_FULL_UPLOAD` | No | `false` | Allow full upload on sync conflict |
|
|
88
|
+
|
|
89
|
+
### Example `.env` File
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Required: Path to your Anki collection
|
|
93
|
+
ANKI_COLLECTION_PATH=/path/to/collection.anki21
|
|
94
|
+
|
|
95
|
+
# Optional: Server configuration
|
|
96
|
+
ANKICONNECT_PORT=8765
|
|
97
|
+
ANKICONNECT_BIND=127.0.0.1
|
|
98
|
+
|
|
99
|
+
# Optional: AnkiWeb sync credentials
|
|
100
|
+
ANKICONNECT_ANKIWEB_USER=your@email.com
|
|
101
|
+
ANKICONNECT_ANKIWEB_PASS=your_password
|
|
102
|
+
|
|
103
|
+
# Optional: Custom sync server
|
|
104
|
+
# ANKIWEB_URL=https://your-sync-server.com
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> ⚠️ **Security Warning**: Store credentials securely. Never commit `.env` files to version control.
|
|
108
|
+
|
|
109
|
+
## 🚀 Usage
|
|
110
|
+
|
|
111
|
+
### Development Server
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run uvicorn anki_connect_server.api:app --reload --port 8765
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Production Server
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run uvicorn anki_connect_server.api:app --host 0.0.0.0 --port 8765
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Using the CLI
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Start the API server
|
|
127
|
+
uv run server
|
|
128
|
+
|
|
129
|
+
# Start the MCP server
|
|
130
|
+
uv run mcp-server
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 📚 API Reference
|
|
134
|
+
|
|
135
|
+
The server exposes a single POST endpoint at `/api` that accepts AnkiConnect-style JSON requests.
|
|
136
|
+
|
|
137
|
+
### Request Format
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"action": "actionName",
|
|
142
|
+
"version": 6,
|
|
143
|
+
"params": {
|
|
144
|
+
"param1": "value1",
|
|
145
|
+
"param2": "value2"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Response Format
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"result": <action_result>,
|
|
155
|
+
"error": null
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
On error:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"result": null,
|
|
164
|
+
"error": "Error message"
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Examples
|
|
169
|
+
|
|
170
|
+
#### Get Deck Names
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
curl -X POST http://localhost:8765/api \
|
|
174
|
+
-H "Content-Type: application/json" \
|
|
175
|
+
-d '{"action": "deckNames", "version": 6}'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Create a Deck
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
curl -X POST http://localhost:8765/api \
|
|
182
|
+
-H "Content-Type: application/json" \
|
|
183
|
+
-d '{"action": "createDeck", "version": 6, "params": {"deck": "My New Deck"}}'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Add a Note
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
curl -X POST http://localhost:8765/api \
|
|
190
|
+
-H "Content-Type: application/json" \
|
|
191
|
+
-d '{
|
|
192
|
+
"action": "addNote",
|
|
193
|
+
"version": 6,
|
|
194
|
+
"params": {
|
|
195
|
+
"note": {
|
|
196
|
+
"deckName": "Default",
|
|
197
|
+
"modelName": "Basic",
|
|
198
|
+
"fields": {
|
|
199
|
+
"Front": "Hello",
|
|
200
|
+
"Back": "World"
|
|
201
|
+
},
|
|
202
|
+
"tags": ["api"]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Sync with AnkiWeb
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
curl -X POST http://localhost:8765/api \
|
|
212
|
+
-H "Content-Type: application/json" \
|
|
213
|
+
-d '{"action": "sync", "version": 6}'
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Batch Multiple Actions
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
curl -X POST http://localhost:8765/api \
|
|
220
|
+
-H "Content-Type: application/json" \
|
|
221
|
+
-d '{
|
|
222
|
+
"action": "multi",
|
|
223
|
+
"version": 6,
|
|
224
|
+
"params": {
|
|
225
|
+
"actions": [
|
|
226
|
+
{"action": "deckNames", "params": {}},
|
|
227
|
+
{"action": "modelNames", "params": {}}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
}'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Supported Actions
|
|
234
|
+
|
|
235
|
+
#### Misc
|
|
236
|
+
- `version` - Get API version
|
|
237
|
+
- `sync` - Sync with AnkiWeb
|
|
238
|
+
- `syncStatus` - Get sync status
|
|
239
|
+
- `syncMedia` - Sync media only
|
|
240
|
+
- `multi` - Execute multiple actions
|
|
241
|
+
- `importPackage` - Import `.apkg` file
|
|
242
|
+
- `exportPackage` - Export deck to `.apkg`
|
|
243
|
+
|
|
244
|
+
#### Decks
|
|
245
|
+
- `deckNames` - Get all deck names
|
|
246
|
+
- `deckNamesAndIds` - Get deck names with IDs
|
|
247
|
+
- `createDeck` - Create a new deck
|
|
248
|
+
- `deleteDecks` - Delete decks
|
|
249
|
+
- `changeDeck` - Move cards to different deck
|
|
250
|
+
- `getDecks` - Get decks for cards
|
|
251
|
+
- `getDeckConfig` - Get deck configuration
|
|
252
|
+
- `saveDeckConfig` - Save deck configuration
|
|
253
|
+
- `setDeckConfigId` - Assign config to decks
|
|
254
|
+
- `cloneDeckConfigId` - Clone deck config
|
|
255
|
+
- `removeDeckConfigId` - Remove deck config
|
|
256
|
+
|
|
257
|
+
#### Models
|
|
258
|
+
- `modelNames` - Get all model names
|
|
259
|
+
- `modelNamesAndIds` - Get model names with IDs
|
|
260
|
+
- `modelFieldNames` - Get field names for model
|
|
261
|
+
- `modelFieldsOnTemplates` - Get fields used in templates
|
|
262
|
+
- `createModel` - Create a new note model
|
|
263
|
+
- `modelTemplates` - Get card templates
|
|
264
|
+
- `modelStyling` - Get CSS styling
|
|
265
|
+
- `updateModelTemplates` - Update templates
|
|
266
|
+
- `updateModelStyling` - Update CSS
|
|
267
|
+
|
|
268
|
+
#### Notes
|
|
269
|
+
- `addNote` - Add a single note
|
|
270
|
+
- `addNotes` - Add multiple notes
|
|
271
|
+
- `canAddNotes` - Check if notes can be added
|
|
272
|
+
- `updateNoteFields` - Update note fields
|
|
273
|
+
- `findNotes` - Search for notes
|
|
274
|
+
- `notesInfo` - Get note details
|
|
275
|
+
- `deleteNotes` - Delete notes
|
|
276
|
+
- `addTags` - Add tags to notes
|
|
277
|
+
- `removeTags` - Remove tags from notes
|
|
278
|
+
- `getTags` - Get all tags
|
|
279
|
+
|
|
280
|
+
#### Cards
|
|
281
|
+
- `findCards` - Search for cards
|
|
282
|
+
- `cardsToNotes` - Get note IDs from card IDs
|
|
283
|
+
- `cardsInfo` - Get card details
|
|
284
|
+
- `suspend` - Suspend cards
|
|
285
|
+
- `unsuspend` - Unsuspend cards
|
|
286
|
+
- `areSuspended` - Check if cards are suspended
|
|
287
|
+
- `areDue` - Check if cards are due
|
|
288
|
+
- `getIntervals` - Get card intervals
|
|
289
|
+
|
|
290
|
+
#### Media
|
|
291
|
+
- `getMediaDirPath` - Get media directory path
|
|
292
|
+
- `storeMediaFile` - Store a media file (base64)
|
|
293
|
+
- `retrieveMediaFile` - Retrieve a media file
|
|
294
|
+
- `deleteMediaFile` - Delete a media file
|
|
295
|
+
|
|
296
|
+
## 🤖 MCP Server
|
|
297
|
+
|
|
298
|
+
The server includes a Model Context Protocol (MCP) integration for AI assistants.
|
|
299
|
+
|
|
300
|
+
### Starting the MCP Server
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
uv run mcp-server
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Available MCP Tools
|
|
307
|
+
|
|
308
|
+
- `get_deck_names` - Get all deck names
|
|
309
|
+
- `get_deck_names_and_ids` - Get decks with IDs
|
|
310
|
+
- `create_deck` - Create a new deck
|
|
311
|
+
- `delete_decks` - Delete decks
|
|
312
|
+
- `get_model_names` - Get all model names
|
|
313
|
+
- `get_model_field_names` - Get fields for a model
|
|
314
|
+
- `add_note` - Add a new note
|
|
315
|
+
- `find_notes` - Search for notes
|
|
316
|
+
- `get_notes_info` - Get note details
|
|
317
|
+
- `delete_notes` - Delete notes
|
|
318
|
+
- `find_cards` - Search for cards
|
|
319
|
+
- `get_cards_info` - Get card details
|
|
320
|
+
- `suspend_cards` / `unsuspend_cards` - Manage card suspension
|
|
321
|
+
- `are_suspended` / `are_due` - Check card status
|
|
322
|
+
- `get_card_intervals` - Get card intervals
|
|
323
|
+
- `get_all_tags` - Get all tags
|
|
324
|
+
- `add_tags` / `remove_tags` - Manage tags
|
|
325
|
+
- `get_media_dir_path` - Get media directory
|
|
326
|
+
- `store_media_file` / `retrieve_media_file` / `delete_media_file` - Media operations
|
|
327
|
+
- `change_deck` - Move cards between decks
|
|
328
|
+
- `cards_to_notes` - Convert card IDs to note IDs
|
|
329
|
+
- `get_deck_config` - Get deck configuration
|
|
330
|
+
- `get_model_templates` / `get_model_styling` - Model customization
|
|
331
|
+
- `get_api_version` - Get API version
|
|
332
|
+
- `import_package` / `export_package` - Import/export decks
|
|
333
|
+
- `sync` / `sync_media` / `get_sync_status` - Sync operations
|
|
334
|
+
|
|
335
|
+
## 🐳 Docker
|
|
336
|
+
|
|
337
|
+
### Building the Image
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
docker build -t anki-connect-server .
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Running the Container
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
docker run -d \
|
|
347
|
+
-p 8765:8765 \
|
|
348
|
+
-v /path/to/collection.anki21:/data/collection.anki21 \
|
|
349
|
+
-e ANKI_COLLECTION_PATH=/data/collection.anki21 \
|
|
350
|
+
-e ANKICONNECT_ANKIWEB_USER=your@email.com \
|
|
351
|
+
-e ANKICONNECT_ANKIWEB_PASS=your_password \
|
|
352
|
+
--name anki-connect-server \
|
|
353
|
+
anki-connect-server
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Docker Compose
|
|
357
|
+
|
|
358
|
+
```yaml
|
|
359
|
+
version: '3.8'
|
|
360
|
+
|
|
361
|
+
services:
|
|
362
|
+
anki-connect-server:
|
|
363
|
+
build: .
|
|
364
|
+
ports:
|
|
365
|
+
- "8765:8765"
|
|
366
|
+
volumes:
|
|
367
|
+
- ./collection.anki21:/data/collection.anki21
|
|
368
|
+
environment:
|
|
369
|
+
- ANKI_COLLECTION_PATH=/data/collection.anki21
|
|
370
|
+
- ANKICONNECT_ANKIWEB_USER=${ANKIWEB_USER}
|
|
371
|
+
- ANKICONNECT_ANKIWEB_PASS=${ANKIWEB_PASS}
|
|
372
|
+
restart: unless-stopped
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
|