simple-webmcp 0.1.0 → 0.2.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/.agents/skills/webmcp-simple/SKILL.md +27 -12
- package/README.md +194 -72
- package/dist/dev-polyfill.cjs +68 -0
- package/dist/dev-polyfill.cjs.map +1 -0
- package/dist/dev-polyfill.d.cts +1 -0
- package/dist/dev-polyfill.d.ts +1 -0
- package/dist/dev-polyfill.js +62 -0
- package/dist/dev-polyfill.js.map +1 -0
- package/dist/devtools.cjs +452 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +14 -0
- package/dist/devtools.d.ts +14 -0
- package/dist/devtools.js +449 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +37 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -21
- package/dist/index.d.ts +4 -21
- package/dist/index.js +37 -37
- package/dist/index.js.map +1 -1
- package/dist/inspect.cjs +342 -0
- package/dist/inspect.cjs.map +1 -0
- package/dist/inspect.d.cts +37 -0
- package/dist/inspect.d.ts +37 -0
- package/dist/inspect.js +334 -0
- package/dist/inspect.js.map +1 -0
- package/dist/polyfill.cjs.map +1 -1
- package/dist/polyfill.d.cts +15 -7
- package/dist/polyfill.d.ts +15 -7
- package/dist/polyfill.js.map +1 -1
- package/dist/react.cjs +850 -12
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +7 -10
- package/dist/react.d.ts +7 -10
- package/dist/react.js +851 -14
- package/dist/react.js.map +1 -1
- package/dist/registry-BcPw1qCf.d.cts +38 -0
- package/dist/registry-C6Pz_Rja.d.ts +38 -0
- package/dist/testing.cjs +68 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +62 -0
- package/dist/testing.js.map +1 -0
- package/dist/{types-D-cwSfEU.d.cts → types-DH78HDAE.d.cts} +2 -2
- package/dist/{types-D-cwSfEU.d.ts → types-DH78HDAE.d.ts} +2 -2
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/package.json +30 -1
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/internal/utils.ts
|
|
7
|
+
function isWebMCPSupported() {
|
|
8
|
+
if (typeof document === "undefined") return false;
|
|
9
|
+
const docAny = document;
|
|
10
|
+
return !!docAny.modelContext && typeof docAny.modelContext.registerTool === "function";
|
|
11
|
+
}
|
|
12
|
+
function getModelContext() {
|
|
13
|
+
if (typeof document === "undefined") return null;
|
|
14
|
+
const docAny = document;
|
|
15
|
+
if (docAny.modelContext && typeof docAny.modelContext.registerTool === "function") {
|
|
16
|
+
return docAny.modelContext;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/errors.ts
|
|
22
|
+
var SimpleWebMCPError = class extends Error {
|
|
23
|
+
constructor(message, code, cause) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.cause = cause;
|
|
27
|
+
this.name = this.constructor.name;
|
|
28
|
+
if (Error.captureStackTrace) {
|
|
29
|
+
Error.captureStackTrace(this, this.constructor);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
toJSON() {
|
|
33
|
+
return {
|
|
34
|
+
name: this.name,
|
|
35
|
+
message: this.message,
|
|
36
|
+
code: this.code,
|
|
37
|
+
cause: this.cause
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var NotSupportedError = class extends SimpleWebMCPError {
|
|
42
|
+
constructor(message = "WebMCP is not supported in this environment", opts) {
|
|
43
|
+
super(message, "NOT_SUPPORTED", opts?.cause);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var NotAllowedError = class extends SimpleWebMCPError {
|
|
47
|
+
constructor(message = "WebMCP tools are blocked by Permissions Policy", opts) {
|
|
48
|
+
super(message, "NOT_ALLOWED", opts?.cause);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var RegistrationError = class extends SimpleWebMCPError {
|
|
52
|
+
constructor(message, opts) {
|
|
53
|
+
super(message, "REGISTRATION_ERROR", opts?.cause);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/internal/registry.ts
|
|
58
|
+
var Registry = class {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
61
|
+
}
|
|
62
|
+
// expose for devtools/tests — now includes contract for inspect
|
|
63
|
+
list() {
|
|
64
|
+
return Array.from(this.entries.values()).map((e) => ({ name: e.name, status: e.status, contract: e.contract }));
|
|
65
|
+
}
|
|
66
|
+
get(name) {
|
|
67
|
+
return this.entries.get(name);
|
|
68
|
+
}
|
|
69
|
+
getContract(name) {
|
|
70
|
+
return this.entries.get(name)?.contract;
|
|
71
|
+
}
|
|
72
|
+
clear() {
|
|
73
|
+
for (const e of this.entries.values()) {
|
|
74
|
+
try {
|
|
75
|
+
e.controller.abort();
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.entries.clear();
|
|
80
|
+
}
|
|
81
|
+
isRegistered(name) {
|
|
82
|
+
return this.entries.get(name)?.status === "registered";
|
|
83
|
+
}
|
|
84
|
+
getStatus(name) {
|
|
85
|
+
return this.entries.get(name)?.status ?? "unregistered";
|
|
86
|
+
}
|
|
87
|
+
async register(contract, opts) {
|
|
88
|
+
const name = contract.name;
|
|
89
|
+
const existing = this.entries.get(name);
|
|
90
|
+
if (existing && (existing.status === "registered" || existing.status === "registering")) {
|
|
91
|
+
return existing.unregister;
|
|
92
|
+
}
|
|
93
|
+
if (!isWebMCPSupported()) {
|
|
94
|
+
if (opts?.signal?.aborted) return () => {
|
|
95
|
+
};
|
|
96
|
+
return () => {
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const modelContext = getModelContext();
|
|
100
|
+
if (!modelContext) throw new NotSupportedError();
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
if (opts?.signal) {
|
|
103
|
+
if (opts.signal.aborted) {
|
|
104
|
+
controller.abort();
|
|
105
|
+
} else {
|
|
106
|
+
opts.signal.addEventListener(
|
|
107
|
+
"abort",
|
|
108
|
+
() => {
|
|
109
|
+
try {
|
|
110
|
+
controller.abort();
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{ once: true }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const entry = {
|
|
119
|
+
name,
|
|
120
|
+
controller,
|
|
121
|
+
status: "registering",
|
|
122
|
+
promise: null,
|
|
123
|
+
unregister: () => {
|
|
124
|
+
if (entry.status === "registered" || entry.status === "registering") {
|
|
125
|
+
entry.status = "unregistering";
|
|
126
|
+
try {
|
|
127
|
+
controller.abort();
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
entry.status = "unregistered";
|
|
131
|
+
this.entries.delete(name);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
contract,
|
|
135
|
+
execute: opts?.execute
|
|
136
|
+
};
|
|
137
|
+
this.entries.set(name, entry);
|
|
138
|
+
const wrappedExecute = opts?.execute ?? (async (args) => ({ content: [{ type: "text", text: `no execute for ${name}` }] }));
|
|
139
|
+
try {
|
|
140
|
+
const regPromise = modelContext.registerTool(
|
|
141
|
+
{
|
|
142
|
+
name: contract.name,
|
|
143
|
+
description: contract.description,
|
|
144
|
+
inputSchema: contract.inputSchema,
|
|
145
|
+
outputSchema: contract.outputSchema,
|
|
146
|
+
annotations: contract.annotations,
|
|
147
|
+
execute: wrappedExecute
|
|
148
|
+
},
|
|
149
|
+
{ signal: controller.signal }
|
|
150
|
+
);
|
|
151
|
+
entry.promise = regPromise;
|
|
152
|
+
await regPromise;
|
|
153
|
+
if (controller.signal.aborted) {
|
|
154
|
+
entry.status = "unregistered";
|
|
155
|
+
this.entries.delete(name);
|
|
156
|
+
} else {
|
|
157
|
+
entry.status = "registered";
|
|
158
|
+
}
|
|
159
|
+
controller.signal.addEventListener("abort", () => {
|
|
160
|
+
entry.status = "unregistered";
|
|
161
|
+
this.entries.delete(name);
|
|
162
|
+
}, { once: true });
|
|
163
|
+
return entry.unregister;
|
|
164
|
+
} catch (err) {
|
|
165
|
+
entry.status = "error";
|
|
166
|
+
this.entries.delete(name);
|
|
167
|
+
const msg = err?.message || String(err);
|
|
168
|
+
if (err?.name === "NotAllowedError" || /NotAllowedError|Permissions Policy|blocked/i.test(msg)) {
|
|
169
|
+
throw new NotAllowedError(msg, { cause: err });
|
|
170
|
+
}
|
|
171
|
+
throw new RegistrationError(`Failed to register tool "${name}": ${msg}`, { cause: err });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
unregister(name) {
|
|
175
|
+
const entry = this.entries.get(name);
|
|
176
|
+
if (!entry) return;
|
|
177
|
+
try {
|
|
178
|
+
entry.controller.abort();
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
this.entries.delete(name);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var REGISTRY_KEY = "__simpleWebmcpRegistry";
|
|
185
|
+
var globalAny = globalThis;
|
|
186
|
+
var registry = globalAny[REGISTRY_KEY] ?? new Registry();
|
|
187
|
+
if (!globalAny[REGISTRY_KEY]) {
|
|
188
|
+
globalAny[REGISTRY_KEY] = registry;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/inspect.ts
|
|
192
|
+
function getModelContextAny() {
|
|
193
|
+
return getModelContext();
|
|
194
|
+
}
|
|
195
|
+
function listTools() {
|
|
196
|
+
const map = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const entry of registry.list()) {
|
|
198
|
+
if (entry.contract) {
|
|
199
|
+
map.set(entry.name, {
|
|
200
|
+
...entry.contract,
|
|
201
|
+
status: entry.status,
|
|
202
|
+
registered: entry.status === "registered",
|
|
203
|
+
source: "registry"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const mc = getModelContextAny();
|
|
208
|
+
if (mc) {
|
|
209
|
+
let nativeTools = [];
|
|
210
|
+
try {
|
|
211
|
+
if (typeof mc.getTools === "function") {
|
|
212
|
+
nativeTools = mc.getTools() ?? [];
|
|
213
|
+
if (nativeTools instanceof Promise) {
|
|
214
|
+
nativeTools = [];
|
|
215
|
+
}
|
|
216
|
+
} else if (typeof mc.listTools === "function") {
|
|
217
|
+
nativeTools = mc.listTools() ?? [];
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
for (const t of nativeTools) {
|
|
222
|
+
if (!t?.name) continue;
|
|
223
|
+
const existing = map.get(t.name);
|
|
224
|
+
if (existing) {
|
|
225
|
+
existing.source = "both";
|
|
226
|
+
} else {
|
|
227
|
+
map.set(t.name, {
|
|
228
|
+
name: t.name,
|
|
229
|
+
description: t.description ?? "",
|
|
230
|
+
inputSchema: t.inputSchema ?? { type: "object", properties: {} },
|
|
231
|
+
outputSchema: t.outputSchema,
|
|
232
|
+
annotations: t.annotations,
|
|
233
|
+
status: "registered",
|
|
234
|
+
registered: true,
|
|
235
|
+
source: "modelContext"
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return Array.from(map.values());
|
|
241
|
+
}
|
|
242
|
+
async function invokeTool(name, args) {
|
|
243
|
+
const mc = getModelContextAny();
|
|
244
|
+
if (mc) {
|
|
245
|
+
try {
|
|
246
|
+
if (typeof mc.executeTool === "function") {
|
|
247
|
+
return await mc.executeTool(name, args);
|
|
248
|
+
}
|
|
249
|
+
if (typeof mc.invokeTool === "function") {
|
|
250
|
+
return await mc.invokeTool(name, args);
|
|
251
|
+
}
|
|
252
|
+
if (typeof mc.callTool === "function") {
|
|
253
|
+
return await mc.callTool(name, args);
|
|
254
|
+
}
|
|
255
|
+
} catch (e) {
|
|
256
|
+
if (e?.name === "NotFoundError" || /not found/i.test(String(e?.message))) ; else {
|
|
257
|
+
throw e;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const entry = registry.get(name);
|
|
262
|
+
if (entry?.execute) {
|
|
263
|
+
return await entry.execute(args);
|
|
264
|
+
}
|
|
265
|
+
if (mc && mc._tools) {
|
|
266
|
+
const toolsMap = mc._tools;
|
|
267
|
+
const found = toolsMap.get(name);
|
|
268
|
+
if (found?.def?.execute) {
|
|
269
|
+
return await found.def.execute(args);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
throw new Error(`Tool "${name}" not found or not invokable in this environment`);
|
|
273
|
+
}
|
|
274
|
+
function isSupported() {
|
|
275
|
+
return isWebMCPSupported();
|
|
276
|
+
}
|
|
277
|
+
function onToolsChanged(callback) {
|
|
278
|
+
let cancelled = false;
|
|
279
|
+
const mc = getModelContextAny();
|
|
280
|
+
if (mc && typeof mc.addEventListener === "function") {
|
|
281
|
+
const handler = () => {
|
|
282
|
+
if (!cancelled) callback(listTools());
|
|
283
|
+
};
|
|
284
|
+
try {
|
|
285
|
+
mc.addEventListener("toolchange", handler);
|
|
286
|
+
return () => {
|
|
287
|
+
cancelled = true;
|
|
288
|
+
try {
|
|
289
|
+
mc.removeEventListener("toolchange", handler);
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const interval = setInterval(() => {
|
|
297
|
+
if (!cancelled) callback(listTools());
|
|
298
|
+
}, 500);
|
|
299
|
+
return () => {
|
|
300
|
+
cancelled = true;
|
|
301
|
+
clearInterval(interval);
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function SchemaView({ schema }) {
|
|
305
|
+
if (!schema) return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#888" }, children: "\u2014" });
|
|
306
|
+
return /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { background: "#0a0a0a", color: "#0f0", padding: 10, borderRadius: 8, fontSize: 12, overflow: "auto", maxHeight: 180, margin: "8px 0" }, children: JSON.stringify(schema, null, 2) });
|
|
307
|
+
}
|
|
308
|
+
function ToolCard({ tool }) {
|
|
309
|
+
const [argsText, setArgsText] = react.useState("{}");
|
|
310
|
+
const [result, setResult] = react.useState(null);
|
|
311
|
+
const [error, setError] = react.useState(null);
|
|
312
|
+
const [loading, setLoading] = react.useState(false);
|
|
313
|
+
const placeholder = react.useMemo(() => {
|
|
314
|
+
const props = tool.inputSchema?.properties ?? {};
|
|
315
|
+
const example = {};
|
|
316
|
+
for (const [k, v] of Object.entries(props)) {
|
|
317
|
+
if (v.default !== void 0) example[k] = v.default;
|
|
318
|
+
else if (v.type === "string") example[k] = "example";
|
|
319
|
+
else if (v.type === "number" || v.type === "integer") example[k] = 1;
|
|
320
|
+
else if (v.type === "boolean") example[k] = true;
|
|
321
|
+
else example[k] = "";
|
|
322
|
+
}
|
|
323
|
+
return JSON.stringify(example, null, 2);
|
|
324
|
+
}, [tool.inputSchema]);
|
|
325
|
+
react.useEffect(() => {
|
|
326
|
+
if (argsText === "{}" && placeholder !== "{}") setArgsText(placeholder);
|
|
327
|
+
}, [placeholder]);
|
|
328
|
+
const handleInvoke = async () => {
|
|
329
|
+
setLoading(true);
|
|
330
|
+
setError(null);
|
|
331
|
+
setResult(null);
|
|
332
|
+
try {
|
|
333
|
+
const args = argsText.trim() ? JSON.parse(argsText) : {};
|
|
334
|
+
const res = await invokeTool(tool.name, args);
|
|
335
|
+
setResult(res);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
setError(e?.message ?? String(e));
|
|
338
|
+
} finally {
|
|
339
|
+
setLoading(false);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid #e5e7eb", borderRadius: 10, padding: 14, marginBottom: 12, background: "#fff" }, children: [
|
|
343
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "start", gap: 12 }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
344
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 700, fontFamily: "monospace", fontSize: 13 }, children: tool.name }),
|
|
345
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#555", fontSize: 13, marginTop: 4 }, children: tool.description || /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#999" }, children: "No description" }) }),
|
|
346
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 11, color: "#888", marginTop: 4 }, children: [
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { background: tool.registered ? "#dcfce7" : "#fee2e2", padding: "2px 6px", borderRadius: 999 }, children: tool.status }),
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: 6 }, children: tool.source }),
|
|
349
|
+
tool.annotations && Object.keys(tool.annotations).length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: 6, fontFamily: "monospace" }, children: JSON.stringify(tool.annotations) })
|
|
350
|
+
] })
|
|
351
|
+
] }) }),
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { style: { marginTop: 8 }, children: [
|
|
353
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { style: { cursor: "pointer", fontSize: 12, color: "#666" }, children: "inputSchema" }),
|
|
354
|
+
/* @__PURE__ */ jsxRuntime.jsx(SchemaView, { schema: tool.inputSchema }),
|
|
355
|
+
tool.outputSchema && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
356
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#666", marginTop: 8 }, children: "outputSchema" }),
|
|
357
|
+
/* @__PURE__ */ jsxRuntime.jsx(SchemaView, { schema: tool.outputSchema })
|
|
358
|
+
] })
|
|
359
|
+
] }),
|
|
360
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 10 }, children: [
|
|
361
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "Invoke" }),
|
|
362
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
363
|
+
"textarea",
|
|
364
|
+
{
|
|
365
|
+
value: argsText,
|
|
366
|
+
onChange: (e) => setArgsText(e.target.value),
|
|
367
|
+
rows: 4,
|
|
368
|
+
placeholder,
|
|
369
|
+
style: { width: "100%", fontFamily: "monospace", fontSize: 12, padding: 8, borderRadius: 8, border: "1px solid #ddd", background: "#fafafa" }
|
|
370
|
+
}
|
|
371
|
+
),
|
|
372
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
373
|
+
"button",
|
|
374
|
+
{
|
|
375
|
+
onClick: handleInvoke,
|
|
376
|
+
disabled: loading,
|
|
377
|
+
style: { marginTop: 8, padding: "8px 14px", borderRadius: 8, border: "1px solid #111", background: loading ? "#999" : "#111", color: "#fff", cursor: loading ? "not-allowed" : "pointer", fontSize: 13 },
|
|
378
|
+
children: loading ? "Invoking\u2026" : `Invoke ${tool.name}`
|
|
379
|
+
}
|
|
380
|
+
),
|
|
381
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { background: "#fef2f2", color: "#b91c1c", padding: 10, borderRadius: 8, marginTop: 8, fontSize: 12, whiteSpace: "pre-wrap" }, children: error }),
|
|
382
|
+
result !== null && !error && /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { background: "#f0fdf4", color: "#065f46", padding: 10, borderRadius: 8, marginTop: 8, fontSize: 12, whiteSpace: "pre-wrap", maxHeight: 200, overflow: "auto" }, children: typeof result === "string" ? result : JSON.stringify(result, null, 2) })
|
|
383
|
+
] })
|
|
384
|
+
] });
|
|
385
|
+
}
|
|
386
|
+
function Inspector({ defaultOpen = true, filter }) {
|
|
387
|
+
const [tools, setTools] = react.useState(() => listTools());
|
|
388
|
+
const [supported, setSupported] = react.useState(() => isSupported());
|
|
389
|
+
const [open, setOpen] = react.useState(defaultOpen);
|
|
390
|
+
react.useEffect(() => {
|
|
391
|
+
setSupported(isSupported());
|
|
392
|
+
setTools(listTools());
|
|
393
|
+
const off = onToolsChanged((t) => setTools(t));
|
|
394
|
+
return off;
|
|
395
|
+
}, []);
|
|
396
|
+
const filtered = filter ? tools.filter(filter) : tools;
|
|
397
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: "system-ui, sans-serif", border: "1px solid #e5e7eb", borderRadius: 12, overflow: "hidden", background: "#f9fafb", maxWidth: 720 }, children: [
|
|
398
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
399
|
+
"div",
|
|
400
|
+
{
|
|
401
|
+
onClick: () => setOpen(!open),
|
|
402
|
+
style: { padding: "12px 16px", background: "#111", color: "#fff", display: "flex", justifyContent: "space-between", alignItems: "center", cursor: "pointer" },
|
|
403
|
+
children: [
|
|
404
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontWeight: 700, fontSize: 13 }, children: [
|
|
405
|
+
"\u{1F50D} simple-webmcp Inspect \u2014 ",
|
|
406
|
+
filtered.length,
|
|
407
|
+
" tool",
|
|
408
|
+
filtered.length !== 1 ? "s" : "",
|
|
409
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 400, opacity: 0.8, marginLeft: 8, fontSize: 11 }, children: supported ? "supported" : "unsupported (dev shim)" })
|
|
410
|
+
] }),
|
|
411
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12 }, children: open ? "\u2212" : "+" })
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 16 }, children: [
|
|
416
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "#666", marginBottom: 12 }, children: [
|
|
417
|
+
"Lists ",
|
|
418
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "registry.list()" }),
|
|
419
|
+
" + ",
|
|
420
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "document.modelContext.getTools()" }),
|
|
421
|
+
" (if native). Invoke calls ",
|
|
422
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "executeTool" }),
|
|
423
|
+
" / ",
|
|
424
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "invokeTool" }),
|
|
425
|
+
" / registry fallback. Works with `simple-webmcp/dev-polyfill` in dev."
|
|
426
|
+
] }),
|
|
427
|
+
filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 24, textAlign: "center", color: "#888", fontSize: 13, background: "#fff", borderRadius: 8, border: "1px dashed #ddd" }, children: [
|
|
428
|
+
"No tools registered yet. Mount a component with ",
|
|
429
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "useWebMCP(fn)" }),
|
|
430
|
+
" or call ",
|
|
431
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "tool.register()" }),
|
|
432
|
+
"."
|
|
433
|
+
] }) : filtered.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToolCard, { tool: t }, t.name)),
|
|
434
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 11, color: "#888", marginTop: 8, textAlign: "center" }, children: [
|
|
435
|
+
"simple-webmcp inspect \u2014 ",
|
|
436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
|
|
437
|
+
"import ",
|
|
438
|
+
"{",
|
|
439
|
+
"listTools, invokeTool",
|
|
440
|
+
"}",
|
|
441
|
+
" from 'simple-webmcp/inspect'"
|
|
442
|
+
] })
|
|
443
|
+
] })
|
|
444
|
+
] })
|
|
445
|
+
] });
|
|
446
|
+
}
|
|
447
|
+
var Inspector_default = Inspector;
|
|
448
|
+
|
|
449
|
+
exports.Inspector = Inspector;
|
|
450
|
+
exports.InspectorDefault = Inspector_default;
|
|
451
|
+
//# sourceMappingURL=devtools.cjs.map
|
|
452
|
+
//# sourceMappingURL=devtools.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/utils.ts","../src/errors.ts","../src/internal/registry.ts","../src/inspect.ts","../src/devtools/Inspector.tsx"],"names":["jsx","useState","useMemo","useEffect","jsxs","Fragment"],"mappings":"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","file":"devtools.cjs","sourcesContent":["import type { JsonSchema, ModelContextLike } from '../types.js';\n\n/**\n * Convert camelCase / PascalCase to snake_case for tool naming.\n * searchCustomers → search_customers\n * SearchCustomers → search_customers\n */\nexport function toSnakeCase(name: string): string {\n if (!name) return 'anonymous_tool';\n return name\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1_$2')\n .toLowerCase()\n .replace(/[^a-z0-9_]+/g, '_')\n .replace(/^_+|_+$/g, '') || 'anonymous_tool';\n}\n\n/**\n * Totally minimal doc extraction — best effort.\n * Full JSDoc inference is build-time. Runtime we just note presence.\n */\nexport function getFunctionName(fn: Function): string {\n return (fn as any).displayName || fn.name || '';\n}\n\n/**\n * Check if WebMCP is supported (native or polyfilled)\n */\nexport function isWebMCPSupported(): boolean {\n if (typeof document === 'undefined') return false;\n const docAny = document as any;\n return !!docAny.modelContext && typeof docAny.modelContext.registerTool === 'function';\n}\n\nexport function getModelContext(): ModelContextLike | null {\n if (typeof document === 'undefined') return null;\n const docAny = document as any;\n if (docAny.modelContext && typeof docAny.modelContext.registerTool === 'function') {\n return docAny.modelContext as ModelContextLike;\n }\n return null;\n}\n\n/**\n * Heuristic check if value looks like JSON Schema (vs StandardSchema)\n */\nexport function looksLikeJsonSchema(value: unknown): value is JsonSchema {\n if (!value || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n // JSON Schema typically has `type` at top, no `~standard`\n if ('~standard' in obj) return false;\n // Has type or properties or $ref etc\n return (\n 'type' in obj ||\n 'properties' in obj ||\n '$ref' in obj ||\n 'anyOf' in obj ||\n 'oneOf' in obj ||\n 'allOf' in obj ||\n 'enum' in obj\n );\n}\n\n/**\n * Check if value is StandardSchema\n */\nexport function isStandardSchema(value: unknown): boolean {\n return (\n !!value &&\n typeof value === 'object' &&\n '~standard' in (value as any) &&\n (value as any)['~standard']?.validate != null &&\n typeof (value as any)['~standard'].validate === 'function'\n );\n}\n\n/**\n * Cheap JSDoc/description fallback — runtime can't parse JSDoc,\n * but we check if fn has __webmcpDescription attached by build plugin or manual.\n */\nexport function getDescription(fn: Function, override?: string): string {\n if (override != null) return override;\n const anyFn = fn as any;\n if (typeof anyFn.__webmcpDescription === 'string') return anyFn.__webmcpDescription;\n // No JSDoc at runtime — return empty and warn elsewhere\n return '';\n}\n\nlet warnedNoDesc = false;\nexport function warnNoDescription(fn: Function) {\n if (warnedNoDesc) return;\n // Only warn once per page load\n warnedNoDesc = true;\n if (typeof console !== 'undefined' && typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[simple-webmcp] tool \"${getFunctionName(fn) || 'anonymous'}\" has no description. Add \\`description:\"...\"\\` or JSDoc. See https://github.com/emingure/simple-webmcp`,\n );\n } else if (typeof console !== 'undefined') {\n // In browser dev, also warn once\n // eslint-disable-next-line no-console\n console.warn(`[simple-webmcp] tool \"${getFunctionName(fn) || 'anonymous'}\" has no description.`);\n }\n}\n","/**\n * Base error for simple-webmcp\n */\nexport class SimpleWebMCPError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = this.constructor.name;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n cause: this.cause,\n };\n }\n}\n\nexport class NotSupportedError extends SimpleWebMCPError {\n constructor(message = 'WebMCP is not supported in this environment', opts?: { cause?: unknown }) {\n super(message, 'NOT_SUPPORTED', opts?.cause);\n }\n}\n\nexport class NotAllowedError extends SimpleWebMCPError {\n constructor(message = 'WebMCP tools are blocked by Permissions Policy', opts?: { cause?: unknown }) {\n super(message, 'NOT_ALLOWED', opts?.cause);\n }\n}\n\nexport class RegistrationError extends SimpleWebMCPError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(message, 'REGISTRATION_ERROR', opts?.cause);\n }\n}\n\nexport class ValidationError extends SimpleWebMCPError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(message, 'VALIDATION_ERROR', opts?.cause);\n }\n}\n\nexport class ConfigurationError extends SimpleWebMCPError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(message, 'CONFIGURATION_ERROR', opts?.cause);\n }\n}\n","import type { ToolContract, RegistrationStatus, ModelContextLike } from '../types.js';\nimport { getModelContext, isWebMCPSupported } from './utils.js';\nimport { RegistrationError, NotSupportedError, NotAllowedError } from '../errors.js';\nimport { wrapExecute } from './normalize.js';\n\n/**\n * Global tool registry — tracks registration state per tool name.\n * Handles async registerTool (Promise<void>) + AbortSignal lifecycle per WebMCP spec (current types).\n * Deduplicates StrictMode double-mount.\n */\n\ntype Entry = {\n name: string;\n controller: AbortController;\n status: RegistrationStatus;\n promise: Promise<void> | null;\n unregister: () => void;\n contract?: ToolContract;\n execute?: (args: unknown, ctx?: unknown) => Promise<any>;\n};\n\nclass Registry {\n private entries = new Map<string, Entry>();\n // expose for devtools/tests — now includes contract for inspect\n list(): Array<{ name: string; status: RegistrationStatus; contract?: ToolContract }> {\n return Array.from(this.entries.values()).map((e) => ({ name: e.name, status: e.status, contract: e.contract }));\n }\n\n get(name: string): Entry | undefined {\n return this.entries.get(name);\n }\n\n getContract(name: string): ToolContract | undefined {\n return this.entries.get(name)?.contract;\n }\n\n clear() {\n for (const e of this.entries.values()) {\n try { e.controller.abort(); } catch {}\n }\n this.entries.clear();\n }\n\n isRegistered(name: string): boolean {\n return this.entries.get(name)?.status === 'registered';\n }\n\n getStatus(name: string): RegistrationStatus {\n return this.entries.get(name)?.status ?? 'unregistered';\n }\n\n async register(\n contract: ToolContract,\n opts?: { signal?: AbortSignal; execute?: (args: unknown, ctx?: unknown) => Promise<any> },\n ): Promise<() => void> {\n const name = contract.name;\n const existing = this.entries.get(name);\n if (existing && (existing.status === 'registered' || existing.status === 'registering')) {\n // Dedup — return existing unregister\n return existing.unregister;\n }\n\n if (!isWebMCPSupported()) {\n // Do not claim success. `supported` and `registered` must be mutually exclusive.\n // Return a no-op unregister; caller should check `isWebMCPSupported()` or\n // `tool.status === 'unsupported'` instead of assuming `registered`.\n if (opts?.signal?.aborted) return () => {};\n return () => {};\n }\n\n const modelContext = getModelContext();\n if (!modelContext) throw new NotSupportedError();\n\n const controller = new AbortController();\n // Wire external signal to internal\n if (opts?.signal) {\n if (opts.signal.aborted) {\n controller.abort();\n } else {\n opts.signal.addEventListener(\n 'abort',\n () => {\n try {\n controller.abort();\n } catch {}\n },\n { once: true },\n );\n }\n }\n\n const entry: Entry = {\n name,\n controller,\n status: 'registering',\n promise: null,\n unregister: () => {\n if (entry.status === 'registered' || entry.status === 'registering') {\n entry.status = 'unregistering';\n try {\n controller.abort();\n } catch {}\n // Small delay to allow spec to unregister\n entry.status = 'unregistered';\n this.entries.delete(name);\n }\n },\n contract,\n execute: opts?.execute,\n };\n this.entries.set(name, entry);\n\n const wrappedExecute = opts?.execute ?? (async (args: unknown) => ({ content: [{ type: 'text', text: `no execute for ${name}` }] }));\n\n try {\n const regPromise = (modelContext as ModelContextLike).registerTool(\n {\n name: contract.name,\n description: contract.description,\n inputSchema: contract.inputSchema,\n outputSchema: contract.outputSchema,\n annotations: contract.annotations,\n execute: wrappedExecute,\n },\n { signal: controller.signal },\n );\n entry.promise = regPromise;\n await regPromise;\n // If not aborted in-flight\n if (controller.signal.aborted) {\n entry.status = 'unregistered';\n this.entries.delete(name);\n } else {\n entry.status = 'registered';\n }\n // Cleanup on abort signal\n controller.signal.addEventListener('abort', () => {\n entry.status = 'unregistered';\n this.entries.delete(name);\n }, { once: true });\n\n return entry.unregister;\n } catch (err: any) {\n entry.status = 'error';\n this.entries.delete(name);\n const msg = err?.message || String(err);\n // Detect Permissions Policy NotAllowedError per spec\n if (err?.name === 'NotAllowedError' || /NotAllowedError|Permissions Policy|blocked/i.test(msg)) {\n throw new NotAllowedError(msg, { cause: err });\n }\n throw new RegistrationError(`Failed to register tool \"${name}\": ${msg}`, { cause: err });\n }\n }\n\n unregister(name: string) {\n const entry = this.entries.get(name);\n if (!entry) return;\n try {\n entry.controller.abort();\n } catch {}\n this.entries.delete(name);\n }\n}\n\nconst REGISTRY_KEY = '__simpleWebmcpRegistry' as const;\nconst globalAny = globalThis as any;\nexport const registry: Registry =\n (globalAny[REGISTRY_KEY] as Registry) ?? new Registry();\nif (!globalAny[REGISTRY_KEY]) {\n globalAny[REGISTRY_KEY] = registry;\n}\n\n// Export wrap helper for external tests\nexport function getRegistry() {\n return registry;\n}\n","/**\n * simple-webmcp/inspect — programmatic inspect for registered tools.\n * Works with native WebMCP (document.modelContext) or dev shim / registry fallback.\n * Used by demo and devtools to list/invoke tools without an LLM.\n */\n\nimport type { ToolContract, RegistrationStatus } from './types.js';\nimport { registry } from './internal/registry.js';\nimport { getModelContext, isWebMCPSupported } from './internal/utils.js';\n\nexport type InspectedTool = ToolContract & {\n status: RegistrationStatus;\n registered: boolean;\n source: 'registry' | 'modelContext' | 'both';\n};\n\nfunction getModelContextAny(): any {\n return getModelContext() as any;\n}\n\n/**\n * List all tools known to this page — merges registry + modelContext.getTools().\n * Handles Chrome's `getTools()` / `listTools()` / shim variations.\n */\nexport function listTools(): InspectedTool[] {\n const map = new Map<string, InspectedTool>();\n\n // From registry (our wrapper, always available)\n for (const entry of registry.list()) {\n if (entry.contract) {\n map.set(entry.name, {\n ...entry.contract,\n status: entry.status,\n registered: entry.status === 'registered',\n source: 'registry',\n });\n }\n }\n\n // From native modelContext.getTools() / listTools() if available\n const mc: any = getModelContextAny();\n if (mc) {\n let nativeTools: any[] = [];\n try {\n if (typeof mc.getTools === 'function') {\n nativeTools = mc.getTools() ?? [];\n // getTools may be async in some specs\n if (nativeTools instanceof Promise) {\n // For sync listTools(), we can't await — caller can use listToolsAsync()\n nativeTools = [];\n }\n } else if (typeof mc.listTools === 'function') {\n nativeTools = mc.listTools() ?? [];\n }\n } catch {\n // ignore\n }\n for (const t of nativeTools) {\n if (!t?.name) continue;\n const existing = map.get(t.name);\n if (existing) {\n existing.source = 'both';\n // Prefer native status if registry says unregistered but native has it\n // Keep our contract's inputSchema as source of truth\n } else {\n map.set(t.name, {\n name: t.name,\n description: t.description ?? '',\n inputSchema: t.inputSchema ?? { type: 'object', properties: {} },\n outputSchema: t.outputSchema,\n annotations: t.annotations,\n status: 'registered',\n registered: true,\n source: 'modelContext',\n });\n }\n }\n }\n\n return Array.from(map.values());\n}\n\n/**\n * Async variant that awaits native getTools() if it returns a promise.\n */\nexport async function listToolsAsync(): Promise<InspectedTool[]> {\n const sync = listTools();\n const mc: any = getModelContextAny();\n if (mc && typeof mc.getTools === 'function') {\n try {\n const native = await mc.getTools();\n if (Array.isArray(native)) {\n const map = new Map(sync.map((t) => [t.name, t]));\n for (const t of native) {\n if (!map.has(t.name)) {\n map.set(t.name, {\n name: t.name,\n description: t.description ?? '',\n inputSchema: t.inputSchema ?? { type: 'object', properties: {} },\n outputSchema: t.outputSchema,\n annotations: t.annotations,\n status: 'registered',\n registered: true,\n source: 'modelContext',\n });\n }\n }\n return Array.from(map.values());\n }\n } catch {}\n }\n return sync;\n}\n\nexport function getTool(name: string): InspectedTool | undefined {\n return listTools().find((t) => t.name === name);\n}\n\n/**\n * Invoke a tool by name with args. Tries in order:\n * 1. modelContext.executeTool / invokeTool / execute (native or shim)\n * 2. registry's stored execute (via our wrapper)\n */\nexport async function invokeTool(name: string, args: unknown): Promise<unknown> {\n const mc: any = getModelContextAny();\n\n // Try native modelContext invoke paths\n if (mc) {\n try {\n if (typeof mc.executeTool === 'function') {\n return await mc.executeTool(name, args);\n }\n if (typeof mc.invokeTool === 'function') {\n return await mc.invokeTool(name, args);\n }\n if (typeof mc.callTool === 'function') {\n return await mc.callTool(name, args);\n }\n } catch (e) {\n // Fall through to registry\n if ((e as any)?.name === 'NotFoundError' || /not found/i.test(String((e as any)?.message))) {\n // continue\n } else {\n throw e;\n }\n }\n }\n\n // Fallback: registry's stored execute (our wrapper's wrappedExecute)\n const entry = registry.get(name);\n if (entry?.execute) {\n return await entry.execute(args as any);\n }\n\n // Also try shim's direct invoke if modelContext is shim with _tools\n if (mc && mc._tools) {\n const toolsMap: Map<string, any> = mc._tools;\n const found = toolsMap.get(name);\n if (found?.def?.execute) {\n return await found.def.execute(args);\n }\n }\n\n throw new Error(`Tool \"${name}\" not found or not invokable in this environment`);\n}\n\nexport function isSupported(): boolean {\n return isWebMCPSupported();\n}\n\n/**\n * Subscribe to tool changes. Uses `toolchange` event if available, otherwise polls registry.\n */\nexport function onToolsChanged(callback: (tools: InspectedTool[]) => void): () => void {\n let cancelled = false;\n const mc: any = getModelContextAny();\n\n // Native toolchange event (Chrome spec)\n if (mc && typeof mc.addEventListener === 'function') {\n const handler = () => {\n if (!cancelled) callback(listTools());\n };\n try {\n mc.addEventListener('toolchange', handler);\n return () => {\n cancelled = true;\n try { mc.removeEventListener('toolchange', handler); } catch {}\n };\n } catch {}\n }\n\n // Fallback: poll registry every 500ms\n const interval = setInterval(() => {\n if (!cancelled) callback(listTools());\n }, 500);\n\n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n}\n\n// Re-export for convenience\nexport { registry };\n","'use client';\n\nimport React, { useEffect, useState, useMemo } from 'react';\nimport { listTools, invokeTool, onToolsChanged, isSupported, type InspectedTool } from '../inspect.js';\n\ntype Props = {\n /** If true, panel is expanded by default */\n defaultOpen?: boolean;\n /** Optional filter to show only specific tools */\n filter?: (tool: InspectedTool) => boolean;\n};\n\nfunction SchemaView({ schema }: { schema: any }) {\n if (!schema) return <span style={{ color: '#888' }}>—</span>;\n return (\n <pre style={{ background: '#0a0a0a', color: '#0f0', padding: 10, borderRadius: 8, fontSize: 12, overflow: 'auto', maxHeight: 180, margin: '8px 0' }}>\n {JSON.stringify(schema, null, 2)}\n </pre>\n );\n}\n\nfunction ToolCard({ tool }: { tool: InspectedTool }) {\n const [argsText, setArgsText] = useState('{}');\n const [result, setResult] = useState<any>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n const placeholder = useMemo(() => {\n const props = tool.inputSchema?.properties ?? {};\n const example: Record<string, any> = {};\n for (const [k, v] of Object.entries(props as Record<string, any>)) {\n if ((v as any).default !== undefined) example[k] = (v as any).default;\n else if ((v as any).type === 'string') example[k] = 'example';\n else if ((v as any).type === 'number' || (v as any).type === 'integer') example[k] = 1;\n else if ((v as any).type === 'boolean') example[k] = true;\n else example[k] = '';\n }\n return JSON.stringify(example, null, 2);\n }, [tool.inputSchema]);\n\n useEffect(() => {\n // Prefill with example if empty\n if (argsText === '{}' && placeholder !== '{}') setArgsText(placeholder);\n }, [placeholder]);\n\n const handleInvoke = async () => {\n setLoading(true);\n setError(null);\n setResult(null);\n try {\n const args = argsText.trim() ? JSON.parse(argsText) : {};\n const res = await invokeTool(tool.name, args);\n setResult(res);\n } catch (e: any) {\n setError(e?.message ?? String(e));\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div style={{ border: '1px solid #e5e7eb', borderRadius: 10, padding: 14, marginBottom: 12, background: '#fff' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', gap: 12 }}>\n <div>\n <div style={{ fontWeight: 700, fontFamily: 'monospace', fontSize: 13 }}>{tool.name}</div>\n <div style={{ color: '#555', fontSize: 13, marginTop: 4 }}>{tool.description || <span style={{color:'#999'}}>No description</span>}</div>\n <div style={{ fontSize: 11, color: '#888', marginTop: 4 }}>\n <span style={{ background: tool.registered ? '#dcfce7' : '#fee2e2', padding: '2px 6px', borderRadius: 999 }}>{tool.status}</span>\n <span style={{ marginLeft: 6 }}>{tool.source}</span>\n {tool.annotations && Object.keys(tool.annotations).length > 0 && (\n <span style={{ marginLeft: 6, fontFamily: 'monospace' }}>{JSON.stringify(tool.annotations)}</span>\n )}\n </div>\n </div>\n </div>\n\n <details style={{ marginTop: 8 }}>\n <summary style={{ cursor: 'pointer', fontSize: 12, color: '#666' }}>inputSchema</summary>\n <SchemaView schema={tool.inputSchema} />\n {tool.outputSchema && (\n <>\n <div style={{ fontSize: 12, color: '#666', marginTop: 8 }}>outputSchema</div>\n <SchemaView schema={tool.outputSchema} />\n </>\n )}\n </details>\n\n <div style={{ marginTop: 10 }}>\n <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 4 }}>Invoke</div>\n <textarea\n value={argsText}\n onChange={(e) => setArgsText(e.target.value)}\n rows={4}\n placeholder={placeholder}\n style={{ width: '100%', fontFamily: 'monospace', fontSize: 12, padding: 8, borderRadius: 8, border: '1px solid #ddd', background: '#fafafa' }}\n />\n <button\n onClick={handleInvoke}\n disabled={loading}\n style={{ marginTop: 8, padding: '8px 14px', borderRadius: 8, border: '1px solid #111', background: loading ? '#999' : '#111', color: '#fff', cursor: loading ? 'not-allowed' : 'pointer', fontSize: 13 }}\n >\n {loading ? 'Invoking…' : `Invoke ${tool.name}`}\n </button>\n {error && (\n <pre style={{ background: '#fef2f2', color: '#b91c1c', padding: 10, borderRadius: 8, marginTop: 8, fontSize: 12, whiteSpace: 'pre-wrap' }}>{error}</pre>\n )}\n {result !== null && !error && (\n <pre style={{ background: '#f0fdf4', color: '#065f46', padding: 10, borderRadius: 8, marginTop: 8, fontSize: 12, whiteSpace: 'pre-wrap', maxHeight: 200, overflow: 'auto' }}>\n {typeof result === 'string' ? result : JSON.stringify(result, null, 2)}\n </pre>\n )}\n </div>\n </div>\n );\n}\n\nexport function Inspector({ defaultOpen = true, filter }: Props) {\n const [tools, setTools] = useState<InspectedTool[]>(() => listTools());\n const [supported, setSupported] = useState<boolean>(() => isSupported());\n const [open, setOpen] = useState(defaultOpen);\n\n useEffect(() => {\n setSupported(isSupported());\n setTools(listTools());\n const off = onToolsChanged((t) => setTools(t));\n return off;\n }, []);\n\n const filtered = filter ? tools.filter(filter) : tools;\n\n return (\n <div style={{ fontFamily: 'system-ui, sans-serif', border: '1px solid #e5e7eb', borderRadius: 12, overflow: 'hidden', background: '#f9fafb', maxWidth: 720 }}>\n <div\n onClick={() => setOpen(!open)}\n style={{ padding: '12px 16px', background: '#111', color: '#fff', display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }}\n >\n <div style={{ fontWeight: 700, fontSize: 13 }}>\n 🔍 simple-webmcp Inspect — {filtered.length} tool{filtered.length !== 1 ? 's' : ''}\n <span style={{ fontWeight: 400, opacity: 0.8, marginLeft: 8, fontSize: 11 }}>\n {supported ? 'supported' : 'unsupported (dev shim)'}\n </span>\n </div>\n <div style={{ fontSize: 12 }}>{open ? '−' : '+'}</div>\n </div>\n\n {open && (\n <div style={{ padding: 16 }}>\n <div style={{ fontSize: 12, color: '#666', marginBottom: 12 }}>\n Lists <code>registry.list()</code> + <code>document.modelContext.getTools()</code> (if native). Invoke calls <code>executeTool</code> / <code>invokeTool</code> / registry fallback. Works with `simple-webmcp/dev-polyfill` in dev.\n </div>\n\n {filtered.length === 0 ? (\n <div style={{ padding: 24, textAlign: 'center', color: '#888', fontSize: 13, background: '#fff', borderRadius: 8, border: '1px dashed #ddd' }}>\n No tools registered yet. Mount a component with <code>useWebMCP(fn)</code> or call <code>tool.register()</code>.\n </div>\n ) : (\n filtered.map((t) => <ToolCard key={t.name} tool={t} />)\n )}\n\n <div style={{ fontSize: 11, color: '#888', marginTop: 8, textAlign: 'center' }}>\n simple-webmcp inspect — <code>import {'{'}listTools, invokeTool{'}'} from 'simple-webmcp/inspect'</code>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default Inspector;\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InspectedTool } from './inspect.cjs';
|
|
3
|
+
import './types-DH78HDAE.cjs';
|
|
4
|
+
import './registry-BcPw1qCf.cjs';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
/** If true, panel is expanded by default */
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
/** Optional filter to show only specific tools */
|
|
10
|
+
filter?: (tool: InspectedTool) => boolean;
|
|
11
|
+
};
|
|
12
|
+
declare function Inspector({ defaultOpen, filter }: Props): React.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { Inspector, Inspector as InspectorDefault };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InspectedTool } from './inspect.js';
|
|
3
|
+
import './types-DH78HDAE.js';
|
|
4
|
+
import './registry-C6Pz_Rja.js';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
/** If true, panel is expanded by default */
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
/** Optional filter to show only specific tools */
|
|
10
|
+
filter?: (tool: InspectedTool) => boolean;
|
|
11
|
+
};
|
|
12
|
+
declare function Inspector({ defaultOpen, filter }: Props): React.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { Inspector, Inspector as InspectorDefault };
|