Skip to content

Support MCP Apps-rendered Elicitations - #733

Open
krubenok wants to merge 7 commits into
modelcontextprotocol:mainfrom
krubenok:feature/app-rendered-elicitations
Open

krubenok wants to merge 7 commits into
modelcontextprotocol:mainfrom
krubenok:feature/app-rendered-elicitations

Conversation

@krubenok

@krubenok krubenok commented Jul 29, 2026 •

Copy link
Copy Markdown

Summary

Add app-rendered form elicitation to the draft MCP Apps specification and SDK. The proposal is entirely contained in the existing io.modelcontextprotocol/ui extension. Core SEP-3118 is closed because no core protocol change is required.

Clients and servers opt in through the nested elicitation: {} setting, alongside core elicitation.form and the Apps MIME type. The server attaches _meta.ui.resourceUri to a complete standard form elicitation, preserving native rendering as a fallback. The host forwards elicitation/create to the specific initialized App and receives the standard ElicitResult.

For core MCP 2026-07-28, this uses request-scoped client capabilities, server/discover, and the existing MRTR flow. Initialized sessions can deliver the same elicitation directly. The Apps bridge independently negotiates version 2026-01-26.

SDK and host behavior

  • Add App.onelicitation, AppBridge.requestElicitation, capability types, metadata helpers, generated schemas, and tests.
  • Bind each elicitation to its originating server connection, resource URI, and App instance, including concurrent requests.
  • Require hosts to validate accepted content against requestedSchema. Resource, initialization, bridge, and validation failures fall back to native rendering; explicit decline and cancel results are preserved.
  • Adapt handler registration, capability storage, context access, and response validation to the SDK 2 APIs now used by ext-apps main.

The wire method and capability shape remain compatible with Inspector's current app-elicitation implementation. No ui/elicitation method is introduced.

Related implementations

This follows #511 and #514 and retains request-bound forwarding rather than using a global/current bridge.

Validation

Rebased on ext-apps main 266a9e3b.

  • SDK build and all example builds: passed.
  • bun test src: 367 passed, including metadata preservation, URL-mode rejection, decline/cancel results, and cancellation propagation.
  • TypeScript typecheck and TypeDoc validation: passed.
  • Formatting and git diff --check: passed.

Client, core, and server SDK dependencies are pinned to 2.1.0 across the SDK and examples, and peer minimums are raised to ^2.1.0. This is the first release containing the fix for cancellation of request ID 0, modelcontextprotocol/typescript-sdk#2654. The regression test now asserts ID 0 and verifies that cancellation reaches the App handler without a warmup request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the MCP Apps SDK and draft specification to support app-rendered form elicitations, negotiated via the existing io.modelcontextprotocol/ui extension (elicitation: {}) and forwarded using the standard elicitation/create request/result shapes.

Changes:

  • Adds new MCP Apps capability + metadata types/schemas for elicitation, and wires them into the SDK type unions.
  • Implements server-side helpers for negotiation and for attaching/reading _meta.ui.resourceUri on elicitation requests, plus tests.
  • Adds app/host APIs (App.onelicitation, AppBridge.requestElicitation) and updates the draft spec to document the end-to-end flow.
Show a summary per file
File Description
src/types.ts Re-exports new elicitation-related spec types/schemas and includes ElicitRequest/ElicitResult in SDK unions.
src/spec.types.ts Adds elicitation capability fields and new McpUiServerCapabilities + McpUiElicitationMeta interfaces.
src/server/index.ts Adds negotiation helpers and helpers to attach/read elicitation UI metadata.
src/server/index.test.ts Adds tests for elicitation negotiation and metadata helpers.
src/generated/schema.ts Adds Zod schemas for new capabilities + elicitation metadata.
src/generated/schema.test.ts Adds type-level checks ensuring schema inference matches spec types.
src/generated/schema.json Updates generated JSON Schema outputs for the new types.
src/app.ts Adds App.onelicitation handler support and capability enforcement for elicitation/create.
src/app-bridge.ts Adds requestElicitation API and capability enforcement for forwarded elicitations.
src/app-bridge.test.ts Adds integration tests for forwarding a form-mode elicitation and failing closed when unsupported.
specification/draft/apps.mdx Documents the protocol negotiation + app-rendered elicitation flow and cleans up formatting.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

