auto-webmcp 0.3.0 → 0.3.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.
@@ -83,7 +83,6 @@ function resolveConfig(userConfig) {
83
83
  return {
84
84
  exclude: userConfig?.exclude ?? [],
85
85
  autoSubmit: userConfig?.autoSubmit ?? false,
86
- enhance: userConfig?.enhance ?? null,
87
86
  overrides: userConfig?.overrides ?? {},
88
87
  debug: userConfig?.debug ?? false
89
88
  };
@@ -484,17 +483,39 @@ function extractDefaultValue(control) {
484
483
  }
485
484
  return void 0;
486
485
  }
486
+ function collectShadowControls(root, visited = /* @__PURE__ */ new Set()) {
487
+ if (visited.has(root))
488
+ return [];
489
+ visited.add(root);
490
+ const results = [];
491
+ for (const el of Array.from(root.querySelectorAll("*"))) {
492
+ if (el.shadowRoot) {
493
+ results.push(
494
+ ...Array.from(
495
+ el.shadowRoot.querySelectorAll(
496
+ "input, textarea, select"
497
+ )
498
+ ),
499
+ ...collectShadowControls(el.shadowRoot, visited)
500
+ );
501
+ }
502
+ }
503
+ return results;
504
+ }
487
505
  function buildSchema(form) {
488
506
  const properties = {};
489
507
  const required = [];
490
508
  const fieldElements = /* @__PURE__ */ new Map();
491
509
  const processedRadioGroups = /* @__PURE__ */ new Set();
492
510
  const processedCheckboxGroups = /* @__PURE__ */ new Set();
493
- const controls = Array.from(
494
- form.querySelectorAll(
495
- "input, textarea, select"
496
- )
497
- );
511
+ const controls = [
512
+ ...Array.from(
513
+ form.querySelectorAll(
514
+ "input, textarea, select"
515
+ )
516
+ ),
517
+ ...collectShadowControls(form)
518
+ ];
498
519
  for (const control of controls) {
499
520
  const name = control.name;
500
521
  const fieldKey = name || resolveNativeControlFallbackKey(control);
@@ -672,9 +693,6 @@ function resolveAriaFieldKey(el) {
672
693
  return null;
673
694
  }
674
695
  function inferAriaFieldTitle(el) {
675
- const nativeTitle = el.getAttribute("toolparamtitle");
676
- if (nativeTitle?.trim())
677
- return nativeTitle.trim();
678
696
  const htmlEl = el;
679
697
  if (htmlEl.dataset?.["webmcpTitle"])
680
698
  return htmlEl.dataset["webmcpTitle"];
@@ -713,9 +731,6 @@ function inferAriaFieldDescription(el) {
713
731
  return "";
714
732
  }
715
733
  function inferFieldTitle(control) {
716
- const nativeTitle = control.getAttribute("toolparamtitle");
717
- if (nativeTitle?.trim())
718
- return nativeTitle.trim();
719
734
  if ("dataset" in control && control.dataset["webmcpTitle"]) {
720
735
  return control.dataset["webmcpTitle"];
721
736
  }
@@ -925,6 +940,9 @@ function buildExecuteHandler(form, config, toolName, metadata) {
925
940
  return async (params) => {
926
941
  pendingFillWarnings.set(form, []);
927
942
  fillFormFields(form, params);
943
+ const missingNow = getMissingRequired(metadata, params);
944
+ if (missingNow.length > 0)
945
+ pendingWarnings.set(form, missingNow);
928
946
  window.dispatchEvent(new CustomEvent("toolactivated", { detail: { toolName } }));
929
947
  return new Promise((resolve, reject) => {
930
948
  pendingExecutions.set(form, { resolve, reject });
@@ -951,9 +969,10 @@ function buildExecuteHandler(form, config, toolName, metadata) {
951
969
  attachSubmitInterceptor(submitForm, toolName);
952
970
  }
953
971
  }
954
- const missing = getMissingRequired(metadata, params);
955
- if (missing.length > 0)
956
- pendingWarnings.set(submitForm, missing);
972
+ if (submitForm !== form && pendingWarnings.has(form)) {
973
+ pendingWarnings.set(submitForm, pendingWarnings.get(form));
974
+ pendingWarnings.delete(form);
975
+ }
957
976
  submitForm.requestSubmit();
958
977
  } catch (err) {
959
978
  reject(err instanceof Error ? err : new Error(String(err)));
@@ -975,17 +994,37 @@ function attachSubmitInterceptor(form, toolName) {
975
994
  pendingExecutions.delete(form);
976
995
  const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));
977
996
  lastFilledSnapshot.delete(form);
978
- const missing = pendingWarnings.get(form);
997
+ const missingRequired = pendingWarnings.get(form) ?? [];
979
998
  pendingWarnings.delete(form);
980
999
  const fillWarnings = pendingFillWarnings.get(form) ?? [];
981
1000
  pendingFillWarnings.delete(form);
982
- const allWarnings = [
983
- ...missing?.length ? [`required fields were not filled: ${missing.join(", ")}`] : [],
984
- ...fillWarnings
1001
+ const skippedFields = fillWarnings.filter((w) => w.type === "not_filled").map((w) => w.field);
1002
+ const structured = {
1003
+ status: missingRequired.length > 0 || skippedFields.length > 0 ? "partial" : "success",
1004
+ filled_fields: formData,
1005
+ skipped_fields: skippedFields,
1006
+ missing_required: missingRequired,
1007
+ warnings: [
1008
+ ...missingRequired.map((f) => ({
1009
+ field: f,
1010
+ type: "missing_required",
1011
+ message: `required field "${f}" was not provided`
1012
+ })),
1013
+ ...fillWarnings
1014
+ ]
1015
+ };
1016
+ const allWarnMessages = [
1017
+ ...missingRequired.length ? [`required fields were not filled: ${missingRequired.join(", ")}`] : [],
1018
+ ...fillWarnings.map((w) => w.message)
985
1019
  ];
986
- const warningText = allWarnings.length ? ` Note: ${allWarnings.join("; ")}.` : "";
1020
+ const warningText = allWarnMessages.length ? ` Note: ${allWarnMessages.join("; ")}.` : "";
987
1021
  const text = `Form submitted. Fields: ${JSON.stringify(formData)}${warningText}`;
988
- const result = { content: [{ type: "text", text }] };
1022
+ const result = {
1023
+ content: [
1024
+ { type: "text", text },
1025
+ { type: "text", text: JSON.stringify(structured) }
1026
+ ]
1027
+ };
989
1028
  if (e.agentInvoked && typeof e.respondWith === "function") {
990
1029
  e.preventDefault();
991
1030
  e.respondWith(Promise.resolve(result));
@@ -1119,16 +1158,26 @@ function fillInput(input, form, key, value) {
1119
1158
  const raw = String(value ?? "");
1120
1159
  const num = Number(raw);
1121
1160
  if (raw === "" || isNaN(num)) {
1122
- pendingFillWarnings.get(form)?.push(`"${key}" expects a number, got: ${JSON.stringify(value)}`);
1161
+ pendingFillWarnings.get(form)?.push({
1162
+ field: key,
1163
+ type: "type_mismatch",
1164
+ message: `"${key}" expects a number, got: ${JSON.stringify(value)}`,
1165
+ original: value
1166
+ });
1123
1167
  return;
1124
1168
  }
1125
1169
  const min = input.min !== "" ? parseFloat(input.min) : -Infinity;
1126
1170
  const max = input.max !== "" ? parseFloat(input.max) : Infinity;
1127
1171
  if (num < min || num > max) {
1128
- pendingFillWarnings.get(form)?.push(
1129
- `"${key}" value ${num} is outside allowed range [${input.min || "?"}, ${input.max || "?"}]`
1130
- );
1131
- input.value = String(Math.min(Math.max(num, min), max));
1172
+ const clamped = Math.min(Math.max(num, min), max);
1173
+ pendingFillWarnings.get(form)?.push({
1174
+ field: key,
1175
+ type: "clamped",
1176
+ message: `"${key}" value ${num} is outside allowed range [${input.min || "?"}, ${input.max || "?"}], clamped to ${clamped}`,
1177
+ original: num,
1178
+ actual: clamped
1179
+ });
1180
+ input.value = String(clamped);
1132
1181
  } else {
1133
1182
  input.value = String(num);
1134
1183
  }
@@ -1315,74 +1364,6 @@ function getMissingRequired(metadata, params) {
1315
1364
  return metadata.inputSchema.required.filter((fieldKey) => !(fieldKey in params));
1316
1365
  }
1317
1366
 
1318
- // src/enhancer.ts
1319
- async function enrichMetadata(metadata, enhancer) {
1320
- try {
1321
- const enriched = await callLLM(metadata, enhancer);
1322
- return { ...metadata, description: enriched };
1323
- } catch (err) {
1324
- console.warn("[auto-webmcp] Enrichment failed, using heuristic description:", err);
1325
- return metadata;
1326
- }
1327
- }
1328
- async function callLLM(metadata, config) {
1329
- const prompt = buildPrompt(metadata);
1330
- if (config.provider === "claude") {
1331
- return callClaude(prompt, config);
1332
- } else {
1333
- return callGemini(prompt, config);
1334
- }
1335
- }
1336
- function buildPrompt(metadata) {
1337
- const fields = Object.entries(metadata.inputSchema.properties).map(([name, prop]) => `- ${prop.title ?? name} (${prop.type}): ${prop.description ?? ""}`).join("\n");
1338
- return `You are helping describe a web form as an AI tool. Given this form information:
1339
-
1340
- Name: ${metadata.name}
1341
- Current description: ${metadata.description}
1342
- Fields:
1343
- ${fields}
1344
-
1345
- Write a concise (1-2 sentence) description of what this tool does and when an AI agent should use it. Be specific and actionable. Respond with only the description, no preamble.`;
1346
- }
1347
- async function callClaude(prompt, config) {
1348
- const model = config.model ?? "claude-haiku-4-5-20251001";
1349
- const response = await fetch("https://api.anthropic.com/v1/messages", {
1350
- method: "POST",
1351
- headers: {
1352
- "x-api-key": config.apiKey,
1353
- "anthropic-version": "2023-06-01",
1354
- "content-type": "application/json"
1355
- },
1356
- body: JSON.stringify({
1357
- model,
1358
- max_tokens: 150,
1359
- messages: [{ role: "user", content: prompt }]
1360
- })
1361
- });
1362
- if (!response.ok) {
1363
- throw new Error(`Claude API error: ${response.status}`);
1364
- }
1365
- const data = await response.json();
1366
- return data.content.filter((block) => block.type === "text").map((block) => block.text).join("").trim();
1367
- }
1368
- async function callGemini(prompt, config) {
1369
- const model = config.model ?? "gemini-1.5-flash";
1370
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${config.apiKey}`;
1371
- const response = await fetch(url, {
1372
- method: "POST",
1373
- headers: { "content-type": "application/json" },
1374
- body: JSON.stringify({
1375
- contents: [{ parts: [{ text: prompt }] }],
1376
- generationConfig: { maxOutputTokens: 150, temperature: 0.2 }
1377
- })
1378
- });
1379
- if (!response.ok) {
1380
- throw new Error(`Gemini API error: ${response.status}`);
1381
- }
1382
- const data = await response.json();
1383
- return data.candidates[0]?.content.parts.map((p) => p.text).join("").trim() ?? "";
1384
- }
1385
-
1386
1367
  // src/discovery.ts
1387
1368
  function emit(type, form, toolName) {
1388
1369
  window.dispatchEvent(
@@ -1414,12 +1395,7 @@ async function registerForm(form, config) {
1414
1395
  } catch {
1415
1396
  }
1416
1397
  }
1417
- let metadata = analyzeForm(form, override);
1418
- if (config.enhance) {
1419
- if (config.debug)
1420
- console.debug(`[auto-webmcp] Enriching: ${metadata.name}\u2026`);
1421
- metadata = await enrichMetadata(metadata, config.enhance);
1422
- }
1398
+ const metadata = analyzeForm(form, override);
1423
1399
  if (config.debug) {
1424
1400
  warnToolQuality(metadata.name, metadata.description);
1425
1401
  }