feat: OpenClaw foundation β MCP-first universal runtime#34
Merged
feat: OpenClaw foundation β MCP-first universal runtime#34
Conversation
β¦dge case tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add list_commands tool, rule management CRUD (add_rule/remove_rule/ update_rule), enriched tool descriptions, expanded ServerInfo instructions, optional mcp section in node.yaml templates, and Layer 5 + OpenClaw Integration in architecture docs. 17 MCP tools total, daemon-only (Enhanced Option B). Design rationale in .omc/plans/openclaw-foundation.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Validate node_name on all 9 MCP handlers to prevent Zenoh key expression injection. Fix silent condition parse failure that could turn conditional rules into unconditional ones. Add rule name and trigger pattern validation, MAX_RULES=100 limit, atomic file writes via tempfile, and sanitized error messages. New shared validation module with 6 tests. 301 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the ros-z abstraction layer with direct Zenoh API usage across
the entire codebase. This eliminates an unnecessary dependency and
simplifies the messaging layer.
Rust core:
- Remove ros-z from workspace and crate dependencies
- Create local MessageTypeName trait (replaces ros-z MessageTypeInfo)
- Update get_descriptor_for_message to use new trait
- Clean ros-z references from cli/debug.rs
Dashboard:
- Remove ros-z format parsing from zenoh.ts, schema-registry.ts,
subscription-manager.ts
- Simplify topic handling (no more %-encoding or 0/ prefix)
- Add 46 vanilla Zenoh test cases (536 total)
Templates & docs:
- Add liveliness token to Python node template
- Update all documentation to reference vanilla Zenoh topics
- Update topic format to bubbaloop/{scope}/{machine_id}/{node}/{resource}
34 files changed, 812 insertions, 5609 deletions (net -4797 lines)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add extern_path for Header in rust-node build.rs template so scaffolded nodes use the self-contained proto pattern. Remove unused @foxglove/ws-protocol dashboard dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comprehensive design document for transforming bubbaloop into an MCP-first universal runtime. Validated by critic, architect, and security reviewers (3 parallel Opus agents). Key decisions: - MCP as primary control plane (~22 tools), Zenoh as data plane - Security foundation (Phase 0): auth, RBAC, rate limiting, audit - Rule engine stays in daemon (real-time reactive system) - 4-phase migration with dashboard migration before Zenoh API removal - Ecosystem plan: Node SDK crate, community registry, contribution tiers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
29 bite-sized tasks across 4 phases: - Phase 0: Security foundation (9 tasks β auth, RBAC, validation) - Phase 1: MCP enhancement (9 tasks β PlatformOperations trait, 22 tools, stdio) - Phase 2: Dashboard migration + test harness (4 tasks β integration tests) - Phase 3: Cleanup + ecosystem (7 tasks β remove Zenoh API, TUI feature flag, SDK design) ~160 tests planned. TDD approach throughout. Exact file paths and code. References design doc: 2026-02-24-mcp-first-refactor-design.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Key corrections based on rmcp, MCP spec, and axum research: - rmcp stdio requires 'transport-io' feature flag - MCP spec: OAuth 2.1 OPTIONAL, stdio has NO auth (process boundary) - RBAC via ServerHandler::call_tool() override (not middleware) - tower-governor instead of broken tower::limit::RateLimitLayer - rmcp 0.16.0 upgrade consideration for task lifecycle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security fix: prevents remote access to dashboard server. Per CLAUDE.md convention: bind localhost only, never 0.0.0.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security fix: prevents PATH hijacking when reading node logs. Adds JOURNALCTL_PATH constant pointing to /usr/bin/journalctl. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CRITICAL security fix: Action::Publish topic and Action::Command node were completely unvalidated. Adds validate_publish_topic() and applies validate_node_name() to command actions. Prevents arbitrary Zenoh topic injection via rule engine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
β¦ machine Security fixes: - query_zenoh now requires key_expr to start with 'bubbaloop/' and rejects wildcard-only queries, preventing unrestricted Zenoh network scanning via MCP. - send_command and other node operations now use scoped key expressions (scope/machine_id) instead of wildcards, preventing cross-machine broadcast. - Rule action execute() receives scope and machine_id for scoped keys. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-generates token at ~/.bubbaloop/mcp-token (0600 perms) on first daemon start. Uses constant-time comparison to prevent timing attacks. Actual enforcement via call_tool() override in next task. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three permission levels: viewer (read-only), operator (day-to-day), admin (system modification). Tool-to-tier mapping defined in required_tier(). Unknown tools default to admin (least privilege). Replaces #[tool_handler] macro with manual ServerHandler impl to intercept call_tool() for RBAC checking before dispatch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every MCP tool invocation is logged with tool name and key parameters
via log::info!("[MCP] tool=<name> ...") for security audit trail.
Rate limiting configured at 100 requests/minute burst via tower_governor
0.8, applied as an axum layer on the MCP HTTP server. A background
cleanup thread retains only recent rate limit entries every 60 seconds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- RBAC: default to Admin tier (single-user localhost) until Phase 1 token-based tiers. Fixes query_zenoh being blocked for all callers. - RBAC: return ErrorData on permission denied (not CallToolResult::success) so MCP clients can properly detect authorization failures. - Rate limiter: replace std::thread::spawn with tokio task that respects shutdown signal, preventing thread leaks on daemon restart. - Rate limiter: replace .unwrap() with .ok_or() for safe error propagation. - Rate limiter: fix config to per_second(1) with burst_size(100) and correct the log message to reflect actual behavior. - Format: normalize formatting across touched files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
β¦form Clean abstraction between MCP server and daemon internals. DaemonPlatform wraps NodeManager+Zenoh for production use. MockPlatform enables contract testing without external dependencies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MCP server no longer directly accesses Arc<NodeManager> or Arc<Session>. All operations go through DaemonPlatform which implements PlatformOperations, enabling future mock-based contract testing. Replaced direct self.node_manager and self.session calls in all tool handlers with self.platform.* calls. Removed execute_daemon_command, zenoh_get_text, and now_ms helper methods (now in DaemonPlatform). The only remaining direct session access is send_command which needs a custom Zenoh query with payload via self.platform.session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables local AI agent integration via stdin/stdout JSON-RPC. Logs redirected to ~/.bubbaloop/mcp-stdio.log to avoid corrupting MCP protocol. No auth on stdio per MCP spec (process boundary provides trust). Also supports HTTP mode via `bubbaloop mcp -p <port>`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover PlatformOperations trait, input validation, RBAC mapping, and error handling against MockPlatform. No Zenoh or systemd needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New tools: get_stream_info, get_system_status, get_machine_info, build_node, get_node_schema, get_events, test_rule. All tools have audit logging and RBAC tier mappings. Added get_trigger_log() and test_rule() methods to Agent. Updated RBAC tests to cover all new tool tier assignments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reflects new tool categories: discovery, lifecycle, data, config, automation, system. Documents dual-plane model (MCP control, Zenoh data) in agent instructions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All Zenoh API queryables now log a deprecation warning on each call. These will be removed in Phase 3 after dashboard migration is complete. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make BubbaLoopMcpServer generic over P: PlatformOperations so tests can plug in MockPlatform without requiring Zenoh/systemd. The server defaults to DaemonPlatform for production code. Key changes: - Add send_zenoh_query() to PlatformOperations trait, refactoring send_command tool to use it instead of accessing session directly - Make BubbaLoopMcpServer<P> generic with default type parameter - Add test-harness feature that exposes MockPlatform and enables rmcp client for in-process duplex transport testing - Create tests/integration_mcp.rs with TestHarness that spins up a real MCP client-server pair over tokio::io::duplex The harness exercises the full MCP stack (serialization, RBAC, tool routing) and is designed to scale to ~30 tests in the next task. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Resolve Cargo.toml, lib.rs, and dashboard merge conflicts - Increase duplex buffer from 8KB to 64KB for headroom - Replace .unwrap() with .expect() in test harness call_with_args Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover nonexistent nodes (start/stop/restart/build), invalid key expressions (missing prefix, wildcard-only), missing node config, get_node_manifest, list_commands, and agent-unavailable paths (add_rule, remove_rule, update_rule, test_rule). Total integration tests: 35. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Outlines bubbaloop-node-sdk crate: Node trait, NodeConfig derive macro, boilerplate handling (Zenoh, health, schema, config, shutdown). Contributors write ~50 lines of business logic instead of ~300. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zenoh_api.rs removed. All external access now goes through MCP tools. Daemon retains Zenoh pub/sub service and agent rule engine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CLI commands still work but log deprecation warnings pointing to equivalent MCP tools. Core commands (daemon, mcp, doctor, status) unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TUI code gated by --features tui. Not included in default build. Reduces binary size and dependency count. Dashboard + MCP replace TUI for normal use; TUI kept for SSH debugging scenarios. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MCP is fundamental to bubbaloop's architecture β it should always be available, not opt-in. Removed all #[cfg(feature = "mcp")] gates. rmcp, schemars, and tower_governor are now unconditional dependencies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
β¦noh pub/sub, enrich manifests - Delete agent rule engine (1,265 lines) β automation belongs in OpenClaw/AI agent layer - Delete ZenohService (677 lines) β MCP replaces Zenoh state pub/sub - Relocate create_session() to daemon/mod.rs - Enrich NodeManifest with capabilities, publishes, subscribes, commands, requires, metadata - Add Capability enum (Sensor, Actuator, Processor, Gateway) - Add TopicSpec, CommandSpec, Requirements types for self-describing nodes - Remove agent from daemon startup, MCP server, and all tool handlers - Remove 6 agent MCP tools (create_automation, list_automations, etc.) - Simplify daemon: registry + lifecycle + health + MCP only - Update templates with rich manifest fields - Update official nodes (openmeteo, rtsp-camera, system-telemetry, network-monitor) - Create OpenClaw integration: SKILL.md, openclaw.json.example, README.md - 2,471 lines removed, 249 lines added across 17 files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update daemon doc: "Bubbaloop Skill Runtime" with clear architecture description - Remove dead duplicate daemon detection (queried deleted ZenohService) - Update startup logs: "skill runtime started", node count, health monitor - Update CLAUDE.md: daemon as passive skill runtime, no agent engine - Update ARCHITECTURE.md: remove agent tools, add "passive runtime" concept - Update README.md: replace agent section with external automation pattern - Update ROADMAP.md: mark agent removal, add skill runtime milestone Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add get_cached_manifests() to NodeManager for static manifest collection - Add get_manifests() to PlatformOperations trait with capability filtering - Add discover_capabilities MCP tool: group nodes by capability type - Update get_node_manifest to use cached manifests instead of Zenoh queries - Add DiscoverCapabilitiesParams for optional capability filter - Both new tools at Viewer RBAC tier (read-only) - Update MockPlatform with test manifest data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The get_node_manifest MCP tool now reads from cached manifests via get_manifests() instead of issuing Zenoh queries. Updated the integration test assertion to match the new behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ship the Node SDK that reduces node authoring from ~300 lines of boilerplate to ~50 lines of business logic. The SDK handles Zenoh session creation (client mode enforced), health heartbeat (5s interval), schema queryable registration, config loading, signal handling, and graceful shutdown. Rust templates now generate SDK-based nodes instead of full-boilerplate scaffolds. The old 527-line node.rs.template is replaced by a lean 98-line main.rs.template with the Node trait + run_node() pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CLAUDE.md: add bubbaloop-node-sdk to structure and key files - skillet-development.md: change "Future: Node SDK" to "Node SDK (Recommended)" - create-your-first-node.md: show SDK-based scaffold and Node trait - plugin-development.md: SDK as primary Quick Start path - README.md: note SDK in Node Lifecycle section - node-sdk-design.md: mark status as Shipped All docs now present the SDK as the primary contributor workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- skillet-development.md: change "Implementation pending" to "Shipped" - plugin-development.md: replace phantom BubbleNode/ZContext/NodeError API with correct patterns and references to skillet-development.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Where Nodes Live" section to skillet-development.md explaining the separate-repo model. Add "Where to Create Your Node" to the getting started guide. Fix broken node-marketplace.md links. Add crates/bubbaloop-nodes/ to .gitignore (stale build artifacts). Update plugin-development.md integration section for standalone repos. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The marketplace guide exists at docs/guides/node-marketplace.md but was accidentally orphaned when fixing the broken relative path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add install_node and remove_node MCP tools to PlatformOperations trait, DaemonPlatform, MockPlatform, and MCP server. This enables AI agents (OpenClaw) to manage the full node lifecycle: install β build β start β stop β remove. Dead code cleanup: - Remove unused API_PREFIX constant from TUI client - Remove deprecated --strict daemon flag and parameter - Remove stale cameras/inference/openmeteo pixi tasks (nodes moved to separate repos) - Remove stale process-compose.yaml node processes - Remove legacy TUI task references from pixi.toml - Clean phantom RBAC entries for unimplemented tools (list_topics, doctor, set_node_config, read_sensor, create_node_instance, set_system_config) 287 unit tests + 28 integration tests pass, zero clippy warnings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 new tests exercising the full MCP transport path through MockPlatform: - Valid source (local path + GitHub shorthand) - Empty source rejection - Shell metacharacter injection prevention (;, |, &) - Existing node removal with post-removal verification - Nonexistent node error handling - Path traversal name validation (../etc/passwd) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- node-marketplace.md: add install flow diagram (7 steps), on-disk layout tree, remove stale inference node, add multi-instance section - quickstart.md: replace stale npm TUI steps with working camera install flow (install β configure β instance β verify) - skillet-development.md: add "How bubbaloop node install Works" section with on-disk result and dev symlink pattern Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
β¦nd simplification - Fix auth token write-then-chmod race with atomic OpenOptions::mode(0o600) - Eliminate TOCTOU in load_or_generate_token, refactor to accept path param - Replace install_node denylist (;|&) with allowlist validation in validation module - Propagate Zenoh config errors in daemon (was .ok(), now .expect()) - Add max 30 retries to daemon create_session (was infinite loop) - Deduplicate MCP port parsing in daemon run() - Document RBAC as deferred (phase-2), add startup warning - Fix MockPlatform to record command variant in execute_command - Remove tempfile from prod dependencies (kept in dev-dependencies) - Log schema query reply failures in node SDK instead of silencing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert discovered string topics to {display, raw} format before merging
with static topics to match the component prop types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dashboard now uses useZenohSubscriptionContext for topic discovery, so tests need the context mocked to avoid provider-not-found errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The component renders "Select a topic to start receiving data" but the test expected "No topic selected". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Refactors bubbaloop into an MCP-first universal runtime where AI agents (OpenClaw, Claude, etc.) manage physical sensors exclusively through MCP tools.
bubbaloop-node-sdkcrate reduces node boilerplate from ~300 to ~50 linesdiscover_capabilitiesMCP tool groups nodes by type (sensor, actuator, compute)install_nodeβstart_nodeβget_node_logsβstop_nodeβremove_nodeKey architectural changes
tuifeature flagTest plan
pixi run checkβ cargo check passespixi run clippyβ zero warningspixi run testβ 287 unit tests passcargo test --features test-harness --test integration_mcpβ 35 integration tests passbubbaloop node install rtsp-cameraβ marketplace install worksbubbaloop node start/stop/logsβ full lifecycle worksbubbaloop mcp --stdioresponds to tool callsπ€ Generated with Claude Code