dsh-intellij-mcp
Đã xác minhdsh-intellij-mcp · v1.0.5 · MIT · Giao diện web
IntelliJ IDEA MCP bridge for DeepSeek Harness: connects to the IDE's built-in MCP server over stdio (idea stdioMcpServer) and registers its tools as mcp__intellij__*, with a settings page for the connection
Cài đặt
dsh plugin add dsh-intellij-mcp Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.
Thẻ
Tác giả
Readme
dsh-intellij-mcp
DeepSeek Harness(DSH)插件:把 IntelliJ IDEA 内置的 MCP 服务器接入 DSH,让 Agent 能直接读写 IDE 里打开的项目、跑构建、做重构与检索,并在插件页提供可配置的连接设置。
安装后模型可用的工具形如 mcp__intellij__*;IDEA 的 MCP 服务器只暴露一个通用执行器,因此实际只有:
mcp__intellij__execute_tool # 参数 command: "<工具名> --<参数> <值>"
前置条件(重要)
idea stdioMcpServer 不是独立的 MCP 服务器,而是一个 stdio ↔ SSE 代理:它把 stdin/stdout 上的 MCP 消息转发给已经在运行的那个 IDE 内部的 SSE 端口。
因此:
- IDE 必须先打开目标项目(本包默认未预置
projectPath,请按下方设置页填你自己的项目路径)。 - 端口必须等于 IDE 实际监听的端口。查法:
IDE 重启后端口可能变化 —— 变了就在插件页改(见下)。ss -lntp | grep 64342 - 端口不对时的唯一症状是:
它永远不会去拉起一个新 IDE —— 见到这个报错,先去看 IDE 是否开着、端口是否变了。io.ktor.client.plugins.sse.SSEClientException: Connection refused
设置页面(GUI)
本包是 Host + Client 双语 bundle:Host 半边持有带 volatile 字段的 Config,Client 半边把这些字段渲染成表单,并注册进两个官方槽位:
| 入口 | 槽位 | 路径 |
|---|---|---|
设置 → IntelliJ MCP(推荐) |
settings.section |
打开设置面板,左栏最后一个导航项就是 IntelliJ MCP(排序 25,在内置插件之后) |
| 侧边栏「插件」面板 | plugins.row.config |
侧栏「插件」→ 点开 dsh-intellij-mcp 包卡片 → 在 intellij-mcp 这一行点「配置」按钮(aria-label="配置 dsh-intellij-mcp") |
两个入口共用同一份表单与同一份暂存状态,都是五个字段。
注意:不是设置里的「内置插件」页。那一页是
dsh-client-ui-settings-plugin-inventory(只读清单),既不派发settings.section也不派发plugins.row.config,因此不会出现配置控件。
只能在 127.0.0.1 页面上编辑(DSH 的硬约束)
dsh-client-ui-settings 按页面来源选择设置的持久化后端:
const persistence = ctx.remote.$host.isLoopback ? "host" : "memory";
而 isLoopbackHostname 只认 localhost、[::1] 和字面 127.x.x.x——任何 DNS 名都不算。所以经反向代理/内网域名访问时(例如 dsh.ceastar.cn,实际由 dsh-pocket 代理到 3080),设置会被降级为进程内 memory 模式:describe 镜像永不加载,表单因此只读。
这是 DSH 的故意安全设计(源码注释 non-loopback pages may remain process-local),不是本插件的缺陷;本插件做的事是无条件注册入口,保证远端页面上导航项照样出现,只是表单里显示一段说明而不是字段。
要在远端机器上改这些设置,请走 loopback:
- 直接在该机器上打开
dsh web打印的http://127.0.0.1:3080/?token=...;或 - 用 SSH 端口转发:
ssh -N -L 3080:127.0.0.1:3080 <机器>,然后本地浏览器访问http://127.0.0.1:3080/?token=...。
| 字段 | 默认值 | 说明 |
|---|---|---|
IDE 启动器 command |
idea |
以 idea stdioMcpServer 方式运行;idea 不在 PATH 时填绝对路径(JetBrains Toolbox 用户通常是 ~/.local/share/JetBrains/Toolbox/scripts/idea) |
项目路径 projectPath |
空 | 作为 IJ_MCP_SERVER_PROJECT_PATH 传入;留空则由 IDE 按调用自行解析工程 |
MCP 端口 port |
64342 |
作为 IJ_MCP_SERVER_PORT 传入,须与 IDE 一致 |
工具调用超时 toolCallTimeoutMs |
120000 |
build_project / run_inspection_kts / 全项目检索常超 60s 默认值 |
重连次数 maxReconnectAttempts |
120 |
退避 500ms 起翻倍至 30s,约覆盖一小时;期间打开 IDE 即可自动连上 |
serverName |
intellij |
非 volatile:工具名前缀来源,须在活跃实例间唯一 |
failOnStartupError |
false |
非 volatile:IDE 没开时 DSH 仍能干净启动,由 reconnect 重试 |
保存即生效:volatile 字段的写入不会重挂插件,Host 半边的 loader/volatile-update 监听会主动断开并按新值重建 MCP 子连接,所以在表单里改完端口/项目路径点保存后,不必重启 Host。
写入落点:当前 profile 的 cordis.patch.yml(作为该行的覆盖层);把字段改回默认值保存即等同于删除该覆盖。
测试连接
表单底部有一个 测试连接 按钮,用来回答「我这样配到底通不通」。它不走 DSH 的设置通道,而是 POST 到 Host 半边注册的一条 exact 路由:
POST /plugins/dsh-intellij-mcp/test
Host 会按表单里的当前值(读的是 volatile ref 的活值,不是磁盘上的旧值)真的拉起一次 idea stdioMcpServer,用 NDJSON 帧完成 MCP 握手(initialize → notifications/initialized → tools/list),然后 SIGKILL 掉这个探测进程。响应是一份 JSON,前端把它渲染成几行结果:
| 结果 | 含义 |
|---|---|
| 绿字「连接成功,IDE 的 MCP 服务器已完成握手。」 | 握手成功,同时列出 protocol 2024-11-05、IntelliJ IDEA MCP Server 2026.2.1 和 Tools: execute_tool |
| 红字「未连接」+ 具体原因 | 见下表 |
| 额外一行「智能体侧的对应工具也已注册。」 | Host 侧 ctx.tools.get("mcp__intellij__execute_tool") 命中,说明 DSH 这一侧也把工具挂上了 |
| 额外一行「本插件自己的工具此刻未注册…在本页保存一次即可重连。」 | 探测成功,但插件当前的连接没挂上工具(改过配置还没保存、或重连已放弃);保存一次即可 |
失败原因(ok: false 的 reason)逐条对应:
reason |
含义与常见成因 |
|---|---|
timeout |
20 秒内没完成握手:IDE 没开、端口不是 IDE 内置 MCP 的端口(查法 ss -lntp | grep 64342)、或启动器路径不对 |
spawn-failed |
启动器根本起不来(路径不存在 / 没有执行权限) |
exited |
启动器在握手完成前退出——典型的是错端口,stderr 会带 SSEClientException: Connection refused |
handshake-failed |
连上了但 initialize 被拒 |
tools-list-failed |
initialize 成功但 tools/list 没答 |
probe-error |
探测本身抛异常 |
结果里还会带回 stderr 尾部(最多 600 字符),直接显示在结果区,便于对照上面那张表。
这条路由和 DSH 自己的
/api不同:它不受客户端isLoopback限制,但远端页面上的表单本身是只读的(见上一节),所以「测试连接」按钮只在 loopback 页面上真正可用。远端页面能看到入口与说明,但不能测试。
为什么不直接给
@deepseek-ai/dsh-mcp-client那一行做设置页:它的Config是普通union、没有任何 volatile 字段,因此永远不会出现在设置表单里。本包因此自己持有 volatileConfig,并在内部用ctx.plugin(mcpClient, 解析后的配置)复用它全部连接与工具桥接逻辑。
安装 / 卸载
本包已发布到 npm:https://www.npmjs.com/package/dsh-intellij-mcp
直接从 registry 安装:
plugin_manager action=install_bundle target=dsh-intellij-mcp
装完后 DSH 会在当前 profile 的 cordis.patch.yml 自动插入 intellij-mcp 行,无需手工配置。国内镜像(npmmirror)已同步该版本,pnpm 默认走镜像即可正常拉到。
首次使用请在设置页填入你自己的 IDE 启动器路径与项目路径 —— 包内默认值是可移植的裸 idea 与空项目路径,不包含任何机器专属路径:
idea不在 PATH 时,填绝对路径(JetBrains Toolbox 用户通常为~/.local/share/JetBrains/Toolbox/scripts/idea)。- 项目路径留空亦可,但 IDE 需已打开目标项目。
必须检查返回值的 application 与 warnings:
application |
含义 |
|---|---|
applied |
已生效(新行已在 Loader 中) |
restart-required |
尚未生效:见下方绕法 |
overridden |
被更高优先级层覆盖 |
restart-required 的绕法:该状态表示包已在 profile dependencies 里,installBundle 直接短路返回、不调用 reload(),于是新补丁没被推进 Loader(表现为 Config.listConfigs(entry="include:intellij-mcp") 仍返回旧的 mcp-client 行,或直接报 unknown entry id)。用 set_bundle 往返一次即可强制 reload:
plugin_manager action=set_bundle target=dsh-intellij-mcp enabled=false # changed:true, application:applied
plugin_manager action=set_bundle target=dsh-intellij-mcp enabled=true # changed:true, application:applied
卸载用 action=remove_bundle。
替换 JS 实现(lib/*.js)需要重启 Host 才会加载新模块代;只改 cordis.patch.yml 可以热加载。
本地开发(link: 安装)
在包目录里改代码、想立刻生效时才需要这条路径:
plugin_manager action=install_bundle target=dsh-intellij-mcp@link:<你的包目录绝对路径>
裸路径形式(target=<你的包目录绝对路径>)会报 ManagementFailure: ambiguous-install —— pnpm 若判定依赖已是最新就不产生变更,而裸路径又匹配不上任何包名,于是 installed.length !== 1 直接抛错。
link: 安装下 Node 按 realpath 解析,裸包名会在包目录下失去解析基准,因此本地开发时包内需要自带 node_modules/@deepseek-ai/,用 symlink 指向 dsh 安装目录里的同款包:
node_modules/@deepseek-ai/dsh-mcp-client -> <dsh>/node_modules/@deepseek-ai/dsh-mcp-client
node_modules/@deepseek-ai/schemastery -> <dsh>/node_modules/@deepseek-ai/schemastery
(这两个 symlink 已在 .gitignore/files 之外,不会进 npm 包;从 registry 正常安装时 peer 依赖由 npm 自动落地,无需它们。)重新安装或换 dsh 版本后若报 Cannot find package '@deepseek-ai/...',按上面的形状重建这两个链接即可。
用法示例
execute_tool 的参数语法是 <工具名> --<参数名> <值>(对象/数组参数传 JSON 字符串):
get_project_modules
read_file --file_path <你的项目路径>/server/pom.xml
search_text --q TODO --directoryPath <你的项目路径>/server
get_file_problems --file_path <abs-path>
git_status
写错语法时的报错原文很明确:
Invalid argument format: 'file_path=...'. Expected '--paramName value' format.
For object/array parameters pass a JSON value, e.g. --findings '[{...}]'.
想拿到完整工具清单:随便传一个不存在的工具名,报错里会把全部可用工具名列出来(约 50 个:apply_patch、build_project、rename_refactoring、search_symbol、get_symbol_info、run_inspection_kts、execute_terminal_command、xdebug_*、数据库类 execute_sql_query / list_schema_objects 等)。
让智能体知道这些能力
IDEA 的 MCP 服务器不在 initialize 里宣告 instructions(实测响应只有 protocolVersion / capabilities / serverInfo),所以 dsh-mcp-client 自动发布的 mcp:intellij 提示词段对本服务器恒为空——光连上,模型并不知道 execute_tool 背后有哪些工具、参数怎么写。
本插件因此在 Host 半边自己发布一个提示词段:
ctx.inject(["systemPrompt"], (child) => {
child.effect(() => child.systemPrompt.section({
name: "intellij-mcp:tools",
order: child.systemPrompt.getSectionOrder("MCP_SERVERS"),
interpolate: false,
text: TOOL_GUIDE,
}), "intellij-mcp: tool guide");
});
段名固定为 intellij-mcp:tools(刻意与 mcp:<serverName> 区分开),排序与其他 MCP server 段并列,text 就是 lib/index.js 里的 TOOL_GUIDE 常量。它包含:
唯一工具与调用语法:
mcp__intellij__execute_tool,参数command形如<工具名> --<参数名> <值>(对象/数组传 JSON 字符串),并点明写错时的报错原文;代理语义:
idea stdioMcpServer只是连接「已经在运行的 IDE」的 stdio↔SSE 代理,拒绝连接 = IDE 没开或端口不对;一条明确的优先级策略:
凡是代码类任务(找符号、读文件、查引用、重构、重命名、格式化、检查、构建)先试 IDE——它能跨整个工程解析真实语义,这是文本搜索做不到的;只有当 IDE 调用失败(IDE 未开、工程未打开、工具报错)或查询本质上是文本(日志、配置值、生成产物、工程外文件)时才退回
bash/rg。按用途分组的工具清单:读取与检索、编辑与重构、构建/检查/运行、调试器(
xdebug_*)、数据库。
改完 lib/index.js 记得重启 Host(systemctl --user restart dsh-web.service)——Host 半边是 ESM,热重载不会重新 import。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
SSEClientException: Connection refused |
IDE 没开,或端口不是 IDE 当前监听的端口 | 打开 IDE / 用 ss -lntp 核对端口并在设置页改 |
工具调用返回 File ... doesn't exist or can't be opened |
路径错(execute_tool 不走 shell,相对路径按 IDE 项目根解析,建议一律传绝对路径) |
传绝对路径 |
| 工具不见了 | 超过 maxReconnectAttempts 后放弃,工具被注销 |
在设置页点保存(重建连接)或重启 Host |
| 模型不知道 IDEA 有哪些工具、也不优先用 IDE | 你用的是 1.0.4 及以前(提示词段 intellij-mcp:tools 是 1.0.5 才发布的;IDEA 自己的 initialize 不宣告 instructions,所以没有这个段模型就是盲的) |
升级到 1.0.5+ 后重启 Host |
设置里没有 IntelliJ MCP 导航项 |
① 你用的是旧版(1.0.1 及以前没有 settings.section 注册);② 或 Host 没有 serve 命名空间 intellij-mcp(行未加载 / 无 volatile 字段) |
① 升级到 1.0.3+ 后重启 Host;② 确认 bundle 已 applied 且该行处于启用状态(Config.listConfigs(entry="include:intellij-mcp") 应返回 name: dsh-intellij-mcp) |
| 表单底部没有 测试连接 按钮 | 你用的是 1.0.3 及以前(该按钮 1.0.4 才有) | 升级到 1.0.4+ 后重启 Host(Host 半边是 ESM,热重载不会重新 import) |
| 导航项能看见,但点进去只有一段「只能在运行 DSH 的那台机器上编辑…」 | 你是经非 loopback 地址访问(任何 DNS 名都算,例如经反代/pocket 代理的 dsh.ceastar.cn),DSH 把设置降级成进程内 memory 模式 |
改用 http://127.0.0.1:3080/?token=...,或 SSH 端口转发到本机后再访问。1.0.2 及以前在同样场景下连导航项都不出现 |
| 设置 →「内置插件」页里找不到配置控件 | 那一页是只读清单,既不派发 settings.section 也不派发 plugins.row.config |
改用设置左栏的 IntelliJ MCP,或侧边栏「插件」面板里该行的「配置」按钮 |
| 「测试连接」报红字「未连接」并附原因 | 探测真的去连了 IDE(见上一节的原因表),说明当前配置在这一次探测里不通 | 按原因处理:开 IDE / 核对端口 / 修正启动器路径;结果区里的 stderr 尾部通常直接给出根因 |
「测试连接」返回 HTTP 404 或 405 |
Host 半边的 exact 路由没注册——改了 lib/index.js 后只做了热重载而没有真实重启 |
systemctl --user restart dsh-web.service。lib/*.js 是 ESM,Host 侧有模块实例缓存,set_bundle 往返只重载 cordis.patch.yml,不会重新 import 代码 |
日志出现 failed generation could not confirm transport closure |
上一代连接未确认关闭,为防重复拉起 IDE 进程而停止重连 | 重启 Host 后重试 |
目录结构
dsh-intellij-mcp/
├── package.json # bundle 清单:dsh.bundle.patch + dsh.client + exports["./client"]
├── cordis.patch.yml # 插入本包自己的 Host 行 intellij-mcp(name: dsh-intellij-mcp)
├── lib/index.js # Host 半边:volatile Config + 管理 mcp-client 子连接 + 测试连接路由 + 工具能力提示词段
├── lib/client.js # Client 半边:把 volatile 字段渲染成设置页表单 + 测试连接按钮
├── locale/{en,zh}.json # 面板显示用标题与描述
├── LICENSE
└── README.md
许可
MIT