内容
# ts-lsp-mcp
一个将TypeScript 7原生语言服务器(`tsc --lsp --stdio`,基于Go的编译器)暴露给编码代理的MCP服务器:跳转到定义、查找引用、悬停类型、文件诊断——由实际构建项目的编译器回答,而不是从文本搜索重建。
```
LLM主机(Claude Code / VS Code / 其他MCP主机)
│ MCP over stdio
▼
ts-lsp-mcp(本项目,Node)
│ LSP(JSON-RPC over stdio)
▼
tsc --lsp --stdio (TypeScript 7原生,Go二进制)
```
设计约束,一开始就说明:
- **一种语言,一个项目根目录,每个实例。** 传递 `--root`;为多个项目运行多个实例。这不是一个多语言桥。
- **只读。** 仅限导航和诊断。没有重命名、代码操作或编辑工具,也不会有;编辑操作保留在代理的正常文件编辑路径中,以便审查。
- **编译器是索引。** 符号查找通过 `workspace/symbol` 在实时服务器上进行。没有侧数据库来构建、预热或使无效。
- **磁盘真实性。** 在每个请求之前,目标文件从磁盘重新读取并同步到服务器,因此结果反映最新的编辑,无论谁进行的编辑。
- **延迟生命周期。** LSP进程在第一个工具调用时启动,如果崩溃则重新启动。
## 与现有工具的关系
多个项目将MCP桥接到语言服务器。哪一个合适取决于仓库和代理;ts-lsp-mcp是为特定情况构建的。
- **[mcp-language-server](https://github.com/isaacphi/mcp-language-server)** (isaacphi,Go)是最著名的通用MCP↔LSP代理:指向任何stdio语言服务器,获取定义、引用、重命名和诊断。对于TypeScript,它驱动基于Node的`typescript-language-server`,因此继承了tsserver时代的启动和查询延迟,其工具接受文件/行/列位置——代理必须先grep才能找到它们。它还包括重命名,这是一种写操作。 一个分支(t3ta)在一个进程中管理多个语言服务器,用于多语言仓库。 如果您的仓库混合了语言,那么这一系列是合适的;ts-lsp-mcp故意不这样做。
- **[lsmcp](https://github.com/mizchi/lsmcp)** (mizchi)是最近的邻居。它是多语言的,具有可插入的 `--bin`,并且已经支持 `tsgo` (TS7原生预览)作为后端——如果您想要TS7原生查询加上一个服务器后面的其他语言,请使用lsmcp。 区别在于架构:lsmcp需要Node 22+,并且维护自己的索引/概述层(项目概述,符号搜索由SQLite支持);ts-lsp-mcp是一个更薄的单语言桥(Node ≥ 18.17),它将编译器的自身语义索引视为唯一的真实来源,并花费精力在查询人体工程学(按名称定位符号,消歧义,错误位置检测)而不是索引。
- **[Serena](https://github.com/oraios/serena)** (Oraios)是一个完整的代理工具包,而不是桥:符号级检索加上符号编辑(替换符号主体,跨文件重命名/移动),40多种语言,Python/uv运行时。 如果您希望代理通过语义工具执行重构,Serena是一个严肃的选择——这里没有任何竞争。 权衡取舍是一个更大的工具表面供模型学习,一个更重的运行时,和(对于TS)底层语言服务器。
- **[agent-lsp](https://www.agent-lsp.com/)** 是一个有状态的多语言运行时:它索引工作区一次,保持服务器温暖,通过约30种语言的扩展路由文件,并在上面分层工作流工具。 再次适合多语言设置;再次是底层TS服务器。
- **原始LSP包装器** ([Tritlo/lsp-mcp](https://github.com/tritlo/lsp-mcp),jonrad/lsp-mcp等)或多或少直接暴露LSP请求——悬停,完成,代码操作,显式服务器启动。 最大通用性,最小人体工程学:代理管理位置,文档生命周期,有时还管理服务器启动。
ts-lsp-mcp与其他工具的不同之处在于:
- **专门构建于 `tsc --lsp`。** TypeScript 7.0没有提供编程API(计划在7.1中提供);LSP是支持的集成表面,Go编译器在毫秒内回答这些查询,而tsserver则需要数秒。 二进制解析和版本控制(拒绝TS ≤ 6的`tsc`,它没有 `--lsp`标志)在这里处理,而不是留给用户。
- **按名称定位符号,并具有消歧义功能。** 每个导航工具都接受一个裸 `symbol` 名称,通过编译器的语义索引解析。 模糊的名称返回消歧义列表;未知名称返回接近的匹配。 基于位置的桥接需要grep-then-position两步操作来回答最常见的代理问题(“X在哪里定义/使用”)。
- **错误位置检测。** 位置查询回声实际找到的标识符,以便错误的位置查询产生明显错误的结果而不是静默错误的结果。
- **设计为只读**,如果您希望在不添加第二个写入路径的情况下进行语义导航,则此功能很重要。
而它故意不做的事:多语言,多根实例,编辑/重构,完成(编写整个编辑的代理几乎不需要光标完成),或嵌入语言 - 参见下面的限制。
## 适用场景
在以下情况下使用ts-lsp-mcp:
1. 项目是纯TypeScript/JavaScript(`.ts/.tsx/.js/.jsx`)并且可以采用TypeScript 7。
2. 代理的故障模式您关心的是导航 - 从grep和猜测中回答“谁调用此”,“此类型是什么”,“此是否仍然类型检查”。
3. 您希望从编译器获得这些答案,而不给代理一个语义写入路径。
在以下情况下选择其他方案:仓库是多语言的(lsmcp,agent-lsp,或mcp-language-server的分支),您想要语义重构工具(Serena),您停留在TS ≤ 6或需要Vue/Svelte/Astro支持(任何基于tsserver的桥接),或您的主机已经提供等效的内置代码智能。
配套项目: [ts-header-mcp](../ts-header-mcp) 解决了相邻的问题 - 通过头样式签名列表提供廉价的整个文件/项目定位。 这两个项目组合使用:头部找到正确的位置,这个服务器用于精确的问题。
## 工具
| 工具 | 功能 |
| --- | --- |
| `ts_definition` | 跳转到定义 - 按 `symbol` 名称或 `file`/`line`/`column` |
| `ts_type_definition` | 跳转到 *type* 的声明 |
| `ts_implementations` | 查找接口/抽象成员的实现 |
| `ts_references` | 整个项目范围内的所有引用(跟随别名导入;无注释/字符串假阳性) |
| `ts_hover` | 推断类型 + 符号的JSDoc |
| `ts_document_symbols` | 文件大纲(默认按声明级别;`depth: "all"` 用于局部变量) |
| `ts_workspace_symbols` | 项目范围内的模糊符号搜索 |
| `ts_diagnostics` | 类型检查文件;带有代码上下文的错误 |
| `ts_server_info` | 使用哪个TS二进制/版本 |
所有位置都是1为基数(行和列),与编辑器和模型显示的内容匹配。
### 按名称定位符号
每个导航工具都接受名称或精确位置:
```jsonc
{ "symbol": "offerSpotToWaitlist" } // 一个调用,不需要位置
{ "symbol": "process", "file": "src/queue.ts" } // 消歧义同名符号
{ "file": "src/queue.ts", "line": 42, "column": 10 } // 精确位置
```
### 输出约定
由于观察到的代理故障模式,存在三种约定:
- 位置查询回声解析的标识符(`124 reference(s) to 'internal' (symbol at …)`),使错误的位置查询可见。
- 引用结果以每个文件的计数为前导。 当所有引用都位于单个文件中时,输出会注意到代码生成或代理间接(例如框架生成的API命名空间)可能隐藏跨文件用法,并建议进行文本搜索交叉检查,因为自信的不完整引用列表比没有引用列表更糟糕。
- 结果以 `//` 前缀的下一步提示结束(`// callers: ts_references({symbol: "…"})`),因此后续调用会拼出调用,而不是依赖工具选择运气。
## 要求
- Node.js ≥ 18.17(运行此服务器)
- TypeScript 7可用于*目标*项目 - 以下任意一项:
- 在该项目中运行 `npm install -D typescript@^7` (推荐),或
- 运行 `npm install -g @typescript/native-preview` (提供 `tsgo`),或
- 通过 `--lsp-path` / `TS_LSP_SERVER_PATH` 使用显式二进制
二进制解析顺序:显式路径 → 项目本地 `node_modules/.bin/tsc` (v7+)→ 项目本地 `tsgo` → PATH上的 `tsgo` → PATH上的 `tsc` (v7+)。 TypeScript ≤ 6的 `tsc` 被拒绝,并带有解释性错误(它没有 `--lsp` 标志)。
## 安装和构建
```bash
cd ts-lsp-mcp
npm install
npm run build
# 对照真实项目进行健全性检查:
node dist/index.js --root /path/to/your/ts/project --self-test
```
`--self-test` 解析二进制文件,进行LSP握手,然后退出 - 在将主机连接起来之前,对每个项目运行一次,以便配置问题以可读错误的形式出现,而不是在代理内部出现静默工具失败。
## 连接
### Claude Code
```bash
claude mcp add ts-lsp -- node /abs/path/to/ts-lsp-mcp/dist/index.js --root /abs/path/to/your/project
```
或在 `.mcp.json` 中位于项目根目录( `--root` 可以省略,因为cwd是项目):
```json
{
"mcpServers": {
"ts-lsp": {
"command": "node",
"args": ["/abs/path/to/ts-lsp-mcp/dist/index.js"]
}
}
}
```
### VS Code (`.vscode/mcp.json`)
```json
{
"servers": {
"ts-lsp": {
"type": "stdio",
"command": "node",
"args": [
"/abs/path/to/ts-lsp-mcp/dist/index.js",
"--root", "${workspaceFolder}"
]
}
}
}
```
或(对于Antigravity IDE 2.1+,省略根目录)
```json
{
"servers": {
"ts-lsp": {
"type": "stdio",
"command": "node",
"args": [
"/abs/path/to/ts-lsp-mcp/dist/index.js"
]
}
}
}
```
### 其他MCP主机
任何支持stdio MCP服务器的主机都以相同的方式工作:配置命令为 `node /abs/path/to/ts-lsp-mcp/dist/index.js --root <projectDir>`。 请参阅主机文档,了解其 `mcp.json` (或等效文件)的位置。
## 配置参考
| 标志 | 环境变量 | 含义 |
| --- | --- | --- |
| `--root <dir>` | `TS_LSP_PROJECT_ROOT` | 项目根目录(默认:cwd) |
| `--lsp-path <bin>` | `TS_LSP_SERVER_PATH` | 显式TS 7二进制文件(`tsc`或`tsgo`) |
| `--self-test` | — | 解析二进制文件,进行LSP握手,退出 |
## 限制
- **单一语言,单个根目录。** 文件夹外部 `--root` 被拒绝。 多语言仓库需要每个项目一个实例,或上述多语言桥之一。
- **不支持嵌入语言。** Vue SFC,Svelte,Astro,MDX和Angular模板依赖于TypeScript 6 API,尚未被TS7的语言服务器处理。 普通的 `.ts/.tsx/.js/.jsx` 完全支持。
- **列是UTF-16代码单元** (LSP默认值)。 对于普通代码,这与字符计数匹配;包含表情符号或星际字符的行可能因代码单元差异而偏移。
- **无编程API备用。** TypeScript 7.0没有编译器API(计划在7.1中提供);如果LSP未公开任何内容,则此服务器也不能公开。
## 开发
```bash
npm run dev # 通过tsx从源代码运行
npm run build # 使用TS7 tsc编译
```
项目布局:
```
src/
index.ts CLI入口,MCP stdio传输
config.ts TS7二进制解析 + 版本控制
session.ts LSP生命周期(延迟启动,崩溃后重启)
resolve.ts 符号名称 -> 位置解析 + 消歧义
lsp/
jsonrpc.ts 内容长度帧编码器
client.ts LSP客户端(握手,请求,诊断)
documents.ts didOpen/didChange从磁盘同步
mcp/
server.ts MCP工具定义
util/
position.ts 1为基数 ⇄ 0为基数,路径 ⇄ URI
snippet.ts 源预览 / 上下文片段
```
## License
MIT