Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/api/openclaw-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/guides/advanced/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

Expand All @@ -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

Expand Down
6 changes: 3 additions & 3 deletions docs/guides/advanced/codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions docs/guides/advanced/openclaw.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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) |

Expand Down
39 changes: 21 additions & 18 deletions docs/sdk/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 |
Expand All @@ -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

Expand Down Expand Up @@ -355,23 +360,21 @@ 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

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
Expand Down Expand Up @@ -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.
Expand All @@ -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. |

Expand All @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading
Loading