PluginManifest — a declaration of the plugin’s identity,
how it authenticates, which capabilities and tools it exposes, and how the host should
surface it. The runtime discovers plugins through the aperium.plugins entry-point group;
transports (MCP, HTTP, in-process) attach as adapters on top of a manifest — they are never
the plugin itself.
This page is for building a first-party plugin in the codebase. If you just want to wire
Aperium to an external system that already speaks MCP, an admin can register it through
the UI with no code — see Custom integrations.
aperium-plugin-example plugin is the canonical copy-me
template and the worked example throughout this page.
1. Scaffold
^[a-z][a-z0-9_-]*$ and must equal the entry-point name. The scaffolder
lays out the package under the aperium.plugins.<slug> namespace and registers the entry
point in the new package’s pyproject.toml:
2. Author the manifest
Every plugin exports abuild_manifest(ctx) -> PluginManifest factory at its package top
level. It is a pure factory: resolve secrets and wire dependencies, but do no I/O at
factory-call time — I/O belongs in lifecycle hooks or tool handlers.
1
Identity and auth
Declare who the plugin is (
PluginIdentity) and how it authenticates. Use auth_shape
and connection_shapes for new plugins so onboarding and health sweepers can project
the setup card. Credentials are resolved at runtime via ctx.secrets — the manifest
only declares what’s needed, never the values.2
Capabilities
CapabilityMetadata entries are routing metadata: domains, entities, operations,
services, routing_hints, and 2-3 routing_examples per user-facing capability.
Tools reference a capability by name. Write-class tools must live under a capability
with approval_required=True.3
Tools
Each
ToolSpec binds an input model, an output model, a handler, a RiskClass, and a
capability. Handler signatures are validated strictly against the schemas at
construction — a drift fails the manifest build.4
A query profile
Every read-capable plugin must declare a query profile (enforced by the
check_query_profile gate). The simplest path: expose one READ tool that returns a
single list of flat, scalar-field rows — the SDK auto-adapts it into a queryable
dataset. Providers with a native query language declare a DatasetPushdown instead.3. Validate
4. Test
Plugin tests live under your package’stests/ directory. The SDK ships a public test
harness (aperium.plugin_sdk.testing) with in-memory fakes so real handlers run unchanged:
uv run dev plugins record my_integration.
5. Smoke
smoke loads every shipped plugin manifest the way the host does at startup — the final
check that your plugin registers cleanly alongside the rest of the fleet.
The worked example
Theplugins/aperium-plugin-example package is the smallest viable, copyable plugin. It’s
a fixture-only reference (never auto-enabled on a tenant) that exercises the
full manifest surface so you can read one working plugin end to end:
- A
build_manifest(ctx)factory with a completePluginIdentity,auth_shape,connection_shapes,uimetadata, and a setup guide. - An
echoREAD tool and alist_echoestool whose flat-row output auto-adapts into the plugin’s query profile. - One capability with routing hints, one background
JobSpec, and a verified webhook spec.
Related
Manifest reference
Every model and field on the PluginManifest surface.
Custom integrations
Register an external MCP server through the Admin Console — no code.