npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webmcp-forms

v0.0.10

Published

AI-powered form tools for WebMCP. Enables AI assistants to fill, validate, clear, and submit web forms through the Model Context Protocol.

Readme

webmcp-forms

AI-powered form tools for WebMCP. Enables AI assistants to fill, validate, clear, and submit web forms through the Model Context Protocol.

🧠 What is webmcp-forms?

webmcp-forms builds on top of webmcp-adapter to expose your web forms as a set of typed, validated AI tools. Once registered, an AI assistant can fill fields, validate the form, and submit it — all through natural language.

🚀 Installation

npm install webmcp-forms webmcp-adapter

For React applications:

npm install webmcp-forms webmcp-adapter webmcp-adapter-react

⚡ Quick Start

import { useState } from 'react'
import { useTools } from 'webmcp-adapter-react'
import { createFormTools } from 'webmcp-forms'

const fields = {
    name: { type: 'string', label: 'Full Name', required: true, minLength: 2 },
    email: { type: 'string', label: 'Email', required: true, pattern: '^[^@]+@[^@]+\\.[^@]+$' },
}

function ContactForm() {
    const [values, setValues] = useState({ name: '', email: '' })

    useTools({
        tools: createFormTools({
            formId: 'contact',
            fields,
            getValues: () => values,
            onChange: (field, value) => setValues(prev => ({ ...prev, [field]: value })),
            onSubmit: () => console.log('Submitted:', values),
        }),
        deps: [values]
    })

    return (
        <form>
            <input value={values.name} onChange={e => setValues(p => ({ ...p, name: e.target.value }))} placeholder="Full Name" />
            <input value={values.email} onChange={e => setValues(p => ({ ...p, email: e.target.value }))} placeholder="Email" />
            <button type="submit">Submit</button>
        </form>
    )
}

🛠 API Reference

createFormTools(options)

Creates an array of ToolDefinition[] for AI interaction. Pass the result directly to registerBatch or useTools.

function createFormTools(options: CreateFormToolsOptions): ToolDefinition[]

Options

| Option | Type | Required | Description | |--------|------|----------|-------------| | formId | string | Yes | Unique identifier for the form. Used as a prefix for all generated tool names (e.g. fill_contact_field) | | fields | Record<string, FormField> | Yes | Field definitions describing each form field and its constraints | | getValues | () => Record<string, JsonValue> | Yes | Function that returns the current form values | | onChange | (field: string, value: JsonValue) => void | Yes | Callback invoked when the AI sets a field value | | onSubmit | () => void \| Promise<void> | No | Called when the AI invokes the submit tool | | onReset | () => void | No | Called when the AI invokes the reset tool | | validationSchema | { form?: StandardSchema, fillField?: StandardSchema, fillMultipleField?: StandardSchema } | No | Per-tool Standard Schema validators (Zod, Valibot, ArkType). Each key targets a specific tool — form for validate-form, fillField for fill-field, fillMultipleField for fill-multiple-field. When a key is provided it replaces the built-in JSON Schema validation for that tool | | selectedTools | Set<FormTools> | No | Specific tools to include. Defaults to all tools | | customTools | ToolDefinition[] | No | Additional custom tools to register alongside the built-in form tools |


FormField

Describes a single form field and its validation constraints.

| Property | Type | Description | |----------|------|-------------| | type | 'string' \| 'number' \| 'boolean' \| 'array' \| 'object' | Field type — required | | label | string | Human-readable label used in validation error messages | | required | boolean | Whether the field must have a non-empty value | | options | string[] | Restricts the value to an enum list | | min / max | number | Min/max range for number fields | | minLength / maxLength | number | Length constraints for string fields | | minItems / maxItems | number | Length constraints for array fields | | step | number | Step constraint for number fields (multipleOf in JSON Schema) | | pattern | string | Regex pattern for string field validation | | placeholder | string | UI hint — not used in validation | | defaultValue | JsonValue | Value used when the field is reset |

