Skip to main content

The finished plugin in one snippet

This is what you are about to build — every OraclePlugin hook wired against Open-Meteo. The canonical source: apps/qiforge-example/src/plugins/weather/weather.plugin.ts.
Each step below builds one of those lines.

Two ways to author a plugin

Every hook in this guide works identically whether you subclass OraclePlugin or use the defineOraclePlugin helper. Pick whichever you prefer — the runtime treats the resulting object exactly the same way.
Subclass and override each hook. Per-session state lives on private fields. Register with new WeatherPlugin().
The rest of this guide uses the class form (matching the reference oracle), but every snippet maps one-to-one onto a defineOraclePlugin field.

Prerequisites

Step-by-step

1

Create the skeleton with a manifest and config schema

File: weather.plugin.ts
That’s a valid plugin — it loads, validates WEATHER_DEFAULT_UNITS, and contributes nothing yet. Everything below adds one hook at a time.
2

Write the upstream client

File: weather-client.ts. Open-Meteo doesn’t need auth.
Keep it tiny — this guide is about the plugin contract, not weather APIs.
3

Add a boot-time tool with getTools

File: weather-tools.ts.getTools(ctx) is called once per agent build. Use it when the tool’s behaviour depends only on plugin config — not on per-request data.
Wire it on the plugin:
The handler still receives a full RuntimeContext at call time — ctx.user, ctx.session, ctx.abortSignal are all live.
4

Add a request-time tool with getRequestTools

When a tool depends on per-request data (e.g. the user’s timezone), register it via getRequestTools(rtCtx) instead:
Boot-time and request-time outputs are merged — both tools end up in the agent’s tool list.
5

Add a sub-agent with getSubAgents

File: weather-sub-agent.ts.Sub-agents have their own prompt and tool list, and the runtime exposes each as a single tool to the main agent (call_weather_planner_agent). Use them for compound tasks that benefit from focused context.
forwardTools: true surfaces the sub-agent’s inner tool calls in the parent chat’s UI events so users see the chain.
6

Add a middleware with getMiddlewares

File: weather-middleware.ts.Middleware runs around every LLM call. Hooks come from LangChain’s createMiddleware.
Plugin middleware runs after the framework’s always-on middleware (capability-gate, tool-validation, tool-repetition-guard, tool-retry) — plus the conditional page-context / safety-guardrail when their hooks are configured — in topological dependency order across plugins. A plugin middleware hook must return undefined or { jumpTo: 'end' as const }, never a partial state object — see Add a middleware.
7

Add an HTTP route with getNestModules + getAuthExcludedRoutes

File: weather.module.ts.To expose a public GET /weather/now?city=X, ship a NestJS module:
Register it from the plugin and opt the route out of UCAN auth:
8

Expose shared state with getSharedState

Other plugins can now read the last weather query for the current session:
Your tool/sub-agent handlers write into this.lastBySession after every successful lookup (see step 3). Consumers read it as rtCtx.shared.lastWeatherQuery.
9

Register the plugin in main.ts

Reference: apps/qiforge-example/src/main.ts.
10

Verify every hook works end-to-end

Boot the app (pnpm dev) and exercise each hook.Full manual walkthrough: apps/qiforge-example/WEATHER-PLUGIN.md.

Test your plugin

Tier A direct-invoke and Tier B agent-loop tests.

Plugin recipes

Per-hook deep dives (tool, sub-agent, middleware, HTTP, state).

Plugin API reference

Every hook signature, exhaustively.

Plugin anatomy

How the runtime turns a class into a registered plugin.