src/server/index.ts:589

  • This helper currently accepts ui:foo because it only checks parsed.protocol === "ui:". If callers rely on it for enforcement, that would treat non-absolute ui: URIs as valid even though the API and spec require an absolute ui:// URI.

Add an explicit resourceUri.startsWith("ui://") check before returning.

    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }
  if (parsed.protocol !== "ui:") {
    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }
  • Files reviewed: 8/11 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment thread src/server/index.ts Resolved
Comment thread src/server/index.test.ts Resolved
@krubenok

krubenok commented Jul 29, 2026 •

Copy link
Copy Markdown
Author

The coordinated public references are now aligned to protocol revision 2026-07-28:

The primary flow uses request-scoped client capabilities, server capabilities from server/discover, and the core MRTR shape: the server embeds the ordinary elicitation/create request in InputRequiredResult; the host validates the App's standard ElicitResult, places it under the matching inputResponses key, and retries the originating request with requestState.

No second extension, custom method, or custom result type is introduced. The Inspector is pinned to ext-apps commit 89ab2bc. The MCP Apps View↔Host bridge independently negotiates Apps protocol version 2026-01-26; that is separate from core MCP 2026-07-28.

The references are generic and contain no product-specific example details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (2)

src/server/index.ts:589

  • getElicitationUiResourceUri is intended to validate an absolute ui:// URI, but it only checks parsed.protocol === "ui:", so ui:relative will pass validation even though it is not an absolute ui:// URI.
  if (parsed.protocol !== "ui:") {
    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }

src/server/index.ts:546

  • withElicitationUi claims to require an absolute ui:// URI, but the validation only checks parsed.protocol === "ui:". This will incorrectly accept values like ui:relative (valid URL with protocol ui: but not ui://), contradicting the spec requirement and the error message.

This issue also appears on line 587 of the same file.

  if (parsed.protocol !== "ui:") {
    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }
  • Files reviewed: 8/11 changed files
  • Comments generated: 0 new
  • Review effort level: Low

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (3)

src/server/index.ts:589

  • getElicitationUiResourceUri currently accepts ui:/... because it only checks the URL protocol. To match the spec wording and getToolUiResourceUri behavior, require the ui:// prefix here as well.
  if (parsed.protocol !== "ui:") {
    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }

src/server/index.test.ts:442

  • This test suite doesn't cover the ui:/... case, which currently passes the new URL() + protocol check even though the API requires an absolute ui:// URI. Adding a regression test here will prevent accidentally re-allowing non-ui:// forms.
    expect(() => withElicitationUi(params, "https://example.com")).toThrow(
      "absolute ui://",
    );

src/server/index.ts:546

  • This validation only checks new URL(resourceUri).protocol === "ui:", so values like ui:/path will be accepted even though the error message (and other helpers like getToolUiResourceUri) require an absolute ui:// URI. Tighten the check to require the ui:// prefix (and ideally reject any non-form mode, not just mode === "url").
  if (parsed.protocol !== "ui:") {
    throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
  }
  • Files reviewed: 8/11 changed files
  • Comments generated: 0 new
  • Review effort level: Low

Copilot AI review requested due to automatic review settings August 3, 2026 23:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (1)

src/app-bridge.ts:455

  • The JSDoc says the ElicitResult “can be returned directly to the server”, but the spec text added in this PR requires the host to validate the app result (including against requestedSchema) and potentially fall back to native rendering on bridge/validation failures. This comment is likely to mislead host implementers into skipping required validation.
   * Hosts should call this only from the core MCP client's handler for the
   * originating `elicitation/create` request. The returned value is the
   * standard MCP `ElicitResult` and can be returned directly to the server.
   */
  • Files reviewed: 8/11 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@krubenok
krubenok marked this pull request as ready for review August 4, 2026 02:37
@krubenok krubenok changed the title Add app-rendered elicitations to the MCP Apps draft Support MCP Apps-rendered Elicitations Aug 19, 2026
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"elicitation": {}

@krubenok krubenok Aug 19, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open question: should we allow declaring form and URL elicitations independently? Unclear what an MCP App-rendered URL elicitation looks like but sticking a comment here in case it inspires a use case from someone else.

@kbsoso9999-maker

This comment was marked as spam.

@krubenok
krubenok force-pushed the feature/app-rendered-elicitations branch from 53b6efd to bc25e0a Compare October 26, 2026 03:07
@liady
liady self-requested a review October 27, 2026 14:09

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants