How an AI coding agent (Claude Code, Cursor, etc.) generates a @letar/forms form using the @letar/form-mcp server — static walkthrough, no live MCP connection from the browser.
"Create a contact form with name, email, and a message field"
Tool call:
list_fields({ category: "text" })Result:
[
{ "name": "String", "fullName": "Form.Field.String", "category": "text" },
{ "name": "Textarea", "fullName": "Form.Field.Textarea", "category": "text" }
]Tool call:
generate_form({
formName: "ContactForm",
withSchema: true,
fields: [
{ name: "name", type: "String", label: "Full Name", required: true },
{ name: "email", type: "String", label: "Email", required: true },
{ name: "message", type: "Textarea", label: "Message" }
]
})Result — ready-to-use component code:
import { z } from 'zod/v4'
import { useAppForm } from '@letar/forms'
const ContactFormSchema = z.object({
name: z.string().min(1, 'Required field'),
email: z.string().min(1, 'Required field'),
message: z.string(),
}).strip()
type ContactFormValues = z.infer<typeof ContactFormSchema>
export function ContactForm() {
const form = useAppForm({
schema: ContactFormSchema,
defaultValues: {
name: '',
email: '',
message: '',
},
onSubmit: async ({ value }) => {
// TODO: call Server Action
console.log(value)
},
})
return (
<Form form={form}>
<Form.Field.String name="name" label="Full Name" required />
<Form.Field.String name="email" label="Email" required />
<Form.Field.Textarea name="message" label="Message" />
<Form.Button.Submit>Save</Form.Button.Submit>
</Form>
)
}Same three fields, rendered with the current declarative Form API (the generated code above uses the older useAppForm style — both produce the same result).