Schema Form

A form generated from a JSON Schema, so a person can invoke a tool by hand. The counterpart to Tool Schema: that one explains the parameters, this one collects them — together they are how you test an MCP tool without writing a client.

src/components/ui/schema-form.tsx

About

From the component's own source, where it was written.

A form generated from a JSON Schema, so a person can invoke a tool by hand.

The counterpart to ToolSchema: that one explains the parameters, this one collects them. Together they are how you test an MCP tool without writing a client — which is the first thing anyone wants when a server misbehaves.

**Validation is reported, not enforced by disabling submit.** A disabled button with no explanation is the worst version of a form: you cannot submit and you are not told why. Missing required fields are listed, and submit stays live so the error surfaces on the field.

It handles the schema subset that tool definitions actually use — string, number, integer, boolean, enum, and string arrays entered one per line. Nested objects are deliberately out of scope: a generated form for a deep schema is worse than a JSON editor, and CodeBlock already is one.

Install

npx astralyx-ui add schema-form

Usage

import { SchemaForm } from '@/components/ui/schema-form'

<SchemaForm
  schema={tool.inputSchema}
  value={args}
  onChange={setArgs}
  onSubmit={(args) => callTool(tool.name, args)}
/>

Composer

Change the props and the source below follows.

Required

Truncate above this.

One per line.

Glob patterns to skip.

Required: path

Props

No props to configure.

tsx
<SchemaForm
  schema={tool.inputSchema}
  value={args}
  onChange={setArgs}
  onSubmit={(args) => callTool(tool.name, args)}
/>

Examples

Calling read_file by hand

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

SchemaForm

Extends Omit<ComponentProps<'form'>, 'onSubmit' | 'onChange'>.

PropTypeDefaultDescription
schemaJsonSchemarequiredHandles the subset tool definitions actually use: string, number, integer, boolean, enum, and string arrays entered one per line.
valueSchemaFormValuerequired—
onChange(next: SchemaFormValue) => voidrequired—
onSubmit(value: SchemaFormValue) => void——
submitLabelReactNode'Run'—
busybooleanfalseDisables every control while the call is in flight.
missingLabel(fields: string[]) => ReactNode(fields) => `Required: ${fields.join(', ')}`Names the fields that are required but empty.
emptyLabelstring'This tool takes no parameters.'—
arrayHintstring'One per line.'Hint under an array field.

Functions

FunctionSignatureDescription
schemaFormMissing(schema: JsonSchema, value: SchemaFormValue) => string[]Required, and either absent or an empty string.

Types

TypeDefinitionDescription
SchemaFormValue
Record<string, unknown>
A form generated from a JSON Schema, so a person can invoke a tool by hand.

Behaviour

TopicDetailDescription
nested objectsout of scopeDeliberately. A generated form for a deep schema is worse than a JSON editor, and CodeBlock already is one.
validationreported, not enforcedSubmit is never disabled for missing fields. A dead button that will not say why is the worst version of a form — the missing names are listed instead.