diff --git a/README.md b/README.md index b3d79d1..c608427 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ slop/ │ │ │ ├── svelte/ # @slop-ai/svelte — useSlop for Svelte 5 runes │ │ │ └── tanstack-start/ # @slop-ai/tanstack-start — SSR adapter │ │ └── integrations/ -│ │ ├── discovery/ # @slop-ai/discovery — provider discovery + agent tool helpers +│ │ ├── discovery/ # @slop-ai/discovery — bridge/discovery primitives with /service and /tools entrypoints │ │ ├── claude/ # Claude Code plugins (native + MCP proxy) │ │ ├── codex/ # Codex plugin (MCP bridge + skill) │ │ └── openclaw-plugin/ # @slop-ai/openclaw-plugin — OpenClaw integration diff --git a/docs/api/index.md b/docs/api/index.md index c2fe130..75b5dba 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -16,7 +16,7 @@ This page maps every published package in the repo to its primary use case and t | `@slop-ai/server` | server, desktop, and CLI providers | `bun add @slop-ai/server` | [API](/api/server), [Guide](/guides/server-apps) | | `@slop-ai/consumer` | custom agents, inspectors, and bridges | `bun add @slop-ai/consumer` | [API](/api/consumer), [Guide](/guides/consumer) | | `@slop-ai/tanstack-start` | TanStack Start full-stack adapter | `bun add @slop-ai/server @slop-ai/tanstack-start` | [API](/api/tanstack-start), [Guide](/guides/tanstack-start) | -| `@slop-ai/discovery` | auto-discovery, bridge, relay, AI tool formatting | `bun add @slop-ai/discovery` | [SDK docs](/sdk/discovery) | +| `@slop-ai/discovery` | bridge and discovery primitives; add `/service` and `/tools` for higher-level helpers | `bun add @slop-ai/discovery` | [SDK docs](/sdk/discovery) | | `@slop-ai/openclaw-plugin` | OpenClaw integration | `bun add @slop-ai/openclaw-plugin` | [API](./openclaw-plugin.md), [Guide](../guides/advanced/openclaw.md) | ## Other SDKs diff --git a/docs/api/openclaw-plugin.md b/docs/api/openclaw-plugin.md index 0edb3a9..5301541 100644 --- a/docs/api/openclaw-plugin.md +++ b/docs/api/openclaw-plugin.md @@ -22,7 +22,7 @@ openclaw plugins install @slop-ai/openclaw-plugin ## How it works -The plugin uses `@slop-ai/discovery` for provider discovery (local dirs, bridge, relay) and for shared tool handlers (`createToolHandlers`, etc.). +The plugin uses `@slop-ai/discovery/service` for provider discovery and connection management, and `@slop-ai/discovery/tools` for shared lifecycle tool handlers. ### State injection diff --git a/docs/guides/advanced/claude-code.md b/docs/guides/advanced/claude-code.md index 197b624..5b83c9c 100644 --- a/docs/guides/advanced/claude-code.md +++ b/docs/guides/advanced/claude-code.md @@ -141,7 +141,7 @@ Server-backed web apps ──direct WebSocket─────┤ Browser SPAs ──postMessage──Extension─────────┤ │ (relay via bridge) │ ├── state.json ──→ hook ──→ Claude context │ │ - @slop-ai/discovery ──────────┘ ├── native: dynamic tools via tools/list_changed + @slop-ai/discovery/* ────────┘ ├── native: dynamic tools via tools/list_changed └── mcp-proxy: fixed tools (app_action, app_action_batch) ``` @@ -157,7 +157,7 @@ The state file includes a `lastUpdated` timestamp. The hook skips injection if t OpenClaw's plugin SDK does not support runtime tool registration — tools must be declared in the plugin manifest and registered once during plugin initialization. The OpenClaw plugin uses meta-tools (`app_action`, `app_action_batch`) instead, with state injection via `before_prompt_build` to give the model full context. See [OpenClaw integration](/guides/advanced/openclaw) for details. -All three integrations share the same underlying `@slop-ai/discovery` package. `claude-slop-native` additionally uses `createDynamicTools()` to expose first-class tools. +All three integrations share the same underlying discovery stack. They use `@slop-ai/discovery/service` for connection orchestration, and `claude-slop-native` additionally uses `@slop-ai/discovery/tools` to expose first-class tools. ## Related diff --git a/docs/guides/advanced/codex.md b/docs/guides/advanced/codex.md index 63a8c04..b563f0e 100644 --- a/docs/guides/advanced/codex.md +++ b/docs/guides/advanced/codex.md @@ -13,7 +13,7 @@ It gives Codex five stable MCP tools for discovering and controlling SLOP-enable ## What it does - **Discovers** SLOP apps from local provider descriptors and the browser extension bridge -- **Connects** to Unix socket, WebSocket, and relay-backed providers through `@slop-ai/discovery` +- **Connects** to Unix socket, WebSocket, and relay-backed providers through `@slop-ai/discovery/service` - **Injects live state** for connected apps on each future user prompt through a Codex hook - **Returns an immediate snapshot** through `connect_app`, including the current state tree and available actions - **Acts through stable meta-tools** so Codex has a predictable tool surface @@ -46,7 +46,7 @@ The plugin's `.mcp.json` starts a local stdio MCP server: - cwd: `./servers` - entrypoint: `./dist/slop-bridge.bundle.mjs` -That bridge wraps `@slop-ai/discovery` and exposes a fixed five-tool surface to Codex. +That bridge wraps `@slop-ai/discovery/service` and `@slop-ai/discovery/tools` and exposes a fixed five-tool surface to Codex. ### Hook-based state injection @@ -106,7 +106,7 @@ The Codex integration currently chooses the same fixed-tool model as the OpenCla - the hook can inject live state without rebuilding tools on every patch - the skill can reliably teach the connect-once, inspect, then act workflow -When Codex support for dynamic per-affordance tool flows becomes desirable, the same `@slop-ai/discovery` layer can be extended in that direction. +When Codex support for dynamic per-affordance tool flows becomes desirable, the same discovery stack can extend in that direction by adding `@slop-ai/discovery/tools` dynamic tool mapping. ## Example interaction diff --git a/docs/guides/advanced/openclaw.md b/docs/guides/advanced/openclaw.md index 64147fa..0e30b9c 100644 --- a/docs/guides/advanced/openclaw.md +++ b/docs/guides/advanced/openclaw.md @@ -10,7 +10,7 @@ ## What it does -- **Discovers** SLOP apps — local native apps, WebSocket servers, and browser tabs via the extension bridge (powered by `@slop-ai/discovery`) +- **Discovers** SLOP apps — local native apps, WebSocket servers, and browser tabs via the extension bridge (powered by `@slop-ai/discovery/service`) - **Injects state** into the prompt before each inference via `before_prompt_build` — the model sees live app state without calling any tool - **Exposes actions** through `app_action` and `app_action_batch` meta-tools - **Supports multiple apps** connected simultaneously @@ -71,7 +71,7 @@ The model knows what state exists and what actions are available without calling ### Discovery -The plugin uses `@slop-ai/discovery` for provider discovery, which covers: +The plugin uses `@slop-ai/discovery/service` for provider discovery and connection management, which covers: - `~/.slop/providers/` — persistent user-level providers - `/tmp/slop/providers/` — session-level ephemeral providers @@ -111,7 +111,7 @@ The workaround is the **meta-tool pattern**: five stable tools (`list_apps`, `co If OpenClaw adds a runtime tool registration API (e.g., `api.registerDynamicTools()` or `api.updateToolList()`), the plumbing is already in place: -- `createDynamicTools(discovery)` from `@slop-ai/discovery` generates namespaced tool definitions from all connected providers +- `createDynamicTools(discovery)` from `@slop-ai/discovery/tools` generates namespaced tool definitions from all connected providers - Each tool maps to `{ providerId, path, action }` via a `resolve()` function - The same helper is already used by the Claude Code MCP server @@ -126,7 +126,7 @@ If OpenClaw adds a runtime tool registration API (e.g., `api.registerDynamicTool | List tool | `list_apps` | `list_apps` | | Connect tool | `connect_app` | `connect_app` | | Disconnect tool | `disconnect_app` | `disconnect_app` | -| Discovery | `@slop-ai/discovery` | `@slop-ai/discovery` | +| Discovery/runtime | `@slop-ai/discovery/service` + `@slop-ai/discovery/tools` | `@slop-ai/discovery/service` | | Bridge support | Yes | Yes | | Staleness protection | 30s timestamp check | Not needed (in-process) | diff --git a/docs/sdk/discovery.md b/docs/sdk/discovery.md index fc9456f..87a14b0 100644 --- a/docs/sdk/discovery.md +++ b/docs/sdk/discovery.md @@ -33,10 +33,12 @@ Integrations (Claude Code plugin, OpenClaw plugin, VS Code extension, custom age | Entry | Purpose | |---|---| -| `@slop-ai/discovery` | **Default.** Discovery service, bridge, relay, CLI, and agent-agnostic helpers: `createToolHandlers`, `createDynamicTools`, `createStateCache`, etc. No Anthropic-specific dependency at import time for these APIs. | +| `@slop-ai/discovery` | **Default.** Bridge and discovery primitives: `createBridgeClient`, `createBridgeServer`, `BridgeRelayTransport`, and shared descriptor types. | +| `@slop-ai/discovery/service` | Discovery service / connection orchestration: `createDiscoveryService`, `DiscoveryService`, `ConnectedProvider`, etc. | +| `@slop-ai/discovery/tools` | Agent-agnostic lifecycle and affordance helpers: `createToolHandlers`, `createDynamicTools`, and tool result types. | | `@slop-ai/discovery/anthropic-agent-sdk` | **Optional.** `createSlopAgentTools` and `createSlopMcpServer` for [`@anthropic-ai/claude-agent-sdk`](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) (`query()`, programmatic MCP). Use only when wiring the Anthropic Agent SDK. | -Integrations that only need discovery + MCP (e.g. `slop-bridge` with the MCP SDK) import from the **default** export and do not need `anthropic-agent-sdk`. +Integrations compose these entrypoints: `@slop-ai/discovery/service` for provider scanning and connection orchestration, `@slop-ai/discovery/tools` when they need lifecycle or dynamic-tool helpers, and the root export when they need direct bridge primitives. ## Phase 1: Core Discovery Layer @@ -69,13 +71,12 @@ Phase 1 includes: - State change callback - `formatTree()` - `affordancesToTools()` -- `createToolHandlers()` -- `createDynamicTools()` Phase 1 explicitly excludes: - Host-specific wrappers such as `cli.ts` and `anthropic-agent-sdk.ts` - Prompt/hook integration glue for Claude Code, Codex, OpenClaw, or other hosts +- TypeScript-only integration helpers such as `@slop-ai/discovery/tools` - File-cache helpers such as `createStateCache()` ### Status labels @@ -256,6 +257,13 @@ Converts all affordances in a state tree into LLM tool definitions: - Returns a `resolve(toolName)` function that maps tool names back to `{ path, action }` for `invoke` - Tool descriptions include the node path, action label, and `[DANGEROUS]` flag +### `@slop-ai/discovery/tools` + +TypeScript-only optional helper subpath for host integrations. It exports: + +- `createToolHandlers(discovery)` for shared lifecycle/meta-tool handlers +- `createDynamicTools(discovery)` for dynamic affordance-to-tool mapping + ### `createDynamicTools(discovery)` Builds namespaced tool definitions from all connected providers' affordances. Each tool name is prefixed with the provider's ID to avoid cross-app collisions: @@ -286,7 +294,7 @@ Consumers can use this to maintain a cache, update a UI, or trigger context inje | Language | Consumer SDK | Core discovery module | Current status | |---|---|---|---| -| TypeScript | `@slop-ai/consumer` | `@slop-ai/discovery` | Reference implementation for phase 1, plus host-specific helpers outside the shared contract | +| TypeScript | `@slop-ai/consumer` | `@slop-ai/discovery/service` | Reference implementation for phase 1, plus optional root and `/tools` entrypoints for host integrations | | Python | `slop-ai` | `slop_ai.discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | | Go | `slop-ai` | `github.com/devteapot/slop/packages/go/slop-ai/discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | | Rust | `slop-ai` | `slop_ai::discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | @@ -311,10 +319,7 @@ Consumers can use this to maintain a cache, update a UI, or trigger context inje | State change callback | Shipped | Shipped | Shipped | Shipped | | `formatTree()` | Shipped | Shipped | Shipped | Shipped | | `affordancesToTools()` | Shipped | Shipped | Shipped | Shipped | -| `createToolHandlers()` | Shipped | Shipped | Shipped | Shipped | -| `createDynamicTools()` | Shipped | Shipped | Shipped | Shipped | -| Host wrappers and prompt injection glue | Shipped | Out of scope | Out of scope | Out of scope | -| State cache / shared file helpers | Shipped | Out of scope | Out of scope | Out of scope | +| TypeScript-only `@slop-ai/discovery/tools` helpers | Shipped | Out of scope | Out of scope | Out of scope | ### Known donor-code drift @@ -355,9 +360,7 @@ A complete phase-1 discovery layer implementation provides: - [ ] Exponential backoff reconnection - [ ] Connection timeout (10 seconds) - [ ] State change callback -- [ ] Dynamic tool generation (`createDynamicTools()` equivalent) - -Host-specific wrappers and prompt injection are intentionally outside phase 1. +Host-specific wrappers, tool-helper layers, and prompt injection are intentionally outside phase 1. ## Integrations @@ -365,13 +368,13 @@ Both the Claude Code and OpenClaw plugins follow the same design principles: - **State injection** — Provider state is injected into the model's context before each turn, not fetched via tool calls - **Minimal tool usage** — Tools are used only for connecting to apps and performing actions, never for reading state -- **Shared discovery** — Both import `@slop-ai/discovery` for provider scanning, bridge, and relay +- **Shared discovery** — Both build on `@slop-ai/discovery/service` for provider scanning and connection orchestration Where they differ is **action dispatch**, due to host platform limitations. ### Dynamic tool injection -When a host supports runtime tool registration, the discovery layer can expose each affordance as a first-class tool. `createDynamicTools(discovery)` generates namespaced tool definitions from all connected providers: +When a host supports runtime tool registration, `@slop-ai/discovery/tools` can expose each affordance as a first-class tool. `createDynamicTools(discovery)` generates namespaced tool definitions from all connected providers: ``` kanban__backlog__add_card({title: "Ship docs"}) ← model calls this directly @@ -408,7 +411,7 @@ Design details: - **Fixed tool surface** — The Codex plugin exposes the same stable five-tool catalog as the OpenClaw plugin. - **Hook-based state injection** — The bridge writes provider state to `/tmp/codex-slop-plugin/state.json` on every state change. The `UserPromptSubmit` hook reads that file and injects fresh markdown into future turns. - **Immediate snapshot on connect** — `connect_app` still returns the current tree and actions right away, so Codex can act in the same turn it first connects. -- **Discovery** — Uses `@slop-ai/discovery` with local descriptor watching plus browser bridge support. +- **Discovery** — Uses `@slop-ai/discovery/service` with local descriptor watching plus browser bridge support. - **Multi-app** — Multiple providers can remain connected concurrently; `app_action` and `app_action_batch` resolve against the requested app ID. See [Codex guide](/guides/advanced/codex) for setup and usage. @@ -417,8 +420,8 @@ See [Codex guide](/guides/advanced/codex) for setup and usage. | Variant | Purpose | |---|---| -| **`claude-slop-native`** | Wraps `createDiscoveryService` + `createDynamicTools` from `@slop-ai/discovery`. Registers dynamic per-app tools via `tools/list_changed`. Static tools: `list_apps`, `connect_app`, `disconnect_app`. | -| **`claude-slop-mcp-proxy`** | Wraps `createDiscoveryService` from `@slop-ai/discovery`, but keeps a fixed tool catalog: `list_apps`, `connect_app`, `disconnect_app`, `app_action`, `app_action_batch`. | +| **`claude-slop-native`** | Wraps `createDiscoveryService` from `@slop-ai/discovery/service` and `createDynamicTools` from `@slop-ai/discovery/tools`. Registers dynamic per-app tools via `tools/list_changed`. Static tools: `list_apps`, `connect_app`, `disconnect_app`. | +| **`claude-slop-mcp-proxy`** | Wraps `createDiscoveryService` from `@slop-ai/discovery/service`, but keeps a fixed tool catalog: `list_apps`, `connect_app`, `disconnect_app`, `app_action`, `app_action_batch`. | | **Shared hook** (`UserPromptSubmit`) | Reads a shared state file and injects connected providers' state trees into Claude's context on every user message — no MCP fetch needed. Also lists discovered-but-not-connected apps. | | **Shared skill** (`slop-connect`) | Teaches Claude the list → connect → inspect → act workflow. | @@ -443,7 +446,7 @@ Design details: - **Meta-tool pattern** — OpenClaw's plugin SDK requires tools to be declared upfront in `openclaw.plugin.json` and registered once. Dynamic tool registration is not supported. Actions go through `app_action(app, path, action, params)` instead of per-app tools. - **State injection** — The `before_prompt_build` hook returns `{ prependContext: stateMarkdown }`, which OpenClaw prepends to the conversation before inference. No file-based IPC needed (in-process). -- **Discovery** — Uses `@slop-ai/discovery` with bridge support. Discovers local providers, session providers, and browser tabs via extension bridge. +- **Discovery** — Uses `@slop-ai/discovery/service` with bridge support. Discovers local providers, session providers, and browser tabs via extension bridge. See [OpenClaw guide](/guides/advanced/openclaw) for setup and usage. diff --git a/packages/typescript/integrations/claude/slop-mcp-proxy/README.md b/packages/typescript/integrations/claude/slop-mcp-proxy/README.md index 0906b6d..88a3ceb 100644 --- a/packages/typescript/integrations/claude/slop-mcp-proxy/README.md +++ b/packages/typescript/integrations/claude/slop-mcp-proxy/README.md @@ -22,7 +22,7 @@ Server-backed web apps ──direct WebSocket─────┤ Browser SPAs ──postMessage──Extension─────────┤ (relay via bridge) │ │ - @slop-ai/discovery SDK ──────┘ + @slop-ai/discovery/* SDK ────┘ ``` ## Setup diff --git a/packages/typescript/integrations/claude/slop-mcp-proxy/servers/slop-bridge.mjs b/packages/typescript/integrations/claude/slop-mcp-proxy/servers/slop-bridge.mjs index 1588e96..46779c4 100644 --- a/packages/typescript/integrations/claude/slop-mcp-proxy/servers/slop-bridge.mjs +++ b/packages/typescript/integrations/claude/slop-mcp-proxy/servers/slop-bridge.mjs @@ -20,8 +20,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; -import { createDiscoveryService } from "@slop-ai/discovery"; -import { createToolHandlers } from "@slop-ai/discovery"; +import { createDiscoveryService } from "@slop-ai/discovery/service"; +import { createToolHandlers } from "@slop-ai/discovery/tools"; import { formatTree } from "@slop-ai/consumer"; import fs from "node:fs"; import path from "node:path"; diff --git a/packages/typescript/integrations/claude/slop-native/README.md b/packages/typescript/integrations/claude/slop-native/README.md index 9ef0889..7fa179a 100644 --- a/packages/typescript/integrations/claude/slop-native/README.md +++ b/packages/typescript/integrations/claude/slop-native/README.md @@ -109,9 +109,9 @@ Claude calls these directly — no proxy through meta-tools needed. Tools are re ## How it works -1. The MCP server uses `createDiscoveryService` from `@slop-ai/discovery` to discover SLOP providers from the local filesystem and the browser extension bridge. +1. The MCP server uses `createDiscoveryService` from `@slop-ai/discovery/service` to discover SLOP providers from the local filesystem and the browser extension bridge. 2. When Claude calls `connect_app` with an app name, the service lazy-connects via the appropriate transport (WebSocket, Unix socket, or extension relay) and subscribes to the state tree. -3. `createDynamicTools` from `@slop-ai/discovery` converts each connected app's affordances into namespaced MCP tools. The server notifies Claude via `tools/list_changed`. +3. `createDynamicTools` from `@slop-ai/discovery/tools` converts each connected app's affordances into namespaced MCP tools. The server notifies Claude via `tools/list_changed`. 4. Claude calls affordance tools directly (e.g. `excalidraw__elements__add_rectangle`). The server resolves each tool name to a provider + path + action and invokes it. 5. The `UserPromptSubmit` hook reads a shared state file (`/tmp/claude-slop-plugin/state.json`) that the MCP server updates whenever state changes, injecting live state into Claude's context. 6. When Claude calls `disconnect_app`, the provider is disconnected, its tools are removed, and state drops from the hook. @@ -126,11 +126,11 @@ Server-backed web apps ──direct WebSocket─────┤ Browser SPAs ──postMessage──Extension─────────┤ (relay via bridge) │ │ - @slop-ai/discovery SDK ──────┘ - ├── createDiscoveryService (discovery + connections) - ├── createDynamicTools (affordance → tool mapping) - ├── createToolHandlers (lifecycle tool logic) - └── createBridgeClient/Server (extension relay) + @slop-ai/discovery/* SDK ────┘ + ├── /service → createDiscoveryService + ├── /tools → createDynamicTools + ├── /tools → createToolHandlers + └── createBridgeClient/Server (extension relay) ``` ## Known limitations @@ -154,4 +154,4 @@ This gives the best of both worlds: direct tool calls with zero overhead during - [Discovery & Bridge docs](/sdk/discovery) — discovery layer architecture - [Claude Code integration guide](/guides/advanced/claude-code) — detailed setup and usage - [`@slop-ai/consumer`](https://www.npmjs.com/package/@slop-ai/consumer) — SLOP consumer SDK -- [`@slop-ai/discovery`](https://www.npmjs.com/package/@slop-ai/discovery) — discovery + bridge + tool mapping +- [`@slop-ai/discovery`](https://www.npmjs.com/package/@slop-ai/discovery) — bridge/discovery primitives with `/service` and `/tools` subpaths diff --git a/packages/typescript/integrations/claude/slop-native/servers/slop-bridge.mjs b/packages/typescript/integrations/claude/slop-native/servers/slop-bridge.mjs index 437fe79..061bbfa 100644 --- a/packages/typescript/integrations/claude/slop-native/servers/slop-bridge.mjs +++ b/packages/typescript/integrations/claude/slop-native/servers/slop-bridge.mjs @@ -18,8 +18,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; -import { createDiscoveryService } from "@slop-ai/discovery"; -import { createToolHandlers, createDynamicTools } from "@slop-ai/discovery"; +import { createDiscoveryService } from "@slop-ai/discovery/service"; +import { createToolHandlers, createDynamicTools } from "@slop-ai/discovery/tools"; import { formatTree } from "@slop-ai/consumer"; import fs from "node:fs"; import path from "node:path"; diff --git a/packages/typescript/integrations/codex/slop/README.md b/packages/typescript/integrations/codex/slop/README.md index 59908d3..cafaf7d 100644 --- a/packages/typescript/integrations/codex/slop/README.md +++ b/packages/typescript/integrations/codex/slop/README.md @@ -11,7 +11,7 @@ Codex plugin for SLOP. It lets Codex discover SLOP-enabled desktop and web apps ## What it does - Discovers SLOP apps from local provider descriptors and the browser extension bridge -- Connects to Unix socket, WebSocket, and browser-relayed providers through `@slop-ai/discovery` +- Connects to Unix socket, WebSocket, and browser-relayed providers through `@slop-ai/discovery/service` - Injects connected apps' live state into future user turns through a `UserPromptSubmit` hook - Gives Codex a fixed MCP surface that works well with Codex skills and tool calling - Supports multiple connected apps at once for cross-app workflows @@ -35,7 +35,7 @@ bun run build ## How it works -Codex loads the plugin-local MCP server from `.mcp.json`. That server uses `@slop-ai/discovery` to: +Codex loads the plugin-local MCP server from `.mcp.json`. That server uses `@slop-ai/discovery/service` and `@slop-ai/discovery/tools` to: 1. discover local and browser-announced SLOP providers 2. connect to a provider on demand with `connect_app` @@ -61,7 +61,7 @@ Codex loads the plugin-local MCP server from `.mcp.json`. That server uses `@slo | `.mcp.json` | Plugin-local MCP server wiring | | `hooks/hooks.json` | `UserPromptSubmit` hook wiring for state injection | | `skills/slop-connect` | Codex-native workflow guidance for app discovery and control | -| `servers/slop-bridge.mjs` | Fixed-tool MCP bridge backed by `@slop-ai/discovery` | +| `servers/slop-bridge.mjs` | Fixed-tool MCP bridge backed by `@slop-ai/discovery/service` and `@slop-ai/discovery/tools` | ## Documentation diff --git a/packages/typescript/integrations/codex/slop/servers/slop-bridge.mjs b/packages/typescript/integrations/codex/slop/servers/slop-bridge.mjs index 97d90f6..4c0669d 100644 --- a/packages/typescript/integrations/codex/slop/servers/slop-bridge.mjs +++ b/packages/typescript/integrations/codex/slop/servers/slop-bridge.mjs @@ -12,7 +12,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; -import { createDiscoveryService, createToolHandlers } from "@slop-ai/discovery"; +import { createDiscoveryService } from "@slop-ai/discovery/service"; +import { createToolHandlers } from "@slop-ai/discovery/tools"; import { formatTree } from "@slop-ai/consumer"; import fs from "node:fs"; import path from "node:path"; diff --git a/packages/typescript/integrations/discovery/package.json b/packages/typescript/integrations/discovery/package.json index 83b0fa0..e79faf1 100644 --- a/packages/typescript/integrations/discovery/package.json +++ b/packages/typescript/integrations/discovery/package.json @@ -2,7 +2,7 @@ "name": "@slop-ai/discovery", "version": "0.1.0", "type": "module", - "description": "SLOP discovery layer — discover, connect, and control local and browser applications. Main export is host-agnostic; optional `@slop-ai/discovery/anthropic-agent-sdk` wraps the Anthropic Claude Agent SDK.", + "description": "SLOP bridge and discovery primitives. Use `@slop-ai/discovery/service` for connection orchestration and `@slop-ai/discovery/tools` for host tool helpers.", "main": "dist/index.js", "types": "dist/index.d.ts", "bin": { @@ -13,6 +13,14 @@ "import": "./dist/index.js", "types": "./dist/index.d.ts" }, + "./service": { + "import": "./dist/service.js", + "types": "./dist/service.d.ts" + }, + "./tools": { + "import": "./dist/tools.js", + "types": "./dist/tools.d.ts" + }, "./anthropic-agent-sdk": { "import": "./dist/anthropic-agent-sdk.js", "types": "./dist/anthropic-agent-sdk.d.ts" @@ -29,7 +37,7 @@ "directory": "packages/typescript/integrations/discovery" }, "scripts": { - "build": "bun build src/index.ts src/anthropic-agent-sdk.ts src/cli.ts --outdir dist --format esm --target node --external '@slop-ai/consumer' --external '@anthropic-ai/claude-agent-sdk' --external '@modelcontextprotocol/sdk' --external 'zod' --external 'ws' && bunx tsc --emitDeclarationOnly --outDir dist", + "build": "bun build src/index.ts src/service.ts src/tools.ts src/anthropic-agent-sdk.ts src/cli.ts --outdir dist --format esm --target node --external '@slop-ai/consumer' --external '@anthropic-ai/claude-agent-sdk' --external '@modelcontextprotocol/sdk' --external 'zod' --external 'ws' && bunx tsc --emitDeclarationOnly --outDir dist", "build:bundle": "bun build src/cli.ts --outfile dist/cli.bundle.js --format esm --target node", "clean": "rm -rf dist" }, diff --git a/packages/typescript/integrations/discovery/src/anthropic-agent-sdk.ts b/packages/typescript/integrations/discovery/src/anthropic-agent-sdk.ts index ad3b805..d8a36ae 100644 --- a/packages/typescript/integrations/discovery/src/anthropic-agent-sdk.ts +++ b/packages/typescript/integrations/discovery/src/anthropic-agent-sdk.ts @@ -2,13 +2,14 @@ * Optional helpers for `@anthropic-ai/claude-agent-sdk` (`tool()`, `createSdkMcpServer`). * * Use when wiring SLOP discovery into Anthropic Agent `query()` or MCP-from-SDK flows. - * Host-agnostic tool logic (`createToolHandlers`, `createDynamicTools`, discovery, bridge) - * is exported from `@slop-ai/discovery` — not from this entry point. + * Host-agnostic discovery, service, and tool helpers live under + * `@slop-ai/discovery`, `@slop-ai/discovery/service`, and + * `@slop-ai/discovery/tools` — not from this entry point. */ import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk"; import { z } from "zod"; -import { createDiscoveryService } from "./discovery"; +import { createDiscoveryService } from "./service"; import { createToolHandlers } from "./tools"; /** diff --git a/packages/typescript/integrations/discovery/src/cli.ts b/packages/typescript/integrations/discovery/src/cli.ts index cce01ef..0ab2852 100644 --- a/packages/typescript/integrations/discovery/src/cli.ts +++ b/packages/typescript/integrations/discovery/src/cli.ts @@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; -import { createDiscoveryService } from "./discovery"; +import { createDiscoveryService } from "./service"; import { createToolHandlers } from "./tools"; import { createStateCache, type StateCache } from "./state-cache"; diff --git a/packages/typescript/integrations/discovery/src/index.ts b/packages/typescript/integrations/discovery/src/index.ts index 9fc713f..7428c22 100644 --- a/packages/typescript/integrations/discovery/src/index.ts +++ b/packages/typescript/integrations/discovery/src/index.ts @@ -1,17 +1,8 @@ -import { createDiscoveryService as createDiscoveryServiceImpl } from "./discovery"; import { createBridgeClient as createBridgeClientImpl } from "./bridge-client"; import { createBridgeServer as createBridgeServerImpl } from "./bridge-server"; import { BridgeRelayTransport as BridgeRelayTransportImpl } from "./relay-transport"; -import { createToolHandlers as createToolHandlersImpl, createDynamicTools as createDynamicToolsImpl } from "./tools"; -import { createStateCache as createStateCacheImpl } from "./state-cache"; -export const createDiscoveryService = createDiscoveryServiceImpl; -export type { - DiscoveryService, - DiscoveryOptions, - ProviderDescriptor, - ConnectedProvider, -} from "./discovery"; +export type { ProviderDescriptor } from "./discovery"; export const createBridgeClient = createBridgeClientImpl; export type { Bridge, @@ -22,14 +13,3 @@ export type { export const createBridgeServer = createBridgeServerImpl; export type { BridgeServerOptions } from "./bridge-server"; export const BridgeRelayTransport = BridgeRelayTransportImpl; - -/** Agent-agnostic tool handlers and dynamic affordance→tool mapping (OpenClaw, MCP bridges, etc.). */ -export const createToolHandlers = createToolHandlersImpl; -export const createDynamicTools = createDynamicToolsImpl; -export type { - ToolResult, - DynamicToolSet, - DynamicToolEntry, -} from "./tools"; -export const createStateCache = createStateCacheImpl; -export type { StateCache } from "./state-cache"; diff --git a/packages/typescript/integrations/discovery/src/service.ts b/packages/typescript/integrations/discovery/src/service.ts new file mode 100644 index 0000000..c39b33d --- /dev/null +++ b/packages/typescript/integrations/discovery/src/service.ts @@ -0,0 +1,7 @@ +export { createDiscoveryService } from "./discovery"; +export type { + DiscoveryService, + DiscoveryOptions, + ProviderDescriptor, + ConnectedProvider, +} from "./discovery"; diff --git a/packages/typescript/integrations/discovery/src/state-cache.ts b/packages/typescript/integrations/discovery/src/state-cache.ts index 8c2e0d8..1dfe943 100644 --- a/packages/typescript/integrations/discovery/src/state-cache.ts +++ b/packages/typescript/integrations/discovery/src/state-cache.ts @@ -1,7 +1,7 @@ import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "node:fs"; import { join } from "node:path"; import { formatTree, affordancesToTools } from "@slop-ai/consumer"; -import type { DiscoveryService, ConnectedProvider } from "./discovery"; +import type { DiscoveryService, ConnectedProvider } from "./service"; const HEADER = `[SLOP — Connected Applications] diff --git a/packages/typescript/integrations/discovery/src/tools.ts b/packages/typescript/integrations/discovery/src/tools.ts index 26c3e2d..fe1089f 100644 --- a/packages/typescript/integrations/discovery/src/tools.ts +++ b/packages/typescript/integrations/discovery/src/tools.ts @@ -1,5 +1,5 @@ import { formatTree, affordancesToTools, type LlmTool } from "@slop-ai/consumer"; -import type { DiscoveryService, ConnectedProvider } from "./discovery"; +import type { DiscoveryService } from "./service"; export interface ToolResult { [key: string]: unknown; diff --git a/packages/typescript/integrations/openclaw-plugin/README.md b/packages/typescript/integrations/openclaw-plugin/README.md index 2bd654b..c8bbe2d 100644 --- a/packages/typescript/integrations/openclaw-plugin/README.md +++ b/packages/typescript/integrations/openclaw-plugin/README.md @@ -24,7 +24,7 @@ openclaw plugins install @slop-ai/openclaw-plugin ## How it works -The plugin uses `@slop-ai/discovery` for provider discovery and connection management, and injects provider state into the prompt via OpenClaw's `before_prompt_build` hook. The model sees live app state before every inference turn, then uses `app_action` or `app_action_batch` with the exact paths and action names shown in context. +The plugin uses `@slop-ai/discovery/service` for provider discovery and connection management, plus `@slop-ai/discovery/tools` for shared lifecycle tool handlers. It injects provider state into the prompt via OpenClaw's `before_prompt_build` hook, so the model sees live app state before every inference turn, then uses `app_action` or `app_action_batch` with the exact paths and action names shown in context. ## How provider discovery works diff --git a/packages/typescript/integrations/openclaw-plugin/package.json b/packages/typescript/integrations/openclaw-plugin/package.json index ff3d285..a260218 100644 --- a/packages/typescript/integrations/openclaw-plugin/package.json +++ b/packages/typescript/integrations/openclaw-plugin/package.json @@ -25,7 +25,7 @@ "extensions": ["./dist/index.js"] }, "scripts": { - "build": "bun build src/index.ts --outdir dist --format esm --target node --external '@slop-ai/consumer' --external '@slop-ai/discovery' --external '@sinclair/typebox' --external openclaw && bunx tsc --emitDeclarationOnly --outDir dist", + "build": "bun build src/index.ts --outdir dist --format esm --target node --external '@slop-ai/consumer' --external '@slop-ai/discovery' --external '@slop-ai/discovery/service' --external '@slop-ai/discovery/tools' --external '@sinclair/typebox' --external openclaw && bunx tsc --emitDeclarationOnly --outDir dist", "clean": "rm -rf dist" }, "dependencies": { diff --git a/packages/typescript/integrations/openclaw-plugin/src/index.ts b/packages/typescript/integrations/openclaw-plugin/src/index.ts index 18fb69d..b96e424 100644 --- a/packages/typescript/integrations/openclaw-plugin/src/index.ts +++ b/packages/typescript/integrations/openclaw-plugin/src/index.ts @@ -1,6 +1,6 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; -import { createDiscoveryService, type DiscoveryService } from "@slop-ai/discovery"; -import { createToolHandlers } from "@slop-ai/discovery"; +import { createDiscoveryService, type DiscoveryService } from "@slop-ai/discovery/service"; +import { createToolHandlers } from "@slop-ai/discovery/tools"; import { formatTree } from "@slop-ai/consumer"; import { registerSlopTools } from "./tools"; diff --git a/packages/typescript/integrations/openclaw-plugin/src/tools.ts b/packages/typescript/integrations/openclaw-plugin/src/tools.ts index 6c4ec51..9c7e17b 100644 --- a/packages/typescript/integrations/openclaw-plugin/src/tools.ts +++ b/packages/typescript/integrations/openclaw-plugin/src/tools.ts @@ -1,6 +1,6 @@ import { Type } from "@sinclair/typebox"; -import type { DiscoveryService } from "@slop-ai/discovery"; -import type { ToolResult } from "@slop-ai/discovery"; +import type { DiscoveryService } from "@slop-ai/discovery/service"; +import type { ToolResult } from "@slop-ai/discovery/tools"; interface ToolHandlers { listApps(): Promise; diff --git a/website/docs/src/content/docs/api/index.md b/website/docs/src/content/docs/api/index.md index 1ba5c3f..0b0748e 100644 --- a/website/docs/src/content/docs/api/index.md +++ b/website/docs/src/content/docs/api/index.md @@ -18,7 +18,7 @@ This page maps every published package in the repo to its primary use case and t | `@slop-ai/server` | server, desktop, and CLI providers | `bun add @slop-ai/server` | [API](/api/server), [Guide](/guides/server-apps) | | `@slop-ai/consumer` | custom agents, inspectors, and bridges | `bun add @slop-ai/consumer` | [API](/api/consumer), [Guide](/guides/consumer) | | `@slop-ai/tanstack-start` | TanStack Start full-stack adapter | `bun add @slop-ai/server @slop-ai/tanstack-start` | [API](/api/tanstack-start), [Guide](/guides/tanstack-start) | -| `@slop-ai/discovery` | auto-discovery, bridge, relay, AI tool formatting | `bun add @slop-ai/discovery` | [SDK docs](/sdk/discovery) | +| `@slop-ai/discovery` | bridge and discovery primitives; add `/service` and `/tools` for higher-level helpers | `bun add @slop-ai/discovery` | [SDK docs](/sdk/discovery) | | `@slop-ai/openclaw-plugin` | OpenClaw integration | `bun add @slop-ai/openclaw-plugin` | [API](/api/openclaw-plugin), [Guide](/guides/advanced/openclaw) | ## Other SDKs diff --git a/website/docs/src/content/docs/api/openclaw-plugin.md b/website/docs/src/content/docs/api/openclaw-plugin.md index 98a40f6..cdb1454 100644 --- a/website/docs/src/content/docs/api/openclaw-plugin.md +++ b/website/docs/src/content/docs/api/openclaw-plugin.md @@ -24,7 +24,7 @@ openclaw plugins install @slop-ai/openclaw-plugin ## How it works -The plugin uses `@slop-ai/discovery` for provider discovery (local dirs, bridge, relay) and for shared tool handlers (`createToolHandlers`, etc.). +The plugin uses `@slop-ai/discovery/service` for provider discovery and connection management, and `@slop-ai/discovery/tools` for shared lifecycle tool handlers. ### State injection diff --git a/website/docs/src/content/docs/guides/advanced/claude-code.md b/website/docs/src/content/docs/guides/advanced/claude-code.md index a77ad37..6e2a4b6 100644 --- a/website/docs/src/content/docs/guides/advanced/claude-code.md +++ b/website/docs/src/content/docs/guides/advanced/claude-code.md @@ -143,7 +143,7 @@ Server-backed web apps ──direct WebSocket─────┤ Browser SPAs ──postMessage──Extension─────────┤ │ (relay via bridge) │ ├── state.json ──→ hook ──→ Claude context │ │ - @slop-ai/discovery ──────────┘ ├── native: dynamic tools via tools/list_changed + @slop-ai/discovery/* ────────┘ ├── native: dynamic tools via tools/list_changed └── mcp-proxy: fixed tools (app_action, app_action_batch) ``` @@ -159,7 +159,7 @@ The state file includes a `lastUpdated` timestamp. The hook skips injection if t OpenClaw's plugin SDK does not support runtime tool registration — tools must be declared in the plugin manifest and registered once during plugin initialization. The OpenClaw plugin uses meta-tools (`app_action`, `app_action_batch`) instead, with state injection via `before_prompt_build` to give the model full context. See [OpenClaw integration](/guides/advanced/openclaw) for details. -All three integrations share the same underlying `@slop-ai/discovery` package. `claude-slop-native` additionally uses `createDynamicTools()` to expose first-class tools. +All three integrations share the same underlying discovery stack. They use `@slop-ai/discovery/service` for connection orchestration, and `claude-slop-native` additionally uses `@slop-ai/discovery/tools` to expose first-class tools. ## Related diff --git a/website/docs/src/content/docs/guides/advanced/codex.md b/website/docs/src/content/docs/guides/advanced/codex.md index db28356..749b7e8 100644 --- a/website/docs/src/content/docs/guides/advanced/codex.md +++ b/website/docs/src/content/docs/guides/advanced/codex.md @@ -15,7 +15,7 @@ It gives Codex five stable MCP tools for discovering and controlling SLOP-enable ## What it does - **Discovers** SLOP apps from local provider descriptors and the browser extension bridge -- **Connects** to Unix socket, WebSocket, and relay-backed providers through `@slop-ai/discovery` +- **Connects** to Unix socket, WebSocket, and relay-backed providers through `@slop-ai/discovery/service` - **Injects live state** for connected apps on each future user prompt through a Codex hook - **Returns an immediate snapshot** through `connect_app`, including the current state tree and available actions - **Acts through stable meta-tools** so Codex has a predictable tool surface @@ -48,7 +48,7 @@ The plugin's `.mcp.json` starts a local stdio MCP server: - cwd: `./servers` - entrypoint: `./dist/slop-bridge.bundle.mjs` -That bridge wraps `@slop-ai/discovery` and exposes a fixed five-tool surface to Codex. +That bridge wraps `@slop-ai/discovery/service` and `@slop-ai/discovery/tools` and exposes a fixed five-tool surface to Codex. ### Hook-based state injection @@ -108,7 +108,7 @@ The Codex integration currently chooses the same fixed-tool model as the OpenCla - the hook can inject live state without rebuilding tools on every patch - the skill can reliably teach the connect-once, inspect, then act workflow -When Codex support for dynamic per-affordance tool flows becomes desirable, the same `@slop-ai/discovery` layer can be extended in that direction. +When Codex support for dynamic per-affordance tool flows becomes desirable, the same discovery stack can extend in that direction by adding `@slop-ai/discovery/tools` dynamic tool mapping. ## Example interaction diff --git a/website/docs/src/content/docs/guides/advanced/openclaw.md b/website/docs/src/content/docs/guides/advanced/openclaw.md index 06d837e..b5896a5 100644 --- a/website/docs/src/content/docs/guides/advanced/openclaw.md +++ b/website/docs/src/content/docs/guides/advanced/openclaw.md @@ -12,7 +12,7 @@ description: "Control SLOP-enabled applications through OpenClaw" ## What it does -- **Discovers** SLOP apps — local native apps, WebSocket servers, and browser tabs via the extension bridge (powered by `@slop-ai/discovery`) +- **Discovers** SLOP apps — local native apps, WebSocket servers, and browser tabs via the extension bridge (powered by `@slop-ai/discovery/service`) - **Injects state** into the prompt before each inference via `before_prompt_build` — the model sees live app state without calling any tool - **Exposes actions** through `app_action` and `app_action_batch` meta-tools - **Supports multiple apps** connected simultaneously @@ -73,7 +73,7 @@ The model knows what state exists and what actions are available without calling ### Discovery -The plugin uses `@slop-ai/discovery` for provider discovery, which covers: +The plugin uses `@slop-ai/discovery/service` for provider discovery and connection management, which covers: - `~/.slop/providers/` — persistent user-level providers - `/tmp/slop/providers/` — session-level ephemeral providers @@ -113,7 +113,7 @@ The workaround is the **meta-tool pattern**: five stable tools (`list_apps`, `co If OpenClaw adds a runtime tool registration API (e.g., `api.registerDynamicTools()` or `api.updateToolList()`), the plumbing is already in place: -- `createDynamicTools(discovery)` from `@slop-ai/discovery` generates namespaced tool definitions from all connected providers +- `createDynamicTools(discovery)` from `@slop-ai/discovery/tools` generates namespaced tool definitions from all connected providers - Each tool maps to `{ providerId, path, action }` via a `resolve()` function - The same helper is already used by the Claude Code MCP server @@ -128,7 +128,7 @@ If OpenClaw adds a runtime tool registration API (e.g., `api.registerDynamicTool | List tool | `list_apps` | `list_apps` | | Connect tool | `connect_app` | `connect_app` | | Disconnect tool | `disconnect_app` | `disconnect_app` | -| Discovery | `@slop-ai/discovery` | `@slop-ai/discovery` | +| Discovery/runtime | `@slop-ai/discovery/service` + `@slop-ai/discovery/tools` | `@slop-ai/discovery/service` | | Bridge support | Yes | Yes | | Staleness protection | 30s timestamp check | Not needed (in-process) | diff --git a/website/docs/src/content/docs/sdk/discovery.md b/website/docs/src/content/docs/sdk/discovery.md index 2fb8533..e0e91d9 100644 --- a/website/docs/src/content/docs/sdk/discovery.md +++ b/website/docs/src/content/docs/sdk/discovery.md @@ -35,10 +35,12 @@ Integrations (Claude Code plugin, OpenClaw plugin, VS Code extension, custom age | Entry | Purpose | |---|---| -| `@slop-ai/discovery` | **Default.** Discovery service, bridge, relay, CLI, and agent-agnostic helpers: `createToolHandlers`, `createDynamicTools`, `createStateCache`, etc. No Anthropic-specific dependency at import time for these APIs. | +| `@slop-ai/discovery` | **Default.** Bridge and discovery primitives: `createBridgeClient`, `createBridgeServer`, `BridgeRelayTransport`, and shared descriptor types. | +| `@slop-ai/discovery/service` | Discovery service / connection orchestration: `createDiscoveryService`, `DiscoveryService`, `ConnectedProvider`, etc. | +| `@slop-ai/discovery/tools` | Agent-agnostic lifecycle and affordance helpers: `createToolHandlers`, `createDynamicTools`, and tool result types. | | `@slop-ai/discovery/anthropic-agent-sdk` | **Optional.** `createSlopAgentTools` and `createSlopMcpServer` for [`@anthropic-ai/claude-agent-sdk`](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) (`query()`, programmatic MCP). Use only when wiring the Anthropic Agent SDK. | -Integrations that only need discovery + MCP (e.g. `slop-bridge` with the MCP SDK) import from the **default** export and do not need `anthropic-agent-sdk`. +Integrations compose these entrypoints: `@slop-ai/discovery/service` for provider scanning and connection orchestration, `@slop-ai/discovery/tools` when they need lifecycle or dynamic-tool helpers, and the root export when they need direct bridge primitives. ## Phase 1: Core Discovery Layer @@ -71,13 +73,12 @@ Phase 1 includes: - State change callback - `formatTree()` - `affordancesToTools()` -- `createToolHandlers()` -- `createDynamicTools()` Phase 1 explicitly excludes: - Host-specific wrappers such as `cli.ts` and `anthropic-agent-sdk.ts` - Prompt/hook integration glue for Claude Code, Codex, OpenClaw, or other hosts +- TypeScript-only integration helpers such as `@slop-ai/discovery/tools` - File-cache helpers such as `createStateCache()` ### Status labels @@ -258,6 +259,13 @@ Converts all affordances in a state tree into LLM tool definitions: - Returns a `resolve(toolName)` function that maps tool names back to `{ path, action }` for `invoke` - Tool descriptions include the node path, action label, and `[DANGEROUS]` flag +### `@slop-ai/discovery/tools` + +TypeScript-only optional helper subpath for host integrations. It exports: + +- `createToolHandlers(discovery)` for shared lifecycle/meta-tool handlers +- `createDynamicTools(discovery)` for dynamic affordance-to-tool mapping + ### `createDynamicTools(discovery)` Builds namespaced tool definitions from all connected providers' affordances. Each tool name is prefixed with the provider's ID to avoid cross-app collisions: @@ -288,7 +296,7 @@ Consumers can use this to maintain a cache, update a UI, or trigger context inje | Language | Consumer SDK | Core discovery module | Current status | |---|---|---|---| -| TypeScript | `@slop-ai/consumer` | `@slop-ai/discovery` | Reference implementation for phase 1, plus host-specific helpers outside the shared contract | +| TypeScript | `@slop-ai/consumer` | `@slop-ai/discovery/service` | Reference implementation for phase 1, plus optional root and `/tools` entrypoints for host integrations | | Python | `slop-ai` | `slop_ai.discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | | Go | `slop-ai` | `github.com/devteapot/slop/packages/go/slop-ai/discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | | Rust | `slop-ai` | `slop_ai::discovery` | Initial phase-1 implementation shipped in the SDK and normalized to the shared contract | @@ -313,10 +321,7 @@ Consumers can use this to maintain a cache, update a UI, or trigger context inje | State change callback | Shipped | Shipped | Shipped | Shipped | | `formatTree()` | Shipped | Shipped | Shipped | Shipped | | `affordancesToTools()` | Shipped | Shipped | Shipped | Shipped | -| `createToolHandlers()` | Shipped | Shipped | Shipped | Shipped | -| `createDynamicTools()` | Shipped | Shipped | Shipped | Shipped | -| Host wrappers and prompt injection glue | Shipped | Out of scope | Out of scope | Out of scope | -| State cache / shared file helpers | Shipped | Out of scope | Out of scope | Out of scope | +| TypeScript-only `@slop-ai/discovery/tools` helpers | Shipped | Out of scope | Out of scope | Out of scope | ### Known donor-code drift @@ -357,9 +362,7 @@ A complete phase-1 discovery layer implementation provides: - [ ] Exponential backoff reconnection - [ ] Connection timeout (10 seconds) - [ ] State change callback -- [ ] Dynamic tool generation (`createDynamicTools()` equivalent) - -Host-specific wrappers and prompt injection are intentionally outside phase 1. +Host-specific wrappers, tool-helper layers, and prompt injection are intentionally outside phase 1. ## Integrations @@ -367,13 +370,13 @@ Both the Claude Code and OpenClaw plugins follow the same design principles: - **State injection** — Provider state is injected into the model's context before each turn, not fetched via tool calls - **Minimal tool usage** — Tools are used only for connecting to apps and performing actions, never for reading state -- **Shared discovery** — Both import `@slop-ai/discovery` for provider scanning, bridge, and relay +- **Shared discovery** — Both build on `@slop-ai/discovery/service` for provider scanning and connection orchestration Where they differ is **action dispatch**, due to host platform limitations. ### Dynamic tool injection -When a host supports runtime tool registration, the discovery layer can expose each affordance as a first-class tool. `createDynamicTools(discovery)` generates namespaced tool definitions from all connected providers: +When a host supports runtime tool registration, `@slop-ai/discovery/tools` can expose each affordance as a first-class tool. `createDynamicTools(discovery)` generates namespaced tool definitions from all connected providers: ``` kanban__backlog__add_card({title: "Ship docs"}) ← model calls this directly @@ -410,7 +413,7 @@ Design details: - **Fixed tool surface** — The Codex plugin exposes the same stable five-tool catalog as the OpenClaw plugin. - **Hook-based state injection** — The bridge writes provider state to `/tmp/codex-slop-plugin/state.json` on every state change. The `UserPromptSubmit` hook reads that file and injects fresh markdown into future turns. - **Immediate snapshot on connect** — `connect_app` still returns the current tree and actions right away, so Codex can act in the same turn it first connects. -- **Discovery** — Uses `@slop-ai/discovery` with local descriptor watching plus browser bridge support. +- **Discovery** — Uses `@slop-ai/discovery/service` with local descriptor watching plus browser bridge support. - **Multi-app** — Multiple providers can remain connected concurrently; `app_action` and `app_action_batch` resolve against the requested app ID. See [Codex guide](/guides/advanced/codex) for setup and usage. @@ -419,8 +422,8 @@ See [Codex guide](/guides/advanced/codex) for setup and usage. | Variant | Purpose | |---|---| -| **`claude-slop-native`** | Wraps `createDiscoveryService` + `createDynamicTools` from `@slop-ai/discovery`. Registers dynamic per-app tools via `tools/list_changed`. Static tools: `list_apps`, `connect_app`, `disconnect_app`. | -| **`claude-slop-mcp-proxy`** | Wraps `createDiscoveryService` from `@slop-ai/discovery`, but keeps a fixed tool catalog: `list_apps`, `connect_app`, `disconnect_app`, `app_action`, `app_action_batch`. | +| **`claude-slop-native`** | Wraps `createDiscoveryService` from `@slop-ai/discovery/service` and `createDynamicTools` from `@slop-ai/discovery/tools`. Registers dynamic per-app tools via `tools/list_changed`. Static tools: `list_apps`, `connect_app`, `disconnect_app`. | +| **`claude-slop-mcp-proxy`** | Wraps `createDiscoveryService` from `@slop-ai/discovery/service`, but keeps a fixed tool catalog: `list_apps`, `connect_app`, `disconnect_app`, `app_action`, `app_action_batch`. | | **Shared hook** (`UserPromptSubmit`) | Reads a shared state file and injects connected providers' state trees into Claude's context on every user message — no MCP fetch needed. Also lists discovered-but-not-connected apps. | | **Shared skill** (`slop-connect`) | Teaches Claude the list → connect → inspect → act workflow. | @@ -445,7 +448,7 @@ Design details: - **Meta-tool pattern** — OpenClaw's plugin SDK requires tools to be declared upfront in `openclaw.plugin.json` and registered once. Dynamic tool registration is not supported. Actions go through `app_action(app, path, action, params)` instead of per-app tools. - **State injection** — The `before_prompt_build` hook returns `{ prependContext: stateMarkdown }`, which OpenClaw prepends to the conversation before inference. No file-based IPC needed (in-process). -- **Discovery** — Uses `@slop-ai/discovery` with bridge support. Discovers local providers, session providers, and browser tabs via extension bridge. +- **Discovery** — Uses `@slop-ai/discovery/service` with bridge support. Discovers local providers, session providers, and browser tabs via extension bridge. See [OpenClaw guide](/guides/advanced/openclaw) for setup and usage.