内容
# codebase-bridge-mcp
一款只读的 [MCP](https://modelcontextprotocol.io) 服务器,允许 Claude 聊天 —— 在桌面或网页应用中 —— 探索您的本地代码仓库并回答问题,提供带有 `文件:行号` 引用的综合答案。**无需复制粘贴文件,无需授予聊天写入机器的权限。**
`ask_codebase` 在您的代码仓库中运行无界面 Claude Code(`claude -p`),并使用**专属**只读工具集(`读取`、`搜索`、`通配符` —— 无编辑、写入、Bash、代理或 MCP),以及一个**限制读取目标代码仓库**的钩子,以便 disposable 代理探索代码并返回综合的、`文件:行号` 答案。参见 [安全性](#安全性) 以了解具体强制执行的内容。
```
Claude 客户端 --MCP--> codebase-bridge --> claude -p (仅限读取/搜索/通配符)
└─ 探索 /path/to/your/repo
```
## 为什么我创建了这个
我最擅长思考问题的方式是在对话式 Claude 聊天中,而不是在编码代理中 —— 思想的来回讨论往往在那里更深入。但是,每次我想要询问代码的特定部分时,我都不得不去寻找正确的文件,找到相关的部分,然后手动粘贴 —— 每个问题都是如此。这打断了我的思维流程,而我正是在这个时候想要自由思考。
我寻找了能够弥补这一空白的工具,但发现现有的代码库工具都指向错误的方向:文件系统和代码仓库打包服务器(像 Repomix)为已经拥有您的代码仓库的编码代理提供服务,而结构索引服务器(像 codebase-memory-mcp)需要一个编码代理作为大脑。没有任何工具能将关于代码的已完成、只读答案带入我正在思考的聊天中。所以我创建了它:它将一个无界面的、只读的 Claude 绑定到一个单一的代码仓库(仅限读取/搜索/通配符 —— 无写入、无 Bash),并将综合答案返回给我的聊天。我可以在对话中随时询问我的代码库,而无需离开思考的流程。
## 要求
- [Claude Code](https://claude.com/claude-code)(`claude`)已安装并认证。
- [`uv`](https://docs.astral.sh/uv/)(运行服务器,无需安装 —— 依赖项在 `server.py` 中声明)。
- 对于可选的 `http` 传输:[`cloudflared`](https://github.com/cloudflare/cloudflared) 或任何 HTTPS 隧道。
## 工具
| 工具 | 功能 |
| ---- | ------------ |
| `ask_codebase(问题, 线程="", 模型="", 难度="", 显示步骤=False)` | 只读探索代码仓库并回答。 |
| `bridge_cost()` | 返回此服务器进程的每个线程 + 总成本账单。 |
| `bridge_forget(线程="")` | 丢弃线程会话(或所有线程),以便下次调用重新开始。 |
`ask_codebase` 参数:
- **`线程`** —— 一个友好名称(例如 `"身份验证调查"`)。重用**相同的名称**以保持一个 Claude Code 会话的活跃 —— **回合制控制**:每个后续调用都保留先前的上下文,因此您可以重定向它,并且它保持廉价(提示缓存命中,测量 45% → 95% → 99% 缓存读取)。省略以进行一次性新鲜会话。
- **`模型`** —— 例如 `"sonnet"` / `"opus"`。**绑定**到线程上的第一个调用;**拒绝**在现有线程上使用不同的模型,并附上注释,因为切换模型会强制重新处理整个上下文。启动新线程以使用不同的模型。
- **`难度`** —— 推理难度:`低`、`中等`、`高`、`极高`、`最大`。每个调用(在线程内安全地变化 —— 与模型不同,它不会使缓存失效)。
越低 = 越便宜/越快;提高它以进行复杂的跨文件推理。未知值被忽略并附上注释。
- **`显示步骤`** —— 附加探索轨迹,以便您看到**如何**到达答案并控制下一步。
示例答案:
```
身份验证在 app/middleware.py:42(verify_token)中强制执行,由 app/security.py:18 中的 @requires_auth 装饰器调用。
[bridge] 通过 2 步探索:
1. 搜索 'verify_token'
2. 读取 app/middleware.py
--
[bridge] 模型=opus-4-8 | 调用=$0.0383 | 缓存 58k 读取 / 19 新鲜(100% 缓存)
[bridge] 线程 'auth' 总计=$0.81,历时 3 次调用 —— 重用此线程以获得廉价的后续调用
```
## 安装 / 运行
### 标准输入输出(推荐 — Claude Desktop & Claude Code)
标准输入输出由客户端作为本地子进程启动:无需 URL,无需隧道,无需令牌。
**Claude Code:**
```sh
claude mcp add codebase-bridge -- uv run --script /ABS/PATH/server.py --repo /ABS/PATH/your-repo
```
**Claude Desktop:** 编辑 `~/Library/Application Support/Claude/claude_desktop_config.json`(macOS)—— 参见 [`examples/claude_desktop_config.json`](examples/claude_desktop_config.json):
```json
{
"mcpServers": {
"codebase-bridge": {
"command": "/opt/homebrew/bin/uv",
"args": ["run", "--script", "/ABS/PATH/server.py", "--repo", "/ABS/PATH/your-repo"]
}
}
}
```
使用 **`uv` 的绝对路径**(`which uv`)—— Claude Desktop 不会继承您的 shell `PATH`。编辑后重启 Desktop。然后询问:
*"在线程 'auth' 上使用 ask_codebase,显示步骤:登录如何工作?*
### http(仅限 claude.ai 网页)
claude.ai 从 Anthropic 的云端访问服务器,因此需要公共 URL + 令牌:
```sh
export BRIDGE_TOKEN=$(openssl rand -hex 16); echo "令牌: $BRIDGE_TOKEN"
uv run --script server.py --transport http --repo /ABS/PATH/your-repo # 127.0.0.1:8765
cloudflared tunnel --url http://localhost:8765 # 新终端
```
http 传输**拒绝在没有 `BRIDGE_TOKEN` 的情况下启动**(通过 `--insecure-no-auth` 故意运行开放服务器,例如当您用自己的身份验证前面运行时)。它
绑定到 **`127.0.0.1`** 默认情况下 —— 隧道连接到本地主机;仅当您确实需要其他接口时才设置 `--host 0.0.0.0`。令牌检查是恒定时间的。
在 claude.ai 中添加 `https://<tunnel>.trycloudflare.com/mcp`(无尾随斜杠)作为自定义连接器,并使用 bearer 令牌。
## 配置
所有旋钮都有 CLI 标志和/或环境变量:
| 环境变量 | CLI | 默认值 | 含义 |
| --- | --- | ------- | ------- |
| `BRIDGE_REPO` | `--repo` | 当前目录 | Claude Code 探索的代码仓库 |
| `BRIDGE_TRANSPORT` | `--transport` | `stdio` | `stdio` 或 `http` |
| `BRIDGE_TOKEN` | — | 未设置 | http bearer 令牌(http 除非设置或 `--insecure-no-auth` 否则拒绝启动) |
| `BRIDGE_HOST` | `--host` | `127.0.0.1` | http 绑定地址 |
| `BRIDGE_PORT` | — | `8765` | http 监听端口 |
| `BRIDGE_TIMEOUT` | — | `240` | 每个调用的时钟上限(秒) |
| `BRIDGE_MODEL` | — | Claude Code 默认值 | 新线程/临时调用的默认模型 |
| `BRIDGE_MAX_CONCURRENT` | — | `4` | `claude` 子进程的并发上限(DoS/成本保护) |
| `BRIDGE_THREAD_TTL` | — | `1800` | 线程会话空闲后丢弃的秒数(`0` = 永不) |
| `BRIDGE_MAX_THREADS` | — | `256` | 活动线程上限,超过后 LRU 驱逐 |
| `BRIDGE_CONFINE_READS` | — | `1` | PreToolUse 钩子限制读取到 `--repo`(`0` = 任意位置读取) |
| — | `--insecure-no-auth` | 关闭 | 允许 http 无令牌(显式选择加入;非环回主机拒绝) |
## 安全性
该项目**未经独立安全审计**。它已加强,保障措施已强制执行并经实证验证 —— 但在将其暴露于不受信任的输入之前请阅读本节。
**默认情况下强制执行(在 Claude 的工具调度层):**
- **专属只读工具集。** 代理使用 `--tools "Read Grep Glob"` 启动 —— *专属* 内置工具标志(不像 `--allowedTools`,它只是累加的,允许未列出的工具仍运行)。代理实际上**没有 Bash、编辑、写入、代理/子代理或 MCP 工具** —— 验证:它仅报告 `Read/Grep/Glob`。
- **读取限制在 `--repo` 内。** `PreToolUse` 钩子(此脚本,通过 `--settings` 注册)在每次工具调用前在主机端运行,并**拒绝任何路径解析到目标代码仓库外部**的读取操作(绝对路径、`../`、`/proc/self/environ` 等)—— 验证:读取 `/etc/hosts` 被拒绝并显示“目标代码仓库外部”。这是一个 `claude` 标志无法实现的功能(Read 按照设计接受绝对路径)。禁用 `BRIDGE_CONFINE_READS=0` 以进行受信任的本地跨代码仓库使用。
- **无 shell 注入 / 无标志注入。** 参数列表(无 shell);问题在 `--` 选项结束分隔符后传递,因此不能被重新解析为标志。
- **服务器自己的机密不会被继承。** `BRIDGE_*` 环境变量(包括 `BRIDGE_TOKEN`)从子环境中剥离,`/proc/self/environ` 在代码仓库外部,因此读取钩子也会阻止它。
- **http 传输。** 拒绝在没有 `BRIDGE_TOKEN` 的情况下启动(使用 `--insecure-no-auth` 覆盖,自身在非环回主机上被拒绝),绑定到 `127.0.0.1`,恒定时间令牌比较,并限制并发子进程(`BRIDGE_MAX_CONCURRENT`)。
**诚实的警告:**
- **这是策略执行,而不是内核沙盒。** 工具限制和读取钩子在 Claude Code 主机端评估并具有确定性(模型无法推理它们),但它们不是 OS 级边界。对于**完全不受信任**的客户端(例如,通过公共隧道暴露),请额外将进程封装在 OS 沙盒中(`sandbox-exec` 在 macOS 上,`bubblewrap` 在 Linux 上,或容器),以便即使假设的 `claude`/Node 泄露也无法逃脱。(`code.claude.com/docs/sandboxing`。)
- **代码仓库**内部的文件是可读的—— 包括任何已提交的机密(`.env`,密钥)。请将其指向您信任的代码仓库以暴露。
- **提示注入。** 来自代码仓库文件中的不受信任内容可能会尝试误导代理;读取限制钩子将影响范围限制在代码仓库本身。
## 已知限制
- **状态在内存中且按进程。** 线程会话和成本账单在重启时重置;目前没有跨重启的持久性(目前的设计如此)。空闲线程在 `BRIDGE_THREAD_TTL` 后被丢弃,并在超过 `BRIDGE_MAX_THREADS` 后被 LRU 驱逐。
- **飞行中取消。** 取消 MCP 请求不会杀死正在运行的 `claude` 子进程;它会完成(受 `BRIDGE_TIMEOUT` 限制)。
- **依赖于 `claude` CLI 的输出模式。** 它解析 `--output-format stream-json`;将来的 CLI 更改可能需要更新(烟雾测试保护工具表面,而不是解析器)。
- **未经独立安全审计。** 已对抗性审查(包括由单独的 AI 审查员),但未经专业审计。
## 成本模型
每次 `ask_codebase` 调用都会启动一个全新的无界面 Claude Code 代理,因此成本会根据其探索的程度而有所不同。重用 `线程` 的好处以**缓存命中率** 的增长和廉价的短期后续调用显示出来 —— 并非每次调用都严格降低价格。状态在每个服务器进程的内存中;重启会重置线程和账单。
## 开发
```sh
uv run tests/smoke.py # MCP 握手 + 工具表面检查(无需 API 密钥)
uv build # 构建轮子和 sdist
```
CI 在每次推送/拉取请求时运行两者(`.github/workflows/ci.yml`)。
## 许可证
MIT —— 参见 [LICENSE](LICENSE)。