Skip to main content
Glama

Anki MCP

model.ts15.9 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { ankiClient } from '../utils/ankiClient.js'; /** * Register all model-related tools with the MCP server */ export function registerModelTools(server: McpServer) { // Tool: Create a new model server.tool( 'create_model', { modelName: z.string().describe('Name of the model to create'), inOrderFields: z.array(z.string()).describe('Array of field names in order'), cardTemplates: z .array( z .object({ Front: z.string().describe('Front template content'), Back: z.string().describe('Back template content'), }) .and(z.record(z.string())) ) .describe('Array of card templates with Front and Back content'), css: z.string().optional().describe('Custom CSS styling for the model'), isCloze: z.boolean().optional().describe('Whether the model should be a cloze type'), }, async ({ modelName, inOrderFields, cardTemplates, css, isCloze }) => { try { const modelParams: { modelName: string; inOrderFields: string[]; cardTemplates: { [key: string]: string; Back: string; Front: string }[]; css?: string; isCloze?: boolean; } = { modelName, inOrderFields, cardTemplates, }; if (css !== undefined) { modelParams.css = css; } if (isCloze !== undefined) { modelParams.isCloze = isCloze; } const result = await ankiClient.model.createModel(modelParams); return { content: [ { type: 'text', text: `Successfully created model "${modelName}" with ID: ${result.id}`, }, ], }; } catch (error) { throw new Error( `Failed to create model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Find and replace in models server.tool( 'find_and_replace_in_models', { modelName: z.string().describe('Name of the model to search in'), fieldText: z.string().describe('Text to find'), replaceText: z.string().describe('Text to replace with'), front: z.boolean().describe('Whether to search in front templates'), back: z.boolean().describe('Whether to search in back templates'), css: z.boolean().describe('Whether to search in CSS'), }, async ({ modelName, fieldText, replaceText, front, back, css }) => { try { const count = await ankiClient.model.findAndReplaceInModels({ model: { modelName, fieldText, replaceText, front, back, css, }, }); return { content: [ { type: 'text', text: `Successfully replaced ${count} occurrences of "${fieldText}" with "${replaceText}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to find and replace in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Add field to model server.tool( 'model_field_add', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field to add'), index: z.number().describe('Position to insert the field (0-based)'), }, async ({ modelName, fieldName, index }) => { try { await ankiClient.model.modelFieldAdd({ modelName, fieldName, index, }); return { content: [ { type: 'text', text: `Successfully added field "${fieldName}" to model "${modelName}" at position ${index}`, }, ], }; } catch (error) { throw new Error( `Failed to add field "${fieldName}" to model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Remove field from model server.tool( 'model_field_remove', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field to remove'), }, async ({ modelName, fieldName }) => { try { await ankiClient.model.modelFieldRemove({ modelName, fieldName, }); return { content: [ { type: 'text', text: `Successfully removed field "${fieldName}" from model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to remove field "${fieldName}" from model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Rename field in model server.tool( 'model_field_rename', { modelName: z.string().describe('Name of the model'), oldFieldName: z.string().describe('Current name of the field'), newFieldName: z.string().describe('New name for the field'), }, async ({ modelName, oldFieldName, newFieldName }) => { try { await ankiClient.model.modelFieldRename({ modelName, oldFieldName, newFieldName, }); return { content: [ { type: 'text', text: `Successfully renamed field "${oldFieldName}" to "${newFieldName}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to rename field in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Reposition field in model server.tool( 'model_field_reposition', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field to reposition'), index: z.number().describe('New position for the field (0-based)'), }, async ({ modelName, fieldName, index }) => { try { await ankiClient.model.modelFieldReposition({ modelName, fieldName, index, }); return { content: [ { type: 'text', text: `Successfully repositioned field "${fieldName}" to position ${index} in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to reposition field "${fieldName}" in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Set field description server.tool( 'model_field_set_description', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field'), index: z.number().describe('Index of the field'), }, async ({ modelName, fieldName, index }) => { try { await ankiClient.model.modelFieldSetDescription({ modelName, fieldName, index, }); return { content: [ { type: 'text', text: `Successfully set description for field "${fieldName}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to set description for field "${fieldName}" in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Set field font server.tool( 'model_field_set_font', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field'), font: z.string().describe('Font name to set'), }, async ({ modelName, fieldName, font }) => { try { await ankiClient.model.modelFieldSetFont({ modelName, fieldName, font, }); return { content: [ { type: 'text', text: `Successfully set font "${font}" for field "${fieldName}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to set font for field "${fieldName}" in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Set field font size server.tool( 'model_field_set_font_size', { modelName: z.string().describe('Name of the model'), fieldName: z.string().describe('Name of the field'), fontSize: z.number().describe('Font size to set'), }, async ({ modelName, fieldName, fontSize }) => { try { await ankiClient.model.modelFieldSetFontSize({ modelName, fieldName, fontSize, }); return { content: [ { type: 'text', text: `Successfully set font size ${fontSize} for field "${fieldName}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to set font size for field "${fieldName}" in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Add template to model server.tool( 'model_template_add', { modelName: z.string().describe('Name of the model'), template: z .object({ Front: z.string().describe('Front template content'), Back: z.string().describe('Back template content'), }) .and(z.record(z.string())) .describe('Template object with Front and Back content'), }, async ({ modelName, template }) => { try { await ankiClient.model.modelTemplateAdd({ modelName, template, }); return { content: [ { type: 'text', text: `Successfully added template to model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to add template to model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Remove template from model server.tool( 'model_template_remove', { modelName: z.string().describe('Name of the model'), templateName: z.string().describe('Name of the template to remove'), }, async ({ modelName, templateName }) => { try { await ankiClient.model.modelTemplateRemove({ modelName, templateName, }); return { content: [ { type: 'text', text: `Successfully removed template "${templateName}" from model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to remove template "${templateName}" from model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Rename template in model server.tool( 'model_template_rename', { modelName: z.string().describe('Name of the model'), oldTemplateName: z.string().describe('Current name of the template'), newTemplateName: z.string().describe('New name for the template'), }, async ({ modelName, oldTemplateName, newTemplateName }) => { try { await ankiClient.model.modelTemplateRename({ modelName, oldTemplateName, newTemplateName, }); return { content: [ { type: 'text', text: `Successfully renamed template "${oldTemplateName}" to "${newTemplateName}" in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to rename template in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Reposition template in model server.tool( 'model_template_reposition', { modelName: z.string().describe('Name of the model'), templateName: z.string().describe('Name of the template to reposition'), index: z.number().describe('New position for the template (0-based)'), }, async ({ modelName, templateName, index }) => { try { await ankiClient.model.modelTemplateReposition({ modelName, templateName, index, }); return { content: [ { type: 'text', text: `Successfully repositioned template "${templateName}" to position ${index} in model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to reposition template "${templateName}" in model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Update model styling server.tool( 'update_model_styling', { modelName: z.string().describe('Name of the model'), css: z.string().describe('New CSS styling for the model'), }, async ({ modelName, css }) => { try { await ankiClient.model.updateModelStyling({ model: { name: modelName, css, }, }); return { content: [ { type: 'text', text: `Successfully updated styling for model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to update styling for model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); // Tool: Update model templates server.tool( 'update_model_templates', { modelName: z.string().describe('Name of the model'), templates: z .record( z.object({ Front: z.string().optional().describe('Front template content'), Back: z.string().optional().describe('Back template content'), }) ) .describe('Object mapping template names to their Front/Back content'), }, async ({ modelName, templates }) => { try { // Filter out undefined values from templates const cleanedTemplates: Record<string, { Back?: string; Front?: string }> = {}; for (const [templateName, template] of Object.entries(templates)) { if (template && typeof template === 'object') { cleanedTemplates[templateName] = {}; const templateObj = template as { Front?: string; Back?: string }; if (templateObj.Front !== undefined) { cleanedTemplates[templateName].Front = templateObj.Front; } if (templateObj.Back !== undefined) { cleanedTemplates[templateName].Back = templateObj.Back; } } } await ankiClient.model.updateModelTemplates({ model: { name: modelName, templates: cleanedTemplates, }, }); return { content: [ { type: 'text', text: `Successfully updated templates for model "${modelName}"`, }, ], }; } catch (error) { throw new Error( `Failed to update templates for model "${modelName}": ${error instanceof Error ? error.message : String(error)}` ); } } ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/arielbk/anki-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server