@ankimcp/anki-mcp-server 0.8.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/LICENSE +7 -0
- package/README.md +756 -0
- package/bin/ankimcp.js +12 -0
- package/dist/anki-config.service.d.ts +10 -0
- package/dist/anki-config.service.js +42 -0
- package/dist/anki-config.service.js.map +1 -0
- package/dist/app.module.d.ts +5 -0
- package/dist/app.module.js +84 -0
- package/dist/app.module.js.map +1 -0
- package/dist/bootstrap.d.ts +3 -0
- package/dist/bootstrap.js +39 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +114 -0
- package/dist/cli.js.map +1 -0
- package/dist/http/guards/origin-validation.guard.d.ts +7 -0
- package/dist/http/guards/origin-validation.guard.js +52 -0
- package/dist/http/guards/origin-validation.guard.js.map +1 -0
- package/dist/main-http.d.ts +1 -0
- package/dist/main-http.js +42 -0
- package/dist/main-http.js.map +1 -0
- package/dist/main-stdio.d.ts +1 -0
- package/dist/main-stdio.js +20 -0
- package/dist/main-stdio.js.map +1 -0
- package/dist/mcp/clients/__mocks__/anki-connect.client.d.ts +6 -0
- package/dist/mcp/clients/__mocks__/anki-connect.client.js +18 -0
- package/dist/mcp/clients/__mocks__/anki-connect.client.js.map +1 -0
- package/dist/mcp/clients/anki-connect.client.d.ts +15 -0
- package/dist/mcp/clients/anki-connect.client.js +145 -0
- package/dist/mcp/clients/anki-connect.client.js.map +1 -0
- package/dist/mcp/config/anki-config.interface.d.ts +7 -0
- package/dist/mcp/config/anki-config.interface.js +5 -0
- package/dist/mcp/config/anki-config.interface.js.map +1 -0
- package/dist/mcp/primitives/essential/index.d.ts +32 -0
- package/dist/mcp/primitives/essential/index.js +130 -0
- package/dist/mcp/primitives/essential/index.js.map +1 -0
- package/dist/mcp/primitives/essential/prompts/review-session.prompt.d.ts +12 -0
- package/dist/mcp/primitives/essential/prompts/review-session.prompt.js +113 -0
- package/dist/mcp/primitives/essential/prompts/review-session.prompt.js.map +1 -0
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/content.md +195 -0
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/index.d.ts +12 -0
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/index.js +89 -0
- package/dist/mcp/primitives/essential/prompts/twenty-rules.prompt/index.js.map +1 -0
- package/dist/mcp/primitives/essential/resources/system-info.resource.d.ts +21 -0
- package/dist/mcp/primitives/essential/resources/system-info.resource.js +115 -0
- package/dist/mcp/primitives/essential/resources/system-info.resource.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/add-note.tool.d.ts +93 -0
- package/dist/mcp/primitives/essential/tools/add-note.tool.js +185 -0
- package/dist/mcp/primitives/essential/tools/add-note.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/create-deck.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js +121 -0
- package/dist/mcp/primitives/essential/tools/create-deck.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/create-model.tool.d.ts +88 -0
- package/dist/mcp/primitives/essential/tools/create-model.tool.js +144 -0
- package/dist/mcp/primitives/essential/tools/create-model.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.d.ts +84 -0
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.js +120 -0
- package/dist/mcp/primitives/essential/tools/delete-notes.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/find-notes.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/find-notes.tool.js +106 -0
- package/dist/mcp/primitives/essential/tools/find-notes.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.d.ts +84 -0
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.js +108 -0
- package/dist/mcp/primitives/essential/tools/get-due-cards.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/list-decks.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js +117 -0
- package/dist/mcp/primitives/essential/tools/list-decks.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/deleteMediaFile.action.d.ts +10 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/deleteMediaFile.action.js +18 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/deleteMediaFile.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.d.ts +12 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.js +22 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/getMediaFilesNames.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/retrieveMediaFile.action.d.ts +12 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/retrieveMediaFile.action.js +29 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/retrieveMediaFile.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.d.ts +15 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.js +41 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/actions/storeMediaFile.action.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/index.d.ts +5 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/index.js +6 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/index.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.d.ts +89 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.js +141 -0
- package/dist/mcp/primitives/essential/tools/mediaActions/mediaActions.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.js +119 -0
- package/dist/mcp/primitives/essential/tools/model-field-names.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/model-names.tool.d.ts +81 -0
- package/dist/mcp/primitives/essential/tools/model-names.tool.js +80 -0
- package/dist/mcp/primitives/essential/tools/model-names.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/model-styling.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/model-styling.tool.js +93 -0
- package/dist/mcp/primitives/essential/tools/model-styling.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/notes-info.tool.d.ts +83 -0
- package/dist/mcp/primitives/essential/tools/notes-info.tool.js +111 -0
- package/dist/mcp/primitives/essential/tools/notes-info.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/present-card.tool.d.ts +84 -0
- package/dist/mcp/primitives/essential/tools/present-card.tool.js +100 -0
- package/dist/mcp/primitives/essential/tools/present-card.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/rate-card.tool.d.ts +84 -0
- package/dist/mcp/primitives/essential/tools/rate-card.tool.js +101 -0
- package/dist/mcp/primitives/essential/tools/rate-card.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/sync.tool.d.ts +81 -0
- package/dist/mcp/primitives/essential/tools/sync.tool.js +61 -0
- package/dist/mcp/primitives/essential/tools/sync.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.d.ts +84 -0
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.js +119 -0
- package/dist/mcp/primitives/essential/tools/update-model-styling.tool.js.map +1 -0
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.d.ts +96 -0
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.js +154 -0
- package/dist/mcp/primitives/essential/tools/update-note-fields.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/index.d.ts +23 -0
- package/dist/mcp/primitives/gui/index.js +94 -0
- package/dist/mcp/primitives/gui/index.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.d.ts +88 -0
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.js +111 -0
- package/dist/mcp/primitives/gui/tools/gui-add-cards.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.d.ts +87 -0
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.js +99 -0
- package/dist/mcp/primitives/gui/tools/gui-browse.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.js +76 -0
- package/dist/mcp/primitives/gui/tools/gui-current-card.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.js +64 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-browser.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.d.ts +83 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.js +88 -0
- package/dist/mcp/primitives/gui/tools/gui-deck-overview.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.d.ts +83 -0
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.js +80 -0
- package/dist/mcp/primitives/gui/tools/gui-edit-note.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.d.ts +83 -0
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.js +90 -0
- package/dist/mcp/primitives/gui/tools/gui-select-card.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.js +83 -0
- package/dist/mcp/primitives/gui/tools/gui-selected-notes.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.js +74 -0
- package/dist/mcp/primitives/gui/tools/gui-show-answer.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.js +74 -0
- package/dist/mcp/primitives/gui/tools/gui-show-question.tool.js.map +1 -0
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.d.ts +81 -0
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.js +74 -0
- package/dist/mcp/primitives/gui/tools/gui-undo.tool.js.map +1 -0
- package/dist/mcp/types/anki.types.d.ts +180 -0
- package/dist/mcp/types/anki.types.js +18 -0
- package/dist/mcp/types/anki.types.js.map +1 -0
- package/dist/mcp/utils/anki.utils.d.ts +19 -0
- package/dist/mcp/utils/anki.utils.js +157 -0
- package/dist/mcp/utils/anki.utils.js.map +1 -0
- package/dist/mcp/utils/markdown.utils.d.ts +4 -0
- package/dist/mcp/utils/markdown.utils.js +60 -0
- package/dist/mcp/utils/markdown.utils.js.map +1 -0
- package/dist/mcp/utils/mcpb-workarounds.d.ts +1 -0
- package/dist/mcp/utils/mcpb-workarounds.js +13 -0
- package/dist/mcp/utils/mcpb-workarounds.js.map +1 -0
- package/dist/services/ngrok.service.d.ts +15 -0
- package/dist/services/ngrok.service.js +120 -0
- package/dist/services/ngrok.service.js.map +1 -0
- package/dist/test-fixtures/mock-data.d.ts +126 -0
- package/dist/test-fixtures/mock-data.js +112 -0
- package/dist/test-fixtures/mock-data.js.map +1 -0
- package/dist/test-fixtures/test-helpers.d.ts +12 -0
- package/dist/test-fixtures/test-helpers.js +24 -0
- package/dist/test-fixtures/test-helpers.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +168 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var NotesInfoTool_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.NotesInfoTool = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const mcp_nest_1 = require("@rekog/mcp-nest");
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const anki_connect_client_1 = require("../../../clients/anki-connect.client");
|
|
18
|
+
const anki_utils_1 = require("../../../utils/anki.utils");
|
|
19
|
+
let NotesInfoTool = NotesInfoTool_1 = class NotesInfoTool {
|
|
20
|
+
ankiClient;
|
|
21
|
+
logger = new common_1.Logger(NotesInfoTool_1.name);
|
|
22
|
+
constructor(ankiClient) {
|
|
23
|
+
this.ankiClient = ankiClient;
|
|
24
|
+
}
|
|
25
|
+
async notesInfo({ notes }, context) {
|
|
26
|
+
try {
|
|
27
|
+
this.logger.log(`Getting information for ${notes.length} note(s)`);
|
|
28
|
+
await context.reportProgress({ progress: 25, total: 100 });
|
|
29
|
+
const notesData = await this.ankiClient.invoke("notesInfo", {
|
|
30
|
+
notes: notes,
|
|
31
|
+
});
|
|
32
|
+
await context.reportProgress({ progress: 75, total: 100 });
|
|
33
|
+
if (!notesData || notesData.length === 0) {
|
|
34
|
+
this.logger.warn("No note information returned");
|
|
35
|
+
await context.reportProgress({ progress: 100, total: 100 });
|
|
36
|
+
return (0, anki_utils_1.createErrorResponse)(new Error("No note information found"), {
|
|
37
|
+
requestedNotes: notes,
|
|
38
|
+
hint: "The note IDs may be invalid or the notes may have been deleted",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const transformedNotes = notesData.map((note) => ({
|
|
42
|
+
noteId: note.noteId,
|
|
43
|
+
modelName: note.modelName,
|
|
44
|
+
tags: note.tags || [],
|
|
45
|
+
fields: note.fields || {},
|
|
46
|
+
cards: note.cards || [],
|
|
47
|
+
mod: note.mod,
|
|
48
|
+
}));
|
|
49
|
+
const validNotes = transformedNotes.filter((note) => note.noteId);
|
|
50
|
+
const deletedCount = notes.length - validNotes.length;
|
|
51
|
+
await context.reportProgress({ progress: 100, total: 100 });
|
|
52
|
+
const message = deletedCount > 0
|
|
53
|
+
? `Retrieved ${validNotes.length} note(s). ${deletedCount} note(s) not found (possibly deleted).`
|
|
54
|
+
: `Successfully retrieved information for ${validNotes.length} note(s)`;
|
|
55
|
+
this.logger.log(message);
|
|
56
|
+
const uniqueModels = [...new Set(validNotes.map((n) => n.modelName))];
|
|
57
|
+
return (0, anki_utils_1.createSuccessResponse)({
|
|
58
|
+
success: true,
|
|
59
|
+
notes: validNotes,
|
|
60
|
+
count: validNotes.length,
|
|
61
|
+
notFound: deletedCount,
|
|
62
|
+
requestedIds: notes,
|
|
63
|
+
message: message,
|
|
64
|
+
models: uniqueModels,
|
|
65
|
+
cssNote: "Each note model has its own CSS styling. Use modelStyling tool to get CSS for specific models.",
|
|
66
|
+
hint: validNotes.length > 0
|
|
67
|
+
? "Fields may contain HTML. Use updateNoteFields to modify content. Do not view notes in Anki browser while updating."
|
|
68
|
+
: "No valid notes found. They may have been deleted.",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.logger.error("Failed to get notes information", error);
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
if (error.message.includes("not found")) {
|
|
75
|
+
return (0, anki_utils_1.createErrorResponse)(error, {
|
|
76
|
+
requestedNotes: notes,
|
|
77
|
+
hint: "One or more note IDs are invalid. Use findNotes to get valid note IDs.",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return (0, anki_utils_1.createErrorResponse)(error, {
|
|
82
|
+
requestedNotes: notes,
|
|
83
|
+
hint: "Make sure Anki is running and the note IDs are valid",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
exports.NotesInfoTool = NotesInfoTool;
|
|
89
|
+
__decorate([
|
|
90
|
+
(0, mcp_nest_1.Tool)({
|
|
91
|
+
name: "notesInfo",
|
|
92
|
+
description: "Get detailed information about specific notes including all fields, tags, model info, and CSS styling. " +
|
|
93
|
+
"Use this after findNotes to get complete note data. Includes CSS for proper rendering awareness.",
|
|
94
|
+
parameters: zod_1.z.object({
|
|
95
|
+
notes: zod_1.z
|
|
96
|
+
.array(zod_1.z.number())
|
|
97
|
+
.min(1)
|
|
98
|
+
.max(100)
|
|
99
|
+
.describe("Array of note IDs to get information for (max 100 at once for performance). " +
|
|
100
|
+
"Get these IDs from findNotes tool."),
|
|
101
|
+
}),
|
|
102
|
+
}),
|
|
103
|
+
__metadata("design:type", Function),
|
|
104
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
105
|
+
__metadata("design:returntype", Promise)
|
|
106
|
+
], NotesInfoTool.prototype, "notesInfo", null);
|
|
107
|
+
exports.NotesInfoTool = NotesInfoTool = NotesInfoTool_1 = __decorate([
|
|
108
|
+
(0, common_1.Injectable)(),
|
|
109
|
+
__metadata("design:paramtypes", [anki_connect_client_1.AnkiConnectClient])
|
|
110
|
+
], NotesInfoTool);
|
|
111
|
+
//# sourceMappingURL=notes-info.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notes-info.tool.js","sourceRoot":"","sources":["../../../../../src/mcp/primitives/essential/tools/notes-info.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Context } from "@rekog/mcp-nest";
|
|
2
|
+
import { AnkiConnectClient } from "@/mcp/clients/anki-connect.client";
|
|
3
|
+
export declare class PresentCardTool {
|
|
4
|
+
private readonly ankiClient;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(ankiClient: AnkiConnectClient);
|
|
7
|
+
presentCard({ card_id, show_answer }: {
|
|
8
|
+
card_id: number;
|
|
9
|
+
show_answer?: boolean;
|
|
10
|
+
}, context: Context): Promise<{
|
|
11
|
+
[x: string]: unknown;
|
|
12
|
+
content: ({
|
|
13
|
+
[x: string]: unknown;
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
_meta?: {
|
|
17
|
+
[x: string]: unknown;
|
|
18
|
+
} | undefined;
|
|
19
|
+
} | {
|
|
20
|
+
[x: string]: unknown;
|
|
21
|
+
type: "image";
|
|
22
|
+
data: string;
|
|
23
|
+
mimeType: string;
|
|
24
|
+
_meta?: {
|
|
25
|
+
[x: string]: unknown;
|
|
26
|
+
} | undefined;
|
|
27
|
+
} | {
|
|
28
|
+
[x: string]: unknown;
|
|
29
|
+
type: "audio";
|
|
30
|
+
data: string;
|
|
31
|
+
mimeType: string;
|
|
32
|
+
_meta?: {
|
|
33
|
+
[x: string]: unknown;
|
|
34
|
+
} | undefined;
|
|
35
|
+
} | {
|
|
36
|
+
[x: string]: unknown;
|
|
37
|
+
type: "resource_link";
|
|
38
|
+
name: string;
|
|
39
|
+
uri: string;
|
|
40
|
+
_meta?: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
} | undefined;
|
|
43
|
+
mimeType?: string | undefined;
|
|
44
|
+
title?: string | undefined;
|
|
45
|
+
description?: string | undefined;
|
|
46
|
+
icons?: {
|
|
47
|
+
[x: string]: unknown;
|
|
48
|
+
src: string;
|
|
49
|
+
mimeType?: string | undefined;
|
|
50
|
+
sizes?: string[] | undefined;
|
|
51
|
+
}[] | undefined;
|
|
52
|
+
} | {
|
|
53
|
+
[x: string]: unknown;
|
|
54
|
+
type: "resource";
|
|
55
|
+
resource: {
|
|
56
|
+
[x: string]: unknown;
|
|
57
|
+
text: string;
|
|
58
|
+
uri: string;
|
|
59
|
+
_meta?: {
|
|
60
|
+
[x: string]: unknown;
|
|
61
|
+
} | undefined;
|
|
62
|
+
mimeType?: string | undefined;
|
|
63
|
+
} | {
|
|
64
|
+
[x: string]: unknown;
|
|
65
|
+
uri: string;
|
|
66
|
+
blob: string;
|
|
67
|
+
_meta?: {
|
|
68
|
+
[x: string]: unknown;
|
|
69
|
+
} | undefined;
|
|
70
|
+
mimeType?: string | undefined;
|
|
71
|
+
};
|
|
72
|
+
_meta?: {
|
|
73
|
+
[x: string]: unknown;
|
|
74
|
+
} | undefined;
|
|
75
|
+
})[];
|
|
76
|
+
_meta?: {
|
|
77
|
+
[x: string]: unknown;
|
|
78
|
+
} | undefined;
|
|
79
|
+
structuredContent?: {
|
|
80
|
+
[x: string]: unknown;
|
|
81
|
+
} | undefined;
|
|
82
|
+
isError?: boolean | undefined;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var PresentCardTool_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.PresentCardTool = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const mcp_nest_1 = require("@rekog/mcp-nest");
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const anki_connect_client_1 = require("../../../clients/anki-connect.client");
|
|
18
|
+
const anki_utils_1 = require("../../../utils/anki.utils");
|
|
19
|
+
let PresentCardTool = PresentCardTool_1 = class PresentCardTool {
|
|
20
|
+
ankiClient;
|
|
21
|
+
logger = new common_1.Logger(PresentCardTool_1.name);
|
|
22
|
+
constructor(ankiClient) {
|
|
23
|
+
this.ankiClient = ankiClient;
|
|
24
|
+
}
|
|
25
|
+
async presentCard({ card_id, show_answer }, context) {
|
|
26
|
+
try {
|
|
27
|
+
const showAnswer = show_answer || false;
|
|
28
|
+
this.logger.log(`Retrieving card ${card_id} for presentation (show_answer: ${showAnswer})`);
|
|
29
|
+
await context.reportProgress({ progress: 25, total: 100 });
|
|
30
|
+
const cardsInfo = await this.ankiClient.invoke("cardsInfo", {
|
|
31
|
+
cards: [card_id],
|
|
32
|
+
});
|
|
33
|
+
if (!cardsInfo || cardsInfo.length === 0) {
|
|
34
|
+
this.logger.warn(`Card not found: ${card_id}`);
|
|
35
|
+
return (0, anki_utils_1.createErrorResponse)(new Error(`Card with ID ${card_id} not found`), { cardId: card_id });
|
|
36
|
+
}
|
|
37
|
+
await context.reportProgress({ progress: 75, total: 100 });
|
|
38
|
+
const card = cardsInfo[0];
|
|
39
|
+
const { front, back } = (0, anki_utils_1.extractCardContent)(card.fields);
|
|
40
|
+
const cardType = (0, anki_utils_1.getCardType)(card.type);
|
|
41
|
+
const presentation = {
|
|
42
|
+
cardId: card.cardId,
|
|
43
|
+
front: front || card.question || "",
|
|
44
|
+
deckName: card.deckName,
|
|
45
|
+
modelName: card.modelName,
|
|
46
|
+
tags: card.tags || [],
|
|
47
|
+
currentInterval: card.interval || 0,
|
|
48
|
+
easeFactor: card.factor || 2500,
|
|
49
|
+
reviews: card.reps || 0,
|
|
50
|
+
lapses: card.lapses || 0,
|
|
51
|
+
cardType,
|
|
52
|
+
noteId: card.note,
|
|
53
|
+
};
|
|
54
|
+
if (showAnswer) {
|
|
55
|
+
presentation.back = back || card.answer || "";
|
|
56
|
+
}
|
|
57
|
+
await context.reportProgress({ progress: 100, total: 100 });
|
|
58
|
+
this.logger.log(`Retrieved card ${card_id} for presentation`);
|
|
59
|
+
const response = {
|
|
60
|
+
success: true,
|
|
61
|
+
card: presentation,
|
|
62
|
+
};
|
|
63
|
+
if (!showAnswer) {
|
|
64
|
+
response.instruction =
|
|
65
|
+
"Question shown. Wait for user's answer, then use show_answer=true";
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
response.instruction =
|
|
69
|
+
"Answer revealed. Evaluate response and suggest rating, then wait for user confirmation";
|
|
70
|
+
}
|
|
71
|
+
return (0, anki_utils_1.createSuccessResponse)(response);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.logger.error(`Failed to retrieve card ${card_id}`, error);
|
|
75
|
+
return (0, anki_utils_1.createErrorResponse)(error, { cardId: card_id });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.PresentCardTool = PresentCardTool;
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, mcp_nest_1.Tool)({
|
|
82
|
+
name: "present_card",
|
|
83
|
+
description: 'Retrieve a card\'s content for review. WORKFLOW: 1) Show question, 2) Wait for user answer, 3) Show answer with show_answer=true, 4) Evaluate and suggest rating (1-4), 5) Wait for user confirmation ("ok"/"next" = accept, or they provide different rating), 6) Only then use rate_card',
|
|
84
|
+
parameters: zod_1.z.object({
|
|
85
|
+
card_id: zod_1.z.number().describe("The ID of the card to retrieve"),
|
|
86
|
+
show_answer: zod_1.z
|
|
87
|
+
.boolean()
|
|
88
|
+
.default(false)
|
|
89
|
+
.describe("Whether to include the answer/back content in the response"),
|
|
90
|
+
}),
|
|
91
|
+
}),
|
|
92
|
+
__metadata("design:type", Function),
|
|
93
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
94
|
+
__metadata("design:returntype", Promise)
|
|
95
|
+
], PresentCardTool.prototype, "presentCard", null);
|
|
96
|
+
exports.PresentCardTool = PresentCardTool = PresentCardTool_1 = __decorate([
|
|
97
|
+
(0, common_1.Injectable)(),
|
|
98
|
+
__metadata("design:paramtypes", [anki_connect_client_1.AnkiConnectClient])
|
|
99
|
+
], PresentCardTool);
|
|
100
|
+
//# sourceMappingURL=present-card.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"present-card.tool.js","sourceRoot":"","sources":["../../../../../src/mcp/primitives/essential/tools/present-card.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Context } from "@rekog/mcp-nest";
|
|
2
|
+
import { AnkiConnectClient } from "@/mcp/clients/anki-connect.client";
|
|
3
|
+
export declare class RateCardTool {
|
|
4
|
+
private readonly ankiClient;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(ankiClient: AnkiConnectClient);
|
|
7
|
+
rateCard({ card_id, rating }: {
|
|
8
|
+
card_id: number;
|
|
9
|
+
rating: number;
|
|
10
|
+
}, context: Context): Promise<{
|
|
11
|
+
[x: string]: unknown;
|
|
12
|
+
content: ({
|
|
13
|
+
[x: string]: unknown;
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
_meta?: {
|
|
17
|
+
[x: string]: unknown;
|
|
18
|
+
} | undefined;
|
|
19
|
+
} | {
|
|
20
|
+
[x: string]: unknown;
|
|
21
|
+
type: "image";
|
|
22
|
+
data: string;
|
|
23
|
+
mimeType: string;
|
|
24
|
+
_meta?: {
|
|
25
|
+
[x: string]: unknown;
|
|
26
|
+
} | undefined;
|
|
27
|
+
} | {
|
|
28
|
+
[x: string]: unknown;
|
|
29
|
+
type: "audio";
|
|
30
|
+
data: string;
|
|
31
|
+
mimeType: string;
|
|
32
|
+
_meta?: {
|
|
33
|
+
[x: string]: unknown;
|
|
34
|
+
} | undefined;
|
|
35
|
+
} | {
|
|
36
|
+
[x: string]: unknown;
|
|
37
|
+
type: "resource_link";
|
|
38
|
+
name: string;
|
|
39
|
+
uri: string;
|
|
40
|
+
_meta?: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
} | undefined;
|
|
43
|
+
mimeType?: string | undefined;
|
|
44
|
+
title?: string | undefined;
|
|
45
|
+
description?: string | undefined;
|
|
46
|
+
icons?: {
|
|
47
|
+
[x: string]: unknown;
|
|
48
|
+
src: string;
|
|
49
|
+
mimeType?: string | undefined;
|
|
50
|
+
sizes?: string[] | undefined;
|
|
51
|
+
}[] | undefined;
|
|
52
|
+
} | {
|
|
53
|
+
[x: string]: unknown;
|
|
54
|
+
type: "resource";
|
|
55
|
+
resource: {
|
|
56
|
+
[x: string]: unknown;
|
|
57
|
+
text: string;
|
|
58
|
+
uri: string;
|
|
59
|
+
_meta?: {
|
|
60
|
+
[x: string]: unknown;
|
|
61
|
+
} | undefined;
|
|
62
|
+
mimeType?: string | undefined;
|
|
63
|
+
} | {
|
|
64
|
+
[x: string]: unknown;
|
|
65
|
+
uri: string;
|
|
66
|
+
blob: string;
|
|
67
|
+
_meta?: {
|
|
68
|
+
[x: string]: unknown;
|
|
69
|
+
} | undefined;
|
|
70
|
+
mimeType?: string | undefined;
|
|
71
|
+
};
|
|
72
|
+
_meta?: {
|
|
73
|
+
[x: string]: unknown;
|
|
74
|
+
} | undefined;
|
|
75
|
+
})[];
|
|
76
|
+
_meta?: {
|
|
77
|
+
[x: string]: unknown;
|
|
78
|
+
} | undefined;
|
|
79
|
+
structuredContent?: {
|
|
80
|
+
[x: string]: unknown;
|
|
81
|
+
} | undefined;
|
|
82
|
+
isError?: boolean | undefined;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var RateCardTool_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.RateCardTool = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const mcp_nest_1 = require("@rekog/mcp-nest");
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const anki_connect_client_1 = require("../../../clients/anki-connect.client");
|
|
18
|
+
const anki_utils_1 = require("../../../utils/anki.utils");
|
|
19
|
+
let RateCardTool = RateCardTool_1 = class RateCardTool {
|
|
20
|
+
ankiClient;
|
|
21
|
+
logger = new common_1.Logger(RateCardTool_1.name);
|
|
22
|
+
constructor(ankiClient) {
|
|
23
|
+
this.ankiClient = ankiClient;
|
|
24
|
+
}
|
|
25
|
+
async rateCard({ card_id, rating }, context) {
|
|
26
|
+
try {
|
|
27
|
+
if (!Number.isInteger(rating) || rating < 1 || rating > 4) {
|
|
28
|
+
return (0, anki_utils_1.createErrorResponse)(new Error("Invalid rating. Must be 1 (Again), 2 (Hard), 3 (Good), or 4 (Easy)"), { cardId: card_id, attemptedRating: rating });
|
|
29
|
+
}
|
|
30
|
+
this.logger.log(`Rating card ${card_id} with rating ${rating}`);
|
|
31
|
+
await context.reportProgress({ progress: 25, total: 100 });
|
|
32
|
+
const answers = [
|
|
33
|
+
{
|
|
34
|
+
cardId: card_id,
|
|
35
|
+
ease: rating,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
const result = await this.ankiClient.invoke("answerCards", {
|
|
39
|
+
answers,
|
|
40
|
+
});
|
|
41
|
+
if (!result) {
|
|
42
|
+
throw new Error(`Failed to rate card ${card_id}`);
|
|
43
|
+
}
|
|
44
|
+
const ratingDesc = (0, anki_utils_1.getRatingDescription)(rating);
|
|
45
|
+
this.logger.log(`Card ${card_id} rated as ${ratingDesc}`);
|
|
46
|
+
await context.reportProgress({ progress: 75, total: 100 });
|
|
47
|
+
const cardsInfo = await this.ankiClient.invoke("cardsInfo", {
|
|
48
|
+
cards: [card_id],
|
|
49
|
+
});
|
|
50
|
+
let nextReview = null;
|
|
51
|
+
if (cardsInfo && cardsInfo.length > 0) {
|
|
52
|
+
const card = cardsInfo[0];
|
|
53
|
+
nextReview = {
|
|
54
|
+
interval: card.interval || 0,
|
|
55
|
+
due: card.due || 0,
|
|
56
|
+
factor: card.factor || 2500,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
await context.reportProgress({ progress: 100, total: 100 });
|
|
60
|
+
return (0, anki_utils_1.createSuccessResponse)({
|
|
61
|
+
success: true,
|
|
62
|
+
cardId: card_id,
|
|
63
|
+
rating: rating,
|
|
64
|
+
ratingDescription: ratingDesc,
|
|
65
|
+
message: `Card successfully rated as ${ratingDesc}`,
|
|
66
|
+
nextReview,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
this.logger.error(`Failed to rate card ${card_id}`, error);
|
|
71
|
+
return (0, anki_utils_1.createErrorResponse)(error, {
|
|
72
|
+
cardId: card_id,
|
|
73
|
+
attemptedRating: rating,
|
|
74
|
+
hint: "Make sure Anki is running and the card exists",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.RateCardTool = RateCardTool;
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, mcp_nest_1.Tool)({
|
|
82
|
+
name: "rate_card",
|
|
83
|
+
description: "Submit a rating for a card to update Anki's spaced repetition scheduling. Use this ONLY after the user confirms or modifies your suggested rating. Do not rate automatically without user input.",
|
|
84
|
+
parameters: zod_1.z.object({
|
|
85
|
+
card_id: zod_1.z.number().describe("The ID of the card to rate"),
|
|
86
|
+
rating: zod_1.z
|
|
87
|
+
.number()
|
|
88
|
+
.min(1)
|
|
89
|
+
.max(4)
|
|
90
|
+
.describe("The rating for the card (use the user's choice, not your suggestion): 1=Again (failed), 2=Hard, 3=Good, 4=Easy"),
|
|
91
|
+
}),
|
|
92
|
+
}),
|
|
93
|
+
__metadata("design:type", Function),
|
|
94
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
95
|
+
__metadata("design:returntype", Promise)
|
|
96
|
+
], RateCardTool.prototype, "rateCard", null);
|
|
97
|
+
exports.RateCardTool = RateCardTool = RateCardTool_1 = __decorate([
|
|
98
|
+
(0, common_1.Injectable)(),
|
|
99
|
+
__metadata("design:paramtypes", [anki_connect_client_1.AnkiConnectClient])
|
|
100
|
+
], RateCardTool);
|
|
101
|
+
//# sourceMappingURL=rate-card.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-card.tool.js","sourceRoot":"","sources":["../../../../../src/mcp/primitives/essential/tools/rate-card.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Context } from "@rekog/mcp-nest";
|
|
2
|
+
import { AnkiConnectClient } from "@/mcp/clients/anki-connect.client";
|
|
3
|
+
export declare class SyncTool {
|
|
4
|
+
private readonly ankiClient;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(ankiClient: AnkiConnectClient);
|
|
7
|
+
sync(_args: Record<string, never>, context: Context): Promise<{
|
|
8
|
+
[x: string]: unknown;
|
|
9
|
+
content: ({
|
|
10
|
+
[x: string]: unknown;
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
_meta?: {
|
|
14
|
+
[x: string]: unknown;
|
|
15
|
+
} | undefined;
|
|
16
|
+
} | {
|
|
17
|
+
[x: string]: unknown;
|
|
18
|
+
type: "image";
|
|
19
|
+
data: string;
|
|
20
|
+
mimeType: string;
|
|
21
|
+
_meta?: {
|
|
22
|
+
[x: string]: unknown;
|
|
23
|
+
} | undefined;
|
|
24
|
+
} | {
|
|
25
|
+
[x: string]: unknown;
|
|
26
|
+
type: "audio";
|
|
27
|
+
data: string;
|
|
28
|
+
mimeType: string;
|
|
29
|
+
_meta?: {
|
|
30
|
+
[x: string]: unknown;
|
|
31
|
+
} | undefined;
|
|
32
|
+
} | {
|
|
33
|
+
[x: string]: unknown;
|
|
34
|
+
type: "resource_link";
|
|
35
|
+
name: string;
|
|
36
|
+
uri: string;
|
|
37
|
+
_meta?: {
|
|
38
|
+
[x: string]: unknown;
|
|
39
|
+
} | undefined;
|
|
40
|
+
mimeType?: string | undefined;
|
|
41
|
+
title?: string | undefined;
|
|
42
|
+
description?: string | undefined;
|
|
43
|
+
icons?: {
|
|
44
|
+
[x: string]: unknown;
|
|
45
|
+
src: string;
|
|
46
|
+
mimeType?: string | undefined;
|
|
47
|
+
sizes?: string[] | undefined;
|
|
48
|
+
}[] | undefined;
|
|
49
|
+
} | {
|
|
50
|
+
[x: string]: unknown;
|
|
51
|
+
type: "resource";
|
|
52
|
+
resource: {
|
|
53
|
+
[x: string]: unknown;
|
|
54
|
+
text: string;
|
|
55
|
+
uri: string;
|
|
56
|
+
_meta?: {
|
|
57
|
+
[x: string]: unknown;
|
|
58
|
+
} | undefined;
|
|
59
|
+
mimeType?: string | undefined;
|
|
60
|
+
} | {
|
|
61
|
+
[x: string]: unknown;
|
|
62
|
+
uri: string;
|
|
63
|
+
blob: string;
|
|
64
|
+
_meta?: {
|
|
65
|
+
[x: string]: unknown;
|
|
66
|
+
} | undefined;
|
|
67
|
+
mimeType?: string | undefined;
|
|
68
|
+
};
|
|
69
|
+
_meta?: {
|
|
70
|
+
[x: string]: unknown;
|
|
71
|
+
} | undefined;
|
|
72
|
+
})[];
|
|
73
|
+
_meta?: {
|
|
74
|
+
[x: string]: unknown;
|
|
75
|
+
} | undefined;
|
|
76
|
+
structuredContent?: {
|
|
77
|
+
[x: string]: unknown;
|
|
78
|
+
} | undefined;
|
|
79
|
+
isError?: boolean | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var SyncTool_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SyncTool = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const mcp_nest_1 = require("@rekog/mcp-nest");
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const anki_connect_client_1 = require("../../../clients/anki-connect.client");
|
|
18
|
+
const anki_utils_1 = require("../../../utils/anki.utils");
|
|
19
|
+
let SyncTool = SyncTool_1 = class SyncTool {
|
|
20
|
+
ankiClient;
|
|
21
|
+
logger = new common_1.Logger(SyncTool_1.name);
|
|
22
|
+
constructor(ankiClient) {
|
|
23
|
+
this.ankiClient = ankiClient;
|
|
24
|
+
}
|
|
25
|
+
async sync(_args, context) {
|
|
26
|
+
try {
|
|
27
|
+
this.logger.log("Synchronizing Anki collection with AnkiWeb");
|
|
28
|
+
await context.reportProgress({ progress: 25, total: 100 });
|
|
29
|
+
await this.ankiClient.invoke("sync");
|
|
30
|
+
this.logger.log("Anki sync completed successfully");
|
|
31
|
+
await context.reportProgress({ progress: 100, total: 100 });
|
|
32
|
+
return (0, anki_utils_1.createSuccessResponse)({
|
|
33
|
+
success: true,
|
|
34
|
+
message: "Successfully synchronized with AnkiWeb",
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
this.logger.error("Failed to sync with AnkiWeb", error);
|
|
40
|
+
return (0, anki_utils_1.createErrorResponse)(error, {
|
|
41
|
+
hint: "Make sure Anki is running and you are logged into AnkiWeb",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.SyncTool = SyncTool;
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, mcp_nest_1.Tool)({
|
|
49
|
+
name: "sync",
|
|
50
|
+
description: "Synchronize local Anki collection with AnkiWeb. IMPORTANT: Always sync at the START of a review session (before getting cards) and at the END when user indicates they are done. This ensures data consistency across devices.",
|
|
51
|
+
parameters: zod_1.z.object({}),
|
|
52
|
+
}),
|
|
53
|
+
__metadata("design:type", Function),
|
|
54
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
55
|
+
__metadata("design:returntype", Promise)
|
|
56
|
+
], SyncTool.prototype, "sync", null);
|
|
57
|
+
exports.SyncTool = SyncTool = SyncTool_1 = __decorate([
|
|
58
|
+
(0, common_1.Injectable)(),
|
|
59
|
+
__metadata("design:paramtypes", [anki_connect_client_1.AnkiConnectClient])
|
|
60
|
+
], SyncTool);
|
|
61
|
+
//# sourceMappingURL=sync.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.tool.js","sourceRoot":"","sources":["../../../../../src/mcp/primitives/essential/tools/sync.tool.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,8CAAuC;AAEvC,6BAAwB;AACxB,8EAAsE;AACtE,0DAGgC;AAMzB,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IAGU;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,UAAQ,CAAC,IAAI,CAAC,CAAC;IAEpD,YAA6B,UAA6B;QAA7B,eAAU,GAAV,UAAU,CAAmB;IAAG,CAAC;IAQxD,AAAN,KAAK,CAAC,IAAI,CAAC,KAA4B,EAAE,OAAgB;QACvD,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;YAC9D,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAG3D,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAErC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;YACpD,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAE5D,OAAO,IAAA,kCAAqB,EAAC;gBAC3B,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,wCAAwC;gBACjD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YAExD,OAAO,IAAA,gCAAmB,EAAC,KAAK,EAAE;gBAChC,IAAI,EAAE,2DAA2D;aAClE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF,CAAA;AAnCY,4BAAQ;AAWb;IANL,IAAA,eAAI,EAAC;QACJ,IAAI,EAAE,MAAM;QACZ,WAAW,EACT,gOAAgO;QAClO,UAAU,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC;KACzB,CAAC;;;;oCAwBD;mBAlCU,QAAQ;IADpB,IAAA,mBAAU,GAAE;qCAI8B,uCAAiB;GAH/C,QAAQ,CAmCpB"}
|