Skip to main content
K

dsh-plugin-openproject

koking0/dsh-plugin-openproject

DeepSeek Harness plugin for OpenProject — manage projects, work packages, time tracking, members, versions and attachments from chat. 36 Chinese-localized tools, zero runtime dependencies.

Install

dsh plugin --profile web add github:koking0/dsh-plugin-openproject

README

@liu_zhao_feng_alex/dsh-plugin-openproject

把 OpenProject 交给 AI Agent 的一个包,三种用法共享同一套零依赖 API 层:

  • 🧩 DSH 原生插件 —— DeepSeek Harness 的 36 个中文工具,直接注册进工具表
  • 🔌 MCP Server —— 零依赖 stdio 服务,WorkBuddy / Claude Desktop / Claude Code / Cursor 等任意 MCP 客户端可用
  • ⌨️ CLI —— 一条命令读写,脚本与 CI 友好

本包不预设任何 OpenProject 实例地址,适用于任意自建或云端部署,装好后按下方 「配置」一节填上你自己的地址与 Token 即可。

  • 📦 零运行时依赖 —— 只用 Node 内置模块,HTTP 走 node:https,可精确控制自签名证书
  • 🀄 全中文工具与输出 —— 工具名、描述、渲染结果都按 OpenProject 中文界面术语对齐
  • 🧠 服务端名字解析 —— 项目名、状态名、负责人姓名、版本名都可直接传,歧义时返回候选
  • 🛡️ 三层写入保护 —— 全局只读 → 项目白名单 → 删除强制二次确认
  • 🔍 先预览后写入 —— 创建工作包/修改工作包支持 dryRun,先看服务端校验结果
  • 🧵 多实例 —— 一套配置同时接多个 OpenProject,用实例名指定打到哪个

为什么不用官方 MCP?OpenProject 官方 MCP 是 Enterprise 付费功能,Community Edition 上 /mcp 会返回 MCP server is not available.。社区 MCP 服务器(如 openproject-ce-mcp) 虽可复用,但工具名为英文、数量上百、且需额外引入 Python 运行时。


三种接入方式

方式适合入口工具数
DSH 原生插件用 DeepSeek Harness@liu_zhao_feng_alex/dsh-plugin-openproject36 个原生工具
MCP ServerWorkBuddy、Claude、Cursor、Continue…openproject-mcp16 个 MCP 工具
CLI脚本 / CI / 不想装任何东西openproject <实例名> <命令>16 条命令

三者的实例配置是同一份(见「配置」一节);MCP 与 CLI 额外支持 op.config.json 多实例写法。


安装(DSH 原生插件)

1. 让 DSH 的 Agent 装(最省事)

对 DSH 说一句:

帮我安装插件 @liu_zhao_feng_alex/dsh-plugin-openproject

Agent 会调用 plugin_manager 的 install_bundle 完成安装与注册。

2. 图形界面

设置 → 插件(Settings → Plugins)里搜索并安装 @liu_zhao_feng_alex/dsh-plugin-openproject。

3. 命令行

# 装进你的 profile(把 web 换成你实际使用的 profile 名)
dsh plugin --profile web add @liu_zhao_feng_alex/dsh-plugin-openproject

装完重启 DSH(或重载插件),然后必须做下面的配置,否则工具会提示未配置实例地址。


配置

第一步:拿到 API Token

在 OpenProject 里:我的账号 → 访问令牌 → 新建,复制生成的 token。

第二步:告诉插件连哪个实例

插件不预设实例地址,必须由你配置。 下面两种方式任选一种即可。

方式 A:环境变量(推荐,跨机器最省事)

环境变量必填说明
OPENPROJECT_BASE_URL✅你的 OpenProject 地址,例如 https://openproject.example.com(结尾不要带 /)
OPENPROJECT_API_TOKEN✅上一步拿到的 API Token
OPENPROJECT_TOKEN_FILE—改用文件存放 Token 时指定路径(覆盖默认位置)
OPENPROJECT_REJECT_UNAUTHORIZED—true 时校验证书;自签名证书的实例用 false(默认即 false)

各系统写法:

