@nimblebrain/synapse 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -45
- package/dist/{chunk-Y4ZDNAYQ.cjs → chunk-B3T6NB32.cjs} +349 -80
- package/dist/chunk-B3T6NB32.cjs.map +1 -0
- package/dist/{chunk-7KEYXJWD.js → chunk-GQ4L63CL.js} +349 -81
- package/dist/chunk-GQ4L63CL.js.map +1 -0
- package/dist/codegen/cli.cjs +1 -1
- package/dist/codegen/cli.js +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/connect.iife.global.js +1 -0
- package/dist/index.cjs +8 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +119 -3
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +18 -2
- package/dist/react/index.d.ts +18 -2
- package/dist/react/index.js +114 -5
- package/dist/react/index.js.map +1 -1
- package/dist/{server-NNW54YW5.js → server-7BRGSPT3.js} +13 -13
- package/dist/{server-NNW54YW5.js.map → server-7BRGSPT3.js.map} +1 -1
- package/dist/{server-3BDZ5S72.cjs → server-SRE7E3G3.cjs} +13 -13
- package/dist/{server-3BDZ5S72.cjs.map → server-SRE7E3G3.cjs.map} +1 -1
- package/dist/synapse-runtime.iife.global.js +1 -1
- package/dist/{types-DElq_otH.d.cts → types-DJ32F5EL.d.cts} +79 -4
- package/dist/{types-DElq_otH.d.ts → types-DJ32F5EL.d.ts} +79 -4
- package/dist/vite/index.cjs +6 -6
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +6 -6
- package/dist/vite/index.js.map +1 -1
- package/package.json +4 -1
- package/dist/chunk-7KEYXJWD.js.map +0 -1
- package/dist/chunk-Y4ZDNAYQ.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
|
-
Agent-aware app SDK for the [MCP ext-apps](https://modelcontextprotocol.io/specification/2025-06-18/user-interaction/ext-apps) protocol.
|
|
10
|
+
Agent-aware app SDK for the [MCP ext-apps](https://modelcontextprotocol.io/specification/2025-06-18/user-interaction/ext-apps) protocol. One `await connect()` and you're live — typed tool calls, reactive data sync, and React hooks that work in any host implementing ext-apps (Claude Desktop, VS Code, ChatGPT, [NimbleBrain](https://nimblebrain.ai), or your own runtime).
|
|
11
11
|
|
|
12
12
|
## What is Synapse?
|
|
13
13
|
|
|
14
14
|
Synapse is an optional enhancement layer over `@modelcontextprotocol/ext-apps`. It wraps the ext-apps protocol handshake and adds:
|
|
15
15
|
|
|
16
|
+
- **Zero-config handshake** — `await connect()` resolves when the host is ready. You never see `ui/initialize`.
|
|
16
17
|
- **Typed tool calls** — call MCP tools with full TypeScript input/output types
|
|
17
18
|
- **Reactive data sync** — subscribe to data change events from the agent
|
|
18
19
|
- **Theme tracking** — automatic light/dark mode and custom design tokens
|
|
@@ -40,36 +41,36 @@ npm install @nimblebrain/synapse
|
|
|
40
41
|
|
|
41
42
|
| Entry Point | Description |
|
|
42
43
|
|-------------|-------------|
|
|
43
|
-
| `@nimblebrain/synapse` | Vanilla JS core (
|
|
44
|
-
| `@nimblebrain/synapse/react` | React hooks and
|
|
44
|
+
| `@nimblebrain/synapse` | Vanilla JS core — `connect()`, `createSynapse()`, `createStore()` |
|
|
45
|
+
| `@nimblebrain/synapse/react` | React hooks and providers (`AppProvider`, `SynapseProvider`) |
|
|
45
46
|
| `@nimblebrain/synapse/vite` | Vite plugin for dev mode |
|
|
46
47
|
| `@nimblebrain/synapse/codegen` | CLI + programmatic code generation |
|
|
48
|
+
| `@nimblebrain/synapse/iife` | Pre-built IIFE bundle for `<script>` tags (`window.Synapse`) |
|
|
47
49
|
|
|
48
50
|
## Quick Start
|
|
49
51
|
|
|
50
52
|
### Vanilla JS
|
|
51
53
|
|
|
52
54
|
```typescript
|
|
53
|
-
import {
|
|
55
|
+
import { connect } from "@nimblebrain/synapse";
|
|
54
56
|
|
|
55
|
-
const
|
|
56
|
-
name: "my-app",
|
|
57
|
-
version: "1.0.0",
|
|
58
|
-
});
|
|
57
|
+
const app = await connect({ name: "my-app", version: "1.0.0" });
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
// Theme, host info, and tool context are available immediately
|
|
60
|
+
console.log(app.theme.mode); // "dark"
|
|
61
|
+
console.log(app.hostInfo); // { name: "nimblebrain", version: "2.0.0" }
|
|
62
|
+
|
|
63
|
+
// Subscribe to tool results from the agent
|
|
64
|
+
app.on("tool-result", (data) => {
|
|
65
|
+
console.log(data.content); // parsed JSON or raw string
|
|
66
|
+
});
|
|
61
67
|
|
|
62
68
|
// Call an MCP tool
|
|
63
|
-
const result = await
|
|
69
|
+
const result = await app.callTool("get_items", { limit: 10 });
|
|
64
70
|
console.log(result.data);
|
|
65
71
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
console.log(`${event.tool} was called on ${event.server}`);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Push state visible to the LLM
|
|
72
|
-
synapse.setVisibleState(
|
|
72
|
+
// Tell the agent what the user sees
|
|
73
|
+
app.updateModelContext(
|
|
73
74
|
{ selectedItem: "item-42" },
|
|
74
75
|
"User is viewing item 42",
|
|
75
76
|
);
|
|
@@ -78,31 +79,44 @@ synapse.setVisibleState(
|
|
|
78
79
|
### React
|
|
79
80
|
|
|
80
81
|
```tsx
|
|
81
|
-
import {
|
|
82
|
+
import { AppProvider, useToolResult, useCallTool, useResize } from "@nimblebrain/synapse/react";
|
|
82
83
|
|
|
83
84
|
function App() {
|
|
84
85
|
return (
|
|
85
|
-
<
|
|
86
|
+
<AppProvider name="my-app" version="1.0.0">
|
|
86
87
|
<ItemList />
|
|
87
|
-
</
|
|
88
|
+
</AppProvider>
|
|
88
89
|
);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
function ItemList() {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
93
|
+
const result = useToolResult();
|
|
94
|
+
const { call, data, isPending } = useCallTool("list_items");
|
|
95
|
+
const resize = useResize();
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</button>
|
|
100
|
-
{data?.map((item) => <div key={item.id}>{item.name}</div>)}
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
97
|
+
useEffect(() => { if (result) resize(); }, [result, resize]);
|
|
98
|
+
|
|
99
|
+
if (!result) return <p>Waiting for data...</p>;
|
|
100
|
+
return result.content.items.map((item) => <div key={item.id}>{item.name}</div>);
|
|
103
101
|
}
|
|
104
102
|
```
|
|
105
103
|
|
|
104
|
+
### Script Tag (IIFE)
|
|
105
|
+
|
|
106
|
+
Drop a single `<script>` tag — no bundler required:
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<script src="https://unpkg.com/@nimblebrain/synapse/dist/connect.iife.global.js"></script>
|
|
110
|
+
<script>
|
|
111
|
+
Synapse.connect({ name: "widget", version: "1.0.0", autoResize: true })
|
|
112
|
+
.then(app => {
|
|
113
|
+
app.on("tool-result", (data) => {
|
|
114
|
+
document.getElementById("root").innerHTML = render(data.content);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
118
|
+
```
|
|
119
|
+
|
|
106
120
|
### Vite Plugin
|
|
107
121
|
|
|
108
122
|
```typescript
|
|
@@ -138,6 +152,54 @@ Or from a directory of `.schema.json` files (generates CRUD tool types):
|
|
|
138
152
|
npx synapse --from-schema ./schemas --out src/generated/types.ts
|
|
139
153
|
```
|
|
140
154
|
|
|
155
|
+
## Handling Events
|
|
156
|
+
|
|
157
|
+
The `App` object returned by `connect()` uses a unified `on()` method for all events. Each call returns an unsubscribe function.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const app = await connect({ name: "my-app", version: "1.0.0" });
|
|
161
|
+
|
|
162
|
+
// Tool results from the agent (parsed content, not raw JSON-RPC)
|
|
163
|
+
const unsub = app.on("tool-result", (data) => {
|
|
164
|
+
console.log(data.content); // parsed JSON or raw string
|
|
165
|
+
console.log(data.structuredContent); // structuredContent if host sent it
|
|
166
|
+
console.log(data.raw); // original params for advanced use
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Tool input arguments (what the agent is calling with)
|
|
170
|
+
app.on("tool-input", (args) => {
|
|
171
|
+
console.log(args); // Record<string, unknown>
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Theme changes
|
|
175
|
+
app.on("theme-changed", (theme) => {
|
|
176
|
+
document.body.classList.toggle("dark", theme.mode === "dark");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Lifecycle — clean up when the host tears down the view
|
|
180
|
+
app.on("teardown", () => {
|
|
181
|
+
saveState();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// NimbleBrain extensions work as passthrough event names
|
|
185
|
+
app.on("synapse/data-changed", (params) => {
|
|
186
|
+
refreshData();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Unsubscribe when done
|
|
190
|
+
unsub();
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
| `on()` Event | Spec Method | Data |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `"tool-result"` | `ui/notifications/tool-result` | `ToolResultData` (parsed) |
|
|
196
|
+
| `"tool-input"` | `ui/notifications/tool-input` | `Record<string, unknown>` |
|
|
197
|
+
| `"tool-input-partial"` | `ui/notifications/tool-input-partial` | `Record<string, unknown>` |
|
|
198
|
+
| `"tool-cancelled"` | `ui/notifications/tool-cancelled` | — |
|
|
199
|
+
| `"theme-changed"` | `ui/notifications/host-context-changed` | `Theme` |
|
|
200
|
+
| `"teardown"` | `ui/resource-teardown` | — |
|
|
201
|
+
| Any custom string | Passed through as-is | `unknown` |
|
|
202
|
+
|
|
141
203
|
## State Store
|
|
142
204
|
|
|
143
205
|
Create a typed, reactive store with optional persistence and agent visibility:
|
|
@@ -178,9 +240,53 @@ function Counter() {
|
|
|
178
240
|
|
|
179
241
|
## API Reference
|
|
180
242
|
|
|
181
|
-
### `
|
|
243
|
+
### `connect(options)` — Recommended
|
|
182
244
|
|
|
183
|
-
Creates a
|
|
245
|
+
Creates a connected `App` instance. The returned promise resolves after the ext-apps handshake completes — theme, host info, and tool context are available immediately.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { connect } from "@nimblebrain/synapse";
|
|
249
|
+
|
|
250
|
+
const app = await connect({ name: "my-app", version: "1.0.0" });
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
| Option | Type | Description |
|
|
254
|
+
|--------|------|-------------|
|
|
255
|
+
| `name` | `string` | App name (must match registered bundle name) |
|
|
256
|
+
| `version` | `string` | Semver version |
|
|
257
|
+
| `autoResize` | `boolean?` | Observe `document.body` and auto-send `size-changed`. Default: `false` |
|
|
258
|
+
|
|
259
|
+
### `App` Properties
|
|
260
|
+
|
|
261
|
+
| Property | Type | Description |
|
|
262
|
+
|----------|------|-------------|
|
|
263
|
+
| `theme` | `Theme` | Current theme (`mode`, `tokens`) |
|
|
264
|
+
| `hostInfo` | `{ name, version }` | Host identity |
|
|
265
|
+
| `toolInfo` | `{ tool } \| null` | Tool context if launched from a tool call |
|
|
266
|
+
| `containerDimensions` | `Dimensions \| null` | Container size constraints from host |
|
|
267
|
+
|
|
268
|
+
### `App` Methods
|
|
269
|
+
|
|
270
|
+
| Method | Description |
|
|
271
|
+
|--------|-------------|
|
|
272
|
+
| `on(event, handler)` | Subscribe to events. Returns unsubscribe function. |
|
|
273
|
+
| `resize(width?, height?)` | Send size to host. Auto-measures `document.body` if no args. |
|
|
274
|
+
| `openLink(url)` | Open a URL (host-aware) |
|
|
275
|
+
| `updateModelContext(state, summary?)` | Push LLM-visible state |
|
|
276
|
+
| `callTool(name, args?)` | Call an MCP tool and get typed result |
|
|
277
|
+
| `sendMessage(text, context?)` | Send a chat message to the agent |
|
|
278
|
+
| `destroy()` | Clean up all listeners, observers, and timers |
|
|
279
|
+
|
|
280
|
+
### `createSynapse(options)` — Advanced / Legacy
|
|
281
|
+
|
|
282
|
+
The original Synapse API. Still fully supported — use it when you need the state store, agent actions, file operations, or NimbleBrain-specific features not yet surfaced in `connect()`.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { createSynapse } from "@nimblebrain/synapse";
|
|
286
|
+
|
|
287
|
+
const synapse = createSynapse({ name: "my-app", version: "1.0.0" });
|
|
288
|
+
await synapse.ready;
|
|
289
|
+
```
|
|
184
290
|
|
|
185
291
|
| Option | Type | Description |
|
|
186
292
|
|--------|------|-------------|
|
|
@@ -197,27 +303,57 @@ Creates a Synapse instance. Returns a `Synapse` object.
|
|
|
197
303
|
| `isNimbleBrainHost` | Whether the host is a NimbleBrain platform |
|
|
198
304
|
| `callTool(name, args?)` | Call an MCP tool and get typed result |
|
|
199
305
|
| `onDataChanged(cb)` | Subscribe to data change events |
|
|
306
|
+
| `onAction(cb)` | Subscribe to agent actions (typed, declarative) |
|
|
200
307
|
| `getTheme()` | Get current theme |
|
|
201
308
|
| `onThemeChanged(cb)` | Subscribe to theme changes |
|
|
202
309
|
| `action(name, params?)` | Dispatch a NB platform action |
|
|
203
310
|
| `chat(message, context?)` | Send a chat message to the agent |
|
|
204
311
|
| `setVisibleState(state, summary?)` | Push LLM-visible state (debounced 250ms) |
|
|
205
|
-
| `
|
|
312
|
+
| `saveFile(name, content, mime?)` | Trigger a file save (NB-only) |
|
|
313
|
+
| `pickFile(options?)` | Open native file picker, single file (NB-only) |
|
|
314
|
+
| `pickFiles(options?)` | Open native file picker, multiple files (NB-only) |
|
|
206
315
|
| `openLink(url)` | Open a URL (host-aware) |
|
|
207
316
|
| `destroy()` | Clean up all listeners and timers |
|
|
208
317
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
|
220
|
-
|
|
318
|
+
## React Hooks
|
|
319
|
+
|
|
320
|
+
### `AppProvider`-based (Recommended)
|
|
321
|
+
|
|
322
|
+
Wrap your app with `<AppProvider>` and use these hooks. Each is a thin wrapper over `connect()`.
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
import { AppProvider, useApp, useToolResult, useToolInput, useResize, useCallTool } from "@nimblebrain/synapse/react";
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
| Hook | Returns | Description |
|
|
329
|
+
|------|---------|-------------|
|
|
330
|
+
| `useApp()` | `App` | Access the connected `App` instance |
|
|
331
|
+
| `useToolResult()` | `ToolResultData \| null` | Re-renders on every `tool-result` event |
|
|
332
|
+
| `useToolInput()` | `Record<string, unknown> \| null` | Re-renders on every `tool-input` event |
|
|
333
|
+
| `useConnectTheme()` | `Theme` | Reactive theme from `connect()` |
|
|
334
|
+
| `useResize()` | `(w?, h?) => void` | Resize helper — auto-measures body if no args |
|
|
335
|
+
| `useCallTool(name)` | `{ call, data, isPending, error }` | Call a tool with loading/error state |
|
|
336
|
+
|
|
337
|
+
### `SynapseProvider`-based (Legacy)
|
|
338
|
+
|
|
339
|
+
For existing apps using `createSynapse()`. Still fully supported.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
import { SynapseProvider, useSynapse, useCallTool, useTheme } from "@nimblebrain/synapse/react";
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
| Hook | Returns | Description |
|
|
346
|
+
|------|---------|-------------|
|
|
347
|
+
| `useSynapse()` | `Synapse` | Access the Synapse instance |
|
|
348
|
+
| `useCallTool(name)` | `{ call, data, isPending, error }` | Call a tool with loading/error state |
|
|
349
|
+
| `useDataSync(cb)` | — | Subscribe to data change events |
|
|
350
|
+
| `useTheme()` | `SynapseTheme` | Reactive theme object |
|
|
351
|
+
| `useAction()` | `(name, params?) => void` | Dispatch platform actions |
|
|
352
|
+
| `useAgentAction(cb)` | — | Subscribe to agent actions |
|
|
353
|
+
| `useChat()` | `(msg, ctx?) => void` | Send chat messages |
|
|
354
|
+
| `useVisibleState()` | `(state, summary?) => void` | Push LLM-visible state |
|
|
355
|
+
| `useFileUpload()` | File picker helpers | File upload (NB-only) |
|
|
356
|
+
| `useStore(store)` | `{ state, dispatch }` | Bind a store to React |
|
|
221
357
|
|
|
222
358
|
## Development
|
|
223
359
|
|