Commit graph inspector/core/mcp/apps.ts
Author SHA1 Message Date
cliffhall
a42d7fd99e refactor: drop the ext-apps SDK-v1 shims now that 2.0.0 peers on SDK v2
Every site tagged `ext-apps#702` carried a workaround for ext-apps
peering on the v1 SDK while the Inspector runs v2. With 2.0.0 the
package's `AppBridge` extends the v2 `Protocol`, takes the v2 `Client`
and `Implementation` directly, and registers its list-changed forwarding
on that client with `setNotificationHandler("notifications/…", handler)`,
so:

- core/mcp/apps.ts: `getToolUiResourceUri(tool)` takes the v2 `Tool`
  without a cast; `UiMetaShape` is replaced by ext-apps' own
  `McpUiToolMeta` / `McpUiResourceMeta`, which resolve under NodeNext
  since ext-apps#705. `AppInfo.csp` / `.permissions` / `.visibility`
  take the named types too.
- core/mcp/inspectorClient.ts: `getAppRendererClient()` returns the SDK
  client itself; the Proxy that translated the 1.x schema-first
  `setNotificationHandler(Schema, handler)` into the method-string form,
  its `notificationMethodFromSchema` helper and its cached field are
  gone. `AppRendererClient` is now `Client` rather than `unknown`.
- createAppBridgeFactory.ts: `new AppBridge(client, HOST_INFO, …)` with
  no `as unknown as`; `resolveSourceUri` passes the tool straight through.
- AppRenderer.tsx: `sendToolResult(result)` — its params ARE the v2
  `CallToolResult` now.
- requestAppElicitation.ts: the double cast on the `elicitation/create`
  send goes too — v2 `Protocol.request` takes the standard `Request`
  shape, which `ElicitRequest` is. The helper itself stays: ext-apps#733
  is not in 2.0.0.
- core/mcp/extensions.ts: `MCP_APP_MIME_TYPE` re-exports ext-apps'
  `RESOURCE_MIME_TYPE` from `/app-bridge`; `UI_EXTENSION_KEY` stays
  hardcoded because `EXTENSION_ID` is still `/server`-only in 2.0.0.