# Windows PowerShell(仅当前会话)
$env:OPENPROJECT_BASE_URL = 'https://openproject.example.com'
$env:OPENPROJECT_API_TOKEN = 'opapi-xxxxxxxx'

# Windows 永久生效(新开终端后有效)
setx OPENPROJECT_BASE_URL "https://openproject.example.com"
setx OPENPROJECT_API_TOKEN "opapi-xxxxxxxx"
# macOS / Linux
export OPENPROJECT_BASE_URL='https://openproject.example.com'
export OPENPROJECT_API_TOKEN='opapi-xxxxxxxx'

也可以用 .env 文件(DSH 会读取 $DSH_HOME/.env 与项目根目录的 .env):

OPENPROJECT_BASE_URL=https://openproject.example.com
OPENPROJECT_API_TOKEN=opapi-xxxxxxxx

环境变量只在你没有在配置里显式写该字段时生效,因此不会被旧配置意外覆盖。

方式 B:profile 配置(不想用环境变量时)

在你自己 profile 的 cordis.patch.yml 里追加:

- id: openproject
  name: "@liu_zhao_feng_alex/dsh-plugin-openproject"
  config:
    baseUrl: 'https://openproject.example.com'
    # 自签名证书实例保持 false;有受信任证书时改为 true
    rejectUnauthorized: false
    # Token 也可以直接读文件(每次调用重新读取,便于轮换)
    tokenFile: '/home/you/.dsh/openproject.token'
    # 凭据服务中的引用名(默认值)
    credentialRef: 'OPENPROJECT_API_TOKEN'
    readOnly: false
    # 只想允许在特定项目里写入时,填项目标识或 ID
    writeProjects: []

⚠️ profile 补丁层会整体替换 config(不是深合并),所以要把需要保留的字段一并写全。

方式 C:DSH 凭据服务

在 DSH 的凭据设置里保存一个名为 OPENPROJECT_API_TOKEN 的密钥即可, 插件会通过 credentialRef 自动读取,无需落明文文件。

方式 D:同时接入多个实例(多实例)

同一个 profile 可以同时接入多个 OpenProject(例如「实验室」和「团队 B」): 在 profile 的 cordis.patch.yml 里用不同的 id 插入多条本插件,每条配一个 toolPrefix 与 label。

# 实例一:覆盖 bundle 层已经插入的那条(id 固定为 openproject)
- id: openproject
  name: "@liu_zhao_feng_alex/dsh-plugin-openproject"
  config:
    label: '实验室'
    toolPrefix: 'lab_'
    baseUrl: 'https://openproject.lab.example.com'
    tokenFile: '/home/you/.dsh/openproject.lab.token'

# 实例二:新增一条 insert,id 必须与上面不同
- insert:
    - id: openproject-corp
      name: "@liu_zhao_feng_alex/dsh-plugin-openproject"
      config:
        label: '团队 B'
        toolPrefix: 'corp_'
        baseUrl: 'https://openproject.example.com'
        tokenFile: '/home/you/.dsh/openproject.corp.token'
  • toolPrefix 决定工具名前缀:实验室的 openproject_list_projects 会注册成 lab_list_projects,团队 B 的是 corp_list_projects,因此同一个对话里两边的工具 可以共存。多个实例必须用不同的前缀;填 lab 会自动补成 lab_。前缀只允许字母、 数字与下划线且以字母开头,形状非法时插件会直接报错而不是静默忽略。
  • label 是实例的人类可读名称,会出现在系统提示词与每个工具描述的前缀里 (如 [实验室] 列出 OpenProject 中当前账号可见的项目),让模型按你提到的组织选对实例。 实例地址若是 IP 或域名,模型光看地址认不出是哪个组织,建议配合 label 使用。
  • 每个实例的 readOnly、writeProjects、requireConfirmForWrites、Token 来源( token / tokenFile / credentialRef)都独立生效。
  • 不配 toolPrefix 时行为与单实例完全一致:工具名仍是 openproject_*,提示词区块也不变。

如果希望实例之间隔离得更彻底(例如一个只读、一个可写,且不想让模型同时看到两边), 也可以给每个实例各建一个 DSH profile:dsh --from-default-profile <模板>。

