@iantay/anki-mcp-server
Version:
A Model Context Protocol (MCP) server that enables LLMs to interact with Anki flashcard software through AnkiConnect (Fork with API key support)
3 lines (2 loc) • 23.1 kB
JavaScript
;var E=require("@modelcontextprotocol/sdk/server/index.js"),b=require("@modelcontextprotocol/sdk/server/stdio.js"),c=require("@modelcontextprotocol/sdk/types.js");var x="0.1.9";var p=require("@modelcontextprotocol/sdk/types.js");var d=require("@modelcontextprotocol/sdk/types.js"),I=require("yanki-connect"),f=class extends Error{constructor(e){super(e),this.name="AnkiConnectionError"}},g=class extends Error{constructor(e){super(e),this.name="AnkiTimeoutError"}},w=class extends Error{constructor(t,n){super(t);this.code=n;this.name="AnkiApiError"}},P={ankiConnectUrl:"http://localhost:8765",apiVersion:6,timeout:5e3,retryTimeout:1e4,defaultDeck:"Default",apiKey:void 0},m=class{constructor(e={}){this.config={...P,...e};let t="127.0.0.1",n=8765;try{let i=new URL(this.config.ankiConnectUrl);t=i.hostname,n=parseInt(i.port)||8765}catch{let s=this.config.ankiConnectUrl.replace(/^https?:\/\//,"").split(":");s.length>=1&&(t=s[0]||"127.0.0.1"),s.length>=2&&(n=parseInt(s[1])||8765)}this.client=new I.YankiConnect({host:`http://${t}`,port:n,version:this.config.apiVersion,key:this.config.apiKey})}async executeWithRetry(e,t=1){let n=null;for(let i=0;i<=t;i++)try{return await e()}catch(s){if(n=this.normalizeError(s),i<t){let o=Math.min(1e3*Math.pow(2,i),this.config.retryTimeout);await new Promise(l=>setTimeout(l,o))}}throw n||new f("Unknown error occurred")}normalizeError(e){return e instanceof Error?e.message.includes("ECONNREFUSED")?new f("Anki is not running. Please start Anki and ensure AnkiConnect plugin is enabled."):e.message.includes("timeout")||e.message.includes("ETIMEDOUT")?new g("Connection to Anki timed out. Please check if Anki is responsive."):e.message.includes("collection unavailable")?new w("Anki collection is unavailable. Please close any open dialogs in Anki."):e:new Error(String(e))}wrapError(e){return e instanceof f?new d.McpError(d.ErrorCode.InternalError,e.message):e instanceof g?new d.McpError(d.ErrorCode.InternalError,e.message):e instanceof w?new d.McpError(d.ErrorCode.InternalError,e.message):new d.McpError(d.ErrorCode.InternalError,`Anki error: ${e.message}`)}async checkConnection(){try{return await this.executeWithRetry(()=>this.client.miscellaneous.version()),!0}catch(e){throw this.wrapError(e instanceof Error?e:new Error(String(e)))}}async getDeckNames(){try{return await this.executeWithRetry(()=>this.client.deck.deckNames())}catch(e){throw this.wrapError(e instanceof Error?e:new Error(String(e)))}}async createDeck(e){try{let t=await this.executeWithRetry(()=>this.client.deck.createDeck({deck:e}));return typeof t=="number"?t:0}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async getModelNames(){try{return await this.executeWithRetry(()=>this.client.model.modelNames())}catch(e){throw this.wrapError(e instanceof Error?e:new Error(String(e)))}}async getModelFieldNames(e){try{return await this.executeWithRetry(()=>this.client.model.modelFieldNames({modelName:e}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async getModelTemplates(e){try{return await this.executeWithRetry(()=>this.client.model.modelTemplates({modelName:e}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async getModelStyling(e){try{return await this.executeWithRetry(()=>this.client.model.modelStyling({modelName:e}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async addNote(e){try{return await this.executeWithRetry(()=>{var t;return this.client.note.addNote({note:{deckName:e.deckName,modelName:e.modelName,fields:e.fields,tags:e.tags||[],options:{allowDuplicate:((t=e.options)==null?void 0:t.allowDuplicate)||!1,duplicateScope:"deck"}}})})}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async addNotes(e){try{return await this.executeWithRetry(()=>this.client.note.addNotes({notes:e.map(t=>({deckName:t.deckName,modelName:t.modelName,fields:t.fields,tags:t.tags||[],options:{allowDuplicate:!1,duplicateScope:"deck"}}))}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async findNotes(e){try{let t=await this.executeWithRetry(()=>this.client.note.findNotes({query:e}));return Array.isArray(t)?t.filter(n=>typeof n=="number"):[]}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async notesInfo(e){try{let t=await this.executeWithRetry(()=>this.client.note.notesInfo({notes:e}));return Array.isArray(t)?t:[]}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async updateNoteFields(e){try{await this.executeWithRetry(()=>this.client.note.updateNoteFields({note:{id:e.id,fields:e.fields}}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async deleteNotes(e){try{await this.executeWithRetry(()=>this.client.note.deleteNotes({notes:e}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async createModel(e){try{let t=e.cardTemplates.map(n=>({name:n.name,Front:n.front,Back:n.back}));await this.executeWithRetry(()=>this.client.model.createModel({modelName:e.modelName,inOrderFields:e.inOrderFields,css:e.css,cardTemplates:t}))}catch(t){throw this.wrapError(t instanceof Error?t:new Error(String(t)))}}async guiSelectedNotes(){try{let e=await this.executeWithRetry(()=>this.client.graphical.guiSelectedNotes());return Array.isArray(e)?e:[]}catch(e){throw this.wrapError(e instanceof Error?e:new Error(String(e)))}}async guiCurrentCard(){try{return await this.executeWithRetry(()=>this.client.graphical.guiCurrentCard())||null}catch(e){throw this.wrapError(e instanceof Error?e:new Error(String(e)))}}};var k=class{constructor(e){this.ankiClient=new m(e),this.modelSchemaCache=new Map,this.allModelSchemasCache=null,this.cacheExpiry=5*60*1e3,this.lastCacheUpdate=0}async listResources(){return await this.ankiClient.checkConnection(),{resources:[{uri:"anki://decks/all",name:"All Decks",description:"List of all available decks in Anki",mimeType:"application/json"}]}}async listResourceTemplates(){return await this.ankiClient.checkConnection(),{resourceTemplates:[{uriTemplate:"anki://note-types/{modelName}",name:"Note Type Schema",description:"Detailed structure information for a specific note type",mimeType:"application/json"},{uriTemplate:"anki://note-types/all",name:"All Note Types",description:"List of all available note types",mimeType:"application/json"},{uriTemplate:"anki://note-types/all-with-schemas",name:"All Note Types with Schemas",description:"Detailed structure information for all note types",mimeType:"application/json"},{uriTemplate:"anki://decks/all",name:"All Decks",description:"Complete list of available decks",mimeType:"application/json"}]}}async readResource(e){if(await this.ankiClient.checkConnection(),e==="anki://decks/all"){let n=await this.ankiClient.getDeckNames();return{contents:[{uri:e,mimeType:"application/json",text:JSON.stringify({decks:n,count:n.length},null,2)}]}}if(e==="anki://note-types/all"){let n=await this.ankiClient.getModelNames();return{contents:[{uri:e,mimeType:"application/json",text:JSON.stringify({noteTypes:n,count:n.length},null,2)}]}}if(e==="anki://note-types/all-with-schemas"){let n=await this.getAllModelSchemas();return{contents:[{uri:e,mimeType:"application/json",text:JSON.stringify({noteTypes:n,count:n.length},null,2)}]}}let t=e.match(/^anki:\/\/note-types\/(.+)$/);if(t){let n=decodeURIComponent(t[1]);try{let i=await this.getModelSchema(n);return{contents:[{uri:e,mimeType:"application/json",text:JSON.stringify({modelName:i.modelName,fields:i.fields,templates:i.templates,css:i.css,createTool:`create_${n.replace(/\s+/g,"_")}_note`},null,2)}]}}catch{throw new p.McpError(p.ErrorCode.InvalidParams,`Note type '${n}' does not exist`)}}throw new p.McpError(p.ErrorCode.InvalidParams,`Unknown resource: ${e}`)}async getModelSchema(e){if(!e)throw new p.McpError(p.ErrorCode.InvalidParams,"Model name is required");let t=Date.now(),n=this.modelSchemaCache.get(e);if(n&&t-this.lastCacheUpdate<this.cacheExpiry)return n;if(!(await this.ankiClient.getModelNames()).includes(e))throw new p.McpError(p.ErrorCode.InvalidParams,`Note type not found: ${e}`);let[s,o,l]=await Promise.all([this.ankiClient.getModelFieldNames(e),this.ankiClient.getModelTemplates(e),this.ankiClient.getModelStyling(e)]),h={modelName:e,fields:s,templates:o,css:l.css};return this.modelSchemaCache.set(e,h),this.lastCacheUpdate=t,h}async getAllModelSchemas(){let e=Date.now();if(this.allModelSchemasCache&&e-this.lastCacheUpdate<this.cacheExpiry)return this.allModelSchemasCache;let t=await this.ankiClient.getModelNames(),n=await Promise.all(t.map(i=>this.getModelSchema(i)));return this.allModelSchemasCache=n,this.lastCacheUpdate=e,n}clearCache(){this.modelSchemaCache.clear(),this.allModelSchemasCache=null,this.lastCacheUpdate=0}};var r=require("@modelcontextprotocol/sdk/types.js");var N=class{constructor(e){this.ankiClient=new m(e)}async getToolSchema(){return{tools:[{name:"list_decks",description:"List all available Anki decks",inputSchema:{type:"object",properties:{},required:[]}},{name:"create_deck",description:"Create a new Anki deck",inputSchema:{type:"object",properties:{name:{type:"string",description:"Name of the deck to create"}},required:["name"]}},{name:"get_note_type_info",description:"Get detailed structure of a note type",inputSchema:{type:"object",properties:{modelName:{type:"string",description:"Name of the note type/model"},includeCss:{type:"boolean",description:"Whether to include CSS information"}},required:["modelName"]}},{name:"create_note",description:"Create a new note (LLM Should call get_note_type_info first)",inputSchema:{type:"object",properties:{type:{type:"string",description:"Note type"},deck:{type:"string",description:"Deck name"},fields:{type:"object",description:"Custom fields for the note(get note type info first)",additionalProperties:!0},allowDuplicate:{type:"boolean",description:"Whether to allow duplicate notes"},tags:{type:"array",items:{type:"string"},description:"Tags for the note"}},required:["type","deck","fields"]}},{name:"batch_create_notes",description:"Create multiple notes at once (llm should call get_note_type_info first )",inputSchema:{type:"object",properties:{notes:{type:"array",items:{type:"object",properties:{type:{type:"string",enum:["Basic","Cloze"]},deck:{type:"string"},fields:{type:"object",additionalProperties:!0},tags:{type:"array",items:{type:"string"}}},required:["type","deck","fields"]}},allowDuplicate:{type:"boolean",description:"Whether to allow duplicate notes"},stopOnError:{type:"boolean",description:"Whether to stop on first error"}},required:["notes"]}},{name:"search_notes",description:"Search for notes using Anki query syntax",inputSchema:{type:"object",properties:{query:{type:"string",description:"Anki search query"}},required:["query"]}},{name:"get_note_info",description:"Get detailed information about a note",inputSchema:{type:"object",properties:{noteId:{type:"number",description:"Note ID"}},required:["noteId"]}},{name:"update_note",description:"Update an existing note",inputSchema:{type:"object",properties:{id:{type:"number",description:"Note ID"},fields:{type:"object",description:"Fields to update"},tags:{type:"array",items:{type:"string"},description:"New tags for the note"}},required:["id","fields"]}},{name:"delete_note",description:"Delete a note",inputSchema:{type:"object",properties:{noteId:{type:"number",description:"Note ID to delete"}},required:["noteId"]}},{name:"batch_delete_notes",description:"Delete multiple notes at once",inputSchema:{type:"object",properties:{noteIds:{type:"array",items:{type:"number"},description:"Array of note IDs to delete"},stopOnError:{type:"boolean",description:"Whether to stop on first error (default: false)"}},required:["noteIds"]}},{name:"list_note_types",description:"List all available note types",inputSchema:{type:"object",properties:{},required:[]}},{name:"create_note_type",description:"Create a new note type",inputSchema:{type:"object",properties:{name:{type:"string",description:"Name of the new note type"},fields:{type:"array",items:{type:"string"},description:"Field names for the note type"},css:{type:"string",description:"CSS styling for the note type"},templates:{type:"array",items:{type:"object",properties:{name:{type:"string"},front:{type:"string"},back:{type:"string"}},required:["name","front","back"]},description:"Card templates"}},required:["name","fields","templates"]}},{name:"gui_selected_notes",description:"Get the selected notes from the Anki GUI browser",inputSchema:{type:"object",properties:{},required:[]}},{name:"gui_current_card",description:"Get the current card being shown in Anki GUI",inputSchema:{type:"object",properties:{},required:[]}}]}}async executeTool(e,t){await this.ankiClient.checkConnection();try{switch(e){case"list_decks":return this.listDecks();case"create_deck":return this.createDeck(t);case"list_note_types":return this.listNoteTypes();case"create_note_type":return this.createNoteType(t);case"get_note_type_info":return this.getNoteTypeInfo(t);case"create_note":return this.createNote(t);case"batch_create_notes":return this.batchCreateNotes(t);case"search_notes":return this.searchNotes(t);case"get_note_info":return this.getNoteInfo(t);case"update_note":return this.updateNote(t);case"delete_note":return this.deleteNote(t);case"batch_delete_notes":return this.batchDeleteNotes(t);case"gui_selected_notes":return this.guiSelectedNotes();case"gui_current_card":return this.guiCurrentCard();default:let n=e.match(/^create_(.+)_note$/);if(n){let i=n[1].replace(/_/g," ");return this.createModelSpecificNote(i,t)}throw new r.McpError(r.ErrorCode.MethodNotFound,`Unknown tool: ${e}`)}}catch(n){if(n instanceof r.McpError)throw n;return{content:[{type:"text",text:`Error: ${n instanceof Error?n.message:String(n)}`}],isError:!0}}}async listDecks(){let e=await this.ankiClient.getDeckNames();return{content:[{type:"text",text:JSON.stringify({decks:e,count:e.length},null,2)}]}}async createDeck(e){if(!e.name)throw new r.McpError(r.ErrorCode.InvalidParams,"Deck name is required");let t=await this.ankiClient.createDeck(e.name);return{content:[{type:"text",text:JSON.stringify({deckId:t,name:e.name},null,2)}]}}async listNoteTypes(){let e=await this.ankiClient.getModelNames();return{content:[{type:"text",text:JSON.stringify({noteTypes:e,count:e.length},null,2)}]}}async createNoteType(e){if(!e.name)throw new r.McpError(r.ErrorCode.InvalidParams,"Note type name is required");if(!e.fields||e.fields.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Fields are required");if(!e.templates||e.templates.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Templates are required");if((await this.ankiClient.getModelNames()).includes(e.name))throw new r.McpError(r.ErrorCode.InvalidParams,`Note type already exists: ${e.name}`);return await this.ankiClient.createModel({modelName:e.name,inOrderFields:e.fields,css:e.css||"",cardTemplates:e.templates}),{content:[{type:"text",text:JSON.stringify({success:!0,modelName:e.name,fields:e.fields,templates:e.templates.length},null,2)}]}}async getNoteTypeInfo(e){if(!e.modelName)throw new r.McpError(r.ErrorCode.InvalidParams,"Model name is required");if(!(await this.ankiClient.getModelNames()).includes(e.modelName))throw new r.McpError(r.ErrorCode.InvalidParams,`Note type not found: ${e.modelName}`);let[n,i]=await Promise.all([this.ankiClient.getModelFieldNames(e.modelName),this.ankiClient.getModelTemplates(e.modelName)]),s={modelName:e.modelName,fields:n,templates:i};if(e.includeCss){let o=await this.ankiClient.getModelStyling(e.modelName);s.css=o.css}return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}async createNote(e){if(!e.type)throw new r.McpError(r.ErrorCode.InvalidParams,"Note type is required");if(!e.deck)throw new r.McpError(r.ErrorCode.InvalidParams,"Deck name is required");if(!e.fields||Object.keys(e.fields).length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Fields are required");if((await this.ankiClient.getDeckNames()).includes(e.deck)||await this.ankiClient.createDeck(e.deck),!(await this.ankiClient.getModelNames()).includes(e.type))throw new r.McpError(r.ErrorCode.InvalidParams,`Note type not found: ${e.type}`);let i=await this.ankiClient.getModelFieldNames(e.type),s={};for(let l of i)s[l]=e.fields[l]||e.fields[l.toLowerCase()]||"";let o=await this.ankiClient.addNote({deckName:e.deck,modelName:e.type,fields:s,tags:e.tags||[],options:{allowDuplicate:e.allowDuplicate||!1}});return{content:[{type:"text",text:JSON.stringify({noteId:o,deck:e.deck,modelName:e.type},null,2)}]}}async createModelSpecificNote(e,t){if(!t.deck)throw new r.McpError(r.ErrorCode.InvalidParams,"Deck name is required");if(!(await this.ankiClient.getModelNames()).includes(e))throw new r.McpError(r.ErrorCode.InvalidParams,`Note type not found: ${e}`);(await this.ankiClient.getDeckNames()).includes(t.deck)||await this.ankiClient.createDeck(t.deck);let s=await this.ankiClient.getModelFieldNames(e),o={};for(let u of s)o[u]=t[u.toLowerCase()]||t[u]||"";let l=Array.isArray(t.tags)?t.tags:[],h=await this.ankiClient.addNote({deckName:t.deck,modelName:e,fields:o,tags:l});return{content:[{type:"text",text:JSON.stringify({noteId:h,deck:t.deck,modelName:e},null,2)}]}}async batchCreateNotes(e){if(!e.notes||!Array.isArray(e.notes)||e.notes.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Notes array is required");let t=[],n=e.stopOnError!==!1;for(let i=0;i<e.notes.length;i++){let s=e.notes[i];try{if((await this.ankiClient.getDeckNames()).includes(s.deck)||await this.ankiClient.createDeck(s.deck),!(await this.ankiClient.getModelNames()).includes(s.type))throw new Error(`Note type not found: ${s.type}`);let h=await this.ankiClient.getModelFieldNames(s.type),u={};for(let S of h)u[S]=s.fields[S]||s.fields[S.toLowerCase()]||"";let v=await this.ankiClient.addNote({deckName:s.deck,modelName:s.type,fields:u,tags:s.tags||[],options:{allowDuplicate:e.allowDuplicate||!1}});t.push({success:!0,noteId:v,index:i})}catch(o){if(t.push({success:!1,error:o instanceof Error?o.message:String(o),index:i}),n)break}}return{content:[{type:"text",text:JSON.stringify({results:t,total:e.notes.length,successful:t.filter(i=>i.success).length,failed:t.filter(i=>!i.success).length},null,2)}]}}async searchNotes(e){if(!e.query)throw new r.McpError(r.ErrorCode.InvalidParams,"Search query is required");let t=await this.ankiClient.findNotes(e.query),n=[];if(t.length>0){let i=Math.min(t.length,50);n=await this.ankiClient.notesInfo(t.slice(0,i))}return{content:[{type:"text",text:JSON.stringify({query:e.query,total:t.length,notes:n,limitApplied:t.length>50},null,2)}]}}async getNoteInfo(e){if(!e.noteId)throw new r.McpError(r.ErrorCode.InvalidParams,"Note ID is required");let t=await this.ankiClient.notesInfo([e.noteId]);if(!t||t.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,`Note not found: ${e.noteId}`);return{content:[{type:"text",text:JSON.stringify(t[0],null,2)}]}}async updateNote(e){if(!e.id)throw new r.McpError(r.ErrorCode.InvalidParams,"Note ID is required");if(!e.fields||Object.keys(e.fields).length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Fields are required");let t=await this.ankiClient.notesInfo([e.id]);if(!t||t.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,`Note not found: ${e.id}`);return await this.ankiClient.updateNoteFields({id:e.id,fields:e.fields}),{content:[{type:"text",text:JSON.stringify({success:!0,noteId:e.id},null,2)}]}}async deleteNote(e){if(!e.noteId)throw new r.McpError(r.ErrorCode.InvalidParams,"Note ID is required");return await this.ankiClient.deleteNotes([e.noteId]),{content:[{type:"text",text:JSON.stringify({success:!0,noteId:e.noteId},null,2)}]}}async batchDeleteNotes(e){if(!e.noteIds||!Array.isArray(e.noteIds)||e.noteIds.length===0)throw new r.McpError(r.ErrorCode.InvalidParams,"Note IDs array is required and cannot be empty");let t=e.stopOnError===!0,n=[];if(t)for(let i of e.noteIds)try{await this.ankiClient.deleteNotes([i]),n.push({success:!0,noteId:i})}catch(s){n.push({success:!1,noteId:i,error:s instanceof Error?s.message:String(s)});break}else try{await this.ankiClient.deleteNotes(e.noteIds);for(let i of e.noteIds)n.push({success:!0,noteId:i})}catch{for(let s of e.noteIds)try{await this.ankiClient.deleteNotes([s]),n.push({success:!0,noteId:s})}catch(o){n.push({success:!1,noteId:s,error:o instanceof Error?o.message:String(o)})}}return{content:[{type:"text",text:JSON.stringify({results:n,total:e.noteIds.length,successful:n.filter(i=>i.success).length,failed:n.filter(i=>!i.success).length},null,2)}]}}async guiSelectedNotes(){let e=await this.ankiClient.guiSelectedNotes(),t=[];return e.length>0&&(t=await this.ankiClient.notesInfo(e)),{content:[{type:"text",text:JSON.stringify({selectedNoteIds:e,count:e.length,notes:t},null,2)}]}}async guiCurrentCard(){let e=await this.ankiClient.guiCurrentCard();return e?{content:[{type:"text",text:JSON.stringify(e,null,2)}]}:{content:[{type:"text",text:JSON.stringify({currentCard:null,message:"No card is currently being shown in Anki"},null,2)}]}}};var C=class{constructor(e={}){this.server=new E.Server({name:"anki-connect-server",version:x},{capabilities:{tools:{},resources:{}}});let t=e.ankiConnectKey?{apiKey:e.ankiConnectKey}:{};this.ankiClient=new m(t),this.resourceHandler=new k(t),this.toolHandler=new N(t),this.setupHandlers(),this.server.onerror=n=>console.error("[MCP Error]",n),process.on("SIGINT",async()=>{await this.server.close(),process.exit(0)})}setupHandlers(){this.server.setRequestHandler(c.ListResourcesRequestSchema,async()=>(await this.checkConnection(),this.resourceHandler.listResources())),this.server.setRequestHandler(c.ListResourceTemplatesRequestSchema,async()=>(await this.checkConnection(),this.resourceHandler.listResourceTemplates())),this.server.setRequestHandler(c.ReadResourceRequestSchema,async e=>(await this.checkConnection(),this.resourceHandler.readResource(e.params.uri))),this.server.setRequestHandler(c.ListToolsRequestSchema,async()=>this.toolHandler.getToolSchema()),this.server.setRequestHandler(c.CallToolRequestSchema,async e=>(await this.checkConnection(),this.toolHandler.executeTool(e.params.name,e.params.arguments)))}async checkConnection(){try{await this.ankiClient.checkConnection()}catch{throw new c.McpError(c.ErrorCode.InternalError,"Failed to connect to Anki. Please make sure Anki is running and the AnkiConnect plugin is enabled.")}}async run(){let e=new b.StdioServerTransport;await this.server.connect(e),console.error("Anki MCP server running on stdio")}};function M(){let a=process.argv.slice(2),e;for(let t=0;t<a.length;t++)a[t]==="--anki-connect-key"&&t+1<a.length&&(e=a[t+1],e=e.trim(),e.startsWith('"')&&e.endsWith('"')&&(e=e.slice(1,-1).trim()),e.startsWith("'")&&e.endsWith("'")&&(e=e.slice(1,-1).trim()),t++);return e&&console.error("[DEBUG] API key configured"),{ankiConnectKey:e}}async function T(){try{let{ankiConnectKey:a}=M();await new C({ankiConnectKey:a}).run()}catch(a){console.error("Failed to start Anki MCP Server:",a),process.exit(1)}}T().catch(console.error);