模块六:运营增长与高级话题 | 第05讲:MCP 协议实战——AI 工具的标准化集成接口
课程项目:VibeNote 智能笔记
技术栈:Next.js、React、TypeScript
本节目标:理解 MCP 架构与三大原语,跑通一个「能搜笔记、能读片段」的最小 Server,并知道何时选 stdio / SSE。
一、开场:为什么你需要 MCP,而不是「再写一套内部工具协议」
参考文章《核心概念与范式演进》指出:Agent 的关键不在于更会聊天,而在于能维持上下文、调用工具、连续完成任务。但现实里每个 IDE、每个框架各自定义工具格式——你在 Cursor 里写一套,在 Claude Desktop 再写一套,在自建 Agent 里又写一套,集成交付成本会被指数放大。
Model Context Protocol(MCP) 的目标,就是把「模型如何发现工具、如何传参、如何读资源」标准化。对你这样的全栈独立开发者,它意味着:
- 写一次 Server,多处复用:同一个
vibenote-mcp-server可被多个 Client 接入。 - 边界清晰:工具能力在 Server 内聚,Client 负责编排与展示。
- 生态可组合:文件系统、GitHub、浏览器、数据库……社区 Server 可直接拼进你的工作流。
本节以 VibeNote 为例:把「搜索笔记」「读取笔记 Markdown」「列出标签」暴露成 MCP Tools/Resources。
二、MCP 架构:Client ↔ Server
flowchart LR
subgraph Client侧
C[Cursor / Claude / 自建App]
end
subgraph Server侧
S[MCP Server\n(独立进程或服务)]
V[VibeNote API / DB]
end
C <-->|JSON-RPC 风格消息| S
S --> V
- Client:发起连接、列举能力、调用工具、读取资源。
- Server:真正实现「查库 / 调 HTTP / 读文件」。
- Host 应用:有时 Client 嵌在 IDE 里;Host 还负责权限与用户确认。
三、传输层:stdio 与 HTTP/SSE 怎么选?
sequenceDiagram
participant H as Host(IDE)
participant S as MCP Server
H->>S: spawn 子进程(stdin/stdout)
loop 协议消息
H->>S: request
S-->>H: response
end
| 传输 | 优点 | 缺点 | 典型场景 |
|---|---|---|---|
| stdio | 本地开发简单、无端口暴露 | 不适合远程多机共享 | Cursor 本地调试 |
| HTTP + SSE | 可远程、可共享 | 部署与鉴权更复杂 | 团队共用工具服务 |
工程建议:先用 stdio 把工具语义跑稳;确有「多客户端远程接入」再 SSE。日志走 stderr,stdout 留给协议,这是新手最常踩的坑之一。
四、三大原语:Tools / Resources / Prompts
1)Tools(可执行动作)
对应传统 Agent 的 function calling:带 inputSchema,会产生副作用或计算结果。
VibeNote 典型 Tools:
search_notes:关键词/标签搜索create_note:创建草稿(需权限)append_section:向某篇笔记追加段落(需权限)
2)Resources(只读上下文)
用 URI 标识,适合把「大块文本」喂给模型做推理,但不应隐含副作用。
示例 URI:
vibenote://notes/{id}vibenote://tags
3)Prompts(模板化工作流)
把高频任务固化成参数化模板,例如 summarize_note、weekly_review。
五、最小 MCP Server(TypeScript + SDK)
下面示例改编自课程仓库的智识小站案例,将命名替换为 VibeNote(你需要把 searchNotes 等函数接到真实 API)。
mkdir vibenote-mcp-server && cd vibenote-mcp-server
pnpm init
pnpm add @modelcontextprotocol/sdk
pnpm add -D typescript tsx
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "vibenote-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "search_notes",
description: "在 VibeNote 中搜索笔记(标题/正文/标签)",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "关键词" },
tag: { type: "string", description: "标签,可选" },
limit: { type: "number", description: "返回数量上限", default: 10 },
},
required: ["query"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "search_notes") {
const { query, tag, limit = 10 } = args as {
query: string;
tag?: string;
limit?: number;
};
const notes = await searchNotes({ query, tag, limit });
return {
content: [{ type: "text", text: JSON.stringify(notes, null, 2) }],
};
}
throw new Error(`未知工具: ${name}`);
});
async function searchNotes(input: {
query: string;
tag?: string;
limit: number;
}) {
const base = process.env.VIBENOTE_API_URL ?? "http://localhost:3000";
const token = process.env.VIBENOTE_API_TOKEN;
const qs = new URLSearchParams({
q: input.query,
limit: String(input.limit),
});
if (input.tag) qs.set("tag", input.tag);
const res = await fetch(`${base}/api/notes/search?${qs}`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (!res.ok) throw new Error(`VibeNote API 错误: ${res.status}`);
return res.json();
}
const transport = new StdioServerTransport();
await server.connect(transport);
运行:
npx tsx src/index.ts
六、安全:MCP 不是「自动可信」
- Token 管理:
VIBENOTE_API_TOKEN只放本机或密钥管理,不要写进仓库。 - 最小权限:
create_note这类工具默认应对接「用户级 token」,而不是服务端超级密钥。 - 审计:记录 tool 调用日志(脱敏),防止被提示词注入诱导越权。
- 输入校验:Server 内对参数做长度与字符集限制,避免超大正文拖垮服务。
七、与「OpenAI Function Calling」的关系
Function Calling 在单次模型请求里声明工具;MCP 在应用层解决「工具从哪来、如何发现、如何维护」。二者可以组合:Client 把 MCP tools 映射成模型接口所需的 schema。
八、VibeNote 工具清单建议(从少到多)
第一期(稳定):
search_notesget_note(读 Markdown)
第二期(协作):
list_workspacecomment_on_note
第三期(谨慎):
delete_note(强烈建议默认关闭或二次确认)
参考 Vercel 工程博客的思路:工具太多会降低稳定性——宁可少而准,不要大而全。
九、小结
- MCP 把 AI 工具集成从「项目私货」提升为「可复用协议」。
- stdio 适合本地;SSE 适合远程共享。
- Tools / Resources / Prompts 分工明确:执行、读上下文、标准化工作流。
- 安全与权限是生产落地的前提,不是上线后再补。
思考题
- 你会把「删除笔记」暴露为 MCP Tool 吗?如果会,需要哪些交互与安全机制?
- 当工具返回 JSON 很大时,你会如何在 Server 侧重做分页与摘要?
- stdio Server 日志误写到 stdout 会发生什么?如何设计防呆?
下节预告
下一讲进入 Context Engineering(上下文工程):当 VibeNote 代码库变大、规范变多,你会发现「提示词技巧」救不了系统性跑偏。我们将讨论被动/主动上下文、分形文档、上下文预算,以及如何把 AGENTS.md 写成真正驱动产出的「规格源文件」。