MCP Elicitation

A server asking the user for structured input mid-run. Useful, and a good phishing surface — the prompt text is written by the server and appears wearing your client’s chrome, so the requesting server is named outside its own copy.

src/components/ui/mcp-elicitation.tsx

About

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

A server asking the user for structured input, mid-run.

MCP elicitation lets a server pause and request data it does not have — a confirmation, a missing field, a choice between branches. It is a genuinely useful capability and a genuinely good phishing surface: the prompt text is written by the server, and it appears inside your client, wearing your client's chrome.

Two rules follow from that, and they are the reason this is a component rather than a SchemaForm in a dialog:

**The requesting server is named at the top, outside the server's own copy.** A message that says "your session expired, re-enter your token" is indistinguishable from the client asking, unless the client says who is asking.

**A field that looks like a credential is refused, not collected.** No legitimate elicitation needs your password, and a component that renders that field anyway is the attack working. The request still displays; the field is replaced with a warning.

Install

npx astralyx-ui add mcp-elicitation

Usage

import { McpElicitation } from '@/components/ui/mcp-elicitation'

<McpElicitation server="postgres" message={request.message} schema={request.schema} onSubmit={reply} />

Composer

Change the props and the source below follows.

Props

No props to configure.

tsx
<McpElicitation
  server="postgres"
  message={request.message}
  schema={request.schema}
  onSubmit={reply}
  onDecline={decline}
/>

Examples

A request that also asks for an API key

API

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

McpElicitation

Extends Omit<ComponentProps<'div'>, 'onSubmit'>.

PropTypeDefaultDescription
serverstringrequiredNamed at the top, outside the server’s own message. “Your session expired, re-enter your token” is indistinguishable from the client asking unless the client says who is asking.
messageReactNoderequiredUntrusted text from the server. Rendered, never interpreted.
schemaJsonSchemarequiredWhat it wants back.
onSubmit(value: Record<string, unknown>) => void——
onDecline() => void——
busybooleanfalse—
submitLabelstring'Send'—
declineLabelstring'Decline'—
askingLabel(server: string) => ReactNode(name) => ( <> <code className="font-mono font-medium">{name}</code> is asking for information </> )—
credentialWarning(fields: string[]) => ReactNode(fields) => `This request asks for ${fields.join(', ')}. No server should need that — the field has not been rendered.`—

Functions

FunctionSignatureDescription
credentialFields(schema: JsonSchema) => string[]Exported, so a client can refuse the request outright rather than rendering it.

Behaviour

TopicDetailDescription
credential refusalautomaticA field named like a password, key, token, secret or seed phrase is stripped from the schema before the form is built, and the request is flagged. No legitimate elicitation needs one.