const fields = {
    // String with length and required constraints
    name: {
        type: 'string',
        label: 'Full Name',
        required: true,
        minLength: 2,
        maxLength: 50
    },
    // String with regex pattern
    email: {
        type: 'string',
        label: 'Email',
        required: true,
        pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
    },
    // Number with range
    age: {
        type: 'number',
        label: 'Age',
        required: true,
        min: 18,
        max: 120
    },
    // Boolean
    subscribe: {
        type: 'boolean',
        label: 'Subscribe to newsletter'
    },
    // String with enum options
    country: {
        type: 'string',
        label: 'Country',
        required: true,
        options: ['US', 'UK', 'CA', 'DE', 'FR']
    },
    // Array with item count constraints
    interests: {
        type: 'array',
        label: 'Interests',
        minItems: 1,
        maxItems: 5
    },
    // Nested object with default value
    address: {
        type: 'object',
        label: 'Address',
        defaultValue: { street: '', city: '', zip: '' }
    }
}

FormTools

Available tool identifiers for selectedTools:

| Tool ID | Generated Tool Name | Description | |---------|---------------------|-------------| | fill-field | fill_{formId}_field | Fill a single form field with a value | | fill-multiple-field | fill_{formId}_multiple_fields | Fill multiple fields at once | | clear-field | clear_{formId}_field | Clear a field to its default empty value | | get-form-state | get_{formId}_state | Get all current form values | | get-field-value | get_{formId}_field_value | Get a specific field's current value | | validate-form | validate_{formId}_form | Validate all fields without submitting | | submit-form | submit_{formId}_form | Submit the form | | reset-form | reset_{formId}_form | Reset all fields to their default values |


📖 Usage

With React

import { useState } from 'react'
import { useTools } from 'webmcp-adapter-react'
import { createFormTools } from 'webmcp-forms'

const fields = {
    name: { type: 'string', label: 'Full Name', required: true, minLength: 2 },
    email: { type: 'string', label: 'Email', required: true, pattern: '^[^@]+@[^@]+\\.[^@]+$' },
    age: { type: 'number', label: 'Age', min: 18, max: 120 },
    subscribe: { type: 'boolean', label: 'Subscribe to newsletter' }
}

function ContactForm() {
    const [values, setValues] = useState({
        name: '',
        email: '',
        age: null,
        subscribe: false
    })

    useTools({
        tools: createFormTools({
            formId: 'contact',
            fields,
            getValues: () => values,
            onChange: (field, value) => {
                setValues(prev => ({ ...prev, [field]: value }))
            },
            onSubmit: () => {
                console.log('Submitted:', values)
            },
            onReset: () => {
                setValues({ name: '', email: '', age: null, subscribe: false })
            }
        }),
        deps: [values]
    })

    return (
        <form onSubmit={e => e.preventDefault()}>
            <input
                value={values.name}
                onChange={e => setValues(prev => ({ ...prev, name: e.target.value }))}
                placeholder="Full Name"
            />
            <input
                type="email"
                value={values.email}
                onChange={e => setValues(prev => ({ ...prev, email: e.target.value }))}
                placeholder="Email"
            />
            <input
                type="number"
                value={values.age ?? ''}
                onChange={e => setValues(prev => ({
                    ...prev,
                    age: e.target.value ? Number(e.target.value) : null
                }))}
                placeholder="Age"
            />
            <label>
                <input
                    type="checkbox"
                    checked={values.subscribe}
                    onChange={e => setValues(prev => ({ ...prev, subscribe: e.target.checked }))}
                />
                Subscribe to newsletter
            </label>
            <button type="submit">Submit</button>
        </form>
    )
}

With a Custom Validation Schema (Zod, Valibot, ArkType)

Pass any Standard Schema-compatible schema via validationSchema for stricter validation with cross-field rules, custom refinements, or richer error messages. When provided, it replaces the built-in per-field JSON Schema validation.

import { z } from 'zod'
import { createFormTools } from 'webmcp-forms'
import { useTools } from 'webmcp-adapter-react'

const fieldsDefinitions = {
    name: z.string().min(2, 'Full Name must be at least 2 characters'),
    email: z.string().email('Must be a valid email address'),
    age: z.number().min(18, 'Must be at least 18').max(120),
    subscribe: z.boolean().optional(),
}

// Used by validate-form — validates the flat values object
const formSchema = z.object(fieldsDefinitions)

// Used by fill-field — validates { field: 'name', value: '...' }
const fieldSpecificSchemas = Object.entries(fieldsDefinitions).map(([key, schema]) =>
    z.object({ field: z.literal(key), value: schema })
) as [ReturnType<typeof z.object>, ...ReturnType<typeof z.object>[]]
const fillFieldSchema = z.union(fieldSpecificSchemas)

