Commit graph inspector/client/src/utils
Author SHA1 Message Date
John Howard
695972a404
oauth: add tenant suffix to oauth flow (#1096) 2026-02-19 06:00:33 -07:00
Ola Hungerford
442638f4c1
Merge branch 'main' into fix/enum-anyof-ref-resolution 2026-02-04 05:41:56 -07:00
Cliff Hall
7ef0971c81
Add Tasks support (#1013)
* ### client/src/App.tsx

- Imports:
  - Added: `Task` and `GetTaskResultSchema` to the same import block.
  - UI Icons: Added `ListTodo` from `lucide-react` to the icon imports.
  - Components: Added `TasksTab` import beside `ToolsTab`.
  - Config utils: Added `getMCPTaskTtl` to the config utils import block.

- State additions:
  - `const [tasks, setTasks] = useState<Task[]>([]);`
  - Extended `errors` state to include a `tasks` key: `tasks: null,`
  - `const [selectedTask, setSelectedTask] = useState<Task | null>(null);`
  - `const [isPollingTask, setIsPollingTask] = useState(false);`
  - `const [nextTaskCursor, setNextTaskCursor] = useState<string | undefined>();`

- Hook: `useConnection({...})` return value usage extended
  - Added destructured functions: `cancelTask: cancelMcpTask` and `listTasks: listMcpTasks` from the custom hook.

- Notification handling:
  - In `onNotification`, added:
    - If `method === "notifications/tasks/list_changed"`, call `listTasks()` (voided).
    - If `method === "notifications/tasks/status"`, treat `notification.params` as a `Task` and update `tasks` state: replace if exists by `taskId`, otherwise prepend. Also update `selectedTask` if it’s the same `taskId`.

- Tab routing:
  - When computing valid tabs, added `tasks` when server declares capability: `...(serverCapabilities?.tasks ? ["tasks"] : []),`
  - When choosing a default tab, added a branch to fall back to `"tasks"` if neither resources/prompts/tools are present but tasks are.

- Effect for Tasks tab:
  - When `mcpClient` is connected and `activeTab === "tasks"`, invoke `listTasks()`.

- Tools → task-augmented calls integration in `callTool`:
  - Parameter signature supports `runAsTask?: boolean` (already present in this file), but now:
    - If `runAsTask` is true, augment the `tools/call` request’s `params` with a `task` object: `{ task: { ttl: getMCPTaskTtl(config) } }`.
  - Use a permissive result schema for tool call: `sendMCPRequest(request, z.any(), "tools")` to avoid version-mismatch schema issues.
  - Task reference detection introduced:
    - `isTaskResult` helper checks for a nested `task` object with `taskId` (i.e., `response.task.taskId`).
  - When task is detected:
    - Set `isPollingTask(true)`.
    - Immediately set a temporary `toolResult` that includes text content “Task created: … Polling for status…” and `_meta` with `"io.modelcontextprotocol/related-task": { taskId }`.
    - Start a polling loop:
      - Delay 1s between polls.
      - Call `tasks/get` with `GetTaskResultSchema` for status.
      - If status is `completed`: call `tasks/result` with `z.any()` to retrieve the final result and set it as `toolResult`; call `listTasks()`.
      - If status is `failed` or `cancelled`: set an error `toolResult` content that includes the status + `statusMessage`; call `listTasks()`.
      - Else (still running): update `toolResult` content with current `status`/`statusMessage` and preserve `_meta` related-task.
    - After loop, set `isPollingTask(false)`.
  - When not a task response, set `toolResult` directly from response (cast to `CompatibilityCallToolResult`).

- Tasks list + cancel helpers in App:
  - `listTasks`: uses `listMcpTasks(nextTaskCursor)` from the hook, updates `tasks`, `nextTaskCursor`, and clears `errors.tasks`.
  - `cancelTask`: calls `cancelMcpTask(taskId)`, updates `tasks` array by `taskId`, updates `selectedTask` if it matches, and clears `errors.tasks`.

- UI integration:
  - Added a `TabsTrigger` for “Tasks” with `<ListTodo />` icon, disabled unless server supports tasks.
  - Added `<TasksTab />` to the main `TabsContent` block, passing: `tasks`, `listTasks`, `clearTasks`, `cancelTask`, `selectedTask`, `setSelectedTask`, `error={errors.tasks}`, `nextCursor={nextTaskCursor}`.
  - Passed `isPollingTask={isPollingTask}` and `toolResult` into `ToolsTab` so the Tools tab can show the live “Polling Task…” state and block reruns while polling.

Note: The raw diff is long; the key hunks align with the above bullet points (imports, state, notifications, tab wiring, request augmentation, polling loop, UI additions).

---

### client/src/components/ToolsTab.tsx

- Props shape changed:
  - Added `isPollingTask?: boolean` prop in the destructured props and in the prop types.
  - The `callTool` callback signature is now `(name, params, metadata?, runAsTask?) => Promise<void>` (runAsTask added earlier; test updates elsewhere reflect this).

- Local state additions:
  - `const [runAsTask, setRunAsTask] = useState(false);`

- Reset behavior:
  - When switching tools (`useEffect` on `selectedTool`), reset `runAsTask(false)`.
  - When clearing the list in `ListPane.clearItems`, also call `setRunAsTask(false)`.

- UI additions:
  - New checkbox control block to toggle “Run as task”:
    - Checkbox `id="run-as-task"`, bound to `runAsTask`, with `onCheckedChange` → `setRunAsTask(checked)`.
    - Label “Run as task”.
  - Run button disabling conditions expanded to include `isPollingTask`.
  - Run button text shows spinner with conditional label:
    - If `isToolRunning || isPollingTask` → show spinner and text `isPollingTask ? "Polling Task..." : "Running..."`.

- Call invocation change:
  - When clicking “Run Tool”, the `callTool` is invoked with `(selectedTool.name, params, metadata?, runAsTask)`.

- ToolResults relay:
  - Passes `isPollingTask` to `<ToolResults />`.

---

### client/src/components/ToolResults.tsx

- Props shape changed:
  - Added optional prop: `isPollingTask?: boolean`.

- Task-running banner logic:
  - Extracts related task from the tool result’s `_meta["io.modelcontextprotocol/related-task"]` if present.
  - Computes `isTaskRunning` as `isPollingTask ||` a text-heuristic against `structuredResult.content` entries that contain text like “Polling” or “Task status”.
  - Header “Tool Result:” now conditionally shows:
    - `Error` (red) if `isError` is true, else
    - `Task Running` (yellow) if `isTaskRunning`, else
    - `Success` (green).

No other changes to validation or rendering of content blocks.

---

### client/src/components/TasksTab.tsx (new file)

- A brand new tab to list and inspect tasks.
- Key elements:
  - Imports `Task` type and multiple status icons.
  - `TaskStatusIcon` component maps task `status` to an icon and color.
  - Main `TasksTab` props: `tasks`, `listTasks`, `clearTasks`, `cancelTask`, `selectedTask`, `setSelectedTask`, `error`, `nextCursor`.
  - Left column (`ListPane`): lists tasks, shows status icon, `taskId`, `status`, and last update time; button text changes to “List More Tasks” if `nextCursor` present; disables button if no cursor and list non-empty.
  - Right column:
    - Shows error `Alert` if `error` prop provided.
    - If a task is selected: header with `Task Details`, a Cancel button when `status === "working"` (shows a spinner while cancelling), and a grid of task fields: Status (with colored label and icon), Last Updated, Created At, TTL (shows “Infinite” if `ttl === null`, otherwise shows numeric with `s` suffix), optional Status Message, and full task JSON via `JsonView`.
    - If no task is selected: centered empty state with a “Refresh Tasks” button.

---

### client/src/lib/hooks/useConnection.ts

- Imports added from `@modelcontextprotocol/sdk/types.js`:
  - `ListTasksResultSchema`, `CancelTaskResultSchema`, `TaskStatusNotificationSchema`.

- Client capabilities on `connect`:
  - Added `tasks: { list: {}, cancel: {} }` into the `clientCapabilities` passed to `new Client(...)`.

- Notification handling setup:
  - The hook’s notification schema registration now includes the `TaskStatusNotificationSchema` in the `setNotificationHandler` list so the app receives `notifications/tasks/status`.

- New hook functions:
  - `cancelTask(taskId: string)` sends `tasks/cancel` with `CancelTaskResultSchema`.
  - `listTasks(cursor?: string)` sends `tasks/list` with `ListTasksResultSchema`.

- Exports:
  - Returned object now includes `cancelTask` and `listTasks`.

---

### client/src/utils/configUtils.ts

- Added a new getter:
  - `export const getMCPTaskTtl = (config: InspectorConfig): number => { return config.MCP_TASK_TTL.value as number; };`

---

### client/src/lib/configurationTypes.ts

- `InspectorConfig` type extended with a new item:
  - `MCP_TASK_TTL: ConfigItem;`
  - Includes descriptive JSDoc about default TTL in milliseconds for newly created tasks.

---

### client/src/lib/constants.ts

- `DEFAULT_INSPECTOR_CONFIG` extended with a default for task TTL:
  - Key: `MCP_TASK_TTL`
  - Label: `"Task TTL"`
  - Description: `"Default Time-to-Live (TTL) in milliseconds for newly created tasks"`
  - Default `value: 60000`
  - `is_session_item: false`

---

### client/src/components/__tests__/ToolsTab.test.tsx

- Expectations updated due to new `callTool` signature (4th arg `runAsTask`). Everywhere the test asserts a `callTool` invocation, an additional trailing `false` argument was added to reflect the default state when the box isn’t checked.
  - Examples of added trailing `false` at various assertion points (line offsets from diff): after calls around prior lines 132, 157, 193, 236, 257, 279, 297, 818, 1082 (now passing 4 arguments: name, params, metadata-or-undefined, false).

---

### Additional notes

- The Tasks feature is wired end-to-end:
  - Client capability declaration (list/cancel)
  - Notification handler for `notifications/tasks/status`
  - Tools call augmentation with `task` `{ ttl }`
  - Polling loop using `tasks/get` and `tasks/result`
  - UI feedback in Tools and dedicated Tasks tab
  - Configurable TTL via new config item and getter

* Fix issue where displayed task on task tab was not updated when task completes, leaving the cancel task button available.

* Handle button the same when input_requred or working state

* ### What changed
- Added **receiver-side (client-side) Tasks support** to the Inspector client for task-augmented **server → client** requests.
- When the server sends `sampling/createMessage` or `elicitation/create` with `params.task`, the client now:
  - Creates a local task and immediately returns a `CreateTaskResult` (`{ task: ... }`).
  - Runs the underlying user-driven flow asynchronously.
  - Updates task state to `completed` / `failed` and makes the final payload available via `tasks/result`.
- Implemented server polling handlers backed by the local task store:
  - `tasks/get`
  - `tasks/result` (blocks until payload is ready)
  - `tasks/list`
  - `tasks/cancel`
- Advertises task-augmented request capabilities via `capabilities.tasks.requests.sampling.createMessage` and `capabilities.tasks.requests.elicitation.create` when the corresponding callbacks are provided.
- Emits best-effort `notifications/tasks/status` updates as local task status changes.

* Fix review requests.

* In the ToolsTab, when a tool call results in an elicitation request, the app automatically switches to the elicitation tab, and once submitted, switches back to the ToolsTab to view the result of the tool call.

However, when a tool call results in a sampling request, it does not switch automatically to the SamplingTab and when the sampling request form is submitted it does not automatically switch back to the ToolsTab to view the result of the tool call.

These changes make the sampling use case work like the elicitation one. Tests were added.
2026-01-22 21:40:59 -07:00
Ola Hungerford
5778a3a93e
Merge pull request #926 from bolinfest/sapling-pr-archive-bolinfest
fix: allow empty elicitation form data when all fields are optional
2025-12-11 21:34:12 -07:00
cliffhall
fd99d879c5 DynamicJsonForm improvements and full enum schema support
In DynamicJsonForm.tsx,
  - Support all five enum shapes used by MCP SDK:
    - Titled single-select via `oneOf`/`anyOf` with `{ const, title }`
    - Untitled single-select via `enum`
    - Titled legacy single-select with optional `enumNames` for labels
    - Titled multi-select via `items.anyOf`/`oneOf`
    - Untitled multi-select via `items.enum`
  - Show field descriptions for string select fields and boolean checkboxes (consistent help text above inputs).
  - Prefer schema property `title` for object field labels; fall back to the JSON key when missing.
  - Allow top‑level form rendering for objects with properties, arrays with items, and primitive types (was overly strict before).
  - Multi-select UI: sensible list size and `minItems`/`maxItems` helper text when present.
  - JSON editor fallback, copy/format controls, and debounced parsing preserved.
  - apply defaults for optional fields during form init

In schemaUtils.ts
  - `generateDefaultValue` now includes optional properties that declare a `default`, ensuring fields like strings show their defaults on first render.

In jsonUtils.ts
  - Add legacy `enumNames?: string[]` and array constraints `minItems?`/`maxItems?`.
2025-12-02 19:37:28 -05:00
Michael Bolin
4b20f15c72 add suggested tests from code review 2025-12-01 10:07:10 -08:00
Michael Bolin
92e01dca08 fix: allow empty elicitation form data when all fields are optional 2025-11-19 17:38:11 -08:00
lorenzo.neumann
c0b3b952f5 Merge branch 'main' of https://github.com/modelcontextprotocol/inspector into meta_properies_in_tools 2025-11-18 15:07:12 +01:00
Konstantin Konstantinov
a5d5593c97
Merge branch 'main' into meta_properies_in_tools 2025-11-15 01:34:22 +02:00
Konstantin Konstantinov
a45247a4f2
add validations against spec, add reserved metadata key validations 2025-11-15 01:26:13 +02:00
Vincent
12332932b0 fix: Modify test mock data 2025-11-08 09:57:48 -08:00
Vincent
8ad6253cd9 fix: Extract function to utils 2025-11-04 09:31:31 -08:00
Edison-A-N
dd0985cb11 fix: enhance resolution and enum handling in anyOf schemas 2025-11-02 11:39:35 +08:00
Cliff Hall
54484e7e8e
Merge branch 'main' into fix-default-null-array-input 2025-10-30 12:04:47 -04:00
cliffhall
37acee9283 Handle nullable arrays in tool inputSchemas
* In DynamicJsonForm.tsx
  - In debouncedUpdateParent, formatJson, and validateJson,
    - when trimming the JSON value, use ?.trim() in case the field is null

* In jsonUtils.ts
  - in tryParseJson
    - use ?.trim() in case the field is null

* In schemaUtils.ts
  - in normalizeUnionType
    - add condition to handle the case where there is an anyOf with array or null as options

* In ToolsTab.tsx
  - for nullable fields, in null checkbox, in onCheckedChange handler
    - when not checked, if prop type is array, set to undefined (empty input field that can be edited rather than the word "null" as the default could provide

Fixes #846
2025-10-29 14:28:49 -04:00
yanOO1497
fb7fae1436 fix: resolve $ref references in tool input schema properties 2025-10-25 17:02:25 +08:00
olaservo
541a59f1fe Add test to explicitly reproduce issue #846 regression
This test verifies that tools with multiple nullable parameters
(default: null) work correctly. In v0.17.0, cleanParams would
incorrectly remove these null values, breaking tool execution.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 21:20:33 -07:00
olaservo
8367ef242b Merge remote-tracking branch 'upstream/main' into issue-846 2025-10-12 20:25:58 -07:00
Greg Saab
9e74437dab update specs 2025-10-08 12:34:13 -04:00
Greg Saab
3fd0feb75a if a field is nullable, provide a means of specifying null 2025-10-08 12:22:47 -04:00
olaservo
0123a71da4 Add input validation guidelines and enhance cleanParams function to preserve default values 2025-10-08 08:43:11 -07:00
olaservo
a11ee99818 Update ToolsTab tests to require JSON fields and adjust default value generation for optional types 2025-09-27 21:17:17 -07:00
olaservo
48fd23064c Remove unused function 2025-09-27 20:39:11 -07:00
olaservo
45a1bde010 Refactor ToolsTab to handle cleared fields as undefined instead of empty strings; update tests for new behavior 2025-09-27 20:21:03 -07:00
olaservo
2846e179a3 Fix formatting with Prettier
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 07:06:31 -07:00
olaservo
754c97956f Omit optional fields with empty strings from tool calls 2025-09-24 07:01:04 -07:00
Max Gerber
71583caf3b
fix: correct authState.authorizationUrl type to URL 2025-09-05 18:40:48 -07:00
Ola Hungerford
73873e2b77
Merge branch 'main' into docs/utils-jsdoc 2025-08-27 20:13:01 -07:00
Jenn Newton
a018a6d758
Apply Prettier formatting
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 15:41:33 +00:00
Jenn Newton
650f3090d2
fix: add validation for OAuth redirect URLs
- Create shared validateRedirectUrl utility to centralize URL validation
- Only allow HTTP and HTTPS protocols in OAuth authorization URLs
- Fix three vulnerable redirect paths that bypassed validation:
  - AuthDebugger.tsx: Direct window.location.href redirect
  - OAuthFlowProgress.tsx: window.open() without validation
  - OAuthFlowProgress.tsx: Direct anchor href attribute
- Add comprehensive test coverage for URL validation
- Update existing auth.ts to use shared validation utility

This prevents malicious MCP servers from injecting javascript: or data:
protocol URLs that would execute XSS attacks when users are redirected
to the authorization endpoint.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 10:08:20 -03:00
Raghunath Prabhakar
85c86de61e chore: add jsdoc comments to utils 2025-08-13 14:14:32 -05:00
Konstantin Konstantinov
929b930618
reusable generateOAuthState helper, introduce in InspectorOAuthClientProvider and in oauth-state-machine, add test 2025-08-11 21:24:26 +03:00
Mats Julius Funke
d4f8e061f3
ci: added tests for the union type 2025-08-03 10:34:40 +02:00
Mats Julius Funke
1d960a1b9a
fix: added explicit handling for float & int 2025-08-03 10:34:19 +02:00
Mats Julius Funke
5469993e27
fix: add boolean union type support for FastMCP optional booleans 2025-08-02 16:35:42 +02:00
Mats Julius Funke
f73d8e6f8a
fix: support FastMCP union types in tool parameter forms 2025-08-02 15:59:45 +02:00
Alex Andru
3a121bc8fc fix: support JSON Schema spec compliant oneOf with const for labeled enums
- Make JsonSchemaType.type optional to support const-only schemas
- Add JsonSchemaConst type for schema objects using const values
- Update oneOf/anyOf to accept both full schemas and const schemas
- Add comprehensive tests for oneOf with const and title patterns

This fixes TypeScript compilation errors when using the JSON Schema
specification's recommended approach for labeled enums:
{ "type": "string", "oneOf": [{ "const": "red", "title": "Stop" }] }

Replaces deprecated enumNames pattern with spec-compliant implementation.
2025-07-04 19:39:03 +02:00
Alex Andru
19ff315dd1 Merge branch 'origin/main' into feature/elicitations
Resolved conflicts by adopting the improved form input handling from main:
- Standardized JsonSchemaType.required to only accept string arrays (JSON Schema standard)
- Adopted enhanced object and array rendering with proper nested support
- Improved boolean field rendering (removed extra wrapper div)
- Enhanced array handling with add/remove functionality
- Simplified required field validation logic

This maintains elicitation functionality while benefiting from the form input fixes.
2025-07-03 16:14:21 +02:00
Sebastian Ko
85575de690
Merge branch 'main' into sebastian/patch-remove-addition-slash 2025-07-02 03:17:50 -04:00
Cliff Hall
6a8c831308
Merge pull request #480 from olaservo/fix-required
Fix fundamental schema issue with required fields defined incorrectly on JsonSchemaType
2025-06-27 14:09:10 -04:00
Felix Weinberger
9fcfd89326
fix: pass proxy port via URL params when non-default
- Update client to read MCP_PROXY_PORT from URL parameters
- Update server to include port in displayed URL when non-default
- Update start scripts to pass SERVER_PORT to client
- Use DEFAULT_MCP_PROXY_LISTEN_PORT constant instead of magic numbers

This fixes the issue where the client couldn't connect to the proxy
when SERVER_PORT was set to a non-default value.
2025-06-26 10:52:49 +01:00
Sebastian Ko
7dc7b09cd4
Change var to let 2025-06-25 00:45:03 -04:00
Ola Hungerford
95975c58e7
Merge branch 'main' into fix-required 2025-06-24 21:35:27 -07:00
Alex Andru
6c809bdab6 test: new elicitation format for jsonUtils 2025-06-19 17:39:24 +02:00
Alex Andru
fc939ebeb0 feat: add elicitation support to jsonUtils 2025-06-19 17:39:24 +02:00
Sebastian Ko
5710318f45
Merge branch 'main' into sebastian/patch-remove-addition-slash 2025-06-15 21:25:34 -04:00
Sebastian Ko
03e21bd3e4
Move remove slashes logic to getMCPProxyAddress 2025-06-15 21:25:01 -04:00
Jerome
760c7e4056
fix: replace require() imports with ES modules in tests
- Replaced all require() imports with proper ES module imports
- Fixed ESLint @typescript-eslint/no-require-imports errors
- Removed unused variable mockGetMCPProxyAddress
- Added proper TypeScript types instead of 'any'
- All tests passing and linting clean

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-14 18:11:16 +01:00
Jerome
62402b49c4
test: add tests for proxy auth header in config endpoint
- Added tests for App.tsx config endpoint to verify X-MCP-Proxy-Auth header usage
- Added tests for getMCPProxyAuthToken utility function
- Tests cover:
  - Sending correct proxy auth header when token is configured
  - Not sending header when token is empty
  - Using custom header names (for future flexibility)
  - Handling config endpoint errors gracefully
- All tests passing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-14 18:01:10 +01:00
Felix Weinberger
ab062d8f9d
refactor: implement split storage for inspector config
- Add is_session_item field to ConfigItem type to control storage location
- Store auth tokens in sessionStorage (cleared on browser close)
- Store other settings in localStorage (persist between sessions)
- Maintain backward compatibility with existing config structure

This provides better security for auth tokens while preserving user
preferences across browser sessions.
2025-06-11 14:42:40 +01:00