PKU harness
Build a plugin

Build a plugin

How to choose between a PLUGIN.md document, a remote plugin and a bundled TypeScript plugin, which repositories to clone, and the order of work from your first tool to a marketplace listing.

A plugin connects PKU harness to one campus service, or adds one feature to the app. Each plugin offers a set of tools: small, typed operations such as "list free classrooms" or "show my card balance". The AI assistant and the user's own taps both call the same tools.

Writing a plugin therefore comes down to three jobs. You find out how the service answers requests, you wrap those requests in tools, and, where it helps, you add a widget or a screen.

This section assumes you can read YAML or TypeScript and use a terminal. You don't need to know the app's internals.

Choose the kind

There are three kinds of plugin. Most people should write the first one.

A plugin document is a folder with a PLUGIN.md file. Its tools are written as YAML steps with small, safe expressions, and the app interprets them on the phone. You publish it with git push, and students install and update it from a link. Use it for most campus integrations: requests to campus or public websites, extraction of the data you need, and the user's campus session when a service requires sign-in. Start with the quickstart, then read Declarative plugins. You can also write it with the AI inside the app.

A remote plugin is a server you run yourself. It speaks the Model Context Protocol (MCP), a standard way for AI apps to call tools over HTTP, using the Streamable HTTP transport. You can write it in any language. Updates go out when you deploy the server; no app release is involved. Use it for your own service, for heavy work, or for anything that needs general-purpose code. Start from templates/remote-plugin.

A bundled plugin is a TypeScript module, written with zod and React Native, that ships inside the app. It reaches users only with app releases, so open an issue before you start. Use it for the app's own features (dashboard, schedule, scheduler, memory, model providers and so on) and for integrations the plugin language can't express yet. Start with definePlugin.

Plugin documentRemote pluginBundled plugin
Written inYAML steps and expressionsAny language (MCP)TypeScript
Runs onThe phone, interpreted by the appYour serverThe phone, inside the app
How updates reach usersgit pushDeploy your serverApp release

The official campus integrations classrooms, campus-events, grades, canteen, shuttle and notices are already plugin documents. The other official integrations are still TypeScript, and we are porting them one at a time.

The order of work

Read the concepts

Four pages set the rules every plugin follows: Plugins, Tools and effects, Permissions and Security. Read them first, because the review checks your plugin against them.

Find the protocol

You need to know which addresses the service uses and what it sends back. If pkucli already talks to the service, port its code and credit the file you used. Porting from pkucli explains how.

Write the tools

Write one tool per operation. Declare its effect honestly, put limits on every input, and write the description for the model. For a plugin document, see Declarative plugins. For code, see Tools and schemas.

Add UI

A plugin document can add generative-UI widgets and screens (described as JSON and drawn by the app), Settings rows and slash commands. A bundled plugin can also add native screens. See UI contributions and Languages.

Test

Each kind has its own tools. For plugin documents, bun run plugin validate|test checks the files and replays recorded responses. For TypeScript logic, use bun test. To exercise the assistant's loop without an API key, use the mock model. To try a remote plugin, use the demo remote plugin. Testing covers all four.

Publish

Share the install link, then list the plugin in the marketplace. See Publishing, and in the marketplace section the manifest reference and the submit flow.

Repositories you need

Start with pkuinfo/pku-harness-plugins. It holds the SDK and the plugin language, the author command-line tool (bun run plugin), the official plugin documents and bundled plugins, the marketplace registry, and the remote-plugin template and examples.

You need pkuinfo/pku-harness, the app itself, only to run a bundled plugin on a phone or emulator. A plugin document doesn't need it until you want to try campus requests for real.

pkuinfo/pkucli is a set of Rust command-line tools for PKU services. Use it as a reference for how each service's protocol works.

mkdir pku-info && cd pku-info
git clone https://github.com/pkuinfo/pku-harness-plugins.git     # SDK, plugins, registry, template
git clone --recurse-submodules https://github.com/pkuinfo/pku-harness.git   # only to run bundled plugins in the app
cd pku-harness-plugins && bun install && bun test

No npm packages

The SDK and the plugins are not published to npm. You use them straight from the GitHub repository: work inside a checkout of pku-harness-plugins, whose bun workspace and tsconfig.json resolve @pku-harness/sdk to packages/sdk/src. The app pulls in the same repository as the git submodule plugins/. Remote plugins don't need the SDK at all.

Ground rules (from CONTRIBUTING.md)

  1. Tools are the API. Give each tool a clear Chinese title, which users see, and a description written for the model that says when and how to use it. Validate every input: with input fields in a plugin document, with zod in a bundled plugin, and with JSON Schema in a remote plugin.
  2. Declare effects honestly. A read-only tool sets effect: read (plugin documents and bundled plugins) or readOnlyHint: true (MCP). A tool that changes state, spends money or posts publicly must say so.
  3. Never ask for campus passwords. Credentials stay on the user's phone.
  4. Return data, not instructions. The model treats tool results as data.
  5. Declare what you reach. In a plugin document, list campus, network and abilities; requires is derived from them. In code, list requires. Don't depend on a shell or a file system on the phone.
  6. Port campus protocols from pkucli when it already implements them, and credit the source.
  7. Write both languages. A plugin document has an i18n.en section. A bundled plugin needs an English overlay for every user-facing manifest string, and a src/strings.ts table for the text in its screens and widgets.

On this page