@nimblebrain/synapse 0.18.0 → 0.20.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 +32 -37
- package/dist/check-JEM5PN64.cjs +435 -0
- package/dist/check-JEM5PN64.cjs.map +1 -0
- package/dist/check-YDZUQT6A.js +429 -0
- package/dist/check-YDZUQT6A.js.map +1 -0
- package/dist/{chunk-VDQZQDU3.cjs → chunk-BNF352SA.cjs} +56 -145
- package/dist/chunk-BNF352SA.cjs.map +1 -0
- package/dist/{chunk-HKRTDGXN.js → chunk-DJS2FKBN.js} +21 -89
- package/dist/chunk-DJS2FKBN.js.map +1 -0
- package/dist/chunk-H3INUKA3.js +32 -0
- package/dist/chunk-H3INUKA3.js.map +1 -0
- package/dist/{chunk-FB7GPBEM.cjs → chunk-HQLSVUOR.cjs} +193 -314
- package/dist/chunk-HQLSVUOR.cjs.map +1 -0
- package/dist/chunk-ISV6HFQE.cjs +40 -0
- package/dist/chunk-ISV6HFQE.cjs.map +1 -0
- package/dist/{chunk-3YSXPBEQ.js → chunk-TR2YNWZ5.js} +190 -313
- package/dist/chunk-TR2YNWZ5.js.map +1 -0
- package/dist/{chunk-ZLASWV4N.js → chunk-UKP4W6DI.js} +56 -145
- package/dist/chunk-UKP4W6DI.js.map +1 -0
- package/dist/{chunk-LPWEQCZV.cjs → chunk-YSL7KJNU.cjs} +21 -92
- package/dist/chunk-YSL7KJNU.cjs.map +1 -0
- package/dist/codegen/cli.cjs +38 -1
- package/dist/codegen/cli.cjs.map +1 -1
- package/dist/codegen/cli.js +38 -1
- package/dist/codegen/cli.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/connect.iife.global.js +66 -43
- package/dist/{detect-BHYg26_d.d.cts → detect-DVGaL2bH.d.cts} +39 -33
- package/dist/{detect-Bf8Q_0dK.d.ts → detect-DVGaL2bH.d.ts} +39 -33
- package/dist/host/index.cjs +17 -17
- package/dist/host/index.d.cts +11 -27
- package/dist/host/index.d.ts +11 -27
- package/dist/host/index.js +2 -2
- package/dist/index.cjs +52 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -31
- package/dist/index.d.ts +104 -31
- package/dist/index.js +34 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +11 -10
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +49 -12
- package/dist/react/index.d.ts +49 -12
- package/dist/react/index.js +6 -5
- package/dist/react/index.js.map +1 -1
- package/dist/{server-QIYIJHD5.cjs → server-BR5MNFOB.cjs} +40 -29
- package/dist/server-BR5MNFOB.cjs.map +1 -0
- package/dist/{server-5N76YCWC.js → server-OOEEWAEG.js} +40 -30
- package/dist/server-OOEEWAEG.js.map +1 -0
- package/dist/synapse-runtime.iife.global.js +66 -43
- package/dist/synapse-ui.iife.global.js +4 -4
- package/dist/{types-uEO4VFJ2.d.cts → types-BxPfGHKO.d.cts} +48 -125
- package/dist/{types-uEO4VFJ2.d.ts → types-BxPfGHKO.d.ts} +48 -125
- package/dist/vite/index.cjs +77 -33
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.d.cts +5 -1
- package/dist/vite/index.d.ts +5 -1
- package/dist/vite/index.js +77 -33
- package/dist/vite/index.js.map +1 -1
- package/package.json +12 -6
- package/dist/chunk-3YSXPBEQ.js.map +0 -1
- package/dist/chunk-FB7GPBEM.cjs.map +0 -1
- package/dist/chunk-HKRTDGXN.js.map +0 -1
- package/dist/chunk-LPWEQCZV.cjs.map +0 -1
- package/dist/chunk-VDQZQDU3.cjs.map +0 -1
- package/dist/chunk-ZLASWV4N.js.map +0 -1
- package/dist/server-5N76YCWC.js.map +0 -1
- package/dist/server-QIYIJHD5.cjs.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,60 +1,132 @@
|
|
|
1
1
|
export { CreateTaskResult, ReadResourceRequest, ReadResourceResult, Task, TaskStatus } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { C as ConnectOptions, A as App, R as RequestFileOptions, F as FileResult, a as CallToolAsTaskOptions, T as TaskHandle } from './types-
|
|
3
|
-
export {
|
|
4
|
-
|
|
2
|
+
import { C as ConnectOptions, A as App, R as RequestFileOptions, F as FileResult, a as CallToolAsTaskOptions, T as TaskHandle } from './types-BxPfGHKO.cjs';
|
|
3
|
+
export { f as AppEventName, D as Dimensions, K as KeyForwardConfig, M as ModelContext, g as TasksCapability, c as Theme, b as ToolCallResult, e as ToolDefinition, d as ToolResultData } from './types-BxPfGHKO.cjs';
|
|
4
|
+
import { McpUiDownloadFileResult } from '@modelcontextprotocol/ext-apps';
|
|
5
5
|
export { McpUiHostContext } from '@modelcontextprotocol/ext-apps';
|
|
6
|
+
export { C as ConnectUIOptions, a as HostCapabilities, b as HostKind, c as HostUnsupportedError, d as SYNAPSE_DATA_ELEMENT_ID, e as SynapseUIClient, S as SynapseUITheme, T as ToolCallError, g as connectUI, h as detectHostKind } from './detect-DVGaL2bH.cjs';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Connect to an MCP Apps host.
|
|
9
10
|
*
|
|
10
|
-
* The one entry point
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The one entry point. The protocol underneath is the spec's own client,
|
|
12
|
+
* `@modelcontextprotocol/ext-apps`'s `App`: it owns the transport, the
|
|
13
|
+
* handshake and the wire schemas. What this adds is the framework on top —
|
|
14
|
+
* theme injection, the parsed payloads, multi-subscriber events, resize, and
|
|
15
|
+
* the NimbleBrain extensions — and it resolves to a ready {@link App}.
|
|
13
16
|
*
|
|
14
17
|
* The `App` it returns stays deliberately small. NimbleBrain's own extensions
|
|
15
|
-
* (`action`, the file picker, `downloadFile`
|
|
18
|
+
* (`action`, the file picker), `downloadFile` and the MCP tasks utility are
|
|
16
19
|
* composable functions over it — import them from the package root.
|
|
17
20
|
*/
|
|
18
21
|
declare function connect(options: ConnectOptions): Promise<App>;
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* A NimbleBrain extension (`synapse/` prefix), because the ext-apps spec has no
|
|
24
|
-
* equivalent: `styles.variables` is a flat `Record<McpUiStyleVariableKey, string>`
|
|
25
|
-
* that can name a font family but cannot carry the rule that loads it.
|
|
26
|
-
* `McpUiHostContext` declares `[key: string]: unknown` for forward
|
|
27
|
-
* compatibility, so riding an extra key is spec-legal, and hosts that don't send
|
|
28
|
-
* it degrade to the web-safe token fallbacks.
|
|
29
|
-
*/
|
|
30
|
-
declare const FONT_FACES_CONTEXT_KEY = "synapse/fontFaces";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Trigger a host-side action.
|
|
24
|
+
* Hand the user a file to save, over the ext-apps `ui/download-file` request.
|
|
34
25
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
26
|
+
* A function over {@link App} rather than a method on it, like the helpers in
|
|
27
|
+
* `extensions.ts` — but unlike them this is spec surface, so it is not gated on
|
|
28
|
+
* the host identity. Any host that advertises `downloadFile` can answer it.
|
|
29
|
+
*
|
|
30
|
+
* The bytes always travel embedded. The spec also allows a `ResourceLink`, which
|
|
31
|
+
* asks the host to fetch a URI the app names; a host holding the user's session
|
|
32
|
+
* has every reason to refuse that, and the NimbleBrain host does.
|
|
38
33
|
*/
|
|
39
|
-
|
|
34
|
+
|
|
40
35
|
/**
|
|
41
36
|
* Hand the user a file to save.
|
|
42
37
|
*
|
|
38
|
+
* A string is sent as the resource's `text`; a `Blob` is read and sent as
|
|
39
|
+
* base64 `blob`. Reading a `Blob` is asynchronous, which is why this returns a
|
|
40
|
+
* promise.
|
|
41
|
+
*
|
|
43
42
|
* Precedence for the MIME type: the explicit argument, then the Blob's own
|
|
44
43
|
* type, then `application/octet-stream`. An empty-string argument falls
|
|
45
44
|
* through — a `""` MIME is effectively "no type".
|
|
45
|
+
*
|
|
46
|
+
* The filename is the last segment of the resource's `file:///` URI, because an
|
|
47
|
+
* embedded resource has no name field of its own.
|
|
48
|
+
*
|
|
49
|
+
* Resolves with the host's result: `{ isError: true }` when the host declined
|
|
50
|
+
* or the user cancelled. Rejects with `HostCapabilityError`, without sending,
|
|
51
|
+
* when the host did not declare `downloadFile` — a host that does not implement
|
|
52
|
+
* the request may never answer it, and a request has no deadline.
|
|
53
|
+
*/
|
|
54
|
+
declare function downloadFile(app: App, filename: string, content: string | Blob, mimeType?: string): Promise<McpUiDownloadFileResult>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Thrown when an app asks for something the host did not declare in
|
|
58
|
+
* `ui/initialize`.
|
|
59
|
+
*
|
|
60
|
+
* The request is never sent. A host that does not implement a request may never
|
|
61
|
+
* answer it, and this SDK's requests carry no deadline, so sending anyway would
|
|
62
|
+
* leave the caller waiting forever rather than failing.
|
|
63
|
+
*
|
|
64
|
+
* `capability` names what was missing, as the host would have declared it: a
|
|
65
|
+
* `hostCapabilities` field (`serverTools`, `downloadFile`) or an extension
|
|
66
|
+
* identifier (`ai.nimblebrain/request-file`, `io.modelcontextprotocol/tasks`).
|
|
67
|
+
*/
|
|
68
|
+
declare class HostCapabilityError extends Error {
|
|
69
|
+
readonly capability: string;
|
|
70
|
+
constructor(feature: string, capability: string);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The NimbleBrain host extensions. This is the complete list; anything else an
|
|
75
|
+
* app sends is spec.
|
|
76
|
+
*
|
|
77
|
+
* Each extension has one name, used both as its method and as the identifier a
|
|
78
|
+
* host declares in `hostCapabilities.experimental` to offer it. The ext-apps
|
|
79
|
+
* host capability type has no field for extensions, and a spec client parses
|
|
80
|
+
* the `ui/initialize` result against that type, so `experimental` is the one
|
|
81
|
+
* slot whose contents reach the app. The MCP tasks capability travels the same
|
|
82
|
+
* way, for the same reason.
|
|
83
|
+
*
|
|
84
|
+
* The gate is the declaration, not the host's name. A host that implements an
|
|
85
|
+
* extension says so; a host that does not is never sent it.
|
|
86
|
+
*/
|
|
87
|
+
declare const NIMBLEBRAIN_EXTENSIONS: {
|
|
88
|
+
/** App → host notification: run a host action (navigate, open a panel). */
|
|
89
|
+
readonly action: {
|
|
90
|
+
readonly method: "ai.nimblebrain/action";
|
|
91
|
+
readonly capability: "ai.nimblebrain/action";
|
|
92
|
+
};
|
|
93
|
+
/** App → host request: the host's file picker, answered `{ files }`. */
|
|
94
|
+
readonly requestFile: {
|
|
95
|
+
readonly method: "ai.nimblebrain/request-file";
|
|
96
|
+
readonly capability: "ai.nimblebrain/request-file";
|
|
97
|
+
};
|
|
98
|
+
/** App → host notification: a keyboard shortcut pressed inside the frame. */
|
|
99
|
+
readonly keydown: {
|
|
100
|
+
readonly method: "ai.nimblebrain/keydown";
|
|
101
|
+
readonly capability: "ai.nimblebrain/keydown";
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
type NimbleBrainExtension = keyof typeof NIMBLEBRAIN_EXTENSIONS;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Whether the host declared a NimbleBrain extension. Use it to decide what to
|
|
108
|
+
* offer, e.g. to hide an upload button on a host with no file picker.
|
|
46
109
|
*/
|
|
47
|
-
declare function
|
|
110
|
+
declare function hostSupports(app: App, extension: NimbleBrainExtension): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Trigger a host-side action.
|
|
113
|
+
*
|
|
114
|
+
* Sends a command *to* the host (navigate, open a panel). A no-op when the host
|
|
115
|
+
* did not declare `ai.nimblebrain/action`.
|
|
116
|
+
*/
|
|
117
|
+
declare function action(app: App, name: string, params?: Record<string, unknown>): void;
|
|
48
118
|
/**
|
|
49
119
|
* Request one file from the user via the host's native file picker.
|
|
50
120
|
*
|
|
51
|
-
* Resolves `null` if the user cancels.
|
|
121
|
+
* Resolves `null` if the user cancels. Rejects with `HostCapabilityError` when
|
|
122
|
+
* the host did not declare `ai.nimblebrain/request-file`.
|
|
52
123
|
*/
|
|
53
124
|
declare function pickFile(app: App, options?: RequestFileOptions): Promise<FileResult | null>;
|
|
54
125
|
/**
|
|
55
126
|
* Request several files from the user.
|
|
56
127
|
*
|
|
57
|
-
* Resolves `[]` if the user cancels.
|
|
128
|
+
* Resolves `[]` if the user cancels. Rejects with `HostCapabilityError` when
|
|
129
|
+
* the host did not declare `ai.nimblebrain/request-file`.
|
|
58
130
|
*/
|
|
59
131
|
declare function pickFiles(app: App, options?: RequestFileOptions): Promise<FileResult[]>;
|
|
60
132
|
|
|
@@ -72,10 +144,11 @@ declare function pickFiles(app: App, options?: RequestFileOptions): Promise<File
|
|
|
72
144
|
* const result = await handle.result();
|
|
73
145
|
* ```
|
|
74
146
|
*
|
|
75
|
-
*
|
|
76
|
-
* a requestor MUST NOT task-augment
|
|
77
|
-
*
|
|
147
|
+
* Rejects with `HostCapabilityError` if the host did not advertise
|
|
148
|
+
* `tasks.requests.tools.call` — per spec a requestor MUST NOT task-augment
|
|
149
|
+
* without matching receiver capability. Check `app.supportsTasks` first, and
|
|
150
|
+
* fall back to `app.callTool`.
|
|
78
151
|
*/
|
|
79
152
|
declare function callToolAsTask<TOutput = unknown>(app: App, toolName: string, args?: unknown, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>>;
|
|
80
153
|
|
|
81
|
-
export { App, CallToolAsTaskOptions, ConnectOptions,
|
|
154
|
+
export { App, CallToolAsTaskOptions, ConnectOptions, FileResult, HostCapabilityError, NIMBLEBRAIN_EXTENSIONS, type NimbleBrainExtension, RequestFileOptions, TaskHandle, action, callToolAsTask, connect, downloadFile, hostSupports, pickFile, pickFiles };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,60 +1,132 @@
|
|
|
1
1
|
export { CreateTaskResult, ReadResourceRequest, ReadResourceResult, Task, TaskStatus } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { C as ConnectOptions, A as App, R as RequestFileOptions, F as FileResult, a as CallToolAsTaskOptions, T as TaskHandle } from './types-
|
|
3
|
-
export {
|
|
4
|
-
|
|
2
|
+
import { C as ConnectOptions, A as App, R as RequestFileOptions, F as FileResult, a as CallToolAsTaskOptions, T as TaskHandle } from './types-BxPfGHKO.js';
|
|
3
|
+
export { f as AppEventName, D as Dimensions, K as KeyForwardConfig, M as ModelContext, g as TasksCapability, c as Theme, b as ToolCallResult, e as ToolDefinition, d as ToolResultData } from './types-BxPfGHKO.js';
|
|
4
|
+
import { McpUiDownloadFileResult } from '@modelcontextprotocol/ext-apps';
|
|
5
5
|
export { McpUiHostContext } from '@modelcontextprotocol/ext-apps';
|
|
6
|
+
export { C as ConnectUIOptions, a as HostCapabilities, b as HostKind, c as HostUnsupportedError, d as SYNAPSE_DATA_ELEMENT_ID, e as SynapseUIClient, S as SynapseUITheme, T as ToolCallError, g as connectUI, h as detectHostKind } from './detect-DVGaL2bH.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Connect to an MCP Apps host.
|
|
9
10
|
*
|
|
10
|
-
* The one entry point
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The one entry point. The protocol underneath is the spec's own client,
|
|
12
|
+
* `@modelcontextprotocol/ext-apps`'s `App`: it owns the transport, the
|
|
13
|
+
* handshake and the wire schemas. What this adds is the framework on top —
|
|
14
|
+
* theme injection, the parsed payloads, multi-subscriber events, resize, and
|
|
15
|
+
* the NimbleBrain extensions — and it resolves to a ready {@link App}.
|
|
13
16
|
*
|
|
14
17
|
* The `App` it returns stays deliberately small. NimbleBrain's own extensions
|
|
15
|
-
* (`action`, the file picker, `downloadFile`
|
|
18
|
+
* (`action`, the file picker), `downloadFile` and the MCP tasks utility are
|
|
16
19
|
* composable functions over it — import them from the package root.
|
|
17
20
|
*/
|
|
18
21
|
declare function connect(options: ConnectOptions): Promise<App>;
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* A NimbleBrain extension (`synapse/` prefix), because the ext-apps spec has no
|
|
24
|
-
* equivalent: `styles.variables` is a flat `Record<McpUiStyleVariableKey, string>`
|
|
25
|
-
* that can name a font family but cannot carry the rule that loads it.
|
|
26
|
-
* `McpUiHostContext` declares `[key: string]: unknown` for forward
|
|
27
|
-
* compatibility, so riding an extra key is spec-legal, and hosts that don't send
|
|
28
|
-
* it degrade to the web-safe token fallbacks.
|
|
29
|
-
*/
|
|
30
|
-
declare const FONT_FACES_CONTEXT_KEY = "synapse/fontFaces";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Trigger a host-side action.
|
|
24
|
+
* Hand the user a file to save, over the ext-apps `ui/download-file` request.
|
|
34
25
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
26
|
+
* A function over {@link App} rather than a method on it, like the helpers in
|
|
27
|
+
* `extensions.ts` — but unlike them this is spec surface, so it is not gated on
|
|
28
|
+
* the host identity. Any host that advertises `downloadFile` can answer it.
|
|
29
|
+
*
|
|
30
|
+
* The bytes always travel embedded. The spec also allows a `ResourceLink`, which
|
|
31
|
+
* asks the host to fetch a URI the app names; a host holding the user's session
|
|
32
|
+
* has every reason to refuse that, and the NimbleBrain host does.
|
|
38
33
|
*/
|
|
39
|
-
|
|
34
|
+
|
|
40
35
|
/**
|
|
41
36
|
* Hand the user a file to save.
|
|
42
37
|
*
|
|
38
|
+
* A string is sent as the resource's `text`; a `Blob` is read and sent as
|
|
39
|
+
* base64 `blob`. Reading a `Blob` is asynchronous, which is why this returns a
|
|
40
|
+
* promise.
|
|
41
|
+
*
|
|
43
42
|
* Precedence for the MIME type: the explicit argument, then the Blob's own
|
|
44
43
|
* type, then `application/octet-stream`. An empty-string argument falls
|
|
45
44
|
* through — a `""` MIME is effectively "no type".
|
|
45
|
+
*
|
|
46
|
+
* The filename is the last segment of the resource's `file:///` URI, because an
|
|
47
|
+
* embedded resource has no name field of its own.
|
|
48
|
+
*
|
|
49
|
+
* Resolves with the host's result: `{ isError: true }` when the host declined
|
|
50
|
+
* or the user cancelled. Rejects with `HostCapabilityError`, without sending,
|
|
51
|
+
* when the host did not declare `downloadFile` — a host that does not implement
|
|
52
|
+
* the request may never answer it, and a request has no deadline.
|
|
53
|
+
*/
|
|
54
|
+
declare function downloadFile(app: App, filename: string, content: string | Blob, mimeType?: string): Promise<McpUiDownloadFileResult>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Thrown when an app asks for something the host did not declare in
|
|
58
|
+
* `ui/initialize`.
|
|
59
|
+
*
|
|
60
|
+
* The request is never sent. A host that does not implement a request may never
|
|
61
|
+
* answer it, and this SDK's requests carry no deadline, so sending anyway would
|
|
62
|
+
* leave the caller waiting forever rather than failing.
|
|
63
|
+
*
|
|
64
|
+
* `capability` names what was missing, as the host would have declared it: a
|
|
65
|
+
* `hostCapabilities` field (`serverTools`, `downloadFile`) or an extension
|
|
66
|
+
* identifier (`ai.nimblebrain/request-file`, `io.modelcontextprotocol/tasks`).
|
|
67
|
+
*/
|
|
68
|
+
declare class HostCapabilityError extends Error {
|
|
69
|
+
readonly capability: string;
|
|
70
|
+
constructor(feature: string, capability: string);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The NimbleBrain host extensions. This is the complete list; anything else an
|
|
75
|
+
* app sends is spec.
|
|
76
|
+
*
|
|
77
|
+
* Each extension has one name, used both as its method and as the identifier a
|
|
78
|
+
* host declares in `hostCapabilities.experimental` to offer it. The ext-apps
|
|
79
|
+
* host capability type has no field for extensions, and a spec client parses
|
|
80
|
+
* the `ui/initialize` result against that type, so `experimental` is the one
|
|
81
|
+
* slot whose contents reach the app. The MCP tasks capability travels the same
|
|
82
|
+
* way, for the same reason.
|
|
83
|
+
*
|
|
84
|
+
* The gate is the declaration, not the host's name. A host that implements an
|
|
85
|
+
* extension says so; a host that does not is never sent it.
|
|
86
|
+
*/
|
|
87
|
+
declare const NIMBLEBRAIN_EXTENSIONS: {
|
|
88
|
+
/** App → host notification: run a host action (navigate, open a panel). */
|
|
89
|
+
readonly action: {
|
|
90
|
+
readonly method: "ai.nimblebrain/action";
|
|
91
|
+
readonly capability: "ai.nimblebrain/action";
|
|
92
|
+
};
|
|
93
|
+
/** App → host request: the host's file picker, answered `{ files }`. */
|
|
94
|
+
readonly requestFile: {
|
|
95
|
+
readonly method: "ai.nimblebrain/request-file";
|
|
96
|
+
readonly capability: "ai.nimblebrain/request-file";
|
|
97
|
+
};
|
|
98
|
+
/** App → host notification: a keyboard shortcut pressed inside the frame. */
|
|
99
|
+
readonly keydown: {
|
|
100
|
+
readonly method: "ai.nimblebrain/keydown";
|
|
101
|
+
readonly capability: "ai.nimblebrain/keydown";
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
type NimbleBrainExtension = keyof typeof NIMBLEBRAIN_EXTENSIONS;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Whether the host declared a NimbleBrain extension. Use it to decide what to
|
|
108
|
+
* offer, e.g. to hide an upload button on a host with no file picker.
|
|
46
109
|
*/
|
|
47
|
-
declare function
|
|
110
|
+
declare function hostSupports(app: App, extension: NimbleBrainExtension): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Trigger a host-side action.
|
|
113
|
+
*
|
|
114
|
+
* Sends a command *to* the host (navigate, open a panel). A no-op when the host
|
|
115
|
+
* did not declare `ai.nimblebrain/action`.
|
|
116
|
+
*/
|
|
117
|
+
declare function action(app: App, name: string, params?: Record<string, unknown>): void;
|
|
48
118
|
/**
|
|
49
119
|
* Request one file from the user via the host's native file picker.
|
|
50
120
|
*
|
|
51
|
-
* Resolves `null` if the user cancels.
|
|
121
|
+
* Resolves `null` if the user cancels. Rejects with `HostCapabilityError` when
|
|
122
|
+
* the host did not declare `ai.nimblebrain/request-file`.
|
|
52
123
|
*/
|
|
53
124
|
declare function pickFile(app: App, options?: RequestFileOptions): Promise<FileResult | null>;
|
|
54
125
|
/**
|
|
55
126
|
* Request several files from the user.
|
|
56
127
|
*
|
|
57
|
-
* Resolves `[]` if the user cancels.
|
|
128
|
+
* Resolves `[]` if the user cancels. Rejects with `HostCapabilityError` when
|
|
129
|
+
* the host did not declare `ai.nimblebrain/request-file`.
|
|
58
130
|
*/
|
|
59
131
|
declare function pickFiles(app: App, options?: RequestFileOptions): Promise<FileResult[]>;
|
|
60
132
|
|
|
@@ -72,10 +144,11 @@ declare function pickFiles(app: App, options?: RequestFileOptions): Promise<File
|
|
|
72
144
|
* const result = await handle.result();
|
|
73
145
|
* ```
|
|
74
146
|
*
|
|
75
|
-
*
|
|
76
|
-
* a requestor MUST NOT task-augment
|
|
77
|
-
*
|
|
147
|
+
* Rejects with `HostCapabilityError` if the host did not advertise
|
|
148
|
+
* `tasks.requests.tools.call` — per spec a requestor MUST NOT task-augment
|
|
149
|
+
* without matching receiver capability. Check `app.supportsTasks` first, and
|
|
150
|
+
* fall back to `app.callTool`.
|
|
78
151
|
*/
|
|
79
152
|
declare function callToolAsTask<TOutput = unknown>(app: App, toolName: string, args?: unknown, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>>;
|
|
80
153
|
|
|
81
|
-
export { App, CallToolAsTaskOptions, ConnectOptions,
|
|
154
|
+
export { App, CallToolAsTaskOptions, ConnectOptions, FileResult, HostCapabilityError, NIMBLEBRAIN_EXTENSIONS, type NimbleBrainExtension, RequestFileOptions, TaskHandle, action, callToolAsTask, connect, downloadFile, hostSupports, pickFile, pickFiles };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
export { HostUnsupportedError, SYNAPSE_DATA_ELEMENT_ID, connectUI, detectHostKind } from './chunk-
|
|
2
|
-
|
|
3
|
-
export {
|
|
1
|
+
export { HostUnsupportedError, SYNAPSE_DATA_ELEMENT_ID, ToolCallError, connectUI, detectHostKind } from './chunk-UKP4W6DI.js';
|
|
2
|
+
import { HostCapabilityError, internalsFor } from './chunk-TR2YNWZ5.js';
|
|
3
|
+
export { HostCapabilityError, action, callToolAsTask, connect, hostSupports, pickFile, pickFiles } from './chunk-TR2YNWZ5.js';
|
|
4
|
+
import './chunk-DJS2FKBN.js';
|
|
5
|
+
export { NIMBLEBRAIN_EXTENSIONS } from './chunk-H3INUKA3.js';
|
|
6
|
+
import { DOWNLOAD_FILE_METHOD } from '@modelcontextprotocol/ext-apps';
|
|
7
|
+
|
|
8
|
+
var BASE64_CHUNK = 32768;
|
|
9
|
+
async function downloadFile(app, filename, content, mimeType) {
|
|
10
|
+
if (!app.hostCapabilities.downloadFile) {
|
|
11
|
+
throw new HostCapabilityError("downloadFile", "downloadFile");
|
|
12
|
+
}
|
|
13
|
+
const internals = internalsFor(app);
|
|
14
|
+
const resolvedMime = mimeType || (content instanceof Blob ? content.type : "") || "application/octet-stream";
|
|
15
|
+
const uri = `file:///${filename}`;
|
|
16
|
+
const resource = typeof content === "string" ? { uri, mimeType: resolvedMime, text: content } : { uri, mimeType: resolvedMime, blob: await toBase64(content) };
|
|
17
|
+
const params = {
|
|
18
|
+
contents: [{ type: "resource", resource }]
|
|
19
|
+
};
|
|
20
|
+
return await internals.request(
|
|
21
|
+
DOWNLOAD_FILE_METHOD,
|
|
22
|
+
params
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
async function toBase64(blob) {
|
|
26
|
+
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
27
|
+
let binary = "";
|
|
28
|
+
for (let i = 0; i < bytes.length; i += BASE64_CHUNK) {
|
|
29
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));
|
|
30
|
+
}
|
|
31
|
+
return btoa(binary);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { downloadFile };
|
|
4
35
|
//# sourceMappingURL=index.js.map
|
|
5
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
1
|
+
{"version":3,"sources":["../src/download-file.ts"],"names":[],"mappings":";;;;;;;AAsBA,IAAM,YAAA,GAAe,KAAA;AAqBrB,eAAsB,YAAA,CACpB,GAAA,EACA,QAAA,EACA,OAAA,EACA,QAAA,EACkC;AAClC,EAAA,IAAI,CAAC,GAAA,CAAI,gBAAA,CAAiB,YAAA,EAAc;AACtC,IAAA,MAAM,IAAI,mBAAA,CAAoB,cAAA,EAAgB,cAAc,CAAA;AAAA,EAC9D;AACA,EAAA,MAAM,SAAA,GAAY,aAAa,GAAG,CAAA;AAClC,EAAA,MAAM,eACJ,QAAA,KAAa,OAAA,YAAmB,IAAA,GAAO,OAAA,CAAQ,OAAO,EAAA,CAAA,IAAO,0BAAA;AAC/D,EAAA,MAAM,GAAA,GAAM,WAAW,QAAQ,CAAA,CAAA;AAC/B,EAAA,MAAM,WACJ,OAAO,OAAA,KAAY,WACf,EAAE,GAAA,EAAK,UAAU,YAAA,EAAc,IAAA,EAAM,SAAQ,GAC7C,EAAE,KAAK,QAAA,EAAU,YAAA,EAAc,MAAM,MAAM,QAAA,CAAS,OAAO,CAAA,EAAE;AACnE,EAAA,MAAM,MAAA,GAA6C;AAAA,IACjD,UAAU,CAAC,EAAE,IAAA,EAAM,UAAA,EAAY,UAAU;AAAA,GAC3C;AACA,EAAA,OAAQ,MAAM,SAAA,CAAU,OAAA;AAAA,IACtB,oBAAA;AAAA,IACA;AAAA,GACF;AACF;AAEA,eAAe,SAAS,IAAA,EAA6B;AACnD,EAAA,MAAM,QAAQ,IAAI,UAAA,CAAW,MAAM,IAAA,CAAK,aAAa,CAAA;AACrD,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,YAAA,EAAc;AACnD,IAAA,MAAA,IAAU,MAAA,CAAO,aAAa,GAAG,KAAA,CAAM,SAAS,CAAA,EAAG,CAAA,GAAI,YAAY,CAAC,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,KAAK,MAAM,CAAA;AACpB","file":"index.js","sourcesContent":["/**\n * Hand the user a file to save, over the ext-apps `ui/download-file` request.\n *\n * A function over {@link App} rather than a method on it, like the helpers in\n * `extensions.ts` — but unlike them this is spec surface, so it is not gated on\n * the host identity. Any host that advertises `downloadFile` can answer it.\n *\n * The bytes always travel embedded. The spec also allows a `ResourceLink`, which\n * asks the host to fetch a URI the app names; a host holding the user's session\n * has every reason to refuse that, and the NimbleBrain host does.\n */\nimport {\n DOWNLOAD_FILE_METHOD,\n type McpUiDownloadFileRequest,\n type McpUiDownloadFileResult,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { EmbeddedResource } from \"@modelcontextprotocol/sdk/types.js\";\nimport { HostCapabilityError } from \"./errors.js\";\nimport { internalsFor } from \"./internals.js\";\nimport type { App } from \"./types.js\";\n\n/** Bytes per `String.fromCharCode` spread — well under any engine's argument limit. */\nconst BASE64_CHUNK = 0x8000;\n\n/**\n * Hand the user a file to save.\n *\n * A string is sent as the resource's `text`; a `Blob` is read and sent as\n * base64 `blob`. Reading a `Blob` is asynchronous, which is why this returns a\n * promise.\n *\n * Precedence for the MIME type: the explicit argument, then the Blob's own\n * type, then `application/octet-stream`. An empty-string argument falls\n * through — a `\"\"` MIME is effectively \"no type\".\n *\n * The filename is the last segment of the resource's `file:///` URI, because an\n * embedded resource has no name field of its own.\n *\n * Resolves with the host's result: `{ isError: true }` when the host declined\n * or the user cancelled. Rejects with `HostCapabilityError`, without sending,\n * when the host did not declare `downloadFile` — a host that does not implement\n * the request may never answer it, and a request has no deadline.\n */\nexport async function downloadFile(\n app: App,\n filename: string,\n content: string | Blob,\n mimeType?: string,\n): Promise<McpUiDownloadFileResult> {\n if (!app.hostCapabilities.downloadFile) {\n throw new HostCapabilityError(\"downloadFile\", \"downloadFile\");\n }\n const internals = internalsFor(app);\n const resolvedMime =\n mimeType || (content instanceof Blob ? content.type : \"\") || \"application/octet-stream\";\n const uri = `file:///${filename}`;\n const resource: EmbeddedResource[\"resource\"] =\n typeof content === \"string\"\n ? { uri, mimeType: resolvedMime, text: content }\n : { uri, mimeType: resolvedMime, blob: await toBase64(content) };\n const params: McpUiDownloadFileRequest[\"params\"] = {\n contents: [{ type: \"resource\", resource }],\n };\n return (await internals.request(\n DOWNLOAD_FILE_METHOD,\n params as unknown as Record<string, unknown>,\n )) as McpUiDownloadFileResult;\n}\n\nasync function toBase64(blob: Blob): Promise<string> {\n const bytes = new Uint8Array(await blob.arrayBuffer());\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));\n }\n return btoa(binary);\n}\n"]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-
|
|
3
|
+
var chunkHQLSVUOR_cjs = require('../chunk-HQLSVUOR.cjs');
|
|
4
|
+
require('../chunk-YSL7KJNU.cjs');
|
|
5
|
+
var chunkISV6HFQE_cjs = require('../chunk-ISV6HFQE.cjs');
|
|
5
6
|
var react = require('react');
|
|
6
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
8
|
|
|
@@ -13,7 +14,7 @@ function AppProvider({ children, ...options }) {
|
|
|
13
14
|
react.useEffect(() => {
|
|
14
15
|
if (connectingRef.current) return;
|
|
15
16
|
connectingRef.current = true;
|
|
16
|
-
|
|
17
|
+
chunkHQLSVUOR_cjs.connect(options).then(setApp, (err) => {
|
|
17
18
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
18
19
|
});
|
|
19
20
|
}, []);
|
|
@@ -78,12 +79,12 @@ function useCallTool(toolName) {
|
|
|
78
79
|
const [data, setData] = react.useState(null);
|
|
79
80
|
const callIdRef = react.useRef(0);
|
|
80
81
|
const call = react.useCallback(
|
|
81
|
-
async (args
|
|
82
|
+
async (args) => {
|
|
82
83
|
const id = ++callIdRef.current;
|
|
83
84
|
setIsPending(true);
|
|
84
85
|
setError(null);
|
|
85
86
|
try {
|
|
86
|
-
const result = await app.callTool(toolName, args
|
|
87
|
+
const result = await app.callTool(toolName, args);
|
|
87
88
|
if (id === callIdRef.current) {
|
|
88
89
|
setData(result.data);
|
|
89
90
|
setIsPending(false);
|
|
@@ -107,7 +108,7 @@ function useDataSync(callback) {
|
|
|
107
108
|
const callbackRef = react.useRef(callback);
|
|
108
109
|
callbackRef.current = callback;
|
|
109
110
|
react.useEffect(() => {
|
|
110
|
-
return app.on(
|
|
111
|
+
return app.on(chunkISV6HFQE_cjs.RESOURCE_LIST_CHANGED_METHOD, (params) => callbackRef.current(params ?? {}));
|
|
111
112
|
}, [app]);
|
|
112
113
|
}
|
|
113
114
|
var MODEL_CONTEXT_DEBOUNCE_MS = 250;
|
|
@@ -148,7 +149,7 @@ function useSendMessage() {
|
|
|
148
149
|
function useAction() {
|
|
149
150
|
const app = useAppContext();
|
|
150
151
|
return react.useCallback(
|
|
151
|
-
(name, params) =>
|
|
152
|
+
(name, params) => chunkHQLSVUOR_cjs.action(app, name, params),
|
|
152
153
|
[app]
|
|
153
154
|
);
|
|
154
155
|
}
|
|
@@ -159,7 +160,7 @@ function useFileUpload() {
|
|
|
159
160
|
async (options) => {
|
|
160
161
|
setIsPending(true);
|
|
161
162
|
try {
|
|
162
|
-
return await
|
|
163
|
+
return await chunkHQLSVUOR_cjs.pickFile(app, options);
|
|
163
164
|
} finally {
|
|
164
165
|
setIsPending(false);
|
|
165
166
|
}
|
|
@@ -170,7 +171,7 @@ function useFileUpload() {
|
|
|
170
171
|
async (options) => {
|
|
171
172
|
setIsPending(true);
|
|
172
173
|
try {
|
|
173
|
-
return await
|
|
174
|
+
return await chunkHQLSVUOR_cjs.pickFiles(app, options);
|
|
174
175
|
} finally {
|
|
175
176
|
setIsPending(false);
|
|
176
177
|
}
|
|
@@ -255,7 +256,7 @@ function useCallToolAsTask(toolName) {
|
|
|
255
256
|
refreshFailureCountRef.current = 0;
|
|
256
257
|
let handle;
|
|
257
258
|
try {
|
|
258
|
-
handle = await
|
|
259
|
+
handle = await chunkHQLSVUOR_cjs.callToolAsTask(app, toolName, args, options);
|
|
259
260
|
} catch (err) {
|
|
260
261
|
if (gen !== genRef.current) throw err;
|
|
261
262
|
const e = err instanceof Error ? err : new Error(String(err));
|