refactor(core): clarify MCP config reload
This commit is contained in:
parent
6a756f8721
commit
473d16ac12
2 changed files with 75 additions and 67 deletions
|
|
@ -150,55 +150,54 @@ function resourceServer(
|
|||
)
|
||||
}
|
||||
|
||||
function resourceMcpLayer(
|
||||
server: string | typeof ConfigMCP.Server.Type,
|
||||
onFormCreated?: (form: Form.Info) => Effect.Effect<void>,
|
||||
options?: MCP.Options,
|
||||
overrides?: {
|
||||
entries?: Config.Interface["entries"]
|
||||
subscribe?: EventV2.Interface["subscribe"]
|
||||
},
|
||||
) {
|
||||
function resourceMcpLayer(input: {
|
||||
server: string | typeof ConfigMCP.Server.Type
|
||||
onFormCreated?: (form: Form.Info) => Effect.Effect<void>
|
||||
options?: MCP.Options
|
||||
entries?: Config.Interface["entries"]
|
||||
subscribe?: EventV2.Interface["subscribe"]
|
||||
}) {
|
||||
const directory = AbsolutePath.make(import.meta.dir)
|
||||
const unusedIntegration = () => Effect.die("unused integration service")
|
||||
return MCP.layer(options).pipe(
|
||||
const entries =
|
||||
input.entries ??
|
||||
(() =>
|
||||
Effect.succeed([
|
||||
new Config.Document({
|
||||
type: "document",
|
||||
info: new Config.Info({
|
||||
mcp: new ConfigMCP.Info({
|
||||
servers: {
|
||||
resources:
|
||||
typeof input.server === "string"
|
||||
? new ConfigMCP.Remote({ type: "remote", url: input.server, oauth: false })
|
||||
: input.server,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]))
|
||||
return MCP.layer(input.options).pipe(
|
||||
Layer.provideMerge(Form.layer),
|
||||
Layer.provide(
|
||||
Layer.mergeAll(
|
||||
Layer.succeed(
|
||||
Config.Service,
|
||||
Config.Service.of({
|
||||
entries:
|
||||
overrides?.entries ??
|
||||
(() =>
|
||||
Effect.succeed([
|
||||
new Config.Document({
|
||||
type: "document",
|
||||
info: new Config.Info({
|
||||
mcp: new ConfigMCP.Info({
|
||||
servers: {
|
||||
resources:
|
||||
typeof server === "string"
|
||||
? new ConfigMCP.Remote({ type: "remote", url: server, oauth: false })
|
||||
: server,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
])),
|
||||
}),
|
||||
Config.Service.of({ entries }),
|
||||
),
|
||||
Layer.succeed(Location.Service, Location.Service.of(location({ directory }))),
|
||||
Layer.mock(EventV2.Service, {
|
||||
subscribe: overrides?.subscribe ?? (() => Stream.never),
|
||||
subscribe: input.subscribe ?? (() => Stream.never),
|
||||
publish: (definition, data) => {
|
||||
const event = {
|
||||
id: EventV2.ID.create(),
|
||||
type: definition.type,
|
||||
data,
|
||||
} as EventV2.Payload<typeof definition>
|
||||
if (event.type !== Form.Event.Created.type || !onFormCreated) return Effect.succeed(event)
|
||||
return onFormCreated(Schema.decodeUnknownSync(Form.Event.Created.data)(data).form).pipe(Effect.as(event))
|
||||
if (event.type !== Form.Event.Created.type || !input.onFormCreated) return Effect.succeed(event)
|
||||
return input
|
||||
.onFormCreated(Schema.decodeUnknownSync(Form.Event.Created.data)(data).form)
|
||||
.pipe(Effect.as(event))
|
||||
},
|
||||
}),
|
||||
Layer.mock(Integration.Service, {
|
||||
|
|
@ -575,7 +574,7 @@ test("accepts empty MCP elicitations without creating forms", async () => {
|
|||
const result = yield* service.callTool({ server: "resources", name: "empty-elicitation" })
|
||||
expect(yield* forms.list()).toEqual([])
|
||||
return result
|
||||
}).pipe(Effect.provide(resourceMcpLayer(server.url)))
|
||||
}).pipe(Effect.provide(resourceMcpLayer({ server: server.url })))
|
||||
|
||||
expect(result.structured).toEqual({ action: "accept", content: {} })
|
||||
}),
|
||||
|
|
@ -602,7 +601,12 @@ test("acknowledges completed MCP URL elicitations without returning internal con
|
|||
expect(yield* forms.state(form.id)).toEqual({ status: "answered", answer: { elicitation: true } })
|
||||
return result
|
||||
}).pipe(
|
||||
Effect.provide(resourceMcpLayer(server.url, (form) => Deferred.succeed(created, form).pipe(Effect.asVoid))),
|
||||
Effect.provide(
|
||||
resourceMcpLayer({
|
||||
server: server.url,
|
||||
onFormCreated: (form) => Deferred.succeed(created, form).pipe(Effect.asVoid),
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
expect(result.structured).toEqual({ action: "accept" })
|
||||
|
|
@ -655,7 +659,10 @@ test("loads and reads MCP resources", async () => {
|
|||
expect(server.clientVersion()).toMatchObject({ name: "sdk", version: "1.2.3" })
|
||||
}).pipe(
|
||||
Effect.provide(
|
||||
resourceMcpLayer(server.url, undefined, { clientInfo: { name: "sdk", version: "1.2.3" } }),
|
||||
resourceMcpLayer({
|
||||
server: server.url,
|
||||
options: { clientInfo: { name: "sdk", version: "1.2.3" } },
|
||||
}),
|
||||
),
|
||||
)
|
||||
}),
|
||||
|
|
@ -718,13 +725,13 @@ test("adds, disconnects, and reconnects MCP servers at runtime", async () => {
|
|||
expect(yield* service.remove("dynamic").pipe(Effect.flip)).toBeInstanceOf(MCP.NotFoundError)
|
||||
}).pipe(
|
||||
Effect.provide(
|
||||
resourceMcpLayer(
|
||||
new ConfigMCP.Local({
|
||||
resourceMcpLayer({
|
||||
server: new ConfigMCP.Local({
|
||||
type: "local",
|
||||
command: [process.execPath, path.join(import.meta.dir, "fixture/mcp-output-schema.ts")],
|
||||
disabled: true,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
)
|
||||
}),
|
||||
|
|
@ -789,12 +796,11 @@ test("reconciles MCP servers when config updates", async () => {
|
|||
expect(replaced[0]?.status).toEqual({ status: "disabled" })
|
||||
}).pipe(
|
||||
Effect.provide(
|
||||
resourceMcpLayer(
|
||||
new ConfigMCP.Local({ type: "local", command, disabled: true }),
|
||||
undefined,
|
||||
undefined,
|
||||
{ entries, subscribe },
|
||||
),
|
||||
resourceMcpLayer({
|
||||
server: new ConfigMCP.Local({ type: "local", command, disabled: true }),
|
||||
entries,
|
||||
subscribe,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}),
|
||||
|
|
@ -833,13 +839,13 @@ test("serializes concurrent MCP lifecycle operations", async () => {
|
|||
expect((yield* service.tools()).length).toBeGreaterThan(0)
|
||||
}).pipe(
|
||||
Effect.provide(
|
||||
resourceMcpLayer(
|
||||
new ConfigMCP.Local({
|
||||
resourceMcpLayer({
|
||||
server: new ConfigMCP.Local({
|
||||
type: "local",
|
||||
command: [process.execPath, path.join(import.meta.dir, "fixture/mcp-output-schema.ts")],
|
||||
disabled: true,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
)
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue