Status: Draft Version: 0.2.0
A runtime binding is the configuration that tells a specific agent runtime how to consume an Effector. The Effector manifest carries one or more [runtime.<name>] tables, each tailored to a specific runtime's expectations.
This design means a single Effector can work across multiple runtimes without the author maintaining separate packages.
Before executing any step, a conformant runtime MUST perform interface type validation:
1. Read [effector.interface] for each step in the pipeline
2. For each sequential pair (A, B):
- Assert: output(A) <: input(B) [structural subtyping]
- If check fails: ERROR, halt pipeline, report type mismatch
3. For context requirements:
- Assert: all declared context types are satisfied by runtime environment
- If unsatisfied: WARN + block (or skip if optional)
4. Log type-check result to runtime audit log
This is analogous to a linker's type resolution pass — it happens once, before execution starts, not per-invocation.
| Code | Meaning | Action |
|---|---|---|
TC_OK |
All types resolve and are compatible | Proceed |
TC_WARN_INFERRED |
One or more types are inferred (not declared) | Proceed with warning |
TC_ERR_MISMATCH |
A sequential type pair is incompatible | Halt, report step indices |
TC_ERR_UNKNOWN |
A type reference is not found in effector-types | Halt if strict mode, warn if lenient |
TC_ERR_CONTEXT |
Required context type is not available | Halt if required, skip if optional |
effector.toml
│
┌──────────────┼──────────────┐
│ │ │
[runtime.openclaw] [runtime.mcp] [runtime.generic]
│ │ │
▼ ▼ ▼
OpenClaw loads Claude/Cursor Any runtime
SKILL.md or loads MCP loads generic
plugin code tool def function
The manifest is the single source of truth. Each runtime reads only its own binding section.
OpenClaw is the reference implementation for the Effector spec. This section documents how OpenClaw consumes each Effector type.
OpenClaw Runtime Architecture
──────────────────────────────
┌─────────────────────────────────────────┐
│ Pi Agent Runtime │
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Gateway │ │ Skill Loader │ │
│ │ WebSocket │ │ (SKILL.md │ │
│ │ :18789 │ │ parser) │ │
│ └──────┬───────┘ └───────┬─────────┘ │
│ │ │ │
│ ┌──────┴───────┐ ┌───────┴─────────┐ │
│ │ Multi-Chan │ │ Plugin SDK │ │
│ │ Inbox │ │ (extensions) │ │
│ │ (20+ ch.) │ │ │ │
│ └──────────────┘ └───────┬─────────┘ │
│ │ │
│ ┌──────────────────────────┴──────────┐ │
│ │ Workspace-as-Kernel │ │
│ │ SOUL.md | AGENTS.md | TOOLS.md │ │
│ │ IDENTITY.md | HEARTBEAT.md │ │
│ └─────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Lobster Engine │ │
│ │ (workflow orchestration) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘
[runtime.openclaw]
format = "skill.md"
entry = "SKILL.md"
min-version = "0.20.0"
[runtime.openclaw.requires]
bins = ["gh"]
env = ["GITHUB_TOKEN"]
skills = ["git-operations"] # Co-dependency
[runtime.openclaw.install]
brew = { formula = "gh", bins = ["gh"] }
apt = { package = "gh", bins = ["gh"] }
[runtime.openclaw.publish]
registry = "clawhub"
tier = "managed" # bundled | managed | workspaceLoading sequence:
- OpenClaw scans
~/.openclaw/workspace/skills/for directories - For each directory, check for
effector.toml(or fall back toSKILL.mdfrontmatter) - Parse manifest and verify requirements
- Register skill in the agent's available tool list
- When contextually relevant, the agent reads
SKILL.mdand follows instructions
[runtime.openclaw]
format = "plugin"
entry = "dist/index.js"
min-version = "0.21.0"
[runtime.openclaw.plugin]
channels = ["telegram"]
providers = []
actions = ["send-message", "fetch-updates"]
permissions = ["network", "storage"]
config-schema = "config.schema.json"Loading sequence:
- OpenClaw discovers
openclaw.plugin.jsonoreffector.tomlinnode_modules/ - Calls
require(entry).register(api)whereapiisOpenClawPluginApi - Extension registers channels, providers, actions, and event hooks
- Extension remains active for the runtime's lifetime
Plugin API surface (OpenClaw-specific):
interface OpenClawPluginApi {
registerChannel(channel: ChannelDefinition): void;
registerProvider(provider: ProviderDefinition): void;
registerAction(action: ActionDefinition): void;
on(event: string, handler: EventHandler): void;
getConfig<T>(schema: JSONSchema): T;
getLogger(namespace: string): Logger;
}[runtime.openclaw]
format = "lobster"
entry = "pipeline.yml"Loading sequence:
- Lobster engine parses
pipeline.yml - Resolves skill references to installed Effectors
- Validates step dependencies and variable references
- Executes pipeline (manual trigger, cron, or event-driven)
[runtime.openclaw]
format = "workspace"
entry = "."
files = ["SOUL.md", "AGENTS.md", "TOOLS.md", "IDENTITY.md", "HEARTBEAT.md"]Loading sequence:
- Copy workspace files to
~/.openclaw/workspace/ - OpenClaw reads workspace files at startup
SOUL.mdshapes agent personality;TOOLS.mddefines available tools- Agent behavior reflects workspace configuration until changed
MCP is the cross-runtime interoperability layer. Any Effector with an MCP binding becomes available to Claude Desktop, Cursor, Windsurf, and other MCP clients.
[runtime.mcp]
format = "mcp-tool"
entry = "mcp/server.js"
transport = "stdio" # stdio | sse | streamable-http
[runtime.mcp.tool]
name = "github_pr_review" # snake_case per MCP convention
input-schema = "mcp/input-schema.json"Loading sequence (stdio):
- MCP client spawns the server process:
node mcp/server.js - Client sends
initializeJSON-RPC message - Server responds with
tools/listincluding the Effector as an MCP tool - Client invokes
tools/callwith input matching the schema - Server executes the Effector's logic and returns results
| Effector Manifest Field | MCP Tool Field |
|---|---|
effector.name |
tool.name (converted to snake_case) |
effector.description |
tool.description |
runtime.mcp.tool.input-schema |
tool.inputSchema |
The openclaw-mcp bridge Effector (already built) demonstrates the canonical pattern: it reads SKILL.md files and exposes them as MCP tools dynamically. This means every skill Effector is automatically MCP-compatible through the bridge, even without an explicit [runtime.mcp] binding.
For developers building custom agents with the Claude Agent SDK:
[runtime.claude-agent-sdk]
format = "tool"
entry = "src/tool.ts"The entry file exports a tool definition compatible with the SDK's Tool interface.
For runtimes not yet defined in the spec, authors can provide a generic binding:
[runtime.generic]
format = "function"
entry = "src/index.js"
exports = ["execute", "validate", "describe"]Contract:
execute(input: object): Promise<object>— Run the Effectorvalidate(input: object): Promise<ValidationResult>— Check input validitydescribe(): EffectorDescription— Return a machine-readable description
This provides a minimal interoperability surface for any runtime to consume.
When a runtime encounters an Effector with multiple bindings, it SHOULD use this priority:
- Exact match — Use the binding for this specific runtime
- Bridge — Use a bridge Effector that translates from a known binding
- Generic — Fall back to the generic binding
- Inference — Infer from the entry file format (e.g., SKILL.md → skill)
- Incompatible — Report that no binding is available
Runtimes that adopt the Effector spec SHOULD register themselves:
# In the runtime's own metadata (not in the Effector manifest)
[runtime]
name = "openclaw"
version = "0.21.0"
spec-version = "0.1.0"
supported-types = ["skill", "extension", "workflow", "workspace"]This allows tooling to check compatibility before installation.