@expandai/mcp-server 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/main.ts ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@effect/ai'
3
+ import { NodeRuntime, NodeSink, NodeStream } from '@effect/platform-node'
4
+ import { Config, Effect, Layer, Logger, LogLevel } from 'effect'
5
+ import packageJson from '../package.json' with { type: 'json' }
6
+ import { ExpandDocs } from './resources/ExpandDocs.js'
7
+ import { ExpandTools } from './tools/index.js'
8
+
9
+ const enableDocs = Config.withDefault(Config.boolean('EXPAND_ENABLE_DOCS'), () => false)
10
+
11
+ const program = Effect.gen(function* () {
12
+ const docsEnabled = yield* enableDocs
13
+
14
+ const layers = docsEnabled ? Layer.mergeAll(ExpandTools, ExpandDocs) : ExpandTools
15
+
16
+ yield* McpServer.layerStdio({
17
+ name: 'expandai-mcp-server',
18
+ version: packageJson.version,
19
+ stdin: NodeStream.stdin,
20
+ stdout: NodeSink.stdout,
21
+ }).pipe(Layer.provide(layers), Layer.provide(Logger.minimumLogLevel(LogLevel.None)), Layer.launch)
22
+ })
23
+
24
+ program.pipe(NodeRuntime.runMain)
@@ -0,0 +1,14 @@
1
+ import { McpServer } from '@effect/ai'
2
+ import { Effect } from 'effect'
3
+
4
+ export const ExpandDocs = McpServer.resource({
5
+ uri: 'expand://about',
6
+ name: 'About expand.ai',
7
+ description: 'Key links for the expand.ai platform',
8
+ content: Effect.succeed(`# expand.ai
9
+
10
+ - Website: https://expand.ai
11
+ - Quickstart: https://expand.ai/docs
12
+ - Dashboard: https://expand.ai/dashboard
13
+ `),
14
+ })
@@ -0,0 +1,94 @@
1
+ import { Tool } from '@effect/ai'
2
+ import { Effect, Schema } from 'effect'
3
+ import { ExpandClient } from '../ExpandClient.js'
4
+ import { PageMeta } from '../generated/ExpandClient.js'
5
+
6
+ export class SnippetsFormat extends Schema.Class<SnippetsFormat>('SnippetsFormat')({
7
+ query: Schema.String.annotations({
8
+ description: 'Query to find relevant content snippets from the page',
9
+ }),
10
+ maxSnippets: Schema.optional(
11
+ Schema.Int.pipe(Schema.greaterThanOrEqualTo(1), Schema.lessThanOrEqualTo(50)),
12
+ ).annotations({
13
+ description: 'Maximum number of snippets to return (1-50, default: 5)',
14
+ }),
15
+ targetSnippetSize: Schema.optional(
16
+ Schema.Int.pipe(Schema.greaterThanOrEqualTo(100), Schema.lessThanOrEqualTo(2000)),
17
+ ).annotations({
18
+ description: 'Target snippet size in characters (100-2000, default: 384)',
19
+ }),
20
+ }) {}
21
+
22
+ export const Format = Schema.Union(
23
+ Schema.Literal('markdown').annotations({
24
+ description: 'Return as cleaned markdown content',
25
+ }),
26
+ Schema.Literal('html').annotations({
27
+ description: 'Return as raw HTML content',
28
+ }),
29
+ Schema.Literal('summary').annotations({
30
+ description: 'Return AI-generated summary of the page content',
31
+ }),
32
+ SnippetsFormat,
33
+ ).annotations({
34
+ description: 'Output format for the fetched content',
35
+ })
36
+
37
+ export class FetchParams extends Schema.Class<FetchParams>('FetchParams')({
38
+ url: Schema.String.annotations({
39
+ description: 'The URL to fetch content from',
40
+ }),
41
+ format: Format,
42
+ includeMeta: Schema.Boolean.annotations({
43
+ description: 'Whether to include page meta tags in the response',
44
+ }),
45
+ }) {}
46
+
47
+ export class FetchResult extends Schema.Class<FetchResult>('FetchResult')({
48
+ content: Schema.String,
49
+ url: Schema.String,
50
+ meta: Schema.optional(PageMeta),
51
+ }) {}
52
+
53
+ export const FetchTool = Tool.make('fetch', {
54
+ description: 'Fetch and extract content from any URL.',
55
+ parameters: FetchParams.fields,
56
+ success: FetchResult,
57
+ })
58
+ .annotate(Tool.Readonly, true)
59
+ .annotate(Tool.Destructive, false)
60
+
61
+ export class Fetch extends Effect.Service<Fetch>()('Fetch', {
62
+ dependencies: [ExpandClient.Default],
63
+ scoped: Effect.gen(function* () {
64
+ const client = yield* ExpandClient
65
+
66
+ const fetch = Effect.fn('Fetch.fetch')(function* ({ url, format, includeMeta }: FetchParams) {
67
+ const result = yield* client.fetch({
68
+ url,
69
+ select: {
70
+ markdown: format === 'markdown',
71
+ html: format === 'html',
72
+ summary: format === 'summary',
73
+ snippets: format instanceof SnippetsFormat ? { ...format } : undefined,
74
+ meta: includeMeta,
75
+ },
76
+ })
77
+
78
+ const content =
79
+ result.data.markdown ??
80
+ result.data.html ??
81
+ result.data.summary ??
82
+ result.data.snippets?.map((snippet) => `${snippet.text} (Score: ${snippet.score})`).join('\n') ??
83
+ ''
84
+
85
+ return new FetchResult({
86
+ content,
87
+ url: result.data.response.url,
88
+ meta: result.data.meta,
89
+ })
90
+ }, Effect.orDie)
91
+
92
+ return { fetch } as const
93
+ }),
94
+ }) {}
@@ -0,0 +1,33 @@
1
+ import { McpServer, Toolkit } from '@effect/ai'
2
+ import { NodeHttpClient } from '@effect/platform-node'
3
+ import { Effect, Layer } from 'effect'
4
+ import { ExpandClient } from '../ExpandClient.js'
5
+ import { Fetch, FetchTool } from './Fetch.js'
6
+
7
+ // =============================================================================
8
+ // Toolkit (add more tools here)
9
+ // =============================================================================
10
+
11
+ const toolkit = Toolkit.make(FetchTool)
12
+
13
+ // =============================================================================
14
+ // Toolkit Layer
15
+ // =============================================================================
16
+
17
+ const ToolkitLayer = toolkit
18
+ .toLayer(
19
+ Effect.gen(function* () {
20
+ const fetchService = yield* Fetch
21
+
22
+ return toolkit.of({
23
+ fetch: fetchService.fetch,
24
+ })
25
+ }),
26
+ )
27
+ .pipe(Layer.provide(Fetch.Default), Layer.provide(ExpandClient.Default), Layer.provide(NodeHttpClient.layerUndici))
28
+
29
+ // =============================================================================
30
+ // Export
31
+ // =============================================================================
32
+
33
+ export const ExpandTools = McpServer.toolkit(toolkit).pipe(Layer.provide(ToolkitLayer))