@mcp-fe/react-tools 0.1.2 → 0.1.4

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.
Files changed (2) hide show
  1. package/index.mjs +117 -792
  2. package/package.json +2 -4
package/index.mjs CHANGED
@@ -1,855 +1,180 @@
1
- import { useState as R, useRef as v, useEffect as w, useCallback as C, createContext as x, useContext as U } from "react";
2
- import { jsx as z } from "react/jsx-runtime";
3
- const N = typeof process < "u" && process.env?.NODE_ENV === "production", I = typeof process < "u" && process.env?.MCP_DEBUG, m = I === "true" || !N && I !== "false", i = {
4
- log: (...c) => {
5
- m && console.log(...c);
6
- },
7
- debug: (...c) => {
8
- m && console.debug(...c);
9
- },
10
- info: (...c) => {
11
- console.info(...c);
12
- },
13
- error: (...c) => {
14
- console.error(...c);
15
- },
16
- warn: (...c) => {
17
- m && console.warn(...c);
18
- }
19
- };
20
- class _ {
21
- // Configurable worker script URLs (defaults kept for backward compatibility)
22
- sharedWorkerUrl = "/mcp-shared-worker.js";
23
- serviceWorkerUrl = "/mcp-service-worker.js";
24
- // Backend websocket URL to pass into the worker(s)
25
- backendWsUrl = "ws://localhost:3001";
26
- serviceWorkerRegistration = null;
27
- sharedWorker = null;
28
- sharedWorkerPort = null;
29
- workerType = null;
30
- pendingAuthToken = null;
31
- // connection status subscribers
32
- connectionStatusCallbacks = /* @__PURE__ */ new Set();
33
- // Mutex/promise to prevent concurrent init runs
34
- initPromise = null;
35
- // Initialization state
36
- isInitialized = !1;
37
- initResolvers = [];
38
- // Queue for operations that need to wait for initialization
39
- pendingRegistrations = [];
40
- // Map to store tool handlers in main thread
41
- toolHandlers = /* @__PURE__ */ new Map();
42
- // Tool registry for tracking registrations and reference counting
43
- toolRegistry = /* @__PURE__ */ new Map();
44
- // Subscribers for tool changes (for React hooks reactivity)
45
- toolChangeListeners = /* @__PURE__ */ new Map();
46
- // Initialize and choose worker implementation (prefer SharedWorker)
47
- // Accept either a ServiceWorkerRegistration OR WorkerInitOptions to configure URLs
48
- async init(e) {
49
- i.log("[WorkerClient] init() called", {
50
- hasOptions: !!e,
51
- currentWorkerType: this.workerType,
52
- initInProgress: !!this.initPromise,
53
- timestamp: Date.now()
54
- });
55
- let r;
56
- const t = e;
57
- if (t && typeof t.scope == "string")
58
- r = e, i.log("[WorkerClient] Using explicit ServiceWorker registration");
59
- else if (e) {
60
- const o = e;
61
- i.log("[WorkerClient] Using WorkerClientInitOptions:", o), o.sharedWorkerUrl && (this.sharedWorkerUrl = o.sharedWorkerUrl), o.serviceWorkerUrl && (this.serviceWorkerUrl = o.serviceWorkerUrl), o.backendWsUrl && (this.backendWsUrl = o.backendWsUrl);
62
- }
63
- return this.initPromise ? this.initPromise.then(async () => {
64
- r && this.workerType !== "service" && await this.init(r);
65
- }) : (this.initPromise = (async () => {
66
- try {
67
- if (r) {
68
- this.serviceWorkerRegistration = r, this.workerType = "service", i.info(
69
- "[WorkerClient] Using ServiceWorker (explicit registration)"
70
- );
71
- try {
72
- const s = {
73
- type: "INIT",
74
- backendUrl: this.backendWsUrl
75
- };
76
- this.pendingAuthToken && (s.token = this.pendingAuthToken), this.serviceWorkerRegistration.active ? this.serviceWorkerRegistration.active.postMessage(s) : "serviceWorker" in navigator && navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage(s);
77
- } catch {
78
- }
79
- return;
80
- }
81
- if (await this.initSharedWorker()) {
82
- this.markAsInitialized();
83
- return;
84
- }
85
- await this.initServiceWorkerFallback(), this.markAsInitialized();
86
- } finally {
87
- this.initPromise = null;
88
- }
89
- })(), this.initPromise);
90
- }
91
- /**
92
- * Mark worker as initialized and process pending registrations
93
- * @private
94
- */
95
- markAsInitialized() {
96
- this.isInitialized = !0, i.log(
97
- "[WorkerClient] Worker initialized, processing pending operations"
98
- ), this.initResolvers.forEach((r) => r()), this.initResolvers = [];
99
- const e = [...this.pendingRegistrations];
100
- this.pendingRegistrations = [], e.forEach(
101
- async ({ name: r, description: t, inputSchema: o, handler: s, resolve: a, reject: h }) => {
102
- try {
103
- await this.registerToolInternal(
104
- r,
105
- t,
106
- o,
107
- s
108
- ), a();
109
- } catch (u) {
110
- h(u instanceof Error ? u : new Error(String(u)));
111
- }
112
- }
113
- );
114
- }
115
- /**
116
- * Wait for worker initialization
117
- * @returns Promise that resolves when worker is initialized
118
- */
119
- async waitForInit() {
120
- if (this.isInitialized)
121
- return Promise.resolve();
122
- if (this.initPromise) {
123
- await this.initPromise;
124
- return;
125
- }
126
- return new Promise((e) => {
127
- this.initResolvers.push(e);
128
- });
129
- }
130
- /**
131
- * Check if worker is initialized
132
- */
133
- get initialized() {
134
- return this.isInitialized;
135
- }
136
- async initSharedWorker() {
137
- if (typeof SharedWorker > "u")
138
- return Promise.resolve(!1);
139
- try {
140
- this.sharedWorker = new SharedWorker(this.sharedWorkerUrl, {
141
- type: "module"
142
- }), this.sharedWorkerPort = this.sharedWorker.port, this.sharedWorkerPort.start(), await new Promise((r, t) => {
143
- let o = !1;
144
- const s = setTimeout(() => {
145
- if (!o) {
146
- const h = this.sharedWorkerPort;
147
- h && (h.onmessage = null), t(new Error("SharedWorker initialization timeout"));
148
- }
149
- }, 2e3), a = this.sharedWorkerPort;
150
- if (!a)
151
- return clearTimeout(s), t(new Error("SharedWorker port not available"));
152
- a.onmessage = (h) => {
153
- try {
154
- const u = h.data;
155
- u && u.type === "CONNECTION_STATUS" && (clearTimeout(s), o = !0, this.workerType = "shared", a.onmessage = null, r(!0));
156
- } catch {
157
- }
158
- };
159
- });
160
- const e = this.sharedWorkerPort;
161
- if (e) {
162
- try {
163
- const r = {
164
- type: "INIT",
165
- backendUrl: this.backendWsUrl
166
- };
167
- this.pendingAuthToken && (r.token = this.pendingAuthToken), e.postMessage(r), this.pendingAuthToken = null;
168
- } catch (r) {
169
- i.warn(
170
- "[WorkerClient] Failed to send INIT to SharedWorker port:",
171
- r
172
- );
173
- }
174
- e.onmessage = (r) => {
175
- try {
176
- const t = r.data;
177
- if (t && t.type === "CONNECTION_STATUS") {
178
- const o = !!t.connected;
179
- this.connectionStatusCallbacks.forEach((s) => {
180
- try {
181
- s(o);
182
- } catch {
183
- }
184
- });
185
- } else t && t.type === "CALL_TOOL" && this.handleToolCall(t.toolName, t.args, t.callId).catch(
186
- (o) => {
187
- i.error(
188
- "[WorkerClient] Failed to handle tool call:",
189
- o
190
- );
191
- }
192
- );
193
- } catch {
194
- }
195
- };
196
- }
197
- return i.info("[WorkerClient] Using SharedWorker"), !0;
198
- } catch (e) {
199
- return i.warn(
200
- "[WorkerClient] SharedWorker not available, falling back to ServiceWorker:",
201
- e
202
- ), !1;
203
- }
204
- }
205
- async initServiceWorkerFallback() {
206
- if ("serviceWorker" in navigator)
207
- try {
208
- const e = await navigator.serviceWorker.getRegistration();
209
- if (e) {
210
- this.serviceWorkerRegistration = e, this.workerType = "service", i.info(
211
- "[WorkerClient] Using existing ServiceWorker registration"
212
- );
213
- return;
214
- }
215
- this.serviceWorkerRegistration = await navigator.serviceWorker.register(
216
- this.serviceWorkerUrl
217
- ), this.workerType = "service", "serviceWorker" in navigator && navigator.serviceWorker.addEventListener(
218
- "message",
219
- (r) => {
220
- try {
221
- const t = r.data;
222
- t && t.type === "CALL_TOOL" && this.handleToolCall(
223
- t.toolName,
224
- t.args,
225
- t.callId
226
- ).catch((o) => {
227
- i.error(
228
- "[WorkerClient] Failed to handle tool call:",
229
- o
230
- );
231
- });
232
- } catch (t) {
233
- i.error(
234
- "[WorkerClient] Error processing ServiceWorker message:",
235
- t
236
- );
237
- }
238
- }
239
- ), i.info("[WorkerClient] Using MCP ServiceWorker (fallback)");
240
- try {
241
- const r = {
242
- type: "INIT",
243
- backendUrl: this.backendWsUrl
244
- };
245
- this.pendingAuthToken && (r.token = this.pendingAuthToken), this.serviceWorkerRegistration.active ? this.serviceWorkerRegistration.active.postMessage(r) : "serviceWorker" in navigator && navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage(r), this.pendingAuthToken = null;
246
- } catch {
247
- }
248
- } catch (e) {
249
- throw i.error("[WorkerClient] Failed to register ServiceWorker:", e), e;
250
- }
251
- else
252
- throw new Error("Neither SharedWorker nor ServiceWorker is supported");
253
- }
254
- // Low-level request that expects a reply via MessageChannel
255
- async request(e, r, t = 5e3) {
256
- if (i.log("[WorkerClient] Request started:", {
257
- type: e,
258
- payload: r,
259
- timeoutMs: t,
260
- workerType: this.workerType,
261
- hasSharedWorkerPort: !!this.sharedWorkerPort,
262
- hasServiceWorkerReg: !!this.serviceWorkerRegistration
263
- }), this.workerType === "shared" && this.sharedWorkerPort)
264
- return new Promise((o, s) => {
265
- const a = new MessageChannel(), h = Math.random().toString(36).substring(7), u = Date.now(), g = setTimeout(() => {
266
- const l = Date.now() - u;
267
- i.error("[WorkerClient] Request timeout:", {
268
- type: e,
269
- requestId: h,
270
- elapsed: l,
271
- timeoutMs: t
272
- }), a.port1.onmessage = null, s(new Error("Request timeout"));
273
- }, t);
274
- a.port1.onmessage = (l) => {
275
- try {
276
- const n = Date.now() - u;
277
- i.log("[WorkerClient] Request response received:", {
278
- type: e,
279
- requestId: h,
280
- elapsed: n,
281
- success: l.data?.success
282
- }), clearTimeout(g), l.data && l.data.success ? o(l.data) : l.data && l.data.success === !1 ? s(new Error(l.data.error || "Worker error")) : o(l.data);
283
- } catch (n) {
284
- clearTimeout(g), a.port1.onmessage = null, s(
285
- n instanceof Error ? n : new Error(String(n))
286
- );
287
- }
288
- };
289
- try {
290
- const l = this.sharedWorkerPort;
291
- if (!l)
292
- return clearTimeout(g), i.error("[WorkerClient] SharedWorker port not available"), s(new Error("SharedWorker port not available"));
293
- i.log("[WorkerClient] Posting message to SharedWorker:", {
294
- type: e,
295
- requestId: h
296
- }), l.postMessage({ type: e, ...r || {} }, [a.port2]);
297
- } catch (l) {
298
- clearTimeout(g), i.error("[WorkerClient] Failed to post message:", l), s(l instanceof Error ? l : new Error(String(l)));
299
- }
300
- });
301
- if (this.workerType === "service" && this.serviceWorkerRegistration) {
302
- const o = this.serviceWorkerRegistration;
303
- if (!o) throw new Error("Service worker registration missing");
304
- if (!o.active && (i.log("[WorkerClient] ServiceWorker not active, waiting..."), await navigator.serviceWorker.ready, !o.active))
305
- throw new Error("Service worker not active");
306
- return new Promise((s, a) => {
307
- const h = new MessageChannel(), u = Math.random().toString(36).substring(7), g = Date.now(), l = setTimeout(() => {
308
- const n = Date.now() - g;
309
- i.error("[WorkerClient] ServiceWorker request timeout:", {
310
- type: e,
311
- requestId: u,
312
- elapsed: n,
313
- timeoutMs: t
314
- }), h.port1.onmessage = null, a(new Error("Request timeout"));
315
- }, t);
316
- h.port1.onmessage = (n) => {
317
- try {
318
- const W = Date.now() - g;
319
- i.log("[WorkerClient] ServiceWorker response received:", {
320
- type: e,
321
- requestId: u,
322
- elapsed: W,
323
- success: n.data?.success
324
- }), clearTimeout(l), n.data && n.data.success ? s(n.data) : n.data && n.data.success === !1 ? a(new Error(n.data.error || "Worker error")) : s(n.data);
325
- } catch (W) {
326
- clearTimeout(l), h.port1.onmessage = null, a(
327
- W instanceof Error ? W : new Error(String(W))
328
- );
329
- }
330
- };
331
- try {
332
- const n = o.active;
333
- if (!n)
334
- return clearTimeout(l), i.error(
335
- "[WorkerClient] ServiceWorker active instance not available"
336
- ), a(
337
- new Error("Service worker active instance not available")
338
- );
339
- i.log("[WorkerClient] Posting message to ServiceWorker:", {
340
- type: e,
341
- requestId: u
342
- }), n.postMessage({ type: e, ...r || {} }, [h.port2]);
343
- } catch (n) {
344
- clearTimeout(l), i.error(
345
- "[WorkerClient] Failed to post message to ServiceWorker:",
346
- n
347
- ), a(n instanceof Error ? n : new Error(String(n)));
348
- }
349
- });
350
- }
351
- throw new Error("No worker registered");
352
- }
353
- // Fire-and-forget postMessage (no response expected)
354
- async post(e, r) {
355
- if (this.workerType === "shared" && this.sharedWorkerPort) {
356
- try {
357
- this.sharedWorkerPort.postMessage({ type: e, ...r || {} });
358
- } catch (t) {
359
- i.error("[WorkerClient] Failed to post to SharedWorker:", t);
360
- }
361
- return;
362
- }
363
- if (this.workerType === "service" && this.serviceWorkerRegistration?.active) {
364
- try {
365
- this.serviceWorkerRegistration.active.postMessage({
366
- type: e,
367
- ...r || {}
368
- });
369
- } catch (t) {
370
- i.error(
371
- "[WorkerClient] Failed to post to ServiceWorker (active):",
372
- t
373
- );
374
- }
375
- return;
376
- }
377
- if ("serviceWorker" in navigator && navigator.serviceWorker.controller) {
378
- try {
379
- navigator.serviceWorker.controller.postMessage({
380
- type: e,
381
- ...r || {}
382
- });
383
- } catch (t) {
384
- i.error(
385
- "[WorkerClient] Failed to post to ServiceWorker.controller:",
386
- t
387
- );
388
- }
389
- return;
390
- }
391
- if (e === "SET_AUTH_TOKEN" && r) {
392
- const t = r.token;
393
- typeof t == "string" && (this.pendingAuthToken = t);
394
- }
395
- }
396
- sendAuthTokenToServiceWorker(e) {
397
- if (this.serviceWorkerRegistration?.active)
398
- try {
399
- this.serviceWorkerRegistration.active.postMessage({
400
- type: "SET_AUTH_TOKEN",
401
- token: e
402
- });
403
- } catch (r) {
404
- console.error(
405
- "[WorkerClient] Failed to send auth token to ServiceWorker:",
406
- r
407
- );
408
- }
409
- else if ("serviceWorker" in navigator && navigator.serviceWorker.controller)
410
- try {
411
- navigator.serviceWorker.controller.postMessage({
412
- type: "SET_AUTH_TOKEN",
413
- token: e
414
- });
415
- } catch (r) {
416
- console.error(
417
- "[WorkerClient] Failed to send auth token to ServiceWorker.controller:",
418
- r
419
- );
420
- }
421
- else
422
- this.pendingAuthToken = e;
423
- }
424
- // Subscription API for consumers to listen for connection status updates
425
- onConnectionStatus(e) {
426
- this.connectionStatusCallbacks.add(e);
427
- }
428
- offConnectionStatus(e) {
429
- this.connectionStatusCallbacks.delete(e);
430
- }
431
- async getConnectionStatus() {
432
- try {
433
- const e = await this.request(
434
- "GET_CONNECTION_STATUS",
435
- void 0,
436
- 2e3
437
- );
438
- return e && typeof e == "object" && "connected" in e ? !!e.connected : !!e?.connected;
439
- } catch {
440
- return !1;
441
- }
442
- }
443
- setAuthToken(e) {
444
- if (this.pendingAuthToken = e, this.workerType === "shared" && this.sharedWorkerPort)
445
- try {
446
- this.sharedWorkerPort.postMessage({ type: "SET_AUTH_TOKEN", token: e }), this.pendingAuthToken = null;
447
- } catch (r) {
448
- i.error(
449
- "[WorkerClient] Failed to set auth token on SharedWorker:",
450
- r
451
- );
452
- }
453
- else this.workerType === "service" && (this.sendAuthTokenToServiceWorker(e), this.pendingAuthToken = null);
454
- }
455
- /**
456
- * Register a custom MCP tool dynamically
457
- *
458
- * The handler function runs in the MAIN THREAD (browser context), not in the worker.
459
- * This means you have full access to:
460
- * - React context, hooks, Redux store
461
- * - DOM API, window, localStorage
462
- * - All your imports and dependencies
463
- * - Closures and external variables
464
- *
465
- * The worker acts as a proxy - it receives MCP tool calls and forwards them
466
- * to your handler via MessageChannel.
467
- *
468
- * @param name - Tool name
469
- * @param description - Tool description
470
- * @param inputSchema - JSON Schema for tool inputs
471
- * @param handler - Async function that handles the tool execution (runs in main thread)
472
- * @returns Promise that resolves when tool is registered
473
- *
474
- * @example With full access to imports and context:
475
- * ```typescript
476
- * import { z } from 'zod';
477
- * import { useMyStore } from './store';
478
- *
479
- * const store = useMyStore();
480
- *
481
- * await client.registerTool(
482
- * 'validate_user',
483
- * 'Validate user data',
484
- * { type: 'object', properties: { username: { type: 'string' } } },
485
- * async (args: any) => {
486
- * // Full access to everything!
487
- * const schema = z.object({ username: z.string().min(3) });
488
- * const validated = schema.parse(args);
489
- *
490
- * // Can access store, context, etc.
491
- * const currentUser = store.getState().user;
492
- *
493
- * return {
494
- * content: [{
495
- * type: 'text',
496
- * text: JSON.stringify({ validated, currentUser })
497
- * }]
498
- * };
499
- * }
500
- * );
501
- * ```
502
- */
503
- async registerTool(e, r, t, o) {
504
- return this.isInitialized ? this.registerToolInternal(e, r, t, o) : (i.log(
505
- `[WorkerClient] Queueing tool registration '${e}' (worker not initialized yet)`
506
- ), new Promise((s, a) => {
507
- this.pendingRegistrations.push({
508
- name: e,
509
- description: r,
510
- inputSchema: t,
511
- handler: o,
512
- resolve: s,
513
- reject: a
514
- });
515
- }));
516
- }
517
- /**
518
- * Internal method to register tool (assumes worker is initialized)
519
- * @private
520
- */
521
- async registerToolInternal(e, r, t, o) {
522
- const s = this.toolRegistry.get(e);
523
- if (s) {
524
- s.refCount++, i.log(
525
- `[WorkerClient] Incremented ref count for '${e}': ${s.refCount}`
526
- ), this.toolHandlers.set(e, o), this.notifyToolChange(e);
527
- return;
528
- }
529
- this.toolHandlers.set(e, o), await this.request("REGISTER_TOOL", {
530
- name: e,
531
- description: r,
532
- inputSchema: t,
533
- handlerType: "proxy"
534
- // Tell worker this is a proxy handler
535
- }), this.toolRegistry.set(e, {
536
- refCount: 1,
537
- description: r,
538
- inputSchema: t,
539
- isRegistered: !0
540
- }), i.log(
541
- `[WorkerClient] Registered tool '${e}' with main-thread handler`
542
- ), this.notifyToolChange(e);
543
- }
544
- /**
545
- * Unregister a previously registered MCP tool
546
- * @param name - Tool name to unregister
547
- * @returns Promise that resolves to true if tool was found and removed
548
- */
549
- async unregisterTool(e) {
550
- const r = this.toolRegistry.get(e);
551
- if (!r)
552
- return i.warn(`[WorkerClient] Cannot unregister '${e}': not found`), !1;
553
- if (r.refCount--, i.log(
554
- `[WorkerClient] Decremented ref count for '${e}': ${r.refCount}`
555
- ), r.refCount <= 0) {
556
- this.toolHandlers.delete(e);
557
- const t = await this.request(
558
- "UNREGISTER_TOOL",
559
- { name: e }
560
- );
561
- return this.toolRegistry.delete(e), i.log(`[WorkerClient] Unregistered tool '${e}'`), this.notifyToolChange(e), t?.success ?? !1;
562
- }
563
- return this.notifyToolChange(e), !0;
564
- }
565
- /**
566
- * Subscribe to tool changes for a specific tool
567
- * Returns unsubscribe function
568
- */
569
- onToolChange(e, r) {
570
- this.toolChangeListeners.has(e) || this.toolChangeListeners.set(e, /* @__PURE__ */ new Set());
571
- const t = this.toolChangeListeners.get(e);
572
- t.add(r);
573
- const o = this.toolRegistry.get(e);
574
- return r(o ? {
575
- refCount: o.refCount,
576
- isRegistered: o.isRegistered
577
- } : null), () => {
578
- t.delete(r), t.size === 0 && this.toolChangeListeners.delete(e);
579
- };
580
- }
581
- /**
582
- * Notify all listeners about tool changes
583
- * @private
584
- */
585
- notifyToolChange(e) {
586
- const r = this.toolChangeListeners.get(e);
587
- if (!r || r.size === 0) return;
588
- const t = this.toolRegistry.get(e), o = t ? {
589
- refCount: t.refCount,
590
- isRegistered: t.isRegistered
591
- } : null;
592
- r.forEach((s) => {
593
- try {
594
- s(o);
595
- } catch (a) {
596
- i.error("[WorkerClient] Error in tool change listener:", a);
597
- }
598
- });
599
- }
600
- /**
601
- * Get tool info from registry
602
- */
603
- getToolInfo(e) {
604
- const r = this.toolRegistry.get(e);
605
- return r ? {
606
- refCount: r.refCount,
607
- isRegistered: r.isRegistered
608
- } : null;
609
- }
610
- /**
611
- * Get all registered tool names
612
- */
613
- getRegisteredTools() {
614
- return Array.from(this.toolRegistry.keys()).filter(
615
- (e) => this.toolRegistry.get(e)?.isRegistered
616
- );
617
- }
618
- /**
619
- * Check if a tool is registered
620
- */
621
- isToolRegistered(e) {
622
- return this.toolRegistry.get(e)?.isRegistered ?? !1;
623
- }
624
- /**
625
- * Handle tool call from worker - execute handler in main thread and return result
626
- * @private
627
- */
628
- async handleToolCall(e, r, t) {
629
- i.log(`[WorkerClient] Handling tool call: ${e}`, {
630
- callId: t,
631
- args: r
632
- });
633
- try {
634
- const o = this.toolHandlers.get(e);
635
- if (!o)
636
- throw new Error(`Tool handler not found: ${e}`);
637
- const s = await o(r);
638
- this.sendToolCallResult(t, { success: !0, result: s });
639
- } catch (o) {
640
- i.error(`[WorkerClient] Tool call failed: ${e}`, o), this.sendToolCallResult(t, {
641
- success: !1,
642
- error: o instanceof Error ? o.message : "Unknown error"
643
- });
644
- }
645
- }
646
- /**
647
- * Send tool call result back to worker
648
- * @private
649
- */
650
- sendToolCallResult(e, r) {
651
- const t = {
652
- type: "TOOL_CALL_RESULT",
653
- callId: e,
654
- success: r.success,
655
- result: r.result,
656
- error: r.error
657
- };
658
- if (this.workerType === "shared" && this.sharedWorkerPort)
659
- try {
660
- this.sharedWorkerPort.postMessage(t);
661
- } catch (o) {
662
- i.error(
663
- "[WorkerClient] Failed to send result to SharedWorker:",
664
- o
665
- );
666
- }
667
- else if (this.workerType === "service" && this.serviceWorkerRegistration?.active)
668
- try {
669
- this.serviceWorkerRegistration.active.postMessage(t);
670
- } catch (o) {
671
- i.error(
672
- "[WorkerClient] Failed to send result to ServiceWorker:",
673
- o
674
- );
675
- }
676
- }
677
- }
678
- const T = new _();
679
- function b(c) {
1
+ import { useState as m, useRef as P, useEffect as M, useCallback as f, createContext as $, useContext as I } from "react";
2
+ import { workerClient as u } from "@mcp-fe/mcp-worker";
3
+ import { jsx as A } from "react/jsx-runtime";
4
+ function b(t) {
680
5
  const {
681
6
  name: e,
682
- description: r,
683
- inputSchema: t,
684
- handler: o,
685
- autoRegister: s = !0,
686
- autoUnregister: a = !0
687
- } = c, [h, u] = R(null), g = v(o), l = v(e), n = v(r), W = v(t), P = v(!0), k = v(!1);
688
- w(() => {
689
- g.current = o, l.current = e, n.current = r, W.current = t;
690
- }, [o, e, r, t]);
691
- const f = C(async (d) => g.current(d), []), y = C(async () => {
692
- const d = l.current, p = n.current, A = W.current;
7
+ description: s,
8
+ inputSchema: i,
9
+ handler: r,
10
+ autoRegister: a = !0,
11
+ autoUnregister: l = !0
12
+ } = t, [d, p] = m(null), g = P(r), T = P(e), C = P(s), w = P(i), v = P(!0), n = P(!1);
13
+ M(() => {
14
+ g.current = r, T.current = e, C.current = s, w.current = i;
15
+ }, [r, e, s, i]);
16
+ const c = f(async (o) => g.current(o), []), y = f(async () => {
17
+ const o = T.current, h = C.current, z = w.current;
693
18
  try {
694
- await T.registerTool(
695
- d,
696
- p,
697
- A,
698
- f
699
- ), k.current = !0, console.log(`[useMCPTool] Registered tool '${d}'`);
700
- } catch (M) {
19
+ await u.registerTool(
20
+ o,
21
+ h,
22
+ z,
23
+ c
24
+ ), n.current = !0, console.log(`[useMCPTool] Registered tool '${o}'`);
25
+ } catch (S) {
701
26
  throw console.error(
702
- `[useMCPTool] Failed to register tool '${d}':`,
703
- M
704
- ), M;
27
+ `[useMCPTool] Failed to register tool '${o}':`,
28
+ S
29
+ ), S;
705
30
  }
706
- }, [f]), S = C(async () => {
707
- const d = l.current;
31
+ }, [c]), R = f(async () => {
32
+ const o = T.current;
708
33
  try {
709
- await T.unregisterTool(d), k.current = !1, console.log(`[useMCPTool] Unregistered tool '${d}'`);
710
- } catch (p) {
34
+ await u.unregisterTool(o), n.current = !1, console.log(`[useMCPTool] Unregistered tool '${o}'`);
35
+ } catch (h) {
711
36
  console.error(
712
- `[useMCPTool] Failed to unregister tool '${d}':`,
713
- p
37
+ `[useMCPTool] Failed to unregister tool '${o}':`,
38
+ h
714
39
  );
715
40
  }
716
41
  }, []);
717
- return w(() => T.onToolChange(e, (p) => {
718
- u(p);
719
- }), [e]), w(() => (s && y().catch((d) => {
42
+ return M(() => u.onToolChange(e, (h) => {
43
+ p(h);
44
+ }), [e]), M(() => (a && y().catch((o) => {
720
45
  console.error(
721
46
  `[useMCPTool] Auto-register failed for '${e}':`,
722
- d
47
+ o
723
48
  );
724
49
  }), () => {
725
- P.current = !1, a && k.current && S().catch((d) => {
50
+ v.current = !1, l && n.current && R().catch((o) => {
726
51
  console.error(
727
52
  `[useMCPTool] Auto-unregister failed for '${e}':`,
728
- d
53
+ o
729
54
  );
730
55
  });
731
56
  }), [e]), {
732
- isRegistered: h?.isRegistered ?? !1,
733
- refCount: h?.refCount ?? 0,
57
+ isRegistered: d?.isRegistered ?? !1,
58
+ refCount: d?.refCount ?? 0,
734
59
  register: y,
735
- unregister: S
60
+ unregister: R
736
61
  };
737
62
  }
738
- function O(c) {
739
- return T.isToolRegistered(c);
63
+ function k(t) {
64
+ return u.isToolRegistered(t);
740
65
  }
741
- function $() {
742
- return T.getRegisteredTools();
66
+ function q() {
67
+ return u.getRegisteredTools();
743
68
  }
744
- function q(c) {
745
- return T.getToolInfo(c);
69
+ function U(t) {
70
+ return u.getToolInfo(t);
746
71
  }
747
- function D(c, e, r, t) {
748
- const o = C(async () => {
749
- const s = await r();
72
+ function F(t, e, s, i) {
73
+ const r = f(async () => {
74
+ const a = await s();
750
75
  return {
751
76
  content: [
752
77
  {
753
78
  type: "text",
754
- text: JSON.stringify(s, null, 2)
79
+ text: JSON.stringify(a, null, 2)
755
80
  }
756
81
  ]
757
82
  };
758
- }, [r]);
83
+ }, [s]);
759
84
  return b({
760
- name: c,
85
+ name: t,
761
86
  description: e,
762
87
  inputSchema: {
763
88
  type: "object",
764
89
  properties: {}
765
90
  },
766
- handler: o,
767
- ...t
91
+ handler: r,
92
+ ...i
768
93
  });
769
94
  }
770
- function H(c, e, r, t, o) {
771
- const s = C(
772
- async (a) => {
773
- const h = await t(a);
95
+ function H(t, e, s, i, r) {
96
+ const a = f(
97
+ async (l) => {
98
+ const d = await i(l);
774
99
  return {
775
100
  content: [
776
101
  {
777
102
  type: "text",
778
- text: JSON.stringify(h, null, 2)
103
+ text: JSON.stringify(d, null, 2)
779
104
  }
780
105
  ]
781
106
  };
782
107
  },
783
- [t]
108
+ [i]
784
109
  );
785
110
  return b({
786
- name: c,
111
+ name: t,
787
112
  description: e,
788
113
  inputSchema: {
789
114
  type: "object",
790
- properties: r,
791
- ...o?.required && { required: o.required }
115
+ properties: s,
116
+ ...r?.required && { required: r.required }
792
117
  },
793
- handler: s,
794
- autoRegister: o?.autoRegister,
795
- autoUnregister: o?.autoUnregister
118
+ handler: a,
119
+ autoRegister: r?.autoRegister,
120
+ autoUnregister: r?.autoUnregister
796
121
  });
797
122
  }
798
- const E = x(null);
799
- function G({
800
- children: c,
123
+ const x = $(null);
124
+ function J({
125
+ children: t,
801
126
  autoInit: e = !0,
802
- backendWsUrl: r = "ws://localhost:3001",
803
- initOptions: t,
804
- onInitialized: o,
805
- onInitError: s
127
+ backendWsUrl: s = "ws://localhost:3001",
128
+ initOptions: i,
129
+ onInitialized: r,
130
+ onInitError: a
806
131
  }) {
807
- const [a, h] = R(!1), [u, g] = R(!1), [l] = R([]), n = C(
808
- async (k) => {
809
- if (a) {
132
+ const [l, d] = m(!1), [p, g] = m(!1), [T] = m([]), C = f(
133
+ async (n) => {
134
+ if (l) {
810
135
  console.log("[MCPToolsProvider] Already initialized");
811
136
  return;
812
137
  }
813
138
  try {
814
- const f = k || t || { backendWsUrl: r };
815
- console.log("[MCPToolsProvider] Initializing worker client...", f), await T.init(f), h(!0);
816
- const y = await T.getConnectionStatus();
817
- g(y), T.onConnectionStatus((S) => {
818
- g(S);
819
- }), console.log("[MCPToolsProvider] Worker client initialized"), o?.();
820
- } catch (f) {
821
- throw console.error("[MCPToolsProvider] Initialization failed:", f), s?.(
822
- f instanceof Error ? f : new Error(String(f))
823
- ), f;
139
+ const c = n || i || { backendWsUrl: s };
140
+ console.log("[MCPToolsProvider] Initializing worker client...", c), await u.init(c), d(!0);
141
+ const y = await u.getConnectionStatus();
142
+ g(y), u.onConnectionStatus((R) => {
143
+ g(R);
144
+ }), console.log("[MCPToolsProvider] Worker client initialized"), r?.();
145
+ } catch (c) {
146
+ throw console.error("[MCPToolsProvider] Initialization failed:", c), a?.(
147
+ c instanceof Error ? c : new Error(String(c))
148
+ ), c;
824
149
  }
825
150
  },
826
- [a, t, r, o, s]
827
- ), W = C(async () => {
828
- const k = await T.getConnectionStatus();
829
- return g(k), k;
151
+ [l, i, s, r, a]
152
+ ), w = f(async () => {
153
+ const n = await u.getConnectionStatus();
154
+ return g(n), n;
830
155
  }, []);
831
- w(() => {
832
- e && n().catch((k) => {
833
- console.error("[MCPToolsProvider] Auto-init failed:", k);
156
+ M(() => {
157
+ e && C().catch((n) => {
158
+ console.error("[MCPToolsProvider] Auto-init failed:", n);
834
159
  });
835
- }, [e, n]), w(() => {
836
- if (!a) return;
837
- const k = setInterval(() => {
160
+ }, [e, C]), M(() => {
161
+ if (!l) return;
162
+ const n = setInterval(() => {
838
163
  }, 5e3);
839
- return () => clearInterval(k);
840
- }, [a]);
841
- const P = {
842
- isInitialized: a,
843
- isConnected: u,
844
- registeredTools: l,
845
- initialize: n,
846
- getConnectionStatus: W
164
+ return () => clearInterval(n);
165
+ }, [l]);
166
+ const v = {
167
+ isInitialized: l,
168
+ isConnected: p,
169
+ registeredTools: T,
170
+ initialize: C,
171
+ getConnectionStatus: w
847
172
  };
848
- return /* @__PURE__ */ z(E.Provider, { value: P, children: c });
173
+ return /* @__PURE__ */ A(x.Provider, { value: v, children: t });
849
174
  }
850
- function K(c = !1) {
851
- const e = U(E);
852
- if (!e && c)
175
+ function D(t = !1) {
176
+ const e = I(x);
177
+ if (!e && t)
853
178
  throw new Error(
854
179
  "useMCPToolsContext must be used within MCPToolsProvider. Either wrap your component tree with <MCPToolsProvider> or set strict=false."
855
180
  );
@@ -863,17 +188,17 @@ function K(c = !1) {
863
188
  getConnectionStatus: async () => !1
864
189
  };
865
190
  }
866
- function J() {
867
- return U(E) !== null;
191
+ function G() {
192
+ return I(x) !== null;
868
193
  }
869
194
  export {
870
- G as MCPToolsProvider,
871
- $ as getRegisteredTools,
872
- q as getToolInfo,
873
- O as isToolRegistered,
874
- J as useHasMCPProvider,
195
+ J as MCPToolsProvider,
196
+ q as getRegisteredTools,
197
+ U as getToolInfo,
198
+ k as isToolRegistered,
199
+ G as useHasMCPProvider,
875
200
  H as useMCPAction,
876
- D as useMCPGetter,
201
+ F as useMCPGetter,
877
202
  b as useMCPTool,
878
- K as useMCPToolsContext
203
+ D as useMCPToolsContext
879
204
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-fe/react-tools",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://mcp-fe.ai",
6
6
  "repository": {
@@ -18,10 +18,8 @@
18
18
  "default": "./index.mjs"
19
19
  }
20
20
  },
21
- "dependencies": {
22
- "@mcp-fe/mcp-worker": "0.1.2"
23
- },
24
21
  "peerDependencies": {
22
+ "@mcp-fe/mcp-worker": "^0.1.4",
25
23
  "react": "^19.0.0",
26
24
  "react-dom": "^19.0.0"
27
25
  },