最近在整理自己的知识库时,一直有个痛点:AI 对话里生成的内容,还得手动复制到笔记工具里重新排版,来回切换效率很低。后来接触到 MCP 协议,发现可以让 AI 客户端直接调用笔记工具的能力。于是我用 NoteGen 做了一次完整实践,把笔记的创建、搜索、删除能力封装成 MCP Server,让 AI 以工具调用的方式直接整理笔记。这篇文章就把整套连接方案、代码实现和排错过程整理出来,希望能帮到同样在做 AI + 笔记工具联动的开发者。
让 AI 工具直接使用 NoteGen:MCP 连接与笔记整理教程
1. 背景与核心概念
1.1 为什么需要让 AI 直接操作笔记工具
在日常开发和学习中,我们经常会让 AI 帮忙总结文章、生成周报、梳理会议纪要。以前的操作方式大概是这样的:在 AI 对话窗口中生成一段内容,然后手动复制出来,再粘贴到笔记软件里,自己写标题、打标签、做分类。如果内容比较多,这个流程会非常繁琐,而且容易丢失上下文。
更麻烦的是,AI 对话窗口本身不具备“记忆”能力。每次重新开启对话,AI 并不知道你之前已经整理过哪些笔记,也不知道你现在的笔记库里有哪些主题,很容易生成重复内容。而如果让 AI 工具通过标准协议直接访问笔记系统,它就可以在生成内容之后,主动完成“查询已有笔记 → 判断是否重复 → 创建新笔记 → 返回笔记 ID”这一整套流程。
这样做有两个明显收益:
第一,减少人工复制粘贴的工作量,让 AI 真正完成从内容生成到知识归档的闭环;第二,AI 在生成前可以先检索现有笔记,结合已有知识做出更合理的输出,而不是每次从零开始。
1.2 MCP 是什么
MCP 的全称是 Model Context Protocol,翻译过来是“模型上下文协议”。它是一种开放的连接规范,用来统一 AI 模型与外部工具、数据源之间的通信方式。你可以把 MCP 理解成 AI 应用世界的“USB 接口”:过去每种工具都要定制一套连接方式,现在大家按同一个协议标准接入,AI 客户端就能以统一方式识别和使用这些工具。
在一个 MCP 架构中,通常涉及三个角色:
- Host:承载 AI 交互的宿主程序,比如 Claude Desktop、Cursor、Cherry Studio 等。
- Client:在 Host 内部与 MCP Server 建立连接、管理会话的客户端组件。
- Server:提供具体能力的服务端,比如“读取本地文件”“查询数据库”“操作笔记工具”。
MCP Server 可以暴露三类能力:Tools 对应可执行的工具函数,Resources 对应可读取的数据资源,Prompts 对应可复用的提示词模板。其中 Tools 是最常使用的,AI 模型会根据用户意图自动决定是否调用某个工具,以及传入什么参数。
1.3 NoteGen 与 MCP 的组合方式
NoteGen 是一类偏重“AI 辅助笔记生成与管理”的工具,通常具备对话生成笔记、Markdown 排版、标签管理、全文检索等能力。本文演示的思路是:为 NoteGen 写一个 MCP Server 适配层,把它的核心能力封装成标准工具,这样任何支持 MCP 的 AI 客户端都能直接调用,而不需要针对每个客户端单独开发适配插件。
这里要说明一点:不同版本的 NoteGen 暴露的 API 和数据结构可能不同。本文会把笔记存储层抽象出来,用本地 JSON 文件作为示例存储,重点演示 MCP Server 的连接与工具组织逻辑。如果你的 NoteGen 版本提供了 HTTP API,只需要把示例中的存储模块替换成真实接口调用即可。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
2.1 运行环境
本文示例以 TypeScript + Node.js 为主,也可以使用 Python 实现,原理一致。建议准备以下环境:
- Node.js 18 或更高版本,包含 npm 包管理器。
- 一个支持 MCP 客户端的 AI 工具,例如 Claude Desktop,或者任意支持自定义 MCP Server 的客户端。
- NoteGen 的实际安装环境,或者一个可访问的笔记服务地址。
如果你尚未安装 Node.js,可以到 Node.js 官网下载 LTS 版本。安装完成后,在终端中执行以下命令确认版本:
bash复制node -v
npm -v
版本号需要根据你的实际环境调整。本文示例代码依赖的是 MCP TypeScript SDK,不同版本在 API 细节上略有差异,下面写代码时我会标注清楚。
2.2 项目依赖
创建一个空目录作为项目根目录,后续代码都放在这个目录下。主要依赖如下:
| 依赖包 | 用途 |
|---|---|
| @modelcontextprotocol/sdk | MCP Server 官方 TypeScript SDK |
| zod | 工具参数校验,MCP SDK 依赖它来生成参数 Schema |
| typescript | TypeScript 编译器 |
| tsx | 开发阶段直接运行 TypeScript 文件 |
安装命令如下:
bash复制mkdir notegen-mcp
cd notegen-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
2.3 准备 NoteGen 服务
为了让示例可运行,本文使用本地 JSON 文件作为笔记存储层。这个选择的好处是零外部依赖,只用一个 data 目录就能模拟真实笔记库。如果你要对接真实 NoteGen 服务,请提前确认以下几点:
- NoteGen 是否提供 HTTP API,认证方式是什么。
- 笔记对象包含哪些字段,比如标题、正文、标签、创建时间。
- 创建笔记的接口是否需要上传分类、来源链接等扩展信息。
确认这些信息后,可以先用 curl 或 Postman 手动调用一次接口,确保网络和认证配置正常,再接入 MCP Server。
3. MCP 核心原理拆解
3.1 MCP 的分层角色
理解 MCP 的分层关系,对后续写代码很有帮助。以 Claude Desktop 为例,用户向 AI 提问,Claude Desktop 是 Host;它内部启动的 MCP Client 负责和 MCP Server 通信;你写好并启动的 notegen-mcp-server 就是 Server。这三个角色之间通过 JSON-RPC 2.0 协议通信。
当用户问“帮我整理一下这段文字,存成笔记”,AI 会先判断当前有没有可用的 MCP 工具。如果有名为 notegen_create_note 的工具,AI 会把用户的话转换成工具参数,通过 Client 发送给 Server。Server 执行真实笔记创建逻辑,把结果返回给 Client,Client 再交给 AI 模型总结成自然语言反馈给用户。
所以你会发现,MCP Server 的本质是“把业务能力翻译成 AI 能理解的函数描述”。业务能力越清晰、参数描述越准确,AI 就越能正确调用。
3.2 Tools、Resources 与 Prompts
在 MCP 中,Tools 是动词,适合执行操作,比如“创建笔记”“删除笔记”“搜索笔记”。Resources 是名词,适合暴露可读取的数据,比如“返回某篇笔记的原始 Markdown 内容”“返回标签列表”。Prompts 则适合暴露标准化的提示词模板。
在“让 AI 直接使用 NoteGen”这个场景中,核心功能是笔记的增删改查,所以主要使用 Tools。等你熟悉 MCP 之后,可以把笔记模板、知识库前缀等能力设计成 Prompts,把只读文件暴露为 Resources。规划时注意:如果某个能力需要 AI 做出判断后才执行,用 Tools;如果只是给 AI 提供上下文信息,用 Resources;如果是用户主动选择某个指令模板,用 Prompts。
3.3 Stdio 与 HTTP/SSE 传输方式
MCP Server 支持两种常见传输方式。
第一种是 stdio,即通过标准输入输出通信。AI 客户端在本地启动一个子进程,比如执行 node dist/index.js,然后通过标准输入输出和这个进程交换 JSON-RPC 消息。这种模式本地配置最简单,安全性较高,因为服务不会暴露端口,适合个人电脑上的笔记本应用。
第二种是 HTTP/SSE,即 MCP Server 作为一个 HTTP 服务运行,客户端通过 HTTP 请求或者 Server-Sent Events 获取数据。这种模式适合远程部署,比如把 NoteGen 的 MCP Server 部署在公司服务器上,让多个开发者的 AI 客户端共同连接。代价是需要对外开放端口,必须考虑认证与访问控制。
本文实战部分先使用 stdio 模式,连接步骤最直观,踩坑最少。理解了 stdio 模式后,再拓展到 HTTP 模式会容易很多。
4. 完整实战案例:为 NoteGen 开发 MCP Server
4.1 项目结构
在 notegen-mcp 目录下,最终项目结构如下:
text复制notegen-mcp/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # MCP Server 入口
│ └── store.ts # 笔记存储模块
└── data/
└── notes.json # 笔记数据文件,首次运行时自动生成
src/store.ts 负责笔记的持久化,src/index.ts 负责把笔记能力注册为 MCP 工具。这样拆分的好处是,将来如果从 JSON 文件切换到真实 NoteGen API,只需要修改 store.ts,不需要改动 MCP 工具注册逻辑。
4.2 初始化 TypeScript 项目
创建 tsconfig.json,内容如下:
json复制{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
同时修改 package.json,增加 scripts 配置。如果你使用 CommonJS,请把 type 字段删除或保留默认值;如果你使用 ESM,则添加 "type": "module"。为了兼容大多数 MCP 封装场景,下面示例使用 ESM:
json复制{
"name": "notegen-mcp",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20.11.0",
"tsx": "^4.19.0",
"typescript": "^5.6.0"
}
}
这里版本号是范围写法,实际安装时会解析到当前最新的兼容版本。如果安装后运行报错,优先检查 SDK 版本与依赖兼容性。
4.3 编写笔记存储模块
创建 src/store.ts,定义笔记数据结构和读写逻辑:
typescript复制// 文件路径:src/store.ts
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
export interface Note {
id: string;
title: string;
content: string;
tags: string[];
updatedAt: string;
}
const DATA_DIR = process.env.NOTEGEN_DATA_DIR ?? path.join(process.cwd(), "data");
const DATA_FILE = path.join(DATA_DIR, "notes.json");
function ensureFile() {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
}
if (!fs.existsSync(DATA_FILE)) {
fs.writeFileSync(DATA_FILE, JSON.stringify([]), "utf-8");
}
}
function readAll(): Note[] {
ensureFile();
const raw = fs.readFileSync(DATA_FILE, "utf-8");
try {
return JSON.parse(raw) as Note[];
} catch {
return [];
}
}
function writeAll(notes: Note[]) {
ensureFile();
fs.writeFileSync(DATA_FILE, JSON.stringify(notes, null, 2), "utf-8");
}
export async function createNote(input: {
title: string;
content: string;
tags: string[];
}): Promise<Note> {
const notes = readAll();
const now = new Date().toISOString();
const note: Note = {
id: crypto.randomUUID(),
title: input.title,
content: input.content,
tags: input.tags ?? [],
updatedAt: now,
};
notes.push(note);
writeAll(notes);
return note;
}
export async function listNotes(): Promise<Note[]> {
return readAll();
}
export async function searchNotes(keyword: string): Promise<Note[]> {
const notes = readAll();
const kw = keyword.toLowerCase();
return notes.filter(
(note) =>
note.title.toLowerCase().includes(kw) ||
note.content.toLowerCase().includes(kw)
);
}
export async function deleteNote(id: string): Promise<boolean> {
const notes = readAll();
const index = notes.findIndex((note) => note.id === id);
if (index === -1) {
return false;
}
notes.splice(index, 1);
writeAll(notes);
return true;
}
这段代码先定义 Note 接口,包含 id、title、content、tags、updatedAt 五个字段。readAll 从 JSON 文件读取全部笔记,writeAll 写回文件。createNote 使用 crypto.randomUUID 生成笔记 ID,deleteNote 根据 ID 删除。注意文件不存在时,ensureFile 会自动创建空数组,避免初体验时报错。
4.4 编写 MCP Server 入口
创建 src/index.ts,注册四个工具:创建笔记、搜索笔记、列出全部笔记、删除笔记。
typescript复制// 文件路径:src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { createNote, deleteNote, listNotes, searchNotes } from "./store.js";
const server = new McpServer({
name: "notegen-mcp-server",
version: "1.0.0",
});
server.tool(
"notegen_create_note",
"在 NoteGen 中创建一篇新笔记,适合将 AI 生成的文本整理为结构化笔记",
{
title: z.string().describe("笔记标题"),
content: z.string().describe("笔记正文,建议使用 Markdown 格式"),
tags: z.array(z.string()).optional().describe("笔记标签列表"),
},
async ({ title, content, tags = [] }) => {
const note = await createNote({ title, content, tags });
return {
content: [
{
type: "text",
text: JSON.stringify(note, null, 2),
},
],
};
}
);
server.tool(
"notegen_search_notes",
"根据标题或正文关键词搜索 NoteGen 中的已有笔记",
{
keyword: z.string().describe("搜索关键词"),
},
async ({ keyword }) => {
const notes = await searchNotes(keyword);
return {
content: [
{
type: "text",
text: JSON.stringify(notes, null, 2),
},
],
};
}
);
server.tool(
"notegen_list_notes",
"列出 NoteGen 中保存的全部笔记",
{},
async () => {
const notes = await listNotes();
return {
content: [
{
type: "text",
text: JSON.stringify(notes, null, 2),
},
],
};
}
);
server.tool(
"notegen_delete_note",
"根据 ID 删除 NoteGen 中的一篇笔记,删除前请确认 ID 正确",
{
id: z.string().describe("要删除的笔记 ID"),
},
async ({ id }) => {
const ok = await deleteNote(id);
return {
content: [
{
type: "text",
text: ok ? `笔记 ${id} 删除成功` : `未找到笔记 ${id}`,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("NoteGen MCP Server 已启动");
}
main().catch((error) => {
console.error("启动失败:", error);
process.exit(1);
});
这段代码的核心是 server.tool 方法,它让 MCP Server 自动生成工具描述和参数 Schema。AI 客户端拿到这个 Schema 后,就能知道该传什么参数。四个工具都是异步函数,返回值统一使用 MCP 规定的结构:content 数组里每一项包含 type 和 text 字段。
有一处细节要特别注意:main 函数中的日志使用了 console.error 而不是 console.log。因为 stdio 模式下 stdout 是 MCP 数据通信通道,乱打日志会破坏 JSON-RPC 消息流,导致客户端无法解析。所有调试日志都要输出到 stderr。
4.5 运行 MCP Server
开发阶段直接使用 tsx 运行源码:
bash复制npm run dev
启动后终端会输出:
text复制NoteGen MCP Server 已启动
如果只是测试协议交互,可以用 MCP Inspector 调试工具。安装并运行:
bash复制npx @modelcontextprotocol/inspector node dist/index.js
Inspector 会打开一个本地调试页面,可以在里面查看工具列表、模拟调用,并观察请求响应是否符合预期。这一步能帮你确认 Server 本身没问题,再接入 AI 客户端。
正式使用前,先编译成 JavaScript:
bash复制npm run build
npm start
4.6 在 AI 客户端中配置连接
以 Claude Desktop 为例,找到客户端的配置文件,一般是 claude_desktop_config.json。不同客户端配置文件路径不同,但 MCP Server 配置结构类似:
json复制{
"mcpServers": {
"notegen": {
"command": "node",
"args": ["/绝对路径/to/notegen-mcp/dist/index.js"],
"env": {
"NOTEGEN_DATA_DIR": "/绝对路径/to/notegen-mcp/data"
}
}
}
}
配置完成后重启 AI 客户端。如果客户端支持 MCP 工具列表展示,你会看到 notegen_create_note、notegen_search_notes、notegen_list_notes、notegen_delete_note 四个工具。
这里强调一个细节:command 一定要填写 Node.js 的可执行文件路径,Windows 下可能是 node,Linux/macOS 下一般也是 node。如果启动失败,优先检查绝对路径是否正确,以及 dist/index.js 是否存在。
4.7 验证 AI 调用
配置成功后,在 AI 对话中输入类似这样的指令:
“请把以下会议纪要整理成 Markdown 笔记,保存到 NoteGen:项目当前进度正常,首页改版完成 80%,API 网关稳定性提升……”
AI 会调用 notegen_create_note 工具。你可以查看 data/notes.json,会发现一条新笔记记录:
json复制[
{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"title": "项目周会纪要",
"content": "## 项目当前进度\n\n- 首页改版完成 80%",
"tags": ["周会", "项目"],
"updatedAt": "2025-01-01T10:00:00.000Z"
}
]
再输入“帮我查一下笔记库里有没有关于 API 网关的笔记”,AI 会调用 notegen_search_notes。你不需要自己打开数据库,AI 自动完成了查询和总结。
5. 常见问题与排查思路
5.1 工具列表为空
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| AI 客户端能看到 Server,但工具列表为空 | SDK 版本太低,或工具描述格式不受支持 | 升级 @modelcontextprotocol/sdk 到最新版本 |
| 配置后客户端显示连接失败 | command 或 args 路径错误 | 检查绝对路径,手动执行一次命令确认可启动 |
| 工具列表出现但调用超时 | Server 进程被杀,或 stdout 被日志污染 | 删除 stdout 日志,改用 stderr 输出 |
5.2 工具调用报参数校验错误
MCP Server 返回参数校验失败时,通常是 AI 传入的参数类型和 zod Schema 不一致。比如你定义了 title 为字符串,AI 却传了一个对象;或者你定义了 tags 是数组,AI 只传了一个字符串。
解决办法是让工具描述更具体,比如在 describe 中明确说明“tags 是字符串数组,例如 ['项目', '周报']”。这能显著提高 AI 的参数生成准确率。
5.3 Node.js 版本过低导致启动失败
MCP SDK 较新版本依赖 Node.js 18 以上的特性。如果你在低版本 Node 下启动报错,需要升级 Node.js,或者将 SDK 回退到兼容旧版本的版本。建议优先升级 Node.js,因为低版本对环境有更多限制。
5.4 Windows 环境下 stdio 模式无法退出
Windows 下 stdio 子进程有时不会随父进程退出,导致 AI 客户端关闭后 node 进程仍然占用终端。这时可以打开任务管理器结束对应的 node 进程。正式使用时,建议在客户端配置中加上 shutdown 指令,或者在 Server 中监听 SIGTERM 信号做清理。
5.5 数据文件损坏导致笔记读取失败
如果 data/notes.json 被手动编辑后变成非法 JSON,readAll 方法会返回空数组,看起来像是笔记全部丢失。避免这个问题的方法是:不要直接编辑数据文件,所有改动都通过 MCP 工具完成。如果需要备份,复制整个 data 目录即可。
6. 最佳实践与工程建议
6.1 工具命名与参数设计
工具名建议使用“产品前缀 + 动词短语”的格式,例如 notegen_create_note、notegen_search_notes。这样在大脑负载较高的 AI 场景中,工具名自解释性强,AI 不容易混淆。
参数设计上遵循三个原则:参数数量少、类型明确、每个字段都有 describe。AI 模型不是程序员,它看不到你的代码注释,只能看到 MCP 工具描述。描述的细致程度直接影响调用成功率。如果你想传 Markdown 格式的正文,就在描述里写清楚“建议使用 Markdown 格式”。
6.2 安全与权限边界
MCP Server 让 AI 获得执行操作的权限,意味着你要对权限边界格外敏感。尤其是删除操作,我在示例中把 notegen_delete_note 定义为独立工具而不是把“更新”和“删除”混在一起,就是为了让 AI 在调用时会收到明确的工具描述提示。
生产环境中还建议加两道防线:
第一,对删除、批量更新等高风险操作做条件校验,例如要求传入确认参数 confirm=true,或者只允许删除指定 ID 范围内的笔记。
第二,如果是远程部署 MCP Server,一定要做认证。HTTP/SSE 模式下不能让任何人随意访问你暴露的接口,可以使用 API Key、OAuth 等机制,并且只开放必要的主机和端口。
6.3 错误处理与日志规范
错误处理上,MCP 工具函数内部不要随便抛出异常。建议捕获业务异常后,把可读的错误信息放进返回值:
typescript复制async ({ id }) => {
try {
const ok = await deleteNote(id);
return {
content: [
{
type: "text",
text: ok ? "删除成功" : `未找到笔记 ${id}`,
},
],
};
} catch (error) {
return {
isError: true,
content: [
{
type: "text",
text: `删除失败:${(error as Error).message}`,
},
],
};
}
}
日志方面,stdio 模式下使用 console.error,远程模式下使用标准日志库。不管哪种模式,都建议记录工具名、参数摘要、耗时和执行结果,方便回溯问题。
6.4 与 NoteGen 真实数据模型对齐
当你想把示例存储层换成真实 NoteGen 服务时,建议先梳理真实笔记的数据模型,再设计 MCP 工具。你在 MCP 层暴露的字段,最好和 NoteGen API 的字段一一对应,避免 AI 生成的参数无法映射。
另外,注意处理真实服务中的大数据量。比如 searchNotes 如果返回几百条结果,会撑爆 AI 的上下文窗口。更稳妥的做法是只返回前 20 条,并附上总条数提示,让 AI 根据结果决定是否需要缩小搜索范围。
7. 总结与学习路线
通过这篇文章,你已经完成了从零开始为 NoteGen 搭建 MCP Server 的全过程:理解了 MCP 中 Host、Client、Server 的分工,掌握了 stdio 模式的连接原理,写出了可以增删查改笔记的 MCP 工具,并在 AI 客户端中验证了真实调用。
下一步你可以做三件事:
第一,把存储层替换成真实 NoteGen 的 HTTP API,让 MCP Server 直接操作线上笔记库。第二,为 MCP Server 增加远程 SSE 部署,让团队内多个 AI 客户端共享同一套笔记能力。第三,探索 Resources 和 Prompts 能力,把常用笔记模板和只读知识库接入进来。
需要特别提醒的是,凡是把 AI 模型连接到真实业务系统,都要把数据安全放在第一位。先在小范围、测试数据上验证,再逐步放开权限。本地文件存储适合个人使用,一旦涉及团队协作或线上数据,必须设计好认证、审计和回滚机制。
MCP 本身并不是很难理解的技术,它把一个“为每个客户端单独开发插件”的问题,变成了“实现一次协议、到处连接”的标准方案。只要动手跑通一个最小示例,后续扩展其他工具会非常顺畅。如果这篇文章对你有帮助,建议收藏备用,遇到连接问题时可以对照排查。
