mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-15 20:52:01 +03:00
fix(core): keep MCP quiet while a location shuts down (#60668)
This commit is contained in:
committed by
GitHub
Unverified
parent
bf5d12c588
commit
2e29481d5c
@@ -184,6 +184,11 @@ export const layer = (options?: Options) =>
|
||||
// Server scopes close in parallel on shutdown, so one slow server's stop grace does not delay
|
||||
// the rest past a service restart's kill deadline and leave them orphaned.
|
||||
const servers = yield* Scope.fork(root, "parallel")
|
||||
// Connection callbacks run here. Created after `servers`, so the layer scope closes this set first
|
||||
// (finalizers run in reverse): in-flight callbacks are interrupted and later ones never start, so
|
||||
// connections closing with the Location publish nothing. Clients re-fetch on MCP events, which
|
||||
// would boot the Location straight back up.
|
||||
const callbacks = yield* FiberSet.makeRuntime<never, void, never>()
|
||||
|
||||
const entries = new Map<ServerName, ServerEntry>()
|
||||
const attached = new Map<ServerName, AttachedEntry[]>()
|
||||
@@ -352,7 +357,7 @@ export const layer = (options?: Options) =>
|
||||
const whenLive =
|
||||
(name: ServerName, entry: ServerEntry, connection: McpClient.Connection) =>
|
||||
<E>(effect: Effect.Effect<void, E>) =>
|
||||
fork(
|
||||
callbacks(
|
||||
Effect.suspend(() => (entry.client === connection ? effect : Effect.void)).pipe(
|
||||
locks.withLock(name),
|
||||
Effect.ignore,
|
||||
|
||||
@@ -1883,6 +1883,39 @@ test("isolates nested configured MCP mutations and reconciles them", async () =>
|
||||
)
|
||||
})
|
||||
|
||||
test("publishes MCP changes while live but not while the location shuts down", async () => {
|
||||
const published: string[] = []
|
||||
await Effect.runPromise(
|
||||
Effect.scoped(
|
||||
Effect.gen(function* () {
|
||||
const server = yield* resourceServer({ listChanged: true })
|
||||
const scope = yield* Scope.make()
|
||||
const context = yield* Layer.buildWithScope(
|
||||
resourceMcpLayer(server.url, undefined, undefined, { published }),
|
||||
scope,
|
||||
)
|
||||
yield* Effect.gen(function* () {
|
||||
const service = yield* Mcp.Service
|
||||
expect(yield* settled(service)).toEqual({ status: "connected" })
|
||||
published.length = 0
|
||||
yield* Effect.promise(server.sendResourceListChanged)
|
||||
yield* Effect.sync(() => published).pipe(
|
||||
Effect.filterOrFail(
|
||||
(types) => types.includes(McpEvent.ResourcesChanged.type),
|
||||
() => new Error("resource change was not published"),
|
||||
),
|
||||
Effect.retry({ times: 200, schedule: Schedule.spaced("10 millis") }),
|
||||
)
|
||||
}).pipe(Effect.provide(context))
|
||||
published.length = 0
|
||||
// Location shutdown closes plugin registrations under State.shutdown, so config does not reconcile.
|
||||
yield* State.shutdown(Scope.close(scope, Exit.void))
|
||||
expect(published.filter((type) => type.startsWith("mcp."))).toEqual([])
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
testEffect(Layer.empty).live("batches MCP transforms without connecting intermediate configurations", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* resourceServer()
|
||||
|
||||
Reference in New Issue
Block a user