Plugin SDK reference
Plugin entry points
Every plugin exports a default entry object. The SDK provides a helper for
each entry shape: defineToolPlugin, definePluginEntry,
defineChannelPluginEntry, defineSetupPluginEntry.
All plugin APIs are experimental, including these entry helpers. Pin and test the OpenClaw host versions your plugin supports.
Where each section moved
Every section of the single-page version now lives on this page or on one of the eight child pages below. The anchors from the single-page version still resolve here.
- Tool policy vocabulary
- Sandbox bind parsing
- Package entries
- defineToolPlugin
- definePluginEntry
- Native provider factories
- Computer Use providers
- defineChannelPluginEntry
- defineSetupPluginEntry
- Registration mode
Plugin shapes
OpenClaw classifies loaded plugins by their registration behavior:
| Shape | Description |
|---|---|
| plain-capability | One capability type (e.g. provider-only) |
| hybrid-capability | Multiple capability types (e.g. provider + speech) |
| hook-only | Only hooks, no capabilities |
| non-capability | Tools/commands/services but no capabilities |
Use openclaw plugins inspect <id> to see a plugin's shape.
Related
- Plugin SDK overview - registration API and subpath reference
- Plugin runtime helpers -
api.runtimeandcreatePluginRuntimeStore - Plugin setup and config - manifest and setup entry loading
- Building channel plugins - building the
ChannelPluginobject - Building provider plugins - provider registration and hooks
MCP subprocess runtime
Import: mcpStdioRuntime from openclaw/plugin-sdk/agent-harness-runtime using dynamic import() when opening a connection. Its frozen object lazily loads one factory:
const { mcpStdioRuntime } = await import("openclaw/plugin-sdk/agent-harness-runtime");const { createMcpStdioClient } = await mcpStdioRuntime.load();Use createMcpStdioClient(params) for a caller-owned MCP proxy subprocess fronting a stateful driver. OpenClaw owns the subprocess and its descendants, newline framing and JSON-RPC validation, initialization, request admission, deadlines, and shutdown. The client starts connecting when the factory returns. Keep this runtime out of plugin registration and paths that do not open MCP connections.
Supply command, optional args, and an exact env. The child inherits no other environment variables. Set clientInfo (name and version), the required protocolVersion, startupTimeoutMs, maxPendingRequests, and maxFrameBytes. The server must return exactly the requested protocol version. OpenClaw retains a fixed 32 KiB stderr tail for unexpected-exit diagnostics. The decoder bounds pending bytes plus each incoming chunk before buffering, preserves fragmented UTF-8, skips empty lines, and requires safe integer response IDs.
The caller supplies errors.unavailable(message, cause?) and errors.protocol(message, cause?), each returning an Error. The first classifies process, lifecycle, admission, deadline, and cancellation failures. The second classifies malformed frames, non-timeout JSON-RPC errors, and handshake contract violations. Plugin-specific tool-result normalization stays with the caller.
The returned client exposes three methods:
isAvailable()synchronously reports whether initialization completed and the connection remains usable.request(method, params, { timeoutMs, signal? })waits for startup and returns the object result. An already-aborted signal or a full pending-request limit rejects only that call. After admission, cancellation or timeout retires the entire connection and rejects pending requests with the retained fatal error. The client suppresses SDK cancellation notifications because it terminates the process instead. A non-timeout JSON-RPC error response rejects only its matching request througherrors.protocol.stop()closes admission, retires pending requests, and awaits startup settlement and owned-process cleanup. It rejects througherrors.unavailablewithproxy cleanup could not be confirmedif cleanup is uncertain. It never stops a separately started service reached through the proxy's socket.
After successful stop(), the optional read-only cleanupResult records forced relay retirement: reason: "forced-relay-exit", signalRequested, the observed relay exit code and signal, durationMs, and escalationAfterMs. It retains signalError when signal delivery reported failure but exit was subsequently confirmed. It is absent for ordinary cleanup. Closed control/output/lineage pipes and a matching closing receipt admit escalation; pending force requests are reconsidered as closure and group-exit facts arrive. A live anchor is killed and reaped through its relay. Confirmed anchor-group absence permits direct native termination of an unresponsive relay. Actual relay exit and server-group disappearance must then be confirmed within the original hard deadline. Uncertain cleanup retains missing closure facts and timing or signal-delivery details in the error's cause chain.
Malformed frames, incompatible initialization, write failures, and unexpected process exit also retire the whole connection. The first fatal error is retained. Create a new client to reconnect. Timeout classification follows the SDK error code, so a timeout-coded server error also retires the connection.
Workspace access
Use openclaw/plugin-sdk/agent-workspace-runtime to declare, register, and acquire
AgentWorkspaceAccess without loading the agent execution runtime. Declare a
configured remote workspace during registration so callers cannot fall back to
local files before its service starts. Register its bridge when ready and release
it when the service stops. Callers keep their existing document authorization.