基于TypeScript的MCP服务器模板:从零构建AI助手扩展能力
1. 项目概述:一个为 Cursor 构建的 MCP 服务器模板
如果你和我一样,日常重度依赖 Cursor 这类 AI 驱动的代码编辑器,那你肯定不止一次想过:能不能让 AI 助手更深入地“理解”和“操作”我自己的项目数据?比如,让它直接读取我本地的 Markdown 笔记、管理待办事项,甚至调用我写的内部 API。这就是 MCP(Model Context Protocol)要解决的问题,而ndlxp2008/mcp-temple这个项目,为我们提供了一个绝佳的起点。
简单来说,这是一个基于 TypeScript 的 MCP 服务器模板。它不是一个功能完备的产品,而是一个清晰、简洁的“脚手架”。它演示了如何构建一个能与 Cursor(或任何兼容 MCP 的客户端)通信的服务器,实现“资源”的列举与访问、“工具”的调用以及“提示”的构建。原作者用它实现了一个微型笔记系统,但这只是冰山一角。通过这个模板,你可以快速上手,将其改造成连接你私有数据库、内部服务或特定文件格式的桥梁,极大地扩展 AI 助手在你工作流中的能力边界。
对于前端/全栈开发者、效率工具爱好者,或者任何想深度定制 AI 工作流的人来说,这个模板的价值在于它剥离了 MCP 的复杂性,直击核心概念。你不用从零开始研究协议细节,而是直接在一个可运行、可调试的例子上进行魔改。接下来,我将带你从零开始,不仅复现这个模板,更深入理解其每一行代码背后的设计逻辑,并分享如何基于它打造属于你自己的、功能强大的 MCP 服务器。
2. MCP 核心概念与模板设计思路拆解
在动手之前,我们必须先搞清楚 MCP 到底是什么,以及这个模板是如何围绕其核心思想构建的。你可以把 MCP 想象成 AI 助手(客户端)和外部数据/服务(服务器)之间的一种“通用翻译协议”。它定义了双方沟通的语言和规则,使得 AI 能够安全、结构化地请求信息或执行操作。
2.1 MCP 的三大支柱:资源、工具与提示
MCP 协议主要围绕三个核心概念展开,这也是模板项目md-emoji-mcp重点演示的部分:
- 资源:代表 AI 可以“读取”的数据实体。在模板中,资源就是一条条笔记,通过
note://{id}这样的统一资源标识符来访问。服务器告诉客户端:“我这里有这些资源(URI 列表),你可以按需读取它们的内容。” 这解决了 AI 访问静态或动态数据的问题。 - 工具:代表 AI 可以“调用”的操作或函数。在模板中,
create_note就是一个工具。AI 助手在理解用户意图后(例如,“创建一条标题为‘会议纪要’的笔记”),会通过 MCP 协议调用这个工具,并传入参数。这解决了 AI 执行动态操作的问题。 - 提示:这是一种更高级的“资源组合包”。它允许服务器预先定义好一个包含特定资源(上下文)和指令(系统提示)的模板,供 AI 直接使用。模板中的
summarize_notes提示,就自动包含了所有笔记的内容作为上下文,引导 AI 生成摘要。这简化了复杂任务的上下文构建过程。
这个模板的设计思路非常清晰:用一个最小化但功能完整的例子(笔记系统),来逐一演示如何实现这三大概念。它没有引入数据库、复杂权限等干扰项,让你可以专注于协议本身的实现逻辑。代码结构干净,类型定义完善(得益于 TypeScript),是绝佳的学习和改造对象。
2.2 为什么选择 TypeScript 和这个架构?
原作者选择 TypeScript 和@modelcontextprotocol/sdk这个官方 SDK 是经过考量的。TypeScript 提供了优秀的类型安全和开发体验,这对于实现一个需要严格遵循协议规范的服务器至关重要,能避免许多低级错误。官方 SDK 则封装了协议通信、序列化/反序列化等底层细节,让我们可以专注于业务逻辑。
项目的架构是典型的 Node.js 应用结构:
src/index.ts:入口文件,初始化服务器并注册资源、工具、提示的处理逻辑。src/server.ts:核心服务器逻辑,定义了资源列表、工具实现等。package.json:定义了构建脚本和依赖。- 构建输出到
build/目录。
这种结构分离了协议处理(SDK)和业务逻辑,使得代码易于理解和扩展。当你需要添加一个新工具(比如“删除笔记”)时,你几乎可以照葫芦画瓢,在server.ts中添加相应的处理函数即可。
3. 环境准备与项目初始化实操
理论说得再多,不如动手跑起来。我们首先需要把这个模板项目克隆到本地,并配置好开发环境。
3.1 获取模板代码与依赖安装
打开你的终端,执行以下命令来获取代码:
# 克隆模板仓库 git clone https://github.com/ndlxp2008/mcp-temple.git # 进入项目目录 cd mcp-temple # 安装项目依赖 (SDK、TypeScript编译器等) npm install这里有个实操心得:由于网络环境差异,npm install有时可能会因为官方 SDK 的包源问题而缓慢或失败。如果你遇到这种情况,可以尝试以下两种方案:
- 检查你的 Node.js 版本,建议使用最新的 LTS 版本(如 18.x, 20.x)。
- 如果速度太慢,可以考虑配置 npm 镜像源,但请注意,对于
@modelcontextprotocol这类作用域包,镜像可能同步不及时。最稳妥的方式是耐心等待或使用稳定的网络环境。
安装完成后,你的项目目录下会生成node_modules文件夹。此时,你可以尝试运行构建命令,验证环境是否正常:
# 编译 TypeScript 代码到 build 目录 npm run build如果看到build目录成功生成,并且没有报错,说明基础环境已经就绪。
3.2 开发与调试模式配置
在开发过程中,我们经常需要修改代码并立即看到效果。模板提供了watch模式:
# 启动监听模式,文件变动后自动重新编译 npm run watch保持这个终端运行,然后我们另开一个终端进行调试。这里是一个关键技巧:直接运行编译后的 JS 文件可能无法直观地调试。模板非常贴心地集成了MCP Inspector的调试脚本。
MCP Inspector 是一个独立的调试工具,它可以充当 MCP 客户端和服务器之间的“中间人”,让我们在浏览器中清晰地看到所有的协议请求和响应,这对于理解数据流和排查问题至关重要。
# 在项目根目录下,启动 Inspector npm run inspector运行后,终端会输出一个本地 URL(通常是http://localhost:5173)。用浏览器打开它,你会看到 Inspector 的界面。现在,我们需要让 Inspector 启动我们的 MCP 服务器。在 Inspector 界面上,通常会有一个输入框让你指定服务器启动命令。
由于我们的代码在watch模式下会持续编译,build/index.js始终是最新的。因此,在 Inspector 的命令输入框中,你应该填写:
node /你的/项目/绝对/路径/mcp-temple/build/index.js注意:请务必将路径替换为你本地项目的绝对路径。在 macOS/Linux 下,你可以使用
pwd命令获取当前路径;在 Windows 的 PowerShell 中,可以使用pwd或$PWD.Path。
配置好后,在 Inspector 中连接服务器。如果一切正常,你将在左侧看到服务器声明的资源列表(note://开头的 URI)和工具列表(create_note),右侧可以看到实时的通信日志。这是你探索 MCP 协议运行机制的“显微镜”。
4. 核心代码解析与自定义改造指南
现在,项目已经跑起来了。让我们深入src/server.ts这个核心文件,看看它是如何实现 MCP 概念的,并以此为基础,规划如何改造它。
4.1 资源管理:如何让 AI “看到”你的数据
在server.ts中,寻找resources相关的代码。模板使用一个内存中的Map对象来模拟存储笔记:
// 模拟一个简单的内存存储 const notes = new Map<string, { title: string; content: string }>();当客户端(如 Cursor)请求列出资源时,服务器会调用listResources方法。该方法遍历notes这个 Map,为每个笔记生成一个资源描述:
new Resource( `note://${id}`, // URI,资源的唯一标识 { title: note.title, description: `A note titled "${note.title}"`, // 可以在这里添加更多自定义元数据,如创建时间、标签等 } )当客户端根据某个 URI(如note://1)请求读取资源内容时,服务器会调用readResource方法,从 Map 中查找对应的笔记,并将其内容以纯文本形式返回。
改造思路 1:连接真实数据源你的数据不可能永远放在内存里。你可以将这里的Map替换成任何持久化存储:
- 本地文件系统:将笔记存储为
./notes/{id}.md文件。listResources改为读取notes目录下的所有文件生成列表;readResource改为读取对应文件内容。 - 数据库:连接 SQLite、PostgreSQL 或 MongoDB。在
listResources中执行SELECT查询;在readResource中根据 ID 查询具体内容。 - 第三方 API:如果你的数据在 Notion、GitHub Issues 或某个内部服务中,可以在这里发起 HTTP 请求来获取资源列表和内容。
关键点:URI 的设计要有意义且稳定。例如,文件系统可以用file://notes/{filename},数据库记录可以用db://notes/{record_id},API 数据可以用api://issues/{issue_number}。这有助于 AI 和开发者理解资源的来源。
4.2 工具实现:如何让 AI “操作”你的系统
工具的定义在tools属性或listTools方法中。模板定义了一个create_note工具:
{ name: "create_note", description: "Create a new text note", inputSchema: { type: "object", properties: { title: { type: "string", description: "The title of the note" }, content: { type: "string", description: "The content of the note" }, }, required: ["title", "content"], }, }当 AI 调用此工具时,会触发callTool方法中对应的处理逻辑。模板中的逻辑很简单:生成一个 ID,将标题和内容存入notes这个 Map。
改造思路 2:实现增删改查全套工具一个笔记系统显然不止创建。我们可以很容易地添加新工具:
delete_note:在listTools中添加定义,接收一个id参数。在callTool中实现从 Map(或数据库)中删除对应记录的逻辑。update_note:接收id,title,content参数,实现更新逻辑。search_notes:接收keyword参数,在notes中过滤标题或内容包含关键词的记录,并返回一个资源列表。这甚至不需要修改存储,只需在callTool中动态生成一批Resource对象返回即可,MCP 协议支持这种动态资源。
注意事项:工具的参数定义 (inputSchema) 要尽可能清晰、详细。好的description能帮助 AI 更好地理解何时以及如何使用这个工具。对于复杂参数,可以使用 JSON Schema 定义嵌套结构。
4.3 提示构建:如何为 AI 预设复杂任务上下文
提示在prompts属性中定义。模板中的summarize_notes提示非常经典:
{ name: "summarize_notes", description: "Get a summary of all notes", messages: [ { role: "user", content: { type: "text", text: "Please summarize the following notes:", }, }, ], }它的特殊之处在于messages[0].content里可以嵌入资源。在getPrompt方法中,服务器会将所有笔记的内容,作为“嵌入资源”填充到提示消息中。这样,当客户端获取这个提示时,它已经是一个包含了全部笔记内容作为上下文的、立即可用的对话起点。
改造思路 3:创建领域特定的提示模板这是发挥创造力的地方。你可以为你的数据定制各种提示:
weekly_report:嵌入本周创建的所有笔记或任务,提示 AI 生成周报。find_conflicts:嵌入项目计划文档和当前任务列表,提示 AI 分析时间或资源冲突。brainstorm_from_notes:嵌入你收集的灵感碎片笔记,提示 AI 进行头脑风暴,生成创意大纲。
核心技巧:提示中的“嵌入资源”是动态的。在getPrompt方法里,你可以根据当前时间、用户身份或其他条件,动态决定嵌入哪些资源,使得每个提示都高度个性化。
5. 集成到 Cursor 及其他客户端的完整流程
开发调试完成后,我们需要让 MCP 服务器真正被 AI 助手使用。这里以 Claude Desktop(Cursor 的原理类似)为例,讲解配置过程。
5.1 配置 Claude Desktop 连接 MCP 服务器
MCP 服务器通过标准输入输出与客户端通信。我们需要在 Claude Desktop 的配置文件中声明我们的服务器。
定位配置文件:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
如果文件或目录不存在,请手动创建。
- macOS:
编辑配置文件:使用任何文本编辑器打开(或创建)该 JSON 文件。其核心结构是
mcpServers对象。{ "mcpServers": { "my-note-server": { "command": "node", "args": ["/ABSOLUTE/PATH/TO/YOUR/mcp-temple/build/index.js"], "env": { // 可以在这里传递环境变量,例如数据库连接字符串 "MY_SECRET_KEY": "your-secret" } } } }"my-note-server":这是你给服务器起的名字,会在客户端界面显示。command:启动服务器的命令。对于 Node.js 程序,就是node。args:命令的参数,即编译后的 JS 文件绝对路径。这里必须使用绝对路径,相对路径会无法找到。env:可选。可以为你服务器的进程设置环境变量,用于传递配置(如 API 密钥),避免硬编码在代码中。
重启 Claude Desktop:保存配置文件后,完全退出并重启 Claude Desktop 应用。
5.2 在 Cursor 中验证与使用
重启后,当你新建一个对话,你应该能在输入框附近或设置里看到可用的工具(不同客户端界面可能不同)。在 Claude Desktop 中,通常会在输入框上方出现工具图标。
你可以尝试直接对 AI 说:“使用我的笔记工具,创建一条标题为‘购物清单’的笔记,内容是‘牛奶、鸡蛋、面包’。” AI 应该能识别出可用的create_note工具,并调用它。调用成功后,你可以继续说:“列出我所有的笔记。” AI 则会通过listResources获取到刚创建的笔记资源,并将其内容作为上下文来回答你。
排查技巧:如果工具没有出现或调用失败,请按以下步骤排查:
- 检查配置文件语法:确保 JSON 格式正确,没有尾随逗号。
- 检查路径:确保
args中的 JS 文件路径绝对正确且文件存在。 - 查看客户端日志:Claude Desktop 通常有日志文件。在 macOS 上,可以在
~/Library/Logs/Claude/目录下查找;Windows 则在%APPDATA%/Claude/logs。查看日志中是否有关于启动 MCP 服务器的错误信息。 - 回归 Inspector:用 Inspector 再次测试你的服务器,确保其本身功能正常。
- 权限问题:确保 Node.js 脚本有可执行权限。
5.3 适配其他 MCP 客户端
MCP 是一个开放协议,除了 Claude Desktop,未来会有越来越多的客户端支持。配置原理大同小异,都是通过某种方式指定服务器启动命令。关注你所用客户端的官方文档即可。
6. 进阶实战:从笔记模板到个性化 MCP 服务器
掌握了基础,我们就可以放飞想象力,将这个笔记模板改造成任何我们需要的工具。下面分享两个我实践过的改造方向。
6.1 案例一:构建项目管理 MCP 服务器
假设我们有一个简单的本地项目管理 JSON 文件projects.json。我们可以改造服务器来管理它。
第一步:定义资源。资源可以是项目 (project://{id})、任务 (task://{project_id}/{task_id})。listResources会读取 JSON 文件,生成项目和任务的 URI 列表。
第二步:定义工具。
create_project: 新增项目。create_task: 在指定项目下创建任务。update_task_status: 更新任务状态(待办、进行中、完成)。get_overdue_tasks: 动态生成一个资源列表,包含所有过期未完成的任务。
第三步:定义提示。
project_status_report: 嵌入当前所有项目的资源,提示 AI 生成状态报告。prioritize_tasks: 嵌入本周末完成的任务,提示 AI 根据工作量进行优先级排序建议。
技术要点:在readResource方法中,需要根据不同的 URI 前缀(project://或task://)来解析 ID,并从 JSON 文件的不同位置读取数据。注意文件读写需要异步操作 (fs.promises)。
6.2 案例二:构建代码仓库分析 MCP 服务器
这个服务器不管理数据,而是提供对本地 Git 仓库的分析能力。
第一步:定义工具(核心)。资源可能不是必须的,我们可以主要暴露工具。
analyze_git_log: 接收repo_path和since参数,执行git log --oneline --since={since},将结果返回给 AI 分析提交趋势。find_large_files: 执行git ls-files | xargs ls -l | sort -nrk5 | head -10,找出仓库中最大的10个文件。get_file_summary: 接收file_path,读取文件内容,并调用一个本地 LLM(或简单的摘要算法)生成简短摘要返回。
第二步:安全考虑。这类服务器需要特别注意。
- 路径限制:在工具实现中,必须将操作限制在指定的安全目录内,防止通过
../../../这样的路径遍历访问系统文件。 - 命令注入:如果工具涉及执行 Shell 命令(如调用 git),务必对输入参数进行严格的校验和转义,绝对避免直接将用户输入拼接成命令字符串。最好使用 Node.js 的
child_process.spawn并传递参数数组。
改造心得:从模板出发,最关键的一步是明确你的服务器要解决的“核心交互”是什么。是让 AI 查询数据(资源为主)?还是让 AI 执行操作(工具为主)?还是为 AI 准备一个复杂的分析场景(提示为主)?想清楚这一点,代码改造就有了明确的方向。
7. 开发、调试与部署中的常见问题
在实际开发和运行中,你可能会遇到一些坑。这里记录下我遇到过的典型问题及其解决方案。
7.1 协议通信与调试问题
| 问题现象 | 可能原因 | 排查与解决 |
|---|---|---|
| Inspector 连接失败,提示“连接错误”或“无法启动服务器” | 1. 启动命令或路径错误。 2. 服务器代码存在语法错误,启动即崩溃。 3. 端口冲突(Inspector 自身)。 | 1. 在终端手动运行node /path/to/build/index.js,看是否有错误输出。2. 检查 npm run build是否成功,确保编译无报错。3. 尝试关闭 Inspector 重启,或更换 Inspector 的端口。 |
| 客户端(如 Claude Desktop)中看不到工具 | 1. 配置文件路径或格式错误。 2. 服务器启动失败,客户端静默忽略。 3. 客户端缓存了旧的配置。 | 1. 使用console.error()在服务器初始化时输出日志,确认客户端是否成功启动了进程。2. 检查客户端日志文件(见5.2节)。 3. 彻底重启客户端,有时需要清除缓存。 |
| 工具调用后无反应或报错 | 1. 工具处理函数 (callTool) 中有未捕获的异常。2. 工具返回的格式不符合 MCP 协议规范。 3. 资源读取失败。 | 1. 在callTool内部用try-catch包裹,并通过console.error打印错误。2. 使用 Inspector 查看完整的请求和响应,对比 SDK 文档检查响应结构。 3. 确保 readResource方法对不存在的 URI 有妥善处理(返回错误)。 |
7.2 性能与生产环境考量
模板项目用于学习和原型设计很棒,但要用于生产环境,还需要考虑以下几点:
- 状态管理:模板使用内存
Map,服务器重启数据就丢失。生产环境必须接入持久化存储(数据库、文件)。同时,注意并发读写可能带来的数据一致性问题。 - 错误处理与日志:在生产中,不能只依赖
console.log。需要集成成熟的日志库(如 Winston、Pino),将错误、警告和信息日志记录到文件或日志服务中,方便排查问题。 - 安全性:
- 身份验证:如果你的服务器操作敏感数据或系统,需要实现身份验证。MCP 协议支持传输层安全,但应用层认证需要你自己设计(例如,通过
env传递密钥,或在首次握手时进行校验)。 - 输入验证:对所有工具的参数进行严格的验证,防止无效或恶意输入导致程序异常。
- 权限控制:不同的工具和资源可能对应不同的操作权限,需要在业务逻辑中实现。
- 身份验证:如果你的服务器操作敏感数据或系统,需要实现身份验证。MCP 协议支持传输层安全,但应用层认证需要你自己设计(例如,通过
- 部署为系统服务:在开发机上用
node命令运行没问题,在生产环境可能需要用systemd(Linux)、launchd(macOS) 或 Windows Service 将其部署为后台服务,并配置开机自启和进程守护。
7.3 生态与未来展望
MCP 协议由 Anthropic 提出,但正在成为一个开放标准。除了 Claude Desktop,未来 VSCode、JetBrains IDE 等编辑器通过插件支持 MCP 也是可以期待的。这意味着你今天基于此模板开发的服务器,未来可能能在更多场景下使用。
社区也已经出现了一些有趣的 MCP 服务器,例如连接 GitHub、Jira、本地文件系统的服务器。多关注github.com/modelcontextprotocol官方仓库和社区动态,能获得很多灵感和现成的解决方案。
这个模板项目就像一把钥匙,为你打开了定制化 AI 助手能力的大门。它的代码量不大,但清晰地勾勒出了 MCP 服务器的骨架。剩下的,就是如何根据你的独特需求,为其注入血肉和灵魂。从管理你的第二个大脑(笔记),到操控你的开发环境,再到连接你公司内部的各种系统,可能性只受限于你的想象力。
