MCP input
Free · MITWhether a tools/call (or prompts/get, resources/read) result asks for more input. A result without resultType comes from an older server and counts as complete, as the spec says.
Preview
It's types and helpers, listed below.
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/mcp-input
Functions
isMcpInputRequired(result: unknown)Whether a tools/call (or prompts/get, resources/read) result asks for more input. A result without resultType comes from an older server and counts as complete, as the spec says.
mcpInputView(result: unknown, options: { server?: string | undefined; words?: McpInputWords | undefined } = {})An input-required result as items to show: forms as questions, confirmations and links as decisions, and anything else as a visible "not supported". Undefined when the result doesn't ask for input. server is your app's own name for the server, which the person should see beside every request.
mcpInputResponses(view: McpInputView, replies: McpInputReply[], options: { own?: Record<string, unknown> | undefined; words?: McpInputWords | undefined } = {})The retry's inputResponses for the person's replies, checked against each form's schema first (the spec asks clients to validate). Every form, confirmation and link needs a reply; a form is sent only once every required field has a valid answer. own holds responses your app made itself, for requests Candor doesn't show (a sampling or roots answer), keyed by the server's ids; anything left is listed in notSent.
mcpRetryParams(original: P, view: Pick<McpInputView, "requestState">, inputResponses: Record<string, unknown>)The params for retrying the original request: its own params (name and arguments for tools/call) with inputResponses and the result's requestState exactly as received. A requestState from an earlier round is removed when this result has none, as the spec requires. Send it as a new request with a new JSON-RPC id.
Types
- McpFieldSchema
- A form field's schema, as MCP restricts it: a string, number, boolean or enum. Unknown keys pass through.
{ /** "string", "number", "integer", "boolean" or "array" (a multi-select enum). */ type?: string | undefined /** A short name for the field. */ title?: string | undefined /** What the field is for, in a sentence. */ description?: string | undefined /** The server's default. Shown as the server's suggestion; Candor never fills it in for the person. */ default?: string | number | boolean | string[]- McpElicitFormRequest
- An `elicitation/create` request in form mode: a message and a flat schema of primitive fields.
{ method: "elicitation/create" params: { /** "form", or left out (which means form). */ mode?: "form" | undefined /** Why the server asks, in its own words. */ message: string requestedSchema: { type: "object"; properties: Record<string, McpFieldSchema>; required?: string[] | undefined } } }- McpElicitUrlRequest
- An `elicitation/create` request in URL mode: a page the person opens outside the client (sign-in, payment).
{ method: "elicitation/create" params: { mode: "url" /** Why the server asks, in its own words. */ message: string /** The page to open, only once the person agrees. */ url: string } }- McpInputRequiredResult
- A result that asks for more input before the call can finish. At least one of `inputRequests` and `requestState` is present. Values in `inputRequests` are left `unknown`: a server may send kinds this file doesn't know.
{ resultType: "input_required" /** The server's requests, keyed by its own ids. */ inputRequests?: Record<string, unknown> | undefined /** Opaque to the client: sent back exactly as received, never read or changed. */ requestState?: string | undefined _meta?: Record<string, unknown> | undefined }- McpElicitResult
- The answer to one elicitation, as MCP takes it back. `content` only when accepted in form mode.
{ action: "accept" | "decline" | "cancel" content?: Record<string, string | number | boolean | string[]> | undefined }- McpQuestion
- A question in ClarifyingQuestions' shape (`ClarifyingQuestion`), defined here so this free file needs nothing from a paid one. The question's id is the form field's name.
| { id: string kind: "choice" prompt: string options: { id: string; label: string; detail?: string | undefined }[] multiple?: boolean | undefined /** The server's default, marked as a suggestion and never pre-selected. */ recommended?: string | undefined /** A short label shown above the question: the field's title when it also has a description. */ tag?: string | undefined /** Always false: an MC- McpQuestionAnswer
- A person's answer, in ClarifyingQuestions' shape (`ClarifyingAnswer`).
{ questionId: string; choice?: string[] | undefined; text?: string | undefined }- McpDecision
- A two-answer confirmation in ApprovalDecision's shape (`Decision`), defined here so this free file needs nothing from a paid one. The choices' ids are "accept" and "decline", which is what the retry sends.
{ id: string question: string /** What to decide on, as label and value: the page's host and its full address. */ facts?: { label: string; value: string }[] | undefined /** Accept first, decline second. */ choices: { id: "accept" | "decline"; label: string; detail?: string | undefined }[] state: "open" }- McpInputItem
- One of the server's requests, as Candor shows it. `key` is the server's id for it.
| { key: string; kind: "questions"; message: string; questions: McpQuestion[]; fields: Record<string, McpFieldSchema>; required: string[] } /** A form with no fields: the server asks to go ahead. Answer it with ApprovalDecision. */ | { key: string; kind: "confirm"; message: string; decision: McpDecision } /** * A page to open outside the client. Show the full `url` and its `host` before the person- McpInputView
- An input-required result, ready to show.
{ /** One per request, in the server's order. */ items: McpInputItem[] /** Sent back exactly as received; absent when the server sent none. */ requestState?: string | undefined /** The server's name as your app knows it (passed in), so the person can tell which server asks. */ server?: string | undefined /** The name the server gives itself in the result's `_meta`. Its own claim: show it as such,- McpInputReply
- A person's reply to one item, by its key.
| { key: string; action: "accept"; answers?: McpQuestionAnswer[] | undefined } /** Declined outright, or dismissed without a choice. */ | { key: string; action: "decline" | "cancel" }- McpInputProblem
- Why an answer can't be sent yet.
{ /** The item's key. */ key: string /** The field, for a form. */ questionId?: string | undefined /** Which rule it broke. */ code: "unanswered" | "required" | "notAnOption" | "notANumber" | "notAnInteger" | "tooSmall" | "tooLarge" | "tooShort" | "tooLong" | "format" | "tooFew" | "tooMany" /** The same in words, from `mcpInputWords`. */ message: string }- McpInputResponses
- What a retry sends, or why it can't be sent yet.
| { ok: true /** The retry's `inputResponses`, keyed by the server's ids. */ inputResponses: Record<string, unknown> /** Keys of requests nothing was sent for (not supported and not answered by your app). The server may ask again. */ notSent: string[] } | { ok: false; problems: McpInputProblem[] }- McpInputWords
- The words, with any you pass in place of the defaults.
Partial<typeof mcpInputWords>