refactor(tui): simplify MCP catalog refresh

This commit is contained in:
Aiden Cline 2026-07-07 14:24:23 -05:00
commit 4d3ff36869
2 changed files with 10 additions and 56 deletions

View file

@ -115,8 +115,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
let connectionGeneration = 0 let connectionGeneration = 0
let statusChanges: Set<string> | undefined let statusChanges: Set<string> | undefined
let bootstrapping: Promise<void> | undefined let bootstrapping: Promise<void> | undefined
const pendingMcpResourceRefresh = new Map<string, LocationRef>()
const mcpResourceRefreshes = new Map<string, Promise<void>>()
function setSessionStatus(sessionID: string, status: DataSessionStatus) { function setSessionStatus(sessionID: string, status: DataSessionStatus) {
statusChanges?.add(sessionID) statusChanges?.add(sessionID)
@ -767,15 +765,10 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
if (bootstrapping) break if (bootstrapping) break
void result.location.mcp.refresh(event.location) void result.location.mcp.refresh(event.location)
break break
case "mcp.resources.changed": { case "mcp.resources.changed":
const location = event.location ?? defaultLocation() if (bootstrapping) break
if (bootstrapping || mcpResourceRefreshes.has(locationKey(location))) { void result.location.mcp.resource.refresh(event.location)
pendingMcpResourceRefresh.set(locationKey(location), location)
break
}
void result.location.mcp.resource.refresh(location)
break break
}
} }
} }
@ -965,26 +958,12 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
catalog(location?: LocationRef) { catalog(location?: LocationRef) {
return store.location[locationKey(location ?? defaultLocation())]?.mcpResource return store.location[locationKey(location ?? defaultLocation())]?.mcpResource
}, },
refresh(ref?: LocationRef) { async refresh(ref?: LocationRef) {
const location = ref ?? defaultLocation() const result = await sdk.api["server.mcp"].catalog({
const key = locationKey(location) location: locationQuery(ref ?? defaultLocation()),
const active = mcpResourceRefreshes.get(key) })
if (active) return active const key = locationKey(result.location)
const refresh = sdk.api["server.mcp"] setStore("location", key, { ...store.location[key], mcpResource: mutable(result.data) })
.catalog({ location: locationQuery(location) })
.then((result) => {
const key = locationKey(result.location)
setStore("location", key, { ...store.location[key], mcpResource: mutable(result.data) })
})
.finally(() => {
mcpResourceRefreshes.delete(key)
const pending = pendingMcpResourceRefresh.get(key)
if (!pending || bootstrapping) return
pendingMcpResourceRefresh.delete(key)
void result.location.mcp.resource.refresh(pending)
})
mcpResourceRefreshes.set(key, refresh)
return refresh
}, },
}, },
}, },
@ -1069,11 +1048,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
}) })
.finally(() => { .finally(() => {
bootstrapping = undefined bootstrapping = undefined
for (const [key, location] of pendingMcpResourceRefresh) {
if (mcpResourceRefreshes.has(key)) continue
pendingMcpResourceRefresh.delete(key)
void result.location.mcp.resource.refresh(location)
}
}) })
return bootstrapping return bootstrapping
} }

View file

@ -112,19 +112,12 @@ test("refreshes MCP resource catalogs after change events", async () => {
const events = createEventStream() const events = createEventStream()
let resources = [{ server: "docs", name: "Readme", uri: "docs://readme" }] let resources = [{ server: "docs", name: "Readme", uri: "docs://readme" }]
let requests = 0 let requests = 0
let release: (() => void) | undefined
const calls = createFetch((url) => { const calls = createFetch((url) => {
if (url.pathname !== "/api/mcp/resource") return if (url.pathname !== "/api/mcp/resource") return
requests++ requests++
const data = { resources, templates: [] }
if (requests === 3)
return new Promise<Response>((resolve) => {
release = () =>
resolve(json({ location: { directory, project: { id: "proj_test", directory } }, data }))
})
return json({ return json({
location: { directory, project: { id: "proj_test", directory } }, location: { directory, project: { id: "proj_test", directory } },
data, data: { resources, templates: [] },
}) })
}, events) }, events)
let data!: ReturnType<typeof useData> let data!: ReturnType<typeof useData>
@ -158,19 +151,6 @@ test("refreshes MCP resource catalogs after change events", async () => {
data: { server: "docs" }, data: { server: "docs" },
}) })
await wait(() => requests === 2 && data.location.mcp.resource.catalog()?.resources[0]?.name === "Guide") await wait(() => requests === 2 && data.location.mcp.resource.catalog()?.resources[0]?.name === "Guide")
const refresh = data.location.mcp.resource.refresh()
await wait(() => requests === 3)
resources = [{ server: "docs", name: "Reference", uri: "docs://reference" }]
emitEvent(events, {
id: "evt_mcp_resources_during_refresh",
created: 2,
type: "mcp.resources.changed",
data: { server: "docs" },
})
release?.()
await refresh
await wait(() => requests === 4 && data.location.mcp.resource.catalog()?.resources[0]?.name === "Reference")
} finally { } finally {
app.renderer.destroy() app.renderer.destroy()
} }