@effect/ai 0.19.2 → 0.19.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/McpSchema.js +184 -34
- package/dist/cjs/McpSchema.js.map +1 -1
- package/dist/cjs/McpServer.js +133 -42
- package/dist/cjs/McpServer.js.map +1 -1
- package/dist/dts/McpSchema.d.ts +742 -438
- package/dist/dts/McpSchema.d.ts.map +1 -1
- package/dist/dts/McpServer.d.ts +52 -42
- package/dist/dts/McpServer.d.ts.map +1 -1
- package/dist/esm/McpSchema.js +173 -31
- package/dist/esm/McpSchema.js.map +1 -1
- package/dist/esm/McpServer.js +132 -41
- package/dist/esm/McpServer.js.map +1 -1
- package/package.json +4 -4
- package/src/McpSchema.ts +214 -92
- package/src/McpServer.ts +276 -132
package/src/McpServer.ts
CHANGED
|
@@ -19,6 +19,7 @@ import * as Layer from "effect/Layer"
|
|
|
19
19
|
import * as Logger from "effect/Logger"
|
|
20
20
|
import * as Mailbox from "effect/Mailbox"
|
|
21
21
|
import * as Option from "effect/Option"
|
|
22
|
+
import * as RcMap from "effect/RcMap"
|
|
22
23
|
import * as Schema from "effect/Schema"
|
|
23
24
|
import * as AST from "effect/SchemaAST"
|
|
24
25
|
import type { Sink } from "effect/Sink"
|
|
@@ -27,13 +28,24 @@ import type * as Types from "effect/Types"
|
|
|
27
28
|
import * as FindMyWay from "find-my-way-ts"
|
|
28
29
|
import * as AiTool from "./AiTool.js"
|
|
29
30
|
import type * as AiToolkit from "./AiToolkit.js"
|
|
30
|
-
import type {
|
|
31
|
-
import {
|
|
31
|
+
import type {
|
|
32
32
|
Annotations,
|
|
33
|
-
|
|
33
|
+
CallTool,
|
|
34
|
+
Complete,
|
|
35
|
+
GetPrompt,
|
|
36
|
+
Param,
|
|
37
|
+
PromptArgument,
|
|
38
|
+
PromptMessage,
|
|
39
|
+
ReadResourceResult,
|
|
40
|
+
ServerCapabilities
|
|
41
|
+
} from "./McpSchema.js"
|
|
42
|
+
import {
|
|
34
43
|
CallToolResult,
|
|
44
|
+
ClientNotificationRpcs,
|
|
35
45
|
ClientRpcs,
|
|
36
46
|
CompleteResult,
|
|
47
|
+
Elicit,
|
|
48
|
+
ElicitationDeclined,
|
|
37
49
|
GetPromptResult,
|
|
38
50
|
InternalError,
|
|
39
51
|
InvalidParams,
|
|
@@ -41,16 +53,15 @@ import {
|
|
|
41
53
|
ListResourcesResult,
|
|
42
54
|
ListResourceTemplatesResult,
|
|
43
55
|
ListToolsResult,
|
|
56
|
+
McpServerClient,
|
|
57
|
+
McpServerClientMiddleware,
|
|
44
58
|
ParamAnnotation,
|
|
45
59
|
Prompt,
|
|
46
|
-
PromptArgument,
|
|
47
|
-
PromptMessage,
|
|
48
|
-
ReadResourceResult,
|
|
49
60
|
Resource,
|
|
50
61
|
ResourceTemplate,
|
|
51
62
|
ServerNotificationRpcs,
|
|
63
|
+
ServerRequestRpcs,
|
|
52
64
|
TextContent,
|
|
53
|
-
TextResourceContents,
|
|
54
65
|
Tool,
|
|
55
66
|
ToolAnnotations
|
|
56
67
|
} from "./McpSchema.js"
|
|
@@ -64,19 +75,22 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
64
75
|
{
|
|
65
76
|
readonly notifications: RpcClient.RpcClient<RpcGroup.Rpcs<typeof ServerNotificationRpcs>>
|
|
66
77
|
readonly notificationsMailbox: Mailbox.ReadonlyMailbox<RpcMessage.Request<any>>
|
|
78
|
+
|
|
67
79
|
readonly tools: ReadonlyArray<Tool>
|
|
68
80
|
readonly addTool: (options: {
|
|
69
81
|
readonly tool: Tool
|
|
70
|
-
readonly handle: (payload: any) => Effect.Effect<CallToolResult>
|
|
82
|
+
readonly handle: (payload: any) => Effect.Effect<CallToolResult, never, McpServerClient>
|
|
71
83
|
}) => Effect.Effect<void>
|
|
72
84
|
readonly callTool: (
|
|
73
85
|
requests: typeof CallTool.payloadSchema.Type
|
|
74
|
-
) => Effect.Effect<CallToolResult, InternalError | InvalidParams>
|
|
86
|
+
) => Effect.Effect<CallToolResult, InternalError | InvalidParams, McpServerClient>
|
|
87
|
+
|
|
75
88
|
readonly resources: ReadonlyArray<Resource>
|
|
76
89
|
readonly addResource: (
|
|
77
90
|
resource: Resource,
|
|
78
|
-
handle: Effect.Effect<ReadResourceResult, InternalError>
|
|
91
|
+
handle: Effect.Effect<typeof ReadResourceResult.Type, InternalError, McpServerClient>
|
|
79
92
|
) => Effect.Effect<void>
|
|
93
|
+
|
|
80
94
|
readonly resourceTemplates: ReadonlyArray<ResourceTemplate>
|
|
81
95
|
readonly addResourceTemplate: (
|
|
82
96
|
options: {
|
|
@@ -86,20 +100,32 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
86
100
|
readonly handle: (
|
|
87
101
|
uri: string,
|
|
88
102
|
params: Array<string>
|
|
89
|
-
) => Effect.Effect<ReadResourceResult, InvalidParams | InternalError>
|
|
103
|
+
) => Effect.Effect<typeof ReadResourceResult.Type, InvalidParams | InternalError, McpServerClient>
|
|
90
104
|
}
|
|
91
105
|
) => Effect.Effect<void>
|
|
92
|
-
|
|
106
|
+
|
|
107
|
+
readonly findResource: (
|
|
108
|
+
uri: string
|
|
109
|
+
) => Effect.Effect<typeof ReadResourceResult.Type, InvalidParams | InternalError, McpServerClient>
|
|
110
|
+
|
|
111
|
+
readonly prompts: ReadonlyArray<Prompt>
|
|
93
112
|
readonly addPrompt: (options: {
|
|
94
113
|
readonly prompt: Prompt
|
|
95
|
-
readonly completions: Record<
|
|
96
|
-
|
|
114
|
+
readonly completions: Record<
|
|
115
|
+
string,
|
|
116
|
+
(input: string) => Effect.Effect<CompleteResult, InternalError, McpServerClient>
|
|
117
|
+
>
|
|
118
|
+
readonly handle: (
|
|
119
|
+
params: Record<string, string>
|
|
120
|
+
) => Effect.Effect<GetPromptResult, InternalError | InvalidParams, McpServerClient>
|
|
97
121
|
}) => Effect.Effect<void>
|
|
98
|
-
readonly prompts: ReadonlyArray<Prompt>
|
|
99
122
|
readonly getPromptResult: (
|
|
100
123
|
request: typeof GetPrompt.payloadSchema.Type
|
|
101
|
-
) => Effect.Effect<GetPromptResult, InternalError | InvalidParams>
|
|
102
|
-
|
|
124
|
+
) => Effect.Effect<GetPromptResult, InternalError | InvalidParams, McpServerClient>
|
|
125
|
+
|
|
126
|
+
readonly completion: (
|
|
127
|
+
complete: typeof Complete.payloadSchema.Type
|
|
128
|
+
) => Effect.Effect<CompleteResult, InternalError, McpServerClient>
|
|
103
129
|
}
|
|
104
130
|
>() {
|
|
105
131
|
/**
|
|
@@ -112,25 +138,29 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
112
138
|
readonly handle: (
|
|
113
139
|
uri: string,
|
|
114
140
|
params: Array<string>
|
|
115
|
-
) => Effect.Effect<ReadResourceResult, InternalError | InvalidParams>
|
|
141
|
+
) => Effect.Effect<typeof ReadResourceResult.Type, InternalError | InvalidParams, McpServerClient>
|
|
116
142
|
} | {
|
|
117
143
|
readonly _tag: "Resource"
|
|
118
|
-
readonly effect: Effect.Effect<ReadResourceResult, InternalError>
|
|
144
|
+
readonly effect: Effect.Effect<typeof ReadResourceResult.Type, InternalError, McpServerClient>
|
|
119
145
|
}
|
|
120
146
|
>()
|
|
121
147
|
const tools = Arr.empty<Tool>()
|
|
122
|
-
const toolMap = new Map<string, (payload: any) => Effect.Effect<CallToolResult, InternalError>>()
|
|
148
|
+
const toolMap = new Map<string, (payload: any) => Effect.Effect<CallToolResult, InternalError, McpServerClient>>()
|
|
123
149
|
const resources: Array<Resource> = []
|
|
124
150
|
const resourceTemplates: Array<ResourceTemplate> = []
|
|
125
151
|
const prompts: Array<Prompt> = []
|
|
126
152
|
const promptMap = new Map<
|
|
127
153
|
string,
|
|
128
|
-
(params: Record<string, string>) => Effect.Effect<GetPromptResult, InternalError | InvalidParams>
|
|
154
|
+
(params: Record<string, string>) => Effect.Effect<GetPromptResult, InternalError | InvalidParams, McpServerClient>
|
|
155
|
+
>()
|
|
156
|
+
const completionsMap = new Map<
|
|
157
|
+
string,
|
|
158
|
+
(input: string) => Effect.Effect<CompleteResult, InternalError, McpServerClient>
|
|
129
159
|
>()
|
|
130
|
-
const completionsMap = new Map<string, (input: string) => Effect.Effect<CompleteResult, InternalError>>()
|
|
131
160
|
const notificationsMailbox = yield* Mailbox.make<RpcMessage.Request<any>>()
|
|
132
161
|
const listChangedHandles = new Map<string, any>()
|
|
133
162
|
const notifications = yield* RpcClient.makeNoSerialization(ServerNotificationRpcs, {
|
|
163
|
+
spanPrefix: "McpServer/Notifications",
|
|
134
164
|
onFromClient(options): Effect.Effect<void> {
|
|
135
165
|
const message = options.message
|
|
136
166
|
if (message._tag !== "Request") {
|
|
@@ -171,7 +201,7 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
171
201
|
return notifications.client["notifications/tools/list_changed"]({})
|
|
172
202
|
}),
|
|
173
203
|
callTool: (request) =>
|
|
174
|
-
Effect.suspend((): Effect.Effect<CallToolResult, InternalError | InvalidParams> => {
|
|
204
|
+
Effect.suspend((): Effect.Effect<CallToolResult, InternalError | InvalidParams, McpServerClient> => {
|
|
175
205
|
const handle = toolMap.get(request.name)
|
|
176
206
|
if (!handle) {
|
|
177
207
|
return Effect.fail(new InvalidParams({ message: `Tool '${request.name}' not found` }))
|
|
@@ -203,11 +233,7 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
203
233
|
Effect.suspend(() => {
|
|
204
234
|
const match = matcher.find(uri)
|
|
205
235
|
if (!match) {
|
|
206
|
-
return Effect.succeed(
|
|
207
|
-
new ReadResourceResult({
|
|
208
|
-
contents: []
|
|
209
|
-
})
|
|
210
|
-
)
|
|
236
|
+
return Effect.succeed({ contents: [] })
|
|
211
237
|
} else if (match.handler._tag === "Resource") {
|
|
212
238
|
return match.handler.effect
|
|
213
239
|
}
|
|
@@ -250,12 +276,13 @@ export class McpServer extends Context.Tag("@effect/ai/McpServer")<
|
|
|
250
276
|
/**
|
|
251
277
|
* @since 1.0.0
|
|
252
278
|
*/
|
|
253
|
-
static readonly layer: Layer.Layer<McpServer> = Layer.scoped(McpServer, McpServer.make)
|
|
279
|
+
static readonly layer: Layer.Layer<McpServer | McpServerClient> = Layer.scoped(McpServer, McpServer.make) as any
|
|
254
280
|
}
|
|
255
281
|
|
|
256
|
-
const LATEST_PROTOCOL_VERSION = "2025-
|
|
282
|
+
const LATEST_PROTOCOL_VERSION = "2025-06-18"
|
|
257
283
|
const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
258
284
|
LATEST_PROTOCOL_VERSION,
|
|
285
|
+
"2025-03-26",
|
|
259
286
|
"2024-11-05",
|
|
260
287
|
"2024-10-07"
|
|
261
288
|
]
|
|
@@ -264,7 +291,13 @@ const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
|
264
291
|
* @since 1.0.0
|
|
265
292
|
* @category Constructors
|
|
266
293
|
*/
|
|
267
|
-
export const run
|
|
294
|
+
export const run: (
|
|
295
|
+
options: { readonly name: string; readonly version: string }
|
|
296
|
+
) => Effect.Effect<
|
|
297
|
+
never,
|
|
298
|
+
never,
|
|
299
|
+
McpServer | RpcServer.Protocol
|
|
300
|
+
> = Effect.fnUntraced(function*(options: {
|
|
268
301
|
readonly name: string
|
|
269
302
|
readonly version: string
|
|
270
303
|
}) {
|
|
@@ -272,23 +305,86 @@ export const run = Effect.fnUntraced(function*(options: {
|
|
|
272
305
|
const handlers = yield* Layer.build(layerHandlers(options))
|
|
273
306
|
const server = yield* McpServer
|
|
274
307
|
|
|
308
|
+
const clients = yield* RcMap.make({
|
|
309
|
+
lookup: Effect.fnUntraced(function*(clientId: number) {
|
|
310
|
+
let write!: (message: RpcMessage.FromServerEncoded) => Effect.Effect<void>
|
|
311
|
+
const client = yield* RpcClient.make(ServerRequestRpcs, {
|
|
312
|
+
spanPrefix: "McpServer/Client"
|
|
313
|
+
}).pipe(
|
|
314
|
+
Effect.provideServiceEffect(
|
|
315
|
+
RpcClient.Protocol,
|
|
316
|
+
RpcClient.Protocol.make(Effect.fnUntraced(function*(writeResponse) {
|
|
317
|
+
write = writeResponse
|
|
318
|
+
return {
|
|
319
|
+
send(request, _transferables) {
|
|
320
|
+
return protocol.send(clientId, {
|
|
321
|
+
...request,
|
|
322
|
+
headers: undefined,
|
|
323
|
+
traceId: undefined,
|
|
324
|
+
spanId: undefined,
|
|
325
|
+
sampled: undefined
|
|
326
|
+
} as any)
|
|
327
|
+
},
|
|
328
|
+
supportsAck: true,
|
|
329
|
+
supportsTransferables: false
|
|
330
|
+
}
|
|
331
|
+
}))
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return { client, write } as const
|
|
336
|
+
}),
|
|
337
|
+
idleTimeToLive: 10000
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
const clientMiddleware = McpServerClientMiddleware.of(({ clientId }) =>
|
|
341
|
+
Effect.sync(() =>
|
|
342
|
+
McpServerClient.of({
|
|
343
|
+
clientId,
|
|
344
|
+
getClient: RcMap.get(clients, clientId).pipe(
|
|
345
|
+
Effect.map(({ client }) => client)
|
|
346
|
+
)
|
|
347
|
+
})
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
|
|
275
351
|
const patchedProtocol = RpcServer.Protocol.of({
|
|
276
352
|
...protocol,
|
|
277
353
|
run: (f) =>
|
|
278
|
-
protocol.run((clientId,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
354
|
+
protocol.run((clientId, request_) => {
|
|
355
|
+
const request = request_ as any as
|
|
356
|
+
| RpcMessage.FromServerEncoded
|
|
357
|
+
| RpcMessage.FromClientEncoded
|
|
358
|
+
switch (request._tag) {
|
|
359
|
+
case "Request": {
|
|
360
|
+
if (ClientNotificationRpcs.requests.has(request.tag)) {
|
|
361
|
+
if (request.tag === "notifications/cancelled") {
|
|
362
|
+
return f(clientId, {
|
|
363
|
+
_tag: "Interrupt",
|
|
364
|
+
requestId: String((request.payload as any).requestId)
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
const handler = handlers.unsafeMap.get(request.tag) as Rpc.Handler<string>
|
|
368
|
+
return handler
|
|
369
|
+
? handler.handler(request.payload, Headers.fromInput(request.headers)) as Effect.Effect<void>
|
|
370
|
+
: Effect.void
|
|
371
|
+
}
|
|
372
|
+
return f(clientId, request)
|
|
285
373
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
374
|
+
case "Ping":
|
|
375
|
+
case "Ack":
|
|
376
|
+
case "Interrupt":
|
|
377
|
+
case "Eof":
|
|
378
|
+
return f(clientId, request)
|
|
379
|
+
case "Pong":
|
|
380
|
+
case "Exit":
|
|
381
|
+
case "Chunk":
|
|
382
|
+
case "Defect":
|
|
383
|
+
return RcMap.get(clients, clientId).pipe(
|
|
384
|
+
Effect.flatMap(({ write }) => write(request)),
|
|
385
|
+
Effect.scoped
|
|
386
|
+
)
|
|
290
387
|
}
|
|
291
|
-
return f(clientId, request)
|
|
292
388
|
})
|
|
293
389
|
})
|
|
294
390
|
|
|
@@ -318,6 +414,7 @@ export const run = Effect.fnUntraced(function*(options: {
|
|
|
318
414
|
disableFatalDefects: true
|
|
319
415
|
}).pipe(
|
|
320
416
|
Effect.provideService(RpcServer.Protocol, patchedProtocol),
|
|
417
|
+
Effect.provideService(McpServerClientMiddleware, clientMiddleware),
|
|
321
418
|
Effect.provide(handlers)
|
|
322
419
|
)
|
|
323
420
|
}, Effect.scoped)
|
|
@@ -329,7 +426,7 @@ export const run = Effect.fnUntraced(function*(options: {
|
|
|
329
426
|
export const layer = (options: {
|
|
330
427
|
readonly name: string
|
|
331
428
|
readonly version: string
|
|
332
|
-
}): Layer.Layer<McpServer, never, RpcServer.Protocol> =>
|
|
429
|
+
}): Layer.Layer<McpServer | McpServerClient, never, RpcServer.Protocol> =>
|
|
333
430
|
Layer.scopedDiscard(Effect.forkScoped(run(options))).pipe(
|
|
334
431
|
Layer.provideMerge(McpServer.layer)
|
|
335
432
|
)
|
|
@@ -396,7 +493,7 @@ export const layerStdio = <EIn, RIn, EOut, ROut>(options: {
|
|
|
396
493
|
readonly version: string
|
|
397
494
|
readonly stdin: Stream<Uint8Array, EIn, RIn>
|
|
398
495
|
readonly stdout: Sink<unknown, Uint8Array | string, unknown, EOut, ROut>
|
|
399
|
-
}): Layer.Layer<McpServer, never, RIn | ROut> =>
|
|
496
|
+
}): Layer.Layer<McpServer | McpServerClient, never, RIn | ROut> =>
|
|
400
497
|
layer(options).pipe(
|
|
401
498
|
Layer.provide(RpcServer.layerProtocolStdio({
|
|
402
499
|
stdin: options.stdin,
|
|
@@ -471,7 +568,7 @@ export const layerHttp = <I = HttpRouter.Default>(options: {
|
|
|
471
568
|
readonly version: string
|
|
472
569
|
readonly path: HttpRouter.PathInput
|
|
473
570
|
readonly routerTag?: HttpRouter.HttpRouter.TagClass<I, string, any, any>
|
|
474
|
-
}): Layer.Layer<McpServer> =>
|
|
571
|
+
}): Layer.Layer<McpServer | McpServerClient> =>
|
|
475
572
|
layer(options).pipe(
|
|
476
573
|
Layer.provide(RpcServer.layerProtocolHttp(options)),
|
|
477
574
|
Layer.provide(RpcSerialization.layerJsonRpc())
|
|
@@ -486,13 +583,17 @@ export const layerHttp = <I = HttpRouter.Default>(options: {
|
|
|
486
583
|
export const registerToolkit: <Tools extends AiTool.Any>(toolkit: AiToolkit.AiToolkit<Tools>) => Effect.Effect<
|
|
487
584
|
void,
|
|
488
585
|
never,
|
|
489
|
-
McpServer | AiTool.ToHandler<Tools>
|
|
586
|
+
McpServer | AiTool.ToHandler<Tools> | Exclude<AiTool.Context<Tools>, McpServerClient>
|
|
490
587
|
> = Effect.fnUntraced(function*<Tools extends AiTool.Any>(
|
|
491
588
|
toolkit: AiToolkit.AiToolkit<Tools>
|
|
492
589
|
) {
|
|
493
590
|
const registry = yield* McpServer
|
|
494
|
-
const built = yield* toolkit
|
|
495
|
-
|
|
591
|
+
const built = yield* (toolkit as any as Effect.Effect<
|
|
592
|
+
AiToolkit.ToHandler<Tools>,
|
|
593
|
+
never,
|
|
594
|
+
Exclude<AiTool.ToHandler<Tools>, McpServerClient>
|
|
595
|
+
>)
|
|
596
|
+
const context = yield* Effect.context<never>()
|
|
496
597
|
for (const tool of built.tools) {
|
|
497
598
|
const mcpTool = new Tool({
|
|
498
599
|
name: tool.name,
|
|
@@ -513,25 +614,25 @@ export const registerToolkit: <Tools extends AiTool.Any>(toolkit: AiToolkit.AiTo
|
|
|
513
614
|
tool: mcpTool,
|
|
514
615
|
handle(payload) {
|
|
515
616
|
return built.handle(tool.name as any, payload).pipe(
|
|
516
|
-
Effect.provide(context),
|
|
617
|
+
Effect.provide(context as Context.Context<any>),
|
|
517
618
|
Effect.match({
|
|
518
619
|
onFailure: (error) =>
|
|
519
620
|
new CallToolResult({
|
|
520
621
|
isError: true,
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
]
|
|
622
|
+
structuredContent: typeof error === "object" ? error : undefined,
|
|
623
|
+
content: [{
|
|
624
|
+
type: "text",
|
|
625
|
+
text: JSON.stringify(error)
|
|
626
|
+
}]
|
|
526
627
|
}),
|
|
527
628
|
onSuccess: (result) =>
|
|
528
629
|
new CallToolResult({
|
|
529
630
|
isError: false,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
]
|
|
631
|
+
structuredContent: typeof result.encodedResult === "object" ? result.encodedResult : undefined,
|
|
632
|
+
content: [{
|
|
633
|
+
type: "text",
|
|
634
|
+
text: JSON.stringify(result.encodedResult)
|
|
635
|
+
}]
|
|
535
636
|
})
|
|
536
637
|
})
|
|
537
638
|
) as any
|
|
@@ -548,7 +649,11 @@ export const registerToolkit: <Tools extends AiTool.Any>(toolkit: AiToolkit.AiTo
|
|
|
548
649
|
*/
|
|
549
650
|
export const toolkit = <Tools extends AiTool.Any>(
|
|
550
651
|
toolkit: AiToolkit.AiToolkit<Tools>
|
|
551
|
-
): Layer.Layer<
|
|
652
|
+
): Layer.Layer<
|
|
653
|
+
never,
|
|
654
|
+
never,
|
|
655
|
+
AiTool.ToHandler<Tools> | Exclude<AiTool.Context<Tools>, McpServerClient>
|
|
656
|
+
> =>
|
|
552
657
|
Layer.effectDiscard(registerToolkit(toolkit)).pipe(
|
|
553
658
|
Layer.provide(McpServer.layer)
|
|
554
659
|
)
|
|
@@ -594,12 +699,12 @@ export const registerResource: {
|
|
|
594
699
|
readonly audience?: ReadonlyArray<"user" | "assistant"> | undefined
|
|
595
700
|
readonly priority?: number | undefined
|
|
596
701
|
readonly content: Effect.Effect<
|
|
597
|
-
ReadResourceResult | string | Uint8Array,
|
|
702
|
+
typeof ReadResourceResult.Type | string | Uint8Array,
|
|
598
703
|
E,
|
|
599
704
|
R
|
|
600
705
|
>
|
|
601
706
|
}
|
|
602
|
-
): Effect.Effect<void, never, R | McpServer>
|
|
707
|
+
): Effect.Effect<void, never, Exclude<R, McpServerClient> | McpServer>
|
|
603
708
|
/**
|
|
604
709
|
* Register a resource with the McpServer.
|
|
605
710
|
*
|
|
@@ -626,23 +731,26 @@ export const registerResource: {
|
|
|
626
731
|
readonly priority?: number | undefined
|
|
627
732
|
readonly completion?: ValidateCompletions<Completions, keyof ResourceCompletions<Schemas>> | undefined
|
|
628
733
|
readonly content: (uri: string, ...params: { readonly [K in keyof Schemas]: Schemas[K]["Type"] }) => Effect.Effect<
|
|
629
|
-
ReadResourceResult | string | Uint8Array,
|
|
734
|
+
typeof ReadResourceResult.Type | string | Uint8Array,
|
|
630
735
|
E,
|
|
631
736
|
R
|
|
632
737
|
>
|
|
633
738
|
}) => Effect.Effect<
|
|
634
739
|
void,
|
|
635
740
|
never,
|
|
636
|
-
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
741
|
+
| Exclude<
|
|
742
|
+
| R
|
|
743
|
+
| (Completions[keyof Completions] extends (input: string) => infer Ret ?
|
|
744
|
+
Ret extends Effect.Effect<infer _A, infer _E, infer _R> ? _R : never
|
|
745
|
+
: never),
|
|
746
|
+
McpServerClient
|
|
747
|
+
>
|
|
640
748
|
| McpServer
|
|
641
749
|
>
|
|
642
750
|
} = function() {
|
|
643
751
|
if (arguments.length === 1) {
|
|
644
|
-
const options = arguments[0] as Resource & Annotations & {
|
|
645
|
-
readonly content: Effect.Effect<ReadResourceResult | string | Uint8Array>
|
|
752
|
+
const options = arguments[0] as Resource & typeof Annotations.Type & {
|
|
753
|
+
readonly content: Effect.Effect<typeof ReadResourceResult.Type | string | Uint8Array>
|
|
646
754
|
}
|
|
647
755
|
return Effect.gen(function*() {
|
|
648
756
|
const context = yield* Effect.context<any>()
|
|
@@ -650,7 +758,7 @@ export const registerResource: {
|
|
|
650
758
|
yield* registry.addResource(
|
|
651
759
|
new Resource({
|
|
652
760
|
...options,
|
|
653
|
-
annotations:
|
|
761
|
+
annotations: options
|
|
654
762
|
}),
|
|
655
763
|
options.content.pipe(
|
|
656
764
|
Effect.provide(context),
|
|
@@ -677,7 +785,7 @@ export const registerResource: {
|
|
|
677
785
|
readonly priority?: number | undefined
|
|
678
786
|
readonly completion?: Record<string, (input: string) => Effect.Effect<any>> | undefined
|
|
679
787
|
readonly content: (uri: string, ...params: Array<any>) => Effect.Effect<
|
|
680
|
-
ReadResourceResult | string | Uint8Array,
|
|
788
|
+
typeof ReadResourceResult.Type | string | Uint8Array,
|
|
681
789
|
E,
|
|
682
790
|
R
|
|
683
791
|
>
|
|
@@ -688,7 +796,7 @@ export const registerResource: {
|
|
|
688
796
|
const template = new ResourceTemplate({
|
|
689
797
|
...options,
|
|
690
798
|
uriTemplate: uriPath,
|
|
691
|
-
annotations:
|
|
799
|
+
annotations: options
|
|
692
800
|
})
|
|
693
801
|
const completions: Record<string, (input: string) => Effect.Effect<CompleteResult, InternalError>> = {}
|
|
694
802
|
for (const [param, handle] of Object.entries(options.completion ?? {})) {
|
|
@@ -757,12 +865,12 @@ export const resource: {
|
|
|
757
865
|
readonly audience?: ReadonlyArray<"user" | "assistant"> | undefined
|
|
758
866
|
readonly priority?: number | undefined
|
|
759
867
|
readonly content: Effect.Effect<
|
|
760
|
-
ReadResourceResult | string | Uint8Array,
|
|
868
|
+
typeof ReadResourceResult.Type | string | Uint8Array,
|
|
761
869
|
E,
|
|
762
870
|
R
|
|
763
871
|
>
|
|
764
872
|
}
|
|
765
|
-
): Layer.Layer<never, never, R
|
|
873
|
+
): Layer.Layer<never, never, Exclude<R, McpServerClient>>
|
|
766
874
|
/**
|
|
767
875
|
* Register a resource with the McpServer.
|
|
768
876
|
*
|
|
@@ -789,17 +897,20 @@ export const resource: {
|
|
|
789
897
|
readonly priority?: number | undefined
|
|
790
898
|
readonly completion?: ValidateCompletions<Completions, keyof ResourceCompletions<Schemas>> | undefined
|
|
791
899
|
readonly content: (uri: string, ...params: { readonly [K in keyof Schemas]: Schemas[K]["Type"] }) => Effect.Effect<
|
|
792
|
-
ReadResourceResult | string | Uint8Array,
|
|
900
|
+
typeof ReadResourceResult.Type | string | Uint8Array,
|
|
793
901
|
E,
|
|
794
902
|
R
|
|
795
903
|
>
|
|
796
904
|
}) => Layer.Layer<
|
|
797
905
|
never,
|
|
798
906
|
never,
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
907
|
+
Exclude<
|
|
908
|
+
| R
|
|
909
|
+
| (Completions[keyof Completions] extends (input: string) => infer Ret ?
|
|
910
|
+
Ret extends Effect.Effect<infer _A, infer _E, infer _R> ? _R : never
|
|
911
|
+
: never),
|
|
912
|
+
McpServerClient
|
|
913
|
+
>
|
|
803
914
|
>
|
|
804
915
|
} = function() {
|
|
805
916
|
if (arguments.length === 1) {
|
|
@@ -818,7 +929,7 @@ export const resource: {
|
|
|
818
929
|
* Register a prompt with the McpServer.
|
|
819
930
|
*
|
|
820
931
|
* @since 1.0.0
|
|
821
|
-
* @category
|
|
932
|
+
* @category Prompts
|
|
822
933
|
*/
|
|
823
934
|
export const registerPrompt = <
|
|
824
935
|
E,
|
|
@@ -835,20 +946,18 @@ export const registerPrompt = <
|
|
|
835
946
|
readonly description?: string | undefined
|
|
836
947
|
readonly parameters?: Schema.Schema<Params, ParamsI, ParamsR> | undefined
|
|
837
948
|
readonly completion?: ValidateCompletions<Completions, Extract<keyof Params, string>> | undefined
|
|
838
|
-
readonly content: (params: Params) => Effect.Effect<Array<PromptMessage> | string, E, R>
|
|
949
|
+
readonly content: (params: Params) => Effect.Effect<Array<typeof PromptMessage.Type> | string, E, R>
|
|
839
950
|
}
|
|
840
|
-
): Effect.Effect<void, never, ParamsR | R | McpServer> => {
|
|
841
|
-
const args = Arr.empty<PromptArgument>()
|
|
951
|
+
): Effect.Effect<void, never, Exclude<ParamsR | R, McpServerClient> | McpServer> => {
|
|
952
|
+
const args = Arr.empty<typeof PromptArgument.Type>()
|
|
842
953
|
const props: Record<string, Schema.Schema.Any> = {}
|
|
843
954
|
const propSignatures = options.parameters ? AST.getPropertySignatures(options.parameters.ast) : []
|
|
844
955
|
for (const prop of propSignatures) {
|
|
845
|
-
args.push(
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
})
|
|
851
|
-
)
|
|
956
|
+
args.push({
|
|
957
|
+
name: prop.name as string,
|
|
958
|
+
description: Option.getOrUndefined(AST.getDescriptionAnnotation(prop)),
|
|
959
|
+
required: !prop.isOptional
|
|
960
|
+
})
|
|
852
961
|
props[prop.name as string] = Schema.make(prop.type)
|
|
853
962
|
}
|
|
854
963
|
const prompt = new Prompt({
|
|
@@ -860,22 +969,23 @@ export const registerPrompt = <
|
|
|
860
969
|
const completion: Record<string, (input: string) => Effect.Effect<any>> = options.completion ?? {}
|
|
861
970
|
return Effect.gen(function*() {
|
|
862
971
|
const registry = yield* McpServer
|
|
863
|
-
const context = yield* Effect.context<R | ParamsR
|
|
864
|
-
const completions: Record<
|
|
972
|
+
const context = yield* Effect.context<Exclude<R | ParamsR, McpServerClient>>()
|
|
973
|
+
const completions: Record<
|
|
974
|
+
string,
|
|
975
|
+
(input: string) => Effect.Effect<CompleteResult, InternalError, McpServerClient>
|
|
976
|
+
> = {}
|
|
865
977
|
for (const [param, handle] of Object.entries(completion)) {
|
|
866
978
|
const encodeArray = Schema.encodeUnknown(Schema.Array(props[param]))
|
|
867
979
|
const handler = (input: string) =>
|
|
868
980
|
handle(input).pipe(
|
|
869
981
|
Effect.flatMap(encodeArray),
|
|
870
|
-
Effect.map((values) =>
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
})
|
|
878
|
-
),
|
|
982
|
+
Effect.map((values) => ({
|
|
983
|
+
completion: {
|
|
984
|
+
values: values as Array<string>,
|
|
985
|
+
total: values.length,
|
|
986
|
+
hasMore: false
|
|
987
|
+
}
|
|
988
|
+
})),
|
|
879
989
|
Effect.catchAllCause((cause) => {
|
|
880
990
|
const prettyError = Cause.prettyErrors(cause)[0]
|
|
881
991
|
return new InternalError({ message: prettyError.message })
|
|
@@ -893,12 +1003,10 @@ export const registerPrompt = <
|
|
|
893
1003
|
Effect.flatMap((params) => options.content(params)),
|
|
894
1004
|
Effect.map((messages) => {
|
|
895
1005
|
messages = typeof messages === "string" ?
|
|
896
|
-
[
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
})
|
|
901
|
-
] :
|
|
1006
|
+
[{
|
|
1007
|
+
role: "user",
|
|
1008
|
+
content: TextContent.make({ text: messages })
|
|
1009
|
+
}] :
|
|
902
1010
|
messages
|
|
903
1011
|
return new GetPromptResult({ messages, description: prompt.description })
|
|
904
1012
|
}),
|
|
@@ -906,7 +1014,7 @@ export const registerPrompt = <
|
|
|
906
1014
|
const prettyError = Cause.prettyErrors(cause)[0]
|
|
907
1015
|
return new InternalError({ message: prettyError.message })
|
|
908
1016
|
}),
|
|
909
|
-
Effect.provide(context)
|
|
1017
|
+
Effect.provide(context as Context.Context<ParamsR | R>)
|
|
910
1018
|
)
|
|
911
1019
|
})
|
|
912
1020
|
})
|
|
@@ -916,7 +1024,7 @@ export const registerPrompt = <
|
|
|
916
1024
|
* Register a prompt with the McpServer.
|
|
917
1025
|
*
|
|
918
1026
|
* @since 1.0.0
|
|
919
|
-
* @category
|
|
1027
|
+
* @category Prompts
|
|
920
1028
|
*/
|
|
921
1029
|
export const prompt = <
|
|
922
1030
|
E,
|
|
@@ -933,13 +1041,49 @@ export const prompt = <
|
|
|
933
1041
|
readonly description?: string | undefined
|
|
934
1042
|
readonly parameters?: Schema.Schema<Params, ParamsI, ParamsR> | undefined
|
|
935
1043
|
readonly completion?: ValidateCompletions<Completions, Extract<keyof Params, string>> | undefined
|
|
936
|
-
readonly content: (params: Params) => Effect.Effect<Array<PromptMessage> | string, E, R>
|
|
1044
|
+
readonly content: (params: Params) => Effect.Effect<Array<typeof PromptMessage.Type> | string, E, R>
|
|
937
1045
|
}
|
|
938
|
-
): Layer.Layer<never, never, ParamsR | R
|
|
1046
|
+
): Layer.Layer<never, never, Exclude<ParamsR | R, McpServerClient>> =>
|
|
939
1047
|
Layer.effectDiscard(registerPrompt(options)).pipe(
|
|
940
1048
|
Layer.provide(McpServer.layer)
|
|
941
1049
|
)
|
|
942
1050
|
|
|
1051
|
+
/**
|
|
1052
|
+
* Create an elicitation request
|
|
1053
|
+
*
|
|
1054
|
+
* @since 1.0.0
|
|
1055
|
+
* @category Elicitation
|
|
1056
|
+
*/
|
|
1057
|
+
export const elicit: <A, I extends Record<string, any>, R>(options: {
|
|
1058
|
+
readonly message: string
|
|
1059
|
+
readonly schema: Schema.Schema<A, I, R>
|
|
1060
|
+
}) => Effect.Effect<
|
|
1061
|
+
A,
|
|
1062
|
+
ElicitationDeclined,
|
|
1063
|
+
McpServerClient | R
|
|
1064
|
+
> = Effect.fnUntraced(function*<A, I extends Record<string, any>, R>(options: {
|
|
1065
|
+
readonly message: string
|
|
1066
|
+
readonly schema: Schema.Schema<A, I, R>
|
|
1067
|
+
}) {
|
|
1068
|
+
const { getClient } = yield* McpServerClient
|
|
1069
|
+
const client = yield* getClient
|
|
1070
|
+
const request = Elicit.payloadSchema.make({
|
|
1071
|
+
message: options.message,
|
|
1072
|
+
requestedSchema: makeJsonSchema(options.schema.ast)
|
|
1073
|
+
})
|
|
1074
|
+
const res = yield* client["elicitation/create"](request).pipe(
|
|
1075
|
+
Effect.catchAllCause((cause) => Effect.fail(new ElicitationDeclined({ cause: Cause.squash(cause), request })))
|
|
1076
|
+
)
|
|
1077
|
+
switch (res.action) {
|
|
1078
|
+
case "accept":
|
|
1079
|
+
return yield* Effect.orDie(Schema.decodeUnknown(options.schema)(res.content))
|
|
1080
|
+
case "cancel":
|
|
1081
|
+
return yield* Effect.interrupt
|
|
1082
|
+
case "decline":
|
|
1083
|
+
return yield* Effect.fail(new ElicitationDeclined({ request }))
|
|
1084
|
+
}
|
|
1085
|
+
}, Effect.scoped)
|
|
1086
|
+
|
|
943
1087
|
// -----------------------------------------------------------------------------
|
|
944
1088
|
// Internal
|
|
945
1089
|
// -----------------------------------------------------------------------------
|
|
@@ -967,14 +1111,15 @@ const compileUriTemplate = (segments: TemplateStringsArray, ...schemas: Readonly
|
|
|
967
1111
|
const arr: Array<Schema.Schema.Any> = []
|
|
968
1112
|
for (let i = 0; i < schemas.length; i++) {
|
|
969
1113
|
const schema = schemas[i]
|
|
1114
|
+
const segment = segments[i + 1]
|
|
970
1115
|
const key = String(i)
|
|
971
1116
|
arr.push(schema)
|
|
972
|
-
routerPath += `:${key}${
|
|
1117
|
+
routerPath += `:${key}${segment.replace(":", "::")}`
|
|
973
1118
|
const paramName = AST.getAnnotation(ParamAnnotation)(schema.ast).pipe(
|
|
974
1119
|
Option.getOrElse(() => `param${key}`)
|
|
975
1120
|
)
|
|
976
1121
|
params[paramName as string] = schema
|
|
977
|
-
uriPath += `{${paramName}}`
|
|
1122
|
+
uriPath += `{${paramName}}${segment}`
|
|
978
1123
|
}
|
|
979
1124
|
pathSchema = Schema.Tuple(...arr)
|
|
980
1125
|
}
|
|
@@ -999,7 +1144,7 @@ const layerHandlers = (serverInfo: {
|
|
|
999
1144
|
ping: () => Effect.succeed({}),
|
|
1000
1145
|
initialize(params) {
|
|
1001
1146
|
const requestedVersion = params.protocolVersion
|
|
1002
|
-
const capabilities: Types.DeepMutable<ServerCapabilities> = {
|
|
1147
|
+
const capabilities: Types.DeepMutable<typeof ServerCapabilities.Type> = {
|
|
1003
1148
|
completions: {}
|
|
1004
1149
|
}
|
|
1005
1150
|
if (server.tools.length > 0) {
|
|
@@ -1064,25 +1209,24 @@ const makeJsonSchema = (ast: AST.AST): JsonSchema.JsonSchema7 => {
|
|
|
1064
1209
|
return schema
|
|
1065
1210
|
}
|
|
1066
1211
|
|
|
1067
|
-
const resolveResourceContent = (
|
|
1212
|
+
const resolveResourceContent = (
|
|
1213
|
+
uri: string,
|
|
1214
|
+
content: typeof ReadResourceResult.Type | string | Uint8Array
|
|
1215
|
+
): typeof ReadResourceResult.Type => {
|
|
1068
1216
|
if (typeof content === "string") {
|
|
1069
|
-
return
|
|
1070
|
-
contents: [
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
]
|
|
1076
|
-
})
|
|
1217
|
+
return {
|
|
1218
|
+
contents: [{
|
|
1219
|
+
uri,
|
|
1220
|
+
text: content
|
|
1221
|
+
}]
|
|
1222
|
+
}
|
|
1077
1223
|
} else if (content instanceof Uint8Array) {
|
|
1078
|
-
return
|
|
1079
|
-
contents: [
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
]
|
|
1085
|
-
})
|
|
1224
|
+
return {
|
|
1225
|
+
contents: [{
|
|
1226
|
+
uri,
|
|
1227
|
+
blob: content
|
|
1228
|
+
}]
|
|
1229
|
+
}
|
|
1086
1230
|
}
|
|
1087
1231
|
return content
|
|
1088
1232
|
}
|