plexus-core 0.3.4

Core infrastructure for Plexus RPC: Activation trait, DynamicHub, schemas
Documentation

hub-core

Core infrastructure for building Plexus RPC services with optional dynamic routing.

Overview

hub-core provides the foundation for building Plexus RPC services with hierarchical routing and schema introspection:

  • Activation - Trait for implementing Plexus RPC services/plugins
  • DynamicHub - Optional routing layer for hosting multiple activations under one namespace
  • PlexusMcpBridge - MCP server integration via rmcp
  • Handle - Typed references to plugin method results
  • hub-macro - Procedural macro for generating activation boilerplate

Key Insight: Any activation can be hosted directly as a Plexus RPC server. DynamicHub is just an activation with .register() - it's not required infrastructure.

Note: Plexus has been renamed to DynamicHub to clarify that it's an activation with dynamic registration, not special infrastructure. The Plexus type alias remains for backwards compatibility but is deprecated.

Quick Start

Single Activation (Direct Hosting)

For a single Plexus RPC service, host it directly without DynamicHub:

use hub_core::activations::echo::Echo;
use std::sync::Arc;

// Single activation - no DynamicHub needed
let echo = Arc::new(Echo::new());
// Use with hub-transport or your own Plexus RPC server

Multiple Activations (Composition)

For composing multiple Plexus RPC activations under one namespace, use DynamicHub:

use hub_core::plexus::DynamicHub;
use hub_core::{Activation, PlexusError};
use std::sync::Arc;

// Create a dynamic hub with explicit namespace and register your activations
let hub = Arc::new(
    DynamicHub::new("myapp")
        .register(MyActivation::new())
        .register(AnotherActivation::new())
);

// Route calls to activations
let stream = hub.route("myactivation.method", json!({})).await?;

When to use DynamicHub: Only when you need to compose multiple top-level activations. For a single service, host the activation directly.

Migration Note: DynamicHub::new() now requires an explicit namespace. Choose a namespace that identifies your application (e.g., "myapp", "substrate", "hub"). The Plexus type alias still works but is deprecated.

Creating Activations

Use the hub-macro crate to generate activation implementations:

use hub_macro::hub_methods;
use async_stream::stream;

#[derive(Clone)]
pub struct MyApp;

#[hub_methods(
    namespace = "myapp",
    version = "1.0.0",
    description = "My application"
)]
impl MyApp {
    /// Say hello
    #[hub_method]
    async fn hello(&self, name: String) -> impl Stream<Item = MyEvent> + Send + 'static {
        stream! {
            yield MyEvent::Greeting { message: format!("Hello, {}!", name) };
        }
    }
}

MCP Bridge

hub-core includes an MCP server bridge that exposes Plexus RPC activations as MCP tools.

Single Activation

use hub_core::{PlexusMcpBridge, activations::echo::Echo};
use std::sync::Arc;

// Bridge a single activation directly
let echo = Arc::new(Echo::new());
let bridge = PlexusMcpBridge::new(echo);
// Use with rmcp server

Multiple Activations (via DynamicHub)

use hub_core::{DynamicHub, PlexusMcpBridge};
use std::sync::Arc;

let hub = Arc::new(
    DynamicHub::new("myapp")
        .register(Echo::new())
        .register(MyApp::new())
);
let bridge = PlexusMcpBridge::new(hub);
// Use with rmcp server

Important: PlexusMcpBridge works with any activation. You can bridge a single activation directly, or use DynamicHub to expose multiple activations.

Architecture Patterns

Hub Activations

Any Plexus RPC activation can route to children by implementing ChildRouter. This enables nested method routing without DynamicHub:

  • Solar - Routes to planets (hardcoded children)
  • DynamicHub - Routes to registered activations (dynamic children via .register())
  • Your custom hub - Can route to any children you define
// Solar is a hub activation with hardcoded children
let solar = Arc::new(Solar::new());
// Supports: solar.mercury.info, solar.earth.luna.info

// DynamicHub is a hub activation with dynamic children
let hub = Arc::new(
    DynamicHub::new("app")
        .register(solar)
        .register(echo)
);
// Supports: app.solar.mercury.info, app.echo.echo

When to Use DynamicHub

Use DynamicHub when:

  • You need to compose multiple top-level Plexus RPC activations
  • You want dynamic registration (add activations at runtime)
  • You're building a multi-service Plexus RPC server (like substrate)

Don't use DynamicHub when:

  • You have a single Plexus RPC service (host it directly)
  • Your activation already routes to children (like Solar)
  • You want a simpler deployment

Direct Activation Hosting

The recommended pattern for single Plexus RPC services is direct hosting:

// Good: Direct hosting for single Plexus RPC service
let my_service = Arc::new(MyService::new());
TransportServer::builder(my_service, converter).serve().await?;

// Unnecessary: DynamicHub for single service
let hub = Arc::new(DynamicHub::new("app").register(MyService::new()));
TransportServer::builder(hub, converter).serve().await?;

Example Activations

hub-core includes two minimal example activations:

  • health - Health check endpoint (manual Activation impl)
  • echo - Echo messages back (hub-macro generated)

See src/activations/ for implementation examples.

License

AGPL-3.0-only