simple-webmcp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/devtools.d.cts +2 -2
- package/dist/devtools.d.ts +2 -2
- package/dist/index.cjs +313 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -34
- package/dist/index.d.ts +60 -34
- package/dist/index.js +307 -5
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.cts +2 -2
- package/dist/inspect.d.ts +2 -2
- package/dist/react.cjs +353 -12
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -2
- package/dist/react.d.ts +23 -2
- package/dist/react.js +353 -15
- package/dist/react.js.map +1 -1
- package/dist/{registry-BcPw1qCf.d.cts → registry-A4DdpmDn.d.cts} +1 -1
- package/dist/{registry-C6Pz_Rja.d.ts → registry-Dscedahn.d.ts} +1 -1
- package/dist/{types-DH78HDAE.d.cts → types-CF8kTj5O.d.cts} +60 -1
- package/dist/{types-DH78HDAE.d.ts → types-CF8kTj5O.d.ts} +60 -1
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -81,6 +81,11 @@ var RegistrationError = class extends SimpleWebMCPError {
|
|
|
81
81
|
super(message, "REGISTRATION_ERROR", opts?.cause);
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
+
var ValidationError = class extends SimpleWebMCPError {
|
|
85
|
+
constructor(message, opts) {
|
|
86
|
+
super(message, "VALIDATION_ERROR", opts?.cause);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
84
89
|
var ConfigurationError = class extends SimpleWebMCPError {
|
|
85
90
|
constructor(message, opts) {
|
|
86
91
|
super(message, "CONFIGURATION_ERROR", opts?.cause);
|
|
@@ -587,7 +592,12 @@ function normalizeResult(value) {
|
|
|
587
592
|
}
|
|
588
593
|
}
|
|
589
594
|
function normalizeError(err) {
|
|
590
|
-
const message =
|
|
595
|
+
const message = (() => {
|
|
596
|
+
if (err instanceof Error) return err.message;
|
|
597
|
+
if (typeof err === "string") return err;
|
|
598
|
+
if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
599
|
+
return err.message;
|
|
600
|
+
}
|
|
591
601
|
try {
|
|
592
602
|
return JSON.stringify(err);
|
|
593
603
|
} catch {
|
|
@@ -599,20 +609,247 @@ function normalizeError(err) {
|
|
|
599
609
|
isError: true
|
|
600
610
|
};
|
|
601
611
|
}
|
|
602
|
-
|
|
612
|
+
|
|
613
|
+
// src/hooks/engine.ts
|
|
614
|
+
var fallbackCounter = 0;
|
|
615
|
+
function genInvocationId() {
|
|
616
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
617
|
+
return crypto.randomUUID();
|
|
618
|
+
}
|
|
619
|
+
return fallbackInvocationId();
|
|
620
|
+
}
|
|
621
|
+
function fallbackInvocationId() {
|
|
622
|
+
fallbackCounter = (fallbackCounter + 1) % Number.MAX_SAFE_INTEGER;
|
|
623
|
+
return `webmcp_${Date.now().toString(36)}_${fallbackCounter}_${Math.random().toString(36).slice(2, 8)}`;
|
|
624
|
+
}
|
|
625
|
+
function isDenyResult(r) {
|
|
626
|
+
return !!r && typeof r === "object" && r.action === "deny";
|
|
627
|
+
}
|
|
628
|
+
async function runErrorHooks(hooks, ctx) {
|
|
629
|
+
if (!hooks || hooks.length === 0) return;
|
|
630
|
+
for (const h of hooks) {
|
|
631
|
+
try {
|
|
632
|
+
await h(ctx);
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function runDeniedHooks(hooks, ctx) {
|
|
638
|
+
if (!hooks || hooks.length === 0) return;
|
|
639
|
+
for (const h of hooks) {
|
|
640
|
+
try {
|
|
641
|
+
await h(ctx);
|
|
642
|
+
} catch {
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function createHookedExecute(fn, tool, contract, options) {
|
|
603
647
|
return async (args, _ctx) => {
|
|
648
|
+
const invocationId = genInvocationId();
|
|
649
|
+
const metadata = {};
|
|
650
|
+
const signal = tool.__activeSignal ?? createNeverAbortedSignal();
|
|
651
|
+
const merged = options.getHooks();
|
|
652
|
+
let currentInput = args;
|
|
653
|
+
const base = { invocationId, tool, contract, signal, metadata };
|
|
654
|
+
const beforeHooks = merged.before ?? [];
|
|
655
|
+
for (const hook of beforeHooks) {
|
|
656
|
+
if (signal.aborted) {
|
|
657
|
+
const abortErr = new DOMException("Aborted", "AbortError");
|
|
658
|
+
await runErrorHooks(merged.error, {
|
|
659
|
+
...base,
|
|
660
|
+
input: currentInput,
|
|
661
|
+
error: abortErr
|
|
662
|
+
});
|
|
663
|
+
return normalizeError(abortErr);
|
|
664
|
+
}
|
|
665
|
+
let res;
|
|
666
|
+
try {
|
|
667
|
+
const ctx = { ...base, input: currentInput };
|
|
668
|
+
res = await hook(ctx);
|
|
669
|
+
} catch (hookErr) {
|
|
670
|
+
await runErrorHooks(merged.error, {
|
|
671
|
+
...base,
|
|
672
|
+
input: currentInput,
|
|
673
|
+
error: hookErr
|
|
674
|
+
});
|
|
675
|
+
return normalizeError(hookErr);
|
|
676
|
+
}
|
|
677
|
+
if (res != null && typeof res === "object") {
|
|
678
|
+
if (isDenyResult(res)) {
|
|
679
|
+
const reason = res.message;
|
|
680
|
+
const code = res.code;
|
|
681
|
+
await runDeniedHooks(merged.denied, {
|
|
682
|
+
...base,
|
|
683
|
+
input: currentInput,
|
|
684
|
+
reason,
|
|
685
|
+
code
|
|
686
|
+
});
|
|
687
|
+
const msg = reason ?? "Tool execution denied";
|
|
688
|
+
return {
|
|
689
|
+
content: [{ type: "text", text: `Denied: ${msg}` }],
|
|
690
|
+
isError: true,
|
|
691
|
+
...code ? { code } : {}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if ("input" in res && res.input !== void 0) {
|
|
695
|
+
currentInput = res.input;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (signal.aborted) {
|
|
700
|
+
const abortErr = new DOMException("Aborted", "AbortError");
|
|
701
|
+
await runErrorHooks(merged.error, {
|
|
702
|
+
...base,
|
|
703
|
+
input: currentInput,
|
|
704
|
+
error: abortErr
|
|
705
|
+
});
|
|
706
|
+
return normalizeError(abortErr);
|
|
707
|
+
}
|
|
708
|
+
if (options.validate) {
|
|
709
|
+
try {
|
|
710
|
+
options.validate(currentInput);
|
|
711
|
+
} catch (valErr) {
|
|
712
|
+
await runErrorHooks(merged.error, {
|
|
713
|
+
...base,
|
|
714
|
+
input: currentInput,
|
|
715
|
+
error: valErr
|
|
716
|
+
});
|
|
717
|
+
return normalizeError(valErr);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
let rawOutput;
|
|
604
721
|
try {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
722
|
+
rawOutput = await fn(currentInput);
|
|
723
|
+
} catch (fnErr) {
|
|
724
|
+
await runErrorHooks(merged.error, {
|
|
725
|
+
...base,
|
|
726
|
+
input: currentInput,
|
|
727
|
+
error: fnErr
|
|
728
|
+
});
|
|
729
|
+
return normalizeError(fnErr);
|
|
730
|
+
}
|
|
731
|
+
let currentOutput = rawOutput;
|
|
732
|
+
const afterHooks = merged.after ?? [];
|
|
733
|
+
for (const hook of afterHooks) {
|
|
734
|
+
if (signal.aborted) ;
|
|
735
|
+
try {
|
|
736
|
+
const ctx = {
|
|
737
|
+
...base,
|
|
738
|
+
input: currentInput,
|
|
739
|
+
output: currentOutput
|
|
740
|
+
};
|
|
741
|
+
const res = await hook(ctx);
|
|
742
|
+
if (res != null && typeof res === "object" && "output" in res && res.output !== void 0) {
|
|
743
|
+
currentOutput = res.output;
|
|
744
|
+
}
|
|
745
|
+
} catch (hookErr) {
|
|
746
|
+
await runErrorHooks(merged.error, {
|
|
747
|
+
...base,
|
|
748
|
+
input: currentInput,
|
|
749
|
+
error: hookErr
|
|
750
|
+
});
|
|
751
|
+
return normalizeError(hookErr);
|
|
609
752
|
}
|
|
610
|
-
return normalizeResult(result);
|
|
611
|
-
} catch (err) {
|
|
612
|
-
return normalizeError(err);
|
|
613
753
|
}
|
|
754
|
+
return normalizeResult(currentOutput);
|
|
614
755
|
};
|
|
615
756
|
}
|
|
757
|
+
function createNeverAbortedSignal() {
|
|
758
|
+
try {
|
|
759
|
+
return new AbortController().signal;
|
|
760
|
+
} catch {
|
|
761
|
+
return {
|
|
762
|
+
aborted: false,
|
|
763
|
+
addEventListener() {
|
|
764
|
+
},
|
|
765
|
+
removeEventListener() {
|
|
766
|
+
},
|
|
767
|
+
dispatchEvent() {
|
|
768
|
+
return false;
|
|
769
|
+
},
|
|
770
|
+
onabort: null,
|
|
771
|
+
reason: void 0,
|
|
772
|
+
throwIfAborted() {
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/hooks/config.ts
|
|
779
|
+
var GLOBAL_KEY = "__simpleWebmcp_hooks";
|
|
780
|
+
function getStore() {
|
|
781
|
+
const g = globalThis;
|
|
782
|
+
if (!g[GLOBAL_KEY]) {
|
|
783
|
+
g[GLOBAL_KEY] = { hooks: {} };
|
|
784
|
+
}
|
|
785
|
+
return g[GLOBAL_KEY];
|
|
786
|
+
}
|
|
787
|
+
function getGlobalHooks() {
|
|
788
|
+
return getStore().hooks ?? {};
|
|
789
|
+
}
|
|
790
|
+
function configureWebMCP(opts) {
|
|
791
|
+
const store = getStore();
|
|
792
|
+
if (!opts.hooks) return;
|
|
793
|
+
if (opts.replace) {
|
|
794
|
+
store.hooks = normalizeHooks(opts.hooks);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
store.hooks = mergeHooks(store.hooks, opts.hooks);
|
|
798
|
+
}
|
|
799
|
+
function resetGlobalHooks() {
|
|
800
|
+
const store = getStore();
|
|
801
|
+
store.hooks = {};
|
|
802
|
+
}
|
|
803
|
+
function normalizeHooks(hooks) {
|
|
804
|
+
return {
|
|
805
|
+
before: hooks.before ? [...hooks.before] : void 0,
|
|
806
|
+
after: hooks.after ? [...hooks.after] : void 0,
|
|
807
|
+
error: hooks.error ? [...hooks.error] : void 0,
|
|
808
|
+
denied: hooks.denied ? [...hooks.denied] : void 0
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function mergeHooks(a, b) {
|
|
812
|
+
if (!a && !b) return {};
|
|
813
|
+
if (!a) return normalizeHooks(b);
|
|
814
|
+
if (!b) return a;
|
|
815
|
+
return {
|
|
816
|
+
before: [...a.before ?? [], ...b.before ?? []],
|
|
817
|
+
after: [...a.after ?? [], ...b.after ?? []],
|
|
818
|
+
error: [...a.error ?? [], ...b.error ?? []],
|
|
819
|
+
denied: [...a.denied ?? [], ...b.denied ?? []]
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
function mergeHooksOrdered(opts) {
|
|
823
|
+
const globalHooks = opts.globalHooks ?? {};
|
|
824
|
+
const scopedHooks = opts.scopedHooks ?? {};
|
|
825
|
+
const toolHooks = opts.toolHooks ?? {};
|
|
826
|
+
const before = [
|
|
827
|
+
...globalHooks.before ?? [],
|
|
828
|
+
...scopedHooks.before ?? [],
|
|
829
|
+
...toolHooks.before ?? []
|
|
830
|
+
];
|
|
831
|
+
const after = [
|
|
832
|
+
...toolHooks.after ?? [],
|
|
833
|
+
...scopedHooks.after ?? [],
|
|
834
|
+
...globalHooks.after ?? []
|
|
835
|
+
];
|
|
836
|
+
const error = [
|
|
837
|
+
...toolHooks.error ?? [],
|
|
838
|
+
...scopedHooks.error ?? [],
|
|
839
|
+
...globalHooks.error ?? []
|
|
840
|
+
];
|
|
841
|
+
const denied = [
|
|
842
|
+
...toolHooks.denied ?? [],
|
|
843
|
+
...scopedHooks.denied ?? [],
|
|
844
|
+
...globalHooks.denied ?? []
|
|
845
|
+
];
|
|
846
|
+
const out = {};
|
|
847
|
+
if (before.length) out.before = before;
|
|
848
|
+
if (after.length) out.after = after;
|
|
849
|
+
if (error.length) out.error = error;
|
|
850
|
+
if (denied.length) out.denied = denied;
|
|
851
|
+
return out;
|
|
852
|
+
}
|
|
616
853
|
|
|
617
854
|
// src/webmcp.ts
|
|
618
855
|
function resolveScope(opts) {
|
|
@@ -627,8 +864,24 @@ function webmcp(fn, options) {
|
|
|
627
864
|
const anyFn = fn;
|
|
628
865
|
if (anyFn.__webmcpBrand === true && anyFn.definition) {
|
|
629
866
|
if (!options || Object.keys(options).length === 0) return anyFn;
|
|
867
|
+
const prevOpts = anyFn.__webmcpOptions || {};
|
|
868
|
+
const mergedOpts = { ...prevOpts, ...options };
|
|
869
|
+
if (prevOpts.hooks || options?.hooks) {
|
|
870
|
+
const prevHooks = prevOpts.hooks ?? {};
|
|
871
|
+
const nextHooks = options.hooks ?? {};
|
|
872
|
+
mergedOpts.hooks = {
|
|
873
|
+
before: [...prevHooks.before ?? [], ...nextHooks.before ?? []],
|
|
874
|
+
after: [...prevHooks.after ?? [], ...nextHooks.after ?? []],
|
|
875
|
+
error: [...prevHooks.error ?? [], ...nextHooks.error ?? []],
|
|
876
|
+
denied: [...prevHooks.denied ?? [], ...nextHooks.denied ?? []]
|
|
877
|
+
};
|
|
878
|
+
for (const k of ["before", "after", "error", "denied"]) {
|
|
879
|
+
if (mergedOpts.hooks[k]?.length === 0) delete mergedOpts.hooks[k];
|
|
880
|
+
}
|
|
881
|
+
if (mergedOpts.hooks && Object.keys(mergedOpts.hooks).length === 0) delete mergedOpts.hooks;
|
|
882
|
+
}
|
|
630
883
|
const original = anyFn.__fn ?? fn;
|
|
631
|
-
return webmcp(original,
|
|
884
|
+
return webmcp(original, mergedOpts);
|
|
632
885
|
}
|
|
633
886
|
const name = options?.name ?? toSnakeCase(getFunctionName(fn));
|
|
634
887
|
if (!name) throw new ConfigurationError('Tool name could not be inferred \u2014 pass {name:"my_tool"}');
|
|
@@ -681,15 +934,30 @@ function webmcp(fn, options) {
|
|
|
681
934
|
let registrationPromise = null;
|
|
682
935
|
let unregisterFn = null;
|
|
683
936
|
let activeController = null;
|
|
684
|
-
const
|
|
937
|
+
const toolHooks = options?.hooks ? { ...options.hooks } : void 0;
|
|
685
938
|
const toolWrapper = wrapper;
|
|
686
939
|
toolWrapper.__webmcpBrand = true;
|
|
687
940
|
toolWrapper.__fn = fn;
|
|
688
941
|
toolWrapper.__webmcpOptions = options;
|
|
689
942
|
if (standard) toolWrapper.__standardSchema = standard;
|
|
690
943
|
if (outNorm.standard) toolWrapper.__outputStandardSchema = outNorm.standard;
|
|
944
|
+
if (toolHooks) toolWrapper.__hooks = toolHooks;
|
|
691
945
|
toolWrapper.tool = contract;
|
|
692
946
|
toolWrapper.definition = contract;
|
|
947
|
+
const hookedExec = createHookedExecute(fn, toolWrapper, contract, {
|
|
948
|
+
getHooks: () => {
|
|
949
|
+
const globalHooks = getGlobalHooks();
|
|
950
|
+
const scopeHooks = toolWrapper.__scopeHooks;
|
|
951
|
+
return mergeHooksOrdered({ globalHooks, scopedHooks: scopeHooks, toolHooks });
|
|
952
|
+
},
|
|
953
|
+
validate: standard ? (input) => {
|
|
954
|
+
const res = standard["~standard"].validate(input);
|
|
955
|
+
if ("issues" in res && res.issues && res.issues.length > 0) {
|
|
956
|
+
const msg = res.issues.map((i) => i.message).join("; ");
|
|
957
|
+
throw new ValidationError(`Validation failed: ${msg}`);
|
|
958
|
+
}
|
|
959
|
+
} : void 0
|
|
960
|
+
});
|
|
693
961
|
Object.defineProperty(toolWrapper, "status", {
|
|
694
962
|
get() {
|
|
695
963
|
return status;
|
|
@@ -722,11 +990,16 @@ function webmcp(fn, options) {
|
|
|
722
990
|
status = "registering";
|
|
723
991
|
const controller = new AbortController();
|
|
724
992
|
activeController = controller;
|
|
993
|
+
toolWrapper.__activeSignal = controller.signal;
|
|
725
994
|
if (opts?.signal) {
|
|
726
995
|
if (opts.signal.aborted) {
|
|
727
996
|
controller.abort();
|
|
728
997
|
status = "unregistered";
|
|
729
998
|
activeController = null;
|
|
999
|
+
try {
|
|
1000
|
+
delete toolWrapper.__activeSignal;
|
|
1001
|
+
} catch {
|
|
1002
|
+
}
|
|
730
1003
|
return () => {
|
|
731
1004
|
};
|
|
732
1005
|
}
|
|
@@ -741,11 +1014,15 @@ function webmcp(fn, options) {
|
|
|
741
1014
|
controller.signal.addEventListener("abort", () => {
|
|
742
1015
|
if (status !== "unregistered") status = "unregistered";
|
|
743
1016
|
activeController = null;
|
|
1017
|
+
try {
|
|
1018
|
+
delete toolWrapper.__activeSignal;
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
744
1021
|
}, { once: true });
|
|
745
1022
|
try {
|
|
746
1023
|
const unregister = await registry.register(contract, {
|
|
747
1024
|
signal: controller.signal,
|
|
748
|
-
execute:
|
|
1025
|
+
execute: hookedExec
|
|
749
1026
|
});
|
|
750
1027
|
unregisterFn = unregister;
|
|
751
1028
|
registrationPromise = Promise.resolve();
|
|
@@ -763,6 +1040,10 @@ function webmcp(fn, options) {
|
|
|
763
1040
|
}
|
|
764
1041
|
status = "unregistered";
|
|
765
1042
|
activeController = null;
|
|
1043
|
+
try {
|
|
1044
|
+
delete toolWrapper.__activeSignal;
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
766
1047
|
unregisterFn = null;
|
|
767
1048
|
};
|
|
768
1049
|
} catch (err) {
|
|
@@ -771,6 +1052,11 @@ function webmcp(fn, options) {
|
|
|
771
1052
|
} else {
|
|
772
1053
|
status = "error";
|
|
773
1054
|
}
|
|
1055
|
+
activeController = null;
|
|
1056
|
+
try {
|
|
1057
|
+
delete toolWrapper.__activeSignal;
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
774
1060
|
registrationPromise = Promise.reject(err);
|
|
775
1061
|
if (err instanceof SimpleWebMCPError) throw err;
|
|
776
1062
|
throw err;
|
|
@@ -789,6 +1075,10 @@ function webmcp(fn, options) {
|
|
|
789
1075
|
}
|
|
790
1076
|
status = "unregistered";
|
|
791
1077
|
activeController = null;
|
|
1078
|
+
try {
|
|
1079
|
+
delete toolWrapper.__activeSignal;
|
|
1080
|
+
} catch {
|
|
1081
|
+
}
|
|
792
1082
|
unregisterFn = null;
|
|
793
1083
|
registrationPromise = null;
|
|
794
1084
|
};
|
|
@@ -807,9 +1097,30 @@ function webmcp(fn, options) {
|
|
|
807
1097
|
webmcp.global = function global(fn, opts) {
|
|
808
1098
|
return webmcp(fn, { ...opts, global: true });
|
|
809
1099
|
};
|
|
1100
|
+
webmcp.configure = configureWebMCP;
|
|
1101
|
+
webmcp.getGlobalHooks = getGlobalHooks;
|
|
1102
|
+
webmcp.resetGlobalHooks = resetGlobalHooks;
|
|
810
1103
|
webmcp.isWebMCPTool = function isWebMCPTool(v) {
|
|
811
1104
|
return !!v?.__webmcpBrand;
|
|
812
1105
|
};
|
|
1106
|
+
var WebMCPHooksContext = react.createContext({});
|
|
1107
|
+
function WebMCPProvider({ hooks, children }) {
|
|
1108
|
+
const parent = react.useContext(WebMCPHooksContext);
|
|
1109
|
+
const merged = react.useMemo(() => {
|
|
1110
|
+
if (!hooks) return parent;
|
|
1111
|
+
if (!parent || Object.keys(parent).length === 0) return hooks;
|
|
1112
|
+
return {
|
|
1113
|
+
before: [...parent.before ?? [], ...hooks.before ?? []],
|
|
1114
|
+
after: [...parent.after ?? [], ...hooks.after ?? []],
|
|
1115
|
+
error: [...parent.error ?? [], ...hooks.error ?? []],
|
|
1116
|
+
denied: [...parent.denied ?? [], ...hooks.denied ?? []]
|
|
1117
|
+
};
|
|
1118
|
+
}, [parent, hooks]);
|
|
1119
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WebMCPHooksContext.Provider, { value: merged, children });
|
|
1120
|
+
}
|
|
1121
|
+
function useWebMCPHooksContext() {
|
|
1122
|
+
return react.useContext(WebMCPHooksContext);
|
|
1123
|
+
}
|
|
813
1124
|
|
|
814
1125
|
// src/react/useWebMCP.ts
|
|
815
1126
|
function isTool(v) {
|
|
@@ -817,6 +1128,7 @@ function isTool(v) {
|
|
|
817
1128
|
}
|
|
818
1129
|
function useWebMCP(arg, opts) {
|
|
819
1130
|
const enabled = opts?.enabled ?? true;
|
|
1131
|
+
const scopedHooks = useWebMCPHooksContext();
|
|
820
1132
|
const isRawFunction = react.useMemo(() => {
|
|
821
1133
|
return typeof arg === "function" && !isTool(arg);
|
|
822
1134
|
}, [arg]);
|
|
@@ -838,6 +1150,7 @@ function useWebMCP(arg, opts) {
|
|
|
838
1150
|
opts?.global,
|
|
839
1151
|
opts?.strict,
|
|
840
1152
|
opts?.outputSchema,
|
|
1153
|
+
opts?.hooks,
|
|
841
1154
|
opts?.enabled
|
|
842
1155
|
]);
|
|
843
1156
|
const [registered, setRegistered] = react.useState(false);
|
|
@@ -850,6 +1163,31 @@ function useWebMCP(arg, opts) {
|
|
|
850
1163
|
}, [supported]);
|
|
851
1164
|
const toolRef = react.useRef(tool);
|
|
852
1165
|
toolRef.current = tool;
|
|
1166
|
+
react.useEffect(() => {
|
|
1167
|
+
const t = toolRef.current;
|
|
1168
|
+
const hasScoped = scopedHooks && Object.keys(scopedHooks).length > 0;
|
|
1169
|
+
if (hasScoped) t.__scopeHooks = scopedHooks;
|
|
1170
|
+
else if (t.__scopeHooks) delete t.__scopeHooks;
|
|
1171
|
+
return () => {
|
|
1172
|
+
const cur = t.__scopeHooks;
|
|
1173
|
+
if (cur === scopedHooks) {
|
|
1174
|
+
try {
|
|
1175
|
+
delete t.__scopeHooks;
|
|
1176
|
+
} catch {
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
}, [scopedHooks, tool]);
|
|
1181
|
+
if (scopedHooks && Object.keys(scopedHooks).length > 0) {
|
|
1182
|
+
tool.__scopeHooks = scopedHooks;
|
|
1183
|
+
} else if (tool.__scopeHooks) {
|
|
1184
|
+
if (!(scopedHooks && Object.keys(scopedHooks).length > 0)) {
|
|
1185
|
+
try {
|
|
1186
|
+
delete tool.__scopeHooks;
|
|
1187
|
+
} catch {
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
853
1191
|
react.useEffect(() => {
|
|
854
1192
|
if (!enabled) {
|
|
855
1193
|
setRegistered(false);
|
|
@@ -941,8 +1279,11 @@ function Scope({ tools, enabled = true, children }) {
|
|
|
941
1279
|
var WebMCPScope = Scope;
|
|
942
1280
|
|
|
943
1281
|
exports.Scope = Scope;
|
|
1282
|
+
exports.WebMCPHooksContext = WebMCPHooksContext;
|
|
1283
|
+
exports.WebMCPProvider = WebMCPProvider;
|
|
944
1284
|
exports.WebMCPScope = WebMCPScope;
|
|
945
1285
|
exports.useTool = useTool;
|
|
946
1286
|
exports.useWebMCP = useWebMCP;
|
|
1287
|
+
exports.useWebMCPHooksContext = useWebMCPHooksContext;
|
|
947
1288
|
//# sourceMappingURL=react.cjs.map
|
|
948
1289
|
//# sourceMappingURL=react.cjs.map
|