@expandai/mcp-server 0.1.3 → 0.2.0

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.
@@ -1,4 +1,4 @@
1
- import { Effect, Config, Schema, Layer, Redacted } from 'effect';
1
+ import { Effect, Config, Schema, Layer, Context, Option, Redacted } from 'effect';
2
2
  import { McpServer, Tool, Toolkit } from '@effect/ai';
3
3
  import { NodeHttpClient } from '@effect/platform-node';
4
4
  import { HttpClient, HttpClientRequest as HttpClientRequest$1 } from '@effect/platform';
@@ -13,7 +13,7 @@ import * as S from 'effect/Schema';
13
13
 
14
14
  // package.json
15
15
  var package_default = {
16
- version: "0.1.3"};
16
+ version: "0.2.0"};
17
17
  var ExpandDocs = McpServer.resource({
18
18
  uri: "expand://about",
19
19
  name: "About expand.ai",
@@ -77,10 +77,14 @@ var HttpApiDecodeError = class extends S.Class("HttpApiDecodeError")({
77
77
  _tag: HttpApiDecodeErrorTag
78
78
  }) {
79
79
  };
80
- var UnauthorizedAccessTag = class extends S.Literal("UnauthorizedAccess") {
80
+ var AuthFailedReason = class extends S.Literal("InvalidApiKey", "InvalidToken", "InvalidSession", "InvalidTenant") {
81
81
  };
82
- var UnauthorizedAccess = class extends S.Class("UnauthorizedAccess")({
83
- _tag: UnauthorizedAccessTag
82
+ var AuthFailedTag = class extends S.Literal("AuthFailed") {
83
+ };
84
+ var AuthFailed = class extends S.Class("AuthFailed")({
85
+ reason: AuthFailedReason,
86
+ description: S.optionalWith(S.String, { nullable: true }),
87
+ _tag: AuthFailedTag
84
88
  }) {
85
89
  };
86
90
  var AssetNotFoundTag = class extends S.Literal("AssetNotFound") {
@@ -123,7 +127,13 @@ var AssetRetrievalError = class extends S.Class("AssetRetrievalError")({
123
127
  _tag: AssetRetrievalErrorTag
124
128
  }) {
125
129
  };
126
- var AssetsGetByUrl500 = class extends S.Union(AssetReadError, InvalidWaczFormat, AssetRetrievalError) {
130
+ var InternalErrorTag = class extends S.Literal("InternalError") {
131
+ };
132
+ var InternalError = class extends S.Class("InternalError")({
133
+ _tag: InternalErrorTag
134
+ }) {
135
+ };
136
+ var AssetsGetByUrl500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat, AssetRetrievalError) {
127
137
  };
128
138
  var CurlErrorInputMethod = class extends S.Literal(
129
139
  "GET",
@@ -137,6 +147,8 @@ var CurlErrorInputMethod = class extends S.Literal(
137
147
  "PATCH"
138
148
  ) {
139
149
  };
150
+ var URL = class extends S.String {
151
+ };
140
152
  var CurlErrorTag = class extends S.Literal("CurlError") {
141
153
  };
142
154
  var CurlError = class extends S.Class("CurlError")({
@@ -147,11 +159,14 @@ var CurlError = class extends S.Class("CurlError")({
147
159
  body: S.optionalWith(S.String, { nullable: true }),
148
160
  headers: S.optionalWith(S.Struct({}), { nullable: true }),
149
161
  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
- }),
162
+ S.Union(
163
+ S.Struct({
164
+ server: S.String,
165
+ username: S.optionalWith(S.String, { nullable: true }),
166
+ password: S.optionalWith(S.String, { nullable: true })
167
+ }),
168
+ URL
169
+ ),
155
170
  { nullable: true }
156
171
  ),
157
172
  timeout: S.optionalWith(S.Number, { nullable: true }),
@@ -171,9 +186,20 @@ var AssetListResponse = class extends S.Class("AssetListResponse")({
171
186
  assets: S.Array(AssetListItem)
172
187
  }) {
173
188
  };
174
- var AssetsList500 = class extends S.Union(AssetReadError, InvalidWaczFormat) {
189
+ var AssetsList500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat) {
175
190
  };
176
- var URL = class extends S.String {
191
+ var AssetsGetWacz500 = class extends S.Union(InternalError, AssetReadError) {
192
+ };
193
+ var DatasetNotFoundTag = class extends S.Literal("DatasetNotFound") {
194
+ };
195
+ var DatasetNotFound = class extends S.Class("DatasetNotFound")({
196
+ path: S.String,
197
+ _tag: DatasetNotFoundTag
198
+ }) {
199
+ };
200
+ var DatasetsGetDataset500 = class extends S.Union(InternalError, DatasetNotFound) {
201
+ };
202
+ var DatasetsGetFinetuneDataset500 = class extends S.Union(InternalError, DatasetNotFound) {
177
203
  };
178
204
  var SelectHtmlConfig = class extends S.Record({ key: S.String, value: S.Unknown }) {
179
205
  };
@@ -266,6 +292,29 @@ var Int2 = class extends S.Int {
266
292
  }),
267
293
  { nullable: true }
268
294
  ),
295
+ links: S.optionalWith(
296
+ S.Union(
297
+ S.Boolean,
298
+ /**
299
+ * Options for customizing link extraction from the page
300
+ */
301
+ S.Struct({
302
+ /**
303
+ * Only include links from the same domain as the fetched URL
304
+ */
305
+ sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }),
306
+ /**
307
+ * Regex patterns - only include links matching at least one pattern
308
+ */
309
+ includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }),
310
+ /**
311
+ * Regex patterns - exclude links matching any pattern
312
+ */
313
+ excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true })
314
+ })
315
+ ),
316
+ { nullable: true }
317
+ ),
269
318
  /**
270
319
  * Include page metadata in the response
271
320
  */
@@ -281,7 +330,15 @@ var Int2 = class extends S.Int {
281
330
  includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
282
331
  }),