Token 的读取顺序

config.token → config.tokenFile → DSH 凭据服务 → 环境变量。 每次调用都会重新读取,所以轮换 Token 后无需重启 DSH。

多实例场景下建议每个实例用各自的 tokenFile(或各自的 credentialRef): config.token 与 OPENPROJECT_API_TOKEN 这类单值来源无法区分实例。

配置项一览

字段默认值说明
baseUrl(空,必填)OpenProject 实例地址;也可由 OPENPROJECT_BASE_URL 提供
token—直接给出 Token(不建议,容易进版本库)
tokenFile$DSH_HOME/openproject.tokenToken 文件路径,按本机主目录自动推导
credentialRefOPENPROJECT_API_TOKENDSH 凭据服务中的引用名
label''实例名称(如「实验室」),用于提示词与工具描述前缀,便于区分多实例
toolPrefix''工具名前缀(如 lab_),多实例时用于区分各实例的工具;自动补结尾下划线
rejectUnauthorizedfalse是否校验 TLS 证书
requestTimeoutMs30000单次请求超时
defaultPageSize / maxPageSize25 / 100分页大小
hoursPerDay8解析 ISO 8601 时长时 1 天 折算的小时数
readOnlyfalse全局只读开关,开启后拒绝一切写入
requireConfirmForWritesfalse为 true 时所有写入都要求 confirm: true
suppressNotificationsfalse为 true 时工作包写入默认不发通知邮件
writeProjects[]写入白名单(项目 ID/标识/名称),为空表示不限制

第三步:验证

对 DSH 说「检查一下 OpenProject 连接」,它会调用 openproject_whoami (多实例时是 <前缀>openproject_whoami,例如 lab_openproject_whoami), 正常会返回你的账号名与实例地址:

已连接 OpenProject:https://openproject.example.com
当前账号:#12 张三(zhangsan) · 管理员
写入状态:允许写入

配置了 label / toolPrefix 时还会带上它们,多实例场景下一眼就能确认打到了哪个实例:

已连接 OpenProject(实验室):https://openproject.lab.example.com
当前账号:#12 张三(zhangsan) · 管理员
写入状态:允许写入
工具名前缀:lab_(本实例工具名形如 lab_openproject_list_projects)

MCP Server(WorkBuddy / Claude / Cursor…)

本包自带一个零依赖 stdio MCP 服务:不用 DSH,任何支持 MCP 的客户端都能接上。

第一步:写实例配置

MCP 与 CLI 共用一份多实例配置,按以下顺序查找(先命中先用):

  1. 环境变量 OPENPROJECT_CONFIG 指向的文件
  2. ~/.openproject/op.config.json
  3. 当前工作目录下的 op.config.json

把仓库里的 op.config.example.json 复制到上面任一位置,填好 instances 与 default 即可。 一个实例都不想建文件时,也可以只用两个环境变量(等同单实例):

环境变量说明
OPENPROJECT_BASE_URL实例地址,例如 https://openproject.example.com
OPENPROJECT_API_TOKENAPI Token
OPENPROJECT_REJECT_UNAUTHORIZED只有显式设成 true 才校验证书;自签名实例保持默认(不校验)

第二步:加进 MCP 客户端

通用写法(用 npx 拉起,无需全局安装):

{
  "mcpServers": {
    "openproject": {
      "command": "npx",
      "args": ["-y", "-p", "@liu_zhao_feng_alex/dsh-plugin-openproject", "openproject-mcp"],
      "env": { "OPENPROJECT_CONFIG": "/绝对路径/op.config.json" }
    }
  }
}

也可以先 npm i -g @liu_zhao_feng_alex/dsh-plugin-openproject,然后把 command 写成 openproject-mcp。

WorkBuddy

WorkBuddy 实际读取的是 ~/.workbuddy/connectors/default/mcp.json(往 ~/.workbuddy/mcp.json 里写是不生效的),在 mcpServers 下加一项:

{
  "mcpServers": {
    "openproject": {
      "command": "node",
      "args": [
        "--no-warnings",
        "C:/Users/<你>/AppData/Roaming/npm/node_modules/@liu_zhao_feng_alex/dsh-plugin-openproject/mcp/server.js"
      ],
      "env": { "OPENPROJECT_CONFIG": "C:/Users/<你>/.openproject/op.config.json" }
    }
  }
}

