Skip to main content
Glama
Pegasus02

Fudan LIMS MCP Server

by Pegasus02
README.md
# Fudan LIMS MCP Server

支持复旦微纳加工平台和高分子科学系平台的标准 Model Context Protocol (MCP) 服务端,全程使用网络接口,无需浏览器自动化。

- **协议标准**:MCP (stdio 模式,通过 JSON-RPC 2.0 通信)
- **依赖要求**:Python 3.8+(无额外 pip 依赖);高分子平台登录需要 Node.js 18+;实时队列预约另需项目内 npm 依赖
- **目标平台**:https://nanofab.fudan.edu.cn/lims/ 和 http://www.polymerpicc.fudan.edu.cn/lims/

---

## 目录结构

- `mcp_server.py`:MCP 服务端主入口,负责处理 stdio 协议、声明工具与分发请求,支持底层引擎热重载。
- `fudan_lims.py`:底层 LIMS HTTP 接口引擎,负责会话管理、多页数据抓取、设备检索、排期解析与预约提交。
- `socket_bridge.cjs`:使用标准 Socket.IO 客户端处理 Engine.IO 3 Polling、连接、心跳和业务回执;不运行浏览器。
- `package.json` / `package-lock.json`:固定 Node 依赖版本;首次部署在本目录执行 `npm ci --ignore-scripts`。
- `lims_cookie.txt`:本地会话凭证缓存文件(由 `lims_login` 或 `lims_set_cookie` 自动生成)。
- `test_mcp_rie.py`:MCP 服务调用自测脚本。

---

## 客户端 MCP 配置

将以下配置添加到任何 MCP 客户端(如 Claude Desktop、Cursor、Codex、DSH Desktop 等)的配置文件中:

```json
{
  "mcpServers": {
    "fudan_lims": {
      "command": "python3",
      "args": ["/path/to/lims_mcp/mcp_server.py"]
    }
  }
}
```

配置后客户端会在收到指令时**自动在后台拉起该服务**,完全无需手动启动。

## 多站点选择

所有业务工具都接受 `site` 参数:`nanofab` 为微纳平台,`polymer` 为高分子平台。省略时默认使用微纳平台,兼容原有调用。`lims_list_sites` 返回站点及实测状态。两个站点的设备 ID、预约 ID 和用户 ID 相互独立,先在目标站点查询再操作。

例如 MCP 工具调用参数:

```json
{"name":"lims_search_equipments","arguments":{"site":"polymer","keyword":"激光直写"}}
```

```json
{"name":"lims_get_my_reservations","arguments":{"site":"polymer"}}
```

也可在 MCP 服务配置中加入 `"env": {"LIMS_SITE": "polymer"}`,将默认站点设为高分子平台。Python 调用使用 `FudanLIMSClient(site="polymer")`。更新后重启 MCP 服务以刷新工具列表。

高分子平台使用 UNO 账号登录:获取公钥、RSA 加密密码、调用登录接口,再完成 OAuth 回调建立 LIMS 会话。账号密码不持久化;微纳 Cookie 保存于 `lims_cookie.txt`,高分子 Cookie 保存于 `lims_polymer_cookie.txt`,互不复用。登录调用同样需要指定 `site="polymer"`。

高分子站点在当前网络环境下使用直连(不读取环境代理),同一 MCP 进程内各次调用共享至少 0.4 秒请求间隔,降低 429 限流风险。搜索使用站点原生关键词表单,避免每次遍历所有仪器。多个独立进程之间不共享限流状态。

2026-09-15,高分子平台已实测登录、仪器搜索、个人预约读取、排期查询和创建预约的 `dry_run` 表单预检;尚未实测新增、修改、取消的真实写入。没有编辑入口的个人预约返回 `component_id: null`、`can_edit: false`、`can_cancel: false`,不推测 ID 或绕过权限。详细记录见 [POLYMER_ADAPTATION_STATUS.md](POLYMER_ADAPTATION_STATUS.md)。

---

## 提供的核心 MCP 工具

1. `lims_check_status`:检查当前系统认证状态与会话有效性。
2. `lims_login`:使用账号和密码登录,自动持久化会话至所选站点的 Cookie 文件。
3. `lims_set_cookie`:手动传入所选站点的会话 Cookie 值实现免密认证。
4. `lims_search_equipments`:按关键词检索仪器列表,返回设备内部 ID(如 DE400、RIE、EBL 等),支持多页扫描与模糊匹配。
5. `lims_get_equipment_schedule`:查询指定设备在指定日期范围内的机时排期与已占时段、预约人、状态及备注。
6. `lims_create_reservation`:预约指定仪器的机时,支持事前冲突检查、动态表单解析、Engine.IO 3 / Socket.IO v2 实时排队、服务端回执处理与最终排期核验。
7. `lims_get_my_reservations`:读取当前登录人的个人预约页,返回设备名称、设备 ID、时段及 `component_id`,支持设备与日期筛选。范围为该页当前展示的预约,非完整历史记录。
8. `lims_update_reservation`:按 `component_id` 修改时间或备注。省略的字段保持原值;`notes: ""` 表示清空备注;支持 `dry_run: true`。冲突检查排除自身,保存后读回核验。
9. `lims_cancel_reservation`:按 `component_id` 取消预约,支持 `dry_run: true`。正式调用会完成平台删除确认,并核验个人列表及原时段日历;结果不明不自动重试。

