auto-webmcp 0.3.5 → 0.3.7

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.
@@ -611,7 +611,7 @@ function buildSchema(form) {
611
611
  required.push(key);
612
612
  }
613
613
  }
614
- return { schema: { type: "object", properties, required }, fieldElements };
614
+ return { schema: { "$schema": "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required }, fieldElements };
615
615
  }
616
616
  function resolveNativeControlFallbackKey(control) {
617
617
  const el = control;
@@ -816,7 +816,24 @@ function analyzeOrphanInputGroup(container, inputs, submitBtn) {
816
816
  const name = inferOrphanToolName(container, submitBtn);
817
817
  const description = inferOrphanToolDescription(container);
818
818
  const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);
819
- return { name, description, inputSchema, fieldElements };
819
+ const annotations = inferOrphanAnnotations(submitBtn);
820
+ return { name, description, inputSchema, annotations, fieldElements };
821
+ }
822
+ function inferOrphanAnnotations(submitBtn) {
823
+ const annotations = {};
824
+ const submitText = submitBtn instanceof HTMLInputElement ? submitBtn.value.trim() : submitBtn?.textContent?.trim() ?? "";
825
+ if (READONLY_BUTTON_PATTERNS.test(submitText)) {
826
+ annotations.readOnlyHint = true;
827
+ annotations.idempotentHint = true;
828
+ }
829
+ if (DESTRUCTIVE_BUTTON_PATTERNS.test(submitText)) {
830
+ annotations.destructiveHint = true;
831
+ }
832
+ if (annotations.readOnlyHint !== true) {
833
+ annotations.openWorldHint = true;
834
+ }
835
+ const hasNonDefault = annotations.readOnlyHint === true || annotations.destructiveHint === true || annotations.idempotentHint === true || annotations.openWorldHint === false;
836
+ return hasNonDefault ? annotations : {};
820
837
  }
821
838
  function inferOrphanToolName(container, submitBtn) {
822
839
  if (submitBtn) {
@@ -871,8 +888,8 @@ function buildSchemaFromInputs(inputs) {
871
888
  const processedRadioGroups = /* @__PURE__ */ new Set();
872
889
  const processedCheckboxGroups = /* @__PURE__ */ new Set();
873
890
  for (const control of inputs) {
874
- const name = control.name;
875
- const fieldKey = name || resolveNativeControlFallbackKey(control);
891
+ const rawName = control.name;
892
+ const fieldKey = (rawName ? sanitizeName(rawName) : null) || resolveNativeControlFallbackKey(control);
876
893
  if (!fieldKey)
877
894
  continue;
878
895
  if (control instanceof HTMLInputElement && control.type === "radio") {
@@ -911,12 +928,12 @@ function buildSchemaFromInputs(inputs) {
911
928
  }
912
929
  }
913
930
  properties[fieldKey] = schemaProp;
914
- if (!name)
931
+ if (!rawName)
915
932
  fieldElements.set(fieldKey, control);
916
933
  if (control.required)
917
934
  required.push(fieldKey);
918
935
  }
919
- return { schema: { type: "object", properties, required }, fieldElements };
936
+ return { schema: { "$schema": "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required }, fieldElements };
920
937
  }
921
938
 
922
939
  // src/discovery.ts
@@ -1536,6 +1553,7 @@ async function scanOrphanInputs(config) {
1536
1553
  if (!isWebMCPSupported())
1537
1554
  return;
1538
1555
  const SUBMIT_BTN_SELECTOR = '[type="submit"]:not([disabled]), button:not([type]):not([disabled])';
1556
+ const SUBMIT_BTN_GROUPING_SELECTOR = '[type="submit"], button:not([type])';
1539
1557
  const SUBMIT_TEXT_RE = /subscribe|submit|sign[\s-]?up|send|join|go|search/i;
1540
1558
  const orphanInputs = Array.from(
1541
1559
  document.querySelectorAll(
@@ -1555,7 +1573,7 @@ async function scanOrphanInputs(config) {
1555
1573
  let container = input.parentElement;
1556
1574
  let foundContainer = input.parentElement ?? document.body;
1557
1575
  while (container && container !== document.body) {
1558
- const hasSubmitBtn = container.querySelector(SUBMIT_BTN_SELECTOR) !== null || Array.from(container.querySelectorAll("button")).some(
1576
+ const hasSubmitBtn = container.querySelector(SUBMIT_BTN_GROUPING_SELECTOR) !== null || Array.from(container.querySelectorAll("button")).some(
1559
1577
  (b) => SUBMIT_TEXT_RE.test(b.textContent ?? "")
1560
1578
  );
1561
1579
  if (hasSubmitBtn) {
@@ -1603,15 +1621,42 @@ async function scanOrphanInputs(config) {
1603
1621
  }
1604
1622
  }
1605
1623
  window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
1606
- return { content: [{ type: "text", text: "Fields filled. Ready to submit." }] };
1624
+ if (!config.autoSubmit) {
1625
+ return { content: [{ type: "text", text: "Fields filled. Ready to submit." }] };
1626
+ }
1627
+ let btn = null;
1628
+ const deadline = Date.now() + 2e3;
1629
+ while (Date.now() < deadline) {
1630
+ const candidates = Array.from(
1631
+ container.querySelectorAll(SUBMIT_BTN_SELECTOR)
1632
+ ).filter((b) => {
1633
+ const r = b.getBoundingClientRect();
1634
+ return r.width > 0 && r.height > 0;
1635
+ });
1636
+ const last = candidates[candidates.length - 1] ?? null;
1637
+ if (last) {
1638
+ btn = last;
1639
+ break;
1640
+ }
1641
+ await new Promise((r) => setTimeout(r, 100));
1642
+ }
1643
+ if (!btn) {
1644
+ return { content: [{ type: "text", text: "Fields filled but the submit button is still disabled. The page may require additional input before submitting." }] };
1645
+ }
1646
+ btn.click();
1647
+ return { content: [{ type: "text", text: "Fields filled and form submitted." }] };
1607
1648
  };
1608
1649
  try {
1609
- await navigator.modelContext.registerTool({
1650
+ const toolDef = {
1610
1651
  name: metadata.name,
1611
1652
  description: metadata.description,
1612
1653
  inputSchema: metadata.inputSchema,
1613
1654
  execute
1614
- });
1655
+ };
1656
+ if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {
1657
+ toolDef.annotations = metadata.annotations;
1658
+ }
1659
+ await navigator.modelContext.registerTool(toolDef);
1615
1660
  if (config.debug) {
1616
1661
  console.debug(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);
1617
1662
  }