PluginContext
An app imports nothing from the host. What it needs arrives here.
async handler(args, ctx: PluginContext) { /* … */ }| Member | Available in | What it is |
|---|---|---|
pluginId |
tools, jobs | Your own id. |
organizationId |
tools | The customer being worked for. |
settings |
tools | Their configuration, validated, defaults applied. |
settingsSavedAt |
tools | When they last saved the panel — the human gesture, null if never. |
storage |
tools | Key/value private to your app and this organisation. |
shared |
tools, jobs | Key/value private to your app, common to every organisation. |
http |
tools, jobs | The only way to the network, limited to allowedHosts. |
secrets |
tools, jobs | The host’s environment variables you declared. core apps only. |
log |
tools, jobs | info, warn, error, with structured metadata. |
now() |
tools, jobs | Injected clock. Use it instead of new Date(): it’s what makes your tests deterministic. |
A job’s context is deliberately narrower — no organisation, no settings, no per-tenant storage — because a job runs once for the whole platform.
Why everything is async
Section titled “Why everything is async”Even where, running in-process, it wouldn’t need to be. PluginContext is the
boundary that becomes a network call the day third-party apps run elsewhere,
and every argument crossing it is serializable for the same reason.
secrets is the one exception, synchronous and non-serializable on purpose:
it’s the thing that must not cross that boundary. A verified or
external app brings its own credentials — it never receives ours.
settingsSavedAt
Section titled “settingsSavedAt”Not “when the row was touched”: when a person last confirmed the configuration.
It exists for values that are only true for a while. A gold price typed by hand is worth something today and nothing next week, and an app that prices with it needs to know whether it was confirmed today or forgotten in March.
Testing without a host
Section titled “Testing without a host”import { createHarness } from "@prezzando/plugin-sdk/testing";
const harness = createHarness(manifest, { settings: { trade: "cartongesso" }, now: new Date("2026-08-20T08:00:00Z"),});
const result = await harness.runTool("gold_price", { fineness: "750" });Storage is in memory, the clock is fixed, and the network is closed unless you
open it. It’s the same harness the CLI uses for run and job.