--no-warnings 用来屏蔽自签名证书实例的 TLS 告警(只是告警,不影响功能)。改完重启客户端,让 Agent 调一次 op_instances / op_whoami 验证。

MCP 工具一览(16 个)

工具说明
op_instances列出已配置的实例
op_whoami检查连通性,返回当前账号与实例地址
op_projects列出 / 按关键字搜索项目
op_wp_list查询工作包(项目、状态、负责人、类型、版本、超期、关键字、排序)
op_wp_get查看工作包详情(含描述、父子、预估与已投入工时)
op_wp_create / op_wp_update新建 / 更新工作包,支持 dry=true 先看服务端校验结果
op_wp_comment给工作包加评论
op_wp_delete删除工作包,必须 confirm=true
op_time_list / op_time_log查询 / 登记工时
op_members / op_versions / op_dict / op_users项目成员、版本、字典(状态/类型/优先级/活动/角色)、用户
op_raw直接打 OpenProject API,兜底用

参数写法很宽松:instance 选实例(省略则用默认实例),命令参数既可以放在 args 对象里 ({"instance":"lab","args":{"project":"示例项目"}}),也可以与 instance 平铺同级 ({"instance":"lab","project":"示例项目"})。

命令行自测(不需要任何客户端):

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | node mcp/server.js

CLI

不想装 MCP、也不想开 DSH 时,直接一条命令:

npm i -g @liu_zhao_feng_alex/dsh-plugin-openproject

openproject instances                                           # 列出实例
openproject lab projects --q=测试 --limit=20                     # 搜项目
openproject lab wp-list --project=示例项目 --status=open --limit=20
openproject studio wp-create --json='{"project":"示例项目","subject":"联调发现的 bug"}'
openproject studio wp-update --id=123 --status=进行中 --progress=50
openproject lab time-log --wp=123 --hours=2 --comment="本地联调"
openproject lab raw --path=/api/v3/users/me                     # 兜底直打 API

不装全局也行:node <包目录>/bin/openproject.js <实例名> <命令>。

  • 写入类命令支持 --dry=true 预览;删除必须 --confirm=true。
  • Windows 控制台会把中文参数转码,长文本/中文建议写进 UTF-8 的 JSON 文件后用 --json-file=x.json 传入。
  • 配置查找顺序与 MCP 完全一致(见上一节)。

工具清单(36 个)

下表按默认前缀 openproject_ 列出。用方式 D配置 toolPrefix 后,本实例的工具名会变成 <前缀>openproject_*(例如 lab_list_projects)。

连接与项目 openproject_whoami · openproject_list_projects · openproject_get_project · openproject_create_project · openproject_update_project

工作包 openproject_list_work_packages · openproject_get_work_package · openproject_create_work_package · openproject_update_work_package · openproject_delete_work_package · openproject_add_work_package_comment · openproject_list_work_package_comments · openproject_list_work_package_statuses · openproject_list_work_package_types · openproject_list_priorities · openproject_list_assignable_users

工时 openproject_list_time_entries · openproject_log_time · openproject_update_time_entry · openproject_delete_time_entry · openproject_list_time_activities · openproject_time_report

成员、角色与版本 openproject_list_memberships · openproject_add_membership · openproject_update_membership · openproject_delete_membership · openproject_list_roles · openproject_list_users · openproject_list_versions · openproject_create_version · openproject_update_version · openproject_delete_version

附件 openproject_upload_attachment · openproject_list_attachments · openproject_download_attachment · openproject_delete_attachment


用法示例

直接说人话即可,插件负责把自然语言翻译成 OpenProject API 调用:

  • 「列出我负责的、还没关闭的工作包」
  • 「在实验室官网项目下建一个任务,标题叫 X,负责人是 Y」
  • 「把 #123 的状态改成已关闭,说明一下原因」
  • 「统计 9 月各成员在这个项目上的工时」
  • 「看一下 #456 的评论和变更记录」
  • 「把这个文件传到 #789 上」

设计要点

  • 名字解析:所有引用类参数都接受人类可读名称,插件在服务端解析; 出现歧义时返回候选列表而不是猜。
  • 先读后写:字典类工具(状态、类型、优先级、角色、活动类型)让模型先取真实取值。
  • 干运行:给创建工作包/修改工作包传 dryRun: true,经服务端表单端点校验并回显将提交的内容。
  • 乐观锁:更新工作包时自动带上刚读到的 lockVersion;期间若有人改过会返回明确的冲突提示。
  • 零依赖:不 import 任何 DSH 内部包,因此不绑定具体 DSH 版本,也不受包管理器 模块解析方式影响。

故障排查

现象原因与处理
「未配置 OpenProject 实例地址」按上方「配置 → 第二步」设置 OPENPROJECT_BASE_URL 或 profile 的 baseUrl
「未配置 OpenProject API Token」设置 OPENPROJECT_API_TOKEN,或写入 $DSH_HOME/openproject.token,或用凭据服务
连接失败 / 证书被拒自签名证书实例需 rejectUnauthorized: false(或 OPENPROJECT_REJECT_UNAUTHORIZED=false)
401 / 403Token 失效或无权限,重新生成 Token
更新返回 409 冲突有人在你读取之后改过该工作包,重新读取后再改(插件已内置该提示)
报「toolPrefix 非法」前缀只允许字母、数字、下划线且以字母开头(如 lab_);多实例时必须各不相同,否则同名工具会互相覆盖
多实例时只有其中一个生效检查两条 insert 的 id 是否不同、toolPrefix 是否不同;插件加载失败会在插件页显示为 import 错误
模型调错实例给每条配置补上 label(如「实验室」);IP 或域名形式的 baseUrl 模型认不出对应哪个组织

已知的 OpenProject 行为(实测)

这些是写插件时踩过的坑,供二次开发参考:

  1. 时间记录筛选工作包用 entity 而不是 workPackage(后者报「筛选器不存在」); 但资源链接里两者同时存在。
  2. 工作包 PATCH 必须携带 lockVersion,否则一律返回 409 UpdateConflict。
  3. /form 端点不校验全部必填项,因此 dryRun 只作预览;插件已在客户端补上必填校验。
  4. hours 是 ISO 8601 duration,且 1 天 按实例的 hoursPerDay 折算。
  5. 类型上的 isDefault 表示「通用类型」而非「项目默认类型」。
  6. 父级为自动排程时,新建子任务会被自动补出截止日期。

开发

git clone https://github.com/koking0/dsh-plugin-openproject.git
cd dsh-plugin-openproject
# 无需安装依赖:本包零运行时依赖
node --check index.js              # 插件校验
node --check cli/openproject.js
node --check mcp/server.js
node bin/openproject.js help       # CLI 自测(不需要任何配置就能看命令列表)
npm pack --dry-run                 # 查看将发布的文件清单

目录结构:

index.js              插件入口:配置归一化、Token 解析、工具注册、系统提示词区块
cordis.patch.yml      bundle 补丁:向 profile 插入一条 Host 插件行
cli/openproject.js    CLI 与命令层:16 条命令,可被 MCP 复用(导出 run/commands/main)
mcp/server.js         零依赖 stdio MCP Server:手写 JSON-RPC,16 个工具
bin/openproject.js    CLI 可执行入口
bin/openproject-mcp.js MCP 可执行入口
op.config.example.json 多实例配置示例(真实配置 op.config.json 已 gitignore)
lib/http.js           零依赖 HTTP 客户端(node:http/node:https、multipart、下载)
lib/api.js            OpenProject API 层:鉴权、分页、HAL、名字解析、字典缓存
lib/guard.js          写入保护
lib/format.js         面向模型的紧凑中文渲染
lib/tools/*.js        按能力域拆分的工具定义(含内联的 defineTool 实现)

发布新版本:改 package.json 的 version 与 CHANGELOG.md,然后

npm publish --access public
# 或打 tag 由 GitHub Actions 自动发布(需在仓库 Secrets 里配 NPM_TOKEN):
git tag v1.2.0 && git push origin v1.2.0

许可

MIT

Related plugins