283
332
  { nullable: true }
284
- )
333
+ ),
334
+ /**
335
+ * Include pruned JSON in the response (opt-in)
336
+ */
337
+ json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }),
338
+ /**
339
+ * Set to true to include extracted links and sidebar content
340
+ */
341
+ appendix: S.optionalWith(S.Boolean, { nullable: true })
285
342
  }),
286
343
  { nullable: true }
287
344
  ),
@@ -449,58 +506,75 @@ var PageMeta = class extends S.Class("PageMeta")({
449
506
  twitter: S.optionalWith(TwitterCardMeta, { nullable: true })
450
507
  }) {
451
508
  };
452
- var FetchData = class extends S.Class("FetchData")({
453
- response: ResponseInfo,
509
+ var FetchLink = class extends S.Class("FetchLink")({
454
510
  /**
455
- * Page metadata extracted from HTML head (title, description, Open Graph, Twitter Card, icons)
511
+ * The URL of the link
456
512
  */
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 }),
513
+ url: S.String,
474
514
  /**
475
- * Relevant snippets extracted from the page based on the search query
515
+ * The anchor text of the link
476
516
  */
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
- )
517
+ text: S.optionalWith(S.String, { nullable: true })
500
518
  }) {
501
519
  };
502
520
  var FetchResult = class extends S.Class("FetchResult")({
503
- data: FetchData
521
+ /**
522
+ * Contains the extracted content in the formats specified by the select configuration
523
+ */
524
+ data: S.Struct({
525
+ response: ResponseInfo,
526
+ meta: S.optionalWith(PageMeta, { nullable: true }),
527
+ /**
528
+ * The HTML content of the fetched page
529
+ */
530
+ html: S.optionalWith(S.String, { nullable: true }),
531
+ /**
532
+ * The markdown-formatted content extracted from the page
533
+ */
534
+ markdown: S.optionalWith(S.String, { nullable: true }),
535
+ /**
536
+ * Base64-encoded data URI of the screenshot image
537
+ */
538
+ screenshot: S.optionalWith(S.String, { nullable: true }),
539
+ /**
540
+ * AI-generated summary of the page content
541
+ */
542
+ summary: S.optionalWith(S.String, { nullable: true }),
543
+ /**
544
+ * Relevant snippets extracted from the page based on the search query
545
+ */
546
+ snippets: S.optionalWith(
547
+ S.Array(
548
+ S.Struct({
549
+ /**
550
+ * Type identifier for TextPart compatibility
551
+ */
552
+ type: S.optionalWith(S.Literal("text"), { nullable: true, default: () => "text" }),
553
+ /**
554
+ * The text content of the snippet
555
+ */
556
+ text: S.String,
557
+ /**
558
+ * Relevance score from the reranker (0-1)
559
+ */
560
+ score: S.Number,
561
+ /**
562
+ * Original chunk index
563
+ */
564
+ index: S.Number
565
+ })
566
+ ),
567
+ { nullable: true }
568
+ ),
569
+ /**
570
+ * Links extracted from the page
571
+ */
572
+ links: S.optionalWith(S.Array(FetchLink), { nullable: true }),
573
+ /**
574
+ * Extracted links and sidebar content
575
+ */
576
+ appendix: S.optionalWith(S.String, { nullable: true })
577
+ })
504
578
  }) {
505
579
  };
