@effect/ai 0.18.12 → 0.18.13

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.
@@ -0,0 +1,691 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as Headers from "@effect/platform/Headers";
5
+ import * as RpcClient from "@effect/rpc/RpcClient";
6
+ import * as RpcSerialization from "@effect/rpc/RpcSerialization";
7
+ import * as RpcServer from "@effect/rpc/RpcServer";
8
+ import * as Arr from "effect/Array";
9
+ import * as Cause from "effect/Cause";
10
+ import * as Context from "effect/Context";
11
+ import * as Effect from "effect/Effect";
12
+ import * as Exit from "effect/Exit";
13
+ import * as JsonSchema from "effect/JSONSchema";
14
+ import * as Layer from "effect/Layer";
15
+ import * as Logger from "effect/Logger";
16
+ import * as Mailbox from "effect/Mailbox";
17
+ import * as Option from "effect/Option";
18
+ import * as Schema from "effect/Schema";
19
+ import * as AST from "effect/SchemaAST";
20
+ import * as FindMyWay from "find-my-way-ts";
21
+ import * as AiTool from "./AiTool.js";
22
+ import { Annotations, BlobResourceContents, CallToolResult, ClientRpcs, CompleteResult, GetPromptResult, InternalError, InvalidParams, ListPromptsResult, ListResourcesResult, ListResourceTemplatesResult, ListToolsResult, ParamAnnotation, Prompt, PromptArgument, PromptMessage, ReadResourceResult, Resource, ResourceTemplate, ServerNotificationRpcs, TextContent, TextResourceContents, Tool, ToolAnnotations } from "./McpSchema.js";
23
+ /**
24
+ * @since 1.0.0
25
+ * @category McpServer
26
+ */
27
+ export class McpServer extends /*#__PURE__*/Context.Tag("@effect/ai/McpServer")() {
28
+ /**
29
+ * @since 1.0.0
30
+ */
31
+ static make = /*#__PURE__*/Effect.gen(function* () {
32
+ const matcher = makeUriMatcher();
33
+ const tools = Arr.empty();
34
+ const toolMap = new Map();
35
+ const resources = [];
36
+ const resourceTemplates = [];
37
+ const prompts = [];
38
+ const promptMap = new Map();
39
+ const completionsMap = new Map();
40
+ const notificationsMailbox = yield* Mailbox.make();
41
+ const listChangedHandles = new Map();
42
+ const notifications = yield* RpcClient.makeNoSerialization(ServerNotificationRpcs, {
43
+ onFromClient(options) {
44
+ const message = options.message;
45
+ if (message._tag !== "Request") {
46
+ return Effect.void;
47
+ }
48
+ if (message.tag.includes("list_changed")) {
49
+ if (!listChangedHandles.has(message.tag)) {
50
+ listChangedHandles.set(message.tag, setTimeout(() => {
51
+ notificationsMailbox.unsafeOffer(message);
52
+ listChangedHandles.delete(message.tag);
53
+ }, 0));
54
+ }
55
+ } else {
56
+ notificationsMailbox.unsafeOffer(message);
57
+ }
58
+ return notifications.write({
59
+ clientId: 0,
60
+ requestId: message.id,
61
+ _tag: "Exit",
62
+ exit: Exit.void
63
+ });
64
+ }
65
+ });
66
+ return McpServer.of({
67
+ notifications: notifications.client,
68
+ notificationsMailbox,
69
+ get tools() {
70
+ return tools;
71
+ },
72
+ addTool: options => Effect.suspend(() => {
73
+ tools.push(options.tool);
74
+ toolMap.set(options.tool.name, options.handle);
75
+ return notifications.client["notifications/tools/list_changed"]({});
76
+ }),
77
+ callTool: request => Effect.suspend(() => {
78
+ const handle = toolMap.get(request.name);
79
+ if (!handle) {
80
+ return Effect.fail(new InvalidParams({
81
+ message: `Tool '${request.name}' not found`
82
+ }));
83
+ }
84
+ return handle(request.arguments);
85
+ }),
86
+ get resources() {
87
+ return resources;
88
+ },
89
+ get resourceTemplates() {
90
+ return resourceTemplates;
91
+ },
92
+ addResource: (resource, effect) => Effect.suspend(() => {
93
+ resources.push(resource);
94
+ matcher.add(resource.uri, {
95
+ _tag: "Resource",
96
+ effect
97
+ });
98
+ return notifications.client["notifications/resources/list_changed"]({});
99
+ }),
100
+ addResourceTemplate: ({
101
+ completions,
102
+ handle,
103
+ routerPath,
104
+ template
105
+ }) => Effect.suspend(() => {
106
+ resourceTemplates.push(template);
107
+ matcher.add(routerPath, {
108
+ _tag: "ResourceTemplate",
109
+ handle
110
+ });
111
+ for (const [param, handle] of Object.entries(completions)) {
112
+ completionsMap.set(`ref/resource/${template.uriTemplate}/${param}`, handle);
113
+ }
114
+ return notifications.client["notifications/resources/list_changed"]({});
115
+ }),
116
+ findResource: uri => Effect.suspend(() => {
117
+ const match = matcher.find(uri);
118
+ if (!match) {
119
+ return Effect.succeed(new ReadResourceResult({
120
+ contents: []
121
+ }));
122
+ } else if (match.handler._tag === "Resource") {
123
+ return match.handler.effect;
124
+ }
125
+ const params = [];
126
+ for (const key of Object.keys(match.params)) {
127
+ params[Number(key)] = match.params[key];
128
+ }
129
+ return match.handler.handle(uri, params);
130
+ }),
131
+ get prompts() {
132
+ return prompts;
133
+ },
134
+ addPrompt: options => Effect.suspend(() => {
135
+ prompts.push(options.prompt);
136
+ promptMap.set(options.prompt.name, options.handle);
137
+ for (const [param, handle] of Object.entries(options.completions)) {
138
+ completionsMap.set(`ref/prompt/${options.prompt.name}/${param}`, handle);
139
+ }
140
+ return notifications.client["notifications/prompts/list_changed"]({});
141
+ }),
142
+ getPromptResult: Effect.fnUntraced(function* ({
143
+ arguments: params,
144
+ name
145
+ }) {
146
+ const handler = promptMap.get(name);
147
+ if (!handler) {
148
+ return yield* new InvalidParams({
149
+ message: `Prompt '${name}' not found`
150
+ });
151
+ }
152
+ return yield* handler(params ?? {});
153
+ }),
154
+ completion: Effect.fnUntraced(function* (complete) {
155
+ const ref = complete.ref;
156
+ const key = ref.type === "ref/resource" ? `ref/resource/${ref.uri}/${complete.argument.name}` : `ref/prompt/${ref.name}/${complete.argument.name}`;
157
+ const handler = completionsMap.get(key);
158
+ return handler ? yield* handler(complete.argument.value) : CompleteResult.empty;
159
+ })
160
+ });
161
+ });
162
+ /**
163
+ * @since 1.0.0
164
+ */
165
+ static layer = /*#__PURE__*/Layer.scoped(McpServer, McpServer.make);
166
+ }
167
+ const LATEST_PROTOCOL_VERSION = "2025-03-26";
168
+ const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2024-11-05", "2024-10-07"];
169
+ /**
170
+ * @since 1.0.0
171
+ * @category Constructors
172
+ */
173
+ export const run = /*#__PURE__*/Effect.fnUntraced(function* (options) {
174
+ const protocol = yield* RpcServer.Protocol;
175
+ const handlers = yield* Layer.build(layerHandlers(options));
176
+ const server = yield* McpServer;
177
+ const patchedProtocol = RpcServer.Protocol.of({
178
+ ...protocol,
179
+ run: f => protocol.run((clientId, request) => {
180
+ if (request._tag === "Request" && request.tag.startsWith("notifications/")) {
181
+ if (request.tag === "notifications/cancelled") {
182
+ return f(clientId, {
183
+ _tag: "Interrupt",
184
+ requestId: String(request.payload.requestId)
185
+ });
186
+ }
187
+ const handler = handlers.unsafeMap.get(request.tag);
188
+ return handler ? handler.handler(request.payload, Headers.fromInput(request.headers)) : Effect.void;
189
+ }
190
+ return f(clientId, request);
191
+ })
192
+ });
193
+ const encodeNotification = Schema.encode(Schema.Union(...Array.from(ServerNotificationRpcs.requests.values(), rpc => rpc.payloadSchema)));
194
+ yield* server.notificationsMailbox.take.pipe(Effect.flatMap(Effect.fnUntraced(function* (request) {
195
+ const encoded = yield* encodeNotification(request.payload);
196
+ const message = {
197
+ _tag: "Request",
198
+ tag: request.tag,
199
+ payload: encoded
200
+ };
201
+ const clientIds = yield* patchedProtocol.clientIds;
202
+ for (const clientId of clientIds) {
203
+ yield* patchedProtocol.send(clientId, message);
204
+ }
205
+ })), Effect.catchAllCause(() => Effect.void), Effect.forever, Effect.forkScoped);
206
+ return yield* RpcServer.make(ClientRpcs, {
207
+ spanPrefix: "McpServer",
208
+ disableFatalDefects: true
209
+ }).pipe(Effect.provideService(RpcServer.Protocol, patchedProtocol), Effect.provide(handlers));
210
+ }, Effect.scoped);
211
+ /**
212
+ * @since 1.0.0
213
+ * @category Layers
214
+ */
215
+ export const layer = options => Layer.scopedDiscard(Effect.forkScoped(run(options))).pipe(Layer.provideMerge(McpServer.layer));
216
+ /**
217
+ * Run the McpServer, using stdio for input and output.
218
+ *
219
+ * ```ts
220
+ * import { McpSchema, McpServer } from "@effect/ai"
221
+ * import { NodeRuntime, NodeSink, NodeStream } from "@effect/platform-node"
222
+ * import { Effect, Layer, Logger, Schema } from "effect"
223
+ *
224
+ * const idParam = McpSchema.param("id", Schema.NumberFromString)
225
+ *
226
+ * // Define a resource template for a README file
227
+ * const ReadmeTemplate = McpServer.resource`file://readme/${idParam}`({
228
+ * name: "README Template",
229
+ * // You can add auto-completion for the ID parameter
230
+ * completion: {
231
+ * id: (_) => Effect.succeed([1, 2, 3, 4, 5])
232
+ * },
233
+ * content: Effect.fn(function*(_uri, id) {
234
+ * return `# MCP Server Demo - ID: ${id}`
235
+ * })
236
+ * })
237
+ *
238
+ * // Define a test prompt with parameters
239
+ * const TestPrompt = McpServer.prompt({
240
+ * name: "Test Prompt",
241
+ * description: "A test prompt to demonstrate MCP server capabilities",
242
+ * parameters: Schema.Struct({
243
+ * flightNumber: Schema.String
244
+ * }),
245
+ * completion: {
246
+ * flightNumber: () => Effect.succeed(["FL123", "FL456", "FL789"])
247
+ * },
248
+ * content: ({ flightNumber }) => Effect.succeed(`Get the booking details for flight number: ${flightNumber}`)
249
+ * })
250
+ *
251
+ * // Merge all the resources and prompts into a single server layer
252
+ * const ServerLayer = Layer.mergeAll(
253
+ * ReadmeTemplate,
254
+ * TestPrompt
255
+ * ).pipe(
256
+ * // Provide the MCP server implementation
257
+ * Layer.provide(McpServer.layerStdio({
258
+ * name: "Demo Server",
259
+ * version: "1.0.0",
260
+ * stdin: NodeStream.stdin,
261
+ * stdout: NodeSink.stdout
262
+ * })),
263
+ * // add a stderr logger
264
+ * Layer.provide(Logger.add(Logger.prettyLogger({ stderr: true })))
265
+ * )
266
+ *
267
+ * Layer.launch(ServerLayer).pipe(NodeRuntime.runMain)
268
+ * ```
269
+ *
270
+ * @since 1.0.0
271
+ * @category Layers
272
+ */
273
+ export const layerStdio = options => layer(options).pipe(Layer.provide(RpcServer.layerProtocolStdio({
274
+ stdin: options.stdin,
275
+ stdout: options.stdout
276
+ })), Layer.provide(RpcSerialization.layerNdJsonRpc()),
277
+ // remove stdout loggers
278
+ Layer.provideMerge(Logger.remove(Logger.defaultLogger)), Layer.provideMerge(Logger.remove(Logger.prettyLoggerDefault)));
279
+ /**
280
+ * Run the McpServer, using HTTP for input and output.
281
+ *
282
+ * ```ts
283
+ * import { McpSchema, McpServer } from "@effect/ai"
284
+ * import { HttpRouter } from "@effect/platform"
285
+ * import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
286
+ * import { Effect, Layer, Schema } from "effect"
287
+ * import { createServer } from "node:http"
288
+ *
289
+ * const idParam = McpSchema.param("id", Schema.NumberFromString)
290
+ *
291
+ * // Define a resource template for a README file
292
+ * const ReadmeTemplate = McpServer.resource`file://readme/${idParam}`({
293
+ * name: "README Template",
294
+ * // You can add auto-completion for the ID parameter
295
+ * completion: {
296
+ * id: (_) => Effect.succeed([1, 2, 3, 4, 5])
297
+ * },
298
+ * content: Effect.fn(function*(_uri, id) {
299
+ * return `# MCP Server Demo - ID: ${id}`
300
+ * })
301
+ * })
302
+ *
303
+ * // Define a test prompt with parameters
304
+ * const TestPrompt = McpServer.prompt({
305
+ * name: "Test Prompt",
306
+ * description: "A test prompt to demonstrate MCP server capabilities",
307
+ * parameters: Schema.Struct({
308
+ * flightNumber: Schema.String
309
+ * }),
310
+ * completion: {
311
+ * flightNumber: () => Effect.succeed(["FL123", "FL456", "FL789"])
312
+ * },
313
+ * content: ({ flightNumber }) => Effect.succeed(`Get the booking details for flight number: ${flightNumber}`)
314
+ * })
315
+ *
316
+ * // Merge all the resources and prompts into a single server layer
317
+ * const ServerLayer = Layer.mergeAll(
318
+ * ReadmeTemplate,
319
+ * TestPrompt,
320
+ * HttpRouter.Default.serve()
321
+ * ).pipe(
322
+ * // Provide the MCP server implementation
323
+ * Layer.provide(McpServer.layerHttp({
324
+ * name: "Demo Server",
325
+ * version: "1.0.0",
326
+ * path: "/mcp"
327
+ * })),
328
+ * Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))
329
+ * )
330
+ *
331
+ * Layer.launch(ServerLayer).pipe(NodeRuntime.runMain)
332
+ * ```
333
+ *
334
+ * @since 1.0.0
335
+ * @category Layers
336
+ */
337
+ export const layerHttp = options => layer(options).pipe(Layer.provide(RpcServer.layerProtocolHttp(options)), Layer.provide(RpcSerialization.layerJsonRpc()));
338
+ /**
339
+ * Register an AiToolkit with the McpServer.
340
+ *
341
+ * @since 1.0.0
342
+ * @category Tools
343
+ */
344
+ export const registerToolkit = /*#__PURE__*/Effect.fnUntraced(function* (toolkit) {
345
+ const registry = yield* McpServer;
346
+ const built = yield* toolkit;
347
+ const context = yield* Effect.context();
348
+ for (const tool of built.tools) {
349
+ const mcpTool = new Tool({
350
+ name: tool.name,
351
+ description: tool.description,
352
+ inputSchema: makeJsonSchema(tool.parametersSchema.ast),
353
+ annotations: new ToolAnnotations({
354
+ ...Context.getOption(tool.annotations, AiTool.Title).pipe(Option.map(title => ({
355
+ title
356
+ })), Option.getOrUndefined),
357
+ readOnlyHint: Context.get(tool.annotations, AiTool.Readonly),
358
+ destructiveHint: Context.get(tool.annotations, AiTool.Destructive),
359
+ idempotentHint: Context.get(tool.annotations, AiTool.Idempotent),
360
+ openWorldHint: Context.get(tool.annotations, AiTool.OpenWorld)
361
+ })
362
+ });
363
+ yield* registry.addTool({
364
+ tool: mcpTool,
365
+ handle(payload) {
366
+ return built.handle(tool.name, payload).pipe(Effect.provide(context), Effect.match({
367
+ onFailure: error => new CallToolResult({
368
+ isError: true,
369
+ content: [new TextContent({
370
+ text: JSON.stringify(error)
371
+ })]
372
+ }),
373
+ onSuccess: result => new CallToolResult({
374
+ isError: false,
375
+ content: [new TextContent({
376
+ text: JSON.stringify(result.encodedResult)
377
+ })]
378
+ })
379
+ }));
380
+ }
381
+ });
382
+ }
383
+ });
384
+ /**
385
+ * Register an AiToolkit with the McpServer.
386
+ *
387
+ * @since 1.0.0
388
+ * @category Tools
389
+ */
390
+ export const toolkit = toolkit => Layer.effectDiscard(registerToolkit(toolkit));
391
+ /**
392
+ * Register a resource with the McpServer.
393
+ *
394
+ * @since 1.0.0
395
+ * @category Resources
396
+ */
397
+ export const registerResource = function () {
398
+ if (arguments.length === 1) {
399
+ const options = arguments[0];
400
+ return Effect.gen(function* () {
401
+ const context = yield* Effect.context();
402
+ const registry = yield* McpServer;
403
+ yield* registry.addResource(new Resource({
404
+ ...options,
405
+ annotations: new Annotations(options)
406
+ }), options.content.pipe(Effect.provide(context), Effect.map(content => resolveResourceContent(options.uri, content)), Effect.catchAllCause(cause => {
407
+ const prettyError = Cause.prettyErrors(cause)[0];
408
+ return new InternalError({
409
+ message: prettyError.message
410
+ });
411
+ })));
412
+ });
413
+ }
414
+ const {
415
+ params,
416
+ routerPath,
417
+ schema,
418
+ uriPath
419
+ } = compileUriTemplate(...arguments);
420
+ return Effect.fnUntraced(function* (options) {
421
+ const context = yield* Effect.context();
422
+ const registry = yield* McpServer;
423
+ const decode = Schema.decodeUnknown(schema);
424
+ const template = new ResourceTemplate({
425
+ ...options,
426
+ uriTemplate: uriPath,
427
+ annotations: new Annotations(options)
428
+ });
429
+ const completions = {};
430
+ for (const [param, handle] of Object.entries(options.completion ?? {})) {
431
+ const encodeArray = Schema.encodeUnknown(Schema.Array(params[param]));
432
+ const handler = input => handle(input).pipe(Effect.flatMap(encodeArray), Effect.map(values => new CompleteResult({
433
+ completion: {
434
+ values: values,
435
+ total: values.length,
436
+ hasMore: false
437
+ }
438
+ })), Effect.catchAllCause(cause => {
439
+ const prettyError = Cause.prettyErrors(cause)[0];
440
+ return new InternalError({
441
+ message: prettyError.message
442
+ });
443
+ }), Effect.provide(context));
444
+ completions[param] = handler;
445
+ }
446
+ yield* registry.addResourceTemplate({
447
+ template,
448
+ routerPath,
449
+ completions,
450
+ handle: (uri, params) => decode(params).pipe(Effect.mapError(error => new InvalidParams({
451
+ message: error.message
452
+ })), Effect.flatMap(params => options.content(uri, ...params).pipe(Effect.map(content => resolveResourceContent(uri, content)), Effect.catchAllCause(cause => {
453
+ const prettyError = Cause.prettyErrors(cause)[0];
454
+ return new InternalError({
455
+ message: prettyError.message
456
+ });
457
+ }))), Effect.provide(context))
458
+ });
459
+ });
460
+ };
461
+ /**
462
+ * Register a resource with the McpServer.
463
+ *
464
+ * @since 1.0.0
465
+ * @category Resources
466
+ */
467
+ export const resource = function () {
468
+ if (arguments.length === 1) {
469
+ return Layer.effectDiscard(registerResource(arguments[0]));
470
+ }
471
+ const register = registerResource(...arguments);
472
+ return options => Layer.effectDiscard(register(options));
473
+ };
474
+ /**
475
+ * Register a prompt with the McpServer.
476
+ *
477
+ * @since 1.0.0
478
+ * @category Resources
479
+ */
480
+ export const registerPrompt = options => {
481
+ const args = Arr.empty();
482
+ const props = {};
483
+ const propSignatures = options.parameters ? AST.getPropertySignatures(options.parameters.ast) : [];
484
+ for (const prop of propSignatures) {
485
+ args.push(new PromptArgument({
486
+ name: prop.name,
487
+ description: Option.getOrUndefined(AST.getDescriptionAnnotation(prop)),
488
+ required: !prop.isOptional
489
+ }));
490
+ props[prop.name] = Schema.make(prop.type);
491
+ }
492
+ const prompt = new Prompt({
493
+ name: options.name,
494
+ description: options.description,
495
+ arguments: args
496
+ });
497
+ const decode = options.parameters ? Schema.decodeUnknown(options.parameters) : () => Effect.succeed({});
498
+ const completion = options.completion ?? {};
499
+ return Effect.gen(function* () {
500
+ const registry = yield* McpServer;
501
+ const context = yield* Effect.context();
502
+ const completions = {};
503
+ for (const [param, handle] of Object.entries(completion)) {
504
+ const encodeArray = Schema.encodeUnknown(Schema.Array(props[param]));
505
+ const handler = input => handle(input).pipe(Effect.flatMap(encodeArray), Effect.map(values => new CompleteResult({
506
+ completion: {
507
+ values: values,
508
+ total: values.length,
509
+ hasMore: false
510
+ }
511
+ })), Effect.catchAllCause(cause => {
512
+ const prettyError = Cause.prettyErrors(cause)[0];
513
+ return new InternalError({
514
+ message: prettyError.message
515
+ });
516
+ }), Effect.provide(context));
517
+ completions[param] = handler;
518
+ }
519
+ yield* registry.addPrompt({
520
+ prompt,
521
+ completions,
522
+ handle: params => decode(params).pipe(Effect.mapError(error => new InvalidParams({
523
+ message: error.message
524
+ })), Effect.flatMap(params => options.content(params)), Effect.map(messages => {
525
+ messages = typeof messages === "string" ? [new PromptMessage({
526
+ role: "user",
527
+ content: new TextContent({
528
+ text: messages
529
+ })
530
+ })] : messages;
531
+ return new GetPromptResult({
532
+ messages,
533
+ description: prompt.description
534
+ });
535
+ }), Effect.catchAllCause(cause => {
536
+ const prettyError = Cause.prettyErrors(cause)[0];
537
+ return new InternalError({
538
+ message: prettyError.message
539
+ });
540
+ }), Effect.provide(context))
541
+ });
542
+ });
543
+ };
544
+ /**
545
+ * Register a prompt with the McpServer.
546
+ *
547
+ * @since 1.0.0
548
+ * @category Resources
549
+ */
550
+ export const prompt = options => Layer.effectDiscard(registerPrompt(options));
551
+ // -----------------------------------------------------------------------------
552
+ // Internal
553
+ // -----------------------------------------------------------------------------
554
+ const makeUriMatcher = () => {
555
+ const router = FindMyWay.make({
556
+ ignoreTrailingSlash: true,
557
+ ignoreDuplicateSlashes: true,
558
+ caseSensitive: true
559
+ });
560
+ const add = (uri, value) => {
561
+ router.on("GET", uri, value);
562
+ };
563
+ const find = uri => router.find("GET", uri);
564
+ return {
565
+ add,
566
+ find
567
+ };
568
+ };
569
+ const compileUriTemplate = (segments, ...schemas) => {
570
+ let routerPath = segments[0].replace(":", "::");
571
+ let uriPath = segments[0];
572
+ const params = {};
573
+ let pathSchema = Schema.Tuple();
574
+ if (schemas.length > 0) {
575
+ const arr = [];
576
+ for (let i = 0; i < schemas.length; i++) {
577
+ const schema = schemas[i];
578
+ const key = String(i);
579
+ arr.push(schema);
580
+ routerPath += `:${key}${segments[i + 1].replace(":", "::")}`;
581
+ const paramName = AST.getAnnotation(ParamAnnotation)(schema.ast).pipe(Option.getOrElse(() => `param${key}`));
582
+ params[paramName] = schema;
583
+ uriPath += `{${paramName}}`;
584
+ }
585
+ pathSchema = Schema.Tuple(...arr);
586
+ }
587
+ return {
588
+ routerPath,
589
+ uriPath,
590
+ schema: pathSchema,
591
+ params
592
+ };
593
+ };
594
+ const layerHandlers = serverInfo => ClientRpcs.toLayer(Effect.gen(function* () {
595
+ const server = yield* McpServer;
596
+ return {
597
+ // Requests
598
+ ping: () => Effect.succeed({}),
599
+ initialize(params) {
600
+ const requestedVersion = params.protocolVersion;
601
+ const capabilities = {
602
+ completions: {}
603
+ };
604
+ if (server.tools.length > 0) {
605
+ capabilities.tools = {
606
+ listChanged: true
607
+ };
608
+ }
609
+ if (server.resources.length > 0 || server.resourceTemplates.length > 0) {
610
+ capabilities.resources = {
611
+ listChanged: true,
612
+ subscribe: false
613
+ };
614
+ }
615
+ if (server.prompts.length > 0) {
616
+ capabilities.prompts = {
617
+ listChanged: true
618
+ };
619
+ }
620
+ return Effect.succeed({
621
+ capabilities,
622
+ serverInfo,
623
+ protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION
624
+ });
625
+ },
626
+ "completion/complete": server.completion,
627
+ "logging/setLevel": () => InternalError.notImplemented,
628
+ "prompts/get": server.getPromptResult,
629
+ "prompts/list": () => Effect.sync(() => new ListPromptsResult({
630
+ prompts: server.prompts
631
+ })),
632
+ "resources/list": () => Effect.sync(() => new ListResourcesResult({
633
+ resources: server.resources
634
+ })),
635
+ "resources/read": ({
636
+ uri
637
+ }) => server.findResource(uri),
638
+ "resources/subscribe": () => InternalError.notImplemented,
639
+ "resources/unsubscribe": () => InternalError.notImplemented,
640
+ "resources/templates/list": () => Effect.sync(() => new ListResourceTemplatesResult({
641
+ resourceTemplates: server.resourceTemplates
642
+ })),
643
+ "tools/call": server.callTool,
644
+ "tools/list": () => Effect.sync(() => new ListToolsResult({
645
+ tools: server.tools
646
+ })),
647
+ // Notifications
648
+ "notifications/cancelled": _ => Effect.void,
649
+ "notifications/initialized": _ => Effect.void,
650
+ "notifications/progress": _ => Effect.void,
651
+ "notifications/roots/list_changed": _ => Effect.void
652
+ };
653
+ }));
654
+ const makeJsonSchema = ast => {
655
+ const props = AST.getPropertySignatures(ast);
656
+ if (props.length === 0) {
657
+ return {
658
+ type: "object",
659
+ properties: {},
660
+ required: [],
661
+ additionalProperties: false
662
+ };
663
+ }
664
+ const $defs = {};
665
+ const schema = JsonSchema.fromAST(ast, {
666
+ definitions: $defs,
667
+ topLevelReferenceStrategy: "skip"
668
+ });
669
+ if (Object.keys($defs).length === 0) return schema;
670
+ schema.$defs = $defs;
671
+ return schema;
672
+ };
673
+ const resolveResourceContent = (uri, content) => {
674
+ if (typeof content === "string") {
675
+ return new ReadResourceResult({
676
+ contents: [new TextResourceContents({
677
+ uri,
678
+ text: content
679
+ })]
680
+ });
681
+ } else if (content instanceof Uint8Array) {
682
+ return new ReadResourceResult({
683
+ contents: [new BlobResourceContents({
684
+ uri,
685
+ blob: content
686
+ })]
687
+ });
688
+ }
689
+ return content;
690
+ };
691
+ //# sourceMappingURL=McpServer.js.map