修改、取消使用查询返回的 **日历组件 ID**,不要混用其他业务记录 ID。只操作当前用户个人页中有编辑入口的预约;取消权限在编辑表单中检查,因此列表中的 `can_cancel: null` 表示尚未检查。

例如预检修改备注:`lims_update_reservation(component_id="70890", notes="新备注", dry_run=true)`;预检取消:`lims_cancel_reservation(component_id="70890", dry_run=true)`。正式操作需明确要求修改或取消,再省略 dry_run 或设为 false。

这三项能力已通过 MCP 线上查询和预检,新增写入逻辑用本地测试覆盖。随后按用户明确要求,已成功取消预约 70890,完成取消功能的真实写入及读回验证;修改功能尚未执行真实保存。暂不支持含重复字段名或多选下拉框的复杂编辑表单,遇到此类表单会停止。

HTTP 请求统一维护 Cookie,正确接收重定向和错误响应中的更新,并与 Socket.IO 请求共享会话。凭证通过子进程标准输入传递,不放入命令行参数。

排期列表查询会影响服务器保存的日历视图。预约前显式加载周视图、取得当次 `cal_week_rel` 和表单上下文,避免列表视图状态影响预约。Socket.IO 禁用自动重连与重发,超时必须先核对排期。

`lims_create_reservation` 支持 `dry_run: true` 预检:会完成认证、排期冲突检查和预约表单加载,但不会提交真实预约。队列正式受理后,要核对排期中的起止时间、预约人 ID,以及回执提供的组件 ID,才报告成功。

2026-09-15 已通过 MCP 成功预约 RIE-10NR 15:30–16:00,预约组件 ID 为 `70890`。完整证据、历史尝试与根因分析见 [MCP_RESERVATION_STATUS.md](MCP_RESERVATION_STATUS.md)。

## 测试

- `python3 -m unittest discover -v`:完全离线的回归测试,不访问 LIMS。
- `npm test`:连接本机真实 Socket.IO 服务,验证 Cookie 更新、心跳、回执关联、单次发送与超时不重发,不访问 LIMS。
- `python3 test_mcp_rie.py`:连接真实 LIMS 的零写入集成测试,只执行查询、冲突拦截和 `dry_run` 表单预检。
- `python3 diag.py`:默认只读诊断。加 `--prepare-form --start 'YYYY-MM-DD HH:MM:SS' --end 'YYYY-MM-DD HH:MM:SS'` 会执行两步表单 AJAX 并打印脱敏一致性检查,但不会发送 Socket 预约事件。

## 凭证安全

- `lims_cookie.txt`、`lims_*_cookie.txt` 和 `.env*` 已加入 `.gitignore`,不要提交到版本库。
- 示例脚本不再包含默认账号或密码;会优先复用现有 Cookie,否则从 `LIMS_USERNAME` / `LIMS_PASSWORD` 环境变量或交互输入获取凭证。
## 预约验证码(智能识别与人工兜底)

高分子平台在提交预约时存在验证码机制:
- **全自动识别(推荐)**:默认启用 `auto_solve_captcha: true`,结合 SVG 矢量几何滤除干扰线与 `ddddocr` 本地高精度识别,实现无人值守全自动排队与确认。
- **人工兜底**:若关闭自动识别或识别异常,接口会返回 `status="captcha_required"`、`challenge_id`、`captcha_svg` 和本地 `captcha_path`。向用户展示图片并输入后,调用 `lims_submit_reservation_captcha(site="polymer", challenge_id="...", captcha="...")` 即可继续提交。
- 无论自动还是人工,成功必须以 `confirmed_in_schedule=true` 为准。待提交的表单和会话仅在内存中暂存 5 分钟,过期自动销毁。

以免改变日历上下文;其他站点不受影响。提交后无论成功、失败或网络超时都不能重放,
结果不明确时应先查询排期,再决定是否重新准备。不要在其他客户端刷新验证码或改变登录。
验证码图片是系统临时目录中的本机文件,不包含登录凭证;远程 MCP 客户端可使用返回的 SVG。
`dry_run` 只检查表单并返回 `captcha_required`,不获取验证码,不代表验证码校验通过。