IMA MCP Server
by shataisheng
README.md
# dsh-ima-mcp
腾讯 IMA(`ima.qq.com`)知识库 / 笔记的 MCP server,打包成 **DSH bundle**,可直接分发到其他机器安装。
安装后 DSH 会多出 13 个 `mcp__ima__*` 工具,用于检索你的 IMA 知识库并**下载文献原文**。
---
## 一、为什么需要一个独立的 bundle
之前工作区里那份 `ima.cordis.yml` **不能直接分发**,它有三处硬编码:
| 问题 | 后果 |
|---|---|
| `args` 写死了 `D:/查找复核/…/server.mjs` | 换台机器路径不存在,server 起不来 |
| `command` 写死了 `C:\Program Files\nodejs\node.exe` | 非 Windows 直接失败 |
| 只提供 PowerShell 凭证脚本 | macOS / Linux 用户没法配置 |
本 bundle 用一套机制把这三处全部消掉。
## 二、路径问题是怎么解决的(核心)
DSH 的 loader 用这种方式求值 `!!js`:
```js
new Function('ctx', 'expr', 'with (ctx) { return eval(expr) }')
```
也就是说,patch 行里能拿到 **`ctx` 的属性和全局对象**,但**拿不到 `__dirname`、`import.meta.url`、`require`**。
所以 patch 自己无法算出「我这个包被装在哪」。
解法是官方文档记载的两段式写法(`docs/user/develop/basic/publish.md` 的 "Give a surface bundle its own command line"):
1. `locate.js` —— 一个普通 Cordis 插件,用 `import.meta.url` 算出自己的目录,
通过 `ctx.provide('imaMcpPath', …)` 把路径**发布成服务**;
2. `cordis.patch.yml` 里的 MCP 行声明 `inject: [imaMcpPath]`,
loader 会等第 1 行就绪,再用 `!!js ctx.imaMcpPath.serverPath` 取值。
同时 `nodePath` 直接用 **`process.execPath`**(跑 DSH 的那个 Node),
而不是字面量 `node` —— 既保证解释器存在且版本够新,也彻底摆脱对用户 `PATH` 的依赖。
这是 Windows 上 stdio 行启动失败最常见的原因。
结果是:**装到哪个目录都能用,不需要任何按机器编辑。**
## 三、安装
### 方式 A:从本地目录安装(开发/内网分发)
```sh
dsh plugin --profile web add ./dsh-ima-mcp
```
### 方式 B:从 tarball 安装(离线 / 内网分发)
打包方:
```sh
cd dsh-ima-mcp
pnpm pack # 产出 dsh-ima-mcp-1.0.1.tgz
```
`pnpm pack` 只收 `package.json` 里 `files` 白名单列出的文件。产物共 **8 个文件**
(`package.json` 由 npm/pnpm 强制包含,不受 `files` 控制):
```
package/locate.js package/server.mjs
package/package.json package/set-credentials.ps1
package/README.md package/set-credentials.sh
package/cordis.patch.yml
```
> 这里刻意不写死字节数:tarball 一改大小就变,写死只会变成下一个过期事实。
> 以 `pnpm pack` 自己打印的为准。
使用方(把 tgz 拷过去即可,无需网络):
```sh
dsh plugin --profile web add /绝对/路径/dsh-ima-mcp-1.0.1.tgz
```
> ⚠️ **相对路径是按「你敲命令时所在的目录」解析的,不是按 tgz 所在目录。**
> `dsh plugin` 把相对路径重写为相对当前工作目录的绝对路径
> (见 `apps/cli/src/plugin.ts` 的 `anchorPathSpec`),pnpm 再在 profile 目录里执行。
> 所以 `./dsh-ima-mcp-1.0.1.tgz` 只有在 **cwd 就是这个 tgz 所在目录**时才成立;
> 从别处执行会得到 pnpm 的
> `ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND … as it does not exist`。
> 拿不准就用绝对路径 —— 从任何目录都能装上。
**验证 tarball 真的可用**(不必装进 DSH):
```sh
mkdir -p /tmp/chk && tar -xzf dsh-ima-mcp-1.0.1.tgz -C /tmp/chk
node /tmp/chk/package/server.mjs --check # 退出码 0 即凭证与接口都正常
```
> 发布新版本时记得改 `package.json` 的 `version`,否则 `dsh plugin add` 可能因版本号相同而不重新安装。
### 方式 C:从 Git 安装(适合持续更新)
仓库已初始化并完成首次提交,可以直接推到 GitHub:
```sh
cd dsh-ima-mcp
git remote add origin git@github.com:<you>/dsh-ima-mcp.git
git push -u origin main
```
使用方安装:
```sh
dsh plugin --profile web add github:<you>/dsh-ima-mcp#<commit-sha>
```
**强烈建议固定 commit sha**(而不是 `#main`):不固定的分支引用意味着日后一次 push 就能悄悄改掉实际执行的代码。
> **本包无需 `allowBuilds` 放行。** `publish.md` 警告过 git 安装的坑:git 只拉源码、
> 不跑 `build`,TypeScript 包会缺 `lib/` 而加载失败,用户还得手工往
> `pnpm-workspace.yaml` 加 `allowBuilds` 授权。本包是**纯 ESM、零依赖、零构建步骤**
> (server 直接用 `server.mjs`),因此整段绕开了。
> **注意 git 安装与 tarball 的差异**:git 会取走**所有被提交的文件**,
> 不像 tarball 那样受 `files` 白名单约束。所以 `.gitignore` 才是防止凭证被
> git 安装带走的那道防线——本包已内置,见第四节。
`.gitattributes` 已把行尾钉死:`set-credentials.sh` 强制 LF
(否则 Windows 检出会转成 CRLF,bash 报 `bad interpreter: ... bash^M`),
`set-credentials.ps1` 保持 CRLF。
### 发布到 GitHub(维护者用)
仓库已初始化(`main` 分支,含全部提交)。先在 <https://github.com/new> 建一个**空仓库**
(不要勾选 README/.gitignore/LICENSE,否则首次推送会冲突),然后在本目录运行:
```powershell
pwsh -File .\publish.ps1 -Repo <用户名>/dsh-ima-mcp
```
脚本会:修正 `package.json` 里的 `repository` 占位符 → 检查 tarball 是否过期并重新打包
→ 提交 → 配置 `origin` → 推送 `main`。
想顺带创建带 tarball 附件的 Release,加 `-CreateRelease`(需要 gh CLI):
```powershell
pwsh -File .\publish.ps1 -Repo <用户名>/dsh-ima-mcp -CreateRelease
```
未安装 gh 时脚本会打印网页创建 Release 的步骤(把 `.tgz` 拖进附件区即可)。
安装 gh:`winget install GitHub.cli` 然后 `gh auth login`。
> **推送认证**:`ssh`(默认)需要把公钥注册到
> <https://github.com/settings/keys>;`https` 需要 Personal Access Token 或
> Git Credential Manager。用 `-Transport https` 切换。
>
> **提醒**:改版本号后再发布,否则 `dsh plugin add` 可能因版本号相同而跳过重新安装。
### 验证与卸载
```sh
dsh --profile web --dump-config # 应出现 dsh-ima-mcp 与 mcp-ima 两行
dsh --profile web # 重启生效
dsh plugin --profile web remove dsh-ima-mcp
```
重启后工具以 `mcp__ima__*` 出现。若没出现,先看 DSH 错误日志。
## 四、凭证:每台机器各自配置,绝不随包分发
**包内不含任何凭证**,`package.json` 的 `files` 白名单也确保不会误打包。
每台机器必须各自生成并写入:
- Windows:`pwsh -File set-credentials.ps1`
- macOS / Linux:`bash set-credentials.sh`
两个脚本都会:备份旧文件 → 写入 `~/.config/ima/client_id` 与 `api_key`
(**裸值,不带换行**)→ 收紧权限(Windows 用 `icacls`,*nix 用 700/600)→
调 `node server.mjs --check` 自检。
凭证从 <https://ima.qq.com/agent-interface> 获取。它是**账号级、不绑定设备**的密钥——
任何拿到它的程序都能从任意位置读写你的 IMA 知识库,
所以**不要提交进 git、不要随包分发、不要放进会同步到云端的目录**。
脚本读取顺序(`server.mjs` 内):
1. 环境变量 `IMA_OPENAPI_CLIENTID` / `IMA_OPENAPI_APIKEY`
2. `~/.config/ima/client_id` / `~/.config/ima/api_key`
> DSH 的 stdio 桥在派生子进程前,会清掉所有形如 `*KEY*`/`*TOKEN*`/`*SECRET*` 的环境变量
> 以及全部 `DSH_*`。所以**推荐走第 2 条**(凭证文件),它在任何情况下都可靠。
> 这也意味着 patch 里的 `env: {}` 留空即可。
## 五、自带的自检能力
`server.mjs` 有两种无需 DSH 的运行模式:
```sh
node server.mjs --check # 凭证自检:退出码 0=正常 2=鉴权失败 3=其他
node server.mjs --replay <file.jsonl> # 走真实 JSON-RPC 分发路径的离线自检
```
## 六、可分发的前置条件
| 要求 | 说明 |
|---|---|
| Node ≥ 18 | 需要全局 `fetch`。由 `process.execPath` 提供的 DSH 自身 Node 必然满足。 |
| DSH 侧有 `@deepseek-ai/dsh-mcp-client` | 随 DSH 一起安装(实测 `~/.dsh/profiles/node_modules` 下存在);若缺失,patch 行会以明确错误失败。 |
| 网络可达 `ima.qq.com` | 仅 HTTPS 出站,无需内网放行。 |
| 各机自备凭证 | 见第四节。 |
## 七、目录内容
```
dsh-ima-mcp/
├── package.json # dsh.bundle.patch 清单 + files 白名单
├── cordis.patch.yml # 两条 row:locate 提供路径 → mcp-client 消费
├── locate.js # Cordis 插件:ctx.provide('imaMcpPath', …)
├── server.mjs # 零依赖 MCP stdio server(13 个工具)
├── set-credentials.ps1 # Windows 凭证脚本
├── set-credentials.sh # macOS / Linux 凭证脚本
├── README.md
├── LICENSE # MIT
├── publish.ps1 # 维护者用:修正元数据 → 打包 → 推送 → 建 Release
├── .gitignore # 防止凭证被 git 安装带走(关键防线)
└── .gitattributes # 行尾:.sh 强制 LF,.ps1 保持 CRLF
```
`files` 白名单决定 tarball 内容(8 个文件):`locate.js`、`server.mjs`、
`cordis.patch.yml`、`package.json`、`README.md`、`LICENSE`、两个凭证脚本。
`publish.ps1`、`.gitignore`、`.gitattributes` 只进 git 仓库,不进 tarball。
`dsh-ima-mcp-1.0.1.tgz` 是 `pnpm pack` 的产物,与源码同目录,未被 git 跟踪
(`.gitignore` 已排除 `*.tgz`)。把它作为 **GitHub Release 附件**发布,
而不是提交进仓库。
## 八、版本变更
### 1.0.1
- **`server.mjs` 的版本号改为从 `package.json` 读取。** 此前 MCP `initialize`
握手里的 `serverInfo.version` 是第二个硬编码字面量(`"1.0.0"`),与
`package.json` 各自独立 —— 只要改了一处、忘了另一处,客户端看到的版本就会
和实际安装的包不一致。现在版本只有一个来源:`package.json`。
`server.mjs` 被单独拷走、同级没有 `package.json` 时回落到 `0.0.0`,服务照常运行。
- README 安装章节改为**绝对路径**,并补上「相对路径按当前工作目录解析」的排错说明。
- 删掉 README 里写死的 tarball 字节数与错误的文件数(原文写 7 个,实际 8 个)。
> 发布新版本时只需改 `package.json` 的 `version` 一处,
> `server.mjs` 会自动跟上。
## 九、已知边界
- 网页 / 公众号文章(media_type 2/6)只能取到**原始 URL**,IMA 不提供归档正文。
- AI 会话(media_type 12)接口拒绝,返回 `220030`。
- `search_knowledge` 返回的 `highlight_content` **恒为空**,判断相关性必须取正文,成本较高。
- 写操作(建笔记 / 追加 / 导入 URL)不可逆,建议用 DSH 的 `tools/pre-execute` 加审批策略。
- IMA 存在未在公开文档中定义的错误码 `200002 skill auth failed`,疑似调用配额触发;
高强度使用后若持续出现,需向 IMA 官方申请提额。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues