PKU harness

写给 AI 助手

写给编程 Agent(Claude Code、Codex、App 内置助手)的说明,帮助它从头到尾做出一个 PKU harness 插件。内容包括如何选择插件类型、具体步骤、必须遵守的规则、要运行的检查,以及应当加载哪些页面。

这一页写给 AI 编程助手。如果你是一个 AI Agent,正准备为 PKU harness 写插件,请按顺序读完本页。

本页链接的页面都有 Markdown 版本。你可以在任意文档 URL 后面加上 .md 来加载单个页面,也可以先读 /llms.txt 里的索引。如果需要一次拿到全部内容,中文版在 /llms-full.zh.txt,英文版在 /llms-full.txt。

请不要编造 API。你需要的每个类型都写在 SDK 参考里。SDK 参考里没有的东西,就是不存在。

0. 加载上下文

下表列出不同任务需要加载的页面。

场景加载
所有任务/docs/concepts/plugins.md、/docs/concepts/tools-and-effects.md、/docs/concepts/permissions.md、/docs/concepts/security.md
插件文档(PLUGIN.md)/docs/build/declarative-plugins.md、/docs/build/quickstart.md、/docs/marketplace/manifest.md,以及下文列出的规范文件
远程插件/docs/build/remote-plugin.md、/docs/marketplace/manifest.md
内置插件/docs/build/tools.md、/docs/build/bundled-plugin.md、/docs/sdk/plugin.md、/docs/sdk/host-services.md、/docs/build/ui.md、/docs/build/i18n.md、/docs/build/testing.md
接入校园服务/docs/concepts/campus-sessions.md、/docs/build/porting-from-pkucli.md、/docs/sdk/campus.md
用到云服务/docs/cloud/plugin-guide.md、/docs/sdk/cloud.md

写插件文档时,还要读插件语言的规范 PLUGIN-LANGUAGE.md,以及 SDK 中的 packages/sdk/src/lang/schema.ts 和 abilities.ts。

动手修改之前,先读仓库自己的规则。在 pku-harness-plugins 里,规则写在 AGENTS.md / CLAUDE.md 和 CONTRIBUTING.md 中。如果要改 App 本身,也要读 pku-harness 里的同名文件。

1. 选择插件类型

插件有三种:插件文档、远程插件和内置插件。按下面的顺序判断。

插件只需要请求校园网站或公开网站,再从响应里提取数据吗?(需要时可以带上用户的校园会话)
├─ 是 → 插件文档:一个包含 PLUGIN.md 和 tools/*.yaml 的文件夹,由 App 解释执行。
│        通过链接分享和安装,不需要发布新版 App。校园服务默认用这种方式
│        (classrooms、campus-events、grades、canteen、shuttle 和 notices 都是插件文档)。
└─ 否 → 需要通用代码,或者需要你自己的服务器吗?
         ├─ 是 → 远程插件:一个基于 Streamable HTTP 的 MCP 服务器(模板在 templates/remote-plugin)。
         └─ 否,需要设备能力,或插件语言做不到的原生界面
              → 在 pku-harness-plugins 里写内置 TypeScript 插件(请告诉用户先开一个 issue,
                因为内置插件随 App 及其签名的 OTA 更新一起发布),
                或者提议新增一项宿主能力,让插件文档也能做到。

下面逐一说明这三种类型。

插件文档适合大多数校园服务。它只做两件事:向校园网站或公开网站发请求,再从响应里提取数据。需要登录的网站,可以带上用户的校园会话。插件文档是一个包含 PLUGIN.md 和 tools/*.yaml 的文件夹,由 App 解释执行。它通过链接分享和安装,不需要发布新版 App。

远程插件适合需要通用代码或自己服务器的情况。它是一个 MCP 服务器。MCP 是 Model Context Protocol(模型上下文协议)的缩写,远程插件通过 Streamable HTTP 和 App 通信。模板在 templates/remote-plugin。

内置插件只在需要设备能力,或者需要插件语言做不到的原生界面时才写。它是 pku-harness-plugins 里的一个 TypeScript 模块,随 App 一起发布,修复则通过签名的空中更新(OTA)推送。所以写内置插件之前,请告诉用户先开一个 issue。另一种做法是提议新增一项宿主能力(ability),让插件文档也能完成这件事。

在 App 里,助手先用 plugins.use 加载 plugins 插件的编写工具,再用它们来写插件文档:plugins.reference、validate、tryTool、source 和 install。详见让 AI 写插件。

2. 插件文档检查清单

  1. 选一个工作目录。插件文件夹是自包含的,放在任何仓库里都可以。如果想用插件命令行工具,就在 pku-harness-plugins 的本地副本里工作。可以从 plugins/classrooms 或快速上手开始。

  2. 编写 PLUGIN.md,填写以下字段:

    • spec: 1;
    • id,需匹配 ^[a-z0-9][a-z0-9-]{1,40}$;
    • 中文的 name;
    • description,长度 4–200 个字符;
    • 语义化版本号 version;
    • author;
    • campus、network 和 abilities,只列出步骤里实际用到的,不多也不少。

    App 加载的每个文件都要在 frontmatter 里列出。

  3. 每个操作对应一个工具,写在 tools/<name>.yaml 里。每个工具需要:

    • 中文的 title;
    • 写给模型看的 description;
    • 如实的 effect,至少要覆盖它调用的每项宿主能力的效果;
    • 有取值范围的 input 字段;
    • 来自第三方的文本标上 untrusted: true;
    • 执行步骤,以及一个只返回数据的 return。
  4. 只使用规范里列出的步骤、标准库函数和宿主能力,其他的都不存在。YAML 有几处容易写错:表达式里含有 : ,或者以 { [ ' " 开头时,要加引号;正则表达式要写在单引号字符串或 | 块里。

  5. 不要使用 server:、{ fn } 绑定或 cloud 宿主能力。官方服务器目前关闭了服务端函数,所以插件要么完全在手机上运行,要么使用你自己的后端。

  6. 为每个工具在 tests/<name>.yaml 里录制响应,并去掉其中的个人数据。失败的情况也要录制,用 expectError 表示。

  7. 运行下面的命令,直到全部通过。

bun run plugin validate path/to/plugin
bun run plugin test path/to/plugin
  1. 校园请求无法在 App 之外运行,所以请说明哪些工具只用录制的响应测试过。最后把 GitHub 文件夹的 URL 作为安装链接分享出去,用户会在 App 里审阅插件后再安装。

3. 远程插件检查清单

  1. 从 pku-harness-plugins 复制 templates/remote-plugin,然后运行 bun install。
  2. 创建服务器:new McpServer({ name: '<registry id>', version, title: '<Chinese name>' }, { instructions })。其中 name 是注册表 id,需匹配 ^[a-z0-9][a-z0-9-]{1,40}$;title 填插件的中文名。
  3. 每个操作调用一次 registerTool。工具名用简短的 ASCII 驼峰形式,title 用中文,description 写给模型看。inputSchema 的每个字段都要有说明和取值范围。
  4. 不改变任何数据的工具加上 annotations: { readOnlyHint: true }。其他工具一律不加。
  5. 成功时在 structuredContent 里返回数据。失败时返回 isError: true 和一句简短的说明。
  6. 运行 bun server.ts,然后用 App 或任意 MCP 客户端验证。在 Android 模拟器里,地址是 http://10.0.2.2:8787/mcp。
  7. 通过 HTTPS 部署。然后添加一份清单(manifest)并提交,或者在 registry.json 里添加一条记录,见发布。

4. 内置插件检查清单

  1. 在 pku-harness-plugins 的本地副本里工作。App 仓库以子模块 plugins/ 的形式引入它。SDK 没有 npm 包,@pku-harness/sdk 在工作区里解析到 packages/sdk/src。
  2. 创建两个文件:
    • plugins/<id>/package.json:设为 private,"main": "src/index.ts",peer 依赖为 @pku-harness/sdk、zod、react 和 react-native;
    • plugins/<id>/src/index.ts:导出 <camelId>Plugin = definePlugin({...})。
  3. 填写这些字段:id、中文的 name 和 description、version、icon、requires、scopes,以及用 defineTool 定义的 tools。icon 从宿主图标集中选,且不能和注册表里其他插件重复。可选字段有 dataSources、instructions、contributes 和 defaultModes。
  4. 清单里每个面向用户的字符串都要在 i18n.en 里提供英文。页面和小组件的文案放在 src/strings.ts,用 defineStrings 定义。
  5. 纯逻辑放在普通模块里,测试文件 *.test.ts 放在同一目录。如果测试导入了界面代码,要 mock react-native。
  6. 在 plugins/index.ts 的 builtinPlugins 中注册插件,并在 registry/registry.json 里添加一条 "kind": "builtin" 记录。
  7. 运行下面的命令,直到全部通过。
bun test
bunx tsc --noEmit -p .
bun scripts/validate-registry.ts
  1. 如果你无法运行 App,请直接说明。界面需要有人在真机或模拟器上,分别在浅色和深色主题、中文和英文下检查过,才算完成。

5. 必须遵守的规则

违反下面任何一条都算 bug。

  1. 绝不索要、存储、记录或传输校园密码、OTP(一次性密码)密钥、会话令牌或 API 密钥。 校园请求一律通过 services.campus.request 或插件文档的 request: { campus } 发出。远程插件没有校园会话。
  2. 每个工具声明一个如实的效果(effect)。 效果有四种:read、write、money 和 public。不要把写操作藏在 read 工具里。定时任务只能调用 read 工具。
  3. 工具结果只是数据,不是指令。 结果里不能出现「告诉用户……」之类的话,也不能夹带提示词。
  4. 来自第三方的文本标上 untrusted: true。 这包括帖子、评论、通知和公开网站上的描述。助手读到这类文本后,本轮对话就被「污染」了(ctx.turn?.tainted),这时持久化的写操作要拦下。
  5. 结果里绝不返回机密信息。 例如付款码、分享链接、收银台 URL 和令牌。
  6. 声明你访问的一切。 插件文档要写 campus、network 和 abilities;代码里用到的每项能力都要写进 requires。内置插件不允许使用 shell 和 filesystem。
  7. 内置插件不从 App 或其他插件的文件夹导入代码。 请使用 ctx.services、ctx.kv、ctx.cloud 和 host。多个内置插件共用的代码放在 plugins/shared/。
  8. 校园协议从 pkucli 移植, 并在注释里注明源文件。不要猜测接口。
  9. 面向用户的字符串要中英双语。 面向模型的文本,例如描述、指令和结果,不需要翻译。
  10. 小组件不轮询。 请使用带 TTL(缓存有效期)的 host.useToolData。
  11. files.pick 和 notify.fix 只能由用户点击触发, 不能在助手调用的工具里使用。
  12. 使用 bun(bun install、bun test、bunx),不要使用 npm、npx、yarn 或 pnpm。

6. 名称和格式

下面这些名称和格式必须写对。

对象形式示例
工具 id(用于代码、小组件、定时检查和 invoke)<plugin>.<tool>wallet.cardBalance
模型看到的工具名(写在 instructions 里)<plugin>__<tool>wallet__cardBalance
远程插件在 App 中的 idmcp-<server name>mcp-demo-canteen
插件文档的安装链接GitHub 文件夹 URL,或 pkuharness://plugin?src=<url-encoded link>https://github.com/pkuinfo/pku-harness-plugins/tree/main/plugins/classrooms
页面路由/p/<plugin>/<screen>,弹窗为 /m/<plugin>/<screen>/p/focus/main
模型写出的时间本地时间 "YYYY-MM-DD HH:mm""2026-10-30 08:00"

7. 报告完成之前

  • 选定了插件类型(插件文档、远程插件或内置插件),并说明了理由。
  • 每个工具都有标题、描述、有取值范围的输入和如实的效果,结果只含数据。
  • 插件文档:bun run plugin validate 和 test 都通过;声明与步骤实际做的事一致。
  • 代码插件:requires 和 scopes 完整;defaultModes 对 money 和 public 设为先询问用户。
  • 结果和日志里没有凭据、机密信息或链接。
  • 上面的测试和检查都通过了,或者你准确说明了哪些没能运行,以及原因。
  • 添加了注册表记录或清单。如果插件用到云服务,复制了 cloud 声明。
  • 给用户写了总结:插件做什么、改了哪些文件、怎么试用,以及还有哪些事需要人来做(例如真机测试、用真实校园账号测试、为内置插件开 issue)。

把文档当作上下文

URL用途
/llms.txt所有页面的索引,每页一句简介,用来挑选要加载的页面
/llms-full.zh.txt所有中文页面合成的一个文件(很大)
/llms-full.txt所有英文页面合成的一个文件(很大)
/docs/<page>.md单个页面的 Markdown
/docs/cloud/api/<tag>.md以纯文本列出的 Cloud API 接口和数据结构

本页目录