@expandai/mcp-server 0.1.1 → 0.1.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.
@@ -0,0 +1,802 @@
1
+ import { Effect, Config, Schema, Layer, Context, Option, Redacted } from 'effect';
2
+ import { McpServer, Tool, Toolkit } from '@effect/ai';
3
+ import { NodeHttpClient } from '@effect/platform-node';
4
+ import { HttpClient, HttpClientRequest as HttpClientRequest$1 } from '@effect/platform';
5
+ import * as HttpClientError from '@effect/platform/HttpClientError';
6
+ import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
7
+ import * as HttpClientResponse from '@effect/platform/HttpClientResponse';
8
+ import * as Data from 'effect/Data';
9
+ import * as Effect2 from 'effect/Effect';
10
+ import * as S from 'effect/Schema';
11
+
12
+ // src/Server.ts
13
+
14
+ // package.json
15
+ var package_default = {
16
+ version: "0.1.4"};
17
+ var ExpandDocs = McpServer.resource({
18
+ uri: "expand://about",
19
+ name: "About expand.ai",
20
+ description: "Key links for the expand.ai platform",
21
+ content: Effect.succeed(`# expand.ai
22
+
23
+ - Website: https://expand.ai
24
+ - Quickstart: https://expand.ai/docs
25
+ - Dashboard: https://expand.ai/dashboard
26
+ `)
27
+ });
28
+ (class extends S.Struct({
29
+ url: S.String
30
+ }) {
31
+ });
32
+ var IssueTag = class extends S.Literal(
33
+ "Pointer",
34
+ "Unexpected",
35
+ "Missing",
36
+ "Composite",
37
+ "Refinement",
38
+ "Transformation",
39
+ "Type",
40
+ "Forbidden"
41
+ ) {
42
+ };
43
+ var PropertyKeyEnumTag = class extends S.Literal("symbol") {
44
+ };
45
+ var PropertyKey = class extends S.Union(
46
+ S.String,
47
+ S.Number,
48
+ /**
49
+ * an object to be decoded into a globally shared symbol
50
+ */
51
+ S.Struct({
52
+ _tag: PropertyKeyEnumTag,
53
+ key: S.String
54
+ })
55
+ ) {
56
+ };
57
+ var Issue = class extends S.Class("Issue")({
58
+ /**
59
+ * The tag identifying the type of parse issue
60
+ */
61
+ _tag: IssueTag,
62
+ /**
63
+ * The path to the property where the issue occurred
64
+ */
65
+ path: S.Array(PropertyKey),
66
+ /**
67
+ * A descriptive message explaining the issue
68
+ */
69
+ message: S.String
70
+ }) {
71
+ };
72
+ var HttpApiDecodeErrorTag = class extends S.Literal("HttpApiDecodeError") {
73
+ };
74
+ var HttpApiDecodeError = class extends S.Class("HttpApiDecodeError")({
75
+ issues: S.Array(Issue),
76
+ message: S.String,
77
+ _tag: HttpApiDecodeErrorTag
78
+ }) {
79
+ };
80
+ var UnauthorizedAccessTag = class extends S.Literal("UnauthorizedAccess") {
81
+ };
82
+ var UnauthorizedAccess = class extends S.Class("UnauthorizedAccess")({
83
+ _tag: UnauthorizedAccessTag
84
+ }) {
85
+ };
86
+ var AssetNotFoundTag = class extends S.Literal("AssetNotFound") {
87
+ };
88
+ var AssetNotFound = class extends S.Class("AssetNotFound")({
89
+ browserSessionId: S.optionalWith(S.String, { nullable: true }),
90
+ filename: S.String,
91
+ _tag: AssetNotFoundTag
92
+ }) {
93
+ };
94
+ var AssetNotInWaczTag = class extends S.Literal("AssetNotInWacz") {
95
+ };
96
+ var AssetNotInWacz = class extends S.Class("AssetNotInWacz")({
97
+ url: S.String,
98
+ _tag: AssetNotInWaczTag
99
+ }) {
100
+ };
101
+ var AssetsGetByUrl404 = class extends S.Union(AssetNotFound, AssetNotInWacz) {
102
+ };
103
+ var AssetReadErrorTag = class extends S.Literal("AssetReadError") {
104
+ };
105
+ var AssetReadError = class extends S.Class("AssetReadError")({
106
+ browserSessionId: S.optionalWith(S.String, { nullable: true }),
107
+ filename: S.String,
108
+ _tag: AssetReadErrorTag
109
+ }) {
110
+ };
111
+ var InvalidWaczFormatTag = class extends S.Literal("InvalidWaczFormat") {
112
+ };
113
+ var InvalidWaczFormat = class extends S.Class("InvalidWaczFormat")({
114
+ message: S.String,
115
+ _tag: InvalidWaczFormatTag
116
+ }) {
117
+ };
118
+ var AssetRetrievalErrorTag = class extends S.Literal("AssetRetrievalError") {
119
+ };
120
+ var AssetRetrievalError = class extends S.Class("AssetRetrievalError")({
121
+ browserSessionId: S.String,
122
+ url: S.String,
123
+ _tag: AssetRetrievalErrorTag
124
+ }) {
125
+ };
126
+ var AssetsGetByUrl500 = class extends S.Union(AssetReadError, InvalidWaczFormat, AssetRetrievalError) {
127
+ };
128
+ var CurlErrorInputMethod = class extends S.Literal(
129
+ "GET",
130
+ "POST",
131
+ "HEAD",
132
+ "PUT",
133
+ "DELETE",
134
+ "CONNECT",
135
+ "OPTIONS",
136
+ "TRACE",
137
+ "PATCH"
138
+ ) {
139
+ };
140
+ var CurlErrorTag = class extends S.Literal("CurlError") {
141
+ };
142
+ var CurlError = class extends S.Class("CurlError")({
143
+ status: S.Number,
144
+ input: S.Struct({
145
+ method: CurlErrorInputMethod,
146
+ url: S.String,
147
+ body: S.optionalWith(S.String, { nullable: true }),
148
+ headers: S.optionalWith(S.Struct({}), { nullable: true }),
149
+ proxy: S.optionalWith(
150
+ S.Struct({
151
+ server: S.String,
152
+ username: S.optionalWith(S.String, { nullable: true }),
153
+ password: S.optionalWith(S.String, { nullable: true })
154
+ }),
155
+ { nullable: true }
156
+ ),
157
+ timeout: S.optionalWith(S.Number, { nullable: true }),
158
+ connectTimeout: S.optionalWith(S.Number, { nullable: true })
159
+ }),
160
+ _tag: CurlErrorTag
161
+ }) {
162
+ };
163
+ var AssetListItem = class extends S.Class("AssetListItem")({
164
+ url: S.String,
165
+ mime: S.String,
166
+ status: S.Number
167
+ }) {
168
+ };
169
+ var AssetListResponse = class extends S.Class("AssetListResponse")({
170
+ browserSessionId: S.String,
171
+ assets: S.Array(AssetListItem)
172
+ }) {
173
+ };
174
+ var AssetsList500 = class extends S.Union(AssetReadError, InvalidWaczFormat) {
175
+ };
176
+ var URL = class extends S.String {
177
+ };
178
+ var SelectHtmlConfig = class extends S.Record({ key: S.String, value: S.Unknown }) {
179
+ };
180
+ var SelectMarkdownConfig = class extends S.Record({ key: S.String, value: S.Unknown }) {
181
+ };
182
+ var SelectScreenshotConfig = class extends S.Class("SelectScreenshotConfig")({
183
+ /**
184
+ * Whether to capture the full page including content below the fold
185
+ */
186
+ fullPage: S.optionalWith(S.Boolean, { nullable: true })
187
+ }) {
188
+ };
189
+ var Trimmed = class extends S.String.pipe(S.pattern(/^\S[\s\S]*\S$|^\S$|^$/)) {
190
+ };
191
+ var Int2 = class extends S.Int {
192
+ };
193
+ (class extends S.Class("FetchRequest")({
194
+ url: URL,
195
+ /**
196
+ * Specifies which content formats to include in the response
197
+ */
198
+ select: S.optionalWith(
199
+ S.Struct({
200
+ html: S.optionalWith(
201
+ S.Union(
202
+ S.Boolean,
203
+ SelectHtmlConfig
204
+ ),
205
+ { nullable: true }
206
+ ),
207
+ /**
208
+ * Include markdown-formatted content in the response
209
+ */
210
+ markdown: S.optionalWith(
211
+ S.Union(
212
+ S.Boolean,
213
+ SelectMarkdownConfig
214
+ ),
215
+ { nullable: true }
216
+ ),
217
+ screenshot: S.optionalWith(
218
+ S.Union(
219
+ S.Boolean,
220
+ SelectScreenshotConfig
221
+ ),
222
+ { nullable: true }
223
+ ),
224
+ summary: S.optionalWith(
225
+ S.Union(
226
+ S.Boolean,
227
+ /**
228
+ * Options for AI-powered page summarization
229
+ */
230
+ S.Struct({
231
+ /**
232
+ * Custom prompt for AI summarization (max 5000 characters)
233
+ */
234
+ prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), {
235
+ nullable: true,
236
+ default: () => "You are a helpful assistant that summarizes web page content.\nGiven the markdown content of a web page, provide a clear and concise summary.\nFocus on the main points and key information.\nKeep the summary informative but brief."
237
+ })
238
+ })
239
+ ),
240
+ { nullable: true }
241
+ ),
242
+ /**
243
+ * Options for extracting relevant snippets from page content using semantic search
244
+ */
245
+ snippets: S.optionalWith(
246
+ S.Struct({
247
+ /**
248
+ * Query to find relevant content snippets from the page (required, non-empty)
249
+ */
250
+ query: Trimmed,
251
+ /**
252
+ * Maximum number of snippets to return (1-50)
253
+ */
254
+ maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }),
255
+ /**
256
+ * Minimum relevance score threshold (0-1). Snippets below this score are filtered out.
257
+ */
258
+ minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), {
259
+ nullable: true,
260
+ default: () => 0.5
261
+ }),
262
+ /**
263
+ * Target snippet size in characters (100-2000)
264
+ */
265
+ targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 })
266
+ }),
267
+ { nullable: true }
268
+ ),
269
+ /**
270
+ * Include page metadata in the response
271
+ */
272
+ meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }),
273
+ /**
274
+ * Configure response info options (headers inclusion)
275
+ */
276
+ response: S.optionalWith(
277
+ S.Struct({
278
+ /**
279
+ * Whether to include HTTP response headers
280
+ */
281
+ includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
282
+ }),
283
+ { nullable: true }
284
+ )
285
+ }),
286
+ { nullable: true }
287
+ ),
288
+ /**
289
+ * Configuration options for browser behavior during the fetch
290
+ */
291
+ browserConfig: S.optionalWith(
292
+ S.Struct({
293
+ /**
294
+ * Whether to scroll the entire page to capture lazy-loaded content
295
+ */
296
+ scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
297
+ }),
298
+ { nullable: true }
299
+ )
300
+ }) {
301
+ });
302
+ var ResponseInfo = class extends S.Class("ResponseInfo")({
303
+ /**
304
+ * The URL that was fetched
305
+ */
306
+ url: S.String,
307
+ /**
308
+ * HTTP status code of the response
309
+ */
310
+ statusCode: S.Number,
311
+ /**
312
+ * Response headers from the fetch operation (keys are lower-cased HTTP header names)
313
+ */
314
+ headers: S.optionalWith(S.Struct({}), { nullable: true })
315
+ }) {
316
+ };
317
+ var IconMeta = class extends S.Class("IconMeta")({
318
+ /**
319
+ * Icon URL or path
320
+ */
321
+ href: S.String,
322
+ /**
323
+ * Link relationship type
324
+ */
325
+ rel: S.String,
326
+ /**
327
+ * MIME type of the icon
328
+ */
329
+ type: S.optionalWith(S.String, { nullable: true }),
330
+ /**
331
+ * Icon dimensions
332
+ */
333
+ sizes: S.optionalWith(S.String, { nullable: true })
334
+ }) {
335
+ };
336
+ var OgImage = class extends S.Class("OgImage")({
337
+ /**
338
+ * Image URL
339
+ */
340
+ url: S.String,
341
+ /**
342
+ * Image width in pixels
343
+ */
344
+ width: S.optionalWith(S.Number, { nullable: true }),
345
+ /**
346
+ * Image height in pixels
347
+ */
348
+ height: S.optionalWith(S.Number, { nullable: true }),
349
+ /**
350
+ * Image alt text for accessibility
351
+ */
352
+ alt: S.optionalWith(S.String, { nullable: true })
353
+ }) {
354
+ };
355
+ var OpenGraphMeta = class extends S.Class("OpenGraphMeta")({
356
+ /**
357
+ * Open Graph title (og:title)
358
+ */
359
+ title: S.optionalWith(S.String, { nullable: true }),
360
+ /**
361
+ * Open Graph description (og:description)
362
+ */
363
+ description: S.optionalWith(S.String, { nullable: true }),
364
+ /**
365
+ * Open Graph type (og:type) - website, article, product, etc.
366
+ */
367
+ type: S.optionalWith(S.String, { nullable: true }),
368
+ /**
369
+ * Canonical URL for the content (og:url)
370
+ */
371
+ url: S.optionalWith(S.String, { nullable: true }),
372
+ /**
373
+ * Site name (og:site_name)
374
+ */
375
+ siteName: S.optionalWith(S.String, { nullable: true }),
376
+ /**
377
+ * Locale in language_TERRITORY format (og:locale)
378
+ */
379
+ locale: S.optionalWith(S.String, { nullable: true }),
380
+ /**
381
+ * Open Graph images (og:image and related properties)
382
+ */
383
+ images: S.optionalWith(S.Array(OgImage), { nullable: true })
384
+ }) {
385
+ };
386
+ var TwitterCardMeta = class extends S.Class("TwitterCardMeta")({
387
+ /**
388
+ * Twitter card type (twitter:card)
389
+ */
390
+ card: S.optionalWith(S.String, { nullable: true }),
391
+ /**
392
+ * Twitter @username of the website (twitter:site)
393
+ */
394
+ site: S.optionalWith(S.String, { nullable: true }),
395
+ /**
396
+ * Twitter @username of content creator (twitter:creator)
397
+ */
398
+ creator: S.optionalWith(S.String, { nullable: true }),
399
+ /**
400
+ * Title for Twitter card (twitter:title)
401
+ */
402
+ title: S.optionalWith(S.String, { nullable: true }),
403
+ /**
404
+ * Description for Twitter card (twitter:description)
405
+ */
406
+ description: S.optionalWith(S.String, { nullable: true }),
407
+ /**
408
+ * Image URL for Twitter card (twitter:image)
409
+ */
410
+ image: S.optionalWith(S.String, { nullable: true })
411
+ }) {
412
+ };
413
+ var PageMeta = class extends S.Class("PageMeta")({
414
+ /**
415
+ * Page title from <title> tag
416
+ */
417
+ title: S.optionalWith(S.String, { nullable: true }),
418
+ /**
419
+ * Meta description from <meta name="description">
420
+ */
421
+ description: S.optionalWith(S.String, { nullable: true }),
422
+ /**
423
+ * Canonical URL from <link rel="canonical">
424
+ */
425
+ canonicalUrl: S.optionalWith(S.String, { nullable: true }),
426
+ /**
427
+ * Page language from <html lang="...">
428
+ */
429
+ language: S.optionalWith(S.String, { nullable: true }),
430
+ /**
431
+ * Character encoding from <meta charset="...">
432
+ */
433
+ charset: S.optionalWith(S.String, { nullable: true }),
434
+ /**
435
+ * Primary favicon URL (first icon found)
436
+ */
437
+ favicon: S.optionalWith(S.String, { nullable: true }),
438
+ /**
439
+ * All icon links (favicons, apple-touch-icons, etc.)
440
+ */
441
+ icons: S.optionalWith(S.Array(IconMeta), { nullable: true }),
442
+ /**
443
+ * Open Graph metadata from og:* meta tags
444
+ */
445
+ openGraph: S.optionalWith(OpenGraphMeta, { nullable: true }),
446
+ /**
447
+ * Twitter Card metadata from twitter:* meta tags
448
+ */
449
+ twitter: S.optionalWith(TwitterCardMeta, { nullable: true })
450
+ }) {
451
+ };
452
+ var FetchData = class extends S.Class("FetchData")({
453
+ response: ResponseInfo,
454
+ /**
455
+ * Page metadata extracted from HTML head (title, description, Open Graph, Twitter Card, icons)
456
+ */
457
+ meta: S.optionalWith(PageMeta, { nullable: true }),
458
+ /**
459
+ * The HTML content of the fetched page
460
+ */
461
+ html: S.optionalWith(S.String, { nullable: true }),
462
+ /**
463
+ * The markdown-formatted content extracted from the page
464
+ */
465
+ markdown: S.optionalWith(S.String, { nullable: true }),
466
+ /**
467
+ * Base64-encoded data URI of the screenshot image
468
+ */
469
+ screenshot: S.optionalWith(S.String, { nullable: true }),
470
+ /**
471
+ * AI-generated summary of the page content
472
+ */
473
+ summary: S.optionalWith(S.String, { nullable: true }),
474
+ /**
475
+ * Relevant snippets extracted from the page based on the search query
476
+ */
477
+ snippets: S.optionalWith(
478
+ S.Array(
479
+ S.Struct({
480
+ /**
481
+ * Type identifier for TextPart compatibility
482
+ */
483
+ type: S.optionalWith(S.Literal("text"), { nullable: true, default: () => "text" }),
484
+ /**
485
+ * The text content of the snippet
486
+ */
487
+ text: S.String,
488
+ /**
489
+ * Relevance score from the reranker (0-1)
490
+ */
491
+ score: S.Number,
492
+ /**
493
+ * Original chunk index
494
+ */
495
+ index: S.Number
496
+ })
497
+ ),
498
+ { nullable: true }
499
+ )
500
+ }) {
501
+ };
502
+ var FetchResult = class extends S.Class("FetchResult")({
503
+ data: FetchData
504
+ }) {
505
+ };
506
+ var FetchErrorTag = class extends S.Literal("FetchError") {
507
+ };
508
+ var FetchError = class extends S.Class("FetchError")({
509
+ _tag: FetchErrorTag
510
+ }) {
511
+ };
512
+ (class extends S.Class("MarkdownRequest")({
513
+ /**
514
+ * The URL to fetch markdown content from
515
+ */
516
+ url: S.String,
517
+ /**
518
+ * Configuration options for browser behavior during the fetch
519
+ */
520
+ browserConfig: S.optionalWith(
521
+ S.Struct({
522
+ /**
523
+ * Whether to scroll the entire page to capture lazy-loaded content
524
+ */
525
+ scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
526
+ }),
527
+ { nullable: true }
528
+ )
529
+ }) {
530
+ });
531
+ var InvalidAssetHashTag = class extends S.Literal("InvalidAssetHash") {
532
+ };
533
+ var InvalidAssetHash = class extends S.Class("InvalidAssetHash")({
534
+ message: S.String,
535
+ _tag: InvalidAssetHashTag
536
+ }) {
537
+ };
538
+ var AssetNotFoundInHarTag = class extends S.Literal("AssetNotFoundInHar") {
539
+ };
540
+ var AssetNotFoundInHar = class extends S.Class("AssetNotFoundInHar")({
541
+ snapshotDir: S.String,
542
+ assetUrl: S.String,
543
+ _tag: AssetNotFoundInHarTag
544
+ }) {
545
+ };
546
+ var HarNotFoundTag = class extends S.Literal("HarNotFound") {
547
+ };
548
+ var HarNotFound = class extends S.Class("HarNotFound")({
549
+ snapshotDir: S.String,
550
+ _tag: HarNotFoundTag
551
+ }) {
552
+ };
553
+ var AssetFetchErrorTag = class extends S.Literal("AssetFetchError") {
554
+ };
555
+ var AssetFetchError = class extends S.Class("AssetFetchError")({
556
+ snapshotDir: S.String,
557
+ assetUrl: S.String,
558
+ _tag: AssetFetchErrorTag
559
+ }) {
560
+ };
561
+ var LocalAssetsGetAsset500 = class extends S.Union(
562
+ InvalidAssetHash,
563
+ AssetNotFoundInHar,
564
+ HarNotFound,
565
+ AssetFetchError
566
+ ) {
567
+ };
568
+ var make = (httpClient, options = {}) => {
569
+ const unexpectedStatus = (response) => Effect2.flatMap(
570
+ Effect2.orElseSucceed(response.json, () => "Unexpected status code"),
571
+ (description) => Effect2.fail(
572
+ new HttpClientError.ResponseError({
573
+ request: response.request,
574
+ response,
575
+ reason: "StatusCode",
576
+ description: typeof description === "string" ? description : JSON.stringify(description)
577
+ })
578
+ )
579
+ );
580
+ const withResponse = options.transformClient ? (f) => (request) => Effect2.flatMap(
581
+ Effect2.flatMap(options.transformClient(httpClient), (client) => client.execute(request)),
582
+ f
583
+ ) : (f) => (request) => Effect2.flatMap(httpClient.execute(request), f);
584
+ const decodeSuccess = (schema) => (response) => HttpClientResponse.schemaBodyJson(schema)(response);
585
+ const decodeError = (tag, schema) => (response) => Effect2.flatMap(
586
+ HttpClientResponse.schemaBodyJson(schema)(response),
587
+ (cause) => Effect2.fail(ClientError(tag, cause, response))
588
+ );
589
+ return {
590
+ httpClient,
591
+ assetsGetByUrl: (browserSessionId, options2) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/asset`).pipe(
592
+ HttpClientRequest.setUrlParams({ url: options2?.["url"] }),
593
+ withResponse(
594
+ HttpClientResponse.matchStatus({
595
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
596
+ "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
597
+ "404": decodeError("AssetsGetByUrl404", AssetsGetByUrl404),
598
+ "500": decodeError("AssetsGetByUrl500", AssetsGetByUrl500),
599
+ "502": decodeError("CurlError", CurlError),
600
+ "429": () => Effect2.void,
601
+ orElse: unexpectedStatus
602
+ })
603
+ )
604
+ ),
605
+ assetsList: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}`).pipe(
606
+ withResponse(
607
+ HttpClientResponse.matchStatus({
608
+ "2xx": decodeSuccess(AssetListResponse),
609
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
610
+ "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
611
+ "404": decodeError("AssetNotFound", AssetNotFound),
612
+ "500": decodeError("AssetsList500", AssetsList500),
613
+ "429": () => Effect2.void,
614
+ orElse: unexpectedStatus
615
+ })
616
+ )
617
+ ),
618
+ fetch: (options2) => HttpClientRequest.post(`/v1/fetch`).pipe(
619
+ HttpClientRequest.bodyUnsafeJson(options2),
620
+ withResponse(
621
+ HttpClientResponse.matchStatus({
622
+ "2xx": decodeSuccess(FetchResult),
623
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
624
+ "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
625
+ "500": decodeError("FetchError", FetchError),
626
+ "429": () => Effect2.void,
627
+ orElse: unexpectedStatus
628
+ })
629
+ )
630
+ ),
631
+ markdown: (options2) => HttpClientRequest.post(`/v1/fetch/markdown`).pipe(
632
+ HttpClientRequest.bodyUnsafeJson(options2),
633
+ withResponse(
634
+ HttpClientResponse.matchStatus({
635
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
636
+ "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
637
+ "500": decodeError("FetchError", FetchError),
638
+ "429": () => Effect2.void,
639
+ orElse: unexpectedStatus
640
+ })
641
+ )
642
+ ),
643
+ localAssetsGetAsset: (assetHash) => HttpClientRequest.get(`/local-asset/${assetHash}`).pipe(
644
+ withResponse(
645
+ HttpClientResponse.matchStatus({
646
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
647
+ "500": decodeError("LocalAssetsGetAsset500", LocalAssetsGetAsset500),
648
+ "204": () => Effect2.void,
649
+ "429": () => Effect2.void,
650
+ orElse: unexpectedStatus
651
+ })
652
+ )
653
+ )
654
+ };
655
+ };
656
+ var ClientErrorImpl = class extends Data.Error {
657
+ };
658
+ var ClientError = (tag, cause, response) => new ClientErrorImpl({
659
+ _tag: tag,
660
+ cause,
661
+ response,
662
+ request: response.request
663
+ });
664
+
665
+ // src/ExpandClient.ts
666
+ var ExpandConfig = Config.all({
667
+ apiKey: Config.option(Config.redacted("EXPAND_API_KEY")),
668
+ baseUrl: Config.withDefault(Config.string("EXPAND_BASE_URL"), "https://api.expand.ai")
669
+ });
670
+ var ExpandClient = class extends Effect.Service()("ExpandClient", {
671
+ dependencies: [],
672
+ scoped: Effect.gen(function* () {
673
+ const config = yield* ExpandConfig;
674
+ const httpClient = yield* HttpClient.HttpClient;
675
+ const requestHeaders = yield* Effect.serviceOption(CurrentRequestHeaders);
676
+ yield* Effect.logInfo(`ExpandClient connected to ${config.baseUrl}`);
677
+ return make(httpClient, {
678
+ transformClient: (client) => Effect.succeed(
679
+ client.pipe(
680
+ HttpClient.mapRequest(HttpClientRequest$1.prependUrl(config.baseUrl)),
681
+ HttpClient.mapRequest((req) => {
682
+ if (Option.isSome(requestHeaders)) {
683
+ const auth = requestHeaders.value["authorization"];
684
+ if (auth) {
685
+ return HttpClientRequest$1.setHeader("authorization", auth)(req);
686
+ }
687
+ }
688
+ if (Option.isSome(config.apiKey)) {
689
+ return HttpClientRequest$1.setHeader("x-expand-api-key", Redacted.value(config.apiKey.value))(req);
690
+ }
691
+ return req;
692
+ })
693
+ )
694
+ )
695
+ });
696
+ })
697
+ }) {
698
+ };
699
+ var SnippetsFormat = class extends Schema.Class("SnippetsFormat")({
700
+ query: Schema.String.annotations({
701
+ description: "Query to find relevant content snippets from the page"
702
+ }),
703
+ maxSnippets: Schema.optional(
704
+ Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50))
705
+ ).annotations({
706
+ description: "Maximum number of snippets to return (1-50, default: 5)"
707
+ }),
708
+ targetSnippetSize: Schema.optional(
709
+ Schema.Int.pipe(Schema.greaterThanOrEqualTo(100), Schema.lessThanOrEqualTo(2e3))
710
+ ).annotations({
711
+ description: "Target snippet size in characters (100-2000, default: 384)"
712
+ })
713
+ }) {
714
+ };
715
+ var Format = Schema.Union(
716
+ Schema.Literal("markdown").annotations({
717
+ description: "Return as cleaned markdown content"
718
+ }),
719
+ Schema.Literal("html").annotations({
720
+ description: "Return as raw HTML content"
721
+ }),
722
+ Schema.Literal("summary").annotations({
723
+ description: "Return AI-generated summary of the page content"
724
+ }),
725
+ SnippetsFormat
726
+ ).annotations({
727
+ description: "Output format for the fetched content"
728
+ });
729
+ var FetchParams = class extends Schema.Class("FetchParams")({
730
+ url: Schema.String.annotations({
731
+ description: "The URL to fetch content from"
732
+ }),
733
+ format: Format,
734
+ includeMeta: Schema.Boolean.annotations({
735
+ description: "Whether to include page meta tags in the response"
736
+ })
737
+ }) {
738
+ };
739
+ var FetchResult2 = class extends Schema.Class("FetchResult")({
740
+ content: Schema.String,
741
+ url: Schema.String,
742
+ meta: Schema.optional(PageMeta)
743
+ }) {
744
+ };
745
+ var FetchTool = Tool.make("fetch", {
746
+ description: "Fetch and extract content from any URL.",
747
+ parameters: FetchParams.fields,
748
+ success: FetchResult2
749
+ }).annotate(Tool.Readonly, true).annotate(Tool.Destructive, false);
750
+ var Fetch = class extends Effect.Service()("Fetch", {
751
+ dependencies: [ExpandClient.Default],
752
+ scoped: Effect.gen(function* () {
753
+ const client = yield* ExpandClient;
754
+ const fetch = Effect.fn("Fetch.fetch")(function* ({ url, format, includeMeta }) {
755
+ const formatLabel = format instanceof SnippetsFormat ? "snippets" : format;
756
+ yield* Effect.logDebug(`Fetching: ${url}`).pipe(Effect.annotateLogs({ format: formatLabel, includeMeta }));
757
+ const result = yield* client.fetch({
758
+ url,
759
+ select: {
760
+ markdown: format === "markdown",
761
+ html: format === "html",
762
+ summary: format === "summary",
763
+ snippets: format instanceof SnippetsFormat ? { ...format } : void 0,
764
+ meta: includeMeta
765
+ }
766
+ });
767
+ const content = result.data.markdown ?? result.data.html ?? result.data.summary ?? result.data.snippets?.map((snippet) => `${snippet.text} (Score: ${snippet.score})`).join("\n") ?? "";
768
+ yield* Effect.logDebug(`\u{1F4E4} Fetched successfully: ${result.data.response.url}`);
769
+ return new FetchResult2({
770
+ content,
771
+ url: result.data.response.url,
772
+ meta: result.data.meta
773
+ });
774
+ }, Effect.orDie);
775
+ return { fetch };
776
+ })
777
+ }) {
778
+ };
779
+
780
+ // src/Toolkit.ts
781
+ var toolkit = Toolkit.make(FetchTool);
782
+ var ToolkitLayer = toolkit.toLayer(
783
+ Effect.gen(function* () {
784
+ yield* Effect.logInfo("Registering tools: fetch");
785
+ const fetchService = yield* Fetch;
786
+ return toolkit.of({
787
+ fetch: fetchService.fetch
788
+ });
789
+ })
790
+ ).pipe(Layer.provide(Fetch.Default), Layer.provide(ExpandClient.Default), Layer.provide(NodeHttpClient.layerUndici));
791
+ var ExpandToolKit = McpServer.toolkit(toolkit).pipe(Layer.provide(ToolkitLayer));
792
+
793
+ // src/Server.ts
794
+ var ServerInfo = {
795
+ name: "expandai-mcp-server",
796
+ version: package_default.version
797
+ };
798
+ var CurrentRequestHeaders = class extends Context.Tag("CurrentRequestHeaders")() {
799
+ };
800
+ var makeServerLayer = (options = {}) => options.includeDocs ? Layer.mergeAll(ExpandToolKit, ExpandDocs) : ExpandToolKit;
801
+
802
+ export { CurrentRequestHeaders, ServerInfo, makeServerLayer };