用 MCP 给 Cursor 装上本地代码库助手:仓库理解、依赖追踪与变更分析
问题背景:为什么普通聊天 AI 很难理解大型项目
把整仓代码直接塞给 AI,通常只会得到两种结果:
- 上下文不够:仓库一大,token 立刻爆;
- 上下文太多但没结构:模型看到了文件内容,却不知道入口、依赖、调用链和最近改动。
对真实项目来说,问题不在“模型会不会写代码”,而在于它能不能先找到对的代码,再基于证据回答。
所以更实用的做法不是聊天式问答,而是把 Cursor 接到一个只访问本地仓库的 MCP Server 上,让 AI 按步骤取证:
- 先看仓库结构
- 再查符号和文本
- 再追依赖和调用链
- 最后结合 git diff 给出变更建议
这就是本文要搭的东西。
目标:把 Cursor 变成“本地仓库问答助手”
我们要做的是一个 MCP Server,给 Cursor 暴露一组受控工具,让它在本地仓库里完成这些任务:
- 仓库结构分析
- 文件检索与关键词搜索
- AST 级符号定位
- 依赖关系追踪
- git diff 摘要
- 生成模块说明和变更建议
核心原则只有一个:不让模型一次性吞全量代码,而是按需拿上下文。
推荐配套看这些站内资产:
nikondrat-cctx-mcp:更偏结构化代码分析和节省 token812diasp-code-review-mcp:适合做代码审查、测试建议和文档生成akshay1018-codedoc-mcp-server:适合审计、重构、文档化bbarney7-cursor-mcp-server:更偏 Cursor 代理执行和仓库检索
方案设计:工具边界先定死
别一上来就暴露“读整个仓库”这种大口子。真正有用的 MCP 工具,应该是小而明确的。
建议的工具集
1)repo_tree
返回仓库目录树,支持深度限制和路径过滤。
用途:快速找模块边界、入口文件、测试目录。
2)grep_text
在本地仓库里做关键词检索,返回命中的文件和行号。
用途:找接口名、错误码、配置项、调用点。
3)ast_symbols
基于 AST/语言服务提取符号:函数、类、方法、导出项。
用途:避免纯文本搜索误伤,定位“这个函数真正定义在哪”。
4)dep_graph
查询模块依赖、函数调用或 import 关系。
用途:追跨文件调用链,分析重构影响面。
5)git_diff_summary
读取本地 git diff,生成摘要、风险点和测试建议。
用途:代码评审、上线前检查、回归定位。
6)read_snippet
只读取指定文件的指定行区间。
用途:让模型拿到最小必要上下文。
为什么要这样拆
- grep 解决“我大概知道关键词”的问题
- AST 解决“我想知道定义和导出”的问题
- 依赖图 解决“影响到谁”的问题
- diff 摘要 解决“最近改了什么”的问题
这四类信息拼起来,已经足够支撑大多数仓库问答。
第一步:搭一个本地 MCP Server
下面给一个最小可用的 TypeScript 结构,重点是思路,不是追求完整框架。
ts // 伪代码 / 起步示例:Node.js + MCP SDK import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import fs from "node:fs/promises"; import path from "node:path"; import { execFile } from "node:child_process"; import { promisify } from "node:util";
const exec = promisify(execFile); const ROOT = process.env.REPO_ROOT!;
function safeJoin(...parts: string[]) { const p = path.resolve(ROOT, ...parts); if (!p.startsWith(path.resolve(ROOT))) throw new Error("path blocked"); return p; }
const server = new Server({ name: "local-repo-assistant", version: "0.1.0" });
server.tool(
"repo_tree",
{
path: z.string().default("."),
depth: z.number().int().min(1).max(6).default(3)
},
async ({ path: rel, depth }) => {
const target = safeJoin(rel);
// 这里可以自己实现递归,也可以调用 tree 命令
const { stdout } = await exec("bash", ["-lc", find '${target}' -maxdepth ${depth} -type f | sed 's#^${ROOT}/##' | head -n 300]);
return { content: [{ type: "text", text: stdout }] };
}
);
server.tool(
"grep_text",
{ query: z.string(), glob: z.string().default("**/*") },
async ({ query, glob }) => {
const cmd = rg -n --hidden --glob '${glob}' '${query.replace(/'/g, "'\\''")}' '${ROOT}' | head -n 200;
const { stdout } = await exec("bash", ["-lc", cmd]);
return { content: [{ type: "text", text: stdout || "no match" }] };
}
);
server.tool(
"read_snippet",
{ file: z.string(), start: z.number().int().min(1), end: z.number().int().min(1) },
async ({ file, start, end }) => {
const abs = safeJoin(file);
const lines = (await fs.readFile(abs, "utf8")).split(/\r?\n/).slice(start - 1, end);
return { content: [{ type: "text", text: lines.map((l, i) => ${start + i}: ${l}).join("\n") }] };
}
);
await server.connect(new StdioServerTransport());
这段代码只做了三件事:
- 只允许访问
REPO_ROOT - 只暴露有限工具
- 只返回片段,不返回整仓
这就是“私有代码保护”的第一层。
第二步:把 AST 和依赖图补上
纯文本检索会漏掉很多信息。比如:
- 重名函数
- 类方法覆盖
- 只在 export 中出现的符号
- import/re-export 链路
所以建议再加两个能力:
AST 符号提取
对常见语言可以先做轻量版:
- TypeScript / JavaScript:
ts-morph或 TypeScript Compiler API - Python:
tree-sitter/ast - Go:
go/parser
你不需要一开始就全语言支持,先覆盖项目主语言就够用。
伪代码:提取 TS 符号
ts // 伪代码 function listSymbols(filePath: string) { const source = ts.createSourceFile(...) return source.statements .filter(isFunctionOrClassOrExport) .map(node => ({ name: getName(node), kind: getKind(node), start: node.pos, end: node.end })) }
依赖图查询
先从 import/export 入手就够用了:
A imports BA re-exports BA calls B(可选,后续再加)
先把“文件依赖图”做出来,再考虑“函数调用图”。很多实际问题只需要前者。
第三步:让 Cursor 分步取上下文
这是最关键的一步。
不要让 Cursor 直接问“把这个仓库解释一下”。你应该让它按下面的顺序拿信息:
- 先看结构:这个模块在哪,入口在哪
- 再定位符号:目标函数/类在哪个文件
- 再读片段:只读相关行
- 再查依赖:谁调用它,改它会影响谁
- 最后结合 diff:最近改动是否引入风险
推荐的系统提示思路
你可以把约束写进 MCP 服务器说明或 Cursor 侧的 system prompt:
- 只在本地仓库内回答
- 不确定就先调用工具,不要猜
- 每次只读取最小必要片段
- 回答时标注依据来自哪些文件和行号
- 如果仓库里没有证据,明确说“未找到”
一个更好的交互流程
用户问:
登录失败时,鉴权链路在哪?改这个模块会影响哪些地方?
Cursor 不应该直接编答案,而应该依次:
grep_text("auth login token")ast_symbols("AuthService")read_snippet("src/auth/service.ts", 40, 120)dep_graph("src/auth/service.ts")git_diff_summary("src/auth/*")
这样出来的答案会比“整仓塞给模型”稳定得多。
第四步:设计高价值提问场景
真正能落地的助手,不是“能聊”,而是能解决固定高频问题。
1)新成员 onboarding
目标:快速理解项目。
问法示例:
- 这个仓库的主入口在哪里?
- 用户请求从哪条链路进入后端?
- 认证、支付、消息各自在哪些目录?
理想输出:
- 仓库结构图
- 关键模块职责
- 3 条最重要的请求链路
- 一份“先看什么,再看什么”的阅读顺序
2)定位跨文件调用链
目标:从一个错误点追到源头。
问法示例:
createOrder被谁调用?- 这个错误码是在哪个中间件抛出的?
- 从 controller 到 service 再到 repository 的链路是什么?
理想输出:
- 调用链列表
- 关键文件和行号
- 可能的断点位置
3)重构影响面分析
目标:知道改哪里最安全。
问法示例:
- 如果把
userId从字符串改成 UUID,会影响哪些模块? - 迁移这个 util,会波及哪些 import?
- 这个接口拆分后,哪些测试要补?
理想输出:
- 直接引用者
- 间接引用者
- 测试覆盖缺口
- 回滚风险点
4)生成模块说明
目标:把代码变成可读文档。
问法示例:
- 说明
billing模块的职责、入口、依赖和边界 - 为
auth目录生成一页开发说明
理想输出:
- 模块职责
- 对外接口
- 关键依赖
- 注意事项
这类场景最适合和 akshay1018-codedoc-mcp-server 搭配使用。
第五步:控制幻觉和保护私有代码
控制幻觉
让 AI 少瞎猜,最有效的办法不是“更聪明的模型”,而是更硬的证据链。
建议这样做:
- 所有结论尽量附文件名和行号
- 没证据就说“不足以判断”
- 不允许编造不存在的函数、目录、配置项
- 答案里区分“事实”和“推测”
一个实用规则:
只要问题涉及仓库内部细节,答案必须至少引用一次工具结果。
评估回答质量
别只看“像不像”。建议做一份小型 benchmark:
- 10 个 onboarding 问题
- 10 个调用链问题
- 10 个影响面分析问题
- 10 个模块文档问题
每题评估四项:
- 准确率:有没有答对
- 完整性:关键路径是否漏掉
- 可追溯性:能否指向具体文件
- 成本:工具调用次数和 token 是否合理
保护私有代码
最低要求:
- 只读本地仓库,不连外网
- 路径白名单,禁止越权访问
- 不暴露环境变量、密钥文件、
.env - 工具返回内容做长度限制
- 日志里不要落完整源码
如果要更严一点,可以再加:
- 敏感文件黑名单
- 只允许只读工具
- 审计工具调用记录
一个推荐的落地配置
如果你想 1 天内做出可用版本,建议按这个顺序:
repo_treegrep_textread_snippetgit_diff_summaryast_symbolsdep_graph
先把“能问、能查、能定位”做出来,再考虑更复杂的代码图谱。
后续想增强,可以接这些站内资产:
nikondrat-cctx-mcp:做结构化仓库理解和上下文压缩812diasp-code-review-mcp:做审查、测试建议、文档建议akshay1018-codedoc-mcp-server:做模块说明和重构文档3rdbrain-architectgbt-mcp-server:需要比较模型、估算成本时可参考mrsolution07-modelrouter-mcp:当你想给不同任务自动选模型时用得上
小结
把 Cursor 变成本地代码库助手,关键不是“让 AI 看更多代码”,而是:
- 用 MCP 把仓库能力拆成小工具
- 让 AI 按结构、符号、依赖、diff 分步取证
- 所有回答都回到本地文件和行号
- 只做仓库内推理,不把私有代码送出边界
这样搭出来的助手,才真正适合大型真实项目:可落地、可复用、可扩展。
// 本文推荐
// 相关阅读
从提示词到可控接入:用最小 MCP Server 打通内部知识库、脚本与工单系统
普通提示词能描述需求,却不能稳定、安全地让 AI 调用你的内部系统。本文用一个最小可运行的 MCP Server 示例,带你从本地调试到 Cursor/Claude Code 接入,搭出可审计、可复用的团队级集成方案。
在 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,让建议更可追溯、更可信。