ByteFisher AI 工程化深度实战(九):自研 MCP Server 实战

ByteFisher AI 实战系列的 19 篇讲过 MCP 的”是什么”,本篇讲”怎么造”。MCP(Model Context Protocol)的价值在于:它把”AI 如何调用工具”从各家私有实现,变成了一个公开协议。2026 年 Claude、Cursor、各种 IDE 都已原生支持 MCP,这意味着:写一个标准 MCP Server,你的博客数据就能被任何 AI 工具直接”操作”。

本篇从协议原理讲到落地实现,最后交付一个能跑的 ByteFisher MCP Server。

一、协议原理:MCP 只有三个概念

MCP 的传输层基于 JSON-RPC 2.0,所有通信都是”请求-响应”或”通知”。核心抽象只有三个:

1
2
3
4
5
6
7
MCP Server 暴露三类原语给 Client(Claude/Cursor/你的代码):

1. tools(工具) —— AI 主动调用的函数:do_something(args)
2. resources(资源) —— 只读的数据:读 /posts/{id}、读 /dashboard
3. prompts(提示) —— 预制的提示词模板:prompt/写钓鱼日记

生命周期:initialize → 能力协商 → 正常通信 → shutdown
1
2
3
4
5
6
7
8
9
10
// 一次 tool 调用(JSON-RPC 请求)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query_fishing_stats",
"arguments": { "year": "2026" }
}
}

1.1 两种传输:stdio 与 HTTP

传输 原理 适用
stdio Server 作为子进程,stdin/stdout 传 JSON-RPC 本地 IDE(Claude Code/Cursor)
HTTP/SSE Server 是远程 HTTP 服务 云端、跨机器、需鉴权

对博客场景:本地开发用 stdio 接进 Cursor 最顺手;给线上用则要 HTTP + 鉴权(博客的工具链在 GitHub Actions / Vercel 上跑,属于后者)。

1.2 工具的 Schema 就是”你的 API 说明书”

MCP 的 tool 声明是一个 JSON Schema,AI 靠它决定”何时调用、传什么参数”。Schema 写得好不好,直接决定 AI 会不会调用:

1
2
3
4
5
6
7
8
9
10
11
{
"name": "query_fishing_stats",
"description": "查询 ByteFisher 博客的鱼获统计数据,返回钓点、鱼种、数量。用户问'今年钓了多少鱼'这类问题用这个工具。",
"inputSchema": {
"type": "object",
"properties": {
"year": { "type": "integer", "description": "年份,默认当前年" },
"spot": { "type": "string", "description": "钓点名称,可选" }
}
}
}

description 里要写”什么时候用、回答什么”,而不是只写”查询鱼获”——模型根据 description 做意图匹配,描述越具体,误用越少。这是工具工程里最容易忽略却最重要的一点。

二、动手写:ByteFisher MCP Server

用官方 TypeScript SDK(@modelcontextprotocol/sdk)实现,三个工具覆盖博客三类数据。

2.1 项目骨架

1
2
3
4
5
6
7
8
tools/mcp-blog/
package.json
src/
server.ts # MCP Server 入口
tools/
fish-stats.ts # 鱼获统计(TiDB)
search-posts.ts # 文章检索(posts-index.json)
new-draft.ts # 生成 Hexo 草稿(写文件)

2.2 Server 注册三个工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = new McpServer({ name: 'bytefisher', version: '1.0.0' });

server.tool(
'query_fishing_stats',
{ year: 'number?', spot: 'string?' },
async ({ year, spot }) => {
// 对接 TiDB Cloud(博客鱼获统计的数据源)
const rows = await tidb.query(
`SELECT spot, species, COUNT(*) as cnt, SUM(weight) as total
FROM catches
WHERE (? IS NULL OR spot = ?)
AND (? IS NULL OR year = ?)
GROUP BY spot, species`,
[spot, spot, year, year]
);
return { content: [{ type: 'text', text: JSON.stringify(rows) }] };
}
);

