@miragon/mcp-toolkit-ui 0.10.1 → 1.0.1
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 +7 -7
- package/dist/app/adapt-data-widget.d.ts +26 -5
- package/dist/app/adapt-data-widget.d.ts.map +1 -1
- package/dist/app/adapt-data-widget.js +39 -5
- package/dist/app/adapt-data-widget.js.map +1 -1
- package/dist/app/host-bridge.d.ts +48 -42
- package/dist/app/host-bridge.d.ts.map +1 -1
- package/dist/app/host-bridge.js +28 -50
- package/dist/app/host-bridge.js.map +1 -1
- package/dist/app/index.d.ts +5 -4
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +9 -8
- package/dist/app/index.js.map +1 -1
- package/dist/app/mcp-app-view.d.ts.map +1 -1
- package/dist/app/mcp-app-view.js +37 -24
- package/dist/app/mcp-app-view.js.map +1 -1
- package/dist/app/mcp-toolkit-app.d.ts +21 -10
- package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
- package/dist/app/mcp-toolkit-app.js +26 -12
- package/dist/app/mcp-toolkit-app.js.map +1 -1
- package/dist/app/mcp-use-host-bridge.d.ts +29 -0
- package/dist/app/mcp-use-host-bridge.d.ts.map +1 -0
- package/dist/app/mcp-use-host-bridge.js +120 -0
- package/dist/app/mcp-use-host-bridge.js.map +1 -0
- package/dist/app/use-host-actions.d.ts +6 -6
- package/dist/app/use-host-actions.js +6 -6
- package/dist/app/widget-fixture.d.ts +24 -21
- package/dist/app/widget-fixture.d.ts.map +1 -1
- package/dist/app/widget-fixture.js +27 -123
- package/dist/app/widget-fixture.js.map +1 -1
- package/dist/hooks/use-tool-result-recovery.d.ts +40 -15
- package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
- package/dist/hooks/use-tool-result-recovery.js +57 -4
- package/dist/hooks/use-tool-result-recovery.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +16 -8
- package/src/app/adapt-data-widget.tsx +50 -6
- package/src/app/host-bridge.test.ts +107 -0
- package/src/app/host-bridge.tsx +81 -78
- package/src/app/index.ts +15 -10
- package/src/app/mcp-app-view.tsx +41 -32
- package/src/app/mcp-toolkit-app.tsx +33 -16
- package/src/app/mcp-use-host-bridge.tsx +164 -0
- package/src/app/use-host-actions.tsx +6 -6
- package/src/app/widget-fixture.tsx +35 -156
- package/src/hooks/use-tool-result-recovery.ts +75 -19
- package/src/index.ts +1 -1
- package/ui-catalog.json +80 -26
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { createContext, useContext,
|
|
3
|
-
import { useWidget } from "mcp-use/react";
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
3
|
import { AppQueryProvider } from "../providers/query-provider.js";
|
|
5
4
|
import { HostBridgeProvider, createStandaloneHostBridge } from "./host-bridge.js";
|
|
6
|
-
/**
|
|
7
|
-
* Key `mcp-use` writes the serialized model-context string under when it flushes
|
|
8
|
-
* to `window.openai.setWidgetState`. Mirrors `MODEL_CONTEXT_KEY` in
|
|
9
|
-
* `mcp-use/react` (not exported, so duplicated here). The harness intercepts
|
|
10
|
-
* `setWidgetState` and reads this field to surface what the model would see.
|
|
11
|
-
*/
|
|
12
|
-
const MODEL_CONTEXT_KEY = "__model_context";
|
|
13
5
|
/**
|
|
14
6
|
* Pure, host-free registry of fake tool implementations. Drives every
|
|
15
7
|
* `callTool` path the harness exposes (`window.openai.callTool`, the toolkit's
|
|
@@ -89,9 +81,10 @@ export function useFixtureHost() {
|
|
|
89
81
|
return useContext(FixtureHostContext);
|
|
90
82
|
}
|
|
91
83
|
/**
|
|
92
|
-
* Default host-context
|
|
93
|
-
*
|
|
94
|
-
*
|
|
84
|
+
* Default host-context values the harness reports. Only `theme` is read since
|
|
85
|
+
* the mcp-use 2.x move (the other host globals were consumed by the 1.x
|
|
86
|
+
* `window.openai` shim); the object keeps its shape so existing playground
|
|
87
|
+
* setups that spread overrides keep compiling.
|
|
95
88
|
*/
|
|
96
89
|
const DEFAULT_GLOBALS = {
|
|
97
90
|
theme: "light",
|
|
@@ -104,60 +97,6 @@ const DEFAULT_GLOBALS = {
|
|
|
104
97
|
capabilities: { hover: true, touch: false },
|
|
105
98
|
},
|
|
106
99
|
};
|
|
107
|
-
/**
|
|
108
|
-
* Build the `window.openai` shim the `mcp-use` React hooks read from. Routes
|
|
109
|
-
* `callTool` through the fixture registry and reports every other bridge verb
|
|
110
|
-
* to `onHostAction`. Returns `null` outside a browser-like environment (SSR)
|
|
111
|
-
* so the harness degrades to a plain render rather than throwing.
|
|
112
|
-
*/
|
|
113
|
-
function buildOpenAiShim(registry, toolOutput, toolInput, globals, onHostAction, onModelContext) {
|
|
114
|
-
if (typeof window === "undefined")
|
|
115
|
-
return null;
|
|
116
|
-
return {
|
|
117
|
-
...DEFAULT_GLOBALS,
|
|
118
|
-
...globals,
|
|
119
|
-
toolInput,
|
|
120
|
-
toolOutput,
|
|
121
|
-
toolResponseMetadata: null,
|
|
122
|
-
widgetState: null,
|
|
123
|
-
callTool: async (name, args) => {
|
|
124
|
-
onHostAction?.({ type: "callTool", name, args });
|
|
125
|
-
return registry.call(name, args);
|
|
126
|
-
},
|
|
127
|
-
sendFollowUpMessage: (args) => {
|
|
128
|
-
onHostAction?.({ type: "sendFollowUpMessage", prompt: args.prompt });
|
|
129
|
-
return Promise.resolve();
|
|
130
|
-
},
|
|
131
|
-
openExternal: (payload) => {
|
|
132
|
-
onHostAction?.({ type: "openExternal", href: payload.href });
|
|
133
|
-
},
|
|
134
|
-
requestDisplayMode: (args) => {
|
|
135
|
-
onHostAction?.({ type: "requestDisplayMode", mode: args.mode });
|
|
136
|
-
return Promise.resolve({ mode: args.mode });
|
|
137
|
-
},
|
|
138
|
-
setWidgetState: (state) => {
|
|
139
|
-
onHostAction?.({ type: "setWidgetState", state });
|
|
140
|
-
// `mcp-use` flushes the serialized `<ModelContext>` tree through
|
|
141
|
-
// setWidgetState under MODEL_CONTEXT_KEY — surface it as the model view.
|
|
142
|
-
const ctx = state?.[MODEL_CONTEXT_KEY];
|
|
143
|
-
if (typeof ctx === "string")
|
|
144
|
-
onModelContext?.(ctx);
|
|
145
|
-
return Promise.resolve();
|
|
146
|
-
},
|
|
147
|
-
notifyIntrinsicHeight: () => Promise.resolve(),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Renders nothing but mounts a `useWidget()` so `mcp-use` registers its
|
|
152
|
-
* model-context flush handler — even for widgets that never call `useWidget`
|
|
153
|
-
* themselves (e.g. a simple `({ keys })` card). Without this, a widget's
|
|
154
|
-
* `<ModelContext>` would render but never flush, and `onModelContext` would
|
|
155
|
-
* stay silent. The flush routes through our `setWidgetState` shim above.
|
|
156
|
-
*/
|
|
157
|
-
function ModelContextPump() {
|
|
158
|
-
useWidget();
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
100
|
/**
|
|
162
101
|
* Build the `WidgetProps` envelope the harness passes to the widget. Seeds a
|
|
163
102
|
* single pipeline step from `data` (keyed by `dataType` when given) and uses
|
|
@@ -186,66 +125,37 @@ export function buildFixtureWidgetProps(data, dataType) {
|
|
|
186
125
|
* without booting a real MCP host or backend.
|
|
187
126
|
*
|
|
188
127
|
* ### What it mocks
|
|
189
|
-
* -
|
|
190
|
-
*
|
|
191
|
-
* {@link FixtureCallToolRegistry} built from `tools`;
|
|
192
|
-
* `openExternal
|
|
128
|
+
* - **A {@link HostBridge}** — a `createStandaloneHostBridge` over the fixture
|
|
129
|
+
* registry and callbacks, provided via `HostBridgeProvider`. `callTool`
|
|
130
|
+
* resolves against the {@link FixtureCallToolRegistry} built from `tools`;
|
|
131
|
+
* `sendFollowup` / `openExternal` are reported to `onHostAction`;
|
|
132
|
+
* `setModelContext` feeds `onModelContext`, so you can *see* what the model
|
|
133
|
+
* would (adapted widgets report through this bridge outside a real view).
|
|
193
134
|
* - **The toolkit `AppQueryProvider`** — so widgets that fetch via `useToolQuery`
|
|
194
135
|
* (e.g. generated `use*` hooks) resolve against the same registry.
|
|
195
|
-
* - **`ModelContext`** — a flush handler captures the serialized context string
|
|
196
|
-
* and forwards it to `onModelContext`, so you can *see* what the model would.
|
|
197
|
-
* - **A {@link HostBridge}** — a `createStandaloneHostBridge` over the same
|
|
198
|
-
* registry and callbacks, provided via `HostBridgeProvider`, so widgets
|
|
199
|
-
* written against `useHostBridge` (the host-portable pattern) are simulated
|
|
200
|
-
* from the same single source as the `window.openai` shim above.
|
|
201
136
|
*
|
|
202
137
|
* ### Boundaries
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
138
|
+
* The harness simulates the **host-portable pattern** (`useHostBridge`,
|
|
139
|
+
* `useToolQuery`) — the surface the toolkit's skills teach. It does not
|
|
140
|
+
* simulate the mcp-use 2.x view runtime: since 2.x the `mcp-use/react` hooks
|
|
141
|
+
* only run under a `bootstrapView`-mounted view speaking the ext-apps
|
|
142
|
+
* postMessage protocol (the 1.x `window.openai` shim this harness used to
|
|
143
|
+
* install has no equivalent seam). A widget that calls `mcp-use/react` hooks
|
|
144
|
+
* directly must be exercised against a real host — or rewritten against
|
|
145
|
+
* `useHostBridge`, which is the portable contract anyway.
|
|
208
146
|
*/
|
|
209
147
|
export function WidgetFixtureHost({ widget: Widget, data = {}, dataType, tools, globals, onModelContext, onHostAction, }) {
|
|
210
148
|
// One registry per `tools` identity. Rebuilt when the fixture map changes so
|
|
211
149
|
// edits in the playground take effect without a full remount.
|
|
212
150
|
const registry = useMemo(() => new FixtureCallToolRegistry(tools), [tools]);
|
|
213
|
-
// Latest callbacks without re-installing the shim on every render. Updated in
|
|
214
|
-
// an effect (not during render) so the shim's closures always see the newest
|
|
215
|
-
// handler while the `window.openai` install effect stays stable.
|
|
216
|
-
const onHostActionRef = useRef(onHostAction);
|
|
217
|
-
const onModelContextRef = useRef(onModelContext);
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
onHostActionRef.current = onHostAction;
|
|
220
|
-
onModelContextRef.current = onModelContext;
|
|
221
|
-
});
|
|
222
151
|
const widgetProps = useMemo(() => buildFixtureWidgetProps(data, dataType), [data, dataType]);
|
|
223
|
-
// Install the `window.openai` shim for the lifetime of this mount. Wake any
|
|
224
|
-
// already-mounted `mcp-use` hooks via the `set_globals` event they listen for.
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
if (typeof window === "undefined")
|
|
227
|
-
return;
|
|
228
|
-
const shim = buildOpenAiShim(registry, data ?? null, data ?? {}, globals ?? {}, (action) => onHostActionRef.current?.(action), (text) => onModelContextRef.current?.(text));
|
|
229
|
-
if (!shim)
|
|
230
|
-
return;
|
|
231
|
-
const prev = window.openai;
|
|
232
|
-
window.openai = shim;
|
|
233
|
-
window.dispatchEvent(new CustomEvent("openai:set_globals", { detail: { globals: shim } }));
|
|
234
|
-
return () => {
|
|
235
|
-
;
|
|
236
|
-
window.openai = prev;
|
|
237
|
-
};
|
|
238
|
-
}, [registry, data, globals]);
|
|
239
152
|
const callTool = useMemo(() => async (name, args) => registry.call(name, args ?? {}), [registry]);
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
// the bridge's reported theme in sync with the shim. Depends on the callback
|
|
247
|
-
// props directly (not the refs) — a fresh Provider value on callback change is
|
|
248
|
-
// fine for a dev harness, and keeps the bridge's closures pure of ref reads.
|
|
153
|
+
// The standalone host bridge is the harness's single simulation source: it
|
|
154
|
+
// logs every verb through the `HostActionLog` channel for the playground's
|
|
155
|
+
// activity log. Depends on the callback props directly — a fresh Provider
|
|
156
|
+
// value on callback change is fine for a dev harness. Only `globals.theme`
|
|
157
|
+
// still influences the simulated host (the remaining globals fed the 1.x
|
|
158
|
+
// `window.openai` shim).
|
|
249
159
|
const bridgeTheme = (globals?.theme ?? DEFAULT_GLOBALS.theme) === "dark" ? "dark" : "light";
|
|
250
160
|
const bridge = useMemo(() => createStandaloneHostBridge({
|
|
251
161
|
callTool: (name, args) => {
|
|
@@ -259,15 +169,9 @@ export function WidgetFixtureHost({ widget: Widget, data = {}, dataType, tools,
|
|
|
259
169
|
theme: bridgeTheme,
|
|
260
170
|
}), [registry, data, bridgeTheme, onHostAction, onModelContext]);
|
|
261
171
|
const value = useMemo(() => ({ registry }), [registry]);
|
|
262
|
-
// Mount the model-context pump only on the client. The pump calls `mcp-use`'s
|
|
263
|
-
// `useWidget`, whose `useSyncExternalStore` lacks a server snapshot and throws
|
|
264
|
-
// under `react-dom/server` — gating it keeps the harness (and any widget
|
|
265
|
-
// rendered through it) SSR-safe. `useSyncExternalStore` with a `false` server
|
|
266
|
-
// snapshot is the idiomatic "are we on the client?" check (no setState-in-effect).
|
|
267
|
-
const isClient = useSyncExternalStore(() => () => { }, () => true, () => false);
|
|
268
172
|
// Spread `widgetProps` (keys/context) AND a `data` alias so both adapted
|
|
269
173
|
// widgets and raw `({ data })` widgets read their fixture from one mount.
|
|
270
174
|
const RenderWidget = Widget;
|
|
271
|
-
return (
|
|
175
|
+
return (_jsx(FixtureHostContext.Provider, { value: value, children: _jsx(HostBridgeProvider, { bridge: bridge, children: _jsx(AppQueryProvider, { callTool: callTool, children: _jsx(RenderWidget, { ...widgetProps, data: data }) }) }) }));
|
|
272
176
|
}
|
|
273
177
|
//# sourceMappingURL=widget-fixture.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-fixture.js","sourceRoot":"","sources":["../../src/app/widget-fixture.tsx"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"widget-fixture.js","sourceRoot":"","sources":["../../src/app/widget-fixture.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,37 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recovery for hosts that deliver the `ui/notifications/tool-result`
|
|
3
3
|
* notification without `structuredContent` (observed on claude.ai and Claude
|
|
4
|
-
* Desktop, which forward only the model-facing `content` blocks).
|
|
5
|
-
* mcp-use
|
|
6
|
-
*
|
|
4
|
+
* Desktop, which forward only the model-facing `content` blocks). Since
|
|
5
|
+
* mcp-use 2.x such a result is not even observable: the `useToolContext`
|
|
6
|
+
* latch ignores content-only successes, so the tool status stays `"pending"`
|
|
7
|
+
* forever and the shell would idle on its loading skeleton.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Because the stripped notification is invisible, readiness is inferred two
|
|
10
|
+
* ways: the hard `resultReady` signal (the latch fired but the payload is
|
|
11
|
+
* invalid), or a grace timer — once the originating tool is known and no
|
|
12
|
+
* result has latched within
|
|
13
|
+
* {@link ToolResultRecoveryOptions.assumeStrippedAfterMs}, the result is
|
|
14
|
+
* assumed delivered-but-stripped. Either way the hook re-executes the
|
|
15
|
+
* originating tool once via the host bridge's official `callTool` — tool-call
|
|
16
|
+
* RESPONSES carry `structuredContent` intact on those hosts — and exposes the
|
|
17
|
+
* decoded payload. Spec-conforming hosts (e.g. the mcp-use inspector) deliver
|
|
18
|
+
* a valid payload up front, so the recovery never fires there.
|
|
14
19
|
*
|
|
15
20
|
* Assumes the originating tool is a read-only render pipeline (true for all
|
|
16
21
|
* toolkit `render-view`/`show_*` tools) — the re-execution must be safe to
|
|
17
|
-
* repeat
|
|
22
|
+
* repeat, and may rarely run concurrently with the original invocation when
|
|
23
|
+
* the grace timer beats a slow but conforming host.
|
|
18
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* Default grace period before a silent latch counts as a stripped result.
|
|
27
|
+
* Long enough for a conforming host to latch a typical render tool first,
|
|
28
|
+
* short enough that users on stripping hosts are not left staring at the
|
|
29
|
+
* skeleton noticeably.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_ASSUME_STRIPPED_AFTER_MS = 2500;
|
|
19
32
|
export interface ToolResultRecoveryOptions {
|
|
20
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The latch fired (`useToolContext().status !== "pending"`) — a structured
|
|
35
|
+
* result or tool error arrived. On hosts that strip `structuredContent`
|
|
36
|
+
* this never turns true; the grace timer below covers that case.
|
|
37
|
+
*/
|
|
21
38
|
resultReady: boolean;
|
|
22
|
-
/** The
|
|
39
|
+
/** The payload the host delivered (`useToolContext().toolOutput`). */
|
|
23
40
|
props: unknown;
|
|
24
41
|
/**
|
|
25
42
|
* Validates a candidate payload. MUST be referentially stable (module-level
|
|
26
43
|
* function or memoised) — it is an effect dependency.
|
|
27
44
|
*/
|
|
28
45
|
isValid: (value: unknown) => boolean;
|
|
29
|
-
/** The tool arguments from `ui/notifications/tool-input` (`
|
|
46
|
+
/** The tool arguments from `ui/notifications/tool-input` (`useToolContext().toolInput`). */
|
|
30
47
|
toolInput: Record<string, unknown> | null | undefined;
|
|
31
|
-
/** Raw host context (`
|
|
48
|
+
/** Raw host context (`useHostContext().hostContext`); `toolInfo` names the originating tool. */
|
|
32
49
|
hostContext: Record<string, unknown> | null | undefined;
|
|
33
|
-
/** The bridge's `callTool
|
|
50
|
+
/** The host bridge's `callTool`. */
|
|
34
51
|
callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
52
|
+
/**
|
|
53
|
+
* Grace period in ms after which a silent latch counts as a stripped
|
|
54
|
+
* result. Armed once the originating tool is known while `resultReady` is
|
|
55
|
+
* still false; re-armed on every `toolInput` update, because streaming
|
|
56
|
+
* arguments mean the invocation may not have finished yet. Defaults to
|
|
57
|
+
* {@link DEFAULT_ASSUME_STRIPPED_AFTER_MS}.
|
|
58
|
+
*/
|
|
59
|
+
assumeStrippedAfterMs?: number;
|
|
35
60
|
}
|
|
36
61
|
export interface ToolResultRecoveryState<T> {
|
|
37
62
|
/** The recovered payload, `null` until the re-execution resolved (or when it failed). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-result-recovery.d.ts","sourceRoot":"","sources":["../../src/hooks/use-tool-result-recovery.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"use-tool-result-recovery.d.ts","sourceRoot":"","sources":["../../src/hooks/use-tool-result-recovery.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,OAAO,CAAA;AAEpD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,KAAK,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAA;IACpC,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IACrD,gGAAgG;IAChG,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IACvD,oCAAoC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3E;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,yFAAyF;IACzF,IAAI,EAAE,CAAC,GAAG,IAAI,CAAA;IACd,oGAAoG;IACpG,MAAM,EAAE,OAAO,CAAA;CAChB;AAID;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAehF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GACnC,CAAC,GAAG,IAAI,CAQV;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,yBAAyB,GACjC,uBAAuB,CAAC,CAAC,CAAC,CAmF5B"}
|
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { parseToolResult } from "../lib/parse-tool-result.js";
|
|
3
|
+
/**
|
|
4
|
+
* Recovery for hosts that deliver the `ui/notifications/tool-result`
|
|
5
|
+
* notification without `structuredContent` (observed on claude.ai and Claude
|
|
6
|
+
* Desktop, which forward only the model-facing `content` blocks). Since
|
|
7
|
+
* mcp-use 2.x such a result is not even observable: the `useToolContext`
|
|
8
|
+
* latch ignores content-only successes, so the tool status stays `"pending"`
|
|
9
|
+
* forever and the shell would idle on its loading skeleton.
|
|
10
|
+
*
|
|
11
|
+
* Because the stripped notification is invisible, readiness is inferred two
|
|
12
|
+
* ways: the hard `resultReady` signal (the latch fired but the payload is
|
|
13
|
+
* invalid), or a grace timer — once the originating tool is known and no
|
|
14
|
+
* result has latched within
|
|
15
|
+
* {@link ToolResultRecoveryOptions.assumeStrippedAfterMs}, the result is
|
|
16
|
+
* assumed delivered-but-stripped. Either way the hook re-executes the
|
|
17
|
+
* originating tool once via the host bridge's official `callTool` — tool-call
|
|
18
|
+
* RESPONSES carry `structuredContent` intact on those hosts — and exposes the
|
|
19
|
+
* decoded payload. Spec-conforming hosts (e.g. the mcp-use inspector) deliver
|
|
20
|
+
* a valid payload up front, so the recovery never fires there.
|
|
21
|
+
*
|
|
22
|
+
* Assumes the originating tool is a read-only render pipeline (true for all
|
|
23
|
+
* toolkit `render-view`/`show_*` tools) — the re-execution must be safe to
|
|
24
|
+
* repeat, and may rarely run concurrently with the original invocation when
|
|
25
|
+
* the grace timer beats a slow but conforming host.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Default grace period before a silent latch counts as a stripped result.
|
|
29
|
+
* Long enough for a conforming host to latch a typical render tool first,
|
|
30
|
+
* short enough that users on stripping hosts are not left staring at the
|
|
31
|
+
* skeleton noticeably.
|
|
32
|
+
*/
|
|
33
|
+
export const DEFAULT_ASSUME_STRIPPED_AFTER_MS = 2500;
|
|
3
34
|
const IDLE = { data: null, failed: false };
|
|
4
35
|
/**
|
|
5
36
|
* Resolves the originating tool name from the host context's `toolInfo`.
|
|
@@ -40,7 +71,7 @@ export function decodeRecoveredResult(result, isValid) {
|
|
|
40
71
|
return decoded !== null && isValid(decoded) ? decoded : null;
|
|
41
72
|
}
|
|
42
73
|
export function useToolResultRecovery(options) {
|
|
43
|
-
const { resultReady, props, isValid, toolInput, hostContext, callTool } = options;
|
|
74
|
+
const { resultReady, props, isValid, toolInput, hostContext, callTool, assumeStrippedAfterMs = DEFAULT_ASSUME_STRIPPED_AFTER_MS, } = options;
|
|
44
75
|
const [state, setState] = useState(IDLE);
|
|
45
76
|
// Single-flight: the promise ref survives StrictMode's dev-mode
|
|
46
77
|
// effect re-run (cleanup + setup on the same instance), so the re-run
|
|
@@ -48,16 +79,38 @@ export function useToolResultRecovery(options) {
|
|
|
48
79
|
const flightRef = useRef(null);
|
|
49
80
|
const flightInputRef = useRef(null);
|
|
50
81
|
const toolName = resolveRecoveryToolName(hostContext);
|
|
82
|
+
// A stripped result is unobservable (the 2.x latch ignores content-only
|
|
83
|
+
// results), so infer it: once a recovery could fire but the latch has been
|
|
84
|
+
// silent for the grace period, treat the result as delivered-but-stripped.
|
|
85
|
+
// Re-armed on every toolInput identity change — streaming arguments mean
|
|
86
|
+
// the invocation may still be running, so the grace period starts over.
|
|
87
|
+
// The re-arm is a render-phase reset (not an effect) so the cascading-
|
|
88
|
+
// render lint stays satisfied; the timer effect below restarts anyway
|
|
89
|
+
// because toolInput is one of its dependencies.
|
|
90
|
+
const [assumedReady, setAssumedReady] = useState(false);
|
|
91
|
+
const [prevTimerInput, setPrevTimerInput] = useState(toolInput);
|
|
92
|
+
if (prevTimerInput !== toolInput) {
|
|
93
|
+
setPrevTimerInput(toolInput);
|
|
94
|
+
if (assumedReady)
|
|
95
|
+
setAssumedReady(false);
|
|
96
|
+
}
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (resultReady || toolName === undefined)
|
|
99
|
+
return;
|
|
100
|
+
const timer = setTimeout(() => setAssumedReady(true), assumeStrippedAfterMs);
|
|
101
|
+
return () => clearTimeout(timer);
|
|
102
|
+
}, [resultReady, toolName, toolInput, assumeStrippedAfterMs]);
|
|
103
|
+
const ready = resultReady || assumedReady;
|
|
51
104
|
useEffect(() => {
|
|
52
105
|
// A new tool run (result no longer ready, or fresh tool-input identity)
|
|
53
106
|
// invalidates any previous recovery attempt and re-arms the single-flight.
|
|
54
|
-
if (flightRef.current && (!
|
|
107
|
+
if (flightRef.current && (!ready || flightInputRef.current !== toolInput)) {
|
|
55
108
|
flightRef.current = null;
|
|
56
109
|
setState((prev) => (prev.data === null && !prev.failed ? prev : IDLE));
|
|
57
110
|
}
|
|
58
111
|
// Never fire on conforming hosts (valid payload) or before the
|
|
59
112
|
// originating tool is known.
|
|
60
|
-
if (!
|
|
113
|
+
if (!ready || isValid(props) || toolName === undefined)
|
|
61
114
|
return;
|
|
62
115
|
if (!flightRef.current) {
|
|
63
116
|
flightInputRef.current = toolInput;
|
|
@@ -87,7 +140,7 @@ export function useToolResultRecovery(options) {
|
|
|
87
140
|
return () => {
|
|
88
141
|
stale = true;
|
|
89
142
|
};
|
|
90
|
-
}, [
|
|
143
|
+
}, [ready, props, isValid, toolName, toolInput, callTool]);
|
|
91
144
|
return state;
|
|
92
145
|
}
|
|
93
146
|
//# sourceMappingURL=use-tool-result-recovery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-result-recovery.js","sourceRoot":"","sources":["../../src/hooks/use-tool-result-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"use-tool-result-recovery.js","sourceRoot":"","sources":["../../src/hooks/use-tool-result-recovery.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ export { LocaleProvider, useLocale, useTranslate } from "./locale/index.js";
|
|
|
49
49
|
// NOTE: MCP App shell (McpAppView, WidgetRenderer) is exported from the
|
|
50
50
|
// `./app` subpath only. Keeping it out of the main barrel prevents consumers
|
|
51
51
|
// that only need primitives (e.g. an admin portal) from pulling `mcp-use/react`
|
|
52
|
-
// and its
|
|
52
|
+
// — the 2.x view runtime and its ext-apps transitive — into their Vite bundle.
|
|
53
53
|
// Utils
|
|
54
54
|
export { cn } from "./lib/utils.js";
|
|
55
55
|
export { parseToolResult, parseViewToolResult } from "./lib/parse-tool-result.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miragon/mcp-toolkit-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "React UI for MCP servers: shadcn primitives, composite components, TanStack Query hooks, and the MCP App shell (McpAppView + WidgetRenderer).",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Miragon/mcp-toolkit.git",
|
|
8
|
+
"directory": "packages/ui"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"sideEffects": [
|
|
7
12
|
"**/*.css"
|
|
@@ -33,11 +38,11 @@
|
|
|
33
38
|
"access": "public"
|
|
34
39
|
},
|
|
35
40
|
"peerDependencies": {
|
|
36
|
-
"mcp-use": "
|
|
41
|
+
"mcp-use": "2.0.4",
|
|
37
42
|
"react": "19.2.8",
|
|
38
43
|
"react-dom": "19.2.8",
|
|
39
44
|
"tailwindcss": "4.3.3",
|
|
40
|
-
"@miragon/mcp-toolkit-core": "0.
|
|
45
|
+
"@miragon/mcp-toolkit-core": "1.0.1"
|
|
41
46
|
},
|
|
42
47
|
"dependencies": {
|
|
43
48
|
"@base-ui/react": "1.6.0",
|
|
@@ -45,21 +50,24 @@
|
|
|
45
50
|
"@tanstack/react-query": "5.101.4",
|
|
46
51
|
"class-variance-authority": "0.7.1",
|
|
47
52
|
"clsx": "2.1.1",
|
|
48
|
-
"lucide-react": "
|
|
53
|
+
"lucide-react": "0.562.0",
|
|
49
54
|
"radix-ui": "1.6.7",
|
|
50
55
|
"tailwind-merge": "3.6.0",
|
|
51
56
|
"tw-animate-css": "1.4.0"
|
|
52
57
|
},
|
|
53
58
|
"devDependencies": {
|
|
54
|
-
"@types/react": "19.2.
|
|
55
|
-
"@types/react-dom": "19.2.
|
|
56
|
-
"mcp-use": "
|
|
59
|
+
"@types/react": "19.2.18",
|
|
60
|
+
"@types/react-dom": "19.2.4",
|
|
61
|
+
"mcp-use": "2.0.4",
|
|
57
62
|
"react": "19.2.8",
|
|
58
63
|
"react-dom": "19.2.8",
|
|
59
64
|
"tailwindcss": "4.3.3",
|
|
60
65
|
"typescript": "6.0.3",
|
|
61
66
|
"vitest": "4.1.10",
|
|
62
|
-
"@miragon/mcp-toolkit-core": "0.
|
|
67
|
+
"@miragon/mcp-toolkit-core": "1.0.1"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22.22.2"
|
|
63
71
|
},
|
|
64
72
|
"scripts": {
|
|
65
73
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { useEffect, type ComponentType, type ReactNode } from "react"
|
|
2
2
|
import { ModelContext } from "mcp-use/react"
|
|
3
3
|
import type { WidgetProps } from "@miragon/mcp-toolkit-core"
|
|
4
|
+
import { useHostBridgeOrNull } from "./host-bridge.js"
|
|
5
|
+
import { useIsInsideMcpUseView } from "./mcp-use-host-bridge.js"
|
|
4
6
|
import type { WidgetComponent } from "./widget-renderer.js"
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -14,6 +16,44 @@ export type DescribeForModel<T> = (
|
|
|
14
16
|
props: Readonly<Record<string, unknown>>,
|
|
15
17
|
) => string
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Model-context reporting that works on every host — the host-portable
|
|
21
|
+
* replacement for rendering `mcp-use/react`'s `ModelContext` directly.
|
|
22
|
+
*
|
|
23
|
+
* mcp-use 2.x's `ModelContext` registers with the view runtime and THROWS
|
|
24
|
+
* outside a `bootstrapView` mount, so it is only rendered inside an mcp-use
|
|
25
|
+
* view (where it aggregates nested nodes into one tree). Everywhere else —
|
|
26
|
+
* ChatGPT via `createChatGptHostBridge`, the `WidgetFixtureHost` harness,
|
|
27
|
+
* standalone apps — the description flows through the bridge's
|
|
28
|
+
* `setModelContext` instead (last-writer-wins; fine for the single-widget
|
|
29
|
+
* mounts those hosts render).
|
|
30
|
+
*
|
|
31
|
+
* Widgets that self-fetch and report their own context (cockpit views like
|
|
32
|
+
* `TasksBoard`) must use this instead of importing `ModelContext` from
|
|
33
|
+
* `mcp-use/react` — the direct import crashes the widget in every non-view
|
|
34
|
+
* mount, starting with the widget playground.
|
|
35
|
+
*/
|
|
36
|
+
export function HostModelContext({
|
|
37
|
+
content,
|
|
38
|
+
children,
|
|
39
|
+
}: {
|
|
40
|
+
content: string
|
|
41
|
+
children: ReactNode
|
|
42
|
+
}): ReactNode {
|
|
43
|
+
const insideView = useIsInsideMcpUseView()
|
|
44
|
+
if (insideView) return <ModelContext content={content}>{children}</ModelContext>
|
|
45
|
+
return <BridgeModelContext content={content}>{children}</BridgeModelContext>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function BridgeModelContext({ content, children }: { content: string; children: ReactNode }) {
|
|
49
|
+
const bridge = useHostBridgeOrNull()
|
|
50
|
+
const setModelContext = bridge?.setModelContext
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
setModelContext?.(content)
|
|
53
|
+
}, [setModelContext, content])
|
|
54
|
+
return children
|
|
55
|
+
}
|
|
56
|
+
|
|
17
57
|
/**
|
|
18
58
|
* Wraps a "single-data" widget component (signature `({ data }: { data: T | null })`)
|
|
19
59
|
* so it can be registered as a framework {@link WidgetComponent} (which receives
|
|
@@ -28,11 +68,11 @@ export type DescribeForModel<T> = (
|
|
|
28
68
|
* times in one view with different scoping (e.g. one tab per process key).
|
|
29
69
|
*
|
|
30
70
|
* When `describeForModel` is provided the adapter wraps the widget in a
|
|
31
|
-
* `
|
|
32
|
-
* active filters, headline numbers) without per-widget boilerplate.
|
|
33
|
-
* description is only rendered once the step data is present; widgets that
|
|
71
|
+
* `HostModelContext` so the model knows what the user is looking at (view
|
|
72
|
+
* identity, active filters, headline numbers) without per-widget boilerplate.
|
|
73
|
+
* The description is only rendered once the step data is present; widgets that
|
|
34
74
|
* self-fetch when the adapter has no data (cockpit views) must render their own
|
|
35
|
-
* `<
|
|
75
|
+
* `<HostModelContext>` instead.
|
|
36
76
|
*/
|
|
37
77
|
export function adaptDataWidget<T>(
|
|
38
78
|
Widget: ComponentType<{ data: T | null } & Record<string, unknown>>,
|
|
@@ -44,7 +84,11 @@ export function adaptDataWidget<T>(
|
|
|
44
84
|
const data = (stepResult?.data ?? null) as T | null
|
|
45
85
|
const widget = <Widget {...(widgetProps ?? {})} data={data} />
|
|
46
86
|
if (data == null || !describeForModel) return widget
|
|
47
|
-
return
|
|
87
|
+
return (
|
|
88
|
+
<HostModelContext content={describeForModel(data, widgetProps ?? {})}>
|
|
89
|
+
{widget}
|
|
90
|
+
</HostModelContext>
|
|
91
|
+
)
|
|
48
92
|
}
|
|
49
93
|
AdaptedWidget.displayName = `Adapted(${Widget.displayName ?? Widget.name ?? "Widget"})`
|
|
50
94
|
return AdaptedWidget
|