This guide exercises the public, server-agnostic reference implementation for rendering a standard MCP form elicitation in an MCP App. It covers:
- MCP core protocol revision
2026-07-28 - multi-round-trip requests (MRTR)
- capability negotiation through core
elicitation.formplus the existingio.modelcontextprotocol/uiextension - resolution through a standard
ElicitResult - native-form fallback when App rendering or negotiation is unavailable
No second MCP extension or product-specific server is involved.
- Inspector reference PR: krubenok/inspector#1
- Inspector branch:
agent/app-rendered-elicitations-reference - Known-good Inspector commit:
3b524cd1202ed8dbb4687399a7abace7e1c1d6ab - MCP Apps amendment: modelcontextprotocol/ext-apps#733
- Pinned ext-apps commit:
89ab2bcbf066ca21f1bd38cb115949f857950b7a
The reference branch pins the ext-apps commit directly, so no private registry or unpublished package is required.
- Git
- Node.js
>=22.19.0 - npm
- Ports
3102and6274available on localhost
git clone \
--branch agent/app-rendered-elicitations-reference \
--single-branch \
https://github.com/krubenok/inspector.git \
inspector-app-elicitation-demo
cd inspector-app-elicitation-demo
npm install
npm run build:web
npm run build:launcherTo reproduce the exact version tested when this guide was written:
git checkout 3b524cd1202ed8dbb4687399a7abace7e1c1d6ab
npm install
npm run build:web
npm run build:launcherIn terminal 1, from the repository root:
cd clients/web
npm run test-servers:build
node ../../test-servers/build/server-composable.js \
--config ../../test-servers/configs/modern-app-elicitation-http.jsonExpected output includes:
Composable server listening at http://127.0.0.1:3102/mcp
The fixture contains:
mrtr_confirm, a tool that returnsinput_requiredon its first call- a complete native boolean schema
- opaque
requestState _meta.ui.resourceUri- a self-contained
text/html;profile=mcp-appresource with Accept, Decline, and Cancel
In terminal 2, from the repository root:
mkdir -p .demo-state/storage
MCP_CATALOG_PATH="$PWD/.demo-state/mcp.json" \
MCP_STORAGE_DIR="$PWD/.demo-state/storage" \
MCP_INSPECTOR_API_TOKEN=local-token \
MCP_AUTO_OPEN_ENABLED=false \
npm run webOpen:
http://localhost:6274/?MCP_INSPECTOR_API_TOKEN=local-token
The isolated .demo-state directory keeps this walkthrough from modifying an
existing Inspector catalog.
- Open Servers.
- Select Add Servers, then add a server manually.
- Name it
apps-elicitation-demo. - Select Streamable HTTP and enter
http://localhost:3102/mcp. - Open the server's Settings.
- Under Options, set Protocol Era to Modern (2026-07-28, sessionless).
- Close settings, then disconnect and reconnect the server.
Verify:
- the server card says
MCP 2026-07-28 - the Protocol sidebar says
Modern - the first negotiation request is
server/discover, not legacyinitialize
- Open Tools.
- Select
mrtr_confirm. - Enter an action, for example
publish demo. - Select Execute Tool.
- Wait for the App Elicitation Request modal.
- Select Accept.
The final result should be:
MRTR complete — confirmation for "publish demo":
{"action":"accept","content":{"confirm":true}}
The Protocol sidebar should group the exchange as an MRTR conversation that moves from Awaiting input to Complete.
Repeat the call with Decline and Cancel if you want to verify all three standard elicitation actions. Decline and cancel do not include accepted content.
On the happy path:
- The client advertises core
elicitation.formandextensions["io.modelcontextprotocol/ui"].elicitation. - The server advertises the matching nested MCP Apps capability.
- The tool returns
input_requiredwith a complete native form and a valid absoluteui://resource URI. - Inspector loads the resource in the existing MCP Apps sandbox.
- The App and host negotiate the first-class App bridge
elicitationcapability. - The App returns a standard elicitation result.
- Inspector validates that result and retries the same
tools/callwith:- unchanged arguments
- a new JSON-RPC request id
- the echoed opaque
requestState inputResponses.confirm
- The server completes the tool call.
The MCP Apps bridge still negotiates its own 2026-01-26 bridge protocol
version. That is independent of the MCP core protocol revision 2026-07-28.
Inspector must retain and show the complete native form instead of the App when any of these conditions is not met:
- either peer omits the nested MCP Apps
elicitationcapability - the client does not support core
elicitation.form _meta.ui.resourceUriis missing or is not a valid absoluteui://URI- the resource cannot be loaded
- the App or host omits its bridge
elicitationcapability - App initialization, bridge negotiation, or result validation fails
This is why the request always carries a complete native form: the App is an enhanced renderer for the standard elicitation, not a separate elicitation protocol.
If npm reports that dependency build scripts were blocked:
npm approve-scripts @modelcontextprotocol/ext-apps
npm installThen rerun the web and launcher builds.
Changing Protocol Era does not renegotiate an active connection. Disconnect and reconnect after selecting Modern (2026-07-28, sessionless).
Check the Protocol sidebar and server settings first. Confirm that both peers
advertised extensions["io.modelcontextprotocol/ui"].elicitation, that the
client also advertised core elicitation.form, and that the request contains a
valid _meta.ui.resourceUri.
Stop the existing process using port 3102 or 6274, or update the fixture
config and corresponding Inspector URL.
- Core SEP: modelcontextprotocol/modelcontextprotocol#3118
- MCP Apps draft and TypeScript App API: modelcontextprotocol/ext-apps#733
- TypeScript SDK reference: modelcontextprotocol/typescript-sdk#2578
- C# SDK implementation: modelcontextprotocol/csharp-sdk#1723
- Inspector tracking issue: modelcontextprotocol/inspector#1854
- Inspector reference implementation: krubenok/inspector#1