auto-webmcp 0.2.5 → 0.2.6

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;AA8YD;;;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;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"}
@@ -454,6 +454,12 @@ function resolveNativeControlFallbackKey(control) {
454
454
  const label = control.getAttribute("aria-label");
455
455
  if (label)
456
456
  return sanitizeName(label);
457
+ if ((control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) && control.placeholder?.trim()) {
458
+ return sanitizeName(control.placeholder.trim());
459
+ }
460
+ if (control instanceof HTMLInputElement && control.type !== "text") {
461
+ return control.type;
462
+ }
457
463
  return null;
458
464
  }
459
465
  function collectAriaControls(form) {
@@ -538,6 +544,9 @@ function inferFieldTitle(control) {
538
544
  return humanizeName(control.name);
539
545
  if (control.id)
540
546
  return humanizeName(control.id);
547
+ if ((control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) && control.placeholder?.trim()) {
548
+ return control.placeholder.trim();
549
+ }
541
550
  return "";
542
551
  }
543
552
  function inferFieldDescription(control) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/registry.ts", "../src/index.ts", "../src/config.ts", "../src/schema.ts", "../src/analyzer.ts", "../src/discovery.ts", "../src/interceptor.ts", "../src/enhancer.ts"],
4
- "sourcesContent": ["/**\n * registry.ts \u2014 Wrapper around navigator.modelContext WebMCP Imperative API\n */\n\nimport { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// WebMCP type declarations (not yet in TypeScript DOM lib)\n// ---------------------------------------------------------------------------\n\nexport interface WebMCPTool {\n name: string;\n description: string;\n inputSchema: object;\n execute: (params: Record<string, unknown>) => Promise<unknown>;\n}\n\ndeclare global {\n interface Navigator {\n modelContext?: {\n registerTool(tool: WebMCPTool): Promise<void>;\n unregisterTool(name: string): Promise<void>;\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/** Tracks registered tools: form element \u2192 tool name */\nconst registeredTools = new Map<HTMLFormElement, string>();\n\n/** True if the browser supports navigator.modelContext */\nexport function isWebMCPSupported(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.modelContext !== 'undefined';\n}\n\n/**\n * Register a form as a WebMCP tool.\n * Silently no-ops if WebMCP is not supported.\n */\nexport async function registerFormTool(\n form: HTMLFormElement,\n metadata: ToolMetadata,\n execute: (params: Record<string, unknown>) => Promise<unknown>,\n): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Unregister any previously-registered tool for this same form element\n const existing = registeredTools.get(form);\n if (existing) {\n await unregisterFormTool(form);\n }\n\n try {\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n } catch {\n // Chrome may hold a stale registration from a previous page load.\n // Unregister by name and retry once.\n try {\n await navigator.modelContext!.unregisterTool(metadata.name);\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n } catch {\n // Give up Chrome registration \u2014 local handlers and form:registered still work.\n }\n }\n\n registeredTools.set(form, metadata.name);\n}\n\n/**\n * Unregister the WebMCP tool associated with a form element.\n * Silently no-ops if not registered or WebMCP not supported.\n */\nexport async function unregisterFormTool(form: HTMLFormElement): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const name = registeredTools.get(form);\n if (!name) return;\n\n try {\n await navigator.modelContext!.unregisterTool(name);\n } catch {\n // Tool may have already been removed \u2014 ignore\n }\n\n registeredTools.delete(form);\n}\n\n/** Get the registered tool name for a form, if any */\nexport function getRegisteredToolName(form: HTMLFormElement): string | undefined {\n return registeredTools.get(form);\n}\n\n/** Return a snapshot of all currently registered form\u2192name pairs */\nexport function getAllRegisteredTools(): Array<{ form: HTMLFormElement; name: string }> {\n return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));\n}\n\n/** Unregister all tools (e.g. on teardown) */\nexport async function unregisterAll(): Promise<void> {\n const entries = Array.from(registeredTools.entries());\n await Promise.all(entries.map(([form]) => unregisterFormTool(form)));\n}\n", "/**\n * index.ts \u2014 Entry point & public API for auto-webmcp\n *\n * Zero-config drop-in:\n * <script src=\"auto-webmcp.iife.js\"></script>\n *\n * ESM usage:\n * import { autoWebMCP } from 'auto-webmcp';\n * autoWebMCP({ exclude: ['#login-form'] });\n */\n\nimport { AutoWebMCPConfig, resolveConfig } from './config.js';\nimport { startDiscovery, stopDiscovery } from './discovery.js';\nimport { unregisterAll, getAllRegisteredTools, isWebMCPSupported } from './registry.js';\n\nexport type { AutoWebMCPConfig } from './config.js';\nexport type { ToolMetadata } from './analyzer.js';\nexport type { JsonSchema, JsonSchemaProperty } from './schema.js';\n\nexport interface AutoWebMCPHandle {\n /** Stop observing and unregister all tools */\n destroy: () => Promise<void>;\n /** Return all currently registered tools */\n getTools: () => Array<{ form: HTMLFormElement; name: string }>;\n /** True if running in a WebMCP-capable browser */\n isSupported: boolean;\n}\n\n/**\n * Initialize auto-webmcp.\n *\n * @param config \u2014 Optional configuration (all fields optional)\n * @returns A handle to inspect or tear down the instance\n */\nexport async function autoWebMCP(config?: AutoWebMCPConfig): Promise<AutoWebMCPHandle> {\n const resolved = resolveConfig(config);\n\n if (resolved.debug) {\n console.debug('[auto-webmcp] Initializing', {\n webmcpSupported: isWebMCPSupported(),\n config: resolved,\n });\n }\n\n await startDiscovery(resolved);\n\n return {\n destroy: async () => {\n stopDiscovery();\n await unregisterAll();\n },\n getTools: getAllRegisteredTools,\n isSupported: isWebMCPSupported(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auto-init for IIFE / script-tag usage\n// ---------------------------------------------------------------------------\n\n// When loaded as a <script> tag, auto-initialize with zero config.\n// Users can prevent this by setting window.__AUTO_WEBMCP_NO_AUTOINIT = true\n// before the script loads.\n\nif (\n typeof window !== 'undefined' &&\n !(window as unknown as Record<string, unknown>)['__AUTO_WEBMCP_NO_AUTOINIT']\n) {\n void autoWebMCP();\n}\n", "/**\n * config.ts \u2014 User configuration merging & defaults\n */\n\nexport interface EnhancerConfig {\n provider: 'gemini' | 'claude';\n apiKey: string;\n model?: string;\n}\n\nexport interface FormOverride {\n name?: string;\n description?: string;\n}\n\nexport interface AutoWebMCPConfig {\n /**\n * CSS selectors for forms to skip. E.g. ['#login-form', '[data-no-webmcp]']\n */\n exclude?: string[];\n\n /**\n * If true, agent-invoked forms are auto-submitted without human confirmation.\n * Default: false\n */\n autoSubmit?: boolean;\n\n /**\n * Optional AI enrichment for richer tool descriptions.\n */\n enhance?: EnhancerConfig;\n\n /**\n * Per-form name/description overrides keyed by CSS selector.\n */\n overrides?: Record<string, FormOverride>;\n\n /**\n * Log registered tools to console on init. Default: false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n exclude: string[];\n autoSubmit: boolean;\n enhance: EnhancerConfig | null;\n overrides: Record<string, FormOverride>;\n debug: boolean;\n}\n\nexport function resolveConfig(userConfig?: AutoWebMCPConfig): ResolvedConfig {\n return {\n exclude: userConfig?.exclude ?? [],\n autoSubmit: userConfig?.autoSubmit ?? false,\n enhance: userConfig?.enhance ?? null,\n overrides: userConfig?.overrides ?? {},\n debug: userConfig?.debug ?? false,\n };\n}\n", "/**\n * schema.ts \u2014 HTML input type \u2192 JSON Schema type mapping\n */\n\nexport const ARIA_ROLES_TO_SCAN = [\n 'textbox', 'combobox', 'checkbox', 'radio', 'switch',\n 'spinbutton', 'searchbox', 'slider',\n] as const;\n\nexport type AriaRole = typeof ARIA_ROLES_TO_SCAN[number];\n\nexport interface JsonSchemaProperty {\n type: string;\n format?: string;\n description?: string;\n title?: string;\n enum?: string[];\n oneOf?: Array<{ const: string; title: string }>;\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface JsonSchema {\n type: 'object';\n properties: Record<string, JsonSchemaProperty>;\n required: string[];\n}\n\n/** Maps an HTML <input type> to a JSON Schema property base */\nexport function inputTypeToSchema(\n input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): JsonSchemaProperty | null {\n if (input instanceof HTMLInputElement) {\n return mapInputElement(input);\n }\n if (input instanceof HTMLTextAreaElement) {\n return { type: 'string' };\n }\n if (input instanceof HTMLSelectElement) {\n return mapSelectElement(input);\n }\n return null;\n}\n\nfunction mapInputElement(input: HTMLInputElement): JsonSchemaProperty | null {\n const type = input.type.toLowerCase();\n\n switch (type) {\n case 'text':\n case 'search':\n case 'tel':\n return buildStringSchema(input);\n\n case 'email':\n return { ...buildStringSchema(input), format: 'email' };\n\n case 'url':\n return { ...buildStringSchema(input), format: 'uri' };\n\n case 'number':\n case 'range': {\n const prop: JsonSchemaProperty = { type: 'number' };\n if (input.min !== '') prop.minimum = parseFloat(input.min);\n if (input.max !== '') prop.maximum = parseFloat(input.max);\n return prop;\n }\n\n case 'date':\n return { type: 'string', format: 'date' };\n\n case 'datetime-local':\n return { type: 'string', format: 'date-time' };\n\n case 'time':\n return { type: 'string', format: 'time' };\n\n case 'month':\n return { type: 'string', pattern: '^\\\\d{4}-\\\\d{2}$' };\n\n case 'week':\n return { type: 'string', pattern: '^\\\\d{4}-W\\\\d{2}$' };\n\n case 'color':\n return { type: 'string', pattern: '^#[0-9a-fA-F]{6}$' };\n\n case 'checkbox':\n return { type: 'boolean' };\n\n case 'radio':\n // Radio groups are handled at the form level in analyzer.ts\n return { type: 'string' };\n\n case 'file':\n case 'hidden':\n case 'submit':\n case 'reset':\n case 'button':\n case 'image':\n // These are not exposed to agents\n return null;\n\n case 'password':\n // Skip passwords \u2014 never expose to agents\n return null;\n\n default:\n return { type: 'string' };\n }\n}\n\nfunction buildStringSchema(input: HTMLInputElement): JsonSchemaProperty {\n const prop: JsonSchemaProperty = { type: 'string' };\n if (input.minLength > 0) prop.minLength = input.minLength;\n if (input.maxLength > 0 && input.maxLength !== 524288) prop.maxLength = input.maxLength;\n if (input.pattern) prop.pattern = input.pattern;\n return prop;\n}\n\nfunction mapSelectElement(select: HTMLSelectElement): JsonSchemaProperty {\n const filtered = Array.from(select.options).filter((o) => o.value !== '');\n\n if (filtered.length === 0) {\n return { type: 'string' };\n }\n\n const enumValues = filtered.map((o) => o.value);\n const oneOf = filtered.map((o) => ({ const: o.value, title: o.text.trim() || o.value }));\n\n return { type: 'string', enum: enumValues, oneOf };\n}\n\n/** Collect all radio button values for a given name within a form */\nexport function collectRadioEnum(form: HTMLFormElement, name: string): string[] {\n const radios = Array.from(\n form.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][name=\"${CSS.escape(name)}\"]`),\n );\n return radios.map((r) => r.value).filter((v) => v !== '');\n}\n\n/** Collect radio button values + label titles as oneOf entries */\nexport function collectRadioOneOf(\n form: HTMLFormElement,\n name: string,\n): Array<{ const: string; title: string }> {\n const radios = Array.from(\n form.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][name=\"${CSS.escape(name)}\"]`),\n ).filter((r) => r.value !== '');\n\n return radios.map((r) => {\n const title = getRadioLabelText(r);\n return { const: r.value, title: title || r.value };\n });\n}\n\n/** Maps an ARIA role element to a JSON Schema property */\nexport function ariaRoleToSchema(el: Element, role: AriaRole): JsonSchemaProperty {\n switch (role) {\n case 'checkbox':\n case 'switch':\n return { type: 'boolean' };\n\n case 'spinbutton':\n case 'slider': {\n const prop: JsonSchemaProperty = { type: 'number' };\n const min = el.getAttribute('aria-valuemin');\n const max = el.getAttribute('aria-valuemax');\n if (min !== null) prop.minimum = parseFloat(min);\n if (max !== null) prop.maximum = parseFloat(max);\n return prop;\n }\n\n case 'combobox': {\n const ownedId = el.getAttribute('aria-owns') ?? el.getAttribute('aria-controls');\n if (ownedId) {\n const listbox = document.getElementById(ownedId);\n if (listbox) {\n const options = Array.from(listbox.querySelectorAll('[role=\"option\"]')).filter(\n (o) => o.getAttribute('aria-disabled') !== 'true',\n );\n if (options.length > 0) {\n const enumValues = options\n .map((o) => (o.getAttribute('data-value') ?? o.textContent ?? '').trim())\n .filter(Boolean);\n const oneOf = options.map((o) => ({\n const: (o.getAttribute('data-value') ?? o.textContent ?? '').trim(),\n title: (o.textContent ?? '').trim(),\n }));\n return { type: 'string', enum: enumValues, oneOf };\n }\n }\n }\n return { type: 'string' };\n }\n\n case 'textbox':\n case 'searchbox':\n case 'radio':\n default:\n return { type: 'string' };\n }\n}\n\nfunction getRadioLabelText(radio: HTMLInputElement): string {\n // 1. Wrapping label\n const parent = radio.closest('label');\n if (parent) {\n const clone = parent.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n const text = clone.textContent?.trim() ?? '';\n if (text) return text;\n }\n // 2. Label pointing to radio by id\n if (radio.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(radio.id)}\"]`);\n if (label) {\n const text = label.textContent?.trim() ?? '';\n if (text) return text;\n }\n }\n return '';\n}\n", "/**\n * analyzer.ts \u2014 Infer tool name, description, and JSON Schema from form DOM\n */\n\nimport { JsonSchema, JsonSchemaProperty, inputTypeToSchema, collectRadioEnum, collectRadioOneOf, ARIA_ROLES_TO_SCAN, AriaRole, ariaRoleToSchema } from './schema.js';\nimport { FormOverride } from './config.js';\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n /** Key \u2192 DOM element for fields not addressable by name (id-keyed or ARIA-role controls). */\n fieldElements?: Map<string, Element>;\n}\n\n// Track form index for fallback naming\nlet formIndex = 0;\n\n/** Reset form index counter (useful in tests) */\nexport function resetFormIndex(): void {\n formIndex = 0;\n}\n\n/** Derive ToolMetadata from a <form> element */\nexport function analyzeForm(form: HTMLFormElement, override?: FormOverride): ToolMetadata {\n const name = override?.name ?? inferToolName(form);\n const description = override?.description ?? inferToolDescription(form);\n const { schema: inputSchema, fieldElements } = buildSchema(form);\n\n return { name, description, inputSchema, fieldElements };\n}\n\n// ---------------------------------------------------------------------------\n// Tool name inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolName(form: HTMLFormElement): string {\n // 1. Native toolname attribute (spec)\n const nativeName = form.getAttribute('toolname');\n if (nativeName) return sanitizeName(nativeName);\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpName'];\n if (explicit) return sanitizeName(explicit);\n\n // 2. Submit button text\n const submitText = getSubmitButtonText(form);\n if (submitText) return sanitizeName(submitText);\n\n // 3. Nearest heading above the form\n const heading = getNearestHeadingText(form);\n if (heading) return sanitizeName(heading);\n\n // 4. Form id or name attribute\n if (form.id) return sanitizeName(form.id);\n if (form.name) return sanitizeName(form.name);\n\n // 5. Form action URL path segment\n if (form.action) {\n const segment = getLastPathSegment(form.action);\n if (segment) return sanitizeName(segment);\n }\n\n // 6. Fallback\n return `form_${++formIndex}`;\n}\n\nfunction sanitizeName(raw: string): string {\n return raw\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 64) || 'form';\n}\n\nfunction getSubmitButtonText(form: HTMLFormElement): string {\n const buttons = [\n ...Array.from(form.querySelectorAll<HTMLButtonElement>('button[type=\"submit\"], button:not([type])')),\n ...Array.from(form.querySelectorAll<HTMLInputElement>('input[type=\"submit\"]')),\n ];\n\n for (const btn of buttons) {\n const text =\n btn instanceof HTMLInputElement\n ? btn.value.trim()\n : btn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return text;\n }\n return '';\n}\n\nfunction getNearestHeadingText(form: HTMLFormElement): string {\n // Walk up the DOM looking for a preceding sibling or parent heading\n let node: Element | null = form;\n while (node) {\n // Check preceding siblings\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n // Stop at body\n if (!node || node === document.body) break;\n }\n return '';\n}\n\nfunction getLastPathSegment(url: string): string {\n try {\n const parsed = new URL(url, window.location.href);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n return '';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool description inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolDescription(form: HTMLFormElement): string {\n // 1. Native tooldescription attribute (spec)\n const nativeDesc = form.getAttribute('tooldescription');\n if (nativeDesc) return nativeDesc.trim();\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpDescription'];\n if (explicit) return explicit.trim();\n\n // 2. <legend> inside the form\n const legend = form.querySelector('legend');\n if (legend?.textContent?.trim()) return legend.textContent.trim();\n\n // 3. aria-label on the form\n const ariaLabel = form.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n // 4. aria-describedby\n const describedById = form.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 5. Combine nearest heading + page title\n const heading = getNearestHeadingText(form);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle) return `${heading} \u2014 ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n\n return 'Submit form';\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema construction\n// ---------------------------------------------------------------------------\n\nfunction buildSchema(form: HTMLFormElement): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n\n // Track which radio group names we've already processed\n const processedRadioGroups = new Set<string>();\n\n const controls = Array.from(\n form.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input, textarea, select',\n ),\n );\n\n for (const control of controls) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n // Skip already-processed radio groups\n if (\n control instanceof HTMLInputElement &&\n control.type === 'radio'\n ) {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types\n\n // Enrich with title and description\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // For radio groups, add enum and oneOf values\n if (\n control instanceof HTMLInputElement &&\n control.type === 'radio'\n ) {\n schemaProp.enum = collectRadioEnum(form, fieldKey);\n const radioOneOf = collectRadioOneOf(form, fieldKey);\n if (radioOneOf.length > 0) schemaProp.oneOf = radioOneOf;\n }\n\n properties[fieldKey] = schemaProp;\n\n // Track id-keyed or aria-label-keyed fields for the interceptor\n if (!name) {\n fieldElements.set(fieldKey, control);\n }\n\n // Mark as required if the HTML attribute says so\n if (control.required) {\n required.push(fieldKey);\n }\n }\n\n // ARIA role-based controls (custom components not using native inputs)\n const ariaControls = collectAriaControls(form);\n const processedAriaRadioGroups = new Set<string>();\n\n for (const { el, role, key } of ariaControls) {\n if (properties[key]) continue; // already covered by a native control\n\n if (role === 'radio') {\n if (processedAriaRadioGroups.has(key)) continue;\n processedAriaRadioGroups.add(key);\n }\n\n const schemaProp = ariaRoleToSchema(el, role);\n schemaProp.title = inferAriaFieldTitle(el);\n const desc = inferAriaFieldDescription(el);\n if (desc) schemaProp.description = desc;\n\n properties[key] = schemaProp;\n fieldElements.set(key, el);\n\n if (el.getAttribute('aria-required') === 'true') {\n required.push(key);\n }\n }\n\n return { schema: { type: 'object', properties, required }, fieldElements };\n}\n\n/** Derive a schema key for a native control that lacks a name attribute */\nfunction resolveNativeControlFallbackKey(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string | null {\n const el = control as HTMLElement;\n if (el.dataset['webmcpName']) return sanitizeName(el.dataset['webmcpName']!);\n if (control.id) return sanitizeName(control.id);\n const label = control.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n return null;\n}\n\n/** Collect ARIA-role-based interactive elements inside a form, excluding native inputs */\nfunction collectAriaControls(form: HTMLFormElement): Array<{ el: Element; role: AriaRole; key: string }> {\n const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role=\"${r}\"]`).join(', ');\n const results: Array<{ el: Element; role: AriaRole; key: string }> = [];\n\n for (const el of Array.from(form.querySelectorAll(selector))) {\n // Skip native inputs \u2014 already handled above\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) continue;\n\n // Skip hidden elements\n if (el.getAttribute('aria-hidden') === 'true' || (el as HTMLElement).hidden) continue;\n\n const role = el.getAttribute('role') as AriaRole;\n const key = resolveAriaFieldKey(el);\n if (!key) continue;\n\n results.push({ el, role, key });\n }\n\n return results;\n}\n\n/** Derive a schema key from an ARIA element */\nfunction resolveAriaFieldKey(el: Element): string | null {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpName']) return sanitizeName(htmlEl.dataset['webmcpName']!);\n if (el.id) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return sanitizeName(text);\n }\n return null;\n}\n\nfunction inferAriaFieldTitle(el: Element): string {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpTitle']) return htmlEl.dataset['webmcpTitle']!;\n const label = el.getAttribute('aria-label');\n if (label) return label.trim();\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return text;\n }\n if (el.id) return humanizeName(el.id);\n return '';\n}\n\nfunction inferAriaFieldDescription(el: Element): string {\n const nativeParamDesc = el.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpDescription']) return htmlEl.dataset['webmcpDescription']!;\n const ariaDesc = el.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n const describedById = el.getAttribute('aria-describedby');\n if (describedById) {\n const text = document.getElementById(describedById)?.textContent?.trim();\n if (text) return text;\n }\n const placeholder = el.getAttribute('placeholder') ?? (el as HTMLElement).dataset?.['placeholder'];\n if (placeholder) return placeholder.trim();\n return '';\n}\n\nfunction inferFieldTitle(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. data-webmcp-title\n if ('dataset' in control && (control as HTMLElement).dataset['webmcpTitle']) {\n return (control as HTMLElement).dataset['webmcpTitle']!;\n }\n\n // 2. Associated <label> text\n const labelText = getAssociatedLabelText(control);\n if (labelText) return labelText;\n\n // 3. name attribute (humanised)\n if (control.name) return humanizeName(control.name);\n\n // 4. id attribute (humanised)\n if (control.id) return humanizeName(control.id);\n\n return '';\n}\n\nfunction inferFieldDescription(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Native toolparamdescription attribute (spec)\n const nativeParamDesc = control.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n\n // 2. data-webmcp-description\n const el = control as HTMLElement;\n if (el.dataset['webmcpDescription']) return el.dataset['webmcpDescription']!;\n\n // 2. aria-description or aria-describedby\n const ariaDesc = control.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n\n const describedById = control.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 3. placeholder (only as a last resort \u2014 can be noisy)\n if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n const ph = control.placeholder?.trim();\n if (ph && ph.length > 0) return ph;\n }\n\n // 4. Associated label text (if title didn't use it, use it for description)\n return '';\n}\n\nfunction getAssociatedLabelText(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Labels collection (for/id association)\n if (control.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (label) {\n const text = labelTextWithoutNested(label);\n if (text) return text;\n }\n }\n\n // 2. Wrapping <label>\n const parent = control.closest('label');\n if (parent) {\n const text = labelTextWithoutNested(parent);\n if (text) return text;\n }\n\n return '';\n}\n\nfunction labelTextWithoutNested(label: HTMLLabelElement): string {\n // Clone and remove any nested input/select/textarea before getting text\n const clone = label.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n return clone.textContent?.trim() ?? '';\n}\n\nfunction humanizeName(raw: string): string {\n return raw\n .replace(/[-_]/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input group analysis (inputs not inside a <form> element)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive ToolMetadata from a group of form controls that are NOT inside a <form>.\n * Used by discovery.ts's orphan-input scanner for pages like newsletter landing pages.\n */\nexport function analyzeOrphanInputGroup(\n container: Element,\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolMetadata {\n const name = inferOrphanToolName(container, submitBtn);\n const description = inferOrphanToolDescription(container);\n const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);\n return { name, description, inputSchema, fieldElements };\n}\n\nfunction inferOrphanToolName(\n container: Element,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): string {\n // 1. Submit button text\n if (submitBtn) {\n const text =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : submitBtn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return sanitizeName(text);\n }\n\n // 2. Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n if (heading) return sanitizeName(heading);\n\n // 3. Page title\n const title = document.title?.trim();\n if (title) return sanitizeName(title);\n\n return `form_${++formIndex}`;\n}\n\nfunction inferOrphanToolDescription(container: Element): string {\n // Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle && heading !== pageTitle) return `${heading} on ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n return 'Submit form';\n}\n\n/**\n * Generic heading search starting from any Element (not just HTMLFormElement).\n * Walks up the DOM checking preceding siblings and parent elements.\n */\nfunction getNearestHeadingTextFrom(el: Element): string {\n // Also check headings inside the container itself\n const inner = el.querySelector('h1, h2, h3');\n if (inner?.textContent?.trim()) return inner.textContent.trim();\n\n let node: Element | null = el;\n while (node) {\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n if (!node || node === document.body) break;\n }\n return '';\n}\n\n/**\n * Build a JSON Schema from an array of form controls (no <form> context needed).\n * Reuses the same field title/description inference as buildSchema().\n */\nfunction buildSchemaFromInputs(\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,\n): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n const processedRadioGroups = new Set<string>();\n\n for (const control of inputs) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue;\n\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n properties[fieldKey] = schemaProp;\n if (!name) fieldElements.set(fieldKey, control);\n if (control.required) required.push(fieldKey);\n }\n\n return { schema: { type: 'object', properties, required }, fieldElements };\n}\n", "/**\n * discovery.ts \u2014 Form scanning & MutationObserver for SPA support\n */\n\nimport { ResolvedConfig } from './config.js';\nimport { analyzeForm, analyzeOrphanInputGroup } from './analyzer.js';\nimport { registerFormTool, unregisterFormTool, isWebMCPSupported } from './registry.js';\nimport { buildExecuteHandler, fillElement } from './interceptor.js';\nimport { enrichMetadata } from './enhancer.js';\nimport { ARIA_ROLES_TO_SCAN } from './schema.js';\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type FormLifecycleEvent = CustomEvent<{\n form: HTMLFormElement;\n toolName: string;\n}>;\n\nfunction emit(type: 'form:registered' | 'form:unregistered', form: HTMLFormElement, toolName: string): void {\n window.dispatchEvent(\n new CustomEvent(type, { detail: { form, toolName } }) as FormLifecycleEvent,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Registration helpers\n// ---------------------------------------------------------------------------\n\n/** Check whether a form should be excluded per config */\nfunction isExcluded(form: HTMLFormElement, config: ResolvedConfig): boolean {\n // Skip forms with data-no-webmcp\n if (form.dataset['noWebmcp'] !== undefined) return true;\n // Skip per config exclude list\n for (const selector of config.exclude) {\n try {\n if (form.matches(selector)) return true;\n } catch {\n // invalid selector \u2014 ignore\n }\n }\n return false;\n}\n\nasync function registerForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n if (isExcluded(form, config)) return;\n\n // Find matching override (first matching selector wins)\n let override;\n for (const [selector, ovr] of Object.entries(config.overrides)) {\n try {\n if (form.matches(selector)) {\n override = ovr;\n break;\n }\n } catch {\n // invalid selector\n }\n }\n\n let metadata = analyzeForm(form, override);\n if (config.enhance) {\n if (config.debug) console.debug(`[auto-webmcp] Enriching: ${metadata.name}\u2026`);\n metadata = await enrichMetadata(metadata, config.enhance);\n }\n\n if (config.debug) {\n warnToolQuality(metadata.name, metadata.description);\n }\n\n const execute = buildExecuteHandler(form, config, metadata.name, metadata);\n\n await registerFormTool(form, metadata, execute);\n registeredForms.add(form);\n registeredFormCount++;\n\n if (config.debug) {\n console.debug(`[auto-webmcp] Registered: ${metadata.name}`, metadata);\n }\n\n emit('form:registered', form, metadata.name);\n}\n\nasync function unregisterForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n const { getRegisteredToolName } = await import('./registry.js');\n const name = getRegisteredToolName(form);\n if (!name) return;\n\n await unregisterFormTool(form);\n registeredForms.delete(form);\n\n if (config.debug) {\n console.debug(`[auto-webmcp] Unregistered: ${name}`);\n }\n\n emit('form:unregistered', form, name);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver\n// ---------------------------------------------------------------------------\n\nlet observer: MutationObserver | null = null;\n\n/** Set of currently registered forms, used to detect lazy-rendered child inputs. */\nconst registeredForms = new WeakSet<HTMLFormElement>();\n\n/** Count of forms registered in the current discovery session. Reset on each startDiscovery call. */\nlet registeredFormCount = 0;\n\n/** Debounce timers for re-analysis when inputs are added to existing forms. */\nconst reAnalysisTimers = new Map<HTMLFormElement, ReturnType<typeof setTimeout>>();\nconst RE_ANALYSIS_DEBOUNCE_MS = 300;\n\n/** Returns true if node is (or contains) an input-like or ARIA-role element. */\nfunction isInterestingNode(node: Element): boolean {\n const tag = node.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;\n const role = node.getAttribute('role');\n if (role && (ARIA_ROLES_TO_SCAN as readonly string[]).includes(role)) return true;\n if (node.querySelector('input, textarea, select')) return true;\n for (const r of ARIA_ROLES_TO_SCAN) {\n if (node.querySelector(`[role=\"${r}\"]`)) return true;\n }\n return false;\n}\n\nfunction scheduleReAnalysis(form: HTMLFormElement, config: ResolvedConfig): void {\n const existing = reAnalysisTimers.get(form);\n if (existing) clearTimeout(existing);\n reAnalysisTimers.set(\n form,\n setTimeout(() => {\n reAnalysisTimers.delete(form);\n void registerForm(form, config);\n }, RE_ANALYSIS_DEBOUNCE_MS),\n );\n}\n\nfunction startObserver(config: ResolvedConfig): void {\n if (observer) return;\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element)) continue;\n\n if (node instanceof HTMLFormElement) {\n void registerForm(node, config);\n continue;\n }\n\n // Newly added child inside an already-registered form?\n const parentForm = node.closest('form');\n if (parentForm instanceof HTMLFormElement && registeredForms.has(parentForm) && isInterestingNode(node)) {\n scheduleReAnalysis(parentForm, config);\n }\n\n // New forms nested inside the added subtree\n for (const form of Array.from(node.querySelectorAll<HTMLFormElement>('form'))) {\n void registerForm(form, config);\n }\n }\n\n for (const node of mutation.removedNodes) {\n if (!(node instanceof Element)) continue;\n\n const forms = node instanceof HTMLFormElement\n ? [node]\n : Array.from(node.querySelectorAll<HTMLFormElement>('form'));\n\n for (const form of forms) {\n void unregisterForm(form, config);\n }\n }\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true });\n}\n\n// ---------------------------------------------------------------------------\n// SPA route change support\n// ---------------------------------------------------------------------------\n\nfunction listenForRouteChanges(config: ResolvedConfig): void {\n // Hash changes\n window.addEventListener('hashchange', () => scanForms(config));\n\n // History API (pushState / replaceState)\n const original = {\n pushState: history.pushState.bind(history),\n replaceState: history.replaceState.bind(history),\n };\n\n history.pushState = function (...args) {\n original.pushState(...args);\n scanForms(config);\n };\n\n history.replaceState = function (...args) {\n original.replaceState(...args);\n scanForms(config);\n };\n\n window.addEventListener('popstate', () => scanForms(config));\n}\n\n// ---------------------------------------------------------------------------\n// Main scan\n// ---------------------------------------------------------------------------\n\nasync function scanForms(config: ResolvedConfig): Promise<void> {\n const forms = Array.from(document.querySelectorAll<HTMLFormElement>('form'));\n await Promise.allSettled(forms.map((form) => registerForm(form, config)));\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input scanner (fallback for pages with no <form> elements)\n// ---------------------------------------------------------------------------\n\n/** Input types that are never useful to expose to agents */\nconst ORPHAN_EXCLUDED_TYPES = new Set([\n 'password', 'hidden', 'file', 'submit', 'reset', 'button', 'image',\n]);\n\n/**\n * Find all visible form controls that are NOT inside a <form> element,\n * group them by their nearest ancestor that also contains a submit button,\n * and register each group as a WebMCP tool.\n *\n * This covers common patterns on landing pages and Ghost blogs where the\n * subscribe/search UI is built from plain inputs + buttons without a <form> tag.\n */\nasync function scanOrphanInputs(config: ResolvedConfig): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const SUBMIT_BTN_SELECTOR = '[type=\"submit\"]:not([disabled]), button:not([type]):not([disabled])';\n const SUBMIT_TEXT_RE = /subscribe|submit|sign[\\s-]?up|send|join|go|search/i;\n\n // Collect visible inputs that are not inside a <form>\n const orphanInputs = Array.from(\n document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input:not(form input), textarea:not(form textarea), select:not(form select)',\n ),\n ).filter((el) => {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {\n return false;\n }\n const rect = el.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n });\n\n if (orphanInputs.length === 0) return;\n\n // Group inputs by the nearest ancestor that also contains a submit button.\n // Walk up from each input until we find a container with a submit-like button.\n const groups = new Map<Element, Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>>();\n\n for (const input of orphanInputs) {\n let container: Element | null = input.parentElement;\n let foundContainer: Element = input.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(input);\n }\n\n for (const [container, inputs] of groups) {\n // Pick the last visible submit button within the container (same logic as\n // handleCallTool in background.js: primary action is always the last one).\n const allCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n\n let submitBtn: HTMLButtonElement | HTMLInputElement | null =\n (allCandidates[allCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n\n // Fallback: nearest button with submit-like text anywhere on the page\n if (!submitBtn) {\n const pageBtns = Array.from(document.querySelectorAll<HTMLButtonElement>('button')).filter(\n (b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && SUBMIT_TEXT_RE.test(b.textContent ?? '');\n },\n );\n submitBtn = pageBtns[pageBtns.length - 1] ?? null;\n }\n\n const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);\n\n // Build key \u2192 element pairs for the execute handler\n const inputPairs: Array<{ key: string; el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement }> = [];\n const schemaProps = metadata.inputSchema.properties;\n for (const el of inputs) {\n const key =\n el.name ||\n (el as HTMLElement).dataset['webmcpName'] ||\n el.id ||\n el.getAttribute('aria-label') ||\n null;\n const safeKey = key\n ? key.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 64)\n : null;\n if (safeKey && schemaProps[safeKey]) {\n inputPairs.push({ key: safeKey, el });\n }\n }\n\n const toolName = metadata.name;\n const execute = async (params: Record<string, unknown>): Promise<{ content: Array<{ type: 'text'; text: string }> }> => {\n for (const { key, el } of inputPairs) {\n if (params[key] !== undefined) {\n fillElement(el, params[key]);\n }\n }\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n return { content: [{ type: 'text', text: 'Fields filled. Ready to submit.' }] };\n };\n\n try {\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n if (config.debug) {\n console.debug(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);\n }\n } catch {\n // Best-effort\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction warnToolQuality(name: string, description: string): void {\n if (/^form_\\d+$|^submit$|^form$/.test(name)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" has a generic name. Consider adding a toolname or data-webmcp-name attribute.`);\n }\n if (!description || description === 'Submit form') {\n console.warn(`[auto-webmcp] Tool \"${name}\" has no meaningful description.`);\n }\n if (/don'?t|do not|never|avoid|not for/i.test(description)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" description contains negative instructions. Per spec best practices, prefer positive descriptions.`);\n }\n}\n\nexport async function startDiscovery(config: ResolvedConfig): Promise<void> {\n if (document.readyState === 'loading') {\n await new Promise<void>((resolve) =>\n document.addEventListener('DOMContentLoaded', () => resolve(), { once: true }),\n );\n }\n\n registeredFormCount = 0;\n startObserver(config);\n listenForRouteChanges(config);\n await scanForms(config);\n\n // If no form-based tools were found, try orphan inputs (inputs outside <form> elements).\n // This covers newsletter subscribe widgets, search bars, and other formless UI patterns.\n if (registeredFormCount === 0) {\n await scanOrphanInputs(config);\n }\n}\n\nexport function stopDiscovery(): void {\n observer?.disconnect();\n observer = null;\n}\n", "/**\n * interceptor.ts \u2014 Form submit interception for agent-invoked submissions\n *\n * WebMCP's `execute` callback receives form parameters and is expected to\n * return a result. This module bridges the gap: it fills form fields with\n * the agent-supplied values, submits the form, and resolves the execute\n * promise with a structured response.\n */\n\nimport { ResolvedConfig } from './config.js';\nimport type { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// Extended SubmitEvent types (WebMCP additions)\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface SubmitEvent {\n /** True when the form was submitted by an AI agent via WebMCP */\n agentInvoked?: boolean;\n /** Call to return a structured result to the agent */\n respondWith?: (promise: Promise<unknown>) => void;\n }\n}\n\nexport interface ExecuteResult {\n content: Array<{ type: 'text'; text: string }>;\n}\n\ntype Resolver = (result: ExecuteResult) => void;\ntype Rejecter = (error: Error) => void;\n\n/** Per-form pending execute promises */\nconst pendingExecutions = new WeakMap<\n HTMLFormElement,\n { resolve: Resolver; reject: Rejecter }\n>();\n\n/** Per-form last-used params (for serializing id-keyed fields not in FormData) */\nconst lastParams = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/** Per-form field element map (key \u2192 DOM element for non-name-addressable fields) */\nconst formFieldElements = new WeakMap<HTMLFormElement, Map<string, Element>>();\n\n// React-compatible native prototype setters (retrieved once at module init).\n// Using these bypasses React's controlled-input tracking so onChange fires correctly.\nconst _inputValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\nconst _textareaValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;\nconst _checkedSetter: ((v: boolean) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked')?.set;\n\n/**\n * Build an `execute` function for a form tool.\n *\n * When the agent calls execute(params):\n * 1. Fills form fields with the supplied params\n * 2. Fires a submit event (or auto-submits if configured)\n * 3. Resolves with structured form data once submitted\n */\nexport function buildExecuteHandler(\n form: HTMLFormElement,\n config: ResolvedConfig,\n toolName: string,\n metadata?: ToolMetadata,\n): (params: Record<string, unknown>) => Promise<ExecuteResult> {\n // Store field element map for this form\n if (metadata?.fieldElements) {\n formFieldElements.set(form, metadata.fieldElements);\n }\n\n // Attach submit/reset listeners once per form\n attachSubmitInterceptor(form, toolName);\n\n return async (params: Record<string, unknown>): Promise<ExecuteResult> => {\n fillFormFields(form, params);\n\n // Dispatch toolactivated event per spec\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n return new Promise<ExecuteResult>((resolve, reject) => {\n pendingExecutions.set(form, { resolve, reject });\n\n if (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) {\n // Wait 300 ms before submitting so React/Vue/etc. can commit any state\n // updates queued by the InputEvents we dispatched during fill.\n // Frameworks like React 18 batch state updates asynchronously \u2014 if we\n // call requestSubmit() synchronously the framework reads stale state.\n setTimeout(() => {\n // Re-fill after React has committed, in case it reset values.\n fillFormFields(form, params);\n\n // If the stored form was remounted (React, Turbo etc.), find the live\n // form via the submit button so requestSubmit() reaches the real DOM.\n let submitForm: HTMLFormElement = form;\n if (!form.isConnected) {\n const liveBtn = document.querySelector<HTMLElement>(\n 'button[type=\"submit\"]:not([disabled]), input[type=\"submit\"]:not([disabled])',\n );\n const found = liveBtn?.closest('form') as HTMLFormElement | null;\n if (found) {\n submitForm = found;\n pendingExecutions.set(submitForm, { resolve, reject });\n attachSubmitInterceptor(submitForm, toolName);\n }\n }\n submitForm.requestSubmit();\n }, 300);\n }\n // Otherwise: the form stays filled; human clicks submit,\n // which fires the submit event interceptor below.\n });\n };\n}\n\nfunction attachSubmitInterceptor(form: HTMLFormElement, toolName: string): void {\n // Guard against attaching multiple times\n if ((form as unknown as Record<string, unknown>)['__awmcp_intercepted']) return;\n (form as unknown as Record<string, unknown>)['__awmcp_intercepted'] = true;\n\n form.addEventListener('submit', (e: SubmitEvent) => {\n const pending = pendingExecutions.get(form);\n if (!pending) return; // Normal human submission \u2014 do nothing\n\n // Agent-invoked path\n const { resolve } = pending;\n pendingExecutions.delete(form);\n\n const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));\n const text = `Form submitted. Fields: ${JSON.stringify(formData)}`;\n const result: ExecuteResult = { content: [{ type: 'text', text }] };\n\n if (e.agentInvoked && typeof e.respondWith === 'function') {\n // Native WebMCP path: use respondWith to return to browser\n e.preventDefault();\n e.respondWith(Promise.resolve(result));\n }\n resolve(result);\n });\n\n // Dispatch toolcancel when form is reset\n form.addEventListener('reset', () => {\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Field filling\n// ---------------------------------------------------------------------------\n\nfunction setReactValue(el: HTMLInputElement | HTMLTextAreaElement, v: string): void {\n el.focus();\n // Select all existing text so the insert replaces it\n el.select?.();\n\n // execCommand('insertText') simulates real typing \u2014 triggers native browser\n // input events that every framework (React, Catalyst, Stimulus, Vue, etc.)\n // listens to. Most reliable cross-framework approach.\n // Guard: execCommand can return true but silently fail for inputs inside\n // shadow DOM (e.g. GitHub's Catalyst components), so verify the value landed.\n if (document.execCommand('insertText', false, v) && el.value === v) {\n return;\n }\n\n // Fallback: native prototype setter (bypasses React controlled-input cache)\n const setter = el instanceof HTMLTextAreaElement ? _textareaValueSetter : _inputValueSetter;\n if (setter) {\n setter.call(el, v);\n } else {\n el.value = v;\n }\n el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: v }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction setReactChecked(el: HTMLInputElement, checked: boolean): void {\n if (_checkedSetter) {\n _checkedSetter.call(el, checked);\n } else {\n el.checked = checked;\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Recursively search shadow roots for a native form control matching selector.\n * GitHub's Catalyst and other custom-element frameworks render inputs inside\n * shadow DOM that form.querySelector() cannot pierce.\n */\nfunction findInShadowRoots(\n root: Document | ShadowRoot,\n selector: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n for (const host of Array.from(root.querySelectorAll('*'))) {\n const sr = (host as Element).shadowRoot;\n if (!sr) continue;\n const found = sr.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(selector);\n if (found) return found;\n const deeper = findInShadowRoots(sr, selector);\n if (deeper) return deeper;\n }\n return null;\n}\n\n/** Find a native form control by name or id, including inside shadow DOM. */\nfunction findNativeField(\n form: HTMLFormElement,\n key: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n const esc = CSS.escape(key);\n // Light DOM first (fast path)\n const light =\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(`[name=\"${esc}\"]`) ??\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n `input#${esc}, textarea#${esc}, select#${esc}`,\n );\n if (light) return light;\n // Shadow DOM fallback: search all shadow roots in the document\n return (\n findInShadowRoots(document, `[name=\"${esc}\"]`) ??\n findInShadowRoots(document, `input#${esc}, textarea#${esc}, select#${esc}`)\n );\n}\n\nfunction fillFormFields(form: HTMLFormElement, params: Record<string, unknown>): void {\n lastParams.set(form, params);\n const fieldEls = formFieldElements.get(form);\n\n for (const [key, value] of Object.entries(params)) {\n const input = findNativeField(form, key);\n\n if (input) {\n if (input instanceof HTMLInputElement) {\n fillInput(input, form, key, value);\n } else if (input instanceof HTMLTextAreaElement) {\n setReactValue(input, String(value ?? ''));\n } else if (input instanceof HTMLSelectElement) {\n input.value = String(value ?? '');\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n continue;\n }\n\n // Fall back to id-keyed or ARIA-role element from the field map.\n const ariaEl = fieldEls?.get(key);\n if (ariaEl) {\n // If the stored element was remounted by a framework (React, Catalyst etc.),\n // find a fresh reference using its actual DOM id (which may differ from the\n // sanitized schema key \u2014 e.g. \"repository-name-input\" \u2192 key \"repository_name_input\").\n let effectiveEl: Element = ariaEl;\n if (!ariaEl.isConnected) {\n const elId = (ariaEl as HTMLElement).id;\n if (elId) {\n const fresh =\n document.getElementById(elId) ??\n findInShadowRoots(document, `#${CSS.escape(elId)}`);\n if (fresh) effectiveEl = fresh;\n }\n }\n if (effectiveEl instanceof HTMLInputElement) {\n fillInput(effectiveEl, form, key, value);\n } else if (effectiveEl instanceof HTMLTextAreaElement) {\n setReactValue(effectiveEl, String(value ?? ''));\n } else if (effectiveEl instanceof HTMLSelectElement) {\n effectiveEl.value = String(value ?? '');\n effectiveEl.dispatchEvent(new Event('change', { bubbles: true }));\n } else {\n fillAriaField(effectiveEl, value);\n }\n }\n }\n}\n\nfunction fillInput(\n input: HTMLInputElement,\n form: HTMLFormElement,\n key: string,\n value: unknown,\n): void {\n const type = input.type.toLowerCase();\n\n if (type === 'checkbox') {\n setReactChecked(input, Boolean(value));\n return;\n }\n\n if (type === 'radio') {\n const esc = CSS.escape(key);\n const radios = form.querySelectorAll<HTMLInputElement>(\n `input[type=\"radio\"][name=\"${esc}\"]`,\n );\n for (const radio of radios) {\n if (radio.value === String(value)) {\n if (_checkedSetter) {\n _checkedSetter.call(radio, true);\n } else {\n radio.checked = true;\n }\n radio.dispatchEvent(new Event('change', { bubbles: true }));\n break;\n }\n }\n return;\n }\n\n setReactValue(input, String(value ?? ''));\n}\n\nfunction fillAriaField(el: Element, value: unknown): void {\n const role = el.getAttribute('role');\n\n if (role === 'checkbox' || role === 'switch') {\n el.setAttribute('aria-checked', String(Boolean(value)));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radio') {\n el.setAttribute('aria-checked', 'true');\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n // textbox, combobox, searchbox, spinbutton\n const htmlEl = el as HTMLElement;\n if (htmlEl.isContentEditable) {\n htmlEl.textContent = String(value ?? '');\n }\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\nfunction serializeFormData(\n form: HTMLFormElement,\n params?: Record<string, unknown>,\n fieldEls?: Map<string, Element>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const data = new FormData(form);\n\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n // Multiple values \u2192 array\n const existing = result[key];\n if (Array.isArray(existing)) {\n existing.push(val);\n } else {\n result[key] = [existing, val];\n }\n } else {\n result[key] = val;\n }\n }\n\n // Supplement with id-keyed or ARIA-keyed fields not captured by FormData\n if (params) {\n for (const key of Object.keys(params)) {\n if (key in result) continue;\n const el =\n findNativeField(form, key) ??\n fieldEls?.get(key) ??\n null;\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n result[key] = el.checked;\n } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n result[key] = el.value;\n } else {\n // ARIA element\n const role = el.getAttribute('role');\n if (role === 'checkbox' || role === 'switch') {\n result[key] = el.getAttribute('aria-checked') === 'true';\n } else {\n result[key] = (el as HTMLElement).textContent?.trim() ?? '';\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Fill a single form control or ARIA element with the given value.\n * Exported for use by orphan-input (formless) tool handlers in discovery.ts.\n */\nexport function fillElement(\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | Element,\n value: unknown,\n): void {\n if (el instanceof HTMLInputElement) {\n const type = el.type.toLowerCase();\n if (type === 'checkbox') {\n setReactChecked(el, Boolean(value));\n } else if (type === 'radio') {\n if (el.value === String(value)) {\n if (_checkedSetter) _checkedSetter.call(el, true);\n else el.checked = true;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n } else {\n setReactValue(el, String(value ?? ''));\n }\n } else if (el instanceof HTMLTextAreaElement) {\n setReactValue(el, String(value ?? ''));\n } else if (el instanceof HTMLSelectElement) {\n el.value = String(value ?? '');\n el.dispatchEvent(new Event('change', { bubbles: true }));\n } else {\n fillAriaField(el, value);\n }\n}\n\nfunction resolveFormAction(form: HTMLFormElement): string {\n if (form.action) {\n try {\n return new URL(form.action, window.location.href).href;\n } catch {\n // ignore\n }\n }\n return window.location.href;\n}\n", "/**\n * enhancer.ts \u2014 Optional LLM-based description enrichment\n *\n * Calls a small LLM to generate richer tool descriptions than heuristics\n * alone can provide. Activated only when config.enhance is set.\n */\n\nimport { ToolMetadata } from './analyzer.js';\nimport { EnhancerConfig } from './config.js';\n\nexport async function enrichMetadata(\n metadata: ToolMetadata,\n enhancer: EnhancerConfig,\n): Promise<ToolMetadata> {\n try {\n const enriched = await callLLM(metadata, enhancer);\n return { ...metadata, description: enriched };\n } catch (err) {\n // Enhancement is optional \u2014 fall back to heuristic description\n console.warn('[auto-webmcp] Enrichment failed, using heuristic description:', err);\n return metadata;\n }\n}\n\nasync function callLLM(metadata: ToolMetadata, config: EnhancerConfig): Promise<string> {\n const prompt = buildPrompt(metadata);\n\n if (config.provider === 'claude') {\n return callClaude(prompt, config);\n } else {\n return callGemini(prompt, config);\n }\n}\n\nfunction buildPrompt(metadata: ToolMetadata): string {\n const fields = Object.entries(metadata.inputSchema.properties)\n .map(([name, prop]) => `- ${prop.title ?? name} (${prop.type}): ${prop.description ?? ''}`)\n .join('\\n');\n\n return `You are helping describe a web form as an AI tool. Given this form information:\n\nName: ${metadata.name}\nCurrent description: ${metadata.description}\nFields:\n${fields}\n\nWrite 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.`;\n}\n\nasync function callClaude(prompt: string, config: EnhancerConfig): Promise<string> {\n const model = config.model ?? 'claude-haiku-4-5-20251001';\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'x-api-key': config.apiKey,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n model,\n max_tokens: 150,\n messages: [{ role: 'user', content: prompt }],\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Claude API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n content: Array<{ type: string; text: string }>;\n };\n\n return data.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text)\n .join('')\n .trim();\n}\n\nasync function callGemini(prompt: string, config: EnhancerConfig): Promise<string> {\n const model = config.model ?? 'gemini-1.5-flash';\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${config.apiKey}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n contents: [{ parts: [{ text: prompt }] }],\n generationConfig: { maxOutputTokens: 150, temperature: 0.2 },\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Gemini API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n candidates: Array<{\n content: { parts: Array<{ text: string }> };\n }>;\n };\n\n return data.candidates[0]?.content.parts.map((p) => p.text).join('').trim() ?? '';\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * registry.ts \u2014 Wrapper around navigator.modelContext WebMCP Imperative API\n */\n\nimport { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// WebMCP type declarations (not yet in TypeScript DOM lib)\n// ---------------------------------------------------------------------------\n\nexport interface WebMCPTool {\n name: string;\n description: string;\n inputSchema: object;\n execute: (params: Record<string, unknown>) => Promise<unknown>;\n}\n\ndeclare global {\n interface Navigator {\n modelContext?: {\n registerTool(tool: WebMCPTool): Promise<void>;\n unregisterTool(name: string): Promise<void>;\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/** Tracks registered tools: form element \u2192 tool name */\nconst registeredTools = new Map<HTMLFormElement, string>();\n\n/** True if the browser supports navigator.modelContext */\nexport function isWebMCPSupported(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.modelContext !== 'undefined';\n}\n\n/**\n * Register a form as a WebMCP tool.\n * Silently no-ops if WebMCP is not supported.\n */\nexport async function registerFormTool(\n form: HTMLFormElement,\n metadata: ToolMetadata,\n execute: (params: Record<string, unknown>) => Promise<unknown>,\n): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n // Unregister any previously-registered tool for this same form element\n const existing = registeredTools.get(form);\n if (existing) {\n await unregisterFormTool(form);\n }\n\n try {\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n } catch {\n // Chrome may hold a stale registration from a previous page load.\n // Unregister by name and retry once.\n try {\n await navigator.modelContext!.unregisterTool(metadata.name);\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n } catch {\n // Give up Chrome registration \u2014 local handlers and form:registered still work.\n }\n }\n\n registeredTools.set(form, metadata.name);\n}\n\n/**\n * Unregister the WebMCP tool associated with a form element.\n * Silently no-ops if not registered or WebMCP not supported.\n */\nexport async function unregisterFormTool(form: HTMLFormElement): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const name = registeredTools.get(form);\n if (!name) return;\n\n try {\n await navigator.modelContext!.unregisterTool(name);\n } catch {\n // Tool may have already been removed \u2014 ignore\n }\n\n registeredTools.delete(form);\n}\n\n/** Get the registered tool name for a form, if any */\nexport function getRegisteredToolName(form: HTMLFormElement): string | undefined {\n return registeredTools.get(form);\n}\n\n/** Return a snapshot of all currently registered form\u2192name pairs */\nexport function getAllRegisteredTools(): Array<{ form: HTMLFormElement; name: string }> {\n return Array.from(registeredTools.entries()).map(([form, name]) => ({ form, name }));\n}\n\n/** Unregister all tools (e.g. on teardown) */\nexport async function unregisterAll(): Promise<void> {\n const entries = Array.from(registeredTools.entries());\n await Promise.all(entries.map(([form]) => unregisterFormTool(form)));\n}\n", "/**\n * index.ts \u2014 Entry point & public API for auto-webmcp\n *\n * Zero-config drop-in:\n * <script src=\"auto-webmcp.iife.js\"></script>\n *\n * ESM usage:\n * import { autoWebMCP } from 'auto-webmcp';\n * autoWebMCP({ exclude: ['#login-form'] });\n */\n\nimport { AutoWebMCPConfig, resolveConfig } from './config.js';\nimport { startDiscovery, stopDiscovery } from './discovery.js';\nimport { unregisterAll, getAllRegisteredTools, isWebMCPSupported } from './registry.js';\n\nexport type { AutoWebMCPConfig } from './config.js';\nexport type { ToolMetadata } from './analyzer.js';\nexport type { JsonSchema, JsonSchemaProperty } from './schema.js';\n\nexport interface AutoWebMCPHandle {\n /** Stop observing and unregister all tools */\n destroy: () => Promise<void>;\n /** Return all currently registered tools */\n getTools: () => Array<{ form: HTMLFormElement; name: string }>;\n /** True if running in a WebMCP-capable browser */\n isSupported: boolean;\n}\n\n/**\n * Initialize auto-webmcp.\n *\n * @param config \u2014 Optional configuration (all fields optional)\n * @returns A handle to inspect or tear down the instance\n */\nexport async function autoWebMCP(config?: AutoWebMCPConfig): Promise<AutoWebMCPHandle> {\n const resolved = resolveConfig(config);\n\n if (resolved.debug) {\n console.debug('[auto-webmcp] Initializing', {\n webmcpSupported: isWebMCPSupported(),\n config: resolved,\n });\n }\n\n await startDiscovery(resolved);\n\n return {\n destroy: async () => {\n stopDiscovery();\n await unregisterAll();\n },\n getTools: getAllRegisteredTools,\n isSupported: isWebMCPSupported(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auto-init for IIFE / script-tag usage\n// ---------------------------------------------------------------------------\n\n// When loaded as a <script> tag, auto-initialize with zero config.\n// Users can prevent this by setting window.__AUTO_WEBMCP_NO_AUTOINIT = true\n// before the script loads.\n\nif (\n typeof window !== 'undefined' &&\n !(window as unknown as Record<string, unknown>)['__AUTO_WEBMCP_NO_AUTOINIT']\n) {\n void autoWebMCP();\n}\n", "/**\n * config.ts \u2014 User configuration merging & defaults\n */\n\nexport interface EnhancerConfig {\n provider: 'gemini' | 'claude';\n apiKey: string;\n model?: string;\n}\n\nexport interface FormOverride {\n name?: string;\n description?: string;\n}\n\nexport interface AutoWebMCPConfig {\n /**\n * CSS selectors for forms to skip. E.g. ['#login-form', '[data-no-webmcp]']\n */\n exclude?: string[];\n\n /**\n * If true, agent-invoked forms are auto-submitted without human confirmation.\n * Default: false\n */\n autoSubmit?: boolean;\n\n /**\n * Optional AI enrichment for richer tool descriptions.\n */\n enhance?: EnhancerConfig;\n\n /**\n * Per-form name/description overrides keyed by CSS selector.\n */\n overrides?: Record<string, FormOverride>;\n\n /**\n * Log registered tools to console on init. Default: false\n */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n exclude: string[];\n autoSubmit: boolean;\n enhance: EnhancerConfig | null;\n overrides: Record<string, FormOverride>;\n debug: boolean;\n}\n\nexport function resolveConfig(userConfig?: AutoWebMCPConfig): ResolvedConfig {\n return {\n exclude: userConfig?.exclude ?? [],\n autoSubmit: userConfig?.autoSubmit ?? false,\n enhance: userConfig?.enhance ?? null,\n overrides: userConfig?.overrides ?? {},\n debug: userConfig?.debug ?? false,\n };\n}\n", "/**\n * schema.ts \u2014 HTML input type \u2192 JSON Schema type mapping\n */\n\nexport const ARIA_ROLES_TO_SCAN = [\n 'textbox', 'combobox', 'checkbox', 'radio', 'switch',\n 'spinbutton', 'searchbox', 'slider',\n] as const;\n\nexport type AriaRole = typeof ARIA_ROLES_TO_SCAN[number];\n\nexport interface JsonSchemaProperty {\n type: string;\n format?: string;\n description?: string;\n title?: string;\n enum?: string[];\n oneOf?: Array<{ const: string; title: string }>;\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface JsonSchema {\n type: 'object';\n properties: Record<string, JsonSchemaProperty>;\n required: string[];\n}\n\n/** Maps an HTML <input type> to a JSON Schema property base */\nexport function inputTypeToSchema(\n input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): JsonSchemaProperty | null {\n if (input instanceof HTMLInputElement) {\n return mapInputElement(input);\n }\n if (input instanceof HTMLTextAreaElement) {\n return { type: 'string' };\n }\n if (input instanceof HTMLSelectElement) {\n return mapSelectElement(input);\n }\n return null;\n}\n\nfunction mapInputElement(input: HTMLInputElement): JsonSchemaProperty | null {\n const type = input.type.toLowerCase();\n\n switch (type) {\n case 'text':\n case 'search':\n case 'tel':\n return buildStringSchema(input);\n\n case 'email':\n return { ...buildStringSchema(input), format: 'email' };\n\n case 'url':\n return { ...buildStringSchema(input), format: 'uri' };\n\n case 'number':\n case 'range': {\n const prop: JsonSchemaProperty = { type: 'number' };\n if (input.min !== '') prop.minimum = parseFloat(input.min);\n if (input.max !== '') prop.maximum = parseFloat(input.max);\n return prop;\n }\n\n case 'date':\n return { type: 'string', format: 'date' };\n\n case 'datetime-local':\n return { type: 'string', format: 'date-time' };\n\n case 'time':\n return { type: 'string', format: 'time' };\n\n case 'month':\n return { type: 'string', pattern: '^\\\\d{4}-\\\\d{2}$' };\n\n case 'week':\n return { type: 'string', pattern: '^\\\\d{4}-W\\\\d{2}$' };\n\n case 'color':\n return { type: 'string', pattern: '^#[0-9a-fA-F]{6}$' };\n\n case 'checkbox':\n return { type: 'boolean' };\n\n case 'radio':\n // Radio groups are handled at the form level in analyzer.ts\n return { type: 'string' };\n\n case 'file':\n case 'hidden':\n case 'submit':\n case 'reset':\n case 'button':\n case 'image':\n // These are not exposed to agents\n return null;\n\n case 'password':\n // Skip passwords \u2014 never expose to agents\n return null;\n\n default:\n return { type: 'string' };\n }\n}\n\nfunction buildStringSchema(input: HTMLInputElement): JsonSchemaProperty {\n const prop: JsonSchemaProperty = { type: 'string' };\n if (input.minLength > 0) prop.minLength = input.minLength;\n if (input.maxLength > 0 && input.maxLength !== 524288) prop.maxLength = input.maxLength;\n if (input.pattern) prop.pattern = input.pattern;\n return prop;\n}\n\nfunction mapSelectElement(select: HTMLSelectElement): JsonSchemaProperty {\n const filtered = Array.from(select.options).filter((o) => o.value !== '');\n\n if (filtered.length === 0) {\n return { type: 'string' };\n }\n\n const enumValues = filtered.map((o) => o.value);\n const oneOf = filtered.map((o) => ({ const: o.value, title: o.text.trim() || o.value }));\n\n return { type: 'string', enum: enumValues, oneOf };\n}\n\n/** Collect all radio button values for a given name within a form */\nexport function collectRadioEnum(form: HTMLFormElement, name: string): string[] {\n const radios = Array.from(\n form.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][name=\"${CSS.escape(name)}\"]`),\n );\n return radios.map((r) => r.value).filter((v) => v !== '');\n}\n\n/** Collect radio button values + label titles as oneOf entries */\nexport function collectRadioOneOf(\n form: HTMLFormElement,\n name: string,\n): Array<{ const: string; title: string }> {\n const radios = Array.from(\n form.querySelectorAll<HTMLInputElement>(`input[type=\"radio\"][name=\"${CSS.escape(name)}\"]`),\n ).filter((r) => r.value !== '');\n\n return radios.map((r) => {\n const title = getRadioLabelText(r);\n return { const: r.value, title: title || r.value };\n });\n}\n\n/** Maps an ARIA role element to a JSON Schema property */\nexport function ariaRoleToSchema(el: Element, role: AriaRole): JsonSchemaProperty {\n switch (role) {\n case 'checkbox':\n case 'switch':\n return { type: 'boolean' };\n\n case 'spinbutton':\n case 'slider': {\n const prop: JsonSchemaProperty = { type: 'number' };\n const min = el.getAttribute('aria-valuemin');\n const max = el.getAttribute('aria-valuemax');\n if (min !== null) prop.minimum = parseFloat(min);\n if (max !== null) prop.maximum = parseFloat(max);\n return prop;\n }\n\n case 'combobox': {\n const ownedId = el.getAttribute('aria-owns') ?? el.getAttribute('aria-controls');\n if (ownedId) {\n const listbox = document.getElementById(ownedId);\n if (listbox) {\n const options = Array.from(listbox.querySelectorAll('[role=\"option\"]')).filter(\n (o) => o.getAttribute('aria-disabled') !== 'true',\n );\n if (options.length > 0) {\n const enumValues = options\n .map((o) => (o.getAttribute('data-value') ?? o.textContent ?? '').trim())\n .filter(Boolean);\n const oneOf = options.map((o) => ({\n const: (o.getAttribute('data-value') ?? o.textContent ?? '').trim(),\n title: (o.textContent ?? '').trim(),\n }));\n return { type: 'string', enum: enumValues, oneOf };\n }\n }\n }\n return { type: 'string' };\n }\n\n case 'textbox':\n case 'searchbox':\n case 'radio':\n default:\n return { type: 'string' };\n }\n}\n\nfunction getRadioLabelText(radio: HTMLInputElement): string {\n // 1. Wrapping label\n const parent = radio.closest('label');\n if (parent) {\n const clone = parent.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n const text = clone.textContent?.trim() ?? '';\n if (text) return text;\n }\n // 2. Label pointing to radio by id\n if (radio.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(radio.id)}\"]`);\n if (label) {\n const text = label.textContent?.trim() ?? '';\n if (text) return text;\n }\n }\n return '';\n}\n", "/**\n * analyzer.ts \u2014 Infer tool name, description, and JSON Schema from form DOM\n */\n\nimport { JsonSchema, JsonSchemaProperty, inputTypeToSchema, collectRadioEnum, collectRadioOneOf, ARIA_ROLES_TO_SCAN, AriaRole, ariaRoleToSchema } from './schema.js';\nimport { FormOverride } from './config.js';\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n /** Key \u2192 DOM element for fields not addressable by name (id-keyed or ARIA-role controls). */\n fieldElements?: Map<string, Element>;\n}\n\n// Track form index for fallback naming\nlet formIndex = 0;\n\n/** Reset form index counter (useful in tests) */\nexport function resetFormIndex(): void {\n formIndex = 0;\n}\n\n/** Derive ToolMetadata from a <form> element */\nexport function analyzeForm(form: HTMLFormElement, override?: FormOverride): ToolMetadata {\n const name = override?.name ?? inferToolName(form);\n const description = override?.description ?? inferToolDescription(form);\n const { schema: inputSchema, fieldElements } = buildSchema(form);\n\n return { name, description, inputSchema, fieldElements };\n}\n\n// ---------------------------------------------------------------------------\n// Tool name inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolName(form: HTMLFormElement): string {\n // 1. Native toolname attribute (spec)\n const nativeName = form.getAttribute('toolname');\n if (nativeName) return sanitizeName(nativeName);\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpName'];\n if (explicit) return sanitizeName(explicit);\n\n // 2. Submit button text\n const submitText = getSubmitButtonText(form);\n if (submitText) return sanitizeName(submitText);\n\n // 3. Nearest heading above the form\n const heading = getNearestHeadingText(form);\n if (heading) return sanitizeName(heading);\n\n // 4. Form id or name attribute\n if (form.id) return sanitizeName(form.id);\n if (form.name) return sanitizeName(form.name);\n\n // 5. Form action URL path segment\n if (form.action) {\n const segment = getLastPathSegment(form.action);\n if (segment) return sanitizeName(segment);\n }\n\n // 6. Fallback\n return `form_${++formIndex}`;\n}\n\nfunction sanitizeName(raw: string): string {\n return raw\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 64) || 'form';\n}\n\nfunction getSubmitButtonText(form: HTMLFormElement): string {\n const buttons = [\n ...Array.from(form.querySelectorAll<HTMLButtonElement>('button[type=\"submit\"], button:not([type])')),\n ...Array.from(form.querySelectorAll<HTMLInputElement>('input[type=\"submit\"]')),\n ];\n\n for (const btn of buttons) {\n const text =\n btn instanceof HTMLInputElement\n ? btn.value.trim()\n : btn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return text;\n }\n return '';\n}\n\nfunction getNearestHeadingText(form: HTMLFormElement): string {\n // Walk up the DOM looking for a preceding sibling or parent heading\n let node: Element | null = form;\n while (node) {\n // Check preceding siblings\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n // Stop at body\n if (!node || node === document.body) break;\n }\n return '';\n}\n\nfunction getLastPathSegment(url: string): string {\n try {\n const parsed = new URL(url, window.location.href);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n return '';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool description inference\n// ---------------------------------------------------------------------------\n\nfunction inferToolDescription(form: HTMLFormElement): string {\n // 1. Native tooldescription attribute (spec)\n const nativeDesc = form.getAttribute('tooldescription');\n if (nativeDesc) return nativeDesc.trim();\n\n // 2. Explicit data attribute\n const explicit = form.dataset['webmcpDescription'];\n if (explicit) return explicit.trim();\n\n // 2. <legend> inside the form\n const legend = form.querySelector('legend');\n if (legend?.textContent?.trim()) return legend.textContent.trim();\n\n // 3. aria-label on the form\n const ariaLabel = form.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n // 4. aria-describedby\n const describedById = form.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 5. Combine nearest heading + page title\n const heading = getNearestHeadingText(form);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle) return `${heading} \u2014 ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n\n return 'Submit form';\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema construction\n// ---------------------------------------------------------------------------\n\nfunction buildSchema(form: HTMLFormElement): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n\n // Track which radio group names we've already processed\n const processedRadioGroups = new Set<string>();\n\n const controls = Array.from(\n form.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input, textarea, select',\n ),\n );\n\n for (const control of controls) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n // Skip already-processed radio groups\n if (\n control instanceof HTMLInputElement &&\n control.type === 'radio'\n ) {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue; // skipped types\n\n // Enrich with title and description\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n // For radio groups, add enum and oneOf values\n if (\n control instanceof HTMLInputElement &&\n control.type === 'radio'\n ) {\n schemaProp.enum = collectRadioEnum(form, fieldKey);\n const radioOneOf = collectRadioOneOf(form, fieldKey);\n if (radioOneOf.length > 0) schemaProp.oneOf = radioOneOf;\n }\n\n properties[fieldKey] = schemaProp;\n\n // Track id-keyed or aria-label-keyed fields for the interceptor\n if (!name) {\n fieldElements.set(fieldKey, control);\n }\n\n // Mark as required if the HTML attribute says so\n if (control.required) {\n required.push(fieldKey);\n }\n }\n\n // ARIA role-based controls (custom components not using native inputs)\n const ariaControls = collectAriaControls(form);\n const processedAriaRadioGroups = new Set<string>();\n\n for (const { el, role, key } of ariaControls) {\n if (properties[key]) continue; // already covered by a native control\n\n if (role === 'radio') {\n if (processedAriaRadioGroups.has(key)) continue;\n processedAriaRadioGroups.add(key);\n }\n\n const schemaProp = ariaRoleToSchema(el, role);\n schemaProp.title = inferAriaFieldTitle(el);\n const desc = inferAriaFieldDescription(el);\n if (desc) schemaProp.description = desc;\n\n properties[key] = schemaProp;\n fieldElements.set(key, el);\n\n if (el.getAttribute('aria-required') === 'true') {\n required.push(key);\n }\n }\n\n return { schema: { type: 'object', properties, required }, fieldElements };\n}\n\n/** Derive a schema key for a native control that lacks a name attribute */\nfunction resolveNativeControlFallbackKey(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string | null {\n const el = control as HTMLElement;\n if (el.dataset['webmcpName']) return sanitizeName(el.dataset['webmcpName']!);\n if (control.id) return sanitizeName(control.id);\n const label = control.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n // Fallback: placeholder text \u2014 common for minimalist forms (e.g. Ghost newsletter)\n // that have no name/id/aria-label on their inputs.\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return sanitizeName(control.placeholder.trim());\n }\n // Final fallback: input type for typed inputs without any text identifier.\n if (control instanceof HTMLInputElement && control.type !== 'text') {\n return control.type;\n }\n return null;\n}\n\n/** Collect ARIA-role-based interactive elements inside a form, excluding native inputs */\nfunction collectAriaControls(form: HTMLFormElement): Array<{ el: Element; role: AriaRole; key: string }> {\n const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role=\"${r}\"]`).join(', ');\n const results: Array<{ el: Element; role: AriaRole; key: string }> = [];\n\n for (const el of Array.from(form.querySelectorAll(selector))) {\n // Skip native inputs \u2014 already handled above\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLSelectElement\n ) continue;\n\n // Skip hidden elements\n if (el.getAttribute('aria-hidden') === 'true' || (el as HTMLElement).hidden) continue;\n\n const role = el.getAttribute('role') as AriaRole;\n const key = resolveAriaFieldKey(el);\n if (!key) continue;\n\n results.push({ el, role, key });\n }\n\n return results;\n}\n\n/** Derive a schema key from an ARIA element */\nfunction resolveAriaFieldKey(el: Element): string | null {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpName']) return sanitizeName(htmlEl.dataset['webmcpName']!);\n if (el.id) return sanitizeName(el.id);\n const label = el.getAttribute('aria-label');\n if (label) return sanitizeName(label);\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return sanitizeName(text);\n }\n return null;\n}\n\nfunction inferAriaFieldTitle(el: Element): string {\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpTitle']) return htmlEl.dataset['webmcpTitle']!;\n const label = el.getAttribute('aria-label');\n if (label) return label.trim();\n const labelledById = el.getAttribute('aria-labelledby');\n if (labelledById) {\n const text = document.getElementById(labelledById)?.textContent?.trim();\n if (text) return text;\n }\n if (el.id) return humanizeName(el.id);\n return '';\n}\n\nfunction inferAriaFieldDescription(el: Element): string {\n const nativeParamDesc = el.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n const htmlEl = el as HTMLElement;\n if (htmlEl.dataset?.['webmcpDescription']) return htmlEl.dataset['webmcpDescription']!;\n const ariaDesc = el.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n const describedById = el.getAttribute('aria-describedby');\n if (describedById) {\n const text = document.getElementById(describedById)?.textContent?.trim();\n if (text) return text;\n }\n const placeholder = el.getAttribute('placeholder') ?? (el as HTMLElement).dataset?.['placeholder'];\n if (placeholder) return placeholder.trim();\n return '';\n}\n\nfunction inferFieldTitle(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. data-webmcp-title\n if ('dataset' in control && (control as HTMLElement).dataset['webmcpTitle']) {\n return (control as HTMLElement).dataset['webmcpTitle']!;\n }\n\n // 2. Associated <label> text\n const labelText = getAssociatedLabelText(control);\n if (labelText) return labelText;\n\n // 3. name attribute (humanised)\n if (control.name) return humanizeName(control.name);\n\n // 4. id attribute (humanised)\n if (control.id) return humanizeName(control.id);\n\n // 5. placeholder text (last resort for inputs with no name/id/label)\n if (\n (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) &&\n control.placeholder?.trim()\n ) {\n return control.placeholder.trim();\n }\n\n return '';\n}\n\nfunction inferFieldDescription(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Native toolparamdescription attribute (spec)\n const nativeParamDesc = control.getAttribute('toolparamdescription');\n if (nativeParamDesc) return nativeParamDesc.trim();\n\n // 2. data-webmcp-description\n const el = control as HTMLElement;\n if (el.dataset['webmcpDescription']) return el.dataset['webmcpDescription']!;\n\n // 2. aria-description or aria-describedby\n const ariaDesc = control.getAttribute('aria-description');\n if (ariaDesc) return ariaDesc;\n\n const describedById = control.getAttribute('aria-describedby');\n if (describedById) {\n const descEl = document.getElementById(describedById);\n if (descEl?.textContent?.trim()) return descEl.textContent.trim();\n }\n\n // 3. placeholder (only as a last resort \u2014 can be noisy)\n if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n const ph = control.placeholder?.trim();\n if (ph && ph.length > 0) return ph;\n }\n\n // 4. Associated label text (if title didn't use it, use it for description)\n return '';\n}\n\nfunction getAssociatedLabelText(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n): string {\n // 1. Labels collection (for/id association)\n if (control.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(control.id)}\"]`);\n if (label) {\n const text = labelTextWithoutNested(label);\n if (text) return text;\n }\n }\n\n // 2. Wrapping <label>\n const parent = control.closest('label');\n if (parent) {\n const text = labelTextWithoutNested(parent);\n if (text) return text;\n }\n\n return '';\n}\n\nfunction labelTextWithoutNested(label: HTMLLabelElement): string {\n // Clone and remove any nested input/select/textarea before getting text\n const clone = label.cloneNode(true) as HTMLLabelElement;\n clone.querySelectorAll('input, select, textarea, button').forEach((el) => el.remove());\n return clone.textContent?.trim() ?? '';\n}\n\nfunction humanizeName(raw: string): string {\n return raw\n .replace(/[-_]/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input group analysis (inputs not inside a <form> element)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive ToolMetadata from a group of form controls that are NOT inside a <form>.\n * Used by discovery.ts's orphan-input scanner for pages like newsletter landing pages.\n */\nexport function analyzeOrphanInputGroup(\n container: Element,\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): ToolMetadata {\n const name = inferOrphanToolName(container, submitBtn);\n const description = inferOrphanToolDescription(container);\n const { schema: inputSchema, fieldElements } = buildSchemaFromInputs(inputs);\n return { name, description, inputSchema, fieldElements };\n}\n\nfunction inferOrphanToolName(\n container: Element,\n submitBtn: HTMLButtonElement | HTMLInputElement | null,\n): string {\n // 1. Submit button text\n if (submitBtn) {\n const text =\n submitBtn instanceof HTMLInputElement\n ? submitBtn.value.trim()\n : submitBtn.textContent?.trim() ?? '';\n if (text && text.length > 0 && text.length < 80) return sanitizeName(text);\n }\n\n // 2. Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n if (heading) return sanitizeName(heading);\n\n // 3. Page title\n const title = document.title?.trim();\n if (title) return sanitizeName(title);\n\n return `form_${++formIndex}`;\n}\n\nfunction inferOrphanToolDescription(container: Element): string {\n // Nearest heading within or above the container\n const heading = getNearestHeadingTextFrom(container);\n const pageTitle = document.title?.trim();\n if (heading && pageTitle && heading !== pageTitle) return `${heading} on ${pageTitle}`;\n if (heading) return heading;\n if (pageTitle) return pageTitle;\n return 'Submit form';\n}\n\n/**\n * Generic heading search starting from any Element (not just HTMLFormElement).\n * Walks up the DOM checking preceding siblings and parent elements.\n */\nfunction getNearestHeadingTextFrom(el: Element): string {\n // Also check headings inside the container itself\n const inner = el.querySelector('h1, h2, h3');\n if (inner?.textContent?.trim()) return inner.textContent.trim();\n\n let node: Element | null = el;\n while (node) {\n let sibling = node.previousElementSibling;\n while (sibling) {\n if (/^H[1-3]$/i.test(sibling.tagName)) {\n const text = sibling.textContent?.trim() ?? '';\n if (text) return text;\n }\n sibling = sibling.previousElementSibling;\n }\n node = node.parentElement;\n if (!node || node === document.body) break;\n }\n return '';\n}\n\n/**\n * Build a JSON Schema from an array of form controls (no <form> context needed).\n * Reuses the same field title/description inference as buildSchema().\n */\nfunction buildSchemaFromInputs(\n inputs: Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,\n): { schema: JsonSchema; fieldElements: Map<string, Element> } {\n const properties: Record<string, JsonSchemaProperty> = {};\n const required: string[] = [];\n const fieldElements = new Map<string, Element>();\n const processedRadioGroups = new Set<string>();\n\n for (const control of inputs) {\n const name = control.name;\n const fieldKey = name || resolveNativeControlFallbackKey(control);\n if (!fieldKey) continue;\n\n if (control instanceof HTMLInputElement && control.type === 'radio') {\n if (processedRadioGroups.has(fieldKey)) continue;\n processedRadioGroups.add(fieldKey);\n }\n\n const schemaProp = inputTypeToSchema(control);\n if (!schemaProp) continue;\n\n schemaProp.title = inferFieldTitle(control);\n const desc = inferFieldDescription(control);\n if (desc) schemaProp.description = desc;\n\n properties[fieldKey] = schemaProp;\n if (!name) fieldElements.set(fieldKey, control);\n if (control.required) required.push(fieldKey);\n }\n\n return { schema: { type: 'object', properties, required }, fieldElements };\n}\n", "/**\n * discovery.ts \u2014 Form scanning & MutationObserver for SPA support\n */\n\nimport { ResolvedConfig } from './config.js';\nimport { analyzeForm, analyzeOrphanInputGroup } from './analyzer.js';\nimport { registerFormTool, unregisterFormTool, isWebMCPSupported } from './registry.js';\nimport { buildExecuteHandler, fillElement } from './interceptor.js';\nimport { enrichMetadata } from './enhancer.js';\nimport { ARIA_ROLES_TO_SCAN } from './schema.js';\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type FormLifecycleEvent = CustomEvent<{\n form: HTMLFormElement;\n toolName: string;\n}>;\n\nfunction emit(type: 'form:registered' | 'form:unregistered', form: HTMLFormElement, toolName: string): void {\n window.dispatchEvent(\n new CustomEvent(type, { detail: { form, toolName } }) as FormLifecycleEvent,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Registration helpers\n// ---------------------------------------------------------------------------\n\n/** Check whether a form should be excluded per config */\nfunction isExcluded(form: HTMLFormElement, config: ResolvedConfig): boolean {\n // Skip forms with data-no-webmcp\n if (form.dataset['noWebmcp'] !== undefined) return true;\n // Skip per config exclude list\n for (const selector of config.exclude) {\n try {\n if (form.matches(selector)) return true;\n } catch {\n // invalid selector \u2014 ignore\n }\n }\n return false;\n}\n\nasync function registerForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n if (isExcluded(form, config)) return;\n\n // Find matching override (first matching selector wins)\n let override;\n for (const [selector, ovr] of Object.entries(config.overrides)) {\n try {\n if (form.matches(selector)) {\n override = ovr;\n break;\n }\n } catch {\n // invalid selector\n }\n }\n\n let metadata = analyzeForm(form, override);\n if (config.enhance) {\n if (config.debug) console.debug(`[auto-webmcp] Enriching: ${metadata.name}\u2026`);\n metadata = await enrichMetadata(metadata, config.enhance);\n }\n\n if (config.debug) {\n warnToolQuality(metadata.name, metadata.description);\n }\n\n const execute = buildExecuteHandler(form, config, metadata.name, metadata);\n\n await registerFormTool(form, metadata, execute);\n registeredForms.add(form);\n registeredFormCount++;\n\n if (config.debug) {\n console.debug(`[auto-webmcp] Registered: ${metadata.name}`, metadata);\n }\n\n emit('form:registered', form, metadata.name);\n}\n\nasync function unregisterForm(form: HTMLFormElement, config: ResolvedConfig): Promise<void> {\n const { getRegisteredToolName } = await import('./registry.js');\n const name = getRegisteredToolName(form);\n if (!name) return;\n\n await unregisterFormTool(form);\n registeredForms.delete(form);\n\n if (config.debug) {\n console.debug(`[auto-webmcp] Unregistered: ${name}`);\n }\n\n emit('form:unregistered', form, name);\n}\n\n// ---------------------------------------------------------------------------\n// MutationObserver\n// ---------------------------------------------------------------------------\n\nlet observer: MutationObserver | null = null;\n\n/** Set of currently registered forms, used to detect lazy-rendered child inputs. */\nconst registeredForms = new WeakSet<HTMLFormElement>();\n\n/** Count of forms registered in the current discovery session. Reset on each startDiscovery call. */\nlet registeredFormCount = 0;\n\n/** Debounce timers for re-analysis when inputs are added to existing forms. */\nconst reAnalysisTimers = new Map<HTMLFormElement, ReturnType<typeof setTimeout>>();\nconst RE_ANALYSIS_DEBOUNCE_MS = 300;\n\n/** Returns true if node is (or contains) an input-like or ARIA-role element. */\nfunction isInterestingNode(node: Element): boolean {\n const tag = node.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;\n const role = node.getAttribute('role');\n if (role && (ARIA_ROLES_TO_SCAN as readonly string[]).includes(role)) return true;\n if (node.querySelector('input, textarea, select')) return true;\n for (const r of ARIA_ROLES_TO_SCAN) {\n if (node.querySelector(`[role=\"${r}\"]`)) return true;\n }\n return false;\n}\n\nfunction scheduleReAnalysis(form: HTMLFormElement, config: ResolvedConfig): void {\n const existing = reAnalysisTimers.get(form);\n if (existing) clearTimeout(existing);\n reAnalysisTimers.set(\n form,\n setTimeout(() => {\n reAnalysisTimers.delete(form);\n void registerForm(form, config);\n }, RE_ANALYSIS_DEBOUNCE_MS),\n );\n}\n\nfunction startObserver(config: ResolvedConfig): void {\n if (observer) return;\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element)) continue;\n\n if (node instanceof HTMLFormElement) {\n void registerForm(node, config);\n continue;\n }\n\n // Newly added child inside an already-registered form?\n const parentForm = node.closest('form');\n if (parentForm instanceof HTMLFormElement && registeredForms.has(parentForm) && isInterestingNode(node)) {\n scheduleReAnalysis(parentForm, config);\n }\n\n // New forms nested inside the added subtree\n for (const form of Array.from(node.querySelectorAll<HTMLFormElement>('form'))) {\n void registerForm(form, config);\n }\n }\n\n for (const node of mutation.removedNodes) {\n if (!(node instanceof Element)) continue;\n\n const forms = node instanceof HTMLFormElement\n ? [node]\n : Array.from(node.querySelectorAll<HTMLFormElement>('form'));\n\n for (const form of forms) {\n void unregisterForm(form, config);\n }\n }\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true });\n}\n\n// ---------------------------------------------------------------------------\n// SPA route change support\n// ---------------------------------------------------------------------------\n\nfunction listenForRouteChanges(config: ResolvedConfig): void {\n // Hash changes\n window.addEventListener('hashchange', () => scanForms(config));\n\n // History API (pushState / replaceState)\n const original = {\n pushState: history.pushState.bind(history),\n replaceState: history.replaceState.bind(history),\n };\n\n history.pushState = function (...args) {\n original.pushState(...args);\n scanForms(config);\n };\n\n history.replaceState = function (...args) {\n original.replaceState(...args);\n scanForms(config);\n };\n\n window.addEventListener('popstate', () => scanForms(config));\n}\n\n// ---------------------------------------------------------------------------\n// Main scan\n// ---------------------------------------------------------------------------\n\nasync function scanForms(config: ResolvedConfig): Promise<void> {\n const forms = Array.from(document.querySelectorAll<HTMLFormElement>('form'));\n await Promise.allSettled(forms.map((form) => registerForm(form, config)));\n}\n\n// ---------------------------------------------------------------------------\n// Orphan input scanner (fallback for pages with no <form> elements)\n// ---------------------------------------------------------------------------\n\n/** Input types that are never useful to expose to agents */\nconst ORPHAN_EXCLUDED_TYPES = new Set([\n 'password', 'hidden', 'file', 'submit', 'reset', 'button', 'image',\n]);\n\n/**\n * Find all visible form controls that are NOT inside a <form> element,\n * group them by their nearest ancestor that also contains a submit button,\n * and register each group as a WebMCP tool.\n *\n * This covers common patterns on landing pages and Ghost blogs where the\n * subscribe/search UI is built from plain inputs + buttons without a <form> tag.\n */\nasync function scanOrphanInputs(config: ResolvedConfig): Promise<void> {\n if (!isWebMCPSupported()) return;\n\n const SUBMIT_BTN_SELECTOR = '[type=\"submit\"]:not([disabled]), button:not([type]):not([disabled])';\n const SUBMIT_TEXT_RE = /subscribe|submit|sign[\\s-]?up|send|join|go|search/i;\n\n // Collect visible inputs that are not inside a <form>\n const orphanInputs = Array.from(\n document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input:not(form input), textarea:not(form textarea), select:not(form select)',\n ),\n ).filter((el) => {\n if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {\n return false;\n }\n const rect = el.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n });\n\n if (orphanInputs.length === 0) return;\n\n // Group inputs by the nearest ancestor that also contains a submit button.\n // Walk up from each input until we find a container with a submit-like button.\n const groups = new Map<Element, Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>>();\n\n for (const input of orphanInputs) {\n let container: Element | null = input.parentElement;\n let foundContainer: Element = input.parentElement ?? document.body;\n\n while (container && container !== document.body) {\n const hasSubmitBtn =\n container.querySelector(SUBMIT_BTN_SELECTOR) !== null ||\n Array.from(container.querySelectorAll('button')).some(\n (b) => SUBMIT_TEXT_RE.test(b.textContent ?? ''),\n );\n if (hasSubmitBtn) {\n foundContainer = container;\n break;\n }\n container = container.parentElement;\n }\n\n if (!groups.has(foundContainer)) groups.set(foundContainer, []);\n groups.get(foundContainer)!.push(input);\n }\n\n for (const [container, inputs] of groups) {\n // Pick the last visible submit button within the container (same logic as\n // handleCallTool in background.js: primary action is always the last one).\n const allCandidates = Array.from(\n container.querySelectorAll<HTMLButtonElement | HTMLInputElement>(SUBMIT_BTN_SELECTOR),\n ).filter((b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0;\n });\n\n let submitBtn: HTMLButtonElement | HTMLInputElement | null =\n (allCandidates[allCandidates.length - 1] as HTMLButtonElement | HTMLInputElement) ?? null;\n\n // Fallback: nearest button with submit-like text anywhere on the page\n if (!submitBtn) {\n const pageBtns = Array.from(document.querySelectorAll<HTMLButtonElement>('button')).filter(\n (b) => {\n const r = b.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && SUBMIT_TEXT_RE.test(b.textContent ?? '');\n },\n );\n submitBtn = pageBtns[pageBtns.length - 1] ?? null;\n }\n\n const metadata = analyzeOrphanInputGroup(container, inputs, submitBtn);\n\n // Build key \u2192 element pairs for the execute handler\n const inputPairs: Array<{ key: string; el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement }> = [];\n const schemaProps = metadata.inputSchema.properties;\n for (const el of inputs) {\n const key =\n el.name ||\n (el as HTMLElement).dataset['webmcpName'] ||\n el.id ||\n el.getAttribute('aria-label') ||\n null;\n const safeKey = key\n ? key.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 64)\n : null;\n if (safeKey && schemaProps[safeKey]) {\n inputPairs.push({ key: safeKey, el });\n }\n }\n\n const toolName = metadata.name;\n const execute = async (params: Record<string, unknown>): Promise<{ content: Array<{ type: 'text'; text: string }> }> => {\n for (const { key, el } of inputPairs) {\n if (params[key] !== undefined) {\n fillElement(el, params[key]);\n }\n }\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n return { content: [{ type: 'text', text: 'Fields filled. Ready to submit.' }] };\n };\n\n try {\n await navigator.modelContext!.registerTool({\n name: metadata.name,\n description: metadata.description,\n inputSchema: metadata.inputSchema,\n execute,\n });\n if (config.debug) {\n console.debug(`[auto-webmcp] Orphan tool registered: ${metadata.name}`, metadata);\n }\n } catch {\n // Best-effort\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction warnToolQuality(name: string, description: string): void {\n if (/^form_\\d+$|^submit$|^form$/.test(name)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" has a generic name. Consider adding a toolname or data-webmcp-name attribute.`);\n }\n if (!description || description === 'Submit form') {\n console.warn(`[auto-webmcp] Tool \"${name}\" has no meaningful description.`);\n }\n if (/don'?t|do not|never|avoid|not for/i.test(description)) {\n console.warn(`[auto-webmcp] Tool \"${name}\" description contains negative instructions. Per spec best practices, prefer positive descriptions.`);\n }\n}\n\nexport async function startDiscovery(config: ResolvedConfig): Promise<void> {\n if (document.readyState === 'loading') {\n await new Promise<void>((resolve) =>\n document.addEventListener('DOMContentLoaded', () => resolve(), { once: true }),\n );\n }\n\n registeredFormCount = 0;\n startObserver(config);\n listenForRouteChanges(config);\n await scanForms(config);\n\n // If no form-based tools were found, try orphan inputs (inputs outside <form> elements).\n // This covers newsletter subscribe widgets, search bars, and other formless UI patterns.\n if (registeredFormCount === 0) {\n await scanOrphanInputs(config);\n }\n}\n\nexport function stopDiscovery(): void {\n observer?.disconnect();\n observer = null;\n}\n", "/**\n * interceptor.ts \u2014 Form submit interception for agent-invoked submissions\n *\n * WebMCP's `execute` callback receives form parameters and is expected to\n * return a result. This module bridges the gap: it fills form fields with\n * the agent-supplied values, submits the form, and resolves the execute\n * promise with a structured response.\n */\n\nimport { ResolvedConfig } from './config.js';\nimport type { ToolMetadata } from './analyzer.js';\n\n// ---------------------------------------------------------------------------\n// Extended SubmitEvent types (WebMCP additions)\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface SubmitEvent {\n /** True when the form was submitted by an AI agent via WebMCP */\n agentInvoked?: boolean;\n /** Call to return a structured result to the agent */\n respondWith?: (promise: Promise<unknown>) => void;\n }\n}\n\nexport interface ExecuteResult {\n content: Array<{ type: 'text'; text: string }>;\n}\n\ntype Resolver = (result: ExecuteResult) => void;\ntype Rejecter = (error: Error) => void;\n\n/** Per-form pending execute promises */\nconst pendingExecutions = new WeakMap<\n HTMLFormElement,\n { resolve: Resolver; reject: Rejecter }\n>();\n\n/** Per-form last-used params (for serializing id-keyed fields not in FormData) */\nconst lastParams = new WeakMap<HTMLFormElement, Record<string, unknown>>();\n\n/** Per-form field element map (key \u2192 DOM element for non-name-addressable fields) */\nconst formFieldElements = new WeakMap<HTMLFormElement, Map<string, Element>>();\n\n// React-compatible native prototype setters (retrieved once at module init).\n// Using these bypasses React's controlled-input tracking so onChange fires correctly.\nconst _inputValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\nconst _textareaValueSetter: ((v: string) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;\nconst _checkedSetter: ((v: boolean) => void) | undefined =\n Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked')?.set;\n\n/**\n * Build an `execute` function for a form tool.\n *\n * When the agent calls execute(params):\n * 1. Fills form fields with the supplied params\n * 2. Fires a submit event (or auto-submits if configured)\n * 3. Resolves with structured form data once submitted\n */\nexport function buildExecuteHandler(\n form: HTMLFormElement,\n config: ResolvedConfig,\n toolName: string,\n metadata?: ToolMetadata,\n): (params: Record<string, unknown>) => Promise<ExecuteResult> {\n // Store field element map for this form\n if (metadata?.fieldElements) {\n formFieldElements.set(form, metadata.fieldElements);\n }\n\n // Attach submit/reset listeners once per form\n attachSubmitInterceptor(form, toolName);\n\n return async (params: Record<string, unknown>): Promise<ExecuteResult> => {\n fillFormFields(form, params);\n\n // Dispatch toolactivated event per spec\n window.dispatchEvent(new CustomEvent('toolactivated', { detail: { toolName } }));\n\n return new Promise<ExecuteResult>((resolve, reject) => {\n pendingExecutions.set(form, { resolve, reject });\n\n if (\n config.autoSubmit ||\n form.hasAttribute('toolautosubmit') ||\n form.dataset['webmcpAutosubmit'] !== undefined\n ) {\n // Wait 300 ms before submitting so React/Vue/etc. can commit any state\n // updates queued by the InputEvents we dispatched during fill.\n // Frameworks like React 18 batch state updates asynchronously \u2014 if we\n // call requestSubmit() synchronously the framework reads stale state.\n setTimeout(() => {\n // Re-fill after React has committed, in case it reset values.\n fillFormFields(form, params);\n\n // If the stored form was remounted (React, Turbo etc.), find the live\n // form via the submit button so requestSubmit() reaches the real DOM.\n let submitForm: HTMLFormElement = form;\n if (!form.isConnected) {\n const liveBtn = document.querySelector<HTMLElement>(\n 'button[type=\"submit\"]:not([disabled]), input[type=\"submit\"]:not([disabled])',\n );\n const found = liveBtn?.closest('form') as HTMLFormElement | null;\n if (found) {\n submitForm = found;\n pendingExecutions.set(submitForm, { resolve, reject });\n attachSubmitInterceptor(submitForm, toolName);\n }\n }\n submitForm.requestSubmit();\n }, 300);\n }\n // Otherwise: the form stays filled; human clicks submit,\n // which fires the submit event interceptor below.\n });\n };\n}\n\nfunction attachSubmitInterceptor(form: HTMLFormElement, toolName: string): void {\n // Guard against attaching multiple times\n if ((form as unknown as Record<string, unknown>)['__awmcp_intercepted']) return;\n (form as unknown as Record<string, unknown>)['__awmcp_intercepted'] = true;\n\n form.addEventListener('submit', (e: SubmitEvent) => {\n const pending = pendingExecutions.get(form);\n if (!pending) return; // Normal human submission \u2014 do nothing\n\n // Agent-invoked path\n const { resolve } = pending;\n pendingExecutions.delete(form);\n\n const formData = serializeFormData(form, lastParams.get(form), formFieldElements.get(form));\n const text = `Form submitted. Fields: ${JSON.stringify(formData)}`;\n const result: ExecuteResult = { content: [{ type: 'text', text }] };\n\n if (e.agentInvoked && typeof e.respondWith === 'function') {\n // Native WebMCP path: use respondWith to return to browser\n e.preventDefault();\n e.respondWith(Promise.resolve(result));\n }\n resolve(result);\n });\n\n // Dispatch toolcancel when form is reset\n form.addEventListener('reset', () => {\n window.dispatchEvent(new CustomEvent('toolcancel', { detail: { toolName } }));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Field filling\n// ---------------------------------------------------------------------------\n\nfunction setReactValue(el: HTMLInputElement | HTMLTextAreaElement, v: string): void {\n el.focus();\n // Select all existing text so the insert replaces it\n el.select?.();\n\n // execCommand('insertText') simulates real typing \u2014 triggers native browser\n // input events that every framework (React, Catalyst, Stimulus, Vue, etc.)\n // listens to. Most reliable cross-framework approach.\n // Guard: execCommand can return true but silently fail for inputs inside\n // shadow DOM (e.g. GitHub's Catalyst components), so verify the value landed.\n if (document.execCommand('insertText', false, v) && el.value === v) {\n return;\n }\n\n // Fallback: native prototype setter (bypasses React controlled-input cache)\n const setter = el instanceof HTMLTextAreaElement ? _textareaValueSetter : _inputValueSetter;\n if (setter) {\n setter.call(el, v);\n } else {\n el.value = v;\n }\n el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertText', data: v }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nfunction setReactChecked(el: HTMLInputElement, checked: boolean): void {\n if (_checkedSetter) {\n _checkedSetter.call(el, checked);\n } else {\n el.checked = checked;\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Recursively search shadow roots for a native form control matching selector.\n * GitHub's Catalyst and other custom-element frameworks render inputs inside\n * shadow DOM that form.querySelector() cannot pierce.\n */\nfunction findInShadowRoots(\n root: Document | ShadowRoot,\n selector: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n for (const host of Array.from(root.querySelectorAll('*'))) {\n const sr = (host as Element).shadowRoot;\n if (!sr) continue;\n const found = sr.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(selector);\n if (found) return found;\n const deeper = findInShadowRoots(sr, selector);\n if (deeper) return deeper;\n }\n return null;\n}\n\n/** Find a native form control by name or id, including inside shadow DOM. */\nfunction findNativeField(\n form: HTMLFormElement,\n key: string,\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null {\n const esc = CSS.escape(key);\n // Light DOM first (fast path)\n const light =\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(`[name=\"${esc}\"]`) ??\n form.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n `input#${esc}, textarea#${esc}, select#${esc}`,\n );\n if (light) return light;\n // Shadow DOM fallback: search all shadow roots in the document\n return (\n findInShadowRoots(document, `[name=\"${esc}\"]`) ??\n findInShadowRoots(document, `input#${esc}, textarea#${esc}, select#${esc}`)\n );\n}\n\nfunction fillFormFields(form: HTMLFormElement, params: Record<string, unknown>): void {\n lastParams.set(form, params);\n const fieldEls = formFieldElements.get(form);\n\n for (const [key, value] of Object.entries(params)) {\n const input = findNativeField(form, key);\n\n if (input) {\n if (input instanceof HTMLInputElement) {\n fillInput(input, form, key, value);\n } else if (input instanceof HTMLTextAreaElement) {\n setReactValue(input, String(value ?? ''));\n } else if (input instanceof HTMLSelectElement) {\n input.value = String(value ?? '');\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n continue;\n }\n\n // Fall back to id-keyed or ARIA-role element from the field map.\n const ariaEl = fieldEls?.get(key);\n if (ariaEl) {\n // If the stored element was remounted by a framework (React, Catalyst etc.),\n // find a fresh reference using its actual DOM id (which may differ from the\n // sanitized schema key \u2014 e.g. \"repository-name-input\" \u2192 key \"repository_name_input\").\n let effectiveEl: Element = ariaEl;\n if (!ariaEl.isConnected) {\n const elId = (ariaEl as HTMLElement).id;\n if (elId) {\n const fresh =\n document.getElementById(elId) ??\n findInShadowRoots(document, `#${CSS.escape(elId)}`);\n if (fresh) effectiveEl = fresh;\n }\n }\n if (effectiveEl instanceof HTMLInputElement) {\n fillInput(effectiveEl, form, key, value);\n } else if (effectiveEl instanceof HTMLTextAreaElement) {\n setReactValue(effectiveEl, String(value ?? ''));\n } else if (effectiveEl instanceof HTMLSelectElement) {\n effectiveEl.value = String(value ?? '');\n effectiveEl.dispatchEvent(new Event('change', { bubbles: true }));\n } else {\n fillAriaField(effectiveEl, value);\n }\n }\n }\n}\n\nfunction fillInput(\n input: HTMLInputElement,\n form: HTMLFormElement,\n key: string,\n value: unknown,\n): void {\n const type = input.type.toLowerCase();\n\n if (type === 'checkbox') {\n setReactChecked(input, Boolean(value));\n return;\n }\n\n if (type === 'radio') {\n const esc = CSS.escape(key);\n const radios = form.querySelectorAll<HTMLInputElement>(\n `input[type=\"radio\"][name=\"${esc}\"]`,\n );\n for (const radio of radios) {\n if (radio.value === String(value)) {\n if (_checkedSetter) {\n _checkedSetter.call(radio, true);\n } else {\n radio.checked = true;\n }\n radio.dispatchEvent(new Event('change', { bubbles: true }));\n break;\n }\n }\n return;\n }\n\n setReactValue(input, String(value ?? ''));\n}\n\nfunction fillAriaField(el: Element, value: unknown): void {\n const role = el.getAttribute('role');\n\n if (role === 'checkbox' || role === 'switch') {\n el.setAttribute('aria-checked', String(Boolean(value)));\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n if (role === 'radio') {\n el.setAttribute('aria-checked', 'true');\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n\n // textbox, combobox, searchbox, spinbutton\n const htmlEl = el as HTMLElement;\n if (htmlEl.isContentEditable) {\n htmlEl.textContent = String(value ?? '');\n }\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\nfunction serializeFormData(\n form: HTMLFormElement,\n params?: Record<string, unknown>,\n fieldEls?: Map<string, Element>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const data = new FormData(form);\n\n for (const [key, val] of data.entries()) {\n if (result[key] !== undefined) {\n // Multiple values \u2192 array\n const existing = result[key];\n if (Array.isArray(existing)) {\n existing.push(val);\n } else {\n result[key] = [existing, val];\n }\n } else {\n result[key] = val;\n }\n }\n\n // Supplement with id-keyed or ARIA-keyed fields not captured by FormData\n if (params) {\n for (const key of Object.keys(params)) {\n if (key in result) continue;\n const el =\n findNativeField(form, key) ??\n fieldEls?.get(key) ??\n null;\n if (!el) continue;\n if (el instanceof HTMLInputElement && el.type === 'checkbox') {\n result[key] = el.checked;\n } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n result[key] = el.value;\n } else {\n // ARIA element\n const role = el.getAttribute('role');\n if (role === 'checkbox' || role === 'switch') {\n result[key] = el.getAttribute('aria-checked') === 'true';\n } else {\n result[key] = (el as HTMLElement).textContent?.trim() ?? '';\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Fill a single form control or ARIA element with the given value.\n * Exported for use by orphan-input (formless) tool handlers in discovery.ts.\n */\nexport function fillElement(\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | Element,\n value: unknown,\n): void {\n if (el instanceof HTMLInputElement) {\n const type = el.type.toLowerCase();\n if (type === 'checkbox') {\n setReactChecked(el, Boolean(value));\n } else if (type === 'radio') {\n if (el.value === String(value)) {\n if (_checkedSetter) _checkedSetter.call(el, true);\n else el.checked = true;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n } else {\n setReactValue(el, String(value ?? ''));\n }\n } else if (el instanceof HTMLTextAreaElement) {\n setReactValue(el, String(value ?? ''));\n } else if (el instanceof HTMLSelectElement) {\n el.value = String(value ?? '');\n el.dispatchEvent(new Event('change', { bubbles: true }));\n } else {\n fillAriaField(el, value);\n }\n}\n\nfunction resolveFormAction(form: HTMLFormElement): string {\n if (form.action) {\n try {\n return new URL(form.action, window.location.href).href;\n } catch {\n // ignore\n }\n }\n return window.location.href;\n}\n", "/**\n * enhancer.ts \u2014 Optional LLM-based description enrichment\n *\n * Calls a small LLM to generate richer tool descriptions than heuristics\n * alone can provide. Activated only when config.enhance is set.\n */\n\nimport { ToolMetadata } from './analyzer.js';\nimport { EnhancerConfig } from './config.js';\n\nexport async function enrichMetadata(\n metadata: ToolMetadata,\n enhancer: EnhancerConfig,\n): Promise<ToolMetadata> {\n try {\n const enriched = await callLLM(metadata, enhancer);\n return { ...metadata, description: enriched };\n } catch (err) {\n // Enhancement is optional \u2014 fall back to heuristic description\n console.warn('[auto-webmcp] Enrichment failed, using heuristic description:', err);\n return metadata;\n }\n}\n\nasync function callLLM(metadata: ToolMetadata, config: EnhancerConfig): Promise<string> {\n const prompt = buildPrompt(metadata);\n\n if (config.provider === 'claude') {\n return callClaude(prompt, config);\n } else {\n return callGemini(prompt, config);\n }\n}\n\nfunction buildPrompt(metadata: ToolMetadata): string {\n const fields = Object.entries(metadata.inputSchema.properties)\n .map(([name, prop]) => `- ${prop.title ?? name} (${prop.type}): ${prop.description ?? ''}`)\n .join('\\n');\n\n return `You are helping describe a web form as an AI tool. Given this form information:\n\nName: ${metadata.name}\nCurrent description: ${metadata.description}\nFields:\n${fields}\n\nWrite 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.`;\n}\n\nasync function callClaude(prompt: string, config: EnhancerConfig): Promise<string> {\n const model = config.model ?? 'claude-haiku-4-5-20251001';\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'x-api-key': config.apiKey,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n model,\n max_tokens: 150,\n messages: [{ role: 'user', content: prompt }],\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Claude API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n content: Array<{ type: string; text: string }>;\n };\n\n return data.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text)\n .join('')\n .trim();\n}\n\nasync function callGemini(prompt: string, config: EnhancerConfig): Promise<string> {\n const model = config.model ?? 'gemini-1.5-flash';\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${config.apiKey}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n contents: [{ parts: [{ text: prompt }] }],\n generationConfig: { maxOutputTokens: 150, temperature: 0.2 },\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Gemini API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n candidates: Array<{\n content: { parts: Array<{ text: string }> };\n }>;\n };\n\n return data.candidates[0]?.content.parts.map((p) => p.text).join('').trim() ?? '';\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["getRegisteredToolName"]
7
7
  }
@@ -435,6 +435,12 @@ function resolveNativeControlFallbackKey(control) {
435
435
  const label = control.getAttribute("aria-label");
436
436
  if (label)
437
437
  return sanitizeName(label);
438
+ if ((control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) && control.placeholder?.trim()) {
439
+ return sanitizeName(control.placeholder.trim());
440
+ }
441
+ if (control instanceof HTMLInputElement && control.type !== "text") {
442
+ return control.type;
443
+ }
438
444
  return null;
439
445
  }
440
446
  function collectAriaControls(form) {
@@ -519,6 +525,9 @@ function inferFieldTitle(control) {
519
525
  return humanizeName(control.name);
520
526
  if (control.id)
521
527
  return humanizeName(control.id);
528
+ if ((control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) && control.placeholder?.trim()) {
529
+ return control.placeholder.trim();
530
+ }
522
531
  return "";
523
532
  }
524
533
  function inferFieldDescription(control) {