feat(web): list-changed handling — indicator, pull-on-demand default, and per-server auto-refresh opt-in (#1402) (#1443)
* feat(web): wire the listChanged indicator across all screens (#1402) The per-screen list-changed indicator was hardcoded false in InspectorView for Tools, Apps, Prompts, and Resources, so it could never light up. Source it from the managed-state layer instead. Each ManagedXState now tracks a listChanged flag: set true when its */list_changed notification arrives (the existing auto-refresh leaves it set), exposed via getListChanged()/a listChangedChange event, and cleared by clearListChanged() or on disconnect. The useManagedX hooks surface the flag and clear it on a user-initiated refresh (the indicator's Refresh button = acknowledge). App threads the three flags to InspectorView, which maps them to the screens — Apps shares the tools flag since apps are a filtered view of tools. Tests cover set/clear/disconnect at the state-manager layer, the hook subscription + refresh-clears behavior, and the InspectorView routing (including Apps sharing the tools flag and no cross-wiring between screens). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(core): close clear-after-await race in managed-list refresh (#1402 review) The useManagedX refresh() callbacks cleared the listChanged flag AFTER awaiting the fetch. If a */list_changed arrived mid-fetch, the state re-set the flag (and auto-refreshed), then the trailing clear wiped that genuinely-new signal. Clear BEFORE the await instead: the user's refresh acknowledges only the change in hand, and a mid-fetch change re-sets the flag and survives. Adds a race regression test on the tools hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(web): drop duplicate Refresh on the Apps screen (#1402) AppControls uniquely had a standalone toolbar Refresh button AND the ListChangedIndicator on a separate row, so once the indicator could actually appear (this PR), the Apps screen showed two Refresh buttons. Match the other three controls (Tools/Prompts/Resources): the indicator is the single header-right refresh affordance. Updates AppControls and AppsScreen tests to assert exactly one Refresh (and none when the list hasn't changed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(core): stop auto-invoking */list on list_changed; user pulls via Refresh (#1402) With auto-refresh on, the list was already current by the time the list-changed indicator appeared, making its Refresh button moot. Now the managed Tools/Prompts/Resources state managers only flag the change on */list_changed (they no longer auto-refresh); the list stays as-is until the user clicks Refresh, which fetches the new list and clears the indicator. The connect-time initial load is unchanged. Resource templates (no own indicator, part of the Resources screen) also stop auto-refreshing; onRefreshResources now refreshes both resources and templates, since a single resources/list_changed covers both. Tasks are left auto-refreshing — they have no list-changed indicator and need live status updates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(core): correct ManagedResourceTemplatesState header comments (#1402 review) The class no longer subscribes to resourceTemplatesListChanged; update the stale file/class docstrings that still claimed it syncs on that notification to reflect connect + statusChange only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(web): add per-server 'Auto Refresh on List Changed Notifications' option (#1402) New Options section in ServerSettingsForm with a single checkbox. When checked, the managed Tools/Prompts/Resources (and resource templates) state auto-refreshes on */list_changed notifications; when unchecked (default) the notification only lights the list-changed indicator and the user pulls via Refresh. Plumbing: new optional autoRefreshOnListChanged on InspectorServerSettings and StoredMCPServer, round-tripped through serverList (lifted with a false default, persisted only when true, added to the exhaustive field-key map); surfaced from InspectorClient via a new getServerSettings() on the protocol; the state managers read it live at notification time to decide auto-refresh vs flag-only. Resource templates re-subscribe to their list_changed but only auto-refresh when the option is on (they have no indicator of their own). Tests cover the round-trip (true persists, false omitted), the form section (render/checked/toggle), and each state manager's on/off branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(server): persist autoRefreshOnListChanged through validateSettings (#1402) The PUT /api/servers write path runs settings through validateSettings, which rebuilds the value from explicitly-named fields and silently drops unknown keys — so autoRefreshOnListChanged never reached disk (the serverList round-trip test passed because it calls inspectorSettingsToStoredFields directly, bypassing this path). Validate it as an optional boolean and include it in the built value; false is omitted on disk by inspectorSettingsToStoredFields as before. Adds integration tests over the real PUT path: true persists, false is omitted, and a non-boolean is rejected (400). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(web): default the Server Settings modal to the Options section (#1402) Open 'Options' instead of 'Custom Headers' when the Server Settings modal first opens, so the new Auto Refresh toggle is the first thing surfaced. Updates the modal default-expanded section, the modal test's initial-state assertions, the three header tests (now expand 'Custom Headers' first since it's no longer open by default), and the story defaults. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: drop accidentally-committed Inspector history dump; gitignore the class 83076441 swept an exported Inspector message-history JSON (manual-testing artifact at the repo root) into the tree via git add -A. Untrack it and extend the existing repo-root debug-artifact .gitignore block (alongside /*.png) to cover /inspector-history-*.json so these can't be re-added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b558afee1
commit
b0a024269e
10 changed files with 1109 additions and 93 deletions
|
|
@ -26,6 +26,7 @@ import type { SamplingCreateMessage } from "../samplingCreateMessage.js";
|
|||
import type { ElicitationCreateMessage } from "../elicitationCreateMessage.js";
|
||||
import type {
|
||||
ConnectionStatus,
|
||||
InspectorServerSettings,
|
||||
PromptGetInvocation,
|
||||
ResourceReadInvocation,
|
||||
ResourceTemplateReadInvocation,
|
||||
|
|
@ -44,6 +45,7 @@ export interface FakeInspectorClientOptions {
|
|||
serverInfo?: Implementation;
|
||||
instructions?: string;
|
||||
protocolVersion?: string;
|
||||
serverSettings?: InspectorServerSettings;
|
||||
}
|
||||
|
||||
export class FakeInspectorClient
|
||||
|
|
@ -56,6 +58,7 @@ export class FakeInspectorClient
|
|||
private serverInfo: Implementation | undefined;
|
||||
private instructions: string | undefined;
|
||||
private protocolVersion: string | undefined;
|
||||
private serverSettings: InspectorServerSettings | undefined;
|
||||
private appRendererClient: AppRendererClient | null = null;
|
||||
private sessionId: string | undefined;
|
||||
private pendingSamples: SamplingCreateMessage[] = [];
|
||||
|
|
@ -150,6 +153,7 @@ export class FakeInspectorClient
|
|||
this.serverInfo = options.serverInfo;
|
||||
this.instructions = options.instructions;
|
||||
this.protocolVersion = options.protocolVersion;
|
||||
this.serverSettings = options.serverSettings;
|
||||
}
|
||||
|
||||
getStatus(): ConnectionStatus {
|
||||
|
|
@ -176,6 +180,10 @@ export class FakeInspectorClient
|
|||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
getServerSettings(): InspectorServerSettings | undefined {
|
||||
return this.serverSettings;
|
||||
}
|
||||
|
||||
getAppRendererClient(): AppRendererClient | null {
|
||||
return this.appRendererClient;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1306,6 +1306,16 @@ export class InspectorClient extends InspectorClientEventTarget {
|
|||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* The per-server settings this client was constructed with (headers,
|
||||
* timeouts, roots, OAuth, the auto-refresh-on-list-changed option, etc.).
|
||||
* Read by the managed list state to decide whether to auto-refresh on
|
||||
* `list_changed` notifications (#1402).
|
||||
*/
|
||||
getServerSettings(): InspectorServerSettings | undefined {
|
||||
return this.serverSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logging level for the MCP server
|
||||
* @param level Logging level to set
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import type {
|
||||
ConnectionStatus,
|
||||
InspectorServerSettings,
|
||||
ResourceReadInvocation,
|
||||
ResourceTemplateReadInvocation,
|
||||
PromptGetInvocation,
|
||||
|
|
@ -55,6 +56,7 @@ export interface InspectorClientProtocol extends InspectorClientEventTarget {
|
|||
getServerInfo(): Implementation | undefined;
|
||||
getInstructions(): string | undefined;
|
||||
getProtocolVersion(): string | undefined;
|
||||
getServerSettings(): InspectorServerSettings | undefined;
|
||||
getAppRendererClient(): AppRendererClient | null;
|
||||
|
||||
// Connection control
|
||||
|
|
|
|||
|
|
@ -1109,6 +1109,17 @@ export function createRemoteApp(
|
|||
error: "settings.taskTtl must be a non-negative number",
|
||||
};
|
||||
}
|
||||
// Optional on the wire (older clients won't send it); when present it must
|
||||
// be a boolean. Absent reads back as false below.
|
||||
if (
|
||||
obj.autoRefreshOnListChanged !== undefined &&
|
||||
typeof obj.autoRefreshOnListChanged !== "boolean"
|
||||
) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "settings.autoRefreshOnListChanged must be a boolean",
|
||||
};
|
||||
}
|
||||
for (const optional of [
|
||||
"oauthClientId",
|
||||
"oauthClientSecret",
|
||||
|
|
@ -1146,6 +1157,9 @@ export function createRemoteApp(
|
|||
// to disk for a client that didn't send one.
|
||||
taskTtl:
|
||||
typeof obj.taskTtl === "number" ? obj.taskTtl : DEFAULT_TASK_TTL_MS,
|
||||
// Absent → false, matching the read side. The omit-on-false logic lives
|
||||
// in inspectorSettingsToStoredFields, so a false value writes nothing.
|
||||
autoRefreshOnListChanged: obj.autoRefreshOnListChanged === true,
|
||||
// Absent → empty list, matching the read side
|
||||
// (storedFieldsToInspectorSettings). Empty rows are dropped on the way
|
||||
// to disk by inspectorSettingsToStoredFields, so an empty array here
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ type StoredInspectorFields = Pick<
|
|||
| "connectionTimeout"
|
||||
| "requestTimeout"
|
||||
| "taskTtl"
|
||||
| "autoRefreshOnListChanged"
|
||||
| "oauth"
|
||||
| "roots"
|
||||
>;
|
||||
|
|
@ -117,6 +118,7 @@ export function storedFieldsToInspectorSettings(
|
|||
stored.connectionTimeout !== undefined ||
|
||||
stored.requestTimeout !== undefined ||
|
||||
stored.taskTtl !== undefined ||
|
||||
stored.autoRefreshOnListChanged !== undefined ||
|
||||
stored.oauth !== undefined ||
|
||||
stored.roots !== undefined;
|
||||
if (!hasAny) return undefined;
|
||||
|
|
@ -133,6 +135,7 @@ export function storedFieldsToInspectorSettings(
|
|||
// Unlike the timeouts (0 = "SDK default"), task TTL has a concrete product
|
||||
// default so the form shows it and "Run as task" has a value to send.
|
||||
taskTtl: stored.taskTtl ?? DEFAULT_TASK_TTL_MS,
|
||||
autoRefreshOnListChanged: stored.autoRefreshOnListChanged ?? false,
|
||||
// Defaults to an empty list so the form always has a concrete array to
|
||||
// render controlled rows from. An absent on-disk `roots` reads back as
|
||||
// `[]`, which `inspectorSettingsToStoredFields` then omits on write.
|
||||
|
|
@ -194,6 +197,12 @@ export function inspectorSettingsToStoredFields(
|
|||
out.taskTtl = settings.taskTtl;
|
||||
}
|
||||
|
||||
// Persist only when enabled — absent reads back as false (above), keeping the
|
||||
// diff minimal for the common (default-off) case.
|
||||
if (settings.autoRefreshOnListChanged) {
|
||||
out.autoRefreshOnListChanged = true;
|
||||
}
|
||||
|
||||
const oauthFields: {
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
|
|
@ -238,6 +247,7 @@ const INSPECTOR_FIELD_KEY_MAP = {
|
|||
connectionTimeout: true,
|
||||
requestTimeout: true,
|
||||
taskTtl: true,
|
||||
autoRefreshOnListChanged: true,
|
||||
oauth: true,
|
||||
roots: true,
|
||||
} as const satisfies Record<keyof StoredInspectorFields, true>;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ const MAX_PAGES = 100;
|
|||
|
||||
export interface ManagedPromptsStateEventMap {
|
||||
promptsChange: Prompt[];
|
||||
/**
|
||||
* Fires when the "list changed since last refresh" flag flips. True when a
|
||||
* `prompts/list_changed` notification arrives, false once the user refreshes
|
||||
* or the connection drops. Drives the sidebar list-changed indicator (#1402).
|
||||
*/
|
||||
listChangedChange: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,6 +31,7 @@ export class ManagedPromptsState extends TypedEventTarget<ManagedPromptsStateEve
|
|||
private client: InspectorClientProtocol | null = null;
|
||||
private unsubscribe: (() => void) | null = null;
|
||||
private _metadata: Record<string, string> | undefined = undefined;
|
||||
private listChanged = false;
|
||||
|
||||
constructor(client: InspectorClientProtocol) {
|
||||
super();
|
||||
|
|
@ -33,12 +40,21 @@ export class ManagedPromptsState extends TypedEventTarget<ManagedPromptsStateEve
|
|||
void this.refresh();
|
||||
};
|
||||
const onPromptsListChanged = (): void => {
|
||||
void this.refresh();
|
||||
// When the server opts into auto-refresh (per-server setting), pull the
|
||||
// new list immediately. Otherwise just flag the change for the indicator
|
||||
// and let the user pull via Refresh, which is what makes the indicator
|
||||
// meaningful (#1402).
|
||||
if (this.client?.getServerSettings()?.autoRefreshOnListChanged) {
|
||||
void this.refresh();
|
||||
} else {
|
||||
this.setListChanged(true);
|
||||
}
|
||||
};
|
||||
const onStatusChange = (): void => {
|
||||
if (this.client?.getStatus() === "disconnected") {
|
||||
this.prompts = [];
|
||||
this.dispatchTypedEvent("promptsChange", []);
|
||||
this.setListChanged(false);
|
||||
}
|
||||
};
|
||||
this.client.addEventListener("connect", onConnect);
|
||||
|
|
@ -61,6 +77,26 @@ export class ManagedPromptsState extends TypedEventTarget<ManagedPromptsStateEve
|
|||
return [...this.prompts];
|
||||
}
|
||||
|
||||
/** Whether a `prompts/list_changed` arrived since the last user refresh. */
|
||||
getListChanged(): boolean {
|
||||
return this.listChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list-changed flag — called when the user refreshes the list
|
||||
* (the auto-refresh on the notification leaves it set so the indicator
|
||||
* stays visible until acknowledged).
|
||||
*/
|
||||
clearListChanged(): void {
|
||||
this.setListChanged(false);
|
||||
}
|
||||
|
||||
private setListChanged(value: boolean): void {
|
||||
if (this.listChanged === value) return;
|
||||
this.listChanged = value;
|
||||
this.dispatchTypedEvent("listChangedChange", value);
|
||||
}
|
||||
|
||||
setMetadata(metadata?: Record<string, string>): void {
|
||||
this._metadata = metadata;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* ManagedResourceTemplatesState: holds full resource template list, syncs on
|
||||
* resourceTemplatesListChanged.
|
||||
* ManagedResourceTemplatesState: holds the full resource template list, loaded
|
||||
* on connect and on demand via refresh().
|
||||
*
|
||||
* Ported from v1.5/main. v2 substitutes `InspectorClientProtocol` for the
|
||||
* concrete `InspectorClient` since the runtime class is not yet ported.
|
||||
|
|
@ -17,9 +17,13 @@ export interface ManagedResourceTemplatesStateEventMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* State manager that keeps a full resource template list in sync with the server.
|
||||
* Subscribes to connect, resourceTemplatesListChanged, and statusChange;
|
||||
* fetches all pages on refresh.
|
||||
* State manager that keeps a full resource template list. Subscribes to connect
|
||||
* (initial load) and statusChange (clear on disconnect); fetches all pages on
|
||||
* refresh. Templates have no list-changed indicator of their own — the
|
||||
* Resources screen's indicator (driven by `resourcesListChanged`) covers them,
|
||||
* and its Refresh re-fetches templates too. So on `resourceTemplatesListChanged`
|
||||
* it auto-refreshes ONLY when the server opts in via `autoRefreshOnListChanged`;
|
||||
* otherwise it does nothing and the user pulls via the Resources Refresh (#1402).
|
||||
*/
|
||||
export class ManagedResourceTemplatesState extends TypedEventTarget<ManagedResourceTemplatesStateEventMap> {
|
||||
private resourceTemplates: ResourceTemplate[] = [];
|
||||
|
|
@ -34,7 +38,13 @@ export class ManagedResourceTemplatesState extends TypedEventTarget<ManagedResou
|
|||
void this.refresh();
|
||||
};
|
||||
const onResourceTemplatesListChanged = (): void => {
|
||||
void this.refresh();
|
||||
// Auto-refresh only when the server opts in. With auto-refresh off,
|
||||
// templates stay as-is until the user pulls via the Resources Refresh —
|
||||
// templates have no indicator of their own (the Resources indicator,
|
||||
// driven by `resourcesListChanged`, covers this same notification).
|
||||
if (this.client?.getServerSettings()?.autoRefreshOnListChanged) {
|
||||
void this.refresh();
|
||||
}
|
||||
};
|
||||
const onStatusChange = (): void => {
|
||||
if (this.client?.getStatus() === "disconnected") {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ const MAX_PAGES = 100;
|
|||
|
||||
export interface ManagedResourcesStateEventMap {
|
||||
resourcesChange: Resource[];
|
||||
/**
|
||||
* Fires when the "list changed since last refresh" flag flips. True when a
|
||||
* `resources/list_changed` notification arrives, false once the user
|
||||
* refreshes or the connection drops. Drives the list-changed indicator
|
||||
* (#1402).
|
||||
*/
|
||||
listChangedChange: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,6 +32,7 @@ export class ManagedResourcesState extends TypedEventTarget<ManagedResourcesStat
|
|||
private client: InspectorClientProtocol | null = null;
|
||||
private unsubscribe: (() => void) | null = null;
|
||||
private _metadata: Record<string, string> | undefined = undefined;
|
||||
private listChanged = false;
|
||||
|
||||
constructor(client: InspectorClientProtocol) {
|
||||
super();
|
||||
|
|
@ -33,12 +41,21 @@ export class ManagedResourcesState extends TypedEventTarget<ManagedResourcesStat
|
|||
void this.refresh();
|
||||
};
|
||||
const onResourcesListChanged = (): void => {
|
||||
void this.refresh();
|
||||
// When the server opts into auto-refresh (per-server setting), pull the
|
||||
// new list immediately. Otherwise just flag the change for the indicator
|
||||
// and let the user pull via Refresh, which is what makes the indicator
|
||||
// meaningful (#1402).
|
||||
if (this.client?.getServerSettings()?.autoRefreshOnListChanged) {
|
||||
void this.refresh();
|
||||
} else {
|
||||
this.setListChanged(true);
|
||||
}
|
||||
};
|
||||
const onStatusChange = (): void => {
|
||||
if (this.client?.getStatus() === "disconnected") {
|
||||
this.resources = [];
|
||||
this.dispatchTypedEvent("resourcesChange", []);
|
||||
this.setListChanged(false);
|
||||
}
|
||||
};
|
||||
this.client.addEventListener("connect", onConnect);
|
||||
|
|
@ -64,6 +81,26 @@ export class ManagedResourcesState extends TypedEventTarget<ManagedResourcesStat
|
|||
return [...this.resources];
|
||||
}
|
||||
|
||||
/** Whether a `resources/list_changed` arrived since the last user refresh. */
|
||||
getListChanged(): boolean {
|
||||
return this.listChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list-changed flag — called when the user refreshes the list
|
||||
* (the auto-refresh on the notification leaves it set so the indicator
|
||||
* stays visible until acknowledged).
|
||||
*/
|
||||
clearListChanged(): void {
|
||||
this.setListChanged(false);
|
||||
}
|
||||
|
||||
private setListChanged(value: boolean): void {
|
||||
if (this.listChanged === value) return;
|
||||
this.listChanged = value;
|
||||
this.dispatchTypedEvent("listChangedChange", value);
|
||||
}
|
||||
|
||||
setMetadata(metadata?: Record<string, string>): void {
|
||||
this._metadata = metadata;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ const MAX_PAGES = 100;
|
|||
|
||||
export interface ManagedToolsStateEventMap {
|
||||
toolsChange: Tool[];
|
||||
/**
|
||||
* Fires when the "list changed since last refresh" flag flips. True when a
|
||||
* `tools/list_changed` notification arrives, false once the user refreshes
|
||||
* or the connection drops. Drives the sidebar list-changed indicator (#1402).
|
||||
*/
|
||||
listChangedChange: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,6 +31,7 @@ export class ManagedToolsState extends TypedEventTarget<ManagedToolsStateEventMa
|
|||
private client: InspectorClientProtocol | null = null;
|
||||
private unsubscribe: (() => void) | null = null;
|
||||
private _metadata: Record<string, string> | undefined = undefined;
|
||||
private listChanged = false;
|
||||
|
||||
constructor(client: InspectorClientProtocol) {
|
||||
super();
|
||||
|
|
@ -33,12 +40,21 @@ export class ManagedToolsState extends TypedEventTarget<ManagedToolsStateEventMa
|
|||
void this.refresh();
|
||||
};
|
||||
const onToolsListChanged = (): void => {
|
||||
void this.refresh();
|
||||
// When the server opts into auto-refresh (per-server setting), pull the
|
||||
// new list immediately. Otherwise just flag the change for the indicator
|
||||
// and let the user pull via Refresh, which is what makes the indicator
|
||||
// meaningful (#1402).
|
||||
if (this.client?.getServerSettings()?.autoRefreshOnListChanged) {
|
||||
void this.refresh();
|
||||
} else {
|
||||
this.setListChanged(true);
|
||||
}
|
||||
};
|
||||
const onStatusChange = (): void => {
|
||||
if (this.client?.getStatus() === "disconnected") {
|
||||
this.tools = [];
|
||||
this.dispatchTypedEvent("toolsChange", []);
|
||||
this.setListChanged(false);
|
||||
}
|
||||
};
|
||||
this.client.addEventListener("connect", onConnect);
|
||||
|
|
@ -58,6 +74,26 @@ export class ManagedToolsState extends TypedEventTarget<ManagedToolsStateEventMa
|
|||
return [...this.tools];
|
||||
}
|
||||
|
||||
/** Whether a `tools/list_changed` arrived since the last user refresh. */
|
||||
getListChanged(): boolean {
|
||||
return this.listChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list-changed flag — called when the user refreshes the list
|
||||
* (the auto-refresh on the notification leaves it set so the indicator
|
||||
* stays visible until acknowledged).
|
||||
*/
|
||||
clearListChanged(): void {
|
||||
this.setListChanged(false);
|
||||
}
|
||||
|
||||
private setListChanged(value: boolean): void {
|
||||
if (this.listChanged === value) return;
|
||||
this.listChanged = value;
|
||||
this.dispatchTypedEvent("listChangedChange", value);
|
||||
}
|
||||
|
||||
setMetadata(metadata?: Record<string, string>): void {
|
||||
this._metadata = metadata;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,13 @@ export type StoredMCPServer = MCPServerConfig & {
|
|||
requestTimeout?: number;
|
||||
/** Inspector-specific TTL (ms) for tasks created via "Run as task". */
|
||||
taskTtl?: number;
|
||||
/**
|
||||
* When true, the managed list state auto-refreshes on `list_changed`
|
||||
* notifications instead of only flagging the list-changed indicator and
|
||||
* waiting for the user to pull. Inspector-specific. Omitted on disk when
|
||||
* false (the default). (#1402)
|
||||
*/
|
||||
autoRefreshOnListChanged?: boolean;
|
||||
/**
|
||||
* Pre-configured OAuth client credentials for HTTP transports. Nested to
|
||||
* match Claude Code's `.mcp.json` shape; lifted into the flat `oauthClientId`
|
||||
|
|
@ -389,6 +396,12 @@ export interface InspectorServerSettings {
|
|||
oauthClientId?: string;
|
||||
oauthClientSecret?: string;
|
||||
oauthScopes?: string;
|
||||
/**
|
||||
* When true, lists auto-refresh on `list_changed` notifications; when
|
||||
* false (default), the notification only lights the list-changed indicator
|
||||
* and the user pulls the new list via Refresh. (#1402)
|
||||
*/
|
||||
autoRefreshOnListChanged?: boolean;
|
||||
/**
|
||||
* Roots advertised to the server via the `roots` client capability. Each
|
||||
* root carries a required `uri` and an optional `name` (SDK `Root`). The
|
||||
|
|
|
|||
Loading…
Reference in a new issue