@mastra/mcp 0.11.3-alpha.1 → 0.11.3-alpha.3
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/CHANGELOG.md +22 -0
- package/dist/client/configuration.d.ts +1 -1
- package/dist/client/configuration.d.ts.map +1 -1
- package/dist/client/promptActions.d.ts +1 -1
- package/dist/client/promptActions.d.ts.map +1 -1
- package/dist/index.cjs +223 -115
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +225 -118
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts +6 -5
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/types.d.ts +69 -5
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +15 -2
- package/.turbo/turbo-build.log +0 -4
- package/eslint.config.js +0 -11
- package/integration-tests/node_modules/.bin/tsc +0 -21
- package/integration-tests/node_modules/.bin/tsserver +0 -21
- package/integration-tests/node_modules/.bin/vitest +0 -21
- package/integration-tests/package.json +0 -29
- package/integration-tests/src/mastra/agents/weather.ts +0 -34
- package/integration-tests/src/mastra/index.ts +0 -15
- package/integration-tests/src/mastra/mcp/index.ts +0 -46
- package/integration-tests/src/mastra/tools/weather.ts +0 -13
- package/integration-tests/src/server.test.ts +0 -238
- package/integration-tests/tsconfig.json +0 -13
- package/integration-tests/vitest.config.ts +0 -14
- package/src/__fixtures__/fire-crawl-complex-schema.ts +0 -1013
- package/src/__fixtures__/server-weather.ts +0 -16
- package/src/__fixtures__/stock-price.ts +0 -128
- package/src/__fixtures__/tools.ts +0 -94
- package/src/__fixtures__/weather.ts +0 -269
- package/src/client/client.test.ts +0 -585
- package/src/client/client.ts +0 -628
- package/src/client/configuration.test.ts +0 -856
- package/src/client/configuration.ts +0 -468
- package/src/client/elicitationActions.ts +0 -26
- package/src/client/index.ts +0 -3
- package/src/client/promptActions.ts +0 -70
- package/src/client/resourceActions.ts +0 -119
- package/src/index.ts +0 -2
- package/src/server/index.ts +0 -2
- package/src/server/promptActions.ts +0 -48
- package/src/server/resourceActions.ts +0 -90
- package/src/server/server-logging.test.ts +0 -181
- package/src/server/server.test.ts +0 -2142
- package/src/server/server.ts +0 -1445
- package/src/server/types.ts +0 -59
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -8
|
@@ -1,1013 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import type { ToolsInput } from '@mastra/core/agent';
|
|
4
|
-
import FirecrawlApp from '@mendable/firecrawl-js';
|
|
5
|
-
import type { ScrapeParams, MapParams, CrawlParams, FirecrawlDocument } from '@mendable/firecrawl-js';
|
|
6
|
-
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
-
import { MCPServer } from '../server/server';
|
|
8
|
-
|
|
9
|
-
// Tool definitions
|
|
10
|
-
const SCRAPE_TOOL: Tool = {
|
|
11
|
-
name: 'firecrawl_scrape',
|
|
12
|
-
description:
|
|
13
|
-
'Scrape a single webpage with advanced options for content extraction. ' +
|
|
14
|
-
'Supports various formats including markdown, HTML, and screenshots. ' +
|
|
15
|
-
'Can execute custom actions like clicking or scrolling before scraping.',
|
|
16
|
-
inputSchema: {
|
|
17
|
-
type: 'object',
|
|
18
|
-
properties: {
|
|
19
|
-
url: {
|
|
20
|
-
type: 'string',
|
|
21
|
-
description: 'The URL to scrape',
|
|
22
|
-
},
|
|
23
|
-
formats: {
|
|
24
|
-
type: 'array',
|
|
25
|
-
items: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
enum: ['markdown', 'html', 'rawHtml', 'screenshot', 'links', 'screenshot@fullPage', 'extract'],
|
|
28
|
-
},
|
|
29
|
-
default: ['markdown'],
|
|
30
|
-
description: "Content formats to extract (default: ['markdown'])",
|
|
31
|
-
},
|
|
32
|
-
onlyMainContent: {
|
|
33
|
-
type: 'boolean',
|
|
34
|
-
description: 'Extract only the main content, filtering out navigation, footers, etc.',
|
|
35
|
-
},
|
|
36
|
-
includeTags: {
|
|
37
|
-
type: 'array',
|
|
38
|
-
items: { type: 'string' },
|
|
39
|
-
description: 'HTML tags to specifically include in extraction',
|
|
40
|
-
},
|
|
41
|
-
excludeTags: {
|
|
42
|
-
type: 'array',
|
|
43
|
-
items: { type: 'string' },
|
|
44
|
-
description: 'HTML tags to exclude from extraction',
|
|
45
|
-
},
|
|
46
|
-
waitFor: {
|
|
47
|
-
type: 'number',
|
|
48
|
-
description: 'Time in milliseconds to wait for dynamic content to load',
|
|
49
|
-
},
|
|
50
|
-
timeout: {
|
|
51
|
-
type: 'number',
|
|
52
|
-
description: 'Maximum time in milliseconds to wait for the page to load',
|
|
53
|
-
},
|
|
54
|
-
actions: {
|
|
55
|
-
type: 'array',
|
|
56
|
-
items: {
|
|
57
|
-
type: 'object',
|
|
58
|
-
properties: {
|
|
59
|
-
type: {
|
|
60
|
-
type: 'string',
|
|
61
|
-
enum: ['wait', 'click', 'screenshot', 'write', 'press', 'scroll', 'scrape', 'executeJavascript'],
|
|
62
|
-
description: 'Type of action to perform',
|
|
63
|
-
},
|
|
64
|
-
selector: {
|
|
65
|
-
type: 'string',
|
|
66
|
-
description: 'CSS selector for the target element',
|
|
67
|
-
},
|
|
68
|
-
milliseconds: {
|
|
69
|
-
type: 'number',
|
|
70
|
-
description: 'Time to wait in milliseconds (for wait action)',
|
|
71
|
-
},
|
|
72
|
-
text: {
|
|
73
|
-
type: 'string',
|
|
74
|
-
description: 'Text to write (for write action)',
|
|
75
|
-
},
|
|
76
|
-
key: {
|
|
77
|
-
type: 'string',
|
|
78
|
-
description: 'Key to press (for press action)',
|
|
79
|
-
},
|
|
80
|
-
direction: {
|
|
81
|
-
type: 'string',
|
|
82
|
-
enum: ['up', 'down'],
|
|
83
|
-
description: 'Scroll direction',
|
|
84
|
-
},
|
|
85
|
-
script: {
|
|
86
|
-
type: 'string',
|
|
87
|
-
description: 'JavaScript code to execute',
|
|
88
|
-
},
|
|
89
|
-
fullPage: {
|
|
90
|
-
type: 'boolean',
|
|
91
|
-
description: 'Take full page screenshot',
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
required: ['type'],
|
|
95
|
-
},
|
|
96
|
-
description: 'List of actions to perform before scraping',
|
|
97
|
-
},
|
|
98
|
-
extract: {
|
|
99
|
-
type: 'object',
|
|
100
|
-
properties: {
|
|
101
|
-
schema: {
|
|
102
|
-
type: 'object',
|
|
103
|
-
description: 'Schema for structured data extraction',
|
|
104
|
-
},
|
|
105
|
-
systemPrompt: {
|
|
106
|
-
type: 'string',
|
|
107
|
-
description: 'System prompt for LLM extraction',
|
|
108
|
-
},
|
|
109
|
-
prompt: {
|
|
110
|
-
type: 'string',
|
|
111
|
-
description: 'User prompt for LLM extraction',
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
description: 'Configuration for structured data extraction',
|
|
115
|
-
},
|
|
116
|
-
mobile: {
|
|
117
|
-
type: 'boolean',
|
|
118
|
-
description: 'Use mobile viewport',
|
|
119
|
-
},
|
|
120
|
-
skipTlsVerification: {
|
|
121
|
-
type: 'boolean',
|
|
122
|
-
description: 'Skip TLS certificate verification',
|
|
123
|
-
},
|
|
124
|
-
removeBase64Images: {
|
|
125
|
-
type: 'boolean',
|
|
126
|
-
description: 'Remove base64 encoded images from output',
|
|
127
|
-
},
|
|
128
|
-
location: {
|
|
129
|
-
type: 'object',
|
|
130
|
-
properties: {
|
|
131
|
-
country: {
|
|
132
|
-
type: 'string',
|
|
133
|
-
description: 'Country code for geolocation',
|
|
134
|
-
},
|
|
135
|
-
languages: {
|
|
136
|
-
type: 'array',
|
|
137
|
-
items: { type: 'string' },
|
|
138
|
-
description: 'Language codes for content',
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
description: 'Location settings for scraping',
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
required: ['url'],
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const MAP_TOOL: Tool = {
|
|
149
|
-
name: 'firecrawl_map',
|
|
150
|
-
description: 'Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.',
|
|
151
|
-
inputSchema: {
|
|
152
|
-
type: 'object',
|
|
153
|
-
properties: {
|
|
154
|
-
url: {
|
|
155
|
-
type: 'string',
|
|
156
|
-
description: 'Starting URL for URL discovery',
|
|
157
|
-
},
|
|
158
|
-
search: {
|
|
159
|
-
type: 'string',
|
|
160
|
-
description: 'Optional search term to filter URLs',
|
|
161
|
-
},
|
|
162
|
-
ignoreSitemap: {
|
|
163
|
-
type: 'boolean',
|
|
164
|
-
description: 'Skip sitemap.xml discovery and only use HTML links',
|
|
165
|
-
},
|
|
166
|
-
sitemapOnly: {
|
|
167
|
-
type: 'boolean',
|
|
168
|
-
description: 'Only use sitemap.xml for discovery, ignore HTML links',
|
|
169
|
-
},
|
|
170
|
-
includeSubdomains: {
|
|
171
|
-
type: 'boolean',
|
|
172
|
-
description: 'Include URLs from subdomains in results',
|
|
173
|
-
},
|
|
174
|
-
limit: {
|
|
175
|
-
type: 'number',
|
|
176
|
-
description: 'Maximum number of URLs to return',
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
required: ['url'],
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const CRAWL_TOOL: Tool = {
|
|
184
|
-
name: 'firecrawl_crawl',
|
|
185
|
-
description:
|
|
186
|
-
'Start an asynchronous crawl of multiple pages from a starting URL. ' +
|
|
187
|
-
'Supports depth control, path filtering, and webhook notifications.',
|
|
188
|
-
inputSchema: {
|
|
189
|
-
type: 'object',
|
|
190
|
-
properties: {
|
|
191
|
-
url: {
|
|
192
|
-
type: 'string',
|
|
193
|
-
description: 'Starting URL for the crawl',
|
|
194
|
-
},
|
|
195
|
-
excludePaths: {
|
|
196
|
-
type: 'array',
|
|
197
|
-
items: { type: 'string' },
|
|
198
|
-
description: 'URL paths to exclude from crawling',
|
|
199
|
-
},
|
|
200
|
-
includePaths: {
|
|
201
|
-
type: 'array',
|
|
202
|
-
items: { type: 'string' },
|
|
203
|
-
description: 'Only crawl these URL paths',
|
|
204
|
-
},
|
|
205
|
-
maxDepth: {
|
|
206
|
-
type: 'number',
|
|
207
|
-
description: 'Maximum link depth to crawl',
|
|
208
|
-
},
|
|
209
|
-
ignoreSitemap: {
|
|
210
|
-
type: 'boolean',
|
|
211
|
-
description: 'Skip sitemap.xml discovery',
|
|
212
|
-
},
|
|
213
|
-
limit: {
|
|
214
|
-
type: 'number',
|
|
215
|
-
description: 'Maximum number of pages to crawl',
|
|
216
|
-
},
|
|
217
|
-
allowBackwardLinks: {
|
|
218
|
-
type: 'boolean',
|
|
219
|
-
description: 'Allow crawling links that point to parent directories',
|
|
220
|
-
},
|
|
221
|
-
allowExternalLinks: {
|
|
222
|
-
type: 'boolean',
|
|
223
|
-
description: 'Allow crawling links to external domains',
|
|
224
|
-
},
|
|
225
|
-
webhook: {
|
|
226
|
-
oneOf: [
|
|
227
|
-
{
|
|
228
|
-
type: 'string',
|
|
229
|
-
description: 'Webhook URL to notify when crawl is complete',
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
type: 'object',
|
|
233
|
-
properties: {
|
|
234
|
-
url: {
|
|
235
|
-
type: 'string',
|
|
236
|
-
description: 'Webhook URL',
|
|
237
|
-
},
|
|
238
|
-
headers: {
|
|
239
|
-
type: 'object',
|
|
240
|
-
description: 'Custom headers for webhook requests',
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
required: ['url'],
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
deduplicateSimilarURLs: {
|
|
248
|
-
type: 'boolean',
|
|
249
|
-
description: 'Remove similar URLs during crawl',
|
|
250
|
-
},
|
|
251
|
-
ignoreQueryParameters: {
|
|
252
|
-
type: 'boolean',
|
|
253
|
-
description: 'Ignore query parameters when comparing URLs',
|
|
254
|
-
},
|
|
255
|
-
scrapeOptions: {
|
|
256
|
-
type: 'object',
|
|
257
|
-
properties: {
|
|
258
|
-
formats: {
|
|
259
|
-
type: 'array',
|
|
260
|
-
items: {
|
|
261
|
-
type: 'string',
|
|
262
|
-
enum: ['markdown', 'html', 'rawHtml', 'screenshot', 'links', 'screenshot@fullPage', 'extract'],
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
onlyMainContent: {
|
|
266
|
-
type: 'boolean',
|
|
267
|
-
},
|
|
268
|
-
includeTags: {
|
|
269
|
-
type: 'array',
|
|
270
|
-
items: { type: 'string' },
|
|
271
|
-
},
|
|
272
|
-
excludeTags: {
|
|
273
|
-
type: 'array',
|
|
274
|
-
items: { type: 'string' },
|
|
275
|
-
},
|
|
276
|
-
waitFor: {
|
|
277
|
-
type: 'number',
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
description: 'Options for scraping each page',
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
required: ['url'],
|
|
284
|
-
},
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const CHECK_CRAWL_STATUS_TOOL: Tool = {
|
|
288
|
-
name: 'firecrawl_check_crawl_status',
|
|
289
|
-
description: 'Check the status of a crawl job.',
|
|
290
|
-
inputSchema: {
|
|
291
|
-
type: 'object',
|
|
292
|
-
properties: {
|
|
293
|
-
id: {
|
|
294
|
-
type: 'string',
|
|
295
|
-
description: 'Crawl job ID to check',
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
required: ['id'],
|
|
299
|
-
},
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const SEARCH_TOOL: Tool = {
|
|
303
|
-
name: 'firecrawl_search',
|
|
304
|
-
description:
|
|
305
|
-
'Search and retrieve content from web pages with optional scraping. ' +
|
|
306
|
-
'Returns SERP results by default (url, title, description) or full page content when scrapeOptions are provided.',
|
|
307
|
-
inputSchema: {
|
|
308
|
-
type: 'object',
|
|
309
|
-
properties: {
|
|
310
|
-
query: {
|
|
311
|
-
type: 'string',
|
|
312
|
-
description: 'Search query string',
|
|
313
|
-
},
|
|
314
|
-
limit: {
|
|
315
|
-
type: 'number',
|
|
316
|
-
description: 'Maximum number of results to return (default: 5)',
|
|
317
|
-
},
|
|
318
|
-
lang: {
|
|
319
|
-
type: 'string',
|
|
320
|
-
description: 'Language code for search results (default: en)',
|
|
321
|
-
},
|
|
322
|
-
country: {
|
|
323
|
-
type: 'string',
|
|
324
|
-
description: 'Country code for search results (default: us)',
|
|
325
|
-
},
|
|
326
|
-
tbs: {
|
|
327
|
-
type: 'string',
|
|
328
|
-
description: 'Time-based search filter',
|
|
329
|
-
},
|
|
330
|
-
filter: {
|
|
331
|
-
type: 'string',
|
|
332
|
-
description: 'Search filter',
|
|
333
|
-
},
|
|
334
|
-
location: {
|
|
335
|
-
type: 'object',
|
|
336
|
-
properties: {
|
|
337
|
-
country: {
|
|
338
|
-
type: 'string',
|
|
339
|
-
description: 'Country code for geolocation',
|
|
340
|
-
},
|
|
341
|
-
languages: {
|
|
342
|
-
type: 'array',
|
|
343
|
-
items: { type: 'string' },
|
|
344
|
-
description: 'Language codes for content',
|
|
345
|
-
},
|
|
346
|
-
},
|
|
347
|
-
description: 'Location settings for search',
|
|
348
|
-
},
|
|
349
|
-
scrapeOptions: {
|
|
350
|
-
type: 'object',
|
|
351
|
-
properties: {
|
|
352
|
-
formats: {
|
|
353
|
-
type: 'array',
|
|
354
|
-
items: {
|
|
355
|
-
type: 'string',
|
|
356
|
-
enum: ['markdown', 'html', 'rawHtml'],
|
|
357
|
-
},
|
|
358
|
-
description: 'Content formats to extract from search results',
|
|
359
|
-
},
|
|
360
|
-
onlyMainContent: {
|
|
361
|
-
type: 'boolean',
|
|
362
|
-
description: 'Extract only the main content from results',
|
|
363
|
-
},
|
|
364
|
-
waitFor: {
|
|
365
|
-
type: 'number',
|
|
366
|
-
description: 'Time in milliseconds to wait for dynamic content',
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
description: 'Options for scraping search results',
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
required: ['query'],
|
|
373
|
-
},
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
const EXTRACT_TOOL: Tool = {
|
|
377
|
-
name: 'firecrawl_extract',
|
|
378
|
-
description:
|
|
379
|
-
'Extract structured information from web pages using LLM. ' +
|
|
380
|
-
'Supports both cloud AI and self-hosted LLM extraction.',
|
|
381
|
-
inputSchema: {
|
|
382
|
-
type: 'object',
|
|
383
|
-
properties: {
|
|
384
|
-
urls: {
|
|
385
|
-
type: 'array',
|
|
386
|
-
items: { type: 'string' },
|
|
387
|
-
description: 'List of URLs to extract information from',
|
|
388
|
-
},
|
|
389
|
-
prompt: {
|
|
390
|
-
type: 'string',
|
|
391
|
-
description: 'Prompt for the LLM extraction',
|
|
392
|
-
},
|
|
393
|
-
systemPrompt: {
|
|
394
|
-
type: 'string',
|
|
395
|
-
description: 'System prompt for LLM extraction',
|
|
396
|
-
},
|
|
397
|
-
schema: {
|
|
398
|
-
type: 'object',
|
|
399
|
-
description: 'JSON schema for structured data extraction',
|
|
400
|
-
},
|
|
401
|
-
allowExternalLinks: {
|
|
402
|
-
type: 'boolean',
|
|
403
|
-
description: 'Allow extraction from external links',
|
|
404
|
-
},
|
|
405
|
-
enableWebSearch: {
|
|
406
|
-
type: 'boolean',
|
|
407
|
-
description: 'Enable web search for additional context',
|
|
408
|
-
},
|
|
409
|
-
includeSubdomains: {
|
|
410
|
-
type: 'boolean',
|
|
411
|
-
description: 'Include subdomains in extraction',
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
required: ['urls'],
|
|
415
|
-
},
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
const DEEP_RESEARCH_TOOL: Tool = {
|
|
419
|
-
name: 'firecrawl_deep_research',
|
|
420
|
-
description: 'Conduct deep research on a query using web crawling, search, and AI analysis.',
|
|
421
|
-
inputSchema: {
|
|
422
|
-
type: 'object',
|
|
423
|
-
properties: {
|
|
424
|
-
query: {
|
|
425
|
-
type: 'string',
|
|
426
|
-
description: 'The query to research',
|
|
427
|
-
},
|
|
428
|
-
maxDepth: {
|
|
429
|
-
type: 'number',
|
|
430
|
-
description: 'Maximum depth of research iterations (1-10)',
|
|
431
|
-
},
|
|
432
|
-
timeLimit: {
|
|
433
|
-
type: 'number',
|
|
434
|
-
description: 'Time limit in seconds (30-300)',
|
|
435
|
-
},
|
|
436
|
-
maxUrls: {
|
|
437
|
-
type: 'number',
|
|
438
|
-
description: 'Maximum number of URLs to analyze (1-1000)',
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
required: ['query'],
|
|
442
|
-
},
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
const GENERATE_LLMSTXT_TOOL: Tool = {
|
|
446
|
-
name: 'firecrawl_generate_llmstxt',
|
|
447
|
-
description:
|
|
448
|
-
'Generate standardized LLMs.txt file for a given URL, which provides context about how LLMs should interact with the website.',
|
|
449
|
-
inputSchema: {
|
|
450
|
-
type: 'object',
|
|
451
|
-
properties: {
|
|
452
|
-
url: {
|
|
453
|
-
type: 'string',
|
|
454
|
-
description: 'The URL to generate LLMs.txt from',
|
|
455
|
-
},
|
|
456
|
-
maxUrls: {
|
|
457
|
-
type: 'number',
|
|
458
|
-
description: 'Maximum number of URLs to process (1-100, default: 10)',
|
|
459
|
-
},
|
|
460
|
-
showFullText: {
|
|
461
|
-
type: 'boolean',
|
|
462
|
-
description: 'Whether to show the full LLMs-full.txt in the response',
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
required: ['url'],
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
// --- Add back necessary interfaces and type guards ---
|
|
470
|
-
interface StatusCheckOptions {
|
|
471
|
-
id: string;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
interface SearchOptions {
|
|
475
|
-
// Assuming this structure is still needed by isSearchOptions
|
|
476
|
-
query: string;
|
|
477
|
-
// ... other fields if required
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
interface ExtractParams<T = any> {
|
|
481
|
-
// Rename to ExtractParams and add generic T
|
|
482
|
-
urls: string[];
|
|
483
|
-
prompt?: string;
|
|
484
|
-
systemPrompt?: string;
|
|
485
|
-
schema?: T | object; // Use generic T for schema
|
|
486
|
-
allowExternalLinks?: boolean;
|
|
487
|
-
enableWebSearch?: boolean;
|
|
488
|
-
includeSubdomains?: boolean;
|
|
489
|
-
origin?: string;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
interface ExtractResponse<T = any> {
|
|
493
|
-
success: boolean;
|
|
494
|
-
data: T;
|
|
495
|
-
error?: string;
|
|
496
|
-
warning?: string;
|
|
497
|
-
creditsUsed?: number;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
interface GenerateLLMsTextParams {
|
|
501
|
-
// Assuming this structure is still needed by isGenerateLLMsTextOptions
|
|
502
|
-
maxUrls?: number;
|
|
503
|
-
showFullText?: boolean;
|
|
504
|
-
__experimental_stream?: boolean;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Type guards
|
|
508
|
-
function isScrapeOptions(args: unknown): args is ScrapeParams & { url: string } {
|
|
509
|
-
return (
|
|
510
|
-
typeof args === 'object' && args !== null && 'url' in args && typeof (args as { url: unknown }).url === 'string'
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function isMapOptions(args: unknown): args is MapParams & { url: string } {
|
|
515
|
-
return (
|
|
516
|
-
typeof args === 'object' && args !== null && 'url' in args && typeof (args as { url: unknown }).url === 'string'
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
function isCrawlOptions(args: unknown): args is CrawlParams & { url: string } {
|
|
521
|
-
return (
|
|
522
|
-
typeof args === 'object' && args !== null && 'url' in args && typeof (args as { url: unknown }).url === 'string'
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function isStatusCheckOptions(args: unknown): args is StatusCheckOptions {
|
|
527
|
-
return typeof args === 'object' && args !== null && 'id' in args && typeof (args as { id: unknown }).id === 'string';
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function isSearchOptions(args: unknown): args is SearchOptions {
|
|
531
|
-
return (
|
|
532
|
-
typeof args === 'object' &&
|
|
533
|
-
args !== null &&
|
|
534
|
-
'query' in args &&
|
|
535
|
-
typeof (args as { query: unknown }).query === 'string'
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function isExtractOptions(args: unknown): args is ExtractParams {
|
|
540
|
-
// Update type guard to use ExtractParams
|
|
541
|
-
if (typeof args !== 'object' || args === null) return false;
|
|
542
|
-
const { urls } = args as { urls?: unknown };
|
|
543
|
-
return Array.isArray(urls) && urls.every((url): url is string => typeof url === 'string');
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function isGenerateLLMsTextOptions(args: unknown): args is { url: string } & Partial<GenerateLLMsTextParams> {
|
|
547
|
-
return (
|
|
548
|
-
typeof args === 'object' && args !== null && 'url' in args && typeof (args as { url: unknown }).url === 'string'
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
// --- End added back interfaces and type guards ---
|
|
552
|
-
|
|
553
|
-
// --- Add back necessary helper functions ---
|
|
554
|
-
const CONFIG = {
|
|
555
|
-
// Simplified config, adjust if needed
|
|
556
|
-
retry: {
|
|
557
|
-
maxAttempts: 3,
|
|
558
|
-
initialDelay: 1000,
|
|
559
|
-
maxDelay: 10000,
|
|
560
|
-
backoffFactor: 2,
|
|
561
|
-
},
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
function delay(ms: number): Promise<void> {
|
|
565
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
let isStdioTransport = false; // Keep this global flag for safeLog
|
|
569
|
-
|
|
570
|
-
function safeLog(
|
|
571
|
-
level: 'error' | 'debug' | 'info' | 'notice' | 'warning' | 'critical' | 'alert' | 'emergency',
|
|
572
|
-
data: any,
|
|
573
|
-
): void {
|
|
574
|
-
if (isStdioTransport) {
|
|
575
|
-
console.error(`[${level}] ${typeof data === 'object' ? JSON.stringify(data) : data}`);
|
|
576
|
-
} else {
|
|
577
|
-
// Assuming our MCPServer instance has a compatible logging method
|
|
578
|
-
// Or we might need to adapt this if logging isn't directly exposed/compatible
|
|
579
|
-
// For now, just console.error as a fallback if not stdio
|
|
580
|
-
console.error(`[${level}] ${typeof data === 'object' ? JSON.stringify(data) : data}`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
async function withRetry<T>(operation: () => Promise<T>, context: string, attempt = 1): Promise<T> {
|
|
585
|
-
try {
|
|
586
|
-
return await operation();
|
|
587
|
-
} catch (error) {
|
|
588
|
-
const isRateLimit =
|
|
589
|
-
error instanceof Error && (error.message.includes('rate limit') || error.message.includes('429'));
|
|
590
|
-
|
|
591
|
-
if (isRateLimit && attempt < CONFIG.retry.maxAttempts) {
|
|
592
|
-
const delayMs = Math.min(
|
|
593
|
-
CONFIG.retry.initialDelay * Math.pow(CONFIG.retry.backoffFactor, attempt - 1),
|
|
594
|
-
CONFIG.retry.maxDelay,
|
|
595
|
-
);
|
|
596
|
-
|
|
597
|
-
safeLog(
|
|
598
|
-
'warning',
|
|
599
|
-
`Rate limit hit for ${context}. Attempt ${attempt}/${CONFIG.retry.maxAttempts}. Retrying in ${delayMs}ms`,
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
await delay(delayMs);
|
|
603
|
-
return withRetry(operation, context, attempt + 1);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
throw error;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
// --- End added back helper functions ---
|
|
610
|
-
|
|
611
|
-
// Define the tool execution logic creator
|
|
612
|
-
const createExecuteFunction = (originalName: string) => async (args: any) => {
|
|
613
|
-
const client = new FirecrawlApp({ apiKey: 'FIXTURE_API_KEY_PLACEHOLDER' });
|
|
614
|
-
const startTime = Date.now();
|
|
615
|
-
try {
|
|
616
|
-
safeLog('info', `[${new Date().toISOString()}] Received request for tool: ${originalName}`);
|
|
617
|
-
|
|
618
|
-
if (!args) {
|
|
619
|
-
throw new Error('No arguments provided');
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
switch (originalName) {
|
|
623
|
-
case 'firecrawl_scrape': {
|
|
624
|
-
if (!isScrapeOptions(args)) {
|
|
625
|
-
throw new Error('Invalid arguments for firecrawl_scrape');
|
|
626
|
-
}
|
|
627
|
-
const { url, ...options } = args;
|
|
628
|
-
try {
|
|
629
|
-
const scrapeStartTime = Date.now();
|
|
630
|
-
safeLog('info', `Starting scrape for URL: ${url} with options: ${JSON.stringify(options)}`);
|
|
631
|
-
|
|
632
|
-
const response = await client.scrapeUrl(url, {
|
|
633
|
-
...options,
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
safeLog('info', `Scrape completed in ${Date.now() - scrapeStartTime}ms`);
|
|
637
|
-
|
|
638
|
-
if ('success' in response && !response.success) {
|
|
639
|
-
throw new Error(response.error || 'Scraping failed');
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
const contentParts = [];
|
|
643
|
-
|
|
644
|
-
if (options.formats?.includes('markdown') && response.markdown) {
|
|
645
|
-
contentParts.push(response.markdown);
|
|
646
|
-
}
|
|
647
|
-
if (options.formats?.includes('html') && response.html) {
|
|
648
|
-
contentParts.push(response.html);
|
|
649
|
-
}
|
|
650
|
-
if (options.formats?.includes('rawHtml') && response.rawHtml) {
|
|
651
|
-
contentParts.push(response.rawHtml);
|
|
652
|
-
}
|
|
653
|
-
if (options.formats?.includes('links') && response.links) {
|
|
654
|
-
contentParts.push(response.links.join('\n'));
|
|
655
|
-
}
|
|
656
|
-
if (options.formats?.includes('screenshot') && response.screenshot) {
|
|
657
|
-
contentParts.push(response.screenshot);
|
|
658
|
-
}
|
|
659
|
-
if (options.formats?.includes('extract') && response.extract) {
|
|
660
|
-
contentParts.push(JSON.stringify(response.extract, null, 2));
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
if (!options.formats || options.formats.length === 0) {
|
|
664
|
-
options.formats = ['markdown'];
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
if (response.warning) {
|
|
668
|
-
safeLog('warning', response.warning);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
return {
|
|
672
|
-
content: [
|
|
673
|
-
{
|
|
674
|
-
type: 'text',
|
|
675
|
-
text: trimResponseText(contentParts.join('\n\n') || 'No content available'),
|
|
676
|
-
},
|
|
677
|
-
],
|
|
678
|
-
isError: false,
|
|
679
|
-
};
|
|
680
|
-
} catch (error) {
|
|
681
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
682
|
-
return {
|
|
683
|
-
content: [{ type: 'text', text: trimResponseText(errorMessage) }],
|
|
684
|
-
isError: true,
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
case 'firecrawl_map': {
|
|
690
|
-
if (!isMapOptions(args)) {
|
|
691
|
-
throw new Error('Invalid arguments for firecrawl_map');
|
|
692
|
-
}
|
|
693
|
-
const { url, ...options } = args;
|
|
694
|
-
const response = await client.mapUrl(url, {
|
|
695
|
-
...options,
|
|
696
|
-
});
|
|
697
|
-
if ('error' in response) {
|
|
698
|
-
throw new Error(response.error);
|
|
699
|
-
}
|
|
700
|
-
if (!response.links) {
|
|
701
|
-
throw new Error('No links received from Firecrawl API');
|
|
702
|
-
}
|
|
703
|
-
return {
|
|
704
|
-
content: [{ type: 'text', text: trimResponseText(response.links.join('\n')) }],
|
|
705
|
-
isError: false,
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
case 'firecrawl_crawl': {
|
|
710
|
-
if (!isCrawlOptions(args)) {
|
|
711
|
-
throw new Error('Invalid arguments for firecrawl_crawl');
|
|
712
|
-
}
|
|
713
|
-
const { url, ...options } = args;
|
|
714
|
-
const response = await withRetry(async () => client.asyncCrawlUrl(url, { ...options }), 'crawl operation');
|
|
715
|
-
|
|
716
|
-
if (!response.success) {
|
|
717
|
-
throw new Error(response.error);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
return {
|
|
721
|
-
content: [
|
|
722
|
-
{
|
|
723
|
-
type: 'text',
|
|
724
|
-
text: trimResponseText(`Started crawl for ${url} with job ID: ${response.id}`),
|
|
725
|
-
},
|
|
726
|
-
],
|
|
727
|
-
isError: false,
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
case 'firecrawl_check_crawl_status': {
|
|
732
|
-
if (!isStatusCheckOptions(args)) {
|
|
733
|
-
throw new Error('Invalid arguments for firecrawl_check_crawl_status');
|
|
734
|
-
}
|
|
735
|
-
const response = await client.checkCrawlStatus(args.id);
|
|
736
|
-
if (!response.success) {
|
|
737
|
-
throw new Error(response.error);
|
|
738
|
-
}
|
|
739
|
-
const status = `Crawl Status:
|
|
740
|
-
Status: ${response.status}
|
|
741
|
-
Progress: ${response.completed}/${response.total}
|
|
742
|
-
Credits Used: ${response.creditsUsed}
|
|
743
|
-
Expires At: ${response.expiresAt}
|
|
744
|
-
${response.data.length > 0 ? '\nResults:\n' + formatResults(response.data) : ''}`;
|
|
745
|
-
return {
|
|
746
|
-
content: [{ type: 'text', text: trimResponseText(status) }],
|
|
747
|
-
isError: false,
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
case 'firecrawl_search': {
|
|
752
|
-
if (!isSearchOptions(args)) {
|
|
753
|
-
throw new Error('Invalid arguments for firecrawl_search');
|
|
754
|
-
}
|
|
755
|
-
try {
|
|
756
|
-
const response = await withRetry(async () => client.search(args.query, { ...args }), 'search operation');
|
|
757
|
-
|
|
758
|
-
if (!response.success) {
|
|
759
|
-
throw new Error(`Search failed: ${response.error || 'Unknown error'}`);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const results = response.data
|
|
763
|
-
.map(
|
|
764
|
-
result =>
|
|
765
|
-
`URL: ${result.url}
|
|
766
|
-
Title: ${result.title || 'No title'}
|
|
767
|
-
Description: ${result.description || 'No description'}
|
|
768
|
-
${result.markdown ? `\nContent:\n${result.markdown}` : ''}`,
|
|
769
|
-
)
|
|
770
|
-
.join('\n\n');
|
|
771
|
-
|
|
772
|
-
return {
|
|
773
|
-
content: [{ type: 'text', text: trimResponseText(results) }],
|
|
774
|
-
isError: false,
|
|
775
|
-
};
|
|
776
|
-
} catch (error) {
|
|
777
|
-
const errorMessage = error instanceof Error ? error.message : `Search failed: ${JSON.stringify(error)}`;
|
|
778
|
-
return {
|
|
779
|
-
content: [{ type: 'text', text: trimResponseText(errorMessage) }],
|
|
780
|
-
isError: true,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
case 'firecrawl_extract': {
|
|
786
|
-
if (!isExtractOptions(args)) {
|
|
787
|
-
throw new Error('Invalid arguments for firecrawl_extract');
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
try {
|
|
791
|
-
const extractStartTime = Date.now();
|
|
792
|
-
|
|
793
|
-
safeLog('info', `Starting extraction for URLs: ${args.urls.join(', ')}`);
|
|
794
|
-
|
|
795
|
-
const extractResponse = await withRetry(
|
|
796
|
-
async () =>
|
|
797
|
-
client.extract(args.urls, {
|
|
798
|
-
prompt: args.prompt,
|
|
799
|
-
systemPrompt: args.systemPrompt,
|
|
800
|
-
schema: args.schema,
|
|
801
|
-
allowExternalLinks: args.allowExternalLinks,
|
|
802
|
-
enableWebSearch: args.enableWebSearch,
|
|
803
|
-
includeSubdomains: args.includeSubdomains,
|
|
804
|
-
} as ExtractParams),
|
|
805
|
-
'extract operation',
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
if (!('success' in extractResponse) || !extractResponse.success) {
|
|
809
|
-
throw new Error(extractResponse.error || 'Extraction failed');
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
const response = extractResponse as ExtractResponse;
|
|
813
|
-
|
|
814
|
-
safeLog('info', `Extraction completed in ${Date.now() - extractStartTime}ms`);
|
|
815
|
-
|
|
816
|
-
const result = {
|
|
817
|
-
content: [
|
|
818
|
-
{
|
|
819
|
-
type: 'text',
|
|
820
|
-
text: trimResponseText(JSON.stringify(response.data, null, 2)),
|
|
821
|
-
},
|
|
822
|
-
],
|
|
823
|
-
isError: false,
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
if (response.warning) {
|
|
827
|
-
safeLog('warning', response.warning);
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
return result;
|
|
831
|
-
} catch (error) {
|
|
832
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
833
|
-
|
|
834
|
-
return {
|
|
835
|
-
content: [{ type: 'text', text: trimResponseText(errorMessage) }],
|
|
836
|
-
isError: true,
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
case 'firecrawl_deep_research': {
|
|
842
|
-
if (!args || typeof args !== 'object' || !('query' in args)) {
|
|
843
|
-
throw new Error('Invalid arguments for firecrawl_deep_research');
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
try {
|
|
847
|
-
const researchStartTime = Date.now();
|
|
848
|
-
safeLog('info', `Starting deep research for query: ${args.query}`);
|
|
849
|
-
|
|
850
|
-
const response = await client.deepResearch(
|
|
851
|
-
args.query as string,
|
|
852
|
-
{
|
|
853
|
-
maxDepth: args.maxDepth as number,
|
|
854
|
-
timeLimit: args.timeLimit as number,
|
|
855
|
-
maxUrls: args.maxUrls as number,
|
|
856
|
-
},
|
|
857
|
-
activity => {
|
|
858
|
-
safeLog('info', `Research activity: ${activity.message} (Depth: ${activity.depth})`);
|
|
859
|
-
},
|
|
860
|
-
source => {
|
|
861
|
-
safeLog('info', `Research source found: ${source.url}${source.title ? ` - ${source.title}` : ''}`);
|
|
862
|
-
},
|
|
863
|
-
);
|
|
864
|
-
|
|
865
|
-
safeLog('info', `Deep research completed in ${Date.now() - researchStartTime}ms`);
|
|
866
|
-
|
|
867
|
-
if (!response.success) {
|
|
868
|
-
throw new Error(response.error || 'Deep research failed');
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
const formattedResponse = {
|
|
872
|
-
finalAnalysis: response.data.finalAnalysis,
|
|
873
|
-
activities: response.data.activities,
|
|
874
|
-
sources: response.data.sources,
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
return {
|
|
878
|
-
content: [
|
|
879
|
-
{
|
|
880
|
-
type: 'text',
|
|
881
|
-
text: trimResponseText(formattedResponse.finalAnalysis),
|
|
882
|
-
},
|
|
883
|
-
],
|
|
884
|
-
isError: false,
|
|
885
|
-
};
|
|
886
|
-
} catch (error) {
|
|
887
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
888
|
-
return {
|
|
889
|
-
content: [{ type: 'text', text: trimResponseText(errorMessage) }],
|
|
890
|
-
isError: true,
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
case 'firecrawl_generate_llmstxt': {
|
|
896
|
-
if (!isGenerateLLMsTextOptions(args)) {
|
|
897
|
-
throw new Error('Invalid arguments for firecrawl_generate_llmstxt');
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
try {
|
|
901
|
-
const { url, ...params } = args;
|
|
902
|
-
const generateStartTime = Date.now();
|
|
903
|
-
|
|
904
|
-
safeLog('info', `Starting LLMs.txt generation for URL: ${url}`);
|
|
905
|
-
|
|
906
|
-
const response = await withRetry(
|
|
907
|
-
async () => client.generateLLMsText(url, { ...params }),
|
|
908
|
-
'LLMs.txt generation',
|
|
909
|
-
);
|
|
910
|
-
|
|
911
|
-
if (!response.success) {
|
|
912
|
-
throw new Error(response.error || 'LLMs.txt generation failed');
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
safeLog('info', `LLMs.txt generation completed in ${Date.now() - generateStartTime}ms`);
|
|
916
|
-
|
|
917
|
-
let resultText = '';
|
|
918
|
-
|
|
919
|
-
if ('data' in response) {
|
|
920
|
-
resultText = `LLMs.txt content:\n\n${response.data.llmstxt}`;
|
|
921
|
-
|
|
922
|
-
if (args.showFullText && response.data.llmsfulltxt) {
|
|
923
|
-
resultText += `\n\nLLMs-full.txt content:\n\n${response.data.llmsfulltxt}`;
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
return {
|
|
928
|
-
content: [{ type: 'text', text: trimResponseText(resultText) }],
|
|
929
|
-
isError: false,
|
|
930
|
-
};
|
|
931
|
-
} catch (error) {
|
|
932
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
933
|
-
return {
|
|
934
|
-
content: [{ type: 'text', text: trimResponseText(errorMessage) }],
|
|
935
|
-
isError: true,
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
default:
|
|
941
|
-
return {
|
|
942
|
-
content: [{ type: 'text', text: trimResponseText(`Unknown tool: ${originalName}`) }],
|
|
943
|
-
isError: true,
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
} catch (error) {
|
|
947
|
-
safeLog('error', {
|
|
948
|
-
message: `Request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
949
|
-
tool: originalName,
|
|
950
|
-
arguments: args,
|
|
951
|
-
timestamp: new Date().toISOString(),
|
|
952
|
-
duration: Date.now() - startTime,
|
|
953
|
-
});
|
|
954
|
-
return {
|
|
955
|
-
content: [
|
|
956
|
-
{
|
|
957
|
-
type: 'text',
|
|
958
|
-
text: trimResponseText(`Error: ${error instanceof Error ? error.message : String(error)}`),
|
|
959
|
-
},
|
|
960
|
-
],
|
|
961
|
-
isError: true,
|
|
962
|
-
};
|
|
963
|
-
} finally {
|
|
964
|
-
safeLog('info', `Request completed in ${Date.now() - startTime}ms`);
|
|
965
|
-
}
|
|
966
|
-
};
|
|
967
|
-
|
|
968
|
-
// Create the tools object with execute functions attached
|
|
969
|
-
export const allTools: ToolsInput = {
|
|
970
|
-
firecrawl_scrape: { ...SCRAPE_TOOL, execute: createExecuteFunction('firecrawl_scrape') } as any,
|
|
971
|
-
firecrawl_map: { ...MAP_TOOL, execute: createExecuteFunction('firecrawl_map') } as any,
|
|
972
|
-
firecrawl_crawl: { ...CRAWL_TOOL, execute: createExecuteFunction('firecrawl_crawl') } as any,
|
|
973
|
-
firecrawl_check_crawl_status: {
|
|
974
|
-
...CHECK_CRAWL_STATUS_TOOL,
|
|
975
|
-
execute: createExecuteFunction('firecrawl_check_crawl_status'),
|
|
976
|
-
} as any,
|
|
977
|
-
firecrawl_search: { ...SEARCH_TOOL, execute: createExecuteFunction('firecrawl_search') } as any,
|
|
978
|
-
firecrawl_extract: { ...EXTRACT_TOOL, execute: createExecuteFunction('firecrawl_extract') } as any,
|
|
979
|
-
firecrawl_deep_research: { ...DEEP_RESEARCH_TOOL, execute: createExecuteFunction('firecrawl_deep_research') } as any,
|
|
980
|
-
firecrawl_generate_llmstxt: {
|
|
981
|
-
...GENERATE_LLMSTXT_TOOL,
|
|
982
|
-
execute: createExecuteFunction('firecrawl_generate_llmstxt'),
|
|
983
|
-
} as any,
|
|
984
|
-
};
|
|
985
|
-
|
|
986
|
-
export const mcpServerName = 'firecrawl-mcp-fixture';
|
|
987
|
-
|
|
988
|
-
const server = new MCPServer({
|
|
989
|
-
name: mcpServerName,
|
|
990
|
-
version: '1.7.0',
|
|
991
|
-
tools: allTools, // Pass tools with execute functions already attached
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
function formatResults(data: FirecrawlDocument[]): string {
|
|
995
|
-
return data
|
|
996
|
-
.map(doc => {
|
|
997
|
-
const content = doc.markdown || doc.html || doc.rawHtml || 'No content';
|
|
998
|
-
return `URL: ${doc.url || 'Unknown URL'}
|
|
999
|
-
Content: ${content.substring(0, 100)}${content.length > 100 ? '...' : ''}
|
|
1000
|
-
${doc.metadata?.title ? `Title: ${doc.metadata.title}` : ''}`;
|
|
1001
|
-
})
|
|
1002
|
-
.join('\n\n');
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
function trimResponseText(text: string): string {
|
|
1006
|
-
return text.trim();
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
server.startStdio().catch(error => {
|
|
1010
|
-
const errorMessage = 'Fatal error running server';
|
|
1011
|
-
console.error(errorMessage, error);
|
|
1012
|
-
process.exit(1);
|
|
1013
|
-
});
|