Repository navigation
Conversation
There was a problem hiding this comment.
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.resourceUrion 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:foobecause it only checksparsed.protocol === "ui:". If callers rely on it for enforcement, that would treat non-absoluteui:URIs as valid even though the API and spec require an absoluteui://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
|
The coordinated public references are now aligned to protocol revision
The primary flow uses request-scoped client capabilities, server capabilities from No second extension, custom method, or custom result type is introduced. The Inspector is pinned to ext-apps commit The references are generic and contain no product-specific example details. |
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (2)
src/server/index.ts:589
getElicitationUiResourceUriis intended to validate an absoluteui://URI, but it only checksparsed.protocol === "ui:", soui:relativewill pass validation even though it is not an absoluteui://URI.
if (parsed.protocol !== "ui:") {
throw new Error("Elicitation UI resourceUri must be an absolute ui:// URI");
}
src/server/index.ts:546
withElicitationUiclaims to require an absoluteui://URI, but the validation only checksparsed.protocol === "ui:". This will incorrectly accept values likeui:relative(valid URL with protocolui:but notui://), 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
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (3)
src/server/index.ts:589
getElicitationUiResourceUricurrently acceptsui:/...because it only checks the URL protocol. To match the spec wording andgetToolUiResourceUribehavior, require theui://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 thenew URL()+ protocol check even though the API requires an absoluteui://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 likeui:/pathwill be accepted even though the error message (and other helpers likegetToolUiResourceUri) require an absoluteui://URI. Tighten the check to require theui://prefix (and ideally reject any non-form mode, not justmode === "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
There was a problem hiding this comment.
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 againstrequestedSchema) 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
| "capabilities": { | ||
| "extensions": { | ||
| "io.modelcontextprotocol/ui": { | ||
| "elicitation": {} |
There was a problem hiding this comment.
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.
This comment was marked as spam.
This comment was marked as spam.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
53b6efd to
bc25e0a
Compare
Summary
Add app-rendered form elicitation to the draft MCP Apps specification and SDK. The proposal is entirely contained in the existing
io.modelcontextprotocol/uiextension. Core SEP-3118 is closed because no core protocol change is required.Clients and servers opt in through the nested
elicitation: {}setting, alongside coreelicitation.formand the Apps MIME type. The server attaches_meta.ui.resourceUrito a complete standard form elicitation, preserving native rendering as a fallback. The host forwardselicitation/createto the specific initialized App and receives the standardElicitResult.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 version2026-01-26.SDK and host behavior
App.onelicitation,AppBridge.requestElicitation, capability types, metadata helpers, generated schemas, and tests.requestedSchema. Resource, initialization, bridge, and validation failures fall back to native rendering; explicitdeclineandcancelresults are preserved.The wire method and capability shape remain compatible with Inspector's current app-elicitation implementation. No
ui/elicitationmethod 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.bun test src: 367 passed, including metadata preservation, URL-mode rejection, decline/cancel results, and cancellation propagation.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.