auto-webmcp 0.2.6 → 0.2.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAA8H,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAKD,iDAAiD;AACjD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAMxF;AAkaD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,KAAK,CAAC,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,EACzE,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GACrD,YAAY,CAKd"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAA8H,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAKD,iDAAiD;AACjD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAMxF;AAgcD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,KAAK,CAAC,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,EACzE,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GACrD,YAAY,CAKd"}
@@ -189,15 +189,55 @@ function buildStringSchema(input) {
189
189
  prop.maxLength = input.maxLength;
190
190
  if (input.pattern)
191
191
  prop.pattern = input.pattern;
192
+ const listId = input.getAttribute("list");
193
+ if (listId) {
194
+ const datalist = input.ownerDocument.getElementById(listId);
195
+ if (datalist instanceof HTMLDataListElement) {
196
+ const options = Array.from(datalist.options).filter(
197
+ (o) => !o.disabled && o.value.trim() !== ""
198
+ );
199
+ if (options.length > 0) {
200
+ prop.enum = options.map((o) => o.value.trim());
201
+ prop.oneOf = options.map((o) => ({
202
+ const: o.value.trim(),
203
+ title: o.textContent?.trim() || o.value.trim()
204
+ }));
205
+ }
206
+ }
207
+ }
192
208
  return prop;
193
209
  }