506
580
  var FetchErrorTag = class extends S.Literal("FetchError") {
@@ -509,25 +583,8 @@ var FetchError = class extends S.Class("FetchError")({
509
583
  _tag: FetchErrorTag
510
584
  }) {
511
585
  };
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
- });
586
+ var Fetch500 = class extends S.Union(InternalError, FetchError) {
587
+ };
531
588
  var InvalidAssetHashTag = class extends S.Literal("InvalidAssetHash") {
532
589
  };
533
590
  var InvalidAssetHash = class extends S.Class("InvalidAssetHash")({
@@ -559,12 +616,223 @@ var AssetFetchError = class extends S.Class("AssetFetchError")({
559
616
  }) {
560
617
  };
561
618
  var LocalAssetsGetAsset500 = class extends S.Union(
619
+ InternalError,
562
620
  InvalidAssetHash,
563
621
  AssetNotFoundInHar,
564
622
  HarNotFound,
565
623
  AssetFetchError
566
624
  ) {
567
625
  };
626
+ (class extends S.Class("PlaygroundFetchRequest")({
627
+ url: URL,
628
+ /**
629
+ * Specifies which content formats to include in the response
630
+ */
631
+ select: S.optionalWith(
632
+ S.Struct({
633
+ html: S.optionalWith(
634
+ S.Union(
635
+ S.Boolean,
636
+ SelectHtmlConfig
637
+ ),
638
+ { nullable: true }
639
+ ),
640
+ /**
641
+ * Include markdown-formatted content in the response
642
+ */
643
+ markdown: S.optionalWith(
644
+ S.Union(
645
+ S.Boolean,
646
+ SelectMarkdownConfig
647
+ ),
648
+ { nullable: true }
649
+ ),
650
+ screenshot: S.optionalWith(
651
+ S.Union(
652
+ S.Boolean,
653
+ SelectScreenshotConfig
654
+ ),
655
+ { nullable: true }
656
+ ),
657
+ summary: S.optionalWith(
658
+ S.Union(
659
+ S.Boolean,
660
+ /**
661
+ * Options for AI-powered page summarization
662
+ */
663
+ S.Struct({
664
+ /**
665
+ * Custom prompt for AI summarization (max 5000 characters)
666
+ */
667
+ prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), {
668
+ nullable: true,
669
+ 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."
670
+ })
671
+ })
672
+ ),
673
+ { nullable: true }
674
+ ),
675
+ /**
676
+ * Options for extracting relevant snippets from page content using semantic search
677
+ */
678
+ snippets: S.optionalWith(
679
+ S.Struct({
680
+ /**
681
+ * Query to find relevant content snippets from the page (required, non-empty)
682
+ */
683
+ query: Trimmed,
684
+ /**
685
+ * Maximum number of snippets to return (1-50)
686
+ */
687
+ maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }),
688
+ /**
689
+ * Minimum relevance score threshold (0-1). Snippets below this score are filtered out.
690
+ */
691
+ minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), {
692
+ nullable: true,
693
+ default: () => 0.5
694
+ }),
695
+ /**
696
+ * Target snippet size in characters (100-2000)
697
+ */
698
+ targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 })
699
+ }),
700
+ { nullable: true }
701
+ ),
702
+ links: S.optionalWith(
703
+ S.Union(
704
+ S.Boolean,
705
+ /**
706
+ * Options for customizing link extraction from the page
707
+ */
708
+ S.Struct({
709
+ /**
710
+ * Only include links from the same domain as the fetched URL
711
+ */
712
+ sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }),
713
+ /**
714
+ * Regex patterns - only include links matching at least one pattern
715
+ */
716
+ includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }),
717
+ /**
718
+ * Regex patterns - exclude links matching any pattern
719
+ */
720
+ excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true })
721
+ })
722
+ ),
723
+ { nullable: true }
724
+ ),
725
+ /**
726
+ * Include page metadata in the response
727
+ */
728
+ meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }),
729
+ /**
730
+ * Configure response info options (headers inclusion)
731
+ */
732
+ response: S.optionalWith(
733
+ S.Struct({
734
+ /**
735
+ * Whether to include HTTP response headers
736
+ */
737
+ includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
738
+ }),
739
+ { nullable: true }
740
+ ),
741
+ /**
742
+ * Include pruned JSON in the response (opt-in)
743
+ */
744
+ json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }),
745
+ /**
746
+ * Set to true to include extracted links and sidebar content
747
+ */
748
+ appendix: S.optionalWith(S.Boolean, { nullable: true })
749
+ }),
750
+ { nullable: true }
751
+ ),
752
+ /**
753
+ * Configuration options for browser behavior during the fetch
754
+ */
755
+ browserConfig: S.optionalWith(
756
+ S.Struct({
757
+ /**
758
+ * Whether to scroll the entire page to capture lazy-loaded content
759
+ */
760
+ scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false })
761
+ }),
762
+ { nullable: true }
763
+ )
764
+ }) {
765
+ });
766
+ var PlaygroundData = class extends S.Class("PlaygroundData")({
767
+ response: ResponseInfo,
768
+ /**
769
+ * HTML with expand-id attributes on elements
770
+ */
771
+ html: S.String,
772
+ browserSessionId: S.String
773
+ }) {
774
+ };
775
+ var PlaygroundResult = class extends S.Class("PlaygroundResult")({
776
+ data: PlaygroundData
777
+ }) {
778
+ };
779
+ var PlaygroundFetch500 = class extends S.Union(InternalError, FetchError) {
780
+ };
781
+ var PlaygroundGet500 = class extends S.Union(InternalError, FetchError) {
782
+ };
783
+ var PlaygroundGetBySession500 = class extends S.Union(InternalError, FetchError) {
784
+ };
785
+ (class extends S.Class("PlaygroundSearchRequest")({
786
+ /**
787
+ * Search query to find relevant content chunks
788
+ */
789
+ query: Trimmed,
790
+ /**
791
+ * Browser session ID from the playground fetch result
792
+ */
793
+ browserSessionId: S.String,
794
+ /**
795
+ * Maximum number of results to return
796
+ */
797
+ maxResults: S.optionalWith(S.Number, { nullable: true }),
798
+ /**
799
+ * Minimum relevance score threshold (0-1). A value of 0 disables filtering.
800
+ */
801
+ minScore: S.optionalWith(S.Number, { nullable: true })
802
+ }) {
803
+ });
804
+ var PlaygroundSearchChunk = class extends S.Class("PlaygroundSearchChunk")({
805
+ /**
806
+ * The chunk text content
807
+ */
808
+ text: S.String,
809
+ /**
810
+ * Relevance score from 0-1
811
+ */
812
+ score: S.Number,
813
+ /**
814
+ * Original chunk index
815
+ */
816
+ index: S.Number,
817
+ /**
818
+ * MDAST originalNodeIds for highlighting in HTML/markdown views
819
+ */
820
+ nodeIds: S.Array(S.Number)
821
+ }) {
822
+ };
823
+ var PlaygroundSearch200 = class extends S.Struct({
824
+ /**
825
+ * Scored and ranked content chunks
826
+ */
827
+ chunks: S.Array(PlaygroundSearchChunk),
828
+ /**
829
+ * Duration of the search operation in milliseconds
830
+ */
831
+ durationMs: S.optionalWith(S.Number, { nullable: true })
832
+ }) {
833
+ };
834
+ var PlaygroundSearch500 = class extends S.Union(InternalError, FetchError) {
835
+ };
568
836
  var make = (httpClient, options = {}) => {
569
837
  const unexpectedStatus = (response) => Effect2.flatMap(
570
838
  Effect2.orElseSucceed(response.json, () => "Unexpected status code"),
@@ -593,49 +861,79 @@ var make = (httpClient, options = {}) => {
593
861
  withResponse(
594
862
  HttpClientResponse.matchStatus({
595
863
  "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
596
- "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
864
+ "401": decodeError("AuthFailed", AuthFailed),
597
865
  "404": decodeError("AssetsGetByUrl404", AssetsGetByUrl404),
598
866
  "500": decodeError("AssetsGetByUrl500", AssetsGetByUrl500),
599
867
  "502": decodeError("CurlError", CurlError),
600
868
  "429": () => Effect2.void,
869
+ "503": () => Effect2.void,
601
870
  orElse: unexpectedStatus
602
871
  })
603
872
  )
604
873
  ),
605
- assetsList: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}`).pipe(
874
+ assetsList: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/list`).pipe(
606
875
  withResponse(
607
876
  HttpClientResponse.matchStatus({
608
877
  "2xx": decodeSuccess(AssetListResponse),
609
878
  "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
610
- "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
879
+ "401": decodeError("AuthFailed", AuthFailed),
611
880
  "404": decodeError("AssetNotFound", AssetNotFound),
612
881
  "500": decodeError("AssetsList500", AssetsList500),
613
882
  "429": () => Effect2.void,
883
+ "503": () => Effect2.void,
614
884
  orElse: unexpectedStatus
615
885
  })
616
886
  )
617
887
  ),
