Skip to main content
Glama
README.md
# normify-mcp-adapter

把 [yan-mc/dsh-normify](https://github.com/yan-mc/dsh-normify)(MIT,v0.5.4)经**零改源码薄转接层**暴露为 MCP stdio 服务器:31 个 `normify_*` 工具可被任意 MCP 宿主(Hermes 等)直接调用。

上游是 DSH 插件,但引擎层(`upstream/lib/`)只依赖 node ≥ 18 + `yaml`,不碰 dsh 运行时——转接层只吃它的注册面 `registerTools(ctx, env)`(假 ctx 收集),上游加/减工具启动时自动跟随。

## 作为 Hermes 插件安装(Agent Plugins v1 便携包)

```bash
hermes plugins install luolianst/hermes-normify-plugin
hermes plugins enable hermes-normify-plugin   # 安装时默认 disabled,按需启用
```

- 要求:Node ≥ 18(`node` 在 PATH);无需其它依赖(`yaml` 已 vendor)
- 安装后 MCP server `normify` 注册 31 个工具,Hermes 侧工具名 = `mcp_normify_<tool>`(桥开了 `NORMIFY_STRIP_PREFIX`,不出现双前缀)
- 结构数据根默认落在**插件数据目录**;要用工作区目录,改注册后 MCP server 的 env `NORMIFY_ROOT_DIR`(或 `hermes mcp` 配置)
- 随包技能 `normify-arch` 讲工具流 + 造数硬约束 + 4 个上游缺陷的规避

## 目录

```
plugin.json         # Agent Plugins v1 便携包清单
mcp.json            # MCP server 声明(stdio + ${PLUGIN_ROOT}/${PLUGIN_DATA} 占位)
skills/             # 随包技能 normify-arch(Agent Skills 形制)
adapter/
  mcp_normify.mjs   # MCP stdio 服务器(本仓库唯一"新代码")
  run_tests.py      # 实测驱动:冒烟链路 + 守卫/复现回归
upstream/           # v0.5.4 发布 tgz 解包(编译产物,零改动)
node_modules/yaml/  # 唯一运行依赖(ISC,vendor 便于开箱即用)
setup_sandbox.py    # 重新拉取/解包上游 release 的脚本
NOTICE.md           # 第三方组件清单(保留版权声明)
dsh-external-dsh-normify-0.5.4.tgz   # 发布包原件
  SHA-256: 4898867fedc0ad0968cbef30765b7bcce354a53d5656debc9988cf031164d35b
```

## 用法

```bash
# 直接起 MCP stdio 服务器
node adapter/mcp_normify.mjs

# 注册进 Hermes(可选)
hermes mcp add normify -- node D:/workSoft/normify-adapter/adapter/mcp_normify.mjs
```

环境变量:

| 变量 | 含义 | 默认 |
|---|---|---|
| `NORMIFY_ROOT_DIR` | 结构数据搜索根(`normify-<slug>/` 建在它下面) | cwd |
| `NORMIFY_REQUIRE_BILINGUAL` | 双语严格模式;`false` = 宽松 | `true` |
| `NORMIFY_STRIP_PREFIX` | `1` = MCP 注册名去掉 `normify_` 前缀(插件包默认开) | 关 |

协议:MCP stdio(newline-delimited JSON-RPC 2.0),`initialize` / `tools/list` / `tools/call` / `ping`。
输入 schema 直取工具自带 JSON Schema;结果走工具自带 `output.render`;返回体 `ok:false` 时置 `isError`。

## 转接层守卫(v0.2.0,针对上游 4 个未修 issue 的输入侧防御)

| 上游缺陷 | 守卫动作 |
|---|---|
| #1 render.out 穿越 | `out` 只收纯文件名(含 `..`/分隔符/盘符一律拒) |
| #1 sync.diff git 注入 | `diff` 拒绝 `-` 开头(不用裸 `--`——上游 issue 实测会静默失效) |
| #1 dir 越界 | `dir`/`project` 路径形态时断言位于 `NORMIFY_ROOT_DIR` 内且目录名 `normify-` 开头 |
| #3 id 末段 `index` 静默覆盖 | 写类调用前置拒绝(读类放行,兼容遗留数据) |
| #2 receipt 自哈希 | `normify_build` 后处理:剔除 `receipt.json` 自引用项并重写产物(⚠️ 此举"碰"了构建产物,属有意的局部修正) |
| #4 文案长度地雷 | 写前同罪预检 name≤60 / 模块 desc≤500 / API desc≤200 / label≤30,超限统一 `adapter/guard` 拒绝 |

守卫拒绝统一返回 `{ok:false, error:{code:'adapter/guard', ...}}`。根治已打包上游 PR(见下)。

## 实测(`python adapter/run_tests.py`)

- 2026-09-22 v0.1.0(无守卫):31/31 工具通、全链路绿,4 个上游未修 issue **全部复现**(v0.5.4 / HEAD `ed404e5`,与 issue 原文逐条一致)
- 2026-09-22 v0.2.0(守卫版):8/8 PASS——冒烟链路依旧绿,6 项守卫断言全部拦截/生效
- 补充发现:#4 的宽松降级**只在 L2 validate 层成立**,写路径 L1 不吃 `requireBilingual`,超限一律 error + 整批回滚(已在上游 PR 根治)

## 跟随上游更新

1. `python setup_sandbox.py`(重新拉最新 release tgz 并解包覆盖 `upstream/`)——或手工换 release 文件夹
2. `node adapter/mcp_normify.mjs` 冒烟:`tools/list` 数量对 + `python adapter/run_tests.py` 看结论
3. 转接层只在 `registerTools` 签名大改时小修

## 许可

上游 MIT(© yan-mc),见 `upstream/LICENSE`;转接层代码同为 MIT。转换不修上游缺陷——已知 4 项见上,真咬到就打本地补丁(MIT 允许)。