dsh-intellij-mcp
Verifieddsh-intellij-mcp · v1.0.5 · MIT · Web UI
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
Install
dsh plugin add dsh-intellij-mcp Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
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