618
- fetch: (options2) => HttpClientRequest.post(`/v1/fetch`).pipe(
619
- HttpClientRequest.bodyUnsafeJson(options2),
888
+ assetsGetWacz: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}`).pipe(
889
+ withResponse(
890
+ HttpClientResponse.matchStatus({
891
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
892
+ "401": decodeError("AuthFailed", AuthFailed),
893
+ "404": decodeError("AssetNotFound", AssetNotFound),
894
+ "500": decodeError("AssetsGetWacz500", AssetsGetWacz500),
895
+ "429": () => Effect2.void,
896
+ "503": () => Effect2.void,
897
+ orElse: unexpectedStatus
898
+ })
899
+ )
900
+ ),
901
+ datasetsGetDataset: () => HttpClientRequest.get(`/datasets/page-analysis`).pipe(
902
+ withResponse(
903
+ HttpClientResponse.matchStatus({
904
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
905
+ "401": decodeError("AuthFailed", AuthFailed),
906
+ "500": decodeError("DatasetsGetDataset500", DatasetsGetDataset500),
907
+ "204": () => Effect2.void,
908
+ "429": () => Effect2.void,
909
+ "503": () => Effect2.void,
910
+ orElse: unexpectedStatus
911
+ })
912
+ )
913
+ ),
914
+ datasetsGetFinetuneDataset: () => HttpClientRequest.get(`/datasets/finetune`).pipe(
620
915
  withResponse(
621
916
  HttpClientResponse.matchStatus({
622
- "2xx": decodeSuccess(FetchResult),
623
917
  "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
624
- "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
625
- "500": decodeError("FetchError", FetchError),
918
+ "401": decodeError("AuthFailed", AuthFailed),
919
+ "500": decodeError("DatasetsGetFinetuneDataset500", DatasetsGetFinetuneDataset500),
920
+ "204": () => Effect2.void,
626
921
  "429": () => Effect2.void,
922
+ "503": () => Effect2.void,
627
923
  orElse: unexpectedStatus
628
924
  })
629
925
  )
630
926
  ),
631
- markdown: (options2) => HttpClientRequest.post(`/v1/fetch/markdown`).pipe(
927
+ fetch: (options2) => HttpClientRequest.post(`/v1/fetch`).pipe(
632
928
  HttpClientRequest.bodyUnsafeJson(options2),
633
929
  withResponse(
634
930
  HttpClientResponse.matchStatus({
931
+ "2xx": decodeSuccess(FetchResult),
635
932
  "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
636
- "401": decodeError("UnauthorizedAccess", UnauthorizedAccess),
637
- "500": decodeError("FetchError", FetchError),
933
+ "401": decodeError("AuthFailed", AuthFailed),
934
+ "500": decodeError("Fetch500", Fetch500),
638
935
  "429": () => Effect2.void,
936
+ "503": () => Effect2.void,
639
937
  orElse: unexpectedStatus
640
938
  })
641
939
  )
@@ -644,9 +942,65 @@ var make = (httpClient, options = {}) => {
644
942
  withResponse(
645
943
  HttpClientResponse.matchStatus({
646
944
  "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
945
+ "401": decodeError("AuthFailed", AuthFailed),
647
946
  "500": decodeError("LocalAssetsGetAsset500", LocalAssetsGetAsset500),
648
947
  "204": () => Effect2.void,
649
948
  "429": () => Effect2.void,
949
+ "503": () => Effect2.void,
950
+ orElse: unexpectedStatus
951
+ })
952
+ )
953
+ ),
954
+ playgroundFetch: (options2) => HttpClientRequest.post(`/v1/playground/fetch`).pipe(
955
+ HttpClientRequest.bodyUnsafeJson(options2),
956
+ withResponse(
957
+ HttpClientResponse.matchStatus({
958
+ "2xx": decodeSuccess(PlaygroundResult),
959
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
960
+ "401": decodeError("AuthFailed", AuthFailed),
961
+ "500": decodeError("PlaygroundFetch500", PlaygroundFetch500),
962
+ "429": () => Effect2.void,
963
+ "503": () => Effect2.void,
964
+ orElse: unexpectedStatus
965
+ })
966
+ )
967
+ ),
968
+ playgroundGet: (fetchRequestId) => HttpClientRequest.get(`/v1/playground/get/${fetchRequestId}`).pipe(
969
+ withResponse(
970
+ HttpClientResponse.matchStatus({
971
+ "2xx": decodeSuccess(PlaygroundResult),
972
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
973
+ "401": decodeError("AuthFailed", AuthFailed),
974
+ "500": decodeError("PlaygroundGet500", PlaygroundGet500),
975
+ "429": () => Effect2.void,
976
+ "503": () => Effect2.void,
977
+ orElse: unexpectedStatus
978
+ })
979
+ )
980
+ ),
981
+ playgroundGetBySession: (browserSessionId) => HttpClientRequest.get(`/v1/playground/session/${browserSessionId}`).pipe(
982
+ withResponse(
983
+ HttpClientResponse.matchStatus({
984
+ "2xx": decodeSuccess(PlaygroundResult),
985
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
986
+ "401": decodeError("AuthFailed", AuthFailed),
987
+ "500": decodeError("PlaygroundGetBySession500", PlaygroundGetBySession500),
988
+ "429": () => Effect2.void,
989
+ "503": () => Effect2.void,
990
+ orElse: unexpectedStatus
991
+ })
992
+ )
993
+ ),
994
+ playgroundSearch: (options2) => HttpClientRequest.post(`/v1/playground/search`).pipe(
995
+ HttpClientRequest.bodyUnsafeJson(options2),
996
+ withResponse(
997
+ HttpClientResponse.matchStatus({
998
+ "2xx": decodeSuccess(PlaygroundSearch200),
999
+ "400": decodeError("HttpApiDecodeError", HttpApiDecodeError),
1000
+ "401": decodeError("AuthFailed", AuthFailed),
1001
+ "500": decodeError("PlaygroundSearch500", PlaygroundSearch500),
1002
+ "429": () => Effect2.void,
1003
+ "503": () => Effect2.void,
650
1004
  orElse: unexpectedStatus
651
1005
  })
652
1006
  )
@@ -664,7 +1018,7 @@ var ClientError = (tag, cause, response) => new ClientErrorImpl({
664
1018
 
665
1019
  // src/ExpandClient.ts
666
1020
  var ExpandConfig = Config.all({
667
- apiKey: Config.redacted("EXPAND_API_KEY"),
1021
+ apiKey: Config.option(Config.redacted("EXPAND_API_KEY")),
668
1022
  baseUrl: Config.withDefault(Config.string("EXPAND_BASE_URL"), "https://api.expand.ai")
669
1023
  });
670
1024
  var ExpandClient = class extends Effect.Service()("ExpandClient", {
@@ -672,93 +1026,106 @@ var ExpandClient = class extends Effect.Service()("ExpandClient", {
672
1026
  scoped: Effect.gen(function* () {
673
1027
  const config = yield* ExpandConfig;
674
1028
  const httpClient = yield* HttpClient.HttpClient;
1029
+ const requestHeaders = yield* Effect.serviceOption(CurrentRequestHeaders);
675
1030
  yield* Effect.logInfo(`ExpandClient connected to ${config.baseUrl}`);
676
1031
  return make(httpClient, {
677
1032
  transformClient: (client) => Effect.succeed(
678
1033
  client.pipe(
679
1034
  HttpClient.mapRequest(HttpClientRequest$1.prependUrl(config.baseUrl)),
680
- HttpClient.mapRequest(HttpClientRequest$1.setHeader("x-expand-api-key", Redacted.value(config.apiKey)))
1035
+ HttpClient.mapRequest((req) => {
1036
+ if (Option.isSome(requestHeaders)) {
1037
+ const auth = requestHeaders.value["authorization"];
1038
+ if (auth) {
1039
+ return HttpClientRequest$1.setHeader("authorization", auth)(req);
1040
+ }
1041
+ }
1042
+ if (Option.isSome(config.apiKey)) {
1043
+ return HttpClientRequest$1.setHeader("x-expand-api-key", Redacted.value(config.apiKey.value))(req);
1044
+ }
1045
+ return req;
1046
+ })
681
1047
  )
682
1048
  )
683
1049
  });
684
1050
  })
685
1051
  }) {
686
1052
  };
687
- var SnippetsFormat = class extends Schema.Class("SnippetsFormat")({
1053
+ var SearchParams = class extends Schema.Class("SearchParams")({
688
1054
  query: Schema.String.annotations({
689
- description: "Query to find relevant content snippets from the page"
1055
+ description: "Query to find relevant content snippets"
690
1056
  }),
691
- maxSnippets: Schema.optional(
692
- Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50))
693
- ).annotations({
694
- description: "Maximum number of snippets to return (1-50, default: 5)"
1057
+ minScore: Schema.optionalWith(Schema.Number.pipe(Schema.greaterThanOrEqualTo(0), Schema.lessThanOrEqualTo(1)), {
1058
+ default: () => 0.6
1059
+ }).annotations({
1060
+ description: "Minimum relevance score (0-1)",
1061
+ default: 0.6
695
1062
  }),
696
- targetSnippetSize: Schema.optional(
697
- Schema.Int.pipe(Schema.greaterThanOrEqualTo(100), Schema.lessThanOrEqualTo(2e3))
698
- ).annotations({
699
- description: "Target snippet size in characters (100-2000, default: 384)"
1063
+ maxResults: Schema.optionalWith(Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50)), {
1064
+ default: () => 5
1065
+ }).annotations({
1066
+ description: "Maximum snippets to return",
1067
+ default: 5
700
1068
  })
701
1069
  }) {
702
1070
  };
703
- var Format = Schema.Union(
704
- Schema.Literal("markdown").annotations({
705
- description: "Return as cleaned markdown content"
706
- }),
707
- Schema.Literal("html").annotations({
708
- description: "Return as raw HTML content"
709
- }),
710
- Schema.Literal("summary").annotations({
711
- description: "Return AI-generated summary of the page content"
712
- }),
713
- SnippetsFormat
714
- ).annotations({
715
- description: "Output format for the fetched content"
716
- });
717
1071
  var FetchParams = class extends Schema.Class("FetchParams")({
718
1072
  url: Schema.String.annotations({
719
1073
  description: "The URL to fetch content from"
720
1074
  }),
721
- format: Format,
722
- includeMeta: Schema.Boolean.annotations({
723
- description: "Whether to include page meta tags in the response"
1075
+ includeMeta: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({
1076
+ description: "Include page meta tags (title, description, Open Graph)",
1077
+ default: false
1078
+ }),
1079
+ includeAppendix: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({
1080
+ description: "Include extracted links and sidebar content",
1081
+ default: false
1082
+ }),
1083
+ includeJson: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({
1084
+ description: "Include JSON extracted from network responses and DOM",
1085
+ default: false
1086
+ }),
1087
+ search: Schema.optional(SearchParams).annotations({
1088
+ description: "Semantic search. When provided, returns snippets instead of markdown."
724
1089
  })
725
1090
  }) {
726
1091
  };
727
- var FetchResult2 = class extends Schema.Class("FetchResult")({
728
- content: Schema.String,
729
- url: Schema.String,
730
- meta: Schema.optional(PageMeta)
731
- }) {
732
- };
733
1092
  var FetchTool = Tool.make("fetch", {
734
- description: "Fetch and extract content from any URL.",
735
- parameters: FetchParams.fields,
736
- success: FetchResult2
1093
+ description: `Fetch and extract content from any URL.
1094
+
1095
+ Returns markdown by default. When \`search\` is provided, returns semantically relevant snippets instead.`,
1096
+ parameters: FetchParams.fields
737
1097
  }).annotate(Tool.Readonly, true).annotate(Tool.Destructive, false);
