最近MCP又火出圈,MCP+Cursor 效率提升王炸组合,开发自己的MCP Server是新AI时代下前端的必备姿势,本文介绍将通过手把手带你实现一个简易的 TODO MCP Server,来带你快速入门,解决开发新境界。
一、MCP 简介
1、MCP 是什么
MCP(Model Context Protocol)是一个革命性的工具,它让我们能够将自己的服务和各种 LLM 客户端(如 Claude、Cursor)无缝连接。简单来说,它就是一个桥梁,让 AI 能够直接调用我们的服务。
MCP 客户端(Client)
:通常是 AI 应用程序(如 Claude Desktop 或其他 LLM 工具),负责发起请求并与服务器通信。MCP 服务器(Server)
:轻量级程序,负责暴露特定的数据源或工具功能,并通过标准化协议与客户端交互。
2、为什么 MCP-Server 如此重要?
-
扩展 AI 能力:通过 MCP-Server,我们可以让 AI 直接操作我们的数据库、调用我们的 API、控制我们的服务,极大扩展了 AI 的实际应用能力。
-
标准化通信:MCP 提供了一个标准的通信协议,使得不同的 AI 模型都能以相同的方式与我们的服务交互,大大降低了集成的复杂度。
3、应用场景:
自动化你的开发工作流、快速创建 AI 驱动的工具和插件等等,万物皆可MCP。可以预见通过MCP,未来我们可以快速提高生产力
二、实现自己的MCP-Server
在本教程中,我们将通过实现一个简单的 TODO MCP Server,来带你手把手实现你的MCP-Server。我们将使用 Cursor 作为我们的 MCP Client,让 Cursor 通过 MCP Server 来操作我们的 TODO 服务。
1. 工程初始化相关配置
bash
mkdir mcp-server-demo
cd mcp-server-demo
# Initialize a new npm project
npm init -y
- 安装依赖
bash
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# Create our files
mkdir src
touch src/index.ts
- 修改package.json
javascript
{
"type": "module",
"bin": {
"mymcp": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
]
}
- 新增tsconfig.json
javascript
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
2. 服务开发
- 在入口文件中引入依赖,并进行服务实例化 将下面代码添加到src/index.ts顶部
javascript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// Create server instance
const server = new McpServer({
name: "TODO",
version: "1.0.0",
});
// Start server
async function main(){
}
- 数据传输(创建MCP服务端、客户端之间的链接)
数据传输主要是MCP Server和MCP Client间的通信方式,根据官方说明,主要有两种方式:
- 本地通信
- 对本地进程使用 stdio 传输
- 高效实现同机通讯
- 简单的流程管理
- 远程通信: HTTP方式,使用 SSE
我们的 TODO demo 主要是用本地通信方式
1) 按照mcp官方文档引入sdk并创建链接,更新index.ts
javascript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// Import
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
// Create server instance
const server = new McpServer({
name: "TODO",
version: "1.0.0",
});
async function main(){
// Connect Transport
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(error => {
console.error(error);
process.exit(1);
})
-
工具函数
工具是MCP服务端向客户端(如Cursor、Claude)公开的可供LLM执行的功能 下面,让我们新增增删查的工具函数,继续完善我们的TODO MCP Server Demo
javascript
// Add Tools
server.tool(
"add-todo",
{
text: z.string()
},
async({text})=>{
return {
content: [
{
type: "text",
text: `${text} was added to our todo with ID 99`
}
]
}
}
)
server.tool(
"get-todo",
{
text: z.string()
},
async({text})=>{
return {
content: [
{
type: "text",
text: `Drink Water(ID 99)`
}
]
}
}
)
server.tool(
"remove-todo",
{
id: z.number()
},
async({id})=>{
return {
content: [
{
type: "text",
text: `TODO ${id} was removed`
}
]
}
}
)
3.在Cursor中引入
以上,我们就已经把简易的服务实现完成,包含了工程初始化、创建工具函数以及服务的启动,下面我们来把它链接到Cursor
- 先完成服务编译
arduino
npm run build
- 复制产物的路径
- 打开cursor的设置,新增mcp-server
到这里可以验证我们的MCP Server 可以正常工作
4. 完善Demo,让 ToDo List 动态化
- 借助npm包 better-sqlite3 来快速实现数据库
bash
npm install better-sqlite3 @types/better-sqlite3
- 新建database.ts文件 下面这个过程是为了让todolist动态化,代码的修改可以让cursor直接完成
ts
import Database from "better-sqlite3";
import path from "path";
import os from 'os';
import { fileURLToPath } from 'url';
// 定义 Todo 类型
export interface Todo {
id: number;
text: string;
}
// Get directory path for ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 数据库文件位置 - 使用项目目录
const DB_LOCATION = '/Users/hongmiaomiao/git/mcp-server-demo/todo.db';
// 创建数据库连接
const db = new Database(DB_LOCATION);
// 创建 todos 表 - 简化表结构
db.exec(`
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL
)
`);
// 数据库操作函数
const dbOperations = {
// 添加 todo
addTodo(text: string): Todo {
const stmt = db.prepare('INSERT INTO todos (text) VALUES (?)');
const result = stmt.run(text);
return {
id: result.lastInsertRowid as number,
text
};
},
// 获取 todo by text
getTodoByText(text: string): Todo[] {
const stmt = db.prepare('SELECT * FROM todos WHERE text LIKE ?');
return stmt.all(`%${text}%`) as Todo[];
},
// 删除 todo
deleteTodo(id: number): void {
const stmt = db.prepare('DELETE FROM todos WHERE id = ?');
stmt.run(id);
}
};
export const { addTodo, getTodoByText, deleteTodo } = dbOperations;
三、效果测试
可以看到现在我们的todo mcp-server成功实现了
作者是素材搬运工,本文为作者学习笔记,源出处 YouTube相关视频