Goal
Implement negotiated, app-rendered form elicitations in Inspector v2 so that a server can ask an MCP App to render and resolve a standard elicitation/create request. Keep the native Inspector elicitation UI as the fallback.
This should consume the MCP Apps draft/API proposed in:
Related design discussion:
This issue is intentionally server-agnostic. The fixture and examples must not depend on a private or product-specific server.
Protocol contract
App rendering is selected only when all four conditions hold:
-
The Inspector client advertises core form elicitation support:
{
"elicitation": {
"form": {}
}
}
-
The Inspector client advertises app-rendered elicitation support in the existing MCP Apps extension:
{
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"],
"elicitation": {}
}
}
}
-
The server advertises the corresponding MCP Apps capability:
{
"extensions": {
"io.modelcontextprotocol/ui": {
"elicitation": {}
}
}
}
-
The elicitation/create request contains a valid absolute ui:// URI:
{
"_meta": {
"ui": {
"resourceUri": "ui://example/choose-option"
}
}
}
Do not introduce a second extension, a custom elicitation method, a custom result shape, or a protocol-level retry mechanism. The app returns the ordinary MCP ElicitResult, including accept, decline, and cancel.
Only form mode is app-renderable. An omitted mode is treated as form; url mode continues through its existing path.
Inspector capability advertisement
The nested MCP Apps elicitation capability must be advertised only by an Inspector client that can actually host MCP Apps and resolve requests through an app bridge. In particular, the web client may opt in when its sandbox renderer is available; CLI/TUI clients must not claim the capability merely because the shared InspectorClient knows the MCP Apps MIME type.
Preserve the existing core elicitation toggle. If form elicitation is disabled, omit both the core form capability and the nested MCP Apps elicitation capability.
Request routing and ownership
Extend the existing standard elicitation/create request handler in core/mcp/inspectorClient.ts. For every incoming request:
- Check the negotiated client and server capabilities.
- Read and validate
_meta.ui.resourceUri.
- If eligible, create or select the renderer instance owned by that exact request.
- Load the requested MCP App resource and complete
ui/initialize.
- Confirm that the app advertised
appCapabilities.elicitation.
- Forward the original
elicitation/create request unchanged through that renderer's AppBridge.
- Validate the returned standard
ElicitResult and form content against the requested schema.
- Resolve the original server request with that result.
The association must be request-scoped—for example, a map keyed by request ID that owns the resource URI, renderer/bridge instance, promise, and cancellation state. Do not use a global or "currently active app" bridge. Concurrent elicitations must not cross wires.
The host-side bridge must advertise:
{
"hostCapabilities": {
"elicitation": {}
}
}
Use the ext-apps helpers introduced by #733 (or their released equivalents) to test negotiated support, extract the resource URI, and issue the request through the exact bridge instance.
Renderer integration
The current AppRenderer is tool-result-oriented and creates its bridge from a Tool. Either generalize it or add a focused elicitation renderer so it can:
- resolve a resource from an explicit absolute
ui:// URI;
- expose initialization and app-capability state;
- send an
elicitation/create request through its exact AppBridge;
- resolve or reject a request-owned promise;
- tear down on completion, abort, disconnect, or navigation;
- render multiple concurrent requests without sharing mutable bridge state.
The app itself must be able to register App.onelicitation and return:
{ "action": "accept", "content": { "choice": "option-a" } }
or:
or:
Native fallback
Fall back to the existing Inspector elicitation queue/UI when any of the following happens before a valid app result is obtained:
- either peer did not negotiate app-rendered elicitation;
- metadata is absent, malformed, non-absolute, non-
ui://, or otherwise unusable;
- resource read or rendering fails;
- sandbox or bridge initialization fails;
- the app does not advertise elicitation support;
- the bridge request fails or times out;
- the app returns an invalid result or accepted content that fails schema validation.
An explicit, valid decline or cancel is a completed elicitation and must be returned to the server without opening the native fallback UI.
Public test fixture
Add a small, generic test server/app fixture that:
- advertises the server-side MCP Apps elicitation capability;
- serves a self-contained MCP App resource;
- requests a simple form choice using
_meta.ui.resourceUri;
- can return or exercise modern MRTR
input_required behavior where applicable;
- demonstrates
accept, decline, and cancel;
- has no dependency on a private application or service.
The UI can be as simple as "Choose option A or B." Its purpose is to verify the protocol and renderer lifecycle, not to be a product example.
Acceptance criteria
- Capability serialization tests cover web opt-in, form-disabled behavior, and non-rendering clients.
- Handler tests cover all four negotiation gates.
- Renderer/bridge tests prove the request is delivered to the exact app instance and its standard result is returned unchanged.
- Two simultaneous requests for different resource URIs cannot resolve through each other's bridges.
- Native fallback is covered for absent capability, invalid metadata, resource/init failure, missing app capability, timeout, invalid result, and schema mismatch.
- Explicit
decline and cancel do not trigger fallback.
- A browser integration test exercises the public fixture end to end.
- Existing native elicitation and task/MRTR behavior remains passing.
Suggested implementation areas
core/mcp/inspectorClient.ts
core/mcp/apps.ts
clients/web/src/components/elements/AppRenderer/
- the existing elicitation state/panel hooks
- Inspector v2 test-server fixtures and browser tests
The implementation should consume an early or released version of @modelcontextprotocol/ext-apps containing #733 rather than creating an Inspector-local protocol variant.
Goal
Implement negotiated, app-rendered form elicitations in Inspector v2 so that a server can ask an MCP App to render and resolve a standard
elicitation/createrequest. Keep the native Inspector elicitation UI as the fallback.This should consume the MCP Apps draft/API proposed in:
Related design discussion:
This issue is intentionally server-agnostic. The fixture and examples must not depend on a private or product-specific server.
Protocol contract
App rendering is selected only when all four conditions hold:
The Inspector client advertises core form elicitation support:
{ "elicitation": { "form": {} } }The Inspector client advertises app-rendered elicitation support in the existing MCP Apps extension:
{ "extensions": { "io.modelcontextprotocol/ui": { "mimeTypes": ["text/html;profile=mcp-app"], "elicitation": {} } } }The server advertises the corresponding MCP Apps capability:
{ "extensions": { "io.modelcontextprotocol/ui": { "elicitation": {} } } }The
elicitation/createrequest contains a valid absoluteui://URI:{ "_meta": { "ui": { "resourceUri": "ui://example/choose-option" } } }Do not introduce a second extension, a custom elicitation method, a custom result shape, or a protocol-level retry mechanism. The app returns the ordinary MCP
ElicitResult, includingaccept,decline, andcancel.Only
formmode is app-renderable. An omitted mode is treated asform;urlmode continues through its existing path.Inspector capability advertisement
The nested MCP Apps
elicitationcapability must be advertised only by an Inspector client that can actually host MCP Apps and resolve requests through an app bridge. In particular, the web client may opt in when its sandbox renderer is available; CLI/TUI clients must not claim the capability merely because the sharedInspectorClientknows the MCP Apps MIME type.Preserve the existing core elicitation toggle. If form elicitation is disabled, omit both the core form capability and the nested MCP Apps elicitation capability.
Request routing and ownership
Extend the existing standard
elicitation/createrequest handler incore/mcp/inspectorClient.ts. For every incoming request:_meta.ui.resourceUri.ui/initialize.appCapabilities.elicitation.elicitation/createrequest unchanged through that renderer'sAppBridge.ElicitResultand form content against the requested schema.The association must be request-scoped—for example, a map keyed by request ID that owns the resource URI, renderer/bridge instance, promise, and cancellation state. Do not use a global or "currently active app" bridge. Concurrent elicitations must not cross wires.
The host-side bridge must advertise:
{ "hostCapabilities": { "elicitation": {} } }Use the ext-apps helpers introduced by #733 (or their released equivalents) to test negotiated support, extract the resource URI, and issue the request through the exact bridge instance.
Renderer integration
The current
AppRendereris tool-result-oriented and creates its bridge from aTool. Either generalize it or add a focused elicitation renderer so it can:ui://URI;elicitation/createrequest through its exactAppBridge;The app itself must be able to register
App.onelicitationand return:{ "action": "accept", "content": { "choice": "option-a" } }or:
{ "action": "decline" }or:
{ "action": "cancel" }Native fallback
Fall back to the existing Inspector elicitation queue/UI when any of the following happens before a valid app result is obtained:
ui://, or otherwise unusable;An explicit, valid
declineorcancelis a completed elicitation and must be returned to the server without opening the native fallback UI.Public test fixture
Add a small, generic test server/app fixture that:
_meta.ui.resourceUri;input_requiredbehavior where applicable;accept,decline, andcancel;The UI can be as simple as "Choose option A or B." Its purpose is to verify the protocol and renderer lifecycle, not to be a product example.
Acceptance criteria
declineandcanceldo not trigger fallback.Suggested implementation areas
core/mcp/inspectorClient.tscore/mcp/apps.tsclients/web/src/components/elements/AppRenderer/The implementation should consume an early or released version of
@modelcontextprotocol/ext-appscontaining #733 rather than creating an Inspector-local protocol variant.