738
1098
  var Fetch = class extends Effect.Service()("Fetch", {
739
1099
  dependencies: [ExpandClient.Default],
740
1100
  scoped: Effect.gen(function* () {
741
1101
  const client = yield* ExpandClient;
742
- const fetch = Effect.fn("Fetch.fetch")(function* ({ url, format, includeMeta }) {
743
- const formatLabel = format instanceof SnippetsFormat ? "snippets" : format;
744
- yield* Effect.logDebug(`Fetching: ${url}`).pipe(Effect.annotateLogs({ format: formatLabel, includeMeta }));
1102
+ const fetch = Effect.fn("Fetch.fetch")(function* ({
1103
+ url,
1104
+ includeMeta,
1105
+ includeAppendix,
1106
+ includeJson,
1107
+ search
1108
+ }) {
1109
+ const hasSearch = search !== void 0;
1110
+ yield* Effect.logDebug(`Fetching: ${url}`).pipe(
1111
+ Effect.annotateLogs({ hasSearch, includeMeta, includeAppendix, includeJson })
1112
+ );
745
1113
  const result = yield* client.fetch({
746
1114
  url,
747
1115
  select: {
748
- markdown: format === "markdown",
749
- html: format === "html",
750
- summary: format === "summary",
751
- snippets: format instanceof SnippetsFormat ? { ...format } : void 0,
752
- meta: includeMeta
1116
+ markdown: !hasSearch,
1117
+ snippets: hasSearch ? {
1118
+ query: search.query,
1119
+ maxSnippets: search.maxResults,
1120
+ minScore: search.minScore
1121
+ } : void 0,
1122
+ meta: includeMeta,
1123
+ json: includeJson,
1124
+ appendix: includeAppendix
753
1125
  }
754
1126
  });
755
- const content = result.data.markdown ?? result.data.html ?? result.data.summary ?? result.data.snippets?.map((snippet) => `${snippet.text} (Score: ${snippet.score})`).join("\n") ?? "";
756
- yield* Effect.logDebug(`\u{1F4E4} Fetched successfully: ${result.data.response.url}`);
757
- return new FetchResult2({
758
- content,
759
- url: result.data.response.url,
760
- meta: result.data.meta
761
- });
1127
+ yield* Effect.logDebug(`Fetched successfully: ${result.data.response.url}`);
1128
+ return result.data;
762
1129
  }, Effect.orDie);
763
1130
  return { fetch };
764
1131
  })
@@ -783,6 +1150,8 @@ var ServerInfo = {
783
1150
  name: "expandai-mcp-server",
784
1151
  version: package_default.version
785
1152
  };
1153
+ var CurrentRequestHeaders = class extends Context.Tag("CurrentRequestHeaders")() {
1154
+ };
786
1155
  var makeServerLayer = (options = {}) => options.includeDocs ? Layer.mergeAll(ExpandToolKit, ExpandDocs) : ExpandToolKit;
787
1156
 
788
- export { ServerInfo, makeServerLayer };
1157
+ export { CurrentRequestHeaders, ServerInfo, makeServerLayer };