fix(tui): refresh MCP status for active location (#36882)
This commit is contained in:
@@ -841,7 +841,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||||||
// Authenticating an MCP integration reconnects its server, which emits mcp.status.changed,
|
// Authenticating an MCP integration reconnects its server, which emits mcp.status.changed,
|
||||||
// so the mcp list refreshes here rather than off integration.updated.
|
// so the mcp list refreshes here rather than off integration.updated.
|
||||||
case "mcp.status.changed":
|
case "mcp.status.changed":
|
||||||
if (bootstrapping) break
|
|
||||||
void result.location.mcp.server.refresh(event.location)
|
void result.location.mcp.server.refresh(event.location)
|
||||||
break
|
break
|
||||||
case "mcp.resources.changed":
|
case "mcp.resources.changed":
|
||||||
@@ -1044,7 +1043,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||||||
return store.location[locationKey(location ?? defaultLocation())]?.mcp?.server
|
return store.location[locationKey(location ?? defaultLocation())]?.mcp?.server
|
||||||
},
|
},
|
||||||
async refresh(ref?: LocationRef) {
|
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)
|
const key = locationKey(result.location)
|
||||||
setStore("location", key, {
|
setStore("location", key, {
|
||||||
...store.location[key],
|
...store.location[key],
|
||||||
@@ -1057,7 +1056,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||||||
return store.location[locationKey(location ?? defaultLocation())]?.mcp?.resource
|
return store.location[locationKey(location ?? defaultLocation())]?.mcp?.resource
|
||||||
},
|
},
|
||||||
async refresh(ref?: LocationRef) {
|
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)
|
const key = locationKey(result.location)
|
||||||
setStore("location", key, {
|
setStore("location", key, {
|
||||||
...store.location[key],
|
...store.location[key],
|
||||||
|
|||||||
@@ -42,6 +42,90 @@ function durable(sessionID: string, seq = 0, version = 1) {
|
|||||||
return { aggregateID: sessionID, seq, version }
|
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(() => (
|
||||||
|
<TestTuiContexts>
|
||||||
|
<ClientProvider api={createApi(calls.fetch)}>
|
||||||
|
<ProjectProvider>
|
||||||
|
<DataProvider>
|
||||||
|
<box />
|
||||||
|
</DataProvider>
|
||||||
|
</ProjectProvider>
|
||||||
|
</ClientProvider>
|
||||||
|
</TestTuiContexts>
|
||||||
|
))
|
||||||
|
|
||||||
|
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<Response>((resolve) => {
|
||||||
|
resolveModels = resolve
|
||||||
|
})
|
||||||
|
return undefined
|
||||||
|
}, events)
|
||||||
|
let data!: ReturnType<typeof useData>
|
||||||
|
|
||||||
|
function Probe() {
|
||||||
|
data = useData()
|
||||||
|
return <box />
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = await testRender(() => (
|
||||||
|
<TestTuiContexts>
|
||||||
|
<ClientProvider api={createApi(calls.fetch)}>
|
||||||
|
<ProjectProvider>
|
||||||
|
<DataProvider>
|
||||||
|
<Probe />
|
||||||
|
</DataProvider>
|
||||||
|
</ProjectProvider>
|
||||||
|
</ClientProvider>
|
||||||
|
</TestTuiContexts>
|
||||||
|
))
|
||||||
|
|
||||||
|
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 () => {
|
test("refreshes resources into reactive getters", async () => {
|
||||||
const events = createEventStream()
|
const events = createEventStream()
|
||||||
const location = {
|
const location = {
|
||||||
|
|||||||
Reference in New Issue
Block a user