从提示词到可控接入:用最小 MCP Server 打通内部知识库、脚本与工单系统
从问题出发:为什么“提示词写得更好”仍然不够
很多团队第一次把 AI 接到研发流程里,都会先从提示词开始:
- 给模型一段系统提示,告诉它“你可以参考内部知识库回答问题”
- 把脚本用自然语言描述给模型,让它“按规范执行发布步骤”
- 告诉模型“如果用户要查工单,就按这个 URL 规则去找”
这样做在 Demo 阶段常常看起来可行,但一到真实研发场景就会暴露几个问题:
1. 普通提示词没有真实的系统接入能力
提示词只能描述,不等于连接。
你可以在提示词里写“去查询内部工单系统”,但模型并不会因此自动拥有:
- 可用的 API 入口
- 明确的参数结构
- 稳定的鉴权方式
- 可复用的返回格式
- 可追踪的调用日志
结果就是:模型会“看起来懂了”,但真正执行时不是编造结果,就是依赖你手工复制粘贴上下文。
2. 提示词缺少边界,容易把权限交太多
如果你直接把数据库口令、部署 Token、SSH 命令、生产脚本路径都塞进提示词,短期也许省事,长期一定危险。
常见风险包括:
- 模型误调用高危脚本
- 把敏感参数泄露到对话上下文
- 将“读”能力和“写”能力混在一起
- 缺少审批、幂等、确认步骤
3. 提示词不可审计,不利于团队协作
研发团队不是一个人用 AI,而是一群人长期用。
如果某次发布失败,你需要知道:
- 模型当时调用了哪个接口
- 参数是什么
- 返回了什么错误
- 是谁触发的
- 调用了只读工具还是高危工具
普通提示词很难沉淀成工程化资产,更谈不上团队复用。
4. 模型需要的是“结构化能力”,不是“更长的说明书”
这也是 MCP 的价值所在。
MCP(Model Context Protocol)不是单纯让你写更复杂的提示词,而是把“模型可调用的外部能力”标准化:
- 用工具(Tools)暴露动作能力
- 用资源(Resources)暴露只读上下文
- 用明确 schema 约束输入输出
- 用鉴权、日志、错误码把集成变成可治理的工程
如果你之前已经在优化提示词,可以把这一步理解为:提示词负责表达意图,MCP 负责连接真实世界。
站内如果你还在打磨提示词本身,可以先看 prompt-master、prompt-optimizer、prompt-architect 或 Ultra Prompt。但一旦目标是稳定接入内部系统,重点就应该从“写提示”转向“设计能力边界”。
这篇教程会做什么
我们用一个最小但实用的案例,做一个本地可运行的 MCP Server,提供三类能力:
- 只读知识库查询:读取本地文档目录中的 Markdown 文件
- 只读工单查询:模拟从内部工单系统读取工单详情
- 受限脚本执行:只允许调用明确白名单里的安全脚本
同时把下面这些工程问题一并讲清楚:
- 怎么定义工具 schema
- 怎么做最小鉴权
- 怎么暴露资源给 AI 读取
- 怎么做错误处理
- 怎么在本地调试
- 怎么接到 Cursor 或 Claude Code
- 怎么避免把高危权限直接给 AI
- 怎么记录调用日志便于审计
- 怎么把一次性原型整理成团队可复用的 Skill/模板
先看最终目录结构
为了让示例尽量容易跑起来,我们用 Node.js。
mcp-internal-gateway/
package.json
server.js
scripts/
health-check.sh
data/
kb/
deploy.md
oncall.md
tickets.json
logs/
这个示例不依赖真实内网系统,你先把能力设计跑通,再替换成自己的知识库、工单 API、部署接口。
第一步:初始化最小项目
确保本机 Node.js 版本 >= 18。
执行:
mkdir mcp-internal-gateway
cd mcp-internal-gateway
npm init -y
npm install @modelcontextprotocol/sdk zod
再创建 package.json 的脚本字段:
{
"name": "mcp-internal-gateway",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.23.8"
}
}
说明:
@modelcontextprotocol/sdk用来实现 MCP Serverzod用来做输入 schema 校验- 这里使用
type: module,代码更简单
第二步:准备最小测试数据
创建知识库文件 data/kb/deploy.md:
# 部署回滚说明
如果 5 分钟内错误率高于 3%,优先执行回滚。
回滚前先确认最近一次变更单号,并在工单系统中备注。
创建 data/kb/oncall.md:
# 值班处理
P1 事故必须在 10 分钟内拉群。
任何生产变更都需要保留操作者与执行时间。
创建工单数据 data/tickets.json:
[
{
"id": "OPS-101",
"title": "支付服务发布后健康检查失败",
"status": "open",
"owner": "alice",
"summary": "发布后 /health 返回 503,需要确认最近配置变更"
},
{
"id": "OPS-102",
"title": "客服反馈登录接口偶发超时",
"status": "investigating",
"owner": "bob",
"summary": "怀疑缓存连接池已满,需先看监控再决定是否重启"
}
]
创建脚本 scripts/health-check.sh:
#!/bin/sh
echo '{"service":"payment-api","status":"ok","latencyMs":42}'
给脚本执行权限:
chmod +x scripts/health-check.sh
第三步:实现最小 MCP Server
下面是核心代码。它做了几件事:
- 暴露一个资源列表,供 AI 读取知识库文件
- 暴露两个工具:
get_ticket和run_safe_script - 用 Zod 对工具输入做校验
- 用环境变量做最小鉴权
- 把每次调用写入本地日志
- 对错误进行结构化返回
创建 server.js:
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { execFile } from "child_process";
import { promisify } from "util";
import { z } from "zod";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const DATA_DIR = path.join(__dirname, "data");
const KB_DIR = path.join(DATA_DIR, "kb");
const TICKETS_FILE = path.join(DATA_DIR, "tickets.json");
const LOG_DIR = path.join(__dirname, "logs");
const MCP_TOKEN = process.env.MCP_TOKEN || "dev-token";
const SAFE_SCRIPTS = {
"health-check": path.join(__dirname, "scripts", "health-check.sh")
};
async function ensureLogDir() {
await fs.mkdir(LOG_DIR, { recursive: true });
}
async function writeAuditLog(entry) {
await ensureLogDir();
const line = JSON.stringify({ time: new Date().toISOString(), ...entry }) + "\n";
await fs.appendFile(path.join(LOG_DIR, "audit.log"), line, "utf8");
}
function checkAuth(args) {
if (!args || args.token !== MCP_TOKEN) {
const err = new Error("Unauthorized");
err.code = "UNAUTHORIZED";
throw err;
}
}
async function loadTickets() {
const raw = await fs.readFile(TICKETS_FILE, "utf8");
return JSON.parse(raw);
}
async function listKbFiles() {
const files = await fs.readdir(KB_DIR);
return files.filter(name => name.endsWith(".md"));
}
const getTicketInput = z.object({
token: z.string(),
ticketId: z.string().min(3)
});
const runSafeScriptInput = z.object({
token: z.string(),
scriptName: z.enum(["health-check"])
});
const server = new Server(
{
name: "internal-gateway",
version: "1.0.0"
},
{
capabilities: {
tools: {},
resources: {}
}
}
);
server.setRequestHandler(ListResourcesRequestSchema, async () => {
const files = await listKbFiles();
return {
resources: files.map(name => ({
uri: `kb://${name}`,
name,
mimeType: "text/markdown",
description: `Internal KB article: ${name}`
}))
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
if (!uri.startsWith("kb://")) {
throw new Error("Unsupported resource URI");
}
const fileName = uri.replace("kb://", "");
const fullPath = path.join(KB_DIR, fileName);
const content = await fs.readFile(fullPath, "utf8");
await writeAuditLog({
type: "read_resource",
uri
});
return {
contents: [
{
uri,
mimeType: "text/markdown",
text: content
}
]
};
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_ticket",
description: "Read a ticket from the internal ticket system by ticket ID",
inputSchema: {
type: "object",
properties: {
token: { type: "string", description: "Access token" },
ticketId: { type: "string", description: "Ticket ID, e.g. OPS-101" }
},
required: ["token", "ticketId"]
}
},
{
name: "run_safe_script",
description: "Run a whitelisted readonly operational script",
inputSchema: {
type: "object",
properties: {
token: { type: "string", description: "Access token" },
scriptName: { type: "string", enum: ["health-check"] }
},
required: ["token", "scriptName"]
}
}
]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (name === "get_ticket") {
const input = getTicketInput.parse(args);
checkAuth(input);
const tickets = await loadTickets();
const ticket = tickets.find(t => t.id === input.ticketId);
if (!ticket) {
return {
content: [
{
type: "text",
text: JSON.stringify({ error: "Ticket not found", ticketId: input.ticketId }, null, 2)
}
],
isError: true
};
}
await writeAuditLog({
type: "tool_call",
tool: name,
ticketId: input.ticketId
});
return {
content: [
{
type: "text",
text: JSON.stringify(ticket, null, 2)
}
]
};
}
if (name === "run_safe_script") {
const input = runSafeScriptInput.parse(args);
checkAuth(input);
const scriptPath = SAFE_SCRIPTS[input.scriptName];
if (!scriptPath) {
return {
content: [
{
type: "text",
text: JSON.stringify({ error: "Script is not allowed" }, null, 2)
}
],
isError: true
};
}
const { stdout, stderr } = await execFileAsync(scriptPath, [], {
timeout: 5000,
env: {
PATH: process.env.PATH
}
});
await writeAuditLog({
type: "tool_call",
tool: name,
scriptName: input.scriptName
});
return {
content: [
{
type: "text",
text: JSON.stringify({ stdout, stderr }, null, 2)
}
]
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({ error: `Unknown tool: ${name}` }, null, 2)
}
],
isError: true
};
} catch (error) {
await writeAuditLog({
type: "tool_error",
tool: name,
message: error.message,
code: error.code || "UNKNOWN"
});
return {
content: [
{
type: "text",
text: JSON.stringify(
{
error: error.message,
code: error.code || "UNKNOWN"
},
null,
2
)
}
],
isError: true
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
这段代码已经具备“最小可运行”条件:
- 能启动
- 能列出工具
- 能读取资源
- 能执行白名单脚本
- 能做参数校验
- 能写审计日志
第四步:本地开发调试流程
很多人第一次做 MCP Server,最容易卡住的不是业务逻辑,而是“看不到它到底有没有工作”。所以本地调试建议固定成下面这个流程。
1. 先在命令行直接启动
MCP_TOKEN=dev-token npm start
如果没有报错,说明 server 至少能完成初始化并通过 stdio 挂起等待客户端连接。
2. 先检查静态部分:资源和工具定义
你在接 Cursor 或 Claude Code 之前,先人工确认两件事:
- 工具名字是不是清晰、单义
inputSchema是否足够严格
尤其是下面这些字段,要尽量在 schema 里写死:
- 必填参数
- 枚举值
- 字符串最小长度
- 能否为空
- 是否允许额外字段
为什么?因为工具定义越模糊,模型越容易“猜参数”。而模型一旦开始猜,就会导致调用稳定性下降。
3. 用最小输入测试失败路径
你不光要测试成功,还要故意测试失败:
- token 错误
- ticketId 不存在
- scriptName 不在白名单
- 脚本超时
真正线上稳定的关键,不是“成功时能跑通”,而是“失败时能返回可解释错误”。
例如这个服务已经把错误统一整理成:
isError: trueerror.messageerror.code
这样 AI 客户端更容易决定下一步是重试、改参数,还是向用户解释。
4. 检查日志是否完整
查看:
cat logs/audit.log
你应该能看到类似记录:
{"time":"2025-01-10T10:00:00.000Z","type":"read_resource","uri":"kb://deploy.md"}
{"time":"2025-01-10T10:01:00.000Z","type":"tool_call","tool":"get_ticket","ticketId":"OPS-101"}
这一点非常关键。没有日志,就没有审计;没有审计,AI 接内部系统就很难过安全评审。
第五步:工具 schema 应该怎么设计,才不会越用越乱
MCP 不只是“把 API 搬给 AI 用”,而是要重新设计 AI 可调用边界。
原则一:工具名描述动作,不描述场景故事
好名字:
get_ticketrun_safe_scriptsearch_kbcreate_deploy_request
不好的名字:
help_devopsdo_releasehandle_issue
原因很简单:越抽象,模型越容易误用。
原则二:一个工具只做一件事
不要把下面这些动作塞进一个工具:
- 查工单 n- 更新工单
- 执行脚本
- 发布服务
- 回滚服务
正确做法是拆开:
get_ticketcomment_ticketcreate_deploy_requestexecute_deploy_plan
这样做的价值是:
- 权限更容易拆分
- 审计更容易定位
- 模型更不容易误触发危险动作
原则三:优先只读,写操作单独升权
本教程示例里,知识库和工单都是只读,脚本也只允许执行白名单里的只读检查脚本。
这不是保守,而是工程常识。
建议你的 MCP 工具按风险分三级:
L1:只读工具
例如:
- 查知识库
- 查工单
- 查监控
- 查发布记录
特点:默认可用,适合让 AI 高频调用。
L2:受限写工具
例如:
- 给工单加备注
- 创建变更申请
- 生成回滚草案
特点:可写但影响有限,适合加二次确认。
L3:高危执行工具
例如:
- 直接部署生产
- 删除资源
- 重启核心服务
- 修改数据库
特点:不要直接暴露给通用 AI 助手,必须走审批、人确认、短时凭证和最小权限。
一句话:能读就别写,能生成计划就别直接执行。
第六步:不要让 AI 直接拿到高危权限
这是整篇最重要的部分。
很多事故不是模型“太笨”,而是工程师“给太多”。
错误示范
以下做法都不推荐:
- 在系统提示里硬编码生产 Token
- 把 SSH 私钥挂给所有工具共用
- 允许 AI 任意传 shell 命令
- 让一个工具同时支持“查看日志”和“删除 Pod”
- 用管理员权限调用内部工单/部署 API
正确做法
1. 永远白名单,不要自由命令执行
本教程里的 run_safe_script 只接受:
health-check
而不是接收:
command: "rm -rf ..."bash: "kubectl delete ..."
也就是说,AI 只能在你预先定义好的动作集合里选,不能自由拼接系统命令。
2. 凭证与工具能力分离
不要让模型知道真实凭证内容。
示例里为了最小运行,把 token 放进参数只是为了演示鉴权流程。真实团队落地时更推荐:
- MCP Server 自己持有服务凭证
- 客户端只传用户身份或会话身份
- 服务端根据身份映射权限
- 对高危工具启用单独审批令牌
也就是说,AI 不应该拿到“万能钥匙”,它应该只拿到“申请调用某个已定义能力的资格”。
3. 把“生成计划”和“执行动作”拆开
部署场景推荐这样设计:
plan_deploy:返回将要执行的步骤、目标环境、风险提示approve_deploy:由人确认后生成一次性授权execute_deploy:只接受签过名的计划 ID,不接受自由参数
这样即使模型理解错了,也最多先生成一个计划,而不是直接改生产。
4. 给高危工具增加显式确认字段
比如:
confirm: "YES_I_UNDERSTAND"changeRequestIdapprover
这不是防君子,而是给流程加“减速带”。
第七步:资源暴露怎么做才实用
很多人上来只想着 Tools,其实 Resources 同样重要。
资源适合承载什么?
- 知识库文档
- 值班手册
- 架构说明
- 故障处理流程
- 发布规范
工具适合承载什么?
- 查某个工单
- 执行健康检查
- 创建申请
- 触发某个受限接口
本教程里我们把 Markdown 文档作为 kb:// 资源暴露出去。这么做有两个好处:
- 只读语义很清楚
- AI 在回答问题时可以引用稳定文档,而不是只靠上下文猜
如果你的内部知识来源很多,建议不要一开始就做“全公司统一搜索”。先从一个可控目录开始,比如:
kb://deploy/*kb://oncall/*kb://service-catalog/*
分层暴露,比一口气把所有文档都交给模型更稳。
第八步:如何在 Cursor 或 Claude Code 中接入
不同客户端的配置格式会有差异,但核心思路一致:通过 stdio 启动本地 MCP Server,然后让客户端发现你的工具和资源。
Cursor 接入思路
在 Cursor 的 MCP 配置里添加一个本地 server,核心信息通常包括:
- 命令:
node - 参数:
/你的路径/server.js - 环境变量:
MCP_TOKEN=dev-token
示意配置如下(字段名可能随版本略有差异,请按你当前客户端文档调整):
{
"mcpServers": {
"internal-gateway": {
"command": "node",
"args": ["/absolute/path/to/mcp-internal-gateway/server.js"],
"env": {
"MCP_TOKEN": "dev-token"
}
}
}
}
接好以后,你可以在 Cursor 里发这样一类请求:
- “读取内部部署规范并总结回滚条件”
- “查询工单 OPS-101,结合知识库给出排查步骤”
- “执行 health-check 并解释结果”
这时候 AI 不再是“凭印象回答”,而是会去读资源、调工具、拿真实返回值。
Claude Code 接入思路
Claude Code 侧同样是把你的 MCP Server 注册为本地能力源。核心依然是:
- stdio 启动
- 指定命令与参数
- 注入必要环境变量
示意配置:
{
"servers": {
"internal-gateway": {
"command": "node",
"args": ["/absolute/path/to/mcp-internal-gateway/server.js"],
"env": {
"MCP_TOKEN": "dev-token"
}
}
}
}
接入成功后,建议先用最简单的提示测试:
- “列出你可用的内部工具”
- “读取 kb://deploy.md”
- “查询 OPS-102,并基于结果给出下一步排查建议”
如果工具能列出但调用失败,优先排查三件事:
- 路径是不是绝对路径
- 环境变量有没有传进去
- Node 版本和依赖是否正确安装
第九步:给一个完整调用示例
假设你在 Cursor 里输入:
“请先读取部署回滚说明,再查询工单 OPS-101,最后告诉我这次问题更适合先回滚还是先排查配置。”
一个合理的执行路径应该是:
- AI 读取
kb://deploy.md - AI 调用
get_ticket,参数为ticketId=OPS-101 - AI 综合两部分内容生成建议
最后回答应类似:
- 工单显示健康检查失败,且摘要提示需要确认最近配置变更
- 知识库说明只有在 5 分钟内错误率高于 3% 时优先回滚
- 当前信息不足以直接回滚,建议先确认最近配置变更与错误率趋势
重点不在于这段自然语言本身,而在于它背后已经依赖了真实资源与工具,而不是“纯靠模型猜”。
第十步:错误处理怎么做,AI 才真的可用
MCP Server 不是 API 文档展示器,它是给模型调的,所以错误处理一定要面向“模型能否纠正下一步”。
推荐做法
1. 返回结构化错误
不要只抛一句:
something went wrong
要尽量返回:
- 错误类型
- 具体对象
- 是否可重试
- 建议修复动作
比如你可以把 get_ticket 的错误进一步细化成:
UNAUTHORIZEDNOT_FOUNDRATE_LIMITEDUPSTREAM_UNAVAILABLE
2. 区分用户错误和系统错误
用户错误:
- ticketId 填错
- scriptName 不在允许列表
- 缺少必填字段
系统错误:
- 上游工单系统超时
- 知识库目录损坏
- 脚本执行失败
这两类错误要分开,不然 AI 容易瞎重试。
3. 给错误留追踪字段
真实团队里建议每次调用都生成:
requestIdactorsessionIdtool
这样你排查问题时,不会只看到一堆匿名报错。
第十一步:审计日志怎么记,才能过团队治理这一关
很多团队把日志理解成“记一下就行”,其实不够。
至少要覆盖下面这些字段:
- 时间
- 调用人/调用会话
- 工具名或资源 URI
- 输入摘要
- 结果状态
- 错误码
- 执行耗时
本教程示例里只做了最基础的本地文件日志,方便你快速跑通。真实项目建议升级成:
- 输出 JSON 日志到 stdout
- 接入 ELK / Loki / Datadog / Cloud Logging
- 对敏感字段脱敏
- 为高危工具单独建审计索引
特别提醒:日志里不要直接记明文口令、Cookie、私钥、原始授权头。
正确做法是:
- 只记 token 指纹或哈希前缀
- 对参数做脱敏摘要
- 对工单内容按字段分级落库
第十二步:把一次性原型整理成团队可复用的 Skill / 工程模板
很多 MCP Demo 最后没落地,不是因为技术不行,而是因为没人把它整理成团队资产。
建议你把原型往两个方向沉淀。
方向一:沉淀为工程模板
把下面这些东西固定下来:
- 统一目录结构
- 工具注册方式
- 鉴权中间件
- 日志中间件
- 错误码规范
- 风险分级规范
- 本地调试脚本
- 示例配置文件
比如团队模板可以长这样:
template-mcp-server/
src/
tools/
resources/
middleware/
auth/
logging/
examples/
docs/
.env.example
README.md
这样新接一个内部系统时,研发只需要实现:
- 工具输入输出
- 上游 API 适配
- 权限映射
而不是每次都从零开始搭架子。
方向二:沉淀为 Skill
工程模板解决“怎么实现”,Skill 解决“怎么稳定使用”。
你可以把实践经验整理成团队 Skill,例如:
- 什么场景该先读资源再调工具
- 工单分析时必须返回哪些字段
- 发布类操作必须分几步确认
- 故障排查回答必须引用哪些知识库资源
站内可以结合 update-skills 持续更新这些经验;如果你已经有一批常见工单,还可以用 kb-article 把高频问题整理成知识库文章,反过来作为 MCP 资源供 AI 读取。
这一层非常重要:
- 模板让能力可复制
- Skill 让使用方式可复制
两者结合,团队才不会一直重复造轮子。
一个更贴近真实团队的落地路线
如果你准备在团队里正式推进,不要一上来就接生产部署。推荐按下面顺序演进:
第 1 阶段:只读接入
先接:
- 知识库
- 工单查询
- 发布记录查询
- 监控只读查询
目标:验证模型能否稳定理解和调用。
第 2 阶段:低风险写操作
再接:
- 工单备注
- 创建变更申请
- 生成排查报告
目标:验证审计、权限、确认流程。
第 3 阶段:半自动执行
例如:
- 先生成部署计划
- 人确认
- 再执行预定义流水线
目标:把执行控制在“计划化、审批化、可追踪”的框架内。
第 4 阶段:团队模板化
最后再做:
- 脚手架
- Skill 沉淀
- 文档规范
- 统一日志和错误码
目标:从“一个人会用”变成“团队能复制”。
小结
把 AI 接到内部系统,难点从来不是“提示词够不够聪明”,而是“能力边界够不够清晰”。
普通提示词的问题在于:
- 只能描述,不能真正连接
- 没有结构化 schema
- 没有稳定鉴权
- 没有权限边界
- 没有审计日志
MCP 的价值在于把这些工程问题标准化,让模型通过资源和工具去接触真实系统,同时把风险控制在你定义的边界内。
本文这个最小案例虽然简单,但已经覆盖了落地时最关键的骨架:
- 用 Resources 暴露只读知识
- 用 Tools 暴露受控动作
- 用 schema 保证调用稳定
- 用白名单限制脚本执行
- 用鉴权和日志满足治理要求
- 用模板和 Skill 把原型变成团队资产
你可以先把这套示例在本地跑起来,再逐步替换成真实的知识库 API、工单系统和部署平台。建议第一步永远从只读能力开始,先让 AI 学会“可靠获取信息”,再考虑“安全执行动作”。
如果你所在团队已经在大规模使用提示词优化工具,可以继续保留那一层;但从工程实践看,提示词负责表达,MCP 负责接入,二者不是替代关系,而是上下游关系。
当你把这层分清楚,AI 才会从“会聊天”真正进化成“可治理的研发助手”。
// from this post
// related reading
在 Cursor 里搭一套可复用的 AI 代码评审 Skill:用自定义 MCP Server 接管 Git diff、测试与静态检查
这篇教程带你从零设计一套面向工程团队的 AI 代码评审方案:用自定义 MCP Server 暴露仓库变更、测试结果和静态检查能力,再在 Cursor 中编排一个可复用 Skill,让 AI 自动完成 PR 初审、风险标注、变更摘要与修复建议。
给 Cursor/Claude Code 接上项目大脑:用 MCP 把代码索引、文档和变更记录变成可检索上下文
只靠 IDE 当前打开的文件,AI 很难在中大型仓库里稳定协作。本文从零搭建一个最小可用 MCP Server,把代码、文档和 Git 变更暴露给 Cursor 或 Claude Code,让建议更可追溯、更可信。
从零搭建基于 MCP 的开发助手工作流:让 Cursor / Claude Code 先读文档、再改代码、最后产出可审查 PR
这篇教程面向有项目经验的开发者,讲清楚如何把 MCP 接进 Cursor 或 Claude Code,搭建一条“读仓库文档与 issue → 定位问题 → 生成修复方案 → 修改代码 → 跑测试 → 输出 PR 说明”的可复用工作流。