// Used by fill-multiple-field — validates { fields: { name?, email?, ... } }
const fillMultipleFieldSchema = z.object({ fields: formSchema.partial() })

useTools({
    tools: createFormTools({
        formId: 'contact',
        fields,
        getValues: () => values,
        onChange: (field, value) => setValues(prev => ({ ...prev, [field]: value })),
        validationSchema: {
            form: formSchema,               // ← validate-form
            fillField: fillFieldSchema,     // ← fill-field
            fillMultipleField: fillMultipleFieldSchema  // ← fill-multiple-field
        }
    }),
    deps: [values]
})

Selecting Specific Tools

By default, all tools are created. Use selectedTools to include only the tools you need:

import { createFormTools } from 'webmcp-forms'
import type { FormTools } from 'webmcp-forms'

useTools({
    tools: createFormTools({
        formId: 'contact',
        fields,
        getValues: () => values,
        onChange: (field, value) => setValues(prev => ({ ...prev, [field]: value })),
        selectedTools: new Set<FormTools>(['fill-field', 'validate-form', 'submit-form'])
    }),
    deps: [values]
})

Adding Custom Tools

Add your own tools alongside the built-in form tools:

import { defineTool } from 'webmcp-adapter'
import { createFormTools } from 'webmcp-forms'

const autofillTool = defineTool({
    name: 'autofill_contact',
    description: 'Auto-fill the contact form with sample data',
    inputSchema: { type: 'object', properties: {}, required: [] },
    execute: () => {
        setValues({
            name: 'John Doe',
            email: '[email protected]',
            age: 30,
            subscribe: true
        })
        return {
            content: [{ type: 'text', text: 'Form auto-filled with sample data!' }],
            structuredContent: { success: true }
        }
    }
})

useTools({
    tools: createFormTools({
        formId: 'contact',
        fields,
        getValues: () => values,
        onChange: (field, value) => setValues(prev => ({ ...prev, [field]: value })),
        customTools: [autofillTool]
    }),
    deps: [values]
})

Vanilla JavaScript

import { createFormTools } from 'webmcp-forms'
import { registerBatch } from 'webmcp-adapter'

let formValues = { name: '', email: '' }

const tools = createFormTools({
    formId: 'contact',
    fields: {
        name: { type: 'string', label: 'Full Name', required: true },
        email: { type: 'string', label: 'Email', required: true }
    },
    getValues: () => formValues,
    onChange: (field, value) => {
        formValues[field] = value
        document.querySelector(`[name="${field}"]`).value = value
    },
    onSubmit: () => {
        console.log('Submitted:', formValues)
    }
})

const unregister = registerBatch(tools)

// Later, to clean up:
// unregister()

📦 Exported Types

// Core function
export { createFormTools } from './createFormTools'

// Types
export type { CreateFormToolsOptions } from './createFormTools'
export type { FormConfig, FormState, FormField, FormTools, FieldType } from './types'

// Individual tool creators (for advanced usage)
export {
    createFillFieldTool,
    createFillMultipleFieldsTool,
    createGetFormStateTool,
    createGetFieldValueTool,
    createSubmitFormTool,
    createResetFormTool,
    createClearFieldTool,
    createValidateFormTool,
} from './tools'

CreateFormToolsOptions

interface CreateFormToolsOptions {
    formId: string
    fields: Record<string, FormField>
    getValues: () => Record<string, JsonValue>
    onChange: (field: string, value: JsonValue) => void
    onSubmit?: () => void | Promise<void>
    onReset?: () => void
    validationSchema?: {
        form?: StandardSchema           // validate-form tool
        fillField?: StandardSchema      // fill-field tool
        fillMultipleField?: StandardSchema  // fill-multiple-field tool
    }
    selectedTools?: Set<FormTools>
    customTools?: ToolDefinition[]
}

FormField

interface FormField {
    type: 'string' | 'number' | 'boolean' | 'array' | 'object'
    label?: string
    required?: boolean
    options?: string[]
    min?: number
    max?: number
    step?: number
    minLength?: number
    maxLength?: number
    minItems?: number
    maxItems?: number
    pattern?: string
    placeholder?: string
    defaultValue?: JsonValue
}

FormTools

type FormTools =
    | 'fill-field'
    | 'fill-multiple-field'
    | 'get-form-state'
    | 'get-field-value'
    | 'submit-form'
    | 'reset-form'
    | 'clear-field'
    | 'validate-form'

🔗 Related Packages

License

MIT