@expandai/mcp-server
Advanced tools
| import { Effect, Config, Schema, Layer, Context, Option, Redacted } from 'effect'; | ||
| import { McpServer, Tool, Toolkit } from '@effect/ai'; | ||
| import { NodeHttpClient } from '@effect/platform-node'; | ||
| import { HttpClient, HttpClientRequest as HttpClientRequest$1 } from '@effect/platform'; | ||
| import * as HttpClientError from '@effect/platform/HttpClientError'; | ||
| import * as HttpClientRequest from '@effect/platform/HttpClientRequest'; | ||
| import * as HttpClientResponse from '@effect/platform/HttpClientResponse'; | ||
| import * as Data from 'effect/Data'; | ||
| import * as Effect2 from 'effect/Effect'; | ||
| import * as S from 'effect/Schema'; | ||
| // src/Server.ts | ||
| // package.json | ||
| var package_default = { | ||
| version: "0.2.1"}; | ||
| var ExpandDocs = McpServer.resource({ | ||
| uri: "expand://about", | ||
| name: "About expand.ai", | ||
| description: "Key links for the expand.ai platform", | ||
| content: Effect.succeed(`# expand.ai | ||
| - Website: https://expand.ai | ||
| - Quickstart: https://expand.ai/docs | ||
| - Dashboard: https://expand.ai/dashboard | ||
| `) | ||
| }); | ||
| (class extends S.Struct({ | ||
| url: S.String | ||
| }) { | ||
| }); | ||
| var IssueTag = class extends S.Literal( | ||
| "Pointer", | ||
| "Unexpected", | ||
| "Missing", | ||
| "Composite", | ||
| "Refinement", | ||
| "Transformation", | ||
| "Type", | ||
| "Forbidden" | ||
| ) { | ||
| }; | ||
| var PropertyKeyEnumTag = class extends S.Literal("symbol") { | ||
| }; | ||
| var PropertyKey = class extends S.Union( | ||
| S.String, | ||
| S.Number, | ||
| /** | ||
| * an object to be decoded into a globally shared symbol | ||
| */ | ||
| S.Struct({ | ||
| _tag: PropertyKeyEnumTag, | ||
| key: S.String | ||
| }) | ||
| ) { | ||
| }; | ||
| var Issue = class extends S.Class("Issue")({ | ||
| /** | ||
| * The tag identifying the type of parse issue | ||
| */ | ||
| _tag: IssueTag, | ||
| /** | ||
| * The path to the property where the issue occurred | ||
| */ | ||
| path: S.Array(PropertyKey), | ||
| /** | ||
| * A descriptive message explaining the issue | ||
| */ | ||
| message: S.String | ||
| }) { | ||
| }; | ||
| var HttpApiDecodeErrorTag = class extends S.Literal("HttpApiDecodeError") { | ||
| }; | ||
| var HttpApiDecodeError = class extends S.Class("HttpApiDecodeError")({ | ||
| issues: S.Array(Issue), | ||
| message: S.String, | ||
| _tag: HttpApiDecodeErrorTag | ||
| }) { | ||
| }; | ||
| var AuthFailedReason = class extends S.Literal("InvalidApiKey", "InvalidToken", "InvalidSession", "InvalidTenant") { | ||
| }; | ||
| var AuthFailedTag = class extends S.Literal("AuthFailed") { | ||
| }; | ||
| var AuthFailed = class extends S.Class("AuthFailed")({ | ||
| reason: AuthFailedReason, | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| _tag: AuthFailedTag | ||
| }) { | ||
| }; | ||
| var AssetNotFoundTag = class extends S.Literal("AssetNotFound") { | ||
| }; | ||
| var AssetNotFound = class extends S.Class("AssetNotFound")({ | ||
| browserSessionId: S.optionalWith(S.String, { nullable: true }), | ||
| filename: S.String, | ||
| _tag: AssetNotFoundTag | ||
| }) { | ||
| }; | ||
| var AssetNotInWaczTag = class extends S.Literal("AssetNotInWacz") { | ||
| }; | ||
| var AssetNotInWacz = class extends S.Class("AssetNotInWacz")({ | ||
| url: S.String, | ||
| _tag: AssetNotInWaczTag | ||
| }) { | ||
| }; | ||
| var AssetsGetByUrl404 = class extends S.Union(AssetNotFound, AssetNotInWacz) { | ||
| }; | ||
| var AssetReadErrorTag = class extends S.Literal("AssetReadError") { | ||
| }; | ||
| var AssetReadError = class extends S.Class("AssetReadError")({ | ||
| browserSessionId: S.optionalWith(S.String, { nullable: true }), | ||
| filename: S.String, | ||
| _tag: AssetReadErrorTag | ||
| }) { | ||
| }; | ||
| var InvalidWaczFormatTag = class extends S.Literal("InvalidWaczFormat") { | ||
| }; | ||
| var InvalidWaczFormat = class extends S.Class("InvalidWaczFormat")({ | ||
| message: S.String, | ||
| _tag: InvalidWaczFormatTag | ||
| }) { | ||
| }; | ||
| var AssetRetrievalErrorTag = class extends S.Literal("AssetRetrievalError") { | ||
| }; | ||
| var AssetRetrievalError = class extends S.Class("AssetRetrievalError")({ | ||
| browserSessionId: S.String, | ||
| url: S.String, | ||
| _tag: AssetRetrievalErrorTag | ||
| }) { | ||
| }; | ||
| var InternalErrorTag = class extends S.Literal("InternalError") { | ||
| }; | ||
| var InternalError = class extends S.Class("InternalError")({ | ||
| _tag: InternalErrorTag | ||
| }) { | ||
| }; | ||
| var AssetsGetByUrl500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat, AssetRetrievalError) { | ||
| }; | ||
| var CurlErrorInputMethod = class extends S.Literal( | ||
| "GET", | ||
| "POST", | ||
| "HEAD", | ||
| "PUT", | ||
| "DELETE", | ||
| "CONNECT", | ||
| "OPTIONS", | ||
| "TRACE", | ||
| "PATCH" | ||
| ) { | ||
| }; | ||
| var URL = class extends S.String { | ||
| }; | ||
| var CurlErrorTag = class extends S.Literal("CurlError") { | ||
| }; | ||
| var CurlError = class extends S.Class("CurlError")({ | ||
| status: S.Number, | ||
| input: S.Struct({ | ||
| method: CurlErrorInputMethod, | ||
| url: S.String, | ||
| body: S.optionalWith(S.String, { nullable: true }), | ||
| headers: S.optionalWith(S.Struct({}), { nullable: true }), | ||
| proxy: S.optionalWith( | ||
| S.Union( | ||
| S.Struct({ | ||
| server: S.String, | ||
| username: S.optionalWith(S.String, { nullable: true }), | ||
| password: S.optionalWith(S.String, { nullable: true }) | ||
| }), | ||
| URL | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| timeout: S.optionalWith(S.Number, { nullable: true }), | ||
| connectTimeout: S.optionalWith(S.Number, { nullable: true }) | ||
| }), | ||
| _tag: CurlErrorTag | ||
| }) { | ||
| }; | ||
| var AssetListItem = class extends S.Class("AssetListItem")({ | ||
| url: S.String, | ||
| mime: S.String, | ||
| status: S.Number | ||
| }) { | ||
| }; | ||
| var AssetListResponse = class extends S.Class("AssetListResponse")({ | ||
| browserSessionId: S.String, | ||
| assets: S.Array(AssetListItem) | ||
| }) { | ||
| }; | ||
| var AssetsList500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat) { | ||
| }; | ||
| var AssetsGetWacz500 = class extends S.Union(InternalError, AssetReadError) { | ||
| }; | ||
| var DatasetNotFoundTag = class extends S.Literal("DatasetNotFound") { | ||
| }; | ||
| var DatasetNotFound = class extends S.Class("DatasetNotFound")({ | ||
| path: S.String, | ||
| _tag: DatasetNotFoundTag | ||
| }) { | ||
| }; | ||
| var DatasetsGetDataset500 = class extends S.Union(InternalError, DatasetNotFound) { | ||
| }; | ||
| var DatasetsGetFinetuneDataset500 = class extends S.Union(InternalError, DatasetNotFound) { | ||
| }; | ||
| var SelectHtmlConfig = class extends S.Record({ key: S.String, value: S.Unknown }) { | ||
| }; | ||
| var SelectMarkdownConfig = class extends S.Record({ key: S.String, value: S.Unknown }) { | ||
| }; | ||
| var SelectScreenshotConfig = class extends S.Class("SelectScreenshotConfig")({ | ||
| /** | ||
| * Whether to capture the full page including content below the fold | ||
| */ | ||
| fullPage: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var Trimmed = class extends S.String.pipe(S.pattern(/^\S[\s\S]*\S$|^\S$|^$/)) { | ||
| }; | ||
| var Int2 = class extends S.Int { | ||
| }; | ||
| (class extends S.Class("FetchRequest")({ | ||
| url: URL, | ||
| /** | ||
| * Specifies which content formats to include in the response | ||
| */ | ||
| select: S.optionalWith( | ||
| S.Struct({ | ||
| html: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectHtmlConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include markdown-formatted content in the response | ||
| */ | ||
| markdown: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectMarkdownConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| screenshot: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectScreenshotConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| summary: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for AI-powered page summarization | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Custom prompt for AI summarization (max 5000 characters) | ||
| */ | ||
| prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), { | ||
| nullable: true, | ||
| 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." | ||
| }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Options for extracting relevant snippets from page content using semantic search | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Query to find relevant content snippets from the page (required, non-empty) | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Maximum number of snippets to return (1-50) | ||
| */ | ||
| maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). Snippets below this score are filtered out. | ||
| */ | ||
| minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), { | ||
| nullable: true, | ||
| default: () => 0.5 | ||
| }), | ||
| /** | ||
| * Target snippet size in characters (100-2000) | ||
| */ | ||
| targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| links: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for customizing link extraction from the page | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Only include links from the same domain as the fetched URL | ||
| */ | ||
| sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Regex patterns - only include links matching at least one pattern | ||
| */ | ||
| includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }), | ||
| /** | ||
| * Regex patterns - exclude links matching any pattern | ||
| */ | ||
| excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include page metadata in the response | ||
| */ | ||
| meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Configure response info options (headers inclusion) | ||
| */ | ||
| response: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to include HTTP response headers | ||
| */ | ||
| includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include pruned JSON in the response (opt-in) | ||
| */ | ||
| json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }), | ||
| /** | ||
| * Set to true to include extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Configuration options for browser behavior during the fetch | ||
| */ | ||
| browserConfig: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to scroll the entire page to capture lazy-loaded content | ||
| */ | ||
| scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ) | ||
| }) { | ||
| }); | ||
| var ResponseInfo = class extends S.Class("ResponseInfo")({ | ||
| /** | ||
| * The URL that was fetched | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * HTTP status code of the response | ||
| */ | ||
| statusCode: S.Number, | ||
| /** | ||
| * Response headers from the fetch operation (keys are lower-cased HTTP header names) | ||
| */ | ||
| headers: S.optionalWith(S.Struct({}), { nullable: true }) | ||
| }) { | ||
| }; | ||
| var IconMeta = class extends S.Class("IconMeta")({ | ||
| /** | ||
| * Icon URL or path | ||
| */ | ||
| href: S.String, | ||
| /** | ||
| * Link relationship type | ||
| */ | ||
| rel: S.String, | ||
| /** | ||
| * MIME type of the icon | ||
| */ | ||
| type: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Icon dimensions | ||
| */ | ||
| sizes: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var OgImage = class extends S.Class("OgImage")({ | ||
| /** | ||
| * Image URL | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * Image width in pixels | ||
| */ | ||
| width: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Image height in pixels | ||
| */ | ||
| height: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Image alt text for accessibility | ||
| */ | ||
| alt: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var OpenGraphMeta = class extends S.Class("OpenGraphMeta")({ | ||
| /** | ||
| * Open Graph title (og:title) | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph description (og:description) | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph type (og:type) - website, article, product, etc. | ||
| */ | ||
| type: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Canonical URL for the content (og:url) | ||
| */ | ||
| url: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Site name (og:site_name) | ||
| */ | ||
| siteName: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Locale in language_TERRITORY format (og:locale) | ||
| */ | ||
| locale: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph images (og:image and related properties) | ||
| */ | ||
| images: S.optionalWith(S.Array(OgImage), { nullable: true }) | ||
| }) { | ||
| }; | ||
| var TwitterCardMeta = class extends S.Class("TwitterCardMeta")({ | ||
| /** | ||
| * Twitter card type (twitter:card) | ||
| */ | ||
| card: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Twitter @username of the website (twitter:site) | ||
| */ | ||
| site: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Twitter @username of content creator (twitter:creator) | ||
| */ | ||
| creator: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Title for Twitter card (twitter:title) | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Description for Twitter card (twitter:description) | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Image URL for Twitter card (twitter:image) | ||
| */ | ||
| image: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var PageMeta = class extends S.Class("PageMeta")({ | ||
| /** | ||
| * Page title from <title> tag | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Meta description from <meta name="description"> | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Canonical URL from <link rel="canonical"> | ||
| */ | ||
| canonicalUrl: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Page language from <html lang="..."> | ||
| */ | ||
| language: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Character encoding from <meta charset="..."> | ||
| */ | ||
| charset: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Primary favicon URL (first icon found) | ||
| */ | ||
| favicon: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * All icon links (favicons, apple-touch-icons, etc.) | ||
| */ | ||
| icons: S.optionalWith(S.Array(IconMeta), { nullable: true }), | ||
| /** | ||
| * Open Graph metadata from og:* meta tags | ||
| */ | ||
| openGraph: S.optionalWith(OpenGraphMeta, { nullable: true }), | ||
| /** | ||
| * Twitter Card metadata from twitter:* meta tags | ||
| */ | ||
| twitter: S.optionalWith(TwitterCardMeta, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var FetchLink = class extends S.Class("FetchLink")({ | ||
| /** | ||
| * The URL of the link | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * The anchor text of the link | ||
| */ | ||
| text: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var FetchResult = class extends S.Class("FetchResult")({ | ||
| /** | ||
| * Contains the extracted content in the formats specified by the select configuration | ||
| */ | ||
| data: S.Struct({ | ||
| response: ResponseInfo, | ||
| meta: S.optionalWith(PageMeta, { nullable: true }), | ||
| /** | ||
| * The HTML content of the fetched page | ||
| */ | ||
| html: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * The markdown-formatted content extracted from the page | ||
| */ | ||
| markdown: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Base64-encoded data URI of the screenshot image | ||
| */ | ||
| screenshot: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * AI-generated summary of the page content | ||
| */ | ||
| summary: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Relevant snippets extracted from the page based on the search query | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Array( | ||
| S.Struct({ | ||
| /** | ||
| * Type identifier for TextPart compatibility | ||
| */ | ||
| type: S.optionalWith(S.Literal("text"), { nullable: true, default: () => "text" }), | ||
| /** | ||
| * The text content of the snippet | ||
| */ | ||
| text: S.String, | ||
| /** | ||
| * Relevance score from the reranker (0-1) | ||
| */ | ||
| score: S.Number, | ||
| /** | ||
| * Original chunk index | ||
| */ | ||
| index: S.Number | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Links extracted from the page | ||
| */ | ||
| links: S.optionalWith(S.Array(FetchLink), { nullable: true }), | ||
| /** | ||
| * Extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.String, { nullable: true }) | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchErrorTag = class extends S.Literal("FetchError") { | ||
| }; | ||
| var FetchError = class extends S.Class("FetchError")({ | ||
| _tag: FetchErrorTag | ||
| }) { | ||
| }; | ||
| var Fetch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var InvalidAssetHashTag = class extends S.Literal("InvalidAssetHash") { | ||
| }; | ||
| var InvalidAssetHash = class extends S.Class("InvalidAssetHash")({ | ||
| message: S.String, | ||
| _tag: InvalidAssetHashTag | ||
| }) { | ||
| }; | ||
| var AssetNotFoundInHarTag = class extends S.Literal("AssetNotFoundInHar") { | ||
| }; | ||
| var AssetNotFoundInHar = class extends S.Class("AssetNotFoundInHar")({ | ||
| snapshotDir: S.String, | ||
| assetUrl: S.String, | ||
| _tag: AssetNotFoundInHarTag | ||
| }) { | ||
| }; | ||
| var HarNotFoundTag = class extends S.Literal("HarNotFound") { | ||
| }; | ||
| var HarNotFound = class extends S.Class("HarNotFound")({ | ||
| snapshotDir: S.String, | ||
| _tag: HarNotFoundTag | ||
| }) { | ||
| }; | ||
| var AssetFetchErrorTag = class extends S.Literal("AssetFetchError") { | ||
| }; | ||
| var AssetFetchError = class extends S.Class("AssetFetchError")({ | ||
| snapshotDir: S.String, | ||
| assetUrl: S.String, | ||
| _tag: AssetFetchErrorTag | ||
| }) { | ||
| }; | ||
| var LocalAssetsGetAsset500 = class extends S.Union( | ||
| InternalError, | ||
| InvalidAssetHash, | ||
| AssetNotFoundInHar, | ||
| HarNotFound, | ||
| AssetFetchError | ||
| ) { | ||
| }; | ||
| (class extends S.Class("PlaygroundFetchRequest")({ | ||
| url: URL, | ||
| /** | ||
| * Specifies which content formats to include in the response | ||
| */ | ||
| select: S.optionalWith( | ||
| S.Struct({ | ||
| html: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectHtmlConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include markdown-formatted content in the response | ||
| */ | ||
| markdown: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectMarkdownConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| screenshot: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectScreenshotConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| summary: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for AI-powered page summarization | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Custom prompt for AI summarization (max 5000 characters) | ||
| */ | ||
| prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), { | ||
| nullable: true, | ||
| 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." | ||
| }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Options for extracting relevant snippets from page content using semantic search | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Query to find relevant content snippets from the page (required, non-empty) | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Maximum number of snippets to return (1-50) | ||
| */ | ||
| maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). Snippets below this score are filtered out. | ||
| */ | ||
| minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), { | ||
| nullable: true, | ||
| default: () => 0.5 | ||
| }), | ||
| /** | ||
| * Target snippet size in characters (100-2000) | ||
| */ | ||
| targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| links: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for customizing link extraction from the page | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Only include links from the same domain as the fetched URL | ||
| */ | ||
| sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Regex patterns - only include links matching at least one pattern | ||
| */ | ||
| includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }), | ||
| /** | ||
| * Regex patterns - exclude links matching any pattern | ||
| */ | ||
| excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include page metadata in the response | ||
| */ | ||
| meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Configure response info options (headers inclusion) | ||
| */ | ||
| response: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to include HTTP response headers | ||
| */ | ||
| includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include pruned JSON in the response (opt-in) | ||
| */ | ||
| json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }), | ||
| /** | ||
| * Set to true to include extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Configuration options for browser behavior during the fetch | ||
| */ | ||
| browserConfig: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to scroll the entire page to capture lazy-loaded content | ||
| */ | ||
| scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ) | ||
| }) { | ||
| }); | ||
| var PlaygroundData = class extends S.Class("PlaygroundData")({ | ||
| response: ResponseInfo, | ||
| /** | ||
| * HTML with expand-id attributes on elements | ||
| */ | ||
| html: S.String, | ||
| browserSessionId: S.String | ||
| }) { | ||
| }; | ||
| var PlaygroundResult = class extends S.Class("PlaygroundResult")({ | ||
| data: PlaygroundData | ||
| }) { | ||
| }; | ||
| var PlaygroundFetch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var PlaygroundGet500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var PlaygroundGetBySession500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| (class extends S.Class("PlaygroundSearchRequest")({ | ||
| /** | ||
| * Search query to find relevant content chunks | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Browser session ID from the playground fetch result | ||
| */ | ||
| browserSessionId: S.String, | ||
| /** | ||
| * Maximum number of results to return | ||
| */ | ||
| maxResults: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). A value of 0 disables filtering. | ||
| */ | ||
| minScore: S.optionalWith(S.Number, { nullable: true }) | ||
| }) { | ||
| }); | ||
| var PlaygroundSearchChunk = class extends S.Class("PlaygroundSearchChunk")({ | ||
| /** | ||
| * The chunk text content | ||
| */ | ||
| text: S.String, | ||
| /** | ||
| * Relevance score from 0-1 | ||
| */ | ||
| score: S.Number, | ||
| /** | ||
| * Original chunk index | ||
| */ | ||
| index: S.Number, | ||
| /** | ||
| * MDAST originalNodeIds for highlighting in HTML/markdown views | ||
| */ | ||
| nodeIds: S.Array(S.Number) | ||
| }) { | ||
| }; | ||
| var PlaygroundSearch200 = class extends S.Struct({ | ||
| /** | ||
| * Scored and ranked content chunks | ||
| */ | ||
| chunks: S.Array(PlaygroundSearchChunk), | ||
| /** | ||
| * Duration of the search operation in milliseconds | ||
| */ | ||
| durationMs: S.optionalWith(S.Number, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var PlaygroundSearch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var make = (httpClient, options = {}) => { | ||
| const unexpectedStatus = (response) => Effect2.flatMap( | ||
| Effect2.orElseSucceed(response.json, () => "Unexpected status code"), | ||
| (description) => Effect2.fail( | ||
| new HttpClientError.ResponseError({ | ||
| request: response.request, | ||
| response, | ||
| reason: "StatusCode", | ||
| description: typeof description === "string" ? description : JSON.stringify(description) | ||
| }) | ||
| ) | ||
| ); | ||
| const withResponse = options.transformClient ? (f) => (request) => Effect2.flatMap( | ||
| Effect2.flatMap(options.transformClient(httpClient), (client) => client.execute(request)), | ||
| f | ||
| ) : (f) => (request) => Effect2.flatMap(httpClient.execute(request), f); | ||
| const decodeSuccess = (schema) => (response) => HttpClientResponse.schemaBodyJson(schema)(response); | ||
| const decodeError = (tag, schema) => (response) => Effect2.flatMap( | ||
| HttpClientResponse.schemaBodyJson(schema)(response), | ||
| (cause) => Effect2.fail(ClientError(tag, cause, response)) | ||
| ); | ||
| return { | ||
| httpClient, | ||
| assetsGetByUrl: (browserSessionId, options2) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/asset`).pipe( | ||
| HttpClientRequest.setUrlParams({ url: options2?.["url"] }), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetsGetByUrl404", AssetsGetByUrl404), | ||
| "500": decodeError("AssetsGetByUrl500", AssetsGetByUrl500), | ||
| "502": decodeError("CurlError", CurlError), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| assetsList: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/list`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(AssetListResponse), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetNotFound", AssetNotFound), | ||
| "500": decodeError("AssetsList500", AssetsList500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| assetsGetWacz: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetNotFound", AssetNotFound), | ||
| "500": decodeError("AssetsGetWacz500", AssetsGetWacz500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| datasetsGetDataset: () => HttpClientRequest.get(`/datasets/page-analysis`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("DatasetsGetDataset500", DatasetsGetDataset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| datasetsGetFinetuneDataset: () => HttpClientRequest.get(`/datasets/finetune`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("DatasetsGetFinetuneDataset500", DatasetsGetFinetuneDataset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| fetch: (options2) => HttpClientRequest.post(`/v1/fetch`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(FetchResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("Fetch500", Fetch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| localAssetsGetAsset: (assetHash) => HttpClientRequest.get(`/local-asset/${assetHash}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("LocalAssetsGetAsset500", LocalAssetsGetAsset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundFetch: (options2) => HttpClientRequest.post(`/v1/playground/fetch`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundFetch500", PlaygroundFetch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundGet: (fetchRequestId) => HttpClientRequest.get(`/v1/playground/get/${fetchRequestId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundGet500", PlaygroundGet500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundGetBySession: (browserSessionId) => HttpClientRequest.get(`/v1/playground/session/${browserSessionId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundGetBySession500", PlaygroundGetBySession500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundSearch: (options2) => HttpClientRequest.post(`/v1/playground/search`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundSearch200), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundSearch500", PlaygroundSearch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ) | ||
| }; | ||
| }; | ||
| var ClientErrorImpl = class extends Data.Error { | ||
| }; | ||
| var ClientError = (tag, cause, response) => new ClientErrorImpl({ | ||
| _tag: tag, | ||
| cause, | ||
| response, | ||
| request: response.request | ||
| }); | ||
| // src/ExpandClient.ts | ||
| var ExpandConfig = Config.all({ | ||
| apiKey: Config.option(Config.redacted("EXPAND_API_KEY")), | ||
| baseUrl: Config.withDefault(Config.string("EXPAND_BASE_URL"), "https://api.expand.ai") | ||
| }); | ||
| var ExpandClient = class extends Effect.Service()("ExpandClient", { | ||
| dependencies: [], | ||
| scoped: Effect.gen(function* () { | ||
| const config = yield* ExpandConfig; | ||
| const httpClient = yield* HttpClient.HttpClient; | ||
| const requestHeaders = yield* Effect.serviceOption(CurrentRequestHeaders); | ||
| yield* Effect.logInfo(`ExpandClient connected to ${config.baseUrl}`); | ||
| return make(httpClient, { | ||
| transformClient: (client) => Effect.succeed( | ||
| client.pipe( | ||
| HttpClient.mapRequest(HttpClientRequest$1.prependUrl(config.baseUrl)), | ||
| HttpClient.mapRequest((req) => { | ||
| if (Option.isSome(requestHeaders)) { | ||
| const auth = requestHeaders.value["authorization"]; | ||
| if (auth) { | ||
| return HttpClientRequest$1.setHeader("authorization", auth)(req); | ||
| } | ||
| } | ||
| if (Option.isSome(config.apiKey)) { | ||
| return HttpClientRequest$1.setHeader("x-expand-api-key", Redacted.value(config.apiKey.value))(req); | ||
| } | ||
| return req; | ||
| }) | ||
| ) | ||
| ) | ||
| }); | ||
| }) | ||
| }) { | ||
| }; | ||
| var SearchParams = class extends Schema.Class("SearchParams")({ | ||
| query: Schema.String.annotations({ | ||
| description: "Query to find relevant content snippets" | ||
| }), | ||
| minScore: Schema.optionalWith(Schema.Number.pipe(Schema.greaterThanOrEqualTo(0), Schema.lessThanOrEqualTo(1)), { | ||
| default: () => 0.6 | ||
| }).annotations({ | ||
| description: "Minimum relevance score (0-1)", | ||
| default: 0.6 | ||
| }), | ||
| maxResults: Schema.optionalWith(Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50)), { | ||
| default: () => 5 | ||
| }).annotations({ | ||
| description: "Maximum snippets to return", | ||
| default: 5 | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchParams = class extends Schema.Class("FetchParams")({ | ||
| url: Schema.String.annotations({ | ||
| description: "The URL to fetch content from" | ||
| }), | ||
| includeMeta: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include page meta tags (title, description, Open Graph)", | ||
| default: false | ||
| }), | ||
| includeAppendix: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include extracted links and sidebar content", | ||
| default: false | ||
| }), | ||
| includeJson: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include JSON extracted from network responses and DOM", | ||
| default: false | ||
| }), | ||
| search: Schema.optional(SearchParams).annotations({ | ||
| description: "Semantic search. When provided, returns snippets instead of markdown." | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchTool = Tool.make("fetch", { | ||
| description: `Fetch and extract content from any URL. | ||
| Returns markdown by default. When \`search\` is provided, returns semantically relevant snippets instead.`, | ||
| parameters: FetchParams.fields | ||
| }).annotate(Tool.Readonly, true).annotate(Tool.Destructive, false); | ||
| var Fetch = class extends Effect.Service()("Fetch", { | ||
| dependencies: [ExpandClient.Default], | ||
| scoped: Effect.gen(function* () { | ||
| const client = yield* ExpandClient; | ||
| const fetch = Effect.fn("Fetch.fetch")(function* ({ | ||
| url, | ||
| includeMeta, | ||
| includeAppendix, | ||
| includeJson, | ||
| search | ||
| }) { | ||
| const hasSearch = search !== void 0; | ||
| yield* Effect.logDebug(`Fetching: ${url}`).pipe( | ||
| Effect.annotateLogs({ hasSearch, includeMeta, includeAppendix, includeJson }) | ||
| ); | ||
| const result = yield* client.fetch({ | ||
| url, | ||
| select: { | ||
| markdown: !hasSearch, | ||
| snippets: hasSearch ? { | ||
| query: search.query, | ||
| maxSnippets: search.maxResults, | ||
| minScore: search.minScore | ||
| } : void 0, | ||
| meta: includeMeta, | ||
| json: includeJson, | ||
| appendix: includeAppendix | ||
| } | ||
| }); | ||
| yield* Effect.logDebug(`Fetched successfully: ${result.data.response.url}`); | ||
| return result.data; | ||
| }, Effect.orDie); | ||
| return { fetch }; | ||
| }) | ||
| }) { | ||
| }; | ||
| // src/Toolkit.ts | ||
| var toolkit = Toolkit.make(FetchTool); | ||
| var ToolkitLayer = toolkit.toLayer( | ||
| Effect.gen(function* () { | ||
| yield* Effect.logInfo("Registering tools: fetch"); | ||
| const fetchService = yield* Fetch; | ||
| return toolkit.of({ | ||
| fetch: fetchService.fetch | ||
| }); | ||
| }) | ||
| ).pipe(Layer.provide(Fetch.Default), Layer.provide(ExpandClient.Default), Layer.provide(NodeHttpClient.layerUndici)); | ||
| var ExpandToolKit = McpServer.toolkit(toolkit).pipe(Layer.provide(ToolkitLayer)); | ||
| // src/Server.ts | ||
| var ServerInfo = { | ||
| name: "expandai-mcp-server", | ||
| version: package_default.version | ||
| }; | ||
| var CurrentRequestHeaders = class extends Context.Tag("CurrentRequestHeaders")() { | ||
| }; | ||
| var makeServerLayer = (options = {}) => options.includeDocs ? Layer.mergeAll(ExpandToolKit, ExpandDocs) : ExpandToolKit; | ||
| export { CurrentRequestHeaders, ServerInfo, makeServerLayer }; |
+1
-1
@@ -42,3 +42,3 @@ #!/usr/bin/env node | ||
| var package_default = { | ||
| version: "0.2.0"}; | ||
| version: "0.2.1"}; | ||
| var ExpandDocs = ai.McpServer.resource({ | ||
@@ -45,0 +45,0 @@ uri: "expand://about", |
+1
-1
| #!/usr/bin/env node | ||
| import { ServerInfo, makeServerLayer } from './chunk-MEO7UHLG.js'; | ||
| import { ServerInfo, makeServerLayer } from './chunk-GFNIR7HL.js'; | ||
| import { McpServer } from '@effect/ai'; | ||
@@ -4,0 +4,0 @@ import { NodeSink, NodeStream, NodeRuntime } from '@effect/platform-node'; |
+1
-1
@@ -43,3 +43,3 @@ 'use strict'; | ||
| var package_default = { | ||
| version: "0.2.0"}; | ||
| version: "0.2.1"}; | ||
| var ExpandDocs = ai.McpServer.resource({ | ||
@@ -46,0 +46,0 @@ uri: "expand://about", |
+1
-1
@@ -1,1 +0,1 @@ | ||
| export { CurrentRequestHeaders, ServerInfo, makeServerLayer } from './chunk-MEO7UHLG.js'; | ||
| export { CurrentRequestHeaders, ServerInfo, makeServerLayer } from './chunk-GFNIR7HL.js'; |
+13
-13
| { | ||
| "name": "@expandai/mcp-server", | ||
| "version": "0.2.0", | ||
| "version": "0.2.1", | ||
| "description": "MCP server for expand.ai - Give AI agents access to the web", | ||
@@ -45,16 +45,16 @@ "type": "module", | ||
| "dependencies": { | ||
| "@effect/ai": "^0.32.1", | ||
| "@effect/platform": "^0.93.6", | ||
| "@effect/platform-node": "^0.103.0", | ||
| "@expandai/sdk": "^0.5.1", | ||
| "effect": "^3.19.11" | ||
| "@effect/ai": "^0.33.2", | ||
| "@effect/platform": "^0.94.5", | ||
| "@effect/platform-node": "^0.104.1", | ||
| "@expandai/sdk": "^0.16.0", | ||
| "effect": "^3.19.18" | ||
| }, | ||
| "devDependencies": { | ||
| "@biomejs/biome": "^2.3.8", | ||
| "@changesets/changelog-github": "^0.5.1", | ||
| "@changesets/cli": "^2.29.7", | ||
| "@tim-smart/openapi-gen": "^0.4.13", | ||
| "@types/node": "^24.7.0", | ||
| "tsup": "^8.5.0", | ||
| "tsx": "^4.20.6", | ||
| "@biomejs/biome": "^2.4.4", | ||
| "@changesets/changelog-github": "^0.5.2", | ||
| "@changesets/cli": "^2.29.8", | ||
| "@tim-smart/openapi-gen": "^1.0.1", | ||
| "@types/node": "^25.3.0", | ||
| "tsup": "^8.5.1", | ||
| "tsx": "^4.21.0", | ||
| "typescript": "^5.9.3" | ||
@@ -61,0 +61,0 @@ }, |
| import { Effect, Config, Schema, Layer, Context, Option, Redacted } from 'effect'; | ||
| import { McpServer, Tool, Toolkit } from '@effect/ai'; | ||
| import { NodeHttpClient } from '@effect/platform-node'; | ||
| import { HttpClient, HttpClientRequest as HttpClientRequest$1 } from '@effect/platform'; | ||
| import * as HttpClientError from '@effect/platform/HttpClientError'; | ||
| import * as HttpClientRequest from '@effect/platform/HttpClientRequest'; | ||
| import * as HttpClientResponse from '@effect/platform/HttpClientResponse'; | ||
| import * as Data from 'effect/Data'; | ||
| import * as Effect2 from 'effect/Effect'; | ||
| import * as S from 'effect/Schema'; | ||
| // src/Server.ts | ||
| // package.json | ||
| var package_default = { | ||
| version: "0.2.0"}; | ||
| var ExpandDocs = McpServer.resource({ | ||
| uri: "expand://about", | ||
| name: "About expand.ai", | ||
| description: "Key links for the expand.ai platform", | ||
| content: Effect.succeed(`# expand.ai | ||
| - Website: https://expand.ai | ||
| - Quickstart: https://expand.ai/docs | ||
| - Dashboard: https://expand.ai/dashboard | ||
| `) | ||
| }); | ||
| (class extends S.Struct({ | ||
| url: S.String | ||
| }) { | ||
| }); | ||
| var IssueTag = class extends S.Literal( | ||
| "Pointer", | ||
| "Unexpected", | ||
| "Missing", | ||
| "Composite", | ||
| "Refinement", | ||
| "Transformation", | ||
| "Type", | ||
| "Forbidden" | ||
| ) { | ||
| }; | ||
| var PropertyKeyEnumTag = class extends S.Literal("symbol") { | ||
| }; | ||
| var PropertyKey = class extends S.Union( | ||
| S.String, | ||
| S.Number, | ||
| /** | ||
| * an object to be decoded into a globally shared symbol | ||
| */ | ||
| S.Struct({ | ||
| _tag: PropertyKeyEnumTag, | ||
| key: S.String | ||
| }) | ||
| ) { | ||
| }; | ||
| var Issue = class extends S.Class("Issue")({ | ||
| /** | ||
| * The tag identifying the type of parse issue | ||
| */ | ||
| _tag: IssueTag, | ||
| /** | ||
| * The path to the property where the issue occurred | ||
| */ | ||
| path: S.Array(PropertyKey), | ||
| /** | ||
| * A descriptive message explaining the issue | ||
| */ | ||
| message: S.String | ||
| }) { | ||
| }; | ||
| var HttpApiDecodeErrorTag = class extends S.Literal("HttpApiDecodeError") { | ||
| }; | ||
| var HttpApiDecodeError = class extends S.Class("HttpApiDecodeError")({ | ||
| issues: S.Array(Issue), | ||
| message: S.String, | ||
| _tag: HttpApiDecodeErrorTag | ||
| }) { | ||
| }; | ||
| var AuthFailedReason = class extends S.Literal("InvalidApiKey", "InvalidToken", "InvalidSession", "InvalidTenant") { | ||
| }; | ||
| var AuthFailedTag = class extends S.Literal("AuthFailed") { | ||
| }; | ||
| var AuthFailed = class extends S.Class("AuthFailed")({ | ||
| reason: AuthFailedReason, | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| _tag: AuthFailedTag | ||
| }) { | ||
| }; | ||
| var AssetNotFoundTag = class extends S.Literal("AssetNotFound") { | ||
| }; | ||
| var AssetNotFound = class extends S.Class("AssetNotFound")({ | ||
| browserSessionId: S.optionalWith(S.String, { nullable: true }), | ||
| filename: S.String, | ||
| _tag: AssetNotFoundTag | ||
| }) { | ||
| }; | ||
| var AssetNotInWaczTag = class extends S.Literal("AssetNotInWacz") { | ||
| }; | ||
| var AssetNotInWacz = class extends S.Class("AssetNotInWacz")({ | ||
| url: S.String, | ||
| _tag: AssetNotInWaczTag | ||
| }) { | ||
| }; | ||
| var AssetsGetByUrl404 = class extends S.Union(AssetNotFound, AssetNotInWacz) { | ||
| }; | ||
| var AssetReadErrorTag = class extends S.Literal("AssetReadError") { | ||
| }; | ||
| var AssetReadError = class extends S.Class("AssetReadError")({ | ||
| browserSessionId: S.optionalWith(S.String, { nullable: true }), | ||
| filename: S.String, | ||
| _tag: AssetReadErrorTag | ||
| }) { | ||
| }; | ||
| var InvalidWaczFormatTag = class extends S.Literal("InvalidWaczFormat") { | ||
| }; | ||
| var InvalidWaczFormat = class extends S.Class("InvalidWaczFormat")({ | ||
| message: S.String, | ||
| _tag: InvalidWaczFormatTag | ||
| }) { | ||
| }; | ||
| var AssetRetrievalErrorTag = class extends S.Literal("AssetRetrievalError") { | ||
| }; | ||
| var AssetRetrievalError = class extends S.Class("AssetRetrievalError")({ | ||
| browserSessionId: S.String, | ||
| url: S.String, | ||
| _tag: AssetRetrievalErrorTag | ||
| }) { | ||
| }; | ||
| var InternalErrorTag = class extends S.Literal("InternalError") { | ||
| }; | ||
| var InternalError = class extends S.Class("InternalError")({ | ||
| _tag: InternalErrorTag | ||
| }) { | ||
| }; | ||
| var AssetsGetByUrl500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat, AssetRetrievalError) { | ||
| }; | ||
| var CurlErrorInputMethod = class extends S.Literal( | ||
| "GET", | ||
| "POST", | ||
| "HEAD", | ||
| "PUT", | ||
| "DELETE", | ||
| "CONNECT", | ||
| "OPTIONS", | ||
| "TRACE", | ||
| "PATCH" | ||
| ) { | ||
| }; | ||
| var URL = class extends S.String { | ||
| }; | ||
| var CurlErrorTag = class extends S.Literal("CurlError") { | ||
| }; | ||
| var CurlError = class extends S.Class("CurlError")({ | ||
| status: S.Number, | ||
| input: S.Struct({ | ||
| method: CurlErrorInputMethod, | ||
| url: S.String, | ||
| body: S.optionalWith(S.String, { nullable: true }), | ||
| headers: S.optionalWith(S.Struct({}), { nullable: true }), | ||
| proxy: S.optionalWith( | ||
| S.Union( | ||
| S.Struct({ | ||
| server: S.String, | ||
| username: S.optionalWith(S.String, { nullable: true }), | ||
| password: S.optionalWith(S.String, { nullable: true }) | ||
| }), | ||
| URL | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| timeout: S.optionalWith(S.Number, { nullable: true }), | ||
| connectTimeout: S.optionalWith(S.Number, { nullable: true }) | ||
| }), | ||
| _tag: CurlErrorTag | ||
| }) { | ||
| }; | ||
| var AssetListItem = class extends S.Class("AssetListItem")({ | ||
| url: S.String, | ||
| mime: S.String, | ||
| status: S.Number | ||
| }) { | ||
| }; | ||
| var AssetListResponse = class extends S.Class("AssetListResponse")({ | ||
| browserSessionId: S.String, | ||
| assets: S.Array(AssetListItem) | ||
| }) { | ||
| }; | ||
| var AssetsList500 = class extends S.Union(InternalError, AssetReadError, InvalidWaczFormat) { | ||
| }; | ||
| var AssetsGetWacz500 = class extends S.Union(InternalError, AssetReadError) { | ||
| }; | ||
| var DatasetNotFoundTag = class extends S.Literal("DatasetNotFound") { | ||
| }; | ||
| var DatasetNotFound = class extends S.Class("DatasetNotFound")({ | ||
| path: S.String, | ||
| _tag: DatasetNotFoundTag | ||
| }) { | ||
| }; | ||
| var DatasetsGetDataset500 = class extends S.Union(InternalError, DatasetNotFound) { | ||
| }; | ||
| var DatasetsGetFinetuneDataset500 = class extends S.Union(InternalError, DatasetNotFound) { | ||
| }; | ||
| var SelectHtmlConfig = class extends S.Record({ key: S.String, value: S.Unknown }) { | ||
| }; | ||
| var SelectMarkdownConfig = class extends S.Record({ key: S.String, value: S.Unknown }) { | ||
| }; | ||
| var SelectScreenshotConfig = class extends S.Class("SelectScreenshotConfig")({ | ||
| /** | ||
| * Whether to capture the full page including content below the fold | ||
| */ | ||
| fullPage: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var Trimmed = class extends S.String.pipe(S.pattern(/^\S[\s\S]*\S$|^\S$|^$/)) { | ||
| }; | ||
| var Int2 = class extends S.Int { | ||
| }; | ||
| (class extends S.Class("FetchRequest")({ | ||
| url: URL, | ||
| /** | ||
| * Specifies which content formats to include in the response | ||
| */ | ||
| select: S.optionalWith( | ||
| S.Struct({ | ||
| html: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectHtmlConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include markdown-formatted content in the response | ||
| */ | ||
| markdown: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectMarkdownConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| screenshot: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectScreenshotConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| summary: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for AI-powered page summarization | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Custom prompt for AI summarization (max 5000 characters) | ||
| */ | ||
| prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), { | ||
| nullable: true, | ||
| 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." | ||
| }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Options for extracting relevant snippets from page content using semantic search | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Query to find relevant content snippets from the page (required, non-empty) | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Maximum number of snippets to return (1-50) | ||
| */ | ||
| maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). Snippets below this score are filtered out. | ||
| */ | ||
| minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), { | ||
| nullable: true, | ||
| default: () => 0.5 | ||
| }), | ||
| /** | ||
| * Target snippet size in characters (100-2000) | ||
| */ | ||
| targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| links: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for customizing link extraction from the page | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Only include links from the same domain as the fetched URL | ||
| */ | ||
| sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Regex patterns - only include links matching at least one pattern | ||
| */ | ||
| includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }), | ||
| /** | ||
| * Regex patterns - exclude links matching any pattern | ||
| */ | ||
| excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include page metadata in the response | ||
| */ | ||
| meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Configure response info options (headers inclusion) | ||
| */ | ||
| response: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to include HTTP response headers | ||
| */ | ||
| includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include pruned JSON in the response (opt-in) | ||
| */ | ||
| json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }), | ||
| /** | ||
| * Set to true to include extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Configuration options for browser behavior during the fetch | ||
| */ | ||
| browserConfig: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to scroll the entire page to capture lazy-loaded content | ||
| */ | ||
| scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ) | ||
| }) { | ||
| }); | ||
| var ResponseInfo = class extends S.Class("ResponseInfo")({ | ||
| /** | ||
| * The URL that was fetched | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * HTTP status code of the response | ||
| */ | ||
| statusCode: S.Number, | ||
| /** | ||
| * Response headers from the fetch operation (keys are lower-cased HTTP header names) | ||
| */ | ||
| headers: S.optionalWith(S.Struct({}), { nullable: true }) | ||
| }) { | ||
| }; | ||
| var IconMeta = class extends S.Class("IconMeta")({ | ||
| /** | ||
| * Icon URL or path | ||
| */ | ||
| href: S.String, | ||
| /** | ||
| * Link relationship type | ||
| */ | ||
| rel: S.String, | ||
| /** | ||
| * MIME type of the icon | ||
| */ | ||
| type: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Icon dimensions | ||
| */ | ||
| sizes: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var OgImage = class extends S.Class("OgImage")({ | ||
| /** | ||
| * Image URL | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * Image width in pixels | ||
| */ | ||
| width: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Image height in pixels | ||
| */ | ||
| height: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Image alt text for accessibility | ||
| */ | ||
| alt: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var OpenGraphMeta = class extends S.Class("OpenGraphMeta")({ | ||
| /** | ||
| * Open Graph title (og:title) | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph description (og:description) | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph type (og:type) - website, article, product, etc. | ||
| */ | ||
| type: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Canonical URL for the content (og:url) | ||
| */ | ||
| url: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Site name (og:site_name) | ||
| */ | ||
| siteName: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Locale in language_TERRITORY format (og:locale) | ||
| */ | ||
| locale: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Open Graph images (og:image and related properties) | ||
| */ | ||
| images: S.optionalWith(S.Array(OgImage), { nullable: true }) | ||
| }) { | ||
| }; | ||
| var TwitterCardMeta = class extends S.Class("TwitterCardMeta")({ | ||
| /** | ||
| * Twitter card type (twitter:card) | ||
| */ | ||
| card: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Twitter @username of the website (twitter:site) | ||
| */ | ||
| site: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Twitter @username of content creator (twitter:creator) | ||
| */ | ||
| creator: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Title for Twitter card (twitter:title) | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Description for Twitter card (twitter:description) | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Image URL for Twitter card (twitter:image) | ||
| */ | ||
| image: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var PageMeta = class extends S.Class("PageMeta")({ | ||
| /** | ||
| * Page title from <title> tag | ||
| */ | ||
| title: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Meta description from <meta name="description"> | ||
| */ | ||
| description: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Canonical URL from <link rel="canonical"> | ||
| */ | ||
| canonicalUrl: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Page language from <html lang="..."> | ||
| */ | ||
| language: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Character encoding from <meta charset="..."> | ||
| */ | ||
| charset: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Primary favicon URL (first icon found) | ||
| */ | ||
| favicon: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * All icon links (favicons, apple-touch-icons, etc.) | ||
| */ | ||
| icons: S.optionalWith(S.Array(IconMeta), { nullable: true }), | ||
| /** | ||
| * Open Graph metadata from og:* meta tags | ||
| */ | ||
| openGraph: S.optionalWith(OpenGraphMeta, { nullable: true }), | ||
| /** | ||
| * Twitter Card metadata from twitter:* meta tags | ||
| */ | ||
| twitter: S.optionalWith(TwitterCardMeta, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var FetchLink = class extends S.Class("FetchLink")({ | ||
| /** | ||
| * The URL of the link | ||
| */ | ||
| url: S.String, | ||
| /** | ||
| * The anchor text of the link | ||
| */ | ||
| text: S.optionalWith(S.String, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var FetchResult = class extends S.Class("FetchResult")({ | ||
| /** | ||
| * Contains the extracted content in the formats specified by the select configuration | ||
| */ | ||
| data: S.Struct({ | ||
| response: ResponseInfo, | ||
| meta: S.optionalWith(PageMeta, { nullable: true }), | ||
| /** | ||
| * The HTML content of the fetched page | ||
| */ | ||
| html: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * The markdown-formatted content extracted from the page | ||
| */ | ||
| markdown: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Base64-encoded data URI of the screenshot image | ||
| */ | ||
| screenshot: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * AI-generated summary of the page content | ||
| */ | ||
| summary: S.optionalWith(S.String, { nullable: true }), | ||
| /** | ||
| * Relevant snippets extracted from the page based on the search query | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Array( | ||
| S.Struct({ | ||
| /** | ||
| * Type identifier for TextPart compatibility | ||
| */ | ||
| type: S.optionalWith(S.Literal("text"), { nullable: true, default: () => "text" }), | ||
| /** | ||
| * The text content of the snippet | ||
| */ | ||
| text: S.String, | ||
| /** | ||
| * Relevance score from the reranker (0-1) | ||
| */ | ||
| score: S.Number, | ||
| /** | ||
| * Original chunk index | ||
| */ | ||
| index: S.Number | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Links extracted from the page | ||
| */ | ||
| links: S.optionalWith(S.Array(FetchLink), { nullable: true }), | ||
| /** | ||
| * Extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.String, { nullable: true }) | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchErrorTag = class extends S.Literal("FetchError") { | ||
| }; | ||
| var FetchError = class extends S.Class("FetchError")({ | ||
| _tag: FetchErrorTag | ||
| }) { | ||
| }; | ||
| var Fetch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var InvalidAssetHashTag = class extends S.Literal("InvalidAssetHash") { | ||
| }; | ||
| var InvalidAssetHash = class extends S.Class("InvalidAssetHash")({ | ||
| message: S.String, | ||
| _tag: InvalidAssetHashTag | ||
| }) { | ||
| }; | ||
| var AssetNotFoundInHarTag = class extends S.Literal("AssetNotFoundInHar") { | ||
| }; | ||
| var AssetNotFoundInHar = class extends S.Class("AssetNotFoundInHar")({ | ||
| snapshotDir: S.String, | ||
| assetUrl: S.String, | ||
| _tag: AssetNotFoundInHarTag | ||
| }) { | ||
| }; | ||
| var HarNotFoundTag = class extends S.Literal("HarNotFound") { | ||
| }; | ||
| var HarNotFound = class extends S.Class("HarNotFound")({ | ||
| snapshotDir: S.String, | ||
| _tag: HarNotFoundTag | ||
| }) { | ||
| }; | ||
| var AssetFetchErrorTag = class extends S.Literal("AssetFetchError") { | ||
| }; | ||
| var AssetFetchError = class extends S.Class("AssetFetchError")({ | ||
| snapshotDir: S.String, | ||
| assetUrl: S.String, | ||
| _tag: AssetFetchErrorTag | ||
| }) { | ||
| }; | ||
| var LocalAssetsGetAsset500 = class extends S.Union( | ||
| InternalError, | ||
| InvalidAssetHash, | ||
| AssetNotFoundInHar, | ||
| HarNotFound, | ||
| AssetFetchError | ||
| ) { | ||
| }; | ||
| (class extends S.Class("PlaygroundFetchRequest")({ | ||
| url: URL, | ||
| /** | ||
| * Specifies which content formats to include in the response | ||
| */ | ||
| select: S.optionalWith( | ||
| S.Struct({ | ||
| html: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectHtmlConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include markdown-formatted content in the response | ||
| */ | ||
| markdown: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectMarkdownConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| screenshot: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| SelectScreenshotConfig | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| summary: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for AI-powered page summarization | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Custom prompt for AI summarization (max 5000 characters) | ||
| */ | ||
| prompt: S.optionalWith(S.String.pipe(S.maxLength(5e3)), { | ||
| nullable: true, | ||
| 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." | ||
| }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Options for extracting relevant snippets from page content using semantic search | ||
| */ | ||
| snippets: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Query to find relevant content snippets from the page (required, non-empty) | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Maximum number of snippets to return (1-50) | ||
| */ | ||
| maxSnippets: S.optionalWith(Int2, { nullable: true, default: () => 5 }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). Snippets below this score are filtered out. | ||
| */ | ||
| minScore: S.optionalWith(S.Number.pipe(S.greaterThanOrEqualTo(0), S.lessThanOrEqualTo(1)), { | ||
| nullable: true, | ||
| default: () => 0.5 | ||
| }), | ||
| /** | ||
| * Target snippet size in characters (100-2000) | ||
| */ | ||
| targetSnippetSize: S.optionalWith(Int2, { nullable: true, default: () => 384 }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| links: S.optionalWith( | ||
| S.Union( | ||
| S.Boolean, | ||
| /** | ||
| * Options for customizing link extraction from the page | ||
| */ | ||
| S.Struct({ | ||
| /** | ||
| * Only include links from the same domain as the fetched URL | ||
| */ | ||
| sameDomainOnly: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Regex patterns - only include links matching at least one pattern | ||
| */ | ||
| includePatterns: S.optionalWith(S.Array(S.String), { nullable: true }), | ||
| /** | ||
| * Regex patterns - exclude links matching any pattern | ||
| */ | ||
| excludePatterns: S.optionalWith(S.Array(S.String), { nullable: true }) | ||
| }) | ||
| ), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include page metadata in the response | ||
| */ | ||
| meta: S.optionalWith(S.Boolean, { nullable: true, default: () => true }), | ||
| /** | ||
| * Configure response info options (headers inclusion) | ||
| */ | ||
| response: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to include HTTP response headers | ||
| */ | ||
| includeHeaders: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Include pruned JSON in the response (opt-in) | ||
| */ | ||
| json: S.optionalWith(S.Boolean, { nullable: true, default: () => false }), | ||
| /** | ||
| * Set to true to include extracted links and sidebar content | ||
| */ | ||
| appendix: S.optionalWith(S.Boolean, { nullable: true }) | ||
| }), | ||
| { nullable: true } | ||
| ), | ||
| /** | ||
| * Configuration options for browser behavior during the fetch | ||
| */ | ||
| browserConfig: S.optionalWith( | ||
| S.Struct({ | ||
| /** | ||
| * Whether to scroll the entire page to capture lazy-loaded content | ||
| */ | ||
| scrollFullPage: S.optionalWith(S.Boolean, { nullable: true, default: () => false }) | ||
| }), | ||
| { nullable: true } | ||
| ) | ||
| }) { | ||
| }); | ||
| var PlaygroundData = class extends S.Class("PlaygroundData")({ | ||
| response: ResponseInfo, | ||
| /** | ||
| * HTML with expand-id attributes on elements | ||
| */ | ||
| html: S.String, | ||
| browserSessionId: S.String | ||
| }) { | ||
| }; | ||
| var PlaygroundResult = class extends S.Class("PlaygroundResult")({ | ||
| data: PlaygroundData | ||
| }) { | ||
| }; | ||
| var PlaygroundFetch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var PlaygroundGet500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var PlaygroundGetBySession500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| (class extends S.Class("PlaygroundSearchRequest")({ | ||
| /** | ||
| * Search query to find relevant content chunks | ||
| */ | ||
| query: Trimmed, | ||
| /** | ||
| * Browser session ID from the playground fetch result | ||
| */ | ||
| browserSessionId: S.String, | ||
| /** | ||
| * Maximum number of results to return | ||
| */ | ||
| maxResults: S.optionalWith(S.Number, { nullable: true }), | ||
| /** | ||
| * Minimum relevance score threshold (0-1). A value of 0 disables filtering. | ||
| */ | ||
| minScore: S.optionalWith(S.Number, { nullable: true }) | ||
| }) { | ||
| }); | ||
| var PlaygroundSearchChunk = class extends S.Class("PlaygroundSearchChunk")({ | ||
| /** | ||
| * The chunk text content | ||
| */ | ||
| text: S.String, | ||
| /** | ||
| * Relevance score from 0-1 | ||
| */ | ||
| score: S.Number, | ||
| /** | ||
| * Original chunk index | ||
| */ | ||
| index: S.Number, | ||
| /** | ||
| * MDAST originalNodeIds for highlighting in HTML/markdown views | ||
| */ | ||
| nodeIds: S.Array(S.Number) | ||
| }) { | ||
| }; | ||
| var PlaygroundSearch200 = class extends S.Struct({ | ||
| /** | ||
| * Scored and ranked content chunks | ||
| */ | ||
| chunks: S.Array(PlaygroundSearchChunk), | ||
| /** | ||
| * Duration of the search operation in milliseconds | ||
| */ | ||
| durationMs: S.optionalWith(S.Number, { nullable: true }) | ||
| }) { | ||
| }; | ||
| var PlaygroundSearch500 = class extends S.Union(InternalError, FetchError) { | ||
| }; | ||
| var make = (httpClient, options = {}) => { | ||
| const unexpectedStatus = (response) => Effect2.flatMap( | ||
| Effect2.orElseSucceed(response.json, () => "Unexpected status code"), | ||
| (description) => Effect2.fail( | ||
| new HttpClientError.ResponseError({ | ||
| request: response.request, | ||
| response, | ||
| reason: "StatusCode", | ||
| description: typeof description === "string" ? description : JSON.stringify(description) | ||
| }) | ||
| ) | ||
| ); | ||
| const withResponse = options.transformClient ? (f) => (request) => Effect2.flatMap( | ||
| Effect2.flatMap(options.transformClient(httpClient), (client) => client.execute(request)), | ||
| f | ||
| ) : (f) => (request) => Effect2.flatMap(httpClient.execute(request), f); | ||
| const decodeSuccess = (schema) => (response) => HttpClientResponse.schemaBodyJson(schema)(response); | ||
| const decodeError = (tag, schema) => (response) => Effect2.flatMap( | ||
| HttpClientResponse.schemaBodyJson(schema)(response), | ||
| (cause) => Effect2.fail(ClientError(tag, cause, response)) | ||
| ); | ||
| return { | ||
| httpClient, | ||
| assetsGetByUrl: (browserSessionId, options2) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/asset`).pipe( | ||
| HttpClientRequest.setUrlParams({ url: options2?.["url"] }), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetsGetByUrl404", AssetsGetByUrl404), | ||
| "500": decodeError("AssetsGetByUrl500", AssetsGetByUrl500), | ||
| "502": decodeError("CurlError", CurlError), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| assetsList: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}/list`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(AssetListResponse), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetNotFound", AssetNotFound), | ||
| "500": decodeError("AssetsList500", AssetsList500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| assetsGetWacz: (browserSessionId) => HttpClientRequest.get(`/v1/assets/${browserSessionId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "404": decodeError("AssetNotFound", AssetNotFound), | ||
| "500": decodeError("AssetsGetWacz500", AssetsGetWacz500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| datasetsGetDataset: () => HttpClientRequest.get(`/datasets/page-analysis`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("DatasetsGetDataset500", DatasetsGetDataset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| datasetsGetFinetuneDataset: () => HttpClientRequest.get(`/datasets/finetune`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("DatasetsGetFinetuneDataset500", DatasetsGetFinetuneDataset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| fetch: (options2) => HttpClientRequest.post(`/v1/fetch`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(FetchResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("Fetch500", Fetch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| localAssetsGetAsset: (assetHash) => HttpClientRequest.get(`/local-asset/${assetHash}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("LocalAssetsGetAsset500", LocalAssetsGetAsset500), | ||
| "204": () => Effect2.void, | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundFetch: (options2) => HttpClientRequest.post(`/v1/playground/fetch`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundFetch500", PlaygroundFetch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundGet: (fetchRequestId) => HttpClientRequest.get(`/v1/playground/get/${fetchRequestId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundGet500", PlaygroundGet500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundGetBySession: (browserSessionId) => HttpClientRequest.get(`/v1/playground/session/${browserSessionId}`).pipe( | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundResult), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundGetBySession500", PlaygroundGetBySession500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ), | ||
| playgroundSearch: (options2) => HttpClientRequest.post(`/v1/playground/search`).pipe( | ||
| HttpClientRequest.bodyUnsafeJson(options2), | ||
| withResponse( | ||
| HttpClientResponse.matchStatus({ | ||
| "2xx": decodeSuccess(PlaygroundSearch200), | ||
| "400": decodeError("HttpApiDecodeError", HttpApiDecodeError), | ||
| "401": decodeError("AuthFailed", AuthFailed), | ||
| "500": decodeError("PlaygroundSearch500", PlaygroundSearch500), | ||
| "429": () => Effect2.void, | ||
| "503": () => Effect2.void, | ||
| orElse: unexpectedStatus | ||
| }) | ||
| ) | ||
| ) | ||
| }; | ||
| }; | ||
| var ClientErrorImpl = class extends Data.Error { | ||
| }; | ||
| var ClientError = (tag, cause, response) => new ClientErrorImpl({ | ||
| _tag: tag, | ||
| cause, | ||
| response, | ||
| request: response.request | ||
| }); | ||
| // src/ExpandClient.ts | ||
| var ExpandConfig = Config.all({ | ||
| apiKey: Config.option(Config.redacted("EXPAND_API_KEY")), | ||
| baseUrl: Config.withDefault(Config.string("EXPAND_BASE_URL"), "https://api.expand.ai") | ||
| }); | ||
| var ExpandClient = class extends Effect.Service()("ExpandClient", { | ||
| dependencies: [], | ||
| scoped: Effect.gen(function* () { | ||
| const config = yield* ExpandConfig; | ||
| const httpClient = yield* HttpClient.HttpClient; | ||
| const requestHeaders = yield* Effect.serviceOption(CurrentRequestHeaders); | ||
| yield* Effect.logInfo(`ExpandClient connected to ${config.baseUrl}`); | ||
| return make(httpClient, { | ||
| transformClient: (client) => Effect.succeed( | ||
| client.pipe( | ||
| HttpClient.mapRequest(HttpClientRequest$1.prependUrl(config.baseUrl)), | ||
| HttpClient.mapRequest((req) => { | ||
| if (Option.isSome(requestHeaders)) { | ||
| const auth = requestHeaders.value["authorization"]; | ||
| if (auth) { | ||
| return HttpClientRequest$1.setHeader("authorization", auth)(req); | ||
| } | ||
| } | ||
| if (Option.isSome(config.apiKey)) { | ||
| return HttpClientRequest$1.setHeader("x-expand-api-key", Redacted.value(config.apiKey.value))(req); | ||
| } | ||
| return req; | ||
| }) | ||
| ) | ||
| ) | ||
| }); | ||
| }) | ||
| }) { | ||
| }; | ||
| var SearchParams = class extends Schema.Class("SearchParams")({ | ||
| query: Schema.String.annotations({ | ||
| description: "Query to find relevant content snippets" | ||
| }), | ||
| minScore: Schema.optionalWith(Schema.Number.pipe(Schema.greaterThanOrEqualTo(0), Schema.lessThanOrEqualTo(1)), { | ||
| default: () => 0.6 | ||
| }).annotations({ | ||
| description: "Minimum relevance score (0-1)", | ||
| default: 0.6 | ||
| }), | ||
| maxResults: Schema.optionalWith(Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50)), { | ||
| default: () => 5 | ||
| }).annotations({ | ||
| description: "Maximum snippets to return", | ||
| default: 5 | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchParams = class extends Schema.Class("FetchParams")({ | ||
| url: Schema.String.annotations({ | ||
| description: "The URL to fetch content from" | ||
| }), | ||
| includeMeta: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include page meta tags (title, description, Open Graph)", | ||
| default: false | ||
| }), | ||
| includeAppendix: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include extracted links and sidebar content", | ||
| default: false | ||
| }), | ||
| includeJson: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({ | ||
| description: "Include JSON extracted from network responses and DOM", | ||
| default: false | ||
| }), | ||
| search: Schema.optional(SearchParams).annotations({ | ||
| description: "Semantic search. When provided, returns snippets instead of markdown." | ||
| }) | ||
| }) { | ||
| }; | ||
| var FetchTool = Tool.make("fetch", { | ||
| description: `Fetch and extract content from any URL. | ||
| Returns markdown by default. When \`search\` is provided, returns semantically relevant snippets instead.`, | ||
| parameters: FetchParams.fields | ||
| }).annotate(Tool.Readonly, true).annotate(Tool.Destructive, false); | ||
| var Fetch = class extends Effect.Service()("Fetch", { | ||
| dependencies: [ExpandClient.Default], | ||
| scoped: Effect.gen(function* () { | ||
| const client = yield* ExpandClient; | ||
| const fetch = Effect.fn("Fetch.fetch")(function* ({ | ||
| url, | ||
| includeMeta, | ||
| includeAppendix, | ||
| includeJson, | ||
| search | ||
| }) { | ||
| const hasSearch = search !== void 0; | ||
| yield* Effect.logDebug(`Fetching: ${url}`).pipe( | ||
| Effect.annotateLogs({ hasSearch, includeMeta, includeAppendix, includeJson }) | ||
| ); | ||
| const result = yield* client.fetch({ | ||
| url, | ||
| select: { | ||
| markdown: !hasSearch, | ||
| snippets: hasSearch ? { | ||
| query: search.query, | ||
| maxSnippets: search.maxResults, | ||
| minScore: search.minScore | ||
| } : void 0, | ||
| meta: includeMeta, | ||
| json: includeJson, | ||
| appendix: includeAppendix | ||
| } | ||
| }); | ||
| yield* Effect.logDebug(`Fetched successfully: ${result.data.response.url}`); | ||
| return result.data; | ||
| }, Effect.orDie); | ||
| return { fetch }; | ||
| }) | ||
| }) { | ||
| }; | ||
| // src/Toolkit.ts | ||
| var toolkit = Toolkit.make(FetchTool); | ||
| var ToolkitLayer = toolkit.toLayer( | ||
| Effect.gen(function* () { | ||
| yield* Effect.logInfo("Registering tools: fetch"); | ||
| const fetchService = yield* Fetch; | ||
| return toolkit.of({ | ||
| fetch: fetchService.fetch | ||
| }); | ||
| }) | ||
| ).pipe(Layer.provide(Fetch.Default), Layer.provide(ExpandClient.Default), Layer.provide(NodeHttpClient.layerUndici)); | ||
| var ExpandToolKit = McpServer.toolkit(toolkit).pipe(Layer.provide(ToolkitLayer)); | ||
| // src/Server.ts | ||
| var ServerInfo = { | ||
| name: "expandai-mcp-server", | ||
| version: package_default.version | ||
| }; | ||
| var CurrentRequestHeaders = class extends Context.Tag("CurrentRequestHeaders")() { | ||
| }; | ||
| var makeServerLayer = (options = {}) => options.includeDocs ? Layer.mergeAll(ExpandToolKit, ExpandDocs) : ExpandToolKit; | ||
| export { CurrentRequestHeaders, ServerInfo, makeServerLayer }; |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated