From a6b5cf94b0aba9e75b5c90630ff86f673bd3bff8 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:49:56 -0500 Subject: [PATCH] fix(tui): refresh MCP status for active location (#36882) --- packages/tui/src/context/data.tsx | 5 +- packages/tui/test/cli/tui/data.test.tsx | 84 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/packages/tui/src/context/data.tsx b/packages/tui/src/context/data.tsx index 114da13a1f..0399a56aa5 100644 --- a/packages/tui/src/context/data.tsx +++ b/packages/tui/src/context/data.tsx @@ -841,7 +841,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ // Authenticating an MCP integration reconnects its server, which emits mcp.status.changed, // so the mcp list refreshes here rather than off integration.updated. case "mcp.status.changed": - if (bootstrapping) break void result.location.mcp.server.refresh(event.location) break case "mcp.resources.changed": @@ -1044,7 +1043,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ return store.location[locationKey(location ?? defaultLocation())]?.mcp?.server }, async refresh(ref?: LocationRef) { - const result = await client.api.mcp.list({ location: locationQuery(ref) }) + const result = await client.api.mcp.list({ location: locationQuery(ref ?? defaultLocation()) }) const key = locationKey(result.location) setStore("location", key, { ...store.location[key], @@ -1057,7 +1056,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ return store.location[locationKey(location ?? defaultLocation())]?.mcp?.resource }, async refresh(ref?: LocationRef) { - const result = await client.api.mcp.resource.catalog({ location: locationQuery(ref) }) + const result = await client.api.mcp.resource.catalog({ location: locationQuery(ref ?? defaultLocation()) }) const key = locationKey(result.location) setStore("location", key, { ...store.location[key], diff --git a/packages/tui/test/cli/tui/data.test.tsx b/packages/tui/test/cli/tui/data.test.tsx index 2671ba1e22..7b2f4d54b3 100644 --- a/packages/tui/test/cli/tui/data.test.tsx +++ b/packages/tui/test/cli/tui/data.test.tsx @@ -42,6 +42,90 @@ function durable(sessionID: string, seq = 0, version = 1) { return { aggregateID: sessionID, seq, version } } +test("bootstraps MCP data for the TUI location", async () => { + const events = createEventStream() + const requests: URL[] = [] + const calls = createFetch((url) => { + if (url.pathname === "/api/mcp" || url.pathname === "/api/mcp/resource") requests.push(url) + return undefined + }, events) + + const app = await testRender(() => ( + + + + + + + + + + )) + + try { + await wait(() => requests.length === 2) + expect(requests.map((url) => url.searchParams.get("location[directory]"))).toEqual([ + process.cwd(), + process.cwd(), + ]) + } finally { + app.renderer.destroy() + } +}) + +test("refreshes MCP status when a connection settles during bootstrap", async () => { + const events = createEventStream() + let mcpRequests = 0 + let resolveModels!: (response: Response) => void + const calls = createFetch((url) => { + if (url.pathname === "/api/mcp") { + mcpRequests++ + return json({ + location: { directory, project: { id: "proj_test", directory } }, + data: [{ name: "context7", status: { status: mcpRequests === 1 ? "pending" : "connected" } }], + }) + } + if (url.pathname === "/api/model") + return new Promise((resolve) => { + resolveModels = resolve + }) + return undefined + }, events) + let data!: ReturnType + + function Probe() { + data = useData() + return + } + + const app = await testRender(() => ( + + + + + + + + + + )) + + try { + await wait(() => data.location.mcp.server.list()?.[0]?.status.status === "pending") + emitEvent(events, { + id: "evt_mcp_connected", + created: 1, + type: "mcp.status.changed", + data: { server: "context7" }, + }) + await wait(() => data.location.mcp.server.list()?.[0]?.status.status === "connected") + expect(mcpRequests).toBe(2) + resolveModels(json({ location: { directory, project: { id: "proj_test", directory } }, data: [] })) + } finally { + app.renderer.destroy() + } +}) + test("refreshes resources into reactive getters", async () => { const events = createEventStream() const location = {