How to Create Your Own MCP Server
Model Context Protocol (MCP) is a communication standard that allows AI assistants to connect with external tools and services. In this guide, I'll show you how to create your own MCP server using an integration with AnkiConnect - an API for the popular learning application Anki.
What is AnkiConnect?
AnkiConnect is an add-on for the Anki application that provides a REST API allowing programmatic control over flashcards. With it, we can:
- Create new flashcards
- Manage card decks
- Search and modify existing notes
- Control the Anki user interface
AnkiConnect runs as a local HTTP server on port 8765, accepting JSON requests and returning responses in the same format.
MCP Project Structure
Before we move to implementation, let's look at our project structure:
mcp-server/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # Main MCP server file
│ ├── methods/
│ │ └── anki.ts # AnkiConnect communication
│ ├── tools/
│ │ └── notes.ts # Note management tools
│ └── types/
│ └── anki/
│ └── notes.ts # TypeScript type definitions
Dependency Configuration
The first step is configuring package.json
with necessary dependencies:
{
"name": "mcp-server",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"axios": "^1.9.0"
},
"devDependencies": {
"@types/node": "^22.15.29",
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
}
}
Key dependencies:
@modelcontextprotocol/sdk
- Official SDK for creating MCP serversaxios
- HTTP library for AnkiConnect communicationtypescript
- For type safety
Implementing AnkiConnect Communication
Let's start by creating the AnkiConnect communication module in src/methods/anki.ts
:
import axios from 'axios';
import { AnkiConnectResponse, AnkiConnectRequest } from '../types/anki';
const BASE_URL = 'http://localhost:8765';
const api = axios.create({
baseURL: BASE_URL,
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});
export async function callAnkiConnect<T = any>(
request: AnkiConnectRequest
): Promise<AnkiConnectResponse<T>> {
try {
const response = await api.post<AnkiConnectResponse<T>>('/', request);
return response.data;
} catch(error) {
console.log('Anki Connect Error:', error);
throw error;
}
}
The callAnkiConnect()
function serves as a universal interface for communicating with the AnkiConnect API.
TypeScript Type Definitions
For type safety, we define data structures in src/types/anki/notes.ts
:
export interface Note {
deckName: string;
modelName: string;
fields: Record<string, string>;
tags?: string[];
audio?: NoteAudio;
}
export interface NoteAudio {
url: string;
filename: string;
skipHash?: string;
fields: string;
}
export interface AddNoteResponse extends AnkiConnectResponse<number | null> {
result: number | null; // Note ID if success, null if error
}
Creating the Main MCP Server
Now let's move to the most important part - implementing the MCP server in src/index.ts
:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { callAnkiConnect } from "./methods/anki.js";
// MCP server initialization
const server = new McpServer({
name: 'anki-connect mcp',
version: '0.0.1'
});
The McpServer
class from the @modelcontextprotocol/sdk
package is the foundation of our server.
Implementing the create_flashcard Tool
The most important part is implementing the flashcard creation tool. Here's how the create_flashcard
tool looks:
server.tool(
'create_flashcard',
{
front: z.string(),
back: z.string()
},
async ({ front, back }) => {
const mcpFlashCard = {
action: "addNote",
version: 6,
params: {
note: {
deckName: "Default",
modelName: "Basic",
fields: {
Front: front,
Back: back
},
tags: ["ai"],
options: {
allowDuplicate: false
}
}
}
};
const responseData = await callAnkiConnect(mcpFlashCard);
return {
content: [{
type: "text",
text: JSON.stringify(responseData)
}]
};
}
);
Implementation Analysis:
-
Schema Definition: We use the
zod
library for input parameter validation:{ front: z.string(), back: z.string() }
-
AnkiConnect Request Structure: We create an object compliant with the AnkiConnect API:
action: "addNote"
- note addition actionversion: 6
- API versionparams.note
- note details
-
Note Configuration:
deckName: "Default"
- default deckmodelName: "Basic"
- basic card model (front/back)fields
- card field mappingtags: ["ai"]
- automatic taggingallowDuplicate: false
- prevent duplicates
-
API Call: We use the
callAnkiConnect()
function to send the request -
Result Return: We format the response according to the MCP standard
Running the Server
At the end of the src/index.ts
file, we add the code to run the server:
// Server startup
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log('Anki MCP Server started!');
}
main().catch(console.error);
Usage Example
After starting the server, host i.e. an AI assistant can use the create_flashcard
tool as follows:
{
"tool": "create_flashcard",
"parameters": {
"front": "What is MCP?",
"back": "Model Context Protocol - a communication standard between AI and external tools"
}
}
Extending Functionality
Our example shows only one tool, but an MCP server can offer many tools. In a full implementation, we can add:
add_note
- more advanced note creationfind_notes
- searching existing notesupdate_note_fields
- modifying note fieldsdeck_names
- deck management
Summary
We've created a functional MCP server that:
- Connects to AnkiConnect API via HTTP
- Provides the
create_flashcard
tool for AI assistants - Validates input data using Zod schema
- Handles errors and logs communication
- Returns formatted responses compliant with the MCP standard
This example shows how easy it is to integrate any API with the MCP ecosystem, opening new possibilities for AI assistants. Thanks to MCP, assistants can not only converse but also perform concrete actions in external applications.
The complete project code is available in the repository on my GitHub, where you'll also find implementations of additional tools and more advanced functionalities.