194
210
  function mapSelectElement(select) {
195
- const filtered = Array.from(select.options).filter((o) => o.value !== "");
196
- if (filtered.length === 0) {
197
- return { type: "string" };
211
+ const enumValues = [];
212
+ const oneOf = [];
213
+ for (const child of Array.from(select.children)) {
214
+ if (child instanceof HTMLOptGroupElement) {
215
+ if (child.disabled)
216
+ continue;
217
+ const groupLabel = child.label?.trim() ?? "";
218
+ for (const opt of Array.from(child.children)) {
219
+ if (!(opt instanceof HTMLOptionElement))
220
+ continue;
221
+ if (opt.disabled || opt.value === "")
222
+ continue;
223
+ enumValues.push(opt.value);
224
+ const entry = {
225
+ const: opt.value,
226
+ title: opt.text.trim() || opt.value
227
+ };
228
+ if (groupLabel)
229
+ entry.group = groupLabel;
230
+ oneOf.push(entry);
231
+ }
232
+ } else if (child instanceof HTMLOptionElement) {
233
+ if (child.disabled || child.value === "")
234
+ continue;
235
+ enumValues.push(child.value);
236
+ oneOf.push({ const: child.value, title: child.text.trim() || child.value });
237
+ }
198
238
  }
199
- const enumValues = filtered.map((o) => o.value);
200
- const oneOf = filtered.map((o) => ({ const: o.value, title: o.text.trim() || o.value }));
239
+ if (enumValues.length === 0)
240
+ return { type: "string" };
201
241
  return { type: "string", enum: enumValues, oneOf };
202
242
  }
203
243
  function collectRadioEnum(form, name) {
@@ -404,6 +444,8 @@ function buildSchema(form) {
404
444
  const schemaProp = inputTypeToSchema(control);
405
445
  if (!schemaProp)
406
446
  continue;
447
+ if (!isControlVisible(control))
448
+ continue;
407
449
  schemaProp.title = inferFieldTitle(control);
408
450
  const desc = inferFieldDescription(control);
409
451
  if (desc)
@@ -597,6 +639,24 @@ function labelTextWithoutNested(label) {
597
639
  function humanizeName(raw) {
598
640
  return raw.replace(/[-_]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim().replace(/\b\w/g, (c) => c.toUpperCase());
599
641
  }
642
+ function isControlVisible(el) {
643
+ const style = window.getComputedStyle(el);
644
+ if (style.display === "none")
645
+ return false;
646
+ if (style.visibility === "hidden")
647
+ return false;
648
+ if (el.offsetParent === null && style.position !== "fixed")
649
+ return false;
650
+ let node = el;
651
+ while (node && node !== document.body) {
652
+ if (node.getAttribute("aria-hidden") === "true")
653
+ return false;
654
+ node = node.parentElement;
655
+ }
656
+ if (el.closest("fieldset")?.disabled)
657
+ return false;
658
+ return true;
659
+ }
600
660
  function analyzeOrphanInputGroup(container, inputs, submitBtn) {
601
661
  const name = inferOrphanToolName(container, submitBtn);
602
662
  const description = inferOrphanToolDescription(container);
@@ -667,6 +727,8 @@ function buildSchemaFromInputs(inputs) {
667
727
  const schemaProp = inputTypeToSchema(control);
668
728
  if (!schemaProp)
669
729
  continue;
730
+ if (!isControlVisible(control))
731
+ continue;
670
732
  schemaProp.title = inferFieldTitle(control);
671
733
  const desc = inferFieldDescription(control);
672
734
  if (desc)
@@ -687,6 +749,7 @@ init_registry();
687
749
  var pendingExecutions = /* @__PURE__ */ new WeakMap();
688
750
  var lastParams = /* @__PURE__ */ new WeakMap();
689
751
  var formFieldElements = /* @__PURE__ */ new WeakMap();
752
+ var pendingWarnings = /* @__PURE__ */ new WeakMap();
690
753
  var _inputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
691
754
  var _textareaValueSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
692
755
  var _checkedSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "checked")?.set;
@@ -701,22 +764,36 @@ function buildExecuteHandler(form, config, toolName, metadata) {
701
764
  return new Promise((resolve, reject) => {
702
765
  pendingExecutions.set(form, { resolve, reject });
703
766
  if (config.autoSubmit || form.hasAttribute("toolautosubmit") || form.dataset["webmcpAutosubmit"] !== void 0) {
704
- setTimeout(() => {
705
- fillFormFields(form, params);
706
- let submitForm = form;
707
- if (!form.isConnected) {
708
- const liveBtn = document.querySelector(
709
- 'button[type="submit"]:not([disabled]), input[type="submit"]:not([disabled])'
710
- );
711
- const found = liveBtn?.closest("form");
712
- if (found) {
713
- submitForm = found;
714
- pendingExecutions.set(submitForm, { resolve, reject });
715
- attachSubmitInterceptor(submitForm, toolName);
767
+ waitForDomStable(form).then(async () => {
768
+ try {
769
+ fillFormFields(form, params);
770
+ for (let attempt = 0; attempt < 2; attempt++) {
771
+ const reset = getResetFields(form, params, formFieldElements.get(form));
772
+ if (reset.length === 0)
773
+ break;
774
+ fillFormFields(form, params);
775
+ await waitForDomStable(form, 400, 100);
716
776
  }
777
+ let submitForm = form;
778
+ if (!form.isConnected) {
779
+ const liveBtn = document.querySelector(
780
+ 'button[type="submit"]:not([disabled]), input[type="submit"]:not([disabled])'
781
+ );
782
+ const found = liveBtn?.closest("form");
783
+ if (found) {
784
+ submitForm = found;
785
+ pendingExecutions.set(submitForm, { resolve, reject });
786
+ attachSubmitInterceptor(submitForm, toolName);
787
+ }
788
+ }
789
+ const missing = getMissingRequired(metadata, params);
790
+ if (missing.length > 0)
791
+ pendingWarnings.set(submitForm, missing);
792
+ submitForm.requestSubmit();
793
+ } catch (err) {
794
+ reject(err instanceof Error ? err : new Error(String(err)));
717
795
  }
718
- submitForm.requestSubmit();
719
- }, 300);
796
+ });
720
797
  }
721
798
  });
722
799
  };
@@ -732,7 +809,10 @@ function attachSubmitInterceptor(form, toolName) {
732
809
  const { resolve } = pending;
733
810
  pendingExecutions.delete(form);
734
811
  const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));
735
- const text = `Form submitted. Fields: ${JSON.stringify(formData)}`;
812
+ const missing = pendingWarnings.get(form);
813
+ pendingWarnings.delete(form);
814
+ const warningText = missing?.length ? ` Note: required fields were not filled: ${missing.join(", ")}.` : "";
815
+ const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;
736
816
  const result = { content: [{ type: "text", text }] };
737
817
  if (e.agentInvoked && typeof e.respondWith === "function") {
738
818
  e.preventDefault();
@@ -938,6 +1018,50 @@ function fillElement(el, value) {
938
1018
  fillAriaField(el, value);
939
1019
  }
940
1020
  }
1021
+ function waitForDomStable(form, maxMs = 800, debounceMs = 150) {
1022
+ return new Promise((resolve) => {
1023
+ let settled = false;
1024
+ let debounceTimer = null;
1025
+ const settle = () => {
1026
+ if (settled)
1027
+ return;
1028
+ settled = true;
1029
+ observer2.disconnect();
1030
+ if (debounceTimer !== null)
1031
+ clearTimeout(debounceTimer);
1032
+ resolve();
1033
+ };
1034
+ const observer2 = new MutationObserver(() => {
1035
+ if (debounceTimer !== null)
1036
+ clearTimeout(debounceTimer);
1037
+ debounceTimer = setTimeout(settle, debounceMs);
1038
+ });
1039
+ observer2.observe(form, { childList: true, subtree: true, attributes: true, characterData: true });
1040
+ setTimeout(settle, maxMs);
1041
+ debounceTimer = setTimeout(settle, debounceMs);
1042
+ });
1043
+ }
1044
+ function getResetFields(form, params, fieldEls) {
1045
+ const reset = [];
1046
+ for (const [key, expected] of Object.entries(params)) {
1047
+ const el = findNativeField(form, key) ?? (fieldEls?.get(key) ?? null);
1048
+ if (!el)
1049
+ continue;
1050
+ if (el instanceof HTMLInputElement && el.type === "checkbox") {
1051
+ if (el.checked !== Boolean(expected))
1052
+ reset.push(key);
1053
+ } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
1054
+ if (el.value !== String(expected ?? ""))
1055
+ reset.push(key);
1056
+ }
1057
+ }
1058
+ return reset;
1059
+ }
1060
+ function getMissingRequired(metadata, params) {
1061
+ if (!metadata?.inputSchema?.required?.length)
1062
+ return [];
1063
+ return metadata.inputSchema.required.filter((fieldKey) => !(fieldKey in params));
1064
+ }
941
1065
 
942
1066
  // src/enhancer.ts
943
1067
  async function enrichMetadata(metadata, enhancer) {