diff --git a/clients/web/src/App.tsx b/clients/web/src/App.tsx index 3489f6fee..8bd418e94 100644 --- a/clients/web/src/App.tsx +++ b/clients/web/src/App.tsx @@ -1133,7 +1133,7 @@ function App() { fetchRequestLogState?.destroy(); stderrLogState?.destroy(); - const { environment } = createWebEnvironment( + const { environment, logger } = createWebEnvironment( getAuthToken(), redirectUrlProvider, onBeforeOAuthRedirect, @@ -1221,6 +1221,7 @@ function App() { // reads the current log. const nextFetchLog = new FetchRequestLogState(client, { sessionStorage: sessionStorageAdapter, + logger, ...(sessionId && { sessionId }), }); fetchLogRef.current = nextFetchLog; diff --git a/clients/web/src/test/core/mcp/state/fetchRequestLogState.test.ts b/clients/web/src/test/core/mcp/state/fetchRequestLogState.test.ts index 6af743e86..591d46065 100644 --- a/clients/web/src/test/core/mcp/state/fetchRequestLogState.test.ts +++ b/clients/web/src/test/core/mcp/state/fetchRequestLogState.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; +import type pino from "pino"; import type { FetchRequestEntry } from "@inspector/core/mcp/types"; import { FetchRequestLogState } from "@inspector/core/mcp/state/fetchRequestLogState"; import { FakeInspectorClient } from "@inspector/core/mcp/__tests__/fakeInspectorClient"; @@ -121,6 +122,9 @@ describe("FetchRequestLogState", () => { }); it("ignores fetchRequestBodyUpdate for unknown ids", () => { + const logger = { debug: vi.fn() } as unknown as pino.Logger; + state.destroy(); + state = new FetchRequestLogState(client, { logger }); client.dispatchTypedEvent("fetchRequest", entry("a")); let changes = 0; state.addEventListener("fetchRequestsChange", () => changes++); @@ -129,6 +133,14 @@ describe("FetchRequestLogState", () => { responseBody: "x", }); expect(changes).toBe(0); + expect(logger.debug).toHaveBeenCalledWith( + { + id: "nonexistent", + storedFetchRequestCount: 1, + maxFetchRequests: 1000, + }, + "Dropped fetch request body update because request entry is no longer in log", + ); }); it("does NOT clear on connect or disconnect", () => { diff --git a/core/mcp/state/fetchRequestLogState.ts b/core/mcp/state/fetchRequestLogState.ts index 9761a0d83..d7c5dff23 100644 --- a/core/mcp/state/fetchRequestLogState.ts +++ b/core/mcp/state/fetchRequestLogState.ts @@ -14,6 +14,7 @@ */ import type { InspectorClientProtocol } from "../inspectorClientProtocol.js"; +import type pino from "pino"; import type { FetchRequestEntry } from "../types.js"; import type { InspectorClientStorage, @@ -24,6 +25,7 @@ import { TypedEventTarget, type TypedEventGeneric, } from "../typedEventTarget.js"; +import { silentLogger } from "../../logging/logger.js"; export interface FetchRequestLogStateEventMap { fetchRequest: FetchRequestEntry; @@ -44,6 +46,8 @@ export interface FetchRequestLogStateOptions { sessionStorage?: InspectorClientStorage; /** Session ID for load/save; required for sessionStorage to have effect. */ sessionId?: string; + /** Logger used for diagnostic events that do not affect UI state. */ + logger?: pino.Logger; } export class FetchRequestLogState extends TypedEventTarget { @@ -51,6 +55,7 @@ export class FetchRequestLogState extends TypedEventTarget void) | null = null; private readonly maxFetchRequests: number; + private readonly logger: pino.Logger; constructor( client: InspectorClientProtocol, @@ -58,6 +63,7 @@ export class FetchRequestLogState extends TypedEventTarget { const { id, responseBody } = event.detail; const idx = this.fetchRequests.findIndex((e) => e.id === id); - if (idx === -1) return; + if (idx === -1) { + this.logger.debug( + { + id, + storedFetchRequestCount: this.fetchRequests.length, + maxFetchRequests: this.maxFetchRequests, + }, + "Dropped fetch request body update because request entry is no longer in log", + ); + return; + } this.fetchRequests[idx] = { ...this.fetchRequests[idx]!, responseBody, @@ -192,9 +208,7 @@ export class FetchRequestLogState extends TypedEventTarget 0 - ? merged.slice(-this.maxFetchRequests) - : merged; + this.maxFetchRequests > 0 ? merged.slice(-this.maxFetchRequests) : merged; this.dispatchTypedEvent("fetchRequestsChange", this.getFetchRequests()); }