{ "name": "mcp-elicitation", "type": "registry:ui", "title": "MCP Elicitation", "description": "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.", "category": "MCP", "dependencies": [ "lucide-react@^1.39.0" ], "registryDependencies": [ "badge", "button", "lib-motion", "lib-styles", "lib-utils", "schema-form", "tool-schema" ], "files": [ { "path": "components/ui/mcp-elicitation.tsx", "type": "registry:ui", "content": "'use client'\n\nimport { useState, type ComponentProps, type ReactNode } from 'react'\nimport { MessageCircleQuestion, ShieldAlert } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { SchemaForm, schemaFormMissing } from '@/components/ui/schema-form'\nimport type { JsonSchema } from '@/components/ui/tool-schema'\nimport { enterFade } from '@/lib/motion'\nimport { radius, surface } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\n/**\n * A server asking the user for structured input, mid-run.\n *\n * MCP elicitation lets a server pause and request data it does not have — a\n * confirmation, a missing field, a choice between branches. It is a genuinely\n * useful capability and a genuinely good phishing surface: the prompt text is\n * written by the server, and it appears inside your client, wearing your\n * client's chrome.\n *\n * Two rules follow from that, and they are the reason this is a component\n * rather than a `SchemaForm` in a dialog:\n *\n * **The requesting server is named at the top, outside the server's own copy.**\n * A message that says \"your session expired, re-enter your token\" is indistinguishable\n * from the client asking, unless the client says who is asking.\n *\n * **A field that looks like a credential is refused, not collected.** No\n * legitimate elicitation needs your password, and a component that renders that\n * field anyway is the attack working. The request still displays; the field is\n * replaced with a warning.\n */\nconst CREDENTIAL = /(password|passwd|secret|api[_-]?key|token|credential|private[_-]?key|seed|mnemonic)/i\n\n/** Property names this component will not render an input for. */\nexport function credentialFields(schema: JsonSchema) {\n return Object.keys(schema.properties ?? {}).filter((key) => CREDENTIAL.test(key))\n}\n\ntype McpElicitationProps = Omit, 'onSubmit'> & {\n /** The server asking. Always shown, outside the server's own message. */\n server: string\n /** The server's prompt. Untrusted text — rendered, never interpreted. */\n message: ReactNode\n /** What it wants back. */\n schema: JsonSchema\n onSubmit?: (value: Record) => void\n onDecline?: () => void\n busy?: boolean\n submitLabel?: string\n declineLabel?: string\n askingLabel?: (server: string) => ReactNode\n credentialWarning?: (fields: string[]) => ReactNode\n}\n\nfunction McpElicitation({\n server,\n message,\n schema,\n onSubmit,\n onDecline,\n busy = false,\n submitLabel = 'Send',\n declineLabel = 'Decline',\n askingLabel = (name) => (\n <>\n {name} is asking for information\n \n ),\n credentialWarning = (fields) =>\n `This request asks for ${fields.join(', ')}. No server should need that — the field has not been rendered.`,\n className,\n ...props\n}: McpElicitationProps) {\n const [value, setValue] = useState>({})\n\n const credentials = credentialFields(schema)\n // The credential properties are stripped before the form ever sees them.\n const safeSchema: JsonSchema = {\n ...schema,\n properties: Object.fromEntries(\n Object.entries(schema.properties ?? {}).filter(([key]) => !CREDENTIAL.test(key)),\n ),\n required: (schema.required ?? []).filter((key) => !CREDENTIAL.test(key)),\n }\n\n const missing = schemaFormMissing(safeSchema, value)\n\n return (\n \n {/* Attribution first, outside anything the server controls. */}\n
\n \n \n \n
\n

{askingLabel(server)}

\n

{message}

\n
\n
\n\n {credentials.length > 0 && (\n \n \n {credentialWarning(credentials)}\n

\n )}\n\n \n\n
\n {onSubmit && (\n \n )}\n {onDecline && (\n \n )}\n {missing.length > 0 && (\n \n {missing.length} required\n \n )}\n
\n \n )\n}\n\nexport { McpElicitation }\nexport type { McpElicitationProps }\n" } ] }