@mcp-elements/vue 0.1.0 → 0.1.2

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/dist/index.js CHANGED
@@ -325,10 +325,18 @@ var McpeDialog = defineComponent8({
325
325
  const close = () => emit("update:modelValue", false);
326
326
  let unlockScroll = null;
327
327
  const lockScroll = () => {
328
- const prev = document.body.style.overflow;
329
- document.body.style.overflow = "hidden";
328
+ const body = document.body;
329
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
330
+ const prevOverflow = body.style.overflow;
331
+ const prevPaddingRight = body.style.paddingRight;
332
+ body.style.overflow = "hidden";
333
+ if (scrollbarWidth > 0) {
334
+ const currentPad = parseFloat(getComputedStyle(body).paddingRight) || 0;
335
+ body.style.paddingRight = `${currentPad + scrollbarWidth}px`;
336
+ }
330
337
  return () => {
331
- document.body.style.overflow = prev;
338
+ body.style.overflow = prevOverflow;
339
+ body.style.paddingRight = prevPaddingRight;
332
340
  };
333
341
  };
334
342
  watch(
@@ -362,48 +370,53 @@ var McpeDialog = defineComponent8({
362
370
  return () => {
363
371
  if (!props.modelValue) return h8("span", { style: "display:none" });
364
372
  return h8(Teleport, { to: "body" }, [
365
- h8("div", {
366
- class: "mcpe-dialog-overlay",
367
- onClick: close
368
- }),
369
373
  h8(
370
374
  "div",
371
375
  {
372
- class: cn8("mcpe-dialog-content", props.class),
373
- role: "dialog",
374
- "aria-modal": props.modal,
375
- onClick: (e) => e.stopPropagation()
376
+ class: "mcpe-dialog-overlay",
377
+ onClick: close
376
378
  },
377
379
  [
378
- slots.default?.(),
379
380
  h8(
380
- "button",
381
+ "div",
381
382
  {
382
- class: "mcpe-dialog-close",
383
- "aria-label": "Close",
384
- type: "button",
385
- onClick: close
383
+ class: cn8("mcpe-dialog-content", props.class),
384
+ role: "dialog",
385
+ "aria-modal": props.modal,
386
+ onClick: (e) => e.stopPropagation()
386
387
  },
387
388
  [
389
+ slots.default?.(),
388
390
  h8(
389
- "svg",
391
+ "button",
390
392
  {
391
- xmlns: "http://www.w3.org/2000/svg",
392
- width: "15",
393
- height: "15",
394
- viewBox: "0 0 24 24",
395
- fill: "none",
396
- stroke: "currentColor",
397
- "stroke-width": "2",
398
- "stroke-linecap": "round",
399
- "stroke-linejoin": "round"
393
+ class: "mcpe-dialog-close",
394
+ "aria-label": "Close",
395
+ type: "button",
396
+ onClick: close
400
397
  },
401
398
  [
402
- h8("path", { d: "M18 6 6 18" }),
403
- h8("path", { d: "m6 6 12 12" })
399
+ h8(
400
+ "svg",
401
+ {
402
+ xmlns: "http://www.w3.org/2000/svg",
403
+ width: "16",
404
+ height: "16",
405
+ viewBox: "0 0 24 24",
406
+ fill: "none",
407
+ stroke: "currentColor",
408
+ "stroke-width": "2",
409
+ "stroke-linecap": "round",
410
+ "stroke-linejoin": "round",
411
+ "aria-hidden": "true"
412
+ },
413
+ [
414
+ h8("path", { d: "M18 6 6 18" }),
415
+ h8("path", { d: "m6 6 12 12" })
416
+ ]
417
+ )
404
418
  ]
405
- ),
406
- h8("span", { class: "sr-only" }, "Close")
419
+ )
407
420
  ]
408
421
  )
409
422
  ]
@@ -497,6 +510,783 @@ var McpeTabs = defineComponent10({
497
510
  };
498
511
  }
499
512
  });
513
+
514
+ // src/mcp/mcp-server-status.ts
515
+ import { defineComponent as defineComponent11, h as h11, computed as computed9 } from "vue";
516
+ import { cn as cn11 } from "@mcp-elements/core";
517
+ var STATUS_LABELS = {
518
+ connected: "Connected",
519
+ connecting: "Connecting",
520
+ disconnected: "Disconnected",
521
+ error: "Error"
522
+ };
523
+ var McpeMcpServerStatus = defineComponent11({
524
+ name: "McpeMcpServerStatus",
525
+ props: {
526
+ status: { type: String, required: true },
527
+ serverName: { type: String, default: "" },
528
+ class: { type: String, default: "" }
529
+ },
530
+ setup(props) {
531
+ const label = computed9(
532
+ () => (props.serverName ? `${props.serverName} \xB7 ` : "") + STATUS_LABELS[props.status]
533
+ );
534
+ const ariaLabel = computed9(
535
+ () => (props.serverName ? `${props.serverName}: ` : "") + STATUS_LABELS[props.status]
536
+ );
537
+ return () => h11(
538
+ "span",
539
+ {
540
+ class: cn11(
541
+ "mcpe-mcp-server-status",
542
+ `mcpe-mcp-server-status-${props.status}`,
543
+ props.class
544
+ ),
545
+ role: "status",
546
+ "aria-live": "polite",
547
+ "aria-label": ariaLabel.value
548
+ },
549
+ [h11("span", { class: "mcpe-mcp-server-status-dot", "aria-hidden": "true" }), label.value]
550
+ );
551
+ }
552
+ });
553
+
554
+ // src/mcp/mcp-tool-call.ts
555
+ import { defineComponent as defineComponent12, h as h12, ref, computed as computed10, watch as watch2, onUnmounted as onUnmounted2 } from "vue";
556
+ import { cn as cn12 } from "@mcp-elements/core";
557
+ var STATUS_LABELS2 = {
558
+ idle: "idle",
559
+ pending: "pending",
560
+ running: "running",
561
+ done: "done",
562
+ error: "error",
563
+ cancelled: "cancelled"
564
+ };
565
+ var McpeMcpToolCall = defineComponent12({
566
+ name: "McpeMcpToolCall",
567
+ props: {
568
+ state: { type: Object, required: true },
569
+ toolName: { type: String, default: "" },
570
+ args: { type: Object, default: void 0 },
571
+ class: { type: String, default: "" }
572
+ },
573
+ emits: ["retry"],
574
+ setup(props, { emit }) {
575
+ const snap = ref({ status: "idle" });
576
+ let unsub;
577
+ watch2(
578
+ () => props.state,
579
+ (state) => {
580
+ unsub?.();
581
+ snap.value = {
582
+ status: state.status,
583
+ tool: state.tool,
584
+ args: state.args,
585
+ result: state.result,
586
+ error: state.error,
587
+ startedAt: state.startedAt,
588
+ endedAt: state.endedAt
589
+ };
590
+ unsub = state.subscribe((s) => {
591
+ snap.value = { ...s };
592
+ });
593
+ },
594
+ { immediate: true }
595
+ );
596
+ onUnmounted2(() => unsub?.());
597
+ const displayName = computed10(() => snap.value.tool ?? props.toolName ?? "unknown");
598
+ const displayArgs = computed10(() => snap.value.args ?? props.args);
599
+ const textBlocks = computed10(
600
+ () => (snap.value.result?.content ?? []).filter((c) => c.type === "text")
601
+ );
602
+ return () => {
603
+ const status = snap.value.status;
604
+ const children = [];
605
+ children.push(
606
+ h12("div", { class: "mcpe-mcp-tool-call-header" }, [
607
+ h12("div", { class: "mcpe-mcp-tool-call-name" }, [
608
+ h12("span", { class: "mcpe-mcp-tool-call-icon", "aria-hidden": "true" }, "fn"),
609
+ h12("span", { class: "mcpe-mcp-tool-call-title" }, displayName.value)
610
+ ]),
611
+ h12(
612
+ "span",
613
+ { class: cn12("mcpe-mcp-tool-call-badge", `mcpe-mcp-tool-call-badge-${status}`) },
614
+ [
615
+ status === "running" ? h12(
616
+ "svg",
617
+ {
618
+ class: "animate-spin h-3 w-3",
619
+ fill: "none",
620
+ viewBox: "0 0 24 24",
621
+ "aria-hidden": "true"
622
+ },
623
+ [
624
+ h12("circle", {
625
+ class: "opacity-25",
626
+ cx: "12",
627
+ cy: "12",
628
+ r: "10",
629
+ stroke: "currentColor",
630
+ "stroke-width": "4"
631
+ }),
632
+ h12("path", {
633
+ class: "opacity-75",
634
+ fill: "currentColor",
635
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
636
+ })
637
+ ]
638
+ ) : null,
639
+ STATUS_LABELS2[status]
640
+ ]
641
+ )
642
+ ])
643
+ );
644
+ if (displayArgs.value) {
645
+ children.push(
646
+ h12("pre", { class: "mcpe-mcp-tool-call-args" }, JSON.stringify(displayArgs.value, null, 2))
647
+ );
648
+ }
649
+ if (status === "running") {
650
+ children.push(
651
+ h12(
652
+ "div",
653
+ {
654
+ class: "mcpe-mcp-tool-call-progress",
655
+ role: "progressbar",
656
+ "aria-label": "Tool running"
657
+ },
658
+ [h12("div", { class: "mcpe-mcp-tool-call-progress-bar", style: "width: 60%" })]
659
+ )
660
+ );
661
+ }
662
+ if (status === "done" && snap.value.result) {
663
+ children.push(
664
+ h12(
665
+ "div",
666
+ { class: "mcpe-mcp-tool-call-result mcpe-mcp-tool-call-result-done" },
667
+ textBlocks.value.map((b) => h12("p", { class: "whitespace-pre-wrap text-sm" }, b.text))
668
+ )
669
+ );
670
+ }
671
+ if (status === "error" && snap.value.error) {
672
+ children.push(
673
+ h12("div", { class: "mcpe-mcp-tool-call-result mcpe-mcp-tool-call-result-error" }, [
674
+ h12("p", { class: "text-sm" }, snap.value.error.message),
675
+ h12(
676
+ "button",
677
+ {
678
+ class: "text-xs underline underline-offset-2",
679
+ type: "button",
680
+ onClick: () => emit("retry")
681
+ },
682
+ "Retry"
683
+ )
684
+ ])
685
+ );
686
+ }
687
+ return h12("div", { class: cn12("mcpe-mcp-tool-call", props.class) }, children);
688
+ };
689
+ }
690
+ });
691
+
692
+ // src/mcp/mcp-tool-form.ts
693
+ import { defineComponent as defineComponent13, h as h13, reactive, computed as computed11, watch as watch3 } from "vue";
694
+ import { cn as cn13, schemaToFields } from "@mcp-elements/core";
695
+ var McpeMcpToolForm = defineComponent13({
696
+ name: "McpeMcpToolForm",
697
+ props: {
698
+ schema: { type: Object, required: true },
699
+ loading: { type: Boolean, default: false },
700
+ submitLabel: { type: String, default: "Run" },
701
+ class: { type: String, default: "" }
702
+ },
703
+ emits: ["submit"],
704
+ setup(props, { emit }) {
705
+ const fields = computed11(() => schemaToFields(props.schema));
706
+ const values = reactive({});
707
+ watch3(
708
+ fields,
709
+ (fs) => {
710
+ for (const k of Object.keys(values)) delete values[k];
711
+ for (const f of fs) {
712
+ if (f.defaultValue !== void 0) values[f.key] = f.defaultValue;
713
+ }
714
+ },
715
+ { immediate: true }
716
+ );
717
+ const getStr = (key) => {
718
+ const v = values[key];
719
+ return v == null ? "" : String(v);
720
+ };
721
+ const getBool = (key) => Boolean(values[key]);
722
+ const inputType = (f) => f.kind === "email" ? "email" : f.kind === "url" ? "url" : f.kind === "date" ? "date" : "text";
723
+ const handleSubmit = (e) => {
724
+ e.preventDefault();
725
+ emit("submit", { ...values });
726
+ };
727
+ const submitButton = () => h13("div", { class: "mcpe-mcp-tool-form-submit" }, [
728
+ h13(
729
+ "button",
730
+ {
731
+ type: "submit",
732
+ class: "mcpe-btn mcpe-btn-primary mcpe-btn-sm",
733
+ disabled: props.loading
734
+ },
735
+ props.loading ? "Running\u2026" : props.submitLabel
736
+ )
737
+ ]);
738
+ const fieldInput = (field) => {
739
+ switch (field.kind) {
740
+ case "switch":
741
+ return h13("input", {
742
+ type: "checkbox",
743
+ id: field.key,
744
+ class: "mcpe-switch",
745
+ checked: getBool(field.key),
746
+ onChange: (e) => {
747
+ values[field.key] = e.target.checked;
748
+ }
749
+ });
750
+ case "select":
751
+ return h13(
752
+ "select",
753
+ {
754
+ id: field.key,
755
+ class: "mcpe-select",
756
+ value: getStr(field.key),
757
+ onChange: (e) => {
758
+ values[field.key] = e.target.value;
759
+ }
760
+ },
761
+ [
762
+ h13("option", { value: "" }, "Select\u2026"),
763
+ ...(field.options ?? []).map(
764
+ (opt) => h13("option", { value: opt.value }, opt.label)
765
+ )
766
+ ]
767
+ );
768
+ case "textarea":
769
+ return h13("textarea", {
770
+ id: field.key,
771
+ class: "mcpe-textarea",
772
+ rows: 4,
773
+ value: getStr(field.key),
774
+ onInput: (e) => {
775
+ values[field.key] = e.target.value;
776
+ }
777
+ });
778
+ case "number":
779
+ return h13("input", {
780
+ type: "number",
781
+ id: field.key,
782
+ class: "mcpe-input",
783
+ value: getStr(field.key),
784
+ onInput: (e) => {
785
+ const raw = e.target.value;
786
+ values[field.key] = raw === "" ? void 0 : Number(raw);
787
+ }
788
+ });
789
+ default:
790
+ return h13("input", {
791
+ type: inputType(field),
792
+ id: field.key,
793
+ class: "mcpe-input",
794
+ value: getStr(field.key),
795
+ onInput: (e) => {
796
+ values[field.key] = e.target.value;
797
+ }
798
+ });
799
+ }
800
+ };
801
+ return () => {
802
+ if (fields.value.length === 0) {
803
+ return h13("form", { class: cn13("mcpe-mcp-tool-form", props.class), onSubmit: handleSubmit }, [
804
+ h13("p", { class: "text-sm text-muted-foreground" }, "This tool takes no inputs."),
805
+ submitButton()
806
+ ]);
807
+ }
808
+ const fieldNodes = fields.value.map((field) => {
809
+ const children = [
810
+ h13(
811
+ "label",
812
+ {
813
+ for: field.key,
814
+ class: cn13(
815
+ "mcpe-mcp-tool-form-label",
816
+ field.required ? "mcpe-mcp-tool-form-label-required" : ""
817
+ )
818
+ },
819
+ field.label
820
+ ),
821
+ fieldInput(field)
822
+ ];
823
+ if (field.help) {
824
+ children.push(h13("p", { class: "mcpe-mcp-tool-form-help" }, field.help));
825
+ }
826
+ return h13("div", { class: "mcpe-mcp-tool-form-field" }, children);
827
+ });
828
+ return h13("form", { class: cn13("mcpe-mcp-tool-form", props.class), onSubmit: handleSubmit }, [
829
+ ...fieldNodes,
830
+ submitButton()
831
+ ]);
832
+ };
833
+ }
834
+ });
835
+
836
+ // src/mcp/mcp-consent-dialog.ts
837
+ import {
838
+ defineComponent as defineComponent14,
839
+ h as h14,
840
+ computed as computed12,
841
+ watch as watch4,
842
+ onMounted as onMounted2,
843
+ onUnmounted as onUnmounted3,
844
+ Teleport as Teleport2
845
+ } from "vue";
846
+ import { parseScopes } from "@mcp-elements/core";
847
+ var McpeMcpConsentDialog = defineComponent14({
848
+ name: "McpeMcpConsentDialog",
849
+ props: {
850
+ open: { type: Boolean, required: true },
851
+ serverName: { type: String, required: true },
852
+ serverIcon: { type: String, default: void 0 },
853
+ scopes: { type: Array, default: () => [] }
854
+ },
855
+ emits: ["approve", "deny"],
856
+ setup(props, { emit }) {
857
+ const parsedScopes = computed12(() => parseScopes(props.scopes.join(" ")));
858
+ const approve = () => emit("approve");
859
+ const deny = () => emit("deny");
860
+ let unlockScroll = null;
861
+ const lockScroll = () => {
862
+ const body = document.body;
863
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
864
+ const prevOverflow = body.style.overflow;
865
+ const prevPaddingRight = body.style.paddingRight;
866
+ body.style.overflow = "hidden";
867
+ if (scrollbarWidth > 0) {
868
+ const currentPad = parseFloat(getComputedStyle(body).paddingRight) || 0;
869
+ body.style.paddingRight = `${currentPad + scrollbarWidth}px`;
870
+ }
871
+ return () => {
872
+ body.style.overflow = prevOverflow;
873
+ body.style.paddingRight = prevPaddingRight;
874
+ };
875
+ };
876
+ watch4(
877
+ () => props.open,
878
+ (isOpen) => {
879
+ if (isOpen) {
880
+ unlockScroll = lockScroll();
881
+ } else if (unlockScroll) {
882
+ unlockScroll();
883
+ unlockScroll = null;
884
+ }
885
+ },
886
+ { immediate: true }
887
+ );
888
+ const handleKeydown = (e) => {
889
+ if (e.key === "Escape" && props.open) deny();
890
+ };
891
+ onMounted2(() => document.addEventListener("keydown", handleKeydown));
892
+ onUnmounted3(() => {
893
+ document.removeEventListener("keydown", handleKeydown);
894
+ if (unlockScroll) unlockScroll();
895
+ });
896
+ return () => {
897
+ if (!props.open) return h14("span", { style: "display:none" });
898
+ const iconChild = props.serverIcon ? h14("img", { src: props.serverIcon, alt: "" }) : props.serverName[0]?.toUpperCase() ?? "?";
899
+ return h14(Teleport2, { to: "body" }, [
900
+ h14(
901
+ "div",
902
+ { class: "mcpe-dialog-overlay", onClick: deny },
903
+ [
904
+ h14(
905
+ "div",
906
+ {
907
+ class: "mcpe-dialog-content",
908
+ role: "dialog",
909
+ "aria-modal": "true",
910
+ "aria-label": `Allow ${props.serverName}?`,
911
+ onClick: (e) => e.stopPropagation()
912
+ },
913
+ [
914
+ h14("div", { class: "mcpe-mcp-consent-dialog" }, [
915
+ h14("div", { class: "mcpe-dialog-header" }, [
916
+ h14("h2", { class: "mcpe-dialog-title" }, "Permission Request"),
917
+ h14(
918
+ "p",
919
+ { class: "mcpe-dialog-description" },
920
+ "Review and approve the permissions this server is requesting."
921
+ )
922
+ ]),
923
+ h14("div", { class: "mcpe-mcp-consent-dialog-server" }, [
924
+ h14("div", { class: "mcpe-mcp-consent-dialog-icon", "aria-hidden": "true" }, [iconChild]),
925
+ h14("div", { class: "mcpe-mcp-consent-dialog-server-text" }, [
926
+ h14("p", { class: "mcpe-mcp-consent-dialog-server-name" }, props.serverName),
927
+ h14("p", { class: "mcpe-mcp-consent-dialog-server-meta" }, "is requesting access to")
928
+ ])
929
+ ]),
930
+ h14(
931
+ "div",
932
+ {
933
+ class: "mcpe-mcp-consent-dialog-scopes",
934
+ role: "list",
935
+ "aria-label": "Requested permissions"
936
+ },
937
+ parsedScopes.value.map(
938
+ (s) => h14("div", { class: "mcpe-mcp-consent-dialog-scope-item", role: "listitem" }, [
939
+ h14("span", { class: "mcpe-mcp-consent-dialog-scope-resource" }, s.resource),
940
+ h14(
941
+ "div",
942
+ { class: "mcpe-mcp-consent-dialog-scope-perms" },
943
+ s.permissions.map(
944
+ (p) => h14(
945
+ "span",
946
+ {
947
+ class: "mcpe-mcp-consent-dialog-scope-perm",
948
+ "data-perm": p.toLowerCase()
949
+ },
950
+ p
951
+ )
952
+ )
953
+ )
954
+ ])
955
+ )
956
+ ),
957
+ h14("div", { class: "mcpe-mcp-consent-dialog-actions" }, [
958
+ h14(
959
+ "button",
960
+ { type: "button", class: "mcpe-btn mcpe-btn-outline", onClick: deny },
961
+ "Deny"
962
+ ),
963
+ h14(
964
+ "button",
965
+ { type: "button", class: "mcpe-btn mcpe-btn-primary", onClick: approve },
966
+ "Allow"
967
+ )
968
+ ])
969
+ ])
970
+ ]
971
+ )
972
+ ]
973
+ )
974
+ ]);
975
+ };
976
+ }
977
+ });
978
+
979
+ // src/mcp/mcp-scope-inspector.ts
980
+ import { defineComponent as defineComponent15, h as h15, ref as ref2, computed as computed13 } from "vue";
981
+ import { cn as cn14, parseScopes as parseScopes2 } from "@mcp-elements/core";
982
+ var McpeMcpScopeInspector = defineComponent15({
983
+ name: "McpeMcpScopeInspector",
984
+ props: {
985
+ scopes: {
986
+ type: [String, Array],
987
+ default: ""
988
+ },
989
+ descriptions: {
990
+ type: Object,
991
+ default: () => ({})
992
+ },
993
+ class: { type: String, default: "" }
994
+ },
995
+ setup(props) {
996
+ const parsed = computed13(
997
+ () => typeof props.scopes === "string" ? parseScopes2(props.scopes) : props.scopes
998
+ );
999
+ const openKeys = ref2(/* @__PURE__ */ new Set());
1000
+ const isOpen = (key) => openKeys.value.has(key);
1001
+ const toggle = (key) => {
1002
+ const next = new Set(openKeys.value);
1003
+ if (next.has(key)) next.delete(key);
1004
+ else next.add(key);
1005
+ openKeys.value = next;
1006
+ };
1007
+ const getDescription = (s) => props.descriptions[s.raw] ?? props.descriptions[s.resource] ?? s.description;
1008
+ return () => h15(
1009
+ "div",
1010
+ { class: cn14("mcpe-mcp-scope-inspector", props.class), role: "list" },
1011
+ parsed.value.map((s) => {
1012
+ const open = isOpen(s.raw);
1013
+ const desc = getDescription(s);
1014
+ const itemChildren = [
1015
+ h15(
1016
+ "button",
1017
+ {
1018
+ type: "button",
1019
+ class: "mcpe-mcp-scope-inspector-trigger",
1020
+ "aria-expanded": open,
1021
+ onClick: () => toggle(s.raw)
1022
+ },
1023
+ [
1024
+ h15("div", { class: "flex items-center gap-3" }, [
1025
+ h15("span", { class: "mcpe-mcp-scope-inspector-resource" }, s.resource),
1026
+ h15(
1027
+ "div",
1028
+ { class: "mcpe-mcp-scope-inspector-perms" },
1029
+ s.permissions.map(
1030
+ (p) => h15("span", { class: "mcpe-mcp-scope-inspector-perm", "data-perm": p.toLowerCase() }, p)
1031
+ )
1032
+ )
1033
+ ]),
1034
+ h15(
1035
+ "svg",
1036
+ {
1037
+ class: cn14(
1038
+ "mcpe-mcp-scope-inspector-chevron",
1039
+ open ? "mcpe-mcp-scope-inspector-chevron-open" : ""
1040
+ ),
1041
+ xmlns: "http://www.w3.org/2000/svg",
1042
+ viewBox: "0 0 24 24",
1043
+ fill: "none",
1044
+ stroke: "currentColor",
1045
+ "stroke-width": "2",
1046
+ "stroke-linecap": "round",
1047
+ "stroke-linejoin": "round",
1048
+ "aria-hidden": "true"
1049
+ },
1050
+ [h15("path", { d: "m6 9 6 6 6-6" })]
1051
+ )
1052
+ ]
1053
+ )
1054
+ ];
1055
+ if (open && desc) {
1056
+ itemChildren.push(
1057
+ h15("div", { role: "region", class: "mcpe-mcp-scope-inspector-body" }, desc)
1058
+ );
1059
+ }
1060
+ return h15(
1061
+ "div",
1062
+ { class: "mcpe-mcp-scope-inspector-item", role: "listitem" },
1063
+ itemChildren
1064
+ );
1065
+ })
1066
+ );
1067
+ }
1068
+ });
1069
+
1070
+ // src/mcp/mcp-resource-browser.ts
1071
+ import { defineComponent as defineComponent16, h as h16 } from "vue";
1072
+ import { cn as cn15 } from "@mcp-elements/core";
1073
+ function mimeTypeLabel(mimeType) {
1074
+ if (!mimeType) return "res";
1075
+ if (mimeType.includes("json")) return "json";
1076
+ if (mimeType.includes("text")) return "txt";
1077
+ if (mimeType.includes("image")) return "img";
1078
+ if (mimeType.includes("pdf")) return "pdf";
1079
+ return mimeType.split("/")[1]?.slice(0, 4) ?? "res";
1080
+ }
1081
+ var McpeMcpResourceBrowser = defineComponent16({
1082
+ name: "McpeMcpResourceBrowser",
1083
+ props: {
1084
+ resources: { type: Array, default: () => [] },
1085
+ selectedUri: { type: String, default: void 0 },
1086
+ loading: { type: Boolean, default: false },
1087
+ class: { type: String, default: "" }
1088
+ },
1089
+ emits: ["select"],
1090
+ setup(props, { emit }) {
1091
+ return () => {
1092
+ if (props.loading) {
1093
+ return h16(
1094
+ "div",
1095
+ { class: cn15("mcpe-mcp-resource-browser", props.class) },
1096
+ [1, 2, 3, 4].map(
1097
+ () => h16("div", { class: "flex items-center gap-3 px-3 py-2.5" }, [
1098
+ h16("div", { class: "h-8 w-8 rounded-md animate-pulse bg-muted" }),
1099
+ h16("div", { class: "h-4 flex-1 rounded animate-pulse bg-muted" })
1100
+ ])
1101
+ )
1102
+ );
1103
+ }
1104
+ if (props.resources.length === 0) {
1105
+ return h16("div", { class: cn15("mcpe-mcp-resource-browser", props.class) }, [
1106
+ h16("p", { class: "mcpe-mcp-resource-browser-empty" }, "No resources available")
1107
+ ]);
1108
+ }
1109
+ return h16(
1110
+ "div",
1111
+ { class: cn15("mcpe-mcp-resource-browser", props.class), role: "list" },
1112
+ props.resources.map((r) => {
1113
+ const selected = props.selectedUri === r.uri;
1114
+ const children = [
1115
+ h16(
1116
+ "span",
1117
+ { class: "mcpe-mcp-resource-browser-icon", "aria-hidden": "true" },
1118
+ mimeTypeLabel(r.mimeType)
1119
+ ),
1120
+ h16("span", { class: "mcpe-mcp-resource-browser-name" }, r.name)
1121
+ ];
1122
+ if (r.mimeType) {
1123
+ children.push(
1124
+ h16("span", { class: "mcpe-mcp-resource-browser-type" }, r.mimeType.split("/")[0])
1125
+ );
1126
+ }
1127
+ return h16(
1128
+ "button",
1129
+ {
1130
+ type: "button",
1131
+ role: "listitem",
1132
+ class: cn15(
1133
+ "mcpe-mcp-resource-browser-item w-full text-left",
1134
+ selected ? "mcpe-mcp-resource-browser-item-selected" : ""
1135
+ ),
1136
+ "aria-selected": selected,
1137
+ "aria-label": r.name,
1138
+ onClick: () => emit("select", r)
1139
+ },
1140
+ children
1141
+ );
1142
+ })
1143
+ );
1144
+ };
1145
+ }
1146
+ });
1147
+
1148
+ // src/mcp/mcp-app-frame.ts
1149
+ import { defineComponent as defineComponent17, h as h17, ref as ref3, watch as watch5, onUnmounted as onUnmounted4 } from "vue";
1150
+ import { cn as cn16, createAppBridge } from "@mcp-elements/core";
1151
+ var McpeMcpAppFrame = defineComponent17({
1152
+ name: "McpeMcpAppFrame",
1153
+ props: {
1154
+ src: { type: String, required: true },
1155
+ height: { type: Number, default: 480 },
1156
+ sandbox: { type: String, default: "allow-scripts allow-same-origin" },
1157
+ class: { type: String, default: "" }
1158
+ },
1159
+ emits: ["message"],
1160
+ setup(props, { emit }) {
1161
+ const frameRef = ref3(null);
1162
+ let bridge = null;
1163
+ let unsub = null;
1164
+ let messageHandler = null;
1165
+ const cleanup = () => {
1166
+ if (unsub) unsub();
1167
+ if (messageHandler) window.removeEventListener("message", messageHandler);
1168
+ unsub = null;
1169
+ messageHandler = null;
1170
+ bridge = null;
1171
+ };
1172
+ const setup = () => {
1173
+ cleanup();
1174
+ bridge = createAppBridge({
1175
+ postMessage: (env) => frameRef.value?.contentWindow?.postMessage(env, "*")
1176
+ });
1177
+ unsub = bridge.onMessage((env) => emit("message", env));
1178
+ messageHandler = (e) => bridge?.receive(e.data);
1179
+ window.addEventListener("message", messageHandler);
1180
+ };
1181
+ watch5([frameRef, () => props.src], () => {
1182
+ if (frameRef.value) setup();
1183
+ });
1184
+ onUnmounted4(cleanup);
1185
+ return () => h17("div", { class: cn16("mcpe-mcp-app-frame", props.class) }, [
1186
+ h17("iframe", {
1187
+ ref: frameRef,
1188
+ src: props.src,
1189
+ sandbox: props.sandbox,
1190
+ height: `${props.height}px`,
1191
+ title: "MCP App",
1192
+ "aria-label": "MCP App frame",
1193
+ style: "display:block;width:100%;border:none"
1194
+ })
1195
+ ]);
1196
+ }
1197
+ });
1198
+
1199
+ // src/composables/use-mcp-tool-state.ts
1200
+ import { ref as ref4, onUnmounted as onUnmounted5 } from "vue";
1201
+ import { createToolState } from "@mcp-elements/core";
1202
+ function useMcpToolState() {
1203
+ const api = createToolState();
1204
+ const snapshot = ref4({
1205
+ status: api.status,
1206
+ tool: api.tool,
1207
+ args: api.args,
1208
+ result: api.result,
1209
+ error: api.error,
1210
+ startedAt: api.startedAt,
1211
+ endedAt: api.endedAt
1212
+ });
1213
+ const unsub = api.subscribe((s) => {
1214
+ snapshot.value = { ...s };
1215
+ });
1216
+ onUnmounted5(unsub);
1217
+ return { api, snapshot };
1218
+ }
1219
+
1220
+ // src/composables/use-mcp-oauth.ts
1221
+ import { ref as ref5, onUnmounted as onUnmounted6 } from "vue";
1222
+ import { createOAuthFlow } from "@mcp-elements/core";
1223
+ function useMcpOAuth() {
1224
+ const api = createOAuthFlow();
1225
+ const snapshot = ref5({
1226
+ status: api.status,
1227
+ verifier: api.verifier,
1228
+ state: api.state,
1229
+ tokens: api.tokens,
1230
+ error: api.error
1231
+ });
1232
+ const unsub = api.subscribe((s) => {
1233
+ snapshot.value = { ...s };
1234
+ });
1235
+ onUnmounted6(unsub);
1236
+ return { api, snapshot };
1237
+ }
1238
+
1239
+ // src/composables/use-mcp-app-bridge.ts
1240
+ import { ref as ref6, watch as watch6, onUnmounted as onUnmounted7 } from "vue";
1241
+ import { createAppBridge as createAppBridge2 } from "@mcp-elements/core";
1242
+ function useMcpAppBridge(options = {}) {
1243
+ const frameRef = ref6(null);
1244
+ let bridge = null;
1245
+ let unsub = null;
1246
+ let messageHandler = null;
1247
+ const cleanup = () => {
1248
+ if (unsub) unsub();
1249
+ if (messageHandler) window.removeEventListener("message", messageHandler);
1250
+ unsub = null;
1251
+ messageHandler = null;
1252
+ bridge = null;
1253
+ };
1254
+ const establish = () => {
1255
+ cleanup();
1256
+ bridge = createAppBridge2({
1257
+ postMessage: (env) => frameRef.value?.contentWindow?.postMessage(env, "*")
1258
+ });
1259
+ if (options.onMessage) unsub = bridge.onMessage(options.onMessage);
1260
+ messageHandler = (e) => bridge?.receive(e.data);
1261
+ window.addEventListener("message", messageHandler);
1262
+ };
1263
+ watch6(frameRef, (el) => {
1264
+ if (el) establish();
1265
+ });
1266
+ onUnmounted7(cleanup);
1267
+ const send = (env) => bridge?.send(env);
1268
+ return { frameRef, send };
1269
+ }
1270
+
1271
+ // src/composables/use-mcp-schema-form.ts
1272
+ import { reactive as reactive2, computed as computed14, watch as watch7 } from "vue";
1273
+ import { schemaToFields as schemaToFields2 } from "@mcp-elements/core";
1274
+ function useMcpSchemaForm(schema) {
1275
+ const getSchema = typeof schema === "function" ? schema : () => schema;
1276
+ const fields = computed14(() => schemaToFields2(getSchema()));
1277
+ const values = reactive2({});
1278
+ const reset = () => {
1279
+ for (const k of Object.keys(values)) delete values[k];
1280
+ for (const f of fields.value) {
1281
+ if (f.defaultValue !== void 0) values[f.key] = f.defaultValue;
1282
+ }
1283
+ };
1284
+ watch7(fields, reset, { immediate: true });
1285
+ const setValue = (key, value) => {
1286
+ values[key] = value;
1287
+ };
1288
+ return { fields, values, setValue, reset };
1289
+ }
500
1290
  export {
501
1291
  McpeAlert,
502
1292
  McpeBadge,
@@ -509,9 +1299,20 @@ export {
509
1299
  McpeCardTitle,
510
1300
  McpeDialog,
511
1301
  McpeInput,
1302
+ McpeMcpAppFrame,
1303
+ McpeMcpConsentDialog,
1304
+ McpeMcpResourceBrowser,
1305
+ McpeMcpScopeInspector,
1306
+ McpeMcpServerStatus,
1307
+ McpeMcpToolCall,
1308
+ McpeMcpToolForm,
512
1309
  McpeSelect,
513
1310
  McpeSwitch,
514
1311
  McpeTabs,
515
- McpeTextarea
1312
+ McpeTextarea,
1313
+ useMcpAppBridge,
1314
+ useMcpOAuth,
1315
+ useMcpSchemaForm,
1316
+ useMcpToolState
516
1317
  };
517
1318
  //# sourceMappingURL=index.js.map