Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Wire plugin tools end-to-end: LLM visibility, execution dispatch, config, hooks, and channel messaging #32

Description

@joshuajbouw

Summary

The OpenClaw Unicity plugin loads and discovers tools through our Node.js MCP bridge, but six critical pathways are broken:

  1. Plugin tools never reach the LLM's tool list
  2. Plugin tool execution routes through ServerManager which doesn't know about plugins
  3. Plugin config never reaches the bridge subprocess
  4. Hook forwarding (before_agent_start) is dead code
  5. Inbound channel messages have no Rust-side handler
  6. Outbound channel message routing has no mechanism

This issue tracks the minimal changes needed to make the Unicity plugin (and any future MCP-backed plugin) fully functional end-to-end.

Sub-Issues (4 PRs)

PR Issue Items Depends On
A1 #33 — Wire plugin tools into LLM tool list and execution dispatch 1+2
A2 #34 — Pass plugin config to bridge via post-init notification 3
B #35 — Forward hook events to plugins and collect agent context 4 #33
C #36 — Channel message routing: inbound notifications and outbound verification 5+6 #33, #34, #35
Phase A (parallel):
  ├── #33 Items 1+2 (tool wiring)
  └── #34 Item 3 (config passing)

Phase B (after #33):
  └── #35 Item 4 (hook forwarding)

Phase C (after all above):
  └── #36 Items 5+6 (channel messaging)

Design Decision: PluginRegistry on AgentRuntime vs ServerManager Registration

Considered: Registering plugin Peer<RoleClient> handles directly into ServerManager so plugin tools flow through the existing MCP tool list and dispatch path automatically.

Rejected because:

  • ToolCall::parse_name() uses split_once(':'). Tool name plugin:my-plugin:echo splits as server=plugin, tool=my-plugin:echo — wrong server lookup. Fixing this requires either dropping the plugin: prefix (name collision risk with MCP servers) or custom parse logic.
  • Lifecycle dual-ownership: both ServerManager::stop() and McpPlugin::unload() would try to close the same peer session.
  • ServerManager requires ServerConfig for each entry (restart policy, command, args) — plugins have their own lifecycle via Plugin::load()/unload().

Chosen approach: Add PluginRegistry as an optional field on AgentRuntime. Explicit, avoids naming conflicts, clean separation of plugin vs MCP server lifecycle.


Scope

  • ~6 files modified, 0-1 new files
  • Core changes in runtime.rs and mcp_plugin.rs
  • Bridge changes: 1-2 notification handlers in astrid_bridge.mjs
  • New dependency: astrid-plugins added to astrid-runtime/Cargo.toml
  • No new crates

Safety & Failure Modes

Scenario Mitigation
Plugin unloads while run_loop holds read lock on PluginRegistry Tool list is snapshotted before iteration. Execution of a just-unloaded tool returns "plugin tool not found" error to LLM — graceful degradation.
Plugin process killed mid-tool-call peer.call_tool() returns transport error, caught by existing ToolExecution error handling.
Config race (notification arrives before bridge handler ready) Bridge dispatch loop runs before handshake completes — notification handler is always ready.
Inbound message flood from plugin Bounded mpsc(256) with backpressure. Messages >1MB rejected at handler.
Concurrent sessions trigger before_agent_start simultaneously Peer is thread-safe. send_notification() is concurrent-safe. No mutex needed.
Plugin tool arguments from LLM Flow through same PreToolCall hook as all other tools — existing security interceptor applies.

Verification

  1. Unit tests: Extend PluginRegistry tests to cover tool list merging; add AgentRuntime test with mock plugin
  2. Integration test: Load Unicity plugin via gateway, verify:
    • Plugin tools appear in list_tools RPC response
    • Calling a plugin tool returns a result (not "server not found")
    • Plugin config is non-empty in bridge (add debug log)
    • before_agent_start hook fires and context is prepended
  3. End-to-end: Send a message through CLI → agent → Unicity channel → verify round-trip
  4. Run: cargo test --workspace -- --quiet to ensure no regressions

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/capsuleastrid-capsule: WASM sandboxing and executionarea/llmastrid-llm: LLM provider abstractionarea/mcpastrid-mcp: MCP bridge and tool dispatcharea/runtimeastrid-runtime: agent execution enginefeatNew feature or capabilityp1High — blocks meaningful progress

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions