> ## Documentation Index
> Fetch the complete documentation index at: https://fastmcp-ts.docs.prefect.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Built-in providers

> Ready-to-mount interactive primitives: approval, choice, file upload, forms

A handful of interactions show up in almost every app: confirm an action, pick from a list, upload a file, fill in a form. Each of these is an entrypoint plus the backend tools that handle its clicks — exactly the pattern from [the overview](/apps/overview), but one you would rather not rebuild every time. Providers package each interaction as a ready-made mini-app, so you get the screen and its wiring without authoring components.

## Mounting a provider

A provider is a `FastMCPApp` under the hood, and you attach one to your server with `addProvider`. That single call mounts the provider's entrypoint and all of its backend tools onto your server, prefix-aware and ready to use.

```typescript server.ts theme={null}
import { FastMCP, Approval, Choice, FileUpload, FormInput } from '@prefecthq/fastmcp-ts/server'

const server = new FastMCP({ name: 'my-server', version: '1.0.0' })

server.addProvider(new Approval())
server.addProvider(new Choice())
server.addProvider(new FileUpload())
server.addProvider(new FormInput())
```

`addProvider` is a convenience over mounting: it accepts a `FastMCPApp` (or a plain `FastMCP`) and mounts its underlying server for you, so you never have to reach for `.server` or call `mount` directly. Because it mounts, every provider tool is namespaced under the provider's name and its action references resolve against that prefix — the wiring described in [Components](/apps/components) is what makes a mounted provider work unchanged.

## Approval

The `Approval` provider renders a confirm/deny card and routes the user's choice back into the conversation. It registers the entrypoint together with two app-only backend tools — one for confirm, one for deny — so each button has a tool to land on while neither appears to the model. The card's buttons use action references, so the provider works the same whether it is mounted standalone or under a prefix.

Reach for `Approval` whenever a model-proposed action needs a human to sign off before it happens: a destructive operation, a purchase, anything you want a person in the loop for.

## Choice

The `Choice` provider renders a list of options as clickable buttons and reports which one the user picked. It registers the entrypoint and a single app-only `_select` backend tool; each option button carries its own value as fixed `args`, so clicking an option calls `_select` with that choice already filled in. This is the multi-option case of the button wiring — one backend tool, distinguished by the arguments each button supplies.

Use `Choice` when you want the user to select from a known set rather than type a free-form answer.

## File upload

The `FileUpload` provider renders a file picker and handles the bytes on the server, where they belong. It registers the entrypoint plus app-only tools to submit and delete files. On submit, the submit tool mints a `FileHandle` — a server-generated id paired with the file's name, MIME type, and bytes — stores the file through a `FileStorageAdapter`, and returns that id to the conversation. The adapter's contract is `save` / `load` / `delete` over an already-minted handle, so swapping in your own storage backend never touches how handles are created. The delete tool removes a stored file by its handle.

Server-side storage is the design. The model and the conversation deal only in handles: small references they can pass to other tools or remove. The bytes live wherever your `FileStorageAdapter` puts them — disk, object storage, a database — and your other tools fetch them by handle when they need the content. Keeping the payload on the server means a multi-megabyte upload costs the conversation a single identifier, and the storage backend is yours to choose by supplying the adapter.

Uploads do not live forever. The default in-memory adapter expires each file after a TTL — `FileUploadOptions.ttlMs`, 30 minutes by default — and this is the cleanup that holds in every era, including a [stateless modern-HTTP](/concepts/state-and-handles) request with no session to attach a file to. On a session-based transport (stdio or legacy HTTP) the provider additionally releases a file when the session closes, but that early cleanup is best-effort, layered on top of the TTL rather than the mechanism to depend on. A custom adapter carries its own lifecycle.

Reach for `FileUpload` whenever an app needs a user to provide a document, image, or dataset that downstream tools will process by reference.

## Form input

The `FormInput` provider turns a Standard Schema into a validated form. You hand it a schema — Zod, Valibot, ArkType, or anything else Standard Schema supports — and it renders a field per property and registers an app-only `_submit` tool that validates the submission against that same schema before your code sees it.

The schema is the single source of truth for both halves of the form. The fields are generated from the schema's JSON Schema `properties` using the same conversion that advertises tool input schemas, so the form's shape always matches what you declared. On submit, the values are validated through the schema's `~standard.validate`, so a submission that reaches your handler has already been checked against the exact contract you wrote.

```typescript server.ts theme={null}
import { FastMCP, FormInput } from '@prefecthq/fastmcp-ts/server'
import { z } from 'zod'

const server = new FastMCP({ name: 'my-server', version: '1.0.0' })

server.addProvider(
  new FormInput({
    name: 'contact',
    schema: z.object({
      email: z.string().email(),
      message: z.string().min(1),
    }),
  }),
)
```

Use `FormInput` when you need structured, validated input and want the form and its validation to stay in lockstep without maintaining them separately.
