apps 0.0.1-beta.84 → 0.0.1-beta.86

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,714 @@
1
+ import {
2
+ prepareElicitation
3
+ } from "./chunk-CJ5F6UDG.js";
4
+ import {
5
+ failOnNetworkRefusal
6
+ } from "./chunk-I6Q7ZPKJ.js";
7
+ import {
8
+ accountProviderError,
9
+ bodyUpstreamError,
10
+ fromPromise,
11
+ httpProviderError,
12
+ providerErrorDetail
13
+ } from "./chunk-W6XCQAXP.js";
14
+ import {
15
+ ElicitationFailed,
16
+ McpCredentialsUnverified,
17
+ McpError,
18
+ McpHealthInput,
19
+ McpServerHeader,
20
+ McpToolMetadata,
21
+ McpToolResult,
22
+ McpToolsOptions,
23
+ RouterIcon,
24
+ ToolResultObservation,
25
+ defaultElicitationLimits,
26
+ defaultMcpClientLimits,
27
+ mcpHealthReserveMs,
28
+ mcpSdkTimerCeilingMs
29
+ } from "./chunk-WUPLD4VN.js";
30
+ import {
31
+ NetworkRefused,
32
+ ProviderError
33
+ } from "./chunk-Q4IZAUIF.js";
34
+ import {
35
+ jsonSchemaDecoder,
36
+ nestJsonSchema,
37
+ preparedJsonSchemaDecoder
38
+ } from "./chunk-3FHDX4KB.js";
39
+ import {
40
+ captureTelemetry,
41
+ owned
42
+ } from "./chunk-QWQM3CLH.js";
43
+ import {
44
+ FetchHttpClient_exports
45
+ } from "./chunk-PD6I2SDG.js";
46
+ import {
47
+ JsonObject
48
+ } from "./chunk-H2CJWXA3.js";
49
+ import {
50
+ HttpClientRequest_exports,
51
+ HttpClient_exports
52
+ } from "./chunk-EOPQ727L.js";
53
+ import {
54
+ Clock_exports,
55
+ Deferred_exports,
56
+ Effect_exports,
57
+ Exit_exports,
58
+ Match_exports,
59
+ Option_exports,
60
+ Predicate_exports,
61
+ Queue_exports,
62
+ Redacted_exports,
63
+ Ref_exports,
64
+ Schema_exports,
65
+ Stream_exports
66
+ } from "./chunk-IFJU5SYQ.js";
67
+
68
+ // src/implementation/mcp.ts
69
+ import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
70
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
71
+ import {
72
+ ErrorCode as ErrorCode3,
73
+ McpError as ProtocolError3,
74
+ ToolListChangedNotificationSchema
75
+ } from "@modelcontextprotocol/sdk/types.js";
76
+ import { SSEClientTransport, SseError } from "@modelcontextprotocol/sdk/client/sse.js";
77
+ import {
78
+ StreamableHTTPClientTransport,
79
+ StreamableHTTPError
80
+ } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
81
+
82
+ // src/implementation/mcp-tools.ts
83
+ var resultFields = {
84
+ content: { type: "array", items: { type: "object" } },
85
+ structuredContent: { type: "object" },
86
+ isError: { type: "boolean" },
87
+ _meta: { type: "object" }
88
+ };
89
+ var definitionReference = /^#\/(?:\$defs|definitions)\/[^/]+$/;
90
+ var locating = /* @__PURE__ */ new Set(["$id", "$anchor", "$dynamicAnchor", "$recursiveAnchor"]);
91
+ var onlyDefinitionReferences = (value) => Array.isArray(value) ? value.every(onlyDefinitionReferences) : value === null || typeof value !== "object" ? true : Object.entries(value).every(
92
+ ([key, item]) => locating.has(key) || key === "$dynamicRef" || key === "$recursiveRef" ? false : key === "$ref" ? typeof item === "string" && definitionReference.test(item) : onlyDefinitionReferences(item)
93
+ );
94
+ var placed = (structuredContent, pointer) => {
95
+ if (onlyDefinitionReferences(structuredContent)) {
96
+ const { $defs, definitions, $schema: _draft, ...schema } = structuredContent;
97
+ return {
98
+ schema,
99
+ root: {
100
+ ...$defs === void 0 ? {} : { $defs },
101
+ ...definitions === void 0 ? {} : { definitions }
102
+ }
103
+ };
104
+ }
105
+ try {
106
+ return { schema: nestJsonSchema(structuredContent, pointer), root: {} };
107
+ } catch {
108
+ return { schema: structuredContent, root: {} };
109
+ }
110
+ };
111
+ var resultSchema = (structuredContent) => {
112
+ if (structuredContent === void 0)
113
+ return { type: "object", properties: resultFields, required: ["content"] };
114
+ const { schema, root } = placed(structuredContent, "#/anyOf/0/properties/structuredContent");
115
+ return {
116
+ // The envelope's keywords mean the same in every draft, so the server's draft applies.
117
+ ...structuredContent.$schema === void 0 ? {} : { $schema: structuredContent.$schema },
118
+ ...root,
119
+ anyOf: [
120
+ {
121
+ type: "object",
122
+ properties: { ...resultFields, structuredContent: schema, isError: { const: false } },
123
+ required: ["content", "structuredContent"]
124
+ },
125
+ {
126
+ type: "object",
127
+ properties: { ...resultFields, isError: { const: true } },
128
+ required: ["content", "isError"]
129
+ }
130
+ ]
131
+ };
132
+ };
133
+ var adaptMcpTool = (client, tool) => Effect_exports.gen(function* () {
134
+ const decoder = yield* jsonSchemaDecoder(tool.inputSchema).pipe(
135
+ Effect_exports.mapError(() => new McpError({ phase: "schema", reason: "invalid_response" }))
136
+ );
137
+ const output = preparedJsonSchemaDecoder(resultSchema(tool.outputSchema));
138
+ const { outputSchema: _upstream, ...metadata } = tool;
139
+ const adapted = {
140
+ ...metadata,
141
+ description: tool.description ?? tool.title ?? tool.name,
142
+ input: decoder,
143
+ output: output.decoder,
144
+ ...tool.annotations?.readOnlyHint === void 0 ? {} : { readOnly: tool.annotations.readOnlyHint },
145
+ run: (context, input) => Effect_exports.gen(function* () {
146
+ const arguments_ = yield* Schema_exports.decodeUnknownEffect(decoder)(input).pipe(
147
+ Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonObject)),
148
+ Effect_exports.mapError(() => new McpError({ phase: "call", reason: "invalid_input" }))
149
+ );
150
+ yield* output.compile.pipe(
151
+ Effect_exports.mapError(() => new McpError({ phase: "schema", reason: "invalid_response" }))
152
+ );
153
+ const result = yield* client.call(tool.name, arguments_, context);
154
+ if (result.isError === true) {
155
+ (yield* ToolResultObservation).failed();
156
+ yield* Effect_exports.annotateCurrentSpan({
157
+ "executor.outcome": "failed",
158
+ "error.type": "McpToolError"
159
+ });
160
+ }
161
+ return result;
162
+ })
163
+ };
164
+ return adapted;
165
+ });
166
+ var adaptMcpTools = (client) => Effect_exports.gen(function* () {
167
+ const { tools: metadata } = yield* client.list;
168
+ const entries = yield* Effect_exports.forEach(
169
+ metadata,
170
+ (tool) => adaptMcpTool(client, tool).pipe(Effect_exports.map((adapted) => [tool.name, adapted]))
171
+ );
172
+ return Object.fromEntries(entries);
173
+ });
174
+
175
+ // src/implementation/mcp-client.ts
176
+ import {
177
+ ErrorCode as ErrorCode2,
178
+ ListToolsResultSchema,
179
+ McpError as ProtocolError2
180
+ } from "@modelcontextprotocol/sdk/types.js";
181
+
182
+ // src/implementation/mcp-call.ts
183
+ import {
184
+ ElicitRequestSchema,
185
+ ElicitResultSchema,
186
+ ErrorCode,
187
+ McpError as ProtocolError
188
+ } from "@modelcontextprotocol/sdk/types.js";
189
+ function callBudget(timeoutMs) {
190
+ return Effect_exports.gen(function* () {
191
+ const clock = yield* Clock_exports.Clock;
192
+ const changes = yield* Queue_exports.unbounded();
193
+ let remaining = timeoutMs, updated = clock.currentTimeMillisUnsafe(), waiting = 0;
194
+ const account = () => {
195
+ const now = clock.currentTimeMillisUnsafe();
196
+ if (waiting === 0) remaining -= Math.max(0, now - updated);
197
+ updated = now;
198
+ return remaining;
199
+ };
200
+ return {
201
+ waitForInput: (input) => Effect_exports.acquireUseRelease(
202
+ Effect_exports.sync(() => {
203
+ account();
204
+ waiting++;
205
+ }).pipe(Effect_exports.andThen(Queue_exports.offer(changes, void 0))),
206
+ () => input,
207
+ () => Effect_exports.sync(() => {
208
+ account();
209
+ waiting--;
210
+ }).pipe(Effect_exports.andThen(Queue_exports.offer(changes, void 0)))
211
+ ),
212
+ expired: Effect_exports.gen(function* () {
213
+ while (true) {
214
+ const left = account();
215
+ if (waiting === 0 && left <= 0)
216
+ return yield* new McpError({ phase: "call", reason: "timeout" });
217
+ if (waiting > 0) yield* Queue_exports.take(changes);
218
+ else yield* Queue_exports.take(changes).pipe(Effect_exports.raceFirst(Effect_exports.sleep(Math.max(1, left))));
219
+ }
220
+ })
221
+ };
222
+ });
223
+ }
224
+ var mcpCall = (client, name, input, context, timeoutMs, failure2) => Effect_exports.scoped(
225
+ Effect_exports.gen(function* () {
226
+ const budget = yield* callBudget(timeoutMs);
227
+ const failed = yield* Deferred_exports.make();
228
+ const runtime = yield* Effect_exports.context();
229
+ const callbacks = /* @__PURE__ */ new Set();
230
+ const lifetime = yield* Effect_exports.acquireRelease(
231
+ Effect_exports.sync(() => new AbortController()),
232
+ (controller) => (
233
+ // oxlint-disable-next-line executor/authored-code-through-adapter -- the MCP SDK's elicitation callbacks
234
+ Effect_exports.promise(async () => {
235
+ controller.abort();
236
+ client.removeRequestHandler("elicitation/create");
237
+ await Promise.allSettled(callbacks);
238
+ })
239
+ )
240
+ );
241
+ client.setRequestHandler(ElicitRequestSchema, (request, extra) => {
242
+ const signal = AbortSignal.any([lifetime.signal, extra.signal]);
243
+ const interaction = Effect_exports.gen(function* () {
244
+ const form = yield* prepareElicitation({
245
+ ...request.params,
246
+ mode: request.params.mode ?? "form"
247
+ });
248
+ const deliver = context.elicit;
249
+ if (deliver === void 0) return yield* new ElicitationFailed({ reason: "unavailable" });
250
+ const answer = yield* budget.waitForInput(
251
+ fromPromise(
252
+ deliver,
253
+ "elicitation"
254
+ )(form.request).pipe(
255
+ Effect_exports.mapError(
256
+ (error) => Option_exports.getOrElse(
257
+ Schema_exports.decodeUnknownOption(ElicitationFailed)(error),
258
+ () => new ElicitationFailed({ reason: "transport" })
259
+ )
260
+ ),
261
+ Effect_exports.timeoutOrElse({
262
+ duration: defaultElicitationLimits.timeoutMs,
263
+ orElse: () => Effect_exports.fail(new ElicitationFailed({ reason: "expired" }))
264
+ })
265
+ )
266
+ ).pipe(Effect_exports.withSpan("provider.mcp.elicitation"));
267
+ return yield* form.respond(answer).pipe(
268
+ Effect_exports.flatMap(
269
+ (response) => Effect_exports.try({
270
+ try: () => ElicitResultSchema.parse(response),
271
+ catch: () => new ElicitationFailed({ reason: "invalid-response" })
272
+ })
273
+ )
274
+ );
275
+ }).pipe(Effect_exports.tapError((error) => Deferred_exports.fail(failed, error)));
276
+ const callback = Effect_exports.runPromiseWith(runtime)(interaction, { signal }).catch(() => {
277
+ if (!lifetime.signal.aborted)
278
+ Effect_exports.runSync(Deferred_exports.fail(failed, new ElicitationFailed({ reason: "transport" })));
279
+ throw new ProtocolError(ErrorCode.InternalError, "User input could not be delivered");
280
+ });
281
+ callbacks.add(callback);
282
+ return callback.finally(() => callbacks.delete(callback));
283
+ });
284
+ return yield* Effect_exports.tryPromise({
285
+ try: (signal) => client.callTool({ name, arguments: input }, void 0, {
286
+ signal: AbortSignal.any([signal, lifetime.signal]),
287
+ timeout: mcpSdkTimerCeilingMs
288
+ }),
289
+ catch: (error) => failure2("call", error)
290
+ }).pipe(
291
+ Effect_exports.raceFirst(Deferred_exports.await(failed)),
292
+ Effect_exports.raceFirst(budget.expired),
293
+ Effect_exports.flatMap(
294
+ (result) => Schema_exports.decodeUnknownEffect(McpToolResult)(result).pipe(
295
+ Effect_exports.mapError(() => new McpError({ phase: "call", reason: "invalid_response" }))
296
+ )
297
+ ),
298
+ Effect_exports.tap(
299
+ (result) => Effect_exports.annotateCurrentSpan("mcp.tool.is_error", result.isError === true)
300
+ )
301
+ );
302
+ })
303
+ ).pipe(
304
+ owned("upstream", "provider.mcp.request", {
305
+ kind: "client",
306
+ attributes: {
307
+ "rpc.system.name": "jsonrpc",
308
+ "rpc.method": "tools/call",
309
+ "mcp.tool.name": name
310
+ }
311
+ })
312
+ );
313
+
314
+ // src/implementation/mcp-client.ts
315
+ var clientCodes = /* @__PURE__ */ new Set([
316
+ ErrorCode2.RequestTimeout,
317
+ ErrorCode2.ConnectionClosed
318
+ ]);
319
+ var answeredError = (error) => {
320
+ if (!(error instanceof ProtocolError2) || clientCodes.has(error.code)) return void 0;
321
+ const prefix = `MCP error ${error.code}: `;
322
+ return bodyUpstreamError({
323
+ error: {
324
+ code: error.code,
325
+ message: error.message.startsWith(prefix) ? error.message.slice(prefix.length) : error.message
326
+ }
327
+ });
328
+ };
329
+ var mcpJsonSchemaValidator = {
330
+ getValidator(document) {
331
+ const decoder = Effect_exports.runSync(jsonSchemaDecoder(document));
332
+ return (input) => {
333
+ const result = Effect_exports.runSyncExit(Schema_exports.decodeUnknownEffect(decoder)(input));
334
+ if (Exit_exports.isFailure(result))
335
+ return {
336
+ valid: false,
337
+ data: void 0,
338
+ errorMessage: "Value does not match the supported JSON Schema"
339
+ };
340
+ return { valid: true, data: result.value, errorMessage: void 0 };
341
+ };
342
+ }
343
+ };
344
+ function mcpClient(withClient2, timeoutMs, failure2) {
345
+ const list = withClient2(
346
+ "discover",
347
+ (client) => Effect_exports.gen(function* () {
348
+ const tools = /* @__PURE__ */ new Map();
349
+ const cursors = /* @__PURE__ */ new Set();
350
+ let cursor;
351
+ do {
352
+ const page = yield* Effect_exports.tryPromise({
353
+ // This session only reads metadata. listTools() also eagerly compiles
354
+ // every output validator; mcpRouter validates the selected tool on call.
355
+ try: (signal) => client.request(
356
+ { method: "tools/list", params: cursor === void 0 ? {} : { cursor } },
357
+ ListToolsResultSchema,
358
+ { signal, timeout: timeoutMs }
359
+ ),
360
+ catch: (error) => failure2("discover", error)
361
+ }).pipe(
362
+ owned("upstream", "provider.mcp.request", {
363
+ kind: "client",
364
+ attributes: { "rpc.system.name": "jsonrpc", "rpc.method": "tools/list" }
365
+ })
366
+ );
367
+ const metadata = yield* Schema_exports.decodeUnknownEffect(Schema_exports.Array(McpToolMetadata))(
368
+ page.tools
369
+ ).pipe(
370
+ Effect_exports.mapError(() => new McpError({ phase: "discover", reason: "invalid_response" }))
371
+ );
372
+ for (const tool of metadata) {
373
+ if (!tool.name || tools.has(tool.name) || tools.size >= defaultMcpClientLimits.maxTools)
374
+ return yield* new McpError({ phase: "discover", reason: "invalid_response" });
375
+ tools.set(tool.name, tool);
376
+ }
377
+ cursor = page.nextCursor;
378
+ if (cursor !== void 0) {
379
+ if (cursors.has(cursor) || cursors.size >= defaultMcpClientLimits.maxPaginationCursors)
380
+ return yield* new McpError({ phase: "discover", reason: "invalid_response" });
381
+ cursors.add(cursor);
382
+ }
383
+ } while (cursor !== void 0);
384
+ const info = client.getServerVersion();
385
+ const instructions = client.getInstructions();
386
+ const icons = info?.icons?.flatMap(
387
+ (icon) => Option_exports.toArray(Schema_exports.decodeUnknownOption(RouterIcon)(icon))
388
+ );
389
+ const server = Schema_exports.decodeUnknownOption(McpServerHeader)({
390
+ ...info?.name === void 0 ? {} : { name: info.name },
391
+ ...info?.title === void 0 ? {} : { title: info.title },
392
+ ...info?.version === void 0 ? {} : { version: info.version },
393
+ ...info?.description === void 0 ? {} : { description: info.description },
394
+ ...info?.websiteUrl === void 0 ? {} : { websiteUrl: info.websiteUrl },
395
+ ...icons === void 0 ? {} : { icons },
396
+ ...instructions === void 0 ? {} : { instructions }
397
+ });
398
+ return {
399
+ tools: [...tools.values()],
400
+ server: Option_exports.getOrElse(server, () => ({}))
401
+ };
402
+ })
403
+ ).pipe(owned("upstream", "provider.mcp.discover"));
404
+ const check = withClient2(
405
+ "discover",
406
+ (client) => (
407
+ // oxlint-disable-next-line executor/authored-code-through-adapter -- MCP SDK
408
+ Effect_exports.tryPromise({
409
+ try: (signal) => client.request({ method: "tools/list", params: {} }, ListToolsResultSchema, {
410
+ signal,
411
+ timeout: timeoutMs
412
+ }),
413
+ catch: (error) => failure2("discover", error)
414
+ }).pipe(
415
+ Effect_exports.asVoid,
416
+ owned("upstream", "provider.mcp.request", {
417
+ kind: "client",
418
+ attributes: { "rpc.system.name": "jsonrpc", "rpc.method": "tools/list" }
419
+ })
420
+ )
421
+ )
422
+ ).pipe(owned("upstream", "provider.mcp.check"));
423
+ const call = (name, input, context) => withClient2("call", (client) => mcpCall(client, name, input, context, timeoutMs, failure2)).pipe(
424
+ owned("upstream", "provider.mcp.call", { attributes: { "mcp.tool.name": name } })
425
+ );
426
+ return { list, check, call };
427
+ }
428
+
429
+ // src/implementation/mcp.ts
430
+ var unreadable = (error) => error instanceof SyntaxError || error instanceof Error && Predicate_exports.hasProperty(error, "issues") && Array.isArray(error.issues);
431
+ var failure = (phase, error) => {
432
+ if (Schema_exports.is(ProviderError)(error) || Schema_exports.is(McpError)(error) || Schema_exports.is(NetworkRefused)(error))
433
+ return error;
434
+ const code = error instanceof UnauthorizedError ? 401 : error instanceof StreamableHTTPError || error instanceof SseError ? error.code : void 0;
435
+ if (code !== void 0 && code >= 300 && code <= 599)
436
+ return httpProviderError(code) ?? new McpError({ phase, reason: "request", status: code });
437
+ const upstream = answeredError(error);
438
+ return new McpError({
439
+ phase,
440
+ reason: code !== void 0 || unreadable(error) ? "invalid_response" : error instanceof ProtocolError3 && error.code === ErrorCode3.RequestTimeout ? "timeout" : "request",
441
+ ...upstream === void 0 ? {} : { upstream }
442
+ });
443
+ };
444
+ var errorBodyLimits = { maxBytes: 65536, readTimeoutMs: 2e3 };
445
+ var errorBody = (response) => Effect_exports.gen(function* () {
446
+ const json = /^application\/(?:[\w.-]+\+)?json$/i.test(
447
+ response.headers["content-type"]?.split(";")[0]?.trim() ?? ""
448
+ );
449
+ const length = response.headers["content-length"];
450
+ if (!json || length !== void 0 && Number(length) > errorBodyLimits.maxBytes)
451
+ return { body: Stream_exports.toReadableStream(response.stream), upstream: void 0 };
452
+ const unreadable2 = new McpError({ phase: "transport", reason: "invalid_response" });
453
+ const read = yield* response.stream.pipe(
454
+ Stream_exports.mapError(() => unreadable2),
455
+ Stream_exports.limitBytes(errorBodyLimits.maxBytes, () => Stream_exports.fail(unreadable2)),
456
+ Stream_exports.decodeText,
457
+ Stream_exports.mkString,
458
+ Effect_exports.timeout(errorBodyLimits.readTimeoutMs),
459
+ Effect_exports.option
460
+ );
461
+ if (Option_exports.isNone(read)) return { body: null, upstream: void 0 };
462
+ const parsed = Schema_exports.decodeUnknownOption(Schema_exports.fromJsonString(Schema_exports.Json))(read.value);
463
+ return {
464
+ body: read.value,
465
+ upstream: Option_exports.isSome(parsed) ? bodyUpstreamError(parsed.value) : void 0
466
+ };
467
+ });
468
+ var transportFetch = (connection, telemetry, rejected, answered) => (url, init) => Effect_exports.runPromiseWith(telemetry.context)(
469
+ Effect_exports.gen(function* () {
470
+ const target = yield* Effect_exports.try({
471
+ try: () => new URL(url),
472
+ catch: () => new McpError({ phase: "transport", reason: "invalid_response" })
473
+ });
474
+ if (connection.url.username || connection.url.password)
475
+ return yield* new McpError({ phase: "transport", reason: "invalid_input" });
476
+ if (target.origin !== connection.url.origin || target.username || target.password)
477
+ return yield* new McpError({ phase: "transport", reason: "invalid_response" });
478
+ const request = yield* Effect_exports.try({
479
+ try: () => {
480
+ const headers = new Headers(Redacted_exports.value(connection.headers));
481
+ new Headers(init?.headers).forEach((value, name) => headers.set(name, value));
482
+ if (!headers.has("user-agent")) headers.set("user-agent", "Executor");
483
+ return HttpClientRequest_exports.fromWeb(new Request(target, { ...init, headers }));
484
+ },
485
+ // The provider's headers cannot form an HTTP request, such as a name with a space.
486
+ catch: () => new McpError({ phase: "transport", reason: "invalid_input" })
487
+ });
488
+ const client = yield* HttpClient_exports.HttpClient;
489
+ const response = yield* client.execute(request);
490
+ yield* failOnNetworkRefusal(response).pipe(
491
+ Effect_exports.tapError((refused) => Deferred_exports.fail(rejected, refused))
492
+ );
493
+ if (response.status < 400)
494
+ return new Response(
495
+ [204, 205, 304].includes(response.status) ? null : Stream_exports.toReadableStream(response.stream),
496
+ {
497
+ status: response.status,
498
+ headers: response.headers
499
+ }
500
+ );
501
+ const { body, upstream } = yield* errorBody(response);
502
+ const provider = httpProviderError(response.status, response.headers);
503
+ if (provider !== void 0) {
504
+ const refused = providerErrorDetail(provider, { upstream });
505
+ yield* Deferred_exports.fail(rejected, refused);
506
+ return yield* refused;
507
+ }
508
+ const session = new Headers(init?.headers).has("mcp-session-id");
509
+ yield* Ref_exports.update(
510
+ answered,
511
+ (responses) => new Map(responses).set(response.status, { upstream, session })
512
+ );
513
+ return new Response(body, { status: response.status, headers: response.headers });
514
+ }).pipe(
515
+ Effect_exports.provide(FetchHttpClient_exports.layer),
516
+ Effect_exports.provideService(FetchHttpClient_exports.RequestInit, { redirect: "manual" }),
517
+ Effect_exports.provideService(
518
+ FetchHttpClient_exports.Fetch,
519
+ (url2, options) => globalThis.fetch(url2, {
520
+ ...options,
521
+ signal: AbortSignal.any([
522
+ ...options?.signal ? [options.signal] : [],
523
+ ...init?.signal ? [init.signal] : []
524
+ ])
525
+ })
526
+ ),
527
+ Effect_exports.mapError((error) => failure("transport", error))
528
+ ),
529
+ init?.signal ? { signal: init.signal } : {}
530
+ );
531
+ var explain = (error, phase, responses) => {
532
+ if (Schema_exports.is(ProviderError)(error)) return providerErrorDetail(error, { phase });
533
+ if (phase === "call" && Schema_exports.is(McpError)(error) && error.phase === "transport" && error.reason === "request")
534
+ return new McpError({
535
+ phase,
536
+ reason: error.reason,
537
+ ...error.status === void 0 ? {} : { status: error.status },
538
+ ...error.upstream === void 0 ? {} : { upstream: error.upstream }
539
+ });
540
+ if (!Schema_exports.is(McpError)(error) || error.status === void 0) return error;
541
+ const response = responses.get(error.status);
542
+ if (response === void 0 || (error.upstream !== void 0 || response.upstream === void 0) && !response.session)
543
+ return error;
544
+ const upstream = error.upstream ?? response.upstream;
545
+ return new McpError({
546
+ phase: error.phase,
547
+ reason: error.reason,
548
+ status: error.status,
549
+ ...upstream === void 0 ? {} : { upstream },
550
+ ...response.session ? { session: true } : {}
551
+ });
552
+ };
553
+ var explained = (phase, answered) => (error) => Ref_exports.get(answered).pipe(
554
+ Effect_exports.flatMap((responses) => Effect_exports.fail(explain(error, phase, responses)))
555
+ );
556
+ function withClient(connection, mode, use, changed) {
557
+ const attempt = (kind) => Effect_exports.scoped(
558
+ Effect_exports.gen(function* () {
559
+ const telemetry = yield* captureTelemetry;
560
+ const rejected = yield* Deferred_exports.make();
561
+ const answered = yield* Ref_exports.make(/* @__PURE__ */ new Map());
562
+ const pending = /* @__PURE__ */ new Set();
563
+ const { client, transport } = yield* Effect_exports.acquireRelease(
564
+ Effect_exports.sync(() => {
565
+ const fetch = transportFetch(connection, telemetry, rejected, answered);
566
+ const client2 = new Client(
567
+ { name: "executor-apps", version: "0.1.0" },
568
+ {
569
+ jsonSchemaValidator: mcpJsonSchemaValidator,
570
+ capabilities: mode === "call" ? { elicitation: { form: {} } } : {}
571
+ }
572
+ );
573
+ if (changed !== void 0) {
574
+ client2.setNotificationHandler(ToolListChangedNotificationSchema, () => {
575
+ const task = Effect_exports.runPromiseWith(telemetry.context)(
576
+ changed.pipe(
577
+ Effect_exports.timeout("5 seconds"),
578
+ Effect_exports.catchCause(() => Effect_exports.logWarning("MCP catalog invalidation failed"))
579
+ )
580
+ );
581
+ pending.add(task);
582
+ return task.finally(() => pending.delete(task));
583
+ });
584
+ }
585
+ return {
586
+ client: client2,
587
+ transport: kind === "http" ? new StreamableHTTPClientTransport(connection.url, {
588
+ fetch,
589
+ reconnectionOptions: {
590
+ maxRetries: 0,
591
+ initialReconnectionDelay: 1e3,
592
+ maxReconnectionDelay: 1e3,
593
+ reconnectionDelayGrowFactor: 1
594
+ }
595
+ }) : new SSEClientTransport(connection.url, { fetch })
596
+ };
597
+ }),
598
+ ({ client: client2, transport: transport2 }) => Effect_exports.gen(function* () {
599
+ client2.removeNotificationHandler("notifications/tools/list_changed");
600
+ yield* Effect_exports.promise(async () => {
601
+ await Promise.allSettled(pending);
602
+ });
603
+ if (transport2 instanceof StreamableHTTPClientTransport && transport2.sessionId !== void 0) {
604
+ yield* Effect_exports.tryPromise(() => transport2.terminateSession()).pipe(
605
+ Effect_exports.timeout(defaultMcpClientLimits.cleanupTimeoutMs),
606
+ Effect_exports.ignore
607
+ );
608
+ }
609
+ yield* Effect_exports.tryPromise(() => client2.close()).pipe(
610
+ Effect_exports.timeout(defaultMcpClientLimits.cleanupTimeoutMs),
611
+ Effect_exports.ignore
612
+ );
613
+ }).pipe(owned("upstream", "provider.mcp.close"))
614
+ );
615
+ const wire = transport;
616
+ yield* Effect_exports.tryPromise({
617
+ try: (signal) => client.connect(wire, { signal, timeout: connection.timeoutMs }),
618
+ catch: (error) => failure("connect", error)
619
+ }).pipe(
620
+ Effect_exports.raceFirst(Deferred_exports.await(rejected)),
621
+ Effect_exports.catch(explained("connect", answered)),
622
+ Effect_exports.timeout(connection.timeoutMs),
623
+ owned("upstream", "provider.mcp.connect")
624
+ );
625
+ return yield* use(client).pipe(
626
+ Effect_exports.raceFirst(Deferred_exports.await(rejected)),
627
+ Effect_exports.catch(explained(mode, answered))
628
+ );
629
+ })
630
+ ).pipe(
631
+ owned("upstream", "provider.mcp.session", {
632
+ attributes: {
633
+ "mcp.transport": kind,
634
+ "mcp.operation": mode,
635
+ "server.address": connection.url.hostname
636
+ }
637
+ })
638
+ );
639
+ return attempt("http").pipe(
640
+ // Fallback is only for negotiation. Tool calls are never automatically replayed.
641
+ Effect_exports.catch(
642
+ (error) => Schema_exports.is(McpError)(error) && error.phase === "connect" && (error.status === 404 || error.status === 405) ? attempt("sse") : Effect_exports.fail(error)
643
+ ),
644
+ (operation) => mode === "discover" ? operation.pipe(Effect_exports.timeout(connection.timeoutMs)) : operation,
645
+ Effect_exports.catchTag(
646
+ "TimeoutError",
647
+ () => Effect_exports.fail(new McpError({ phase: "transport", reason: "timeout" }))
648
+ )
649
+ );
650
+ }
651
+ var mcpClientEffect = (input, changed) => Effect_exports.gen(function* () {
652
+ const options = yield* Schema_exports.decodeUnknownEffect(McpToolsOptions)(input).pipe(
653
+ Effect_exports.mapError(() => new McpError({ phase: "connect", reason: "invalid_input" }))
654
+ );
655
+ const url = new URL(options.url);
656
+ if (url.username || url.password || url.hash)
657
+ return yield* new McpError({ phase: "connect", reason: "invalid_input" });
658
+ const connection = {
659
+ url,
660
+ headers: Redacted_exports.make({ ...options.headers }),
661
+ timeoutMs: options.timeoutMs ?? defaultMcpClientLimits.timeoutMs
662
+ };
663
+ return mcpClient(
664
+ (mode, use) => withClient(connection, mode, use, changed).pipe(
665
+ Effect_exports.mapError(
666
+ (error) => options.accountId === void 0 ? error : accountProviderError(error, options.accountId)
667
+ )
668
+ ),
669
+ connection.timeoutMs,
670
+ failure
671
+ );
672
+ });
673
+ var refusesCredentials = (error) => Schema_exports.is(ProviderError)(error) && Match_exports.value(error.reason).pipe(
674
+ Match_exports.whenOr("unauthorized", "forbidden", "rejected", () => true),
675
+ Match_exports.whenOr("rate_limited", "unavailable", () => false),
676
+ Match_exports.exhaustive
677
+ );
678
+ var mcpHealthEffect = (check, options) => Effect_exports.gen(function* () {
679
+ const { deadline, timeoutMs, ...parsed } = yield* Schema_exports.decodeUnknownEffect(McpHealthInput)({
680
+ ...options,
681
+ accountId: check.account.id,
682
+ signal: check.signal,
683
+ deadline: check.deadline
684
+ }).pipe(Effect_exports.mapError(() => new McpError({ phase: "connect", reason: "invalid_input" })));
685
+ const end = Math.min(
686
+ (yield* Clock_exports.currentTimeMillis) + (timeoutMs ?? defaultMcpClientLimits.timeoutMs),
687
+ deadline === void 0 ? Number.POSITIVE_INFINITY : deadline - mcpHealthReserveMs
688
+ );
689
+ const remaining = Effect_exports.flatMap(
690
+ Clock_exports.currentTimeMillis,
691
+ (now) => end - now >= 1 ? Effect_exports.succeed(Math.floor(end - now)) : Effect_exports.fail(new McpError({ phase: "transport", reason: "timeout" }))
692
+ );
693
+ yield* (yield* mcpClientEffect({ ...parsed, timeoutMs: yield* remaining })).check;
694
+ return yield* remaining.pipe(
695
+ Effect_exports.flatMap(
696
+ (timeoutMs2) => mcpClientEffect({ url: parsed.url, signal: parsed.signal, timeoutMs: timeoutMs2 })
697
+ ),
698
+ Effect_exports.flatMap((anonymous) => anonymous.check),
699
+ Effect_exports.matchEffect({
700
+ onSuccess: () => Effect_exports.fail(new McpCredentialsUnverified({ anonymous: "answered" })),
701
+ // Executor's network refused the attempt, so the server never answered it.
702
+ onFailure: (error) => Schema_exports.is(NetworkRefused)(error) ? Effect_exports.fail(error) : refusesCredentials(error) ? Effect_exports.void : Effect_exports.fail(new McpCredentialsUnverified({ anonymous: error }))
703
+ }),
704
+ owned("upstream", "provider.mcp.anonymous")
705
+ );
706
+ }).pipe(owned("upstream", "provider.mcp.health"));
707
+ var mcpToolsEffect = (input) => mcpClientEffect(input).pipe(Effect_exports.flatMap(adaptMcpTools));
708
+
709
+ export {
710
+ adaptMcpTool,
711
+ mcpClientEffect,
712
+ mcpHealthEffect,
713
+ mcpToolsEffect
714
+ };