inspector/clients/web
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Cliff Hall 5cabc1ecd7
feat(web): URL elicitation — explicit completion step + error-path (-32042) handling (#1415) (#1436)
* feat(web): two-step URL elicitation completion (#1415)

Replace accept-on-open with an explicit completion step for URL
elicitations. Opening the URL now only transitions the panel to its
waiting state; the elicitation resolves with `accept` only when the user
clicks the revealed "I've completed it" action, or with `cancel`.

- ElicitationUrlPanel: add `onComplete`, reveal the completion button and
  relabel open→"Reopen in Browser" while `isWaiting`.
- PendingClientRequestModal: wire local `isWaiting`; open no longer sends
  a response. Removed the accept-on-open comment that pointed here.
- Tests cover open→waiting→complete and open→cancel, and assert opening
  alone does not resolve the elicitation.

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

* feat(web): handle URL-elicitation-required (-32042) error path

Implements the spec's "URL mode with elicitation required error" flow
(2025-11-25). When a tools/call returns a `-32042`
UrlElicitationRequired error, the client now surfaces the required URL
elicitation(s) and retries the call once completed, instead of showing a
bare error.

Core (InspectorClient.callTool):
- Detect `-32042` (typed UrlElicitationRequiredError or generic McpError)
  via the new `getUrlElicitationsFromError` helper.
- Surface each carried elicitation in order through the existing pending-
  elicitation queue / modal (reusing the two-step "Open → I've completed
  it" UI), then re-issue the original call once all are accepted. A
  decline/cancel aborts with a clear message; a bounded retry count guards
  a server that keeps returning the error.
- `ElicitationCreateMessage.completeIfPending()` lets the optional
  `notifications/elicitation/complete` auto-advance/auto-accept an open
  URL elicitation; the completion-notification handler now resolves
  (was: only removed) the pending entry.
- Extracted `attemptToolCall` / `dispatchFailedToolCall` from the old
  callTool body to support the retry loop without duplicating bookkeeping.

Web (App):
- A non-spec `-32042` with no elicitations (nothing to open) shows a
  dedicated yellow toast linking to a new UrlElicitationErrorModal with
  the raw error body, mirroring the output-schema-validation toast.

Tests: helper unit tests, completeIfPending unit tests, callTool
error-path retry/cancel/no-list/ordinary-error tests (injected fake
client), the new modal (test + stories), and an App toast test.

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

* feat(web): abort URL-elicitation error path on a repeated URL

Guard the -32042 retry loop: if the server's retry error re-requests a
URL the user already completed earlier in the same call, completing it
again can't make progress, so callTool aborts with a new typed
UrlElicitationLoopError instead of re-prompting (which would loop until
the retry cap). The web layer detects it and shows a "URL elicitation
loop" toast naming the repeated URL rather than a generic failure.

Tests: core abort-on-repeat test (URL presented once, two attempts, then
loop error) and an App loop-toast test.

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

* fix(web): let Copy-URL reach the URL-elicitation completion step

Address review of #1436: the "I've completed it" action was gated behind
clicking "Open in Browser" (only that set isWaiting), so a user who used
Copy URL to finish the flow externally could only Cancel. Copying the URL
now also moves the panel into the waiting state, exposing the completion
step. Also trims the now-redundant second sentence of the waiting hint.

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

* refactor(core): address review of error-path URL elicitation

Review follow-ups on #1436 (all non-blocking):
- Remove the leftover bare block scope in attemptToolCall (extraction
  artifact); body de-indented one level.
- Log a warning when callTool gives up after the URL-elicitation retry
  cap is hit, so a server that keeps demanding new elicitations is
  diagnosable rather than looking like an ordinary failure.
- Assert the dispatch-once invariant on the loop-abort and cancel-abort
  paths (exactly one failed toolCallResultChange event).

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

* style(core): fix attemptToolCall body indentation

Run prettier over the extracted attemptToolCall method body, which was left
over-indented by 2 spaces after the bare-block removal in 0754b43d. core/ is
outside clients/web's format:check scope, so npm run validate did not catch it.

Whitespace-only; the pre-existing line 658 wrap (also outside the format scope)
is left as-is, out of scope for this PR.

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

* fix(core): settle pending elicitations on disconnect to avoid callTool hang

Address review of #1436: an error-path `awaitUrlElicitation` promise only
settled via respond, but `disconnect()` cleared `pendingElicitations`
wholesale — so a disconnect mid-elicitation (server crash, manual
disconnect, transport close) left `callTool` (and the UI's in-flight
toolCallState) hanging forever.

- Add `ElicitationCreateMessage.cancel()`: settles a pending elicitation
  as `{ action: "cancel" }` without calling `onRemove` (the queue is
  cleared by the caller, so it must not splice mid-iteration).
- `disconnect()` now cancels each pending elicitation before clearing the
  array, and dispatches `pendingElicitationsChange` (it already did so for
  samples) so the modal closes on a transport-close teardown too.

Tests: disconnect-mid-elicitation settles callTool (no hang); cancel()
unit tests (resolves-without-remove, no-op once responded).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 18:25:01 -04:00
..
.storybook Phase 1: Style refinements and component tweaks from Storybook review 2026-04-17 19:26:21 -04:00
public feat(connection-info): wire Connection Info modal to the connection handshake (#1377) 2026-05-30 16:42:37 -04:00
server fix(dev): handle client disconnect in the dev proxy stream pump (#1418) (#1419) 2026-06-02 19:08:48 -04:00
src feat(web): URL elicitation — explicit completion step + error-path (-32042) handling (#1415) (#1436) 2026-06-06 18:25:01 -04:00
static Port v1.5 web dev backend (vite-hono-plugin + remote-server bootstrap) (#1321) 2026-05-18 19:22:14 -04:00
.gitignore test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
chromatic.config.json add chromatic.config.json 2026-03-22 15:42:09 -04:00
eslint.config.js test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
index.html feat(connection-info): wire Connection Info modal to the connection handshake (#1377) 2026-05-30 16:42:37 -04:00
package-lock.json feat(servers): move oauthClientSecret and stdio env values to OS keychain (#1356) (#1360) 2026-05-26 10:33:46 -04:00
package.json chore(web): silence benign config-load dep warnings in npm run dev (#1408) 2026-06-01 19:48:12 -04:00
README.md * Added base project to clients/web 2026-03-16 22:47:03 -04:00
tsconfig.app.json feat(servers): move oauthClientSecret and stdio env values to OS keychain (#1356) (#1360) 2026-05-26 10:33:46 -04:00
tsconfig.json test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
tsconfig.node.json feat(servers): move oauthClientSecret and stdio env values to OS keychain (#1356) (#1360) 2026-05-26 10:33:46 -04:00
tsconfig.storybook.json fix dark mode, storybook config, and minor UI issues 2026-03-23 13:12:37 -04:00
tsconfig.test.json feat(servers): move oauthClientSecret and stdio env values to OS keychain (#1356) (#1360) 2026-05-26 10:33:46 -04:00
vite.config.ts feat(servers): move oauthClientSecret and stdio env values to OS keychain (#1356) (#1360) 2026-05-26 10:33:46 -04:00
vitest.shims.d.ts * Added base project to clients/web 2026-03-16 22:47:03 -04:00

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])