-
Notifications
You must be signed in to change notification settings - Fork 0
[kata/root/M015/S05] Plugin Documentation Update #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,123 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: peer-registry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Peer discovery, registration, and cross-instance signal forwarding. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Use when configuring multi-machine deployments where multiple Assay | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instances need to discover each other and forward signals across hosts. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Peer Registry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Register, discover, and forward signals between Assay instances running on different machines. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Each Assay MCP server can register itself as a **peer** in the state backend. Other instances query the peer registry to discover where to forward signals for sessions they don't own locally. This enables multi-machine orchestration without a central message broker. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ┌──────────────┐ ┌──────────────┐ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ Machine A │ │ Machine B │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ assay-mcp │◄───────►│ assay-mcp │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ :7432 │ HTTP │ :7432 │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ │ forward │ │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ worker-1 │ │ worker-2 │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ worker-3 │ │ orchestrator│ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| └──────────────┘ └──────────────┘ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| │ │ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| └────────┬───────────────┘ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| peers.json (or Smelt API) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## PeerInfo Type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Each registered peer is a `PeerInfo` record: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "peer_id": "machine-a", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "signal_url": "http://192.168.1.10:7432", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "registered_at": "2026-03-29T12:00:00Z" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Field | Type | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | --- | --- | --- | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `peer_id` | `String` | Unique identifier (typically hostname or UUID) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `signal_url` | `String` | HTTP endpoint for the signal server | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `registered_at` | `DateTime<Utc>` | When this peer was registered | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Backend Methods | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The `StateBackend` trait provides three peer registry methods with default no-op implementations: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Method | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | --- | --- | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `register_peer(peer: &PeerInfo)` | Upsert a peer entry (by `peer_id`) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `list_peers()` | Return all registered peers | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `unregister_peer(peer_id: &str)` | Remove a peer entry (idempotent) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### LocalFsBackend | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Stores peers in `{assay_dir}/peers.json`. Writes are atomic (temp file + rename). Suitable for single-machine multi-process setups where all Assay instances share the same `.assay/` directory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### SmeltBackend | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Registers peers by POSTing `PeerInfo` JSON to `{smelt_url}/api/v1/peers`. Graceful degradation — registration failure logs a warning but does not abort startup. `list_peers` and `unregister_peer` use the default no-op implementations (Smelt manages peer lifecycle server-side). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Other Backends | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `NoopBackend`, `LinearBackend`, `GitHubBackend`, and `SshSyncBackend` all return `supports_peer_registry: false` and use the default no-op implementations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Automatic Lifecycle | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The MCP server manages peer registration automatically: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1. **On startup** — after the signal endpoint binds, the server calls `register_peer` with its hostname and `signal_url` derived from `ASSAY_SIGNAL_BIND` and `ASSAY_SIGNAL_PORT`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1. **On startup** — after the signal endpoint binds, the server calls `register_peer` with its hostname and `signal_url` derived from `ASSAY_SIGNAL_BIND` and `ASSAY_SIGNAL_PORT`. | |
| 1. **On startup** — after the signal endpoint binds, the server calls `register_peer` with its hostname and `signal_url`. If `ASSAY_SIGNAL_URL` is set, that value is used directly. Otherwise, `signal_url` is derived from `ASSAY_SIGNAL_BIND` and `ASSAY_SIGNAL_PORT` (note that `0.0.0.0` is not routable from other machines, so in multi-machine deployments you should set `ASSAY_SIGNAL_URL` to a reachable host or IP). |
Copilot
AI
Mar 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs state that SmeltBackend supports peer registry / cross-instance forwarding, but the code currently reports supports_peer_registry: false for SmeltBackend (and does not implement list_peers/unregister_peer). As written, using SmeltBackend will disable forwarding because the signal server gates forwarding on capabilities().supports_peer_registry. Please update the docs to match current behavior, or (if the intended behavior is support) update the backend capability flag and implementations in code.
| Registers peers by POSTing `PeerInfo` JSON to `{smelt_url}/api/v1/peers`. Graceful degradation — registration failure logs a warning but does not abort startup. `list_peers` and `unregister_peer` use the default no-op implementations (Smelt manages peer lifecycle server-side). | |
| ### Other Backends | |
| `NoopBackend`, `LinearBackend`, `GitHubBackend`, and `SshSyncBackend` all return `supports_peer_registry: false` and use the default no-op implementations. | |
| ## Automatic Lifecycle | |
| The MCP server manages peer registration automatically: | |
| 1. **On startup** — after the signal endpoint binds, the server calls `register_peer` with its hostname and `signal_url` derived from `ASSAY_SIGNAL_BIND` and `ASSAY_SIGNAL_PORT`. | |
| 2. **On clean shutdown** — the server calls `unregister_peer` to remove itself. | |
| No manual registration is needed for normal operation. | |
| As of this version, `SmeltBackend` reports `supports_peer_registry: false` and relies on the default no-op implementations of `register_peer`, `list_peers`, and `unregister_peer`. Deployments using `SmeltBackend` do not currently participate in the peer registry or cross-instance signal forwarding. | |
| ### Other Backends | |
| `SmeltBackend`, `NoopBackend`, `LinearBackend`, `GitHubBackend`, and `SshSyncBackend` all return `supports_peer_registry: false` and use the default no-op implementations. | |
| ## Automatic Lifecycle | |
| When using a backend that reports `supports_peer_registry: true` (for example, `LocalFsBackend`), the MCP server manages peer registration automatically: | |
| 1. **On startup** — after the signal endpoint binds, the server calls `register_peer` with its hostname and `signal_url` derived from `ASSAY_SIGNAL_BIND` and `ASSAY_SIGNAL_PORT`. | |
| 2. **On clean shutdown** — the server calls `unregister_peer` to remove itself (this is a no-op for backends like `SmeltBackend` that do not support the peer registry). | |
| No manual registration is needed for normal operation when the selected backend supports the peer registry. |
Copilot
AI
Mar 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The environment variable table omits ASSAY_SIGNAL_URL, but the server supports it and it is required for correct peer registration when binding ASSAY_SIGNAL_BIND=0.0.0.0 (otherwise peers get an unroutable URL). Please document ASSAY_SIGNAL_URL here (and its relationship to ASSAY_SIGNAL_BIND/ASSAY_SIGNAL_PORT).
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||||
| #!/usr/bin/env bash | ||||||||||||
| # verify-docs.sh — Structural test for smelt-agent plugin documentation. | ||||||||||||
| # | ||||||||||||
| # Checks that MCP tool names referenced in plugin docs exist in the | ||||||||||||
| # assay-mcp router (server.rs). Exits non-zero on any mismatch. | ||||||||||||
| # | ||||||||||||
| # Usage: bash plugins/smelt-agent/tests/verify-docs.sh | ||||||||||||
|
|
||||||||||||
| set -euo pipefail | ||||||||||||
|
|
||||||||||||
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||||||||||||
| PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" | ||||||||||||
| REPO_ROOT="$(cd "$PLUGIN_DIR/../.." && pwd)" | ||||||||||||
|
|
||||||||||||
| SERVER_RS="$REPO_ROOT/crates/assay-mcp/src/server.rs" | ||||||||||||
|
|
||||||||||||
| if [ ! -f "$SERVER_RS" ]; then | ||||||||||||
| echo "ERROR: server.rs not found at $SERVER_RS" | ||||||||||||
| exit 1 | ||||||||||||
| fi | ||||||||||||
|
|
||||||||||||
| # Extract MCP tool names from the router (pub async fn declarations in the | ||||||||||||
| # #[tool_router] impl block). These are the canonical tool names. | ||||||||||||
| ROUTER_TOOLS=$(grep 'pub async fn' "$SERVER_RS" \ | ||||||||||||
| | grep -oE 'fn [a-z_]+' \ | ||||||||||||
| | sed 's/fn //' \ | ||||||||||||
| | grep -v '^serve$' \ | ||||||||||||
| | sort -u) | ||||||||||||
|
|
||||||||||||
| # Extract tool names referenced in the MCP Tools table in AGENTS.md. | ||||||||||||
| # Table rows look like: | `tool_name` | description | | ||||||||||||
| DOC_TOOLS=$(grep -oE '`[a-z_]+`' "$PLUGIN_DIR/AGENTS.md" \ | ||||||||||||
| | tr -d '`' \ | ||||||||||||
| | sort -u) | ||||||||||||
|
|
||||||||||||
| # Known tools that exist on feature branches but not yet on main. | ||||||||||||
| # These are documented in advance of the M015 merge and will be | ||||||||||||
| # validated once M015 lands. | ||||||||||||
| PENDING_TOOLS="" | ||||||||||||
|
|
||||||||||||
|
Comment on lines
+37
to
+40
|
||||||||||||
| # These are documented in advance of the M015 merge and will be | |
| # validated once M015 lands. | |
| PENDING_TOOLS="poll_signals send_signal" | |
| # Currently none; add tool names here temporarily if docs lead server.rs. | |
| PENDING_TOOLS="" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This says signals are consumed on read with “exactly-once delivery”, but the underlying inbox polling is best-effort and can deliver duplicates if file deletion fails (it logs a warning: “may be delivered twice”). Also,
poll_signalscan consume-but-skip malformed messages during JSON decode. Please soften this claim (e.g., at-least-once / best-effort) to match actual semantics.