Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@expandai/mcp-server

Package Overview
Dependencies
Maintainers
3
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@expandai/mcp-server - npm Package Compare versions

Comparing version
0.2.0
to
0.2.1
+1157
dist/chunk-GFNIR7HL.js
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",

#!/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';

@@ -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 +0,1 @@

export { CurrentRequestHeaders, ServerInfo, makeServerLayer } from './chunk-MEO7UHLG.js';
export { CurrentRequestHeaders, ServerInfo, makeServerLayer } from './chunk-GFNIR7HL.js';
{
"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 };