Tests: the Proxy-translation test is removed with the Proxy; the
`getAppRendererClient` tests assert the real `Client` instance and that
disconnect withdraws it; the `useInspectorClient` sentinel is a real
(unconnected) `Client`. Comments that cited ext-apps 1.7.5 now cite 2.0.0
where the fact still holds (#733 still unreleased).

Closes #1745
Refs #2309

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsmUAZ6aLUAFqenjJKSSRp
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-09-13 19:21:06 -04:00
cliffhall
19a6df7ad3 SDK v2 migration (behavior-neutral, versionNegotiation: 'legacy') (#1624)
Migrate all three clients + core + test-servers from
@modelcontextprotocol/sdk@1.29 to SDK v2 (2.0.0-beta.4), keeping behavior
identical against legacy (2025-11-25) servers. Foundation for the SDK V2 +
New Spec stack.

Packages
- Split @modelcontextprotocol/sdk -> @modelcontextprotocol/client (Client,
  transports, OAuth, error taxonomy, protocol types) + core (Zod schemas);
  stdio -> client/stdio, Ajv -> client/validators/ajv. Test-servers ->
  @modelcontextprotocol/server (+ server-legacy/sse). Deterministic import
  rewrite (schemas -> core, everything else -> client); the codemod misroutes
  client code to /server so it was not used for path routing.
- new Client(..., { versionNegotiation: { mode: 'legacy' } }) — nothing
  2026-era goes on the wire.

Runtime (core/mcp/inspectorClient.ts)
- Error taxonomy: McpError -> ProtocolError, ErrorCode -> ProtocolErrorCode;
  RequestTimeout -> SdkError/SdkErrorCode.
- Handlers take method strings; task methods (tasks/*, notifications/tasks/*)
  use the 3-arg custom form (excluded from v2's spec-method set).
- Legacy tasks kept working (per project requirement): requestor tasks re-
  implemented over raw client.request + explicit deprecated schemas;
  experimental.tasks.callToolStream replaced by a local pollTaskToolCall
  async generator (with _progressHandlers revival so task-execution progress
  still reaches the caller). Receiver (server-initiated) tasks: the v2 Client
  validates a sampling/elicit handler result and rejects the legacy { task }
  response with -32602 — installReceiverTaskResponseBypass swaps the wrapped
  entry in the private _requestHandlers map for the task-augmented branch so
  { task } reaches the wire (fixes server-initiated sampling-via-task).
- Pagination: list verbs use raw client.request({method}, List*ResultSchema)
  for single-page behavior (v2's client.listX() auto-aggregate all pages).

Web / auth / apps
- Rename local JsonSchemaType -> InspectorFormSchema + add toFormSchema()
  structural narrowing at the SDK-schema -> form boundary.
- ext-apps still peers on SDK v1: one compat boundary with documented casts
  (remove when ext-apps#702 lands a v2 peer).
- OAuth provider saveClientInformation reconciled with v2's
  OAuthClientInformationContext; InvalidClientMetadataError re-homed as a
  local class (server-legacy/auth is not browser-safe).
- vitest.shared.mts dedupes @modelcontextprotocol/client+core to one copy so
  vi.mock intercepts the SUT's import.

Accepted v2 behavior changes (per #1624 / spec §9.2): unknown-tool call
rejects with -32602; cancellation is stream-abort (no guaranteed
notifications/cancelled frame); output-schema strictness restored via the
Inspector's own Ajv check on the default path.

npm run ci green: validate (web 3087 / cli 212 / tui 274 / launcher 5),
coverage (>=90 per-file, all clients), smoke (launcher/cli/web), storybook
(410).

Closes #1624

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:44:24 -04:00
cliffhall
ee4b232a1f refactor(core): narrow readUiMeta from unknown; document ui-meta precedence
Address round-1 review nits on apps.ts:
- readUiMeta now accepts `unknown` and narrows internally, removing the
  three `as WithUiMeta` casts at its call sites (readability win).
- Add a comment documenting that the content-block-then-result precedence
  for `_meta.ui` is intentional (posture lives on the content block; no
  shallow merge).
- Add a TODO to switch to the named McpUiToolMeta/McpUiResourceMeta types
  once the upstream extensionless re-export is fixed for NodeNext.

Coverage: apps.ts 100% lines / 97.05% branch / 100% funcs — clears the gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
2026-07-01 19:14:16 -04:00
cliffhall
1a6e32be50 feat(core): add AppInfo + extractAppInfo() to core/mcp/apps.ts (closes #1556)
Add a machine-readable summary of an MCP App's UI metadata: the AppInfo
interface plus extractAppInfo(), which merges a tool's _meta.ui
(resourceUri, visibility) with the linked UI resource's _meta.ui (csp,
permissions, domain, prefersBorder, mimeType). Content-block matching is
exact-URI first, then a lowercase + trailing-slash normalized match, then
a single-block fallback since resources/read returns the requested
resource by definition. Shared plumbing for the programmatic-review path
(CLI --app-info, integration tests) without rendering the app.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
2026-07-01 18:20:13 -04:00
Cliff Hall
8fa1a0c074
feat(apps): add isAppTool helper in core (#1260) (#1272)
* feat(apps): add isAppTool helper in core (#1260)

Adds @modelcontextprotocol/ext-apps as a dependency (root + clients/web)
and a thin core/mcp/apps.ts wrapper exposing:

- getAppResourceUri(tool): re-export of getToolUiResourceUri so all
  Inspector clients consume the same nested/flat _meta resolution.
- isAppTool(tool): boolean predicate built on top of getAppResourceUri.

Both surface the underlying ext-apps throw on a malformed _meta.ui.resourceUri
(string that does not start with "ui://") rather than swallowing it — server
bugs should be visible.

Tests cover nested format, deprecated flat format, nested-wins precedence,
missing _meta, and the invalid-URI throw.

Refs #1260

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(apps): address PR #1272 review feedback

- core/mcp/apps.ts: convert getAppResourceUri from a typed const re-export to
  a function declaration so its export style matches isAppTool. Behavior is
  unchanged.
- core/mcp/apps.ts: document the filter-iteration implication on isAppTool —
  callers using tools.filter(isAppTool) will halt on a server-malformed URI.
- apps.test.ts: add the symmetric isAppTool false-case for `_meta.ui` present
  without `resourceUri`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:03:11 -04:00