@ankimcp/anki-mcp-server
Version:
Model Context Protocol server for Anki - enables AI assistants to interact with your Anki flashcards
74 lines • 3.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const app_module_1 = require("./app.module");
const bootstrap_1 = require("./bootstrap");
const cli_1 = require("./cli");
const cli_2 = require("./cli/index.js");
const ngrok_service_1 = require("./services/ngrok.service");
const config_1 = require("./config");
const host_validation_guard_1 = require("./http/guards/host-validation.guard");
const mcp_http_factory_1 = require("./http/mcp-http.factory");
async function bootstrap() {
(0, cli_1.checkForUpdates)();
const options = (0, cli_1.parseCliArgs)();
const cli = (0, cli_2.createCli)(options.debug);
const tunnelUrl = (0, cli_1.parseOptionalUrl)(options.tunnel, "--tunnel", cli);
const configInput = (0, config_1.buildConfigInput)({
port: options.port,
host: options.host,
ankiConnect: options.ankiConnect,
readOnly: options.readOnly,
tunnel: tunnelUrl,
ngrok: options.ngrok,
debug: options.debug,
});
const validatedConfig = config_1.configSchema.parse((0, config_1.transformEnvToConfig)(configInput));
const pinoLogger = (0, bootstrap_1.createPinoLogger)(bootstrap_1.LOG_DESTINATION.STDOUT, configInput.LOG_LEVEL || "info");
const loggerService = (0, bootstrap_1.createLoggerService)(pinoLogger);
const mcpHttp = (0, mcp_http_factory_1.createMcpHttpServer)(validatedConfig.mcpServer);
const app = await core_1.NestFactory.create(app_module_1.AppModule.forHttp(mcpHttp, configInput), {
logger: loggerService,
bufferLogs: true,
});
const boundHost = validatedConfig.host;
if ((0, host_validation_guard_1.shouldWarnLoopbackOnly)(boundHost, validatedConfig.allowedHosts)) {
new common_1.Logger("HttpBootstrap").warn(`Server is bound to ${boundHost} but ALLOWED_HOSTS is not set. ` +
`For DNS-rebinding protection, only loopback Host headers ` +
`(localhost, 127.0.0.1, ::1) are accepted — requests via a LAN or ` +
`public hostname will be rejected with 403. Set ALLOWED_HOSTS ` +
`(comma-separated hostnames) to permit them.`);
}
mcpHttp.strategy.setHttpAdapter(app.getHttpAdapter());
app.connectMicroservice({ strategy: mcpHttp.strategy });
await app.startAllMicroservices();
await app.listen(validatedConfig.port, validatedConfig.host);
let ngrokUrl;
if (options.ngrok) {
try {
const ngrokService = new ngrok_service_1.NgrokService();
const tunnelInfo = await ngrokService.start(validatedConfig.port);
ngrokUrl = tunnelInfo.publicUrl;
}
catch (err) {
cli.blank();
cli.error(`Failed to start ngrok: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
cli.blank();
cli.info("Server is still running locally without tunnel.");
cli.blank();
}
}
(0, cli_1.displayStartupBanner)(cli, {
port: validatedConfig.port,
host: validatedConfig.host,
ankiConnect: validatedConfig.ankiConnect.url,
readOnly: validatedConfig.readOnly,
}, ngrokUrl);
}
bootstrap().catch((err) => {
const cli = (0, cli_2.createCli)(false);
cli.error(`Failed to start MCP HTTP server: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
process.exit(1);
});
//# sourceMappingURL=main-http.js.map