@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
package/bin/ankimcp.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Check if --stdio flag is present
|
|
4
|
+
const isStdioMode = process.argv.includes('--stdio');
|
|
5
|
+
|
|
6
|
+
if (isStdioMode) {
|
|
7
|
+
// STDIO mode - for MCP clients like Cursor, Cline, Zed, etc.
|
|
8
|
+
require('../dist/main-stdio.js');
|
|
9
|
+
} else {
|
|
10
|
+
// HTTP mode (default) - for web-based AI assistants
|
|
11
|
+
require('../dist/main-http.js');
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConfigService } from "@nestjs/config";
|
|
2
|
+
import { IAnkiConfig } from "./mcp/config/anki-config.interface";
|
|
3
|
+
export declare class AnkiConfigService implements IAnkiConfig {
|
|
4
|
+
private configService;
|
|
5
|
+
constructor(configService: ConfigService);
|
|
6
|
+
get ankiConnectUrl(): string;
|
|
7
|
+
get ankiConnectApiVersion(): number;
|
|
8
|
+
get ankiConnectApiKey(): string | undefined;
|
|
9
|
+
get ankiConnectTimeout(): number;
|
|
10
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AnkiConfigService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const config_1 = require("@nestjs/config");
|
|
15
|
+
const mcpb_workarounds_1 = require("./mcp/utils/mcpb-workarounds");
|
|
16
|
+
let AnkiConfigService = class AnkiConfigService {
|
|
17
|
+
configService;
|
|
18
|
+
constructor(configService) {
|
|
19
|
+
this.configService = configService;
|
|
20
|
+
}
|
|
21
|
+
get ankiConnectUrl() {
|
|
22
|
+
return this.configService.get("ANKI_CONNECT_URL", "http://localhost:8765");
|
|
23
|
+
}
|
|
24
|
+
get ankiConnectApiVersion() {
|
|
25
|
+
const version = this.configService.get("ANKI_CONNECT_API_VERSION", "6");
|
|
26
|
+
return parseInt(version, 10);
|
|
27
|
+
}
|
|
28
|
+
get ankiConnectApiKey() {
|
|
29
|
+
const apiKey = this.configService.get("ANKI_CONNECT_API_KEY");
|
|
30
|
+
return (0, mcpb_workarounds_1.sanitizeMcpbConfigValue)(apiKey);
|
|
31
|
+
}
|
|
32
|
+
get ankiConnectTimeout() {
|
|
33
|
+
const timeout = this.configService.get("ANKI_CONNECT_TIMEOUT", "5000");
|
|
34
|
+
return parseInt(timeout, 10);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
exports.AnkiConfigService = AnkiConfigService;
|
|
38
|
+
exports.AnkiConfigService = AnkiConfigService = __decorate([
|
|
39
|
+
(0, common_1.Injectable)(),
|
|
40
|
+
__metadata("design:paramtypes", [config_1.ConfigService])
|
|
41
|
+
], AnkiConfigService);
|
|
42
|
+
//# sourceMappingURL=anki-config.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anki-config.service.js","sourceRoot":"","sources":["../src/anki-config.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAA+C;AAE/C,mEAAuE;AAMhE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACR;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAC3B,kBAAkB,EAClB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,IAAI,qBAAqB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,0BAA0B,EAC1B,GAAG,CACJ,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,iBAAiB;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,sBAAsB,CAAC,CAAC;QACtE,OAAO,IAAA,0CAAuB,EAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,kBAAkB;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACpC,sBAAsB,EACtB,MAAM,CACP,CAAC;QACF,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;CACF,CAAA;AA9BY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEwB,sBAAa;GADrC,iBAAiB,CA8B7B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
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 AppModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.AppModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const config_1 = require("@nestjs/config");
|
|
13
|
+
const mcp_nest_1 = require("@rekog/mcp-nest");
|
|
14
|
+
const essential_1 = require("./mcp/primitives/essential");
|
|
15
|
+
const gui_1 = require("./mcp/primitives/gui");
|
|
16
|
+
const anki_config_service_1 = require("./anki-config.service");
|
|
17
|
+
let AppModule = AppModule_1 = class AppModule {
|
|
18
|
+
static forStdio() {
|
|
19
|
+
return {
|
|
20
|
+
module: AppModule_1,
|
|
21
|
+
imports: [
|
|
22
|
+
config_1.ConfigModule.forRoot({
|
|
23
|
+
isGlobal: true,
|
|
24
|
+
cache: true,
|
|
25
|
+
envFilePath: [".env.local", ".env"],
|
|
26
|
+
}),
|
|
27
|
+
mcp_nest_1.McpModule.forRoot({
|
|
28
|
+
name: process.env.MCP_SERVER_NAME || "anki-mcp-server",
|
|
29
|
+
version: process.env.MCP_SERVER_VERSION || "1.0.0",
|
|
30
|
+
transport: mcp_nest_1.McpTransportType.STDIO,
|
|
31
|
+
}),
|
|
32
|
+
essential_1.McpPrimitivesAnkiEssentialModule.forRoot({
|
|
33
|
+
ankiConfigProvider: {
|
|
34
|
+
provide: essential_1.ANKI_CONFIG,
|
|
35
|
+
useClass: anki_config_service_1.AnkiConfigService,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
gui_1.McpPrimitivesAnkiGuiModule.forRoot({
|
|
39
|
+
ankiConfigProvider: {
|
|
40
|
+
provide: essential_1.ANKI_CONFIG,
|
|
41
|
+
useClass: anki_config_service_1.AnkiConfigService,
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
providers: [anki_config_service_1.AnkiConfigService],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
static forHttp() {
|
|
49
|
+
return {
|
|
50
|
+
module: AppModule_1,
|
|
51
|
+
imports: [
|
|
52
|
+
config_1.ConfigModule.forRoot({
|
|
53
|
+
isGlobal: true,
|
|
54
|
+
cache: true,
|
|
55
|
+
envFilePath: [".env.local", ".env"],
|
|
56
|
+
}),
|
|
57
|
+
mcp_nest_1.McpModule.forRoot({
|
|
58
|
+
name: process.env.MCP_SERVER_NAME || "anki-mcp-server",
|
|
59
|
+
version: process.env.MCP_SERVER_VERSION || "1.0.0",
|
|
60
|
+
transport: mcp_nest_1.McpTransportType.STREAMABLE_HTTP,
|
|
61
|
+
mcpEndpoint: "/",
|
|
62
|
+
}),
|
|
63
|
+
essential_1.McpPrimitivesAnkiEssentialModule.forRoot({
|
|
64
|
+
ankiConfigProvider: {
|
|
65
|
+
provide: essential_1.ANKI_CONFIG,
|
|
66
|
+
useClass: anki_config_service_1.AnkiConfigService,
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
gui_1.McpPrimitivesAnkiGuiModule.forRoot({
|
|
70
|
+
ankiConfigProvider: {
|
|
71
|
+
provide: essential_1.ANKI_CONFIG,
|
|
72
|
+
useClass: anki_config_service_1.AnkiConfigService,
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
],
|
|
76
|
+
providers: [anki_config_service_1.AnkiConfigService],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
exports.AppModule = AppModule;
|
|
81
|
+
exports.AppModule = AppModule = AppModule_1 = __decorate([
|
|
82
|
+
(0, common_1.Module)({})
|
|
83
|
+
], AppModule);
|
|
84
|
+
//# sourceMappingURL=app.module.js.map
|
|
@@ -0,0 +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,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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPinoLogger = createPinoLogger;
|
|
4
|
+
exports.createLoggerService = createLoggerService;
|
|
5
|
+
const pino_1 = require("pino");
|
|
6
|
+
function createPinoLogger(destination) {
|
|
7
|
+
return (0, pino_1.pino)({
|
|
8
|
+
level: process.env.LOG_LEVEL || "info",
|
|
9
|
+
transport: {
|
|
10
|
+
target: "pino-pretty",
|
|
11
|
+
options: {
|
|
12
|
+
destination,
|
|
13
|
+
colorize: true,
|
|
14
|
+
translateTime: "HH:MM:ss Z",
|
|
15
|
+
ignore: "pid,hostname",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function createLoggerService(pinoLogger) {
|
|
21
|
+
return {
|
|
22
|
+
log: (message, context) => {
|
|
23
|
+
pinoLogger.info({ context }, message);
|
|
24
|
+
},
|
|
25
|
+
error: (message, trace, context) => {
|
|
26
|
+
pinoLogger.error({ context, trace }, message);
|
|
27
|
+
},
|
|
28
|
+
warn: (message, context) => {
|
|
29
|
+
pinoLogger.warn({ context }, message);
|
|
30
|
+
},
|
|
31
|
+
debug: (message, context) => {
|
|
32
|
+
pinoLogger.debug({ context }, message);
|
|
33
|
+
},
|
|
34
|
+
verbose: (message, context) => {
|
|
35
|
+
pinoLogger.trace({ context }, message);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":";;AASA,4CAaC;AAQD,kDAkBC;AA/CD,+BAA4B;AAQ5B,SAAgB,gBAAgB,CAAC,WAAkB;IACjD,OAAO,IAAA,WAAI,EAAC;QACV,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;QACtC,SAAS,EAAE;YACT,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,WAAW;gBACX,QAAQ,EAAE,IAAI;gBACd,aAAa,EAAE,YAAY;gBAC3B,MAAM,EAAE,cAAc;aACvB;SACF;KACF,CAAC,CAAC;AACL,CAAC;AAQD,SAAgB,mBAAmB,CAAC,UAAe;IACjD,OAAO;QACL,GAAG,EAAE,CAAC,OAAY,EAAE,OAAgB,EAAE,EAAE;YACtC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,KAAK,EAAE,CAAC,OAAY,EAAE,KAAc,EAAE,OAAgB,EAAE,EAAE;YACxD,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,EAAE,CAAC,OAAY,EAAE,OAAgB,EAAE,EAAE;YACvC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,KAAK,EAAE,CAAC,OAAY,EAAE,OAAgB,EAAE,EAAE;YACxC,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,EAAE,CAAC,OAAY,EAAE,OAAgB,EAAE,EAAE;YAC1C,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CliOptions {
|
|
2
|
+
port: number;
|
|
3
|
+
host: string;
|
|
4
|
+
ankiConnect: string;
|
|
5
|
+
ngrok: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function checkForUpdates(): void;
|
|
8
|
+
export declare function parseCliArgs(): CliOptions;
|
|
9
|
+
export declare function displayStartupBanner(options: CliOptions, ngrokUrl?: string): void;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.checkForUpdates = checkForUpdates;
|
|
7
|
+
exports.parseCliArgs = parseCliArgs;
|
|
8
|
+
exports.displayStartupBanner = displayStartupBanner;
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const update_notifier_1 = __importDefault(require("update-notifier"));
|
|
13
|
+
function getPackageJson() {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, "../package.json"), "utf-8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { version: "0.0.0", name: "anki-mcp-http" };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getVersion() {
|
|
22
|
+
return getPackageJson().version;
|
|
23
|
+
}
|
|
24
|
+
function checkForUpdates() {
|
|
25
|
+
(0, update_notifier_1.default)({ pkg: getPackageJson() }).notify();
|
|
26
|
+
}
|
|
27
|
+
function parseCliArgs() {
|
|
28
|
+
const program = new commander_1.Command();
|
|
29
|
+
program
|
|
30
|
+
.name("ankimcp")
|
|
31
|
+
.description("AnkiMCP Server - Model Context Protocol server for Anki")
|
|
32
|
+
.version(getVersion())
|
|
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
|
+
Transport Modes:
|
|
40
|
+
HTTP Mode (default): For web-based AI assistants (ChatGPT, Claude.ai)
|
|
41
|
+
STDIO Mode: For desktop MCP clients (Cursor, Cline, Zed)
|
|
42
|
+
|
|
43
|
+
Examples - HTTP Mode:
|
|
44
|
+
$ anki-mcp-http # Use defaults
|
|
45
|
+
$ anki-mcp-http --port 8080 # Custom port
|
|
46
|
+
$ anki-mcp-http --host 0.0.0.0 --port 3000 # Listen on all interfaces
|
|
47
|
+
$ anki-mcp-http --anki-connect http://localhost:8765
|
|
48
|
+
|
|
49
|
+
Examples - HTTP Mode with Ngrok:
|
|
50
|
+
$ anki-mcp-http --ngrok # Start with ngrok tunnel
|
|
51
|
+
$ anki-mcp-http --port 8080 --ngrok # Custom port + ngrok
|
|
52
|
+
$ anki-mcp-http --host 0.0.0.0 --ngrok # Public host + ngrok
|
|
53
|
+
|
|
54
|
+
Examples - STDIO Mode:
|
|
55
|
+
$ anki-mcp-http --stdio # For use with npx in MCP clients
|
|
56
|
+
|
|
57
|
+
# MCP client configuration (Cursor, Cline, Zed, etc.):
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"anki-mcp": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["-y", "anki-mcp-http", "--stdio"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Ngrok Setup (one-time):
|
|
68
|
+
1. Install: npm install -g ngrok
|
|
69
|
+
2. Get auth token from: https://dashboard.ngrok.com/get-started/your-authtoken
|
|
70
|
+
3. Setup: ngrok config add-authtoken <your-token>
|
|
71
|
+
4. Run: anki-mcp-http --ngrok
|
|
72
|
+
`);
|
|
73
|
+
program.parse();
|
|
74
|
+
const options = program.opts();
|
|
75
|
+
return {
|
|
76
|
+
port: parseInt(options.port.toString(), 10),
|
|
77
|
+
host: options.host,
|
|
78
|
+
ankiConnect: options.ankiConnect,
|
|
79
|
+
ngrok: options.ngrok || false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function displayStartupBanner(options, ngrokUrl) {
|
|
83
|
+
const version = getVersion();
|
|
84
|
+
const title = `AnkiMCP HTTP Server v${version}`;
|
|
85
|
+
const padding = Math.floor((64 - title.length) / 2);
|
|
86
|
+
const paddedTitle = " ".repeat(padding) + title + " ".repeat(64 - padding - title.length);
|
|
87
|
+
console.log(`
|
|
88
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
89
|
+
║${paddedTitle}║
|
|
90
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
91
|
+
|
|
92
|
+
🚀 Server running on: http://${options.host}:${options.port}
|
|
93
|
+
🔌 AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` : ""}
|
|
94
|
+
|
|
95
|
+
Configuration:
|
|
96
|
+
• Port: ${options.port} (override: --port 8080)
|
|
97
|
+
• Host: ${options.host} (override: --host 0.0.0.0)
|
|
98
|
+
• AnkiConnect: ${options.ankiConnect}
|
|
99
|
+
(override: --anki-connect http://localhost:8765)${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` : ""}
|
|
100
|
+
${!ngrokUrl
|
|
101
|
+
? `
|
|
102
|
+
Usage with ngrok:
|
|
103
|
+
1. Install: npm install -g ngrok
|
|
104
|
+
2. Setup: ngrok config add-authtoken <your-token>
|
|
105
|
+
3. Run: anki-mcp-http --ngrok
|
|
106
|
+
`
|
|
107
|
+
: `
|
|
108
|
+
Share this URL with your AI assistant:
|
|
109
|
+
${ngrokUrl}
|
|
110
|
+
`}
|
|
111
|
+
Run 'anki-mcp-http --help' for more options.
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.OriginValidationGuard = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let OriginValidationGuard = class OriginValidationGuard {
|
|
15
|
+
allowedOrigins;
|
|
16
|
+
constructor() {
|
|
17
|
+
const defaultOrigins = "http://localhost:*,http://127.0.0.1:*,https://localhost:*,https://127.0.0.1:*";
|
|
18
|
+
const originsEnv = process.env.ALLOWED_ORIGINS || defaultOrigins;
|
|
19
|
+
this.allowedOrigins = originsEnv.split(",").map((o) => o.trim());
|
|
20
|
+
}
|
|
21
|
+
canActivate(context) {
|
|
22
|
+
const request = context.switchToHttp().getRequest();
|
|
23
|
+
const origin = request.headers.origin || request.headers.referer;
|
|
24
|
+
if (!origin) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const isAllowed = this.allowedOrigins.some((allowedOrigin) => this.matchesPattern(origin, allowedOrigin));
|
|
28
|
+
if (!isAllowed) {
|
|
29
|
+
console.warn(`[OriginValidationGuard] Rejected request from unauthorized origin: ${origin}`);
|
|
30
|
+
}
|
|
31
|
+
return isAllowed;
|
|
32
|
+
}
|
|
33
|
+
matchesPattern(origin, pattern) {
|
|
34
|
+
if (origin === pattern) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (pattern.includes("*")) {
|
|
38
|
+
const regexPattern = pattern
|
|
39
|
+
.replace(/\./g, "\\.")
|
|
40
|
+
.replace(/\*/g, ".*");
|
|
41
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
42
|
+
return regex.test(origin);
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.OriginValidationGuard = OriginValidationGuard;
|
|
48
|
+
exports.OriginValidationGuard = OriginValidationGuard = __decorate([
|
|
49
|
+
(0, common_1.Injectable)(),
|
|
50
|
+
__metadata("design:paramtypes", [])
|
|
51
|
+
], OriginValidationGuard);
|
|
52
|
+
//# sourceMappingURL=origin-validation.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin-validation.guard.js","sourceRoot":"","sources":["../../../src/http/guards/origin-validation.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA2E;AAiBpE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACf,cAAc,CAAW;IAE1C;QACE,MAAM,cAAc,GAClB,+EAA+E,CAAC;QAClF,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,cAAc,CAAC;QACjE,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAW,CAAC;QAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAGjE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QAGD,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE,CAC3D,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,CAAC,CAC3C,CAAC;QAEF,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,sEAAsE,MAAM,EAAE,CAC/E,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IASO,cAAc,CAAC,MAAc,EAAE,OAAe;QAEpD,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAGD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,YAAY,GAAG,OAAO;iBACzB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;iBACrB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACxB,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,YAAY,GAAG,CAAC,CAAC;YAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF,CAAA;AAzDY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;;GACA,qBAAqB,CAyDjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@nestjs/core");
|
|
4
|
+
const app_module_1 = require("./app.module");
|
|
5
|
+
const bootstrap_1 = require("./bootstrap");
|
|
6
|
+
const origin_validation_guard_1 = require("./http/guards/origin-validation.guard");
|
|
7
|
+
const cli_1 = require("./cli");
|
|
8
|
+
const ngrok_service_1 = require("./services/ngrok.service");
|
|
9
|
+
async function bootstrap() {
|
|
10
|
+
(0, cli_1.checkForUpdates)();
|
|
11
|
+
const options = (0, cli_1.parseCliArgs)();
|
|
12
|
+
process.env.PORT = options.port.toString();
|
|
13
|
+
process.env.HOST = options.host;
|
|
14
|
+
process.env.ANKI_CONNECT_URL = options.ankiConnect;
|
|
15
|
+
const pinoLogger = (0, bootstrap_1.createPinoLogger)(1);
|
|
16
|
+
const loggerService = (0, bootstrap_1.createLoggerService)(pinoLogger);
|
|
17
|
+
const app = await core_1.NestFactory.create(app_module_1.AppModule.forHttp(), {
|
|
18
|
+
logger: loggerService,
|
|
19
|
+
bufferLogs: true,
|
|
20
|
+
});
|
|
21
|
+
app.useGlobalGuards(new origin_validation_guard_1.OriginValidationGuard());
|
|
22
|
+
await app.listen(options.port, options.host);
|
|
23
|
+
let ngrokUrl;
|
|
24
|
+
if (options.ngrok) {
|
|
25
|
+
try {
|
|
26
|
+
const ngrokService = new ngrok_service_1.NgrokService();
|
|
27
|
+
const tunnelInfo = await ngrokService.start(options.port);
|
|
28
|
+
ngrokUrl = tunnelInfo.publicUrl;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error("\n❌ Failed to start ngrok:");
|
|
32
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
33
|
+
console.error("\nServer is still running locally without tunnel.\n");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
(0, cli_1.displayStartupBanner)(options, ngrokUrl);
|
|
37
|
+
}
|
|
38
|
+
bootstrap().catch((err) => {
|
|
39
|
+
console.error("Failed to start MCP HTTP server:", err);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=main-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-http.js","sourceRoot":"","sources":["../src/main-http.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AACpE,mFAA8E;AAC9E,+BAA4E;AAC5E,4DAAwD;AAExD,KAAK,UAAU,SAAS;IAEtB,IAAA,qBAAe,GAAE,CAAC;IAElB,MAAM,OAAO,GAAG,IAAA,kBAAY,GAAE,CAAC;IAG/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@nestjs/core");
|
|
4
|
+
const app_module_1 = require("./app.module");
|
|
5
|
+
const bootstrap_1 = require("./bootstrap");
|
|
6
|
+
async function bootstrap() {
|
|
7
|
+
const pinoLogger = (0, bootstrap_1.createPinoLogger)(2);
|
|
8
|
+
const loggerService = (0, bootstrap_1.createLoggerService)(pinoLogger);
|
|
9
|
+
await core_1.NestFactory.createApplicationContext(app_module_1.AppModule.forStdio(), {
|
|
10
|
+
logger: loggerService,
|
|
11
|
+
bufferLogs: true,
|
|
12
|
+
});
|
|
13
|
+
pinoLogger.info("MCP STDIO server started successfully");
|
|
14
|
+
await new Promise(() => { });
|
|
15
|
+
}
|
|
16
|
+
bootstrap().catch((err) => {
|
|
17
|
+
console.error("Failed to start MCP STDIO server:", err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=main-stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-stdio.js","sourceRoot":"","sources":["../src/main-stdio.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AAEpE,KAAK,UAAU,SAAS;IAEtB,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,CAAC,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,UAAU,CAAC,CAAC;IAGtD,MAAM,kBAAW,CAAC,wBAAwB,CAAC,sBAAS,CAAC,QAAQ,EAAE,EAAE;QAC/D,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAEH,UAAU,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IAGzD,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const AnkiConnectClient: jest.Mock<any, any, any>;
|
|
2
|
+
export declare class AnkiConnectError extends Error {
|
|
3
|
+
readonly action?: string | undefined;
|
|
4
|
+
readonly originalError?: string | undefined;
|
|
5
|
+
constructor(message: string, action?: string | undefined, originalError?: string | undefined);
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AnkiConnectError = exports.AnkiConnectClient = void 0;
|
|
4
|
+
exports.AnkiConnectClient = jest.fn().mockImplementation(() => ({
|
|
5
|
+
invoke: jest.fn(),
|
|
6
|
+
}));
|
|
7
|
+
class AnkiConnectError extends Error {
|
|
8
|
+
action;
|
|
9
|
+
originalError;
|
|
10
|
+
constructor(message, action, originalError) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.action = action;
|
|
13
|
+
this.originalError = originalError;
|
|
14
|
+
this.name = "AnkiConnectError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.AnkiConnectError = AnkiConnectError;
|
|
18
|
+
//# sourceMappingURL=anki-connect.client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anki-connect.client.js","sourceRoot":"","sources":["../../../../src/mcp/clients/__mocks__/anki-connect.client.ts"],"names":[],"mappings":";;;AACa,QAAA,iBAAiB,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC;IACnE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;CAClB,CAAC,CAAC,CAAC;AAEJ,MAAa,gBAAiB,SAAQ,KAAK;IAGvB;IACA;IAHlB,YACE,OAAe,EACC,MAAe,EACf,aAAsB;QAEtC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAS;QACf,kBAAa,GAAb,aAAa,CAAS;QAGtC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AATD,4CASC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IAnkiConfig } from "../config/anki-config.interface";
|
|
2
|
+
export declare class AnkiConnectError extends Error {
|
|
3
|
+
readonly action?: string | undefined;
|
|
4
|
+
readonly originalError?: string | undefined;
|
|
5
|
+
constructor(message: string, action?: string | undefined, originalError?: string | undefined);
|
|
6
|
+
}
|
|
7
|
+
export declare class AnkiConnectClient {
|
|
8
|
+
private readonly config;
|
|
9
|
+
private readonly client;
|
|
10
|
+
private readonly apiVersion;
|
|
11
|
+
private readonly apiKey?;
|
|
12
|
+
private readonly logger;
|
|
13
|
+
constructor(config: IAnkiConfig);
|
|
14
|
+
invoke<T = any>(action: string, params?: Record<string, any>): Promise<T>;
|
|
15
|
+
}
|