server.tool(
'search_blog_posts',
{ query: 'string', top_k: 'number?' },
async ({ query, top_k }) => {
// 复用第 04 篇的混合检索逻辑
const results = await hybridSearch(query, top_k ?? 5);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
}
);

server.tool(
'create_hexo_draft',
{ title: 'string', content: 'string', tags: 'string[]', date: 'string?' },
async ({ title, content, tags, date }) => {
const slug = title.replace(/\s+/g, '-');
const file = `source/_posts/${slug}.md`;
// 按 AGENTS.md 的 front matter 规范写文件
writeFileSync(file, buildFrontMatter({ title, date, tags }) + '\n' + content);
return { content: [{ type: 'text', text: `草稿已生成:${file}` }] };
}
);

query_fishing_stats 对接 TiDB Cloud 的鱼获数据,search_blog_posts 复用第 04 篇的检索,create_hexo_draft 直接写 source/_posts/ 下的 Hexo 草稿——博客的既有资产被统一封装成了 AI 可调用的工具面。

2.3 接入 Cursor / Claude Code

Cursor 配置 .cursor/mcp.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"mcpServers": {
"bytefisher": {
"command": "node",
"args": ["tools/mcp-blog/dist/server.js"],
"env": {
"TIDB_HOST": "xxx",
"TIDB_USER": "xxx",
"TIDB_PASSWORD": "xxx"
}
}
}
}

配置完,在 Cursor 里直接说”帮我查一下今年在花碑水库的鱼获”或”把这段日记整理成 Hexo 草稿”,Cursor 会自动调对应的 MCP tool。不需要写任何胶水代码——这就是协议标准的威力。

三、工程化的四个关键点

3.1 工具要”幂等”或至少可安全重试

第 02 篇讲调用层重试,工具层同理:AI 可能因为超时重试调用。create_hexo_draft 必须做文件存在性检查(已存在则报错或改名),避免重复写文件。写操作的工具比读操作更需要对幂等性设计。

3.2 鉴权与敏感信息

stdio 传输的 env 里有 TiDB 密码——绝对不要提交到仓库。用 .env + gitignore,或用 Cursor 的 secret store。HTTP 传输的 Server 必须加 token 鉴权,并在入口做来源校验(对齐第 02 篇的 CORS 思路)。

3.3 工具失败要有语义化错误

MCP 的 tool 返回结构支持错误消息。失败时返回对 AI 友好的错误(”查询失败:数据库未就绪,请稍后重试”),而不是抛裸异常——模型会根据错误信息决定”要不要换个参数重试”。

3.4 日志与可观测

Server 里每次 tool 调用打结构化日志(工具名、参数、耗时、结果长度)。这既是调试依据,也是第 11 篇 trace 的一部分——工具调用是 AI 应用里最值得追踪的 span。

四、落地清单与小结

优先级 改造 收益
P0 用 SDK 搭 stdio Server(鱼获/检索两工具) 立即能在 Cursor 里用
P0 tool description 写清”何时用” AI 调用准确率
P1 Hexo 草稿工具(写操作幂等) AI 直接产出博客内容
P1 HTTP 传输 + token 鉴权 云端可用
P2 工具日志落库 可观测
P2 prompts 预制(”写一篇钓鱼日记”模板) 降低 AI 使用门槛

核心结论:

  • MCP 只有三个概念:tools(动作)、resources(只读数据)、prompts(提示模板),传输是 JSON-RPC over stdio/HTTP。
  • 写 MCP Server 本质是给 AI 设计 API 说明书——description 决定 AI 会不会用、用得准不准。
  • 博客资产(TiDB 鱼获、文章索引、Hexo 草稿)封装成 MCP 后,任何 AI 工具零胶水接入。
  • 工程要点:写操作幂等、敏感信息隔离、错误语义化、工具调用日志化。

下一篇进入多 Agent 编排工程化:把前几篇的 Agent/MCP/工具能力组合成生产级流水线,讲失败恢复、并行调度、成本与延迟平衡。


本系列持续更新中,每周一篇。

ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
分享: