firecrawl-mcp 3.25.4 → 3.25.5
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/README.md +2 -2
- package/dist/index.js +717 -387
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45,10 +45,9 @@ var alexandriaFeedbackFields = {
|
|
|
45
45
|
)
|
|
46
46
|
).max(20).optional()
|
|
47
47
|
};
|
|
48
|
-
var ALEXANDRIA_FEEDBACK_GUIDANCE = 'After an Alexandria task, whether a capability ran or discovery found nothing for the website, call firecrawl_feedback once per website with endpoint "alexandria": requestedWebsite (the url the user needed data from and the requestedFunctionality they needed), a rating, a rationale from observed results, and any providerFeedback or capabilityFeedback gaps. It is free, needs no job ID, has no deadline, and follows the answer rather than replacing it.';
|
|
49
48
|
var ALEXANDRIA_FEEDBACK_HINT = {
|
|
50
49
|
name: "firecrawl_feedback",
|
|
51
|
-
when: "
|
|
50
|
+
when: "Optional after task completion; at most once per website, including uncovered sites. Free; no job ID or deadline.",
|
|
52
51
|
arguments: {
|
|
53
52
|
endpoint: "alexandria",
|
|
54
53
|
rating: "<good | partial | bad>",
|
|
@@ -86,14 +85,295 @@ import { readFile } from "fs/promises";
|
|
|
86
85
|
import { createRequire as createRequire2 } from "module";
|
|
87
86
|
import { randomUUID } from "crypto";
|
|
88
87
|
import path from "path";
|
|
89
|
-
import { z as
|
|
88
|
+
import { z as z8 } from "zod";
|
|
90
89
|
|
|
91
|
-
// src/
|
|
90
|
+
// src/tool-output.ts
|
|
92
91
|
import { z as z2 } from "zod";
|
|
92
|
+
function isRecord(value) {
|
|
93
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
94
|
+
}
|
|
95
|
+
function withStructured(text, data) {
|
|
96
|
+
return isRecord(data) ? { content: [{ type: "text", text }], structuredContent: data } : { content: [{ type: "text", text }] };
|
|
97
|
+
}
|
|
98
|
+
function structuredText(data) {
|
|
99
|
+
return withStructured(JSON.stringify(data, null, 2), data);
|
|
100
|
+
}
|
|
101
|
+
function structuredCompact(data) {
|
|
102
|
+
return withStructured(JSON.stringify(data), data);
|
|
103
|
+
}
|
|
104
|
+
function structuredJsonText(text) {
|
|
105
|
+
try {
|
|
106
|
+
return withStructured(text, JSON.parse(text));
|
|
107
|
+
} catch {
|
|
108
|
+
return { content: [{ type: "text", text }] };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
var str = (description) => z2.string().nullable().optional().describe(description);
|
|
112
|
+
var num = (description) => z2.number().nullable().optional().describe(description);
|
|
113
|
+
var bool = (description) => z2.boolean().nullable().optional().describe(description);
|
|
114
|
+
var unknown = (description) => z2.unknown().optional().describe(description);
|
|
115
|
+
var success = bool("Whether the API call succeeded.");
|
|
116
|
+
var error = unknown("Error message or error object when the call did not succeed.");
|
|
117
|
+
var warning = str("Non-fatal warning about the result.");
|
|
118
|
+
var alexandriaEnvelope = {
|
|
119
|
+
requestId: str("Identifier of this logical execution; reuse it only for a retry of the identical payload."),
|
|
120
|
+
scrape_id: str("Identifier of the underlying scrape."),
|
|
121
|
+
receipt: unknown("Billing receipt for the execution."),
|
|
122
|
+
creditsCost: num("Credits this call consumed."),
|
|
123
|
+
delivery: str("`retained` when the full result stayed server-side instead of being inlined."),
|
|
124
|
+
responseBytes: num("Size of the full result in bytes."),
|
|
125
|
+
estimatedTokens: num("Estimated token cost of the full result."),
|
|
126
|
+
tokenEstimateMethod: str("How `estimatedTokens` was derived."),
|
|
127
|
+
inlineTokenBudget: num("Token budget above which a result is retained rather than inlined."),
|
|
128
|
+
workspaceId: str("Workspace holding a retained result, for inspection through virtual Bash."),
|
|
129
|
+
idleTtlSeconds: num("Seconds a retained workspace stays available while idle."),
|
|
130
|
+
message: str("Guidance that accompanies the result."),
|
|
131
|
+
nextTool: unknown("A follow-up tool call (`{name, arguments}`) that continues or inspects this result."),
|
|
132
|
+
feedbackTool: unknown("Pointer to the feedback tool for reporting how this result served the task.")
|
|
133
|
+
};
|
|
134
|
+
var scrapeOutputSchema = z2.object({
|
|
135
|
+
// URL mode: the scraped document, with the formats that were requested.
|
|
136
|
+
markdown: str("Page content as markdown."),
|
|
137
|
+
html: str("Processed HTML of the page."),
|
|
138
|
+
rawHtml: str("Unprocessed HTML of the page."),
|
|
139
|
+
summary: str("Summary of the page content."),
|
|
140
|
+
links: unknown("Links found on the page."),
|
|
141
|
+
screenshot: unknown("Screenshot of the page."),
|
|
142
|
+
images: unknown("Images found on the page."),
|
|
143
|
+
audio: unknown("Audio extracted from the page."),
|
|
144
|
+
video: unknown("Video extracted from the page."),
|
|
145
|
+
answer: str("Targeted answer to the question that was asked of the page."),
|
|
146
|
+
highlights: unknown("Highlighted passages from the page."),
|
|
147
|
+
pages: unknown("Physical PDF pages, when `parsers[].pages` is set."),
|
|
148
|
+
blocks: unknown("Typed PDF layout blocks, when `parsers[].blocks` is set."),
|
|
149
|
+
product: unknown("Product data extracted from the page."),
|
|
150
|
+
menu: unknown("Menu data extracted from the page."),
|
|
151
|
+
json: unknown("Structured data matching the requested JSON schema or prompt."),
|
|
152
|
+
attributes: unknown("Values collected by the requested attribute selectors."),
|
|
153
|
+
actions: unknown("Results of the browser actions that ran during the scrape."),
|
|
154
|
+
changeTracking: unknown("Change-tracking comparison against the previous scrape."),
|
|
155
|
+
branding: unknown("Branding data extracted from the page."),
|
|
156
|
+
metadata: unknown("Page metadata; authenticated responses can include `metadata.scrapeId` for scrape feedback."),
|
|
157
|
+
tools: unknown("Domain-matched Alexandria tools for the page, when `domainTools` is set."),
|
|
158
|
+
warning,
|
|
159
|
+
// Alexandria mode and error envelopes.
|
|
160
|
+
success,
|
|
161
|
+
error,
|
|
162
|
+
data: unknown("Alexandria mode: per-capability results in `data.alexandria`, each with `data`, `records`, or an `error`."),
|
|
163
|
+
...alexandriaEnvelope
|
|
164
|
+
}).describe("A scraped document, or the Alexandria execution envelope when `alexandria` was passed.");
|
|
165
|
+
var mapOutputSchema = z2.object({
|
|
166
|
+
links: unknown("URLs discovered under the website."),
|
|
167
|
+
success,
|
|
168
|
+
error,
|
|
169
|
+
warning,
|
|
170
|
+
id: str("Identifier of the map run, for optional map feedback.")
|
|
171
|
+
}).describe("URLs indexed under the requested website.");
|
|
172
|
+
var searchOutputSchema = z2.object({
|
|
173
|
+
success,
|
|
174
|
+
data: unknown("Ranked results grouped by source, such as `web`, `news`, `images`, and `alexandria`."),
|
|
175
|
+
error,
|
|
176
|
+
warning,
|
|
177
|
+
id: str("Search identifier, for optional `firecrawl_search_feedback`."),
|
|
178
|
+
creditsUsed: num("Credits this search consumed."),
|
|
179
|
+
tools: unknown("Domain-matched Alexandria tools for the results."),
|
|
180
|
+
nextTool: unknown("A follow-up tool call that continues this search."),
|
|
181
|
+
feedbackTool: unknown("Pointer to the feedback tool for this search.")
|
|
182
|
+
}).describe("Ranked search results grouped by source.");
|
|
183
|
+
var findToolsOutputSchema = z2.object({
|
|
184
|
+
success,
|
|
185
|
+
data: unknown("Discovery page in `data.alexandria[0].data`, with `level`, `items`, `total`, and an optional `nextTool`."),
|
|
186
|
+
error,
|
|
187
|
+
...alexandriaEnvelope
|
|
188
|
+
}).describe("One page of the Alexandria catalogue: categories, providers, tools, or one contract.");
|
|
189
|
+
var feedbackOutputSchema = z2.object({
|
|
190
|
+
success,
|
|
191
|
+
error,
|
|
192
|
+
status: num("HTTP status when the submission was rejected."),
|
|
193
|
+
feedbackErrorCode: str("Machine-readable reason the submission was rejected."),
|
|
194
|
+
retryable: bool("Whether retrying the submission can succeed."),
|
|
195
|
+
message: str("Human-readable result of the submission."),
|
|
196
|
+
feedbackId: str("Identifier of the recorded feedback."),
|
|
197
|
+
creditsRefunded: num("Credits refunded for this submission, when eligible."),
|
|
198
|
+
creditsRefundedToday: num("Credits refunded to the team so far this UTC day."),
|
|
199
|
+
dailyRefundCap: num("The team's daily refund cap in credits."),
|
|
200
|
+
dailyCapReached: bool("Whether the daily refund cap is reached; further feedback today still records but refunds nothing."),
|
|
201
|
+
alreadySubmitted: bool("Whether feedback for this job was already recorded."),
|
|
202
|
+
warning,
|
|
203
|
+
data: unknown("Payload returned with the accepted feedback."),
|
|
204
|
+
raw: str("Response body when it was not JSON.")
|
|
205
|
+
}).describe("Result of submitting feedback.");
|
|
206
|
+
var crawlOutputSchema = z2.object({
|
|
207
|
+
success,
|
|
208
|
+
error,
|
|
209
|
+
warning,
|
|
210
|
+
id: str("Crawl identifier, for `firecrawl_check_crawl_status`."),
|
|
211
|
+
status: str("Crawl state, such as `scraping`, `completed`, or `failed`."),
|
|
212
|
+
completed: num("Pages crawled so far."),
|
|
213
|
+
total: num("Pages the crawl expects to visit."),
|
|
214
|
+
creditsUsed: num("Credits the crawl consumed."),
|
|
215
|
+
expiresAt: str("When the crawl results expire."),
|
|
216
|
+
next: str("URL of the next page of results, when the data is paginated."),
|
|
217
|
+
url: str("The URL the crawl started from."),
|
|
218
|
+
data: unknown("Scraped documents collected by the crawl.")
|
|
219
|
+
}).describe("Crawl job state and the documents collected so far.");
|
|
220
|
+
var agentOutputSchema = z2.object({
|
|
221
|
+
success,
|
|
222
|
+
error,
|
|
223
|
+
id: str("Agent job identifier, for `firecrawl_agent_status`."),
|
|
224
|
+
status: str("Job state at submission time."),
|
|
225
|
+
threadId: str("Research thread this job belongs to."),
|
|
226
|
+
threadTurn: num("Turn number of this job within its thread.")
|
|
227
|
+
}).describe("The queued research job. The result is read with `firecrawl_agent_status`.");
|
|
228
|
+
var agentStatusOutputSchema = z2.object({
|
|
229
|
+
success,
|
|
230
|
+
error,
|
|
231
|
+
id: str("Agent job identifier."),
|
|
232
|
+
status: str("Job state: `processing` is non-terminal; `completed` and `failed` are terminal."),
|
|
233
|
+
progress: unknown("Progress information for a running job."),
|
|
234
|
+
data: unknown("Research result, present once the job has completed."),
|
|
235
|
+
creditsUsed: num("Credits the job consumed."),
|
|
236
|
+
sources: unknown("Sources the agent read."),
|
|
237
|
+
expiresAt: str("When the job result expires and can no longer be read."),
|
|
238
|
+
model: str("Agent model that ran the job."),
|
|
239
|
+
mode: str("Agent mode the job ran in."),
|
|
240
|
+
threadId: str("Research thread this job belongs to."),
|
|
241
|
+
threadTurn: num("Turn number of this job within its thread.")
|
|
242
|
+
}).describe("Progress or final result of a research agent job.");
|
|
243
|
+
var interactOutputSchema = z2.object({
|
|
244
|
+
success,
|
|
245
|
+
error: unknown("Why the interaction or the session could not run."),
|
|
246
|
+
url: str("The URL the session was opened from, when opening failed."),
|
|
247
|
+
scrapeId: str("Session identifier; pass it to reuse the session or to `firecrawl_interact_stop`."),
|
|
248
|
+
result: unknown("Interaction result, when it is not an object of its own."),
|
|
249
|
+
output: unknown("Execution output."),
|
|
250
|
+
stdout: str("Standard output of the executed code."),
|
|
251
|
+
stderr: str("Standard error of the executed code."),
|
|
252
|
+
exitCode: num("Exit status of the executed code."),
|
|
253
|
+
data: unknown("Payload returned by the interaction."),
|
|
254
|
+
sessionUrl: str("URL for viewing the live session."),
|
|
255
|
+
liveUrl: str("URL for viewing the live session.")
|
|
256
|
+
}).describe("Result of a browser interaction, with the session identifier to continue or stop it.");
|
|
257
|
+
var interactStopOutputSchema = z2.object({
|
|
258
|
+
success,
|
|
259
|
+
error,
|
|
260
|
+
message: str("Confirmation that the session was stopped."),
|
|
261
|
+
scrapeId: str("Session that was stopped.")
|
|
262
|
+
}).describe("Confirmation that the interact session was stopped.");
|
|
263
|
+
var parseOutputSchema = z2.object({
|
|
264
|
+
success,
|
|
265
|
+
error,
|
|
266
|
+
warning,
|
|
267
|
+
mode: str("Which phase of the hosted flow produced this response."),
|
|
268
|
+
message: str("Guidance for the next call."),
|
|
269
|
+
data: unknown("Parsed document content; can include `data.metadata.scrapeId` for parse feedback."),
|
|
270
|
+
upload: z2.object({
|
|
271
|
+
command: str("Local command that performs the upload. It carries no Firecrawl API key."),
|
|
272
|
+
method: str("HTTP method for the upload."),
|
|
273
|
+
headers: unknown("Headers the upload request must send."),
|
|
274
|
+
fields: unknown("Form fields the upload request must send, for a POST upload."),
|
|
275
|
+
uploadUrl: str("URL to upload the local file to."),
|
|
276
|
+
uploadRef: str("Reference to pass back on the second call."),
|
|
277
|
+
expiresAt: str("When the upload URL expires."),
|
|
278
|
+
maxSizeBytes: num("Largest file the upload URL accepts.")
|
|
279
|
+
}).optional().describe("Hosted phase one: how to upload the local file."),
|
|
280
|
+
nextToolCall: unknown("Hosted phase one: the second `firecrawl_parse` call to make once the upload succeeds, as `{name, arguments}`."),
|
|
281
|
+
notes: z2.array(z2.string()).nullable().optional().describe("Hosted phase one: constraints on completing the upload flow."),
|
|
282
|
+
raw: str("Response body when it was not JSON.")
|
|
283
|
+
}).describe("Parsed document content, or the upload instructions for the hosted two-call flow.");
|
|
284
|
+
var deprecatedToolOutputSchema = z2.object({
|
|
285
|
+
code: str("Always `DEPRECATED_TOOL`."),
|
|
286
|
+
message: str("Why the tool is unavailable and what to call instead."),
|
|
287
|
+
replacement: unknown("The replacement tool, with instructions and example arguments."),
|
|
288
|
+
docs_url: str("Documentation for the replacement.")
|
|
289
|
+
}).describe("A pointer to the replacement tool. This entry point is deprecated and always errors.");
|
|
290
|
+
var monitorOutputSchema = z2.object({
|
|
291
|
+
success,
|
|
292
|
+
error,
|
|
293
|
+
data: unknown("The monitor record: configuration, schedule, targets, and current state.")
|
|
294
|
+
}).describe("One monitor record.");
|
|
295
|
+
var monitorListOutputSchema = z2.object({
|
|
296
|
+
success,
|
|
297
|
+
error,
|
|
298
|
+
data: unknown("One page of monitor records."),
|
|
299
|
+
pagination: unknown("Pagination metadata for the listing.")
|
|
300
|
+
}).describe("One page of monitors for the authenticated account.");
|
|
301
|
+
var monitorDeleteOutputSchema = z2.object({
|
|
302
|
+
success,
|
|
303
|
+
error,
|
|
304
|
+
data: unknown("Deletion details, when the API returns any.")
|
|
305
|
+
}).describe("Deletion status for the monitor.");
|
|
306
|
+
var monitorRunOutputSchema = z2.object({
|
|
307
|
+
success,
|
|
308
|
+
error,
|
|
309
|
+
data: unknown("The queued check.")
|
|
310
|
+
}).describe("The check queued outside the monitor schedule.");
|
|
311
|
+
var monitorChecksOutputSchema = z2.object({
|
|
312
|
+
success,
|
|
313
|
+
error,
|
|
314
|
+
data: unknown("One page of check summaries."),
|
|
315
|
+
pagination: unknown("Pagination metadata for the listing.")
|
|
316
|
+
}).describe("One page of historical checks for the monitor.");
|
|
317
|
+
var monitorCheckOutputSchema = z2.object({
|
|
318
|
+
success,
|
|
319
|
+
error,
|
|
320
|
+
data: unknown("The check and its page-level results: each page reports `same`, `new`, `changed`, `removed`, or `error`, with diffs and any goal judgment."),
|
|
321
|
+
pagination: unknown("Pagination metadata for the page results."),
|
|
322
|
+
next: str("URL of the next page of results, when more exist.")
|
|
323
|
+
}).describe("One monitor check with its page-level diff results.");
|
|
324
|
+
var paperSchema = z2.looseObject({
|
|
325
|
+
paperId: str("Canonical paper identifier."),
|
|
326
|
+
primaryId: str("Display identifier, ordered for citation and fetch use."),
|
|
327
|
+
ids: unknown("Source identifiers by namespace, such as `arxiv`, `doi`, or `pmid`."),
|
|
328
|
+
title: str("Paper title."),
|
|
329
|
+
abstract: str("Paper abstract."),
|
|
330
|
+
authors: unknown("Authors, as a comma-joined string or as `{name, affiliation}` entries."),
|
|
331
|
+
categories: z2.array(z2.string()).nullable().optional().describe("Paper categories, such as `cs.LG`."),
|
|
332
|
+
createdDate: str("Date the paper was first indexed or published."),
|
|
333
|
+
updateDate: str("Date the paper was last updated.")
|
|
334
|
+
});
|
|
335
|
+
var researchSearchOutputSchema = z2.object({
|
|
336
|
+
results: z2.array(paperSchema).describe("Ranked papers, in the order the text block lists them.")
|
|
337
|
+
}).describe("Ranked papers with canonical IDs, titles, authors, and abstracts.");
|
|
338
|
+
var researchPaperOutputSchema = z2.object({
|
|
339
|
+
paper: paperSchema.optional().describe("Canonical metadata for the requested paper, absent when it was not found.")
|
|
340
|
+
}).describe("Canonical metadata for one paper.");
|
|
341
|
+
var researchRelatedOutputSchema = z2.object({
|
|
342
|
+
results: z2.array(paperSchema).describe("Ranked citation-graph candidates."),
|
|
343
|
+
poolSize: z2.number().describe("Number of candidates evaluated before ranking."),
|
|
344
|
+
note: str("Note about how the candidates were produced.")
|
|
345
|
+
}).describe("Ranked citation-graph candidates and the size of the evaluated pool.");
|
|
346
|
+
var researchReadOutputSchema = z2.object({
|
|
347
|
+
passages: z2.array(z2.looseObject({ text: str("Passage text.") })).describe("In-body passages relevant to the question; empty when no full text is indexed.")
|
|
348
|
+
}).describe("Full-text passages from one paper.");
|
|
349
|
+
var developerSearchOutputSchema = z2.object({
|
|
350
|
+
results: z2.array(
|
|
351
|
+
z2.looseObject({
|
|
352
|
+
id: str("Stable result id, such as `issue:owner/repo#123` or `doc:<hash>`; its prefix gives the source type."),
|
|
353
|
+
url: str("Source URL."),
|
|
354
|
+
title: str("Result title."),
|
|
355
|
+
passages: z2.array(z2.looseObject({ text: str("Matched passage, in markdown.") })).optional().describe("Matched passages.")
|
|
356
|
+
})
|
|
357
|
+
).describe("Ranked results, in the order the text block lists them.")
|
|
358
|
+
}).describe("Ranked developer-index results with their matched passages.");
|
|
359
|
+
var creditUsageOutputSchema = z2.object({
|
|
360
|
+
// Current view.
|
|
361
|
+
remainingCredits: num("Credits left in the current billing period; can exceed `planCredits` when extra credits were purchased or granted."),
|
|
362
|
+
planCredits: num("Credits included in the plan for the period."),
|
|
363
|
+
billingPeriodStart: str("Start of the current billing period; null when the billing provider reports no period."),
|
|
364
|
+
billingPeriodEnd: str("End of the current billing period; null when the billing provider reports no period."),
|
|
365
|
+
// Historical view.
|
|
366
|
+
success,
|
|
367
|
+
error,
|
|
368
|
+
periods: unknown("Historical periods sorted by start date, each with `startDate`, `endDate`, `creditsUsed`, and `apiKey` when broken down by key.")
|
|
369
|
+
}).describe("The team credit balance, or historical credit consumption by period.");
|
|
370
|
+
|
|
371
|
+
// src/alexandria.ts
|
|
372
|
+
import { z as z3 } from "zod";
|
|
93
373
|
var catalogueTypes = ["alexandria", "exchange"];
|
|
94
|
-
var searchSourceSchema =
|
|
95
|
-
|
|
96
|
-
|
|
374
|
+
var searchSourceSchema = z3.union([
|
|
375
|
+
z3.enum(["web", "images", "news", ...catalogueTypes]),
|
|
376
|
+
z3.object({ type: z3.enum(["web", "images", "news", ...catalogueTypes]) }).strict()
|
|
97
377
|
]);
|
|
98
378
|
function hasAlexandria(sources) {
|
|
99
379
|
return Array.isArray(sources) && sources.some(
|
|
@@ -116,25 +396,25 @@ function normalizeSearchSources(sources) {
|
|
|
116
396
|
function searchQueryIsValid(args2) {
|
|
117
397
|
return !!args2.query?.trim();
|
|
118
398
|
}
|
|
119
|
-
var findToolsSchema =
|
|
120
|
-
query:
|
|
121
|
-
urls:
|
|
122
|
-
|
|
399
|
+
var findToolsSchema = z3.object({
|
|
400
|
+
query: z3.string().trim().min(1).max(2e3).optional().describe("Semantic lookup of tools for the data you need. Selectors constrain the search."),
|
|
401
|
+
urls: z3.array(
|
|
402
|
+
z3.string().url().regex(/^https?:\/\//)
|
|
123
403
|
).min(1).max(100).optional(),
|
|
124
|
-
providers:
|
|
125
|
-
categories:
|
|
126
|
-
groups:
|
|
127
|
-
capabilities:
|
|
128
|
-
level:
|
|
129
|
-
expand:
|
|
130
|
-
limit:
|
|
131
|
-
offset:
|
|
404
|
+
providers: z3.array(z3.string().min(1)).min(1).max(50).optional().describe("Provider IDs returned by category browsing. Lists compact tools by default."),
|
|
405
|
+
categories: z3.array(z3.string().min(1)).min(1).max(50).optional().describe("Category IDs returned by an empty call. Lists providers by default."),
|
|
406
|
+
groups: z3.array(z3.string().min(1)).min(1).max(50).optional().describe("Optional group IDs for explicit group browsing."),
|
|
407
|
+
capabilities: z3.array(z3.string().min(1)).min(1).max(50).optional().describe("Exact capability IDs. A selected capability expands its complete contract by default."),
|
|
408
|
+
level: z3.enum(["categories", "providers", "groups", "tools"]).optional(),
|
|
409
|
+
expand: z3.array(z3.enum(["options", "response", "examples"])).optional().describe('Use ["options", "response"] for inputs and output shape without example payloads; [] keeps results compact. Omit for full contracts on selected capabilities.'),
|
|
410
|
+
limit: z3.number().int().min(1).max(100).optional(),
|
|
411
|
+
offset: z3.number().int().nonnegative().optional()
|
|
132
412
|
}).strict();
|
|
133
413
|
var ALEXANDRIA_CATALOGUE_VERTICALS = "companies, people, jobs, finance and filings, public records and government spending, real estate, places and restaurants, retail and prices, package registries and developer data, news, research, and more";
|
|
134
414
|
var ALEXANDRIA_CATALOGUE_SENTENCE = "Alexandria is Firecrawl's catalogue of data providers and workflows across " + ALEXANDRIA_CATALOGUE_VERTICALS + "; providers return typed, sourced records through published contracts.";
|
|
135
|
-
var ALEXANDRIA_SOURCES_OPT_OUT = '
|
|
415
|
+
var ALEXANDRIA_SOURCES_OPT_OUT = 'A search with sources: ["web"] omits semantic provider discovery; domainTools: true can still return website-matched tools. Web-only results use domainTools: false.';
|
|
136
416
|
var ALEXANDRIA_SEARCH_LEAD = "Authenticated search also returns matching Alexandria data providers in data.tools (" + ALEXANDRIA_CATALOGUE_VERTICALS + "). Prefer a provider over scraping pages when the task needs the same fields across several entities, exact figures or timestamps, provenance, or many records; use web results when they already answer the question. " + ALEXANDRIA_SOURCES_OPT_OUT;
|
|
137
|
-
var ALEXANDRIA_CONTRACT_GUIDANCE = "
|
|
417
|
+
var ALEXANDRIA_CONTRACT_GUIDANCE = "The selected contract marks required inputs and any requiresOneOf groups (at least one member per group); it may include example.request/example.response and response.key (which may differ from records). Where pagination is declared, its fields govern paging with the same filters; catalogue next is separate from provider pagination.";
|
|
138
418
|
function findToolsOptions(args2) {
|
|
139
419
|
const level = args2.level ?? (args2.query || args2.capabilities?.length || args2.providers?.length || args2.groups?.length || args2.urls?.length ? "tools" : args2.categories?.length ? "providers" : "categories");
|
|
140
420
|
if (level === "categories" && (args2.query || args2.providers?.length || args2.categories?.length || args2.groups?.length || args2.capabilities?.length || args2.urls?.length || args2.expand !== void 0)) {
|
|
@@ -164,7 +444,7 @@ function withFindToolsNavigation(envelope) {
|
|
|
164
444
|
if (link(page.next)) page.nextTool = link(page.next);
|
|
165
445
|
return envelope;
|
|
166
446
|
}
|
|
167
|
-
var ALEXANDRIA_SEARCH_INSTRUCTIONS = 'Authenticated search combines web results, semantic tool summaries and domain matches. Use sources: ["alexandria"] for semantic tools only
|
|
447
|
+
var ALEXANDRIA_SEARCH_INSTRUCTIONS = 'Authenticated search combines web results, semantic tool summaries and domain matches. Use sources: ["alexandria"] for semantic tools only. ' + ALEXANDRIA_SOURCES_OPT_OUT + ' Tool matches describe available structured-data capabilities, not executed data. toolDetail: "compact" (default) returns only provider, capability and description; "summary" adds metadata; "full" includes their input and output contracts. firecrawl_scrape with an alexandria body executes a selected capability; firecrawl_find_tools provides catalogue browsing and full contracts.';
|
|
168
448
|
|
|
169
449
|
// src/alexandria-output.ts
|
|
170
450
|
var INLINE_TOKEN_BUDGET = 2e4;
|
|
@@ -231,7 +511,7 @@ async function alexandriaOutput(payload, calls, post, recoveryRequestId, extras
|
|
|
231
511
|
}
|
|
232
512
|
|
|
233
513
|
// src/developer.ts
|
|
234
|
-
import { z as
|
|
514
|
+
import { z as z4 } from "zod";
|
|
235
515
|
|
|
236
516
|
// src/origin.ts
|
|
237
517
|
import { createRequire } from "module";
|
|
@@ -297,12 +577,13 @@ Search an index of public repositories, GitHub issues, merged pull requests, rep
|
|
|
297
577
|
|
|
298
578
|
Returns ranked results with an ID, source type, URL, title, and the matched passages in markdown.
|
|
299
579
|
`,
|
|
300
|
-
|
|
301
|
-
|
|
580
|
+
outputSchema: developerSearchOutputSchema,
|
|
581
|
+
parameters: z4.object({
|
|
582
|
+
query: z4.string().min(1).describe(
|
|
302
583
|
"Natural-language developer question or search phrase, including the library, error message, or API involved when relevant."
|
|
303
584
|
),
|
|
304
|
-
k:
|
|
305
|
-
skills:
|
|
585
|
+
k: z4.number().int().min(1).max(100).optional().describe("Number of ranked results to return (default 10)."),
|
|
586
|
+
skills: z4.enum(["only"]).optional().describe('Set to "only" to search only agent-skill files.')
|
|
306
587
|
}),
|
|
307
588
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
308
589
|
const { query, k, skills } = args2;
|
|
@@ -312,7 +593,8 @@ Returns ranked results with an ID, source type, URL, title, and the matched pass
|
|
|
312
593
|
if (skills != null) params.append("skills", skills);
|
|
313
594
|
const client = getClient2(session);
|
|
314
595
|
const res = await client.http.get(`${BASE}?${params.toString()}`, originHeaders(requestOrigin(mcpClient, session)));
|
|
315
|
-
|
|
596
|
+
const results = res.data?.results ?? [];
|
|
597
|
+
return withStructured(fmtDeveloper(results), { results });
|
|
316
598
|
}
|
|
317
599
|
});
|
|
318
600
|
}
|
|
@@ -332,7 +614,7 @@ function extractSingleTrustedClientIp(rawForwardedFor) {
|
|
|
332
614
|
}
|
|
333
615
|
|
|
334
616
|
// src/monitor.ts
|
|
335
|
-
import { z as
|
|
617
|
+
import { z as z5 } from "zod";
|
|
336
618
|
|
|
337
619
|
// src/session-credential.ts
|
|
338
620
|
import { createHmac } from "crypto";
|
|
@@ -457,11 +739,8 @@ async function monitorRequest(session, origin, path2, init = {}) {
|
|
|
457
739
|
}
|
|
458
740
|
return payload;
|
|
459
741
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
var pageStatusSchema = z4.enum(["same", "new", "changed", "removed", "error"]);
|
|
464
|
-
var checkStatusSchema = z4.enum([
|
|
742
|
+
var pageStatusSchema = z5.enum(["same", "new", "changed", "removed", "error"]);
|
|
743
|
+
var checkStatusSchema = z5.enum([
|
|
465
744
|
"queued",
|
|
466
745
|
"running",
|
|
467
746
|
"completed",
|
|
@@ -554,22 +833,23 @@ Create a recurring scrape, crawl, or search monitor that compares each check wit
|
|
|
554
833
|
|
|
555
834
|
In the simple form, a \`goal\` is required. If \`queries\` contains one or more non-empty values and is supplied with \`page\`/\`pages\`, \`queries\` create the search target and page targets are ignored. A monitor schedules future network checks and can send configured email or webhook notifications. Returns the created monitor.
|
|
556
835
|
`,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
836
|
+
outputSchema: monitorOutputSchema,
|
|
837
|
+
parameters: z5.object({
|
|
838
|
+
body: z5.record(z5.string(), z5.any()).optional(),
|
|
839
|
+
page: z5.string().optional(),
|
|
840
|
+
pages: z5.array(z5.string()).optional(),
|
|
841
|
+
queries: z5.array(z5.string()).optional(),
|
|
842
|
+
searchWindow: z5.enum(["5m", "15m", "1h", "6h", "24h", "7d"]).optional(),
|
|
843
|
+
maxResults: z5.number().int().min(1).max(50).optional(),
|
|
844
|
+
includeDomains: z5.array(z5.string()).optional(),
|
|
845
|
+
excludeDomains: z5.array(z5.string()).optional(),
|
|
846
|
+
goal: z5.string().optional(),
|
|
847
|
+
name: z5.string().optional(),
|
|
848
|
+
scheduleText: z5.string().optional(),
|
|
849
|
+
timezone: z5.string().optional(),
|
|
850
|
+
email: z5.string().optional(),
|
|
851
|
+
includeDiffs: z5.boolean().optional(),
|
|
852
|
+
webhookUrl: z5.string().optional()
|
|
573
853
|
}),
|
|
574
854
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
575
855
|
const body = buildMonitorCreateBody(args2);
|
|
@@ -583,7 +863,7 @@ In the simple form, a \`goal\` is required. If \`queries\` contains one or more
|
|
|
583
863
|
body
|
|
584
864
|
}
|
|
585
865
|
);
|
|
586
|
-
return
|
|
866
|
+
return structuredText(res);
|
|
587
867
|
}
|
|
588
868
|
});
|
|
589
869
|
server2.addTool({
|
|
@@ -600,9 +880,10 @@ In the simple form, a \`goal\` is required. If \`queries\` contains one or more
|
|
|
600
880
|
description: `
|
|
601
881
|
List monitors for the authenticated account with optional pagination controls. Returns one page of monitor records and pagination metadata.
|
|
602
882
|
`,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
883
|
+
outputSchema: monitorListOutputSchema,
|
|
884
|
+
parameters: z5.object({
|
|
885
|
+
limit: z5.number().int().positive().optional(),
|
|
886
|
+
offset: z5.number().int().nonnegative().optional()
|
|
606
887
|
}),
|
|
607
888
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
608
889
|
const { limit, offset } = args2;
|
|
@@ -614,7 +895,7 @@ List monitors for the authenticated account with optional pagination controls. R
|
|
|
614
895
|
query: { limit, offset }
|
|
615
896
|
}
|
|
616
897
|
);
|
|
617
|
-
return
|
|
898
|
+
return structuredText(res);
|
|
618
899
|
}
|
|
619
900
|
});
|
|
620
901
|
server2.addTool({
|
|
@@ -631,7 +912,8 @@ List monitors for the authenticated account with optional pagination controls. R
|
|
|
631
912
|
description: `
|
|
632
913
|
Retrieve one monitor by ID, including its configuration and current state. This does not run or modify the monitor.
|
|
633
914
|
`,
|
|
634
|
-
|
|
915
|
+
outputSchema: monitorOutputSchema,
|
|
916
|
+
parameters: z5.object({ id: z5.string() }),
|
|
635
917
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
636
918
|
const { id } = args2;
|
|
637
919
|
const res = await monitorRequest(
|
|
@@ -639,7 +921,7 @@ Retrieve one monitor by ID, including its configuration and current state. This
|
|
|
639
921
|
requestOrigin(mcpClient, session),
|
|
640
922
|
`/monitor/${encodeURIComponent(id)}`
|
|
641
923
|
);
|
|
642
|
-
return
|
|
924
|
+
return structuredText(res);
|
|
643
925
|
}
|
|
644
926
|
});
|
|
645
927
|
server2.addTool({
|
|
@@ -658,9 +940,10 @@ Patch an existing monitor by ID. The body can change its name, active/paused sta
|
|
|
658
940
|
|
|
659
941
|
Returns the updated monitor.
|
|
660
942
|
`,
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
943
|
+
outputSchema: monitorOutputSchema,
|
|
944
|
+
parameters: z5.object({
|
|
945
|
+
id: z5.string(),
|
|
946
|
+
body: z5.record(z5.string(), z5.any())
|
|
664
947
|
}),
|
|
665
948
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
666
949
|
const { id, body } = args2;
|
|
@@ -670,7 +953,7 @@ Returns the updated monitor.
|
|
|
670
953
|
`/monitor/${encodeURIComponent(id)}`,
|
|
671
954
|
{ method: "PATCH", body }
|
|
672
955
|
);
|
|
673
|
-
return
|
|
956
|
+
return structuredText(res);
|
|
674
957
|
}
|
|
675
958
|
});
|
|
676
959
|
server2.addTool({
|
|
@@ -687,7 +970,8 @@ Returns the updated monitor.
|
|
|
687
970
|
description: `
|
|
688
971
|
Permanently delete a monitor by ID and stop its future schedule. This operation cannot be undone and returns deletion status.
|
|
689
972
|
`,
|
|
690
|
-
|
|
973
|
+
outputSchema: monitorDeleteOutputSchema,
|
|
974
|
+
parameters: z5.object({ id: z5.string() }),
|
|
691
975
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
692
976
|
const { id } = args2;
|
|
693
977
|
log.info("Deleting monitor", { id });
|
|
@@ -697,7 +981,7 @@ Permanently delete a monitor by ID and stop its future schedule. This operation
|
|
|
697
981
|
`/monitor/${encodeURIComponent(id)}`,
|
|
698
982
|
{ method: "DELETE" }
|
|
699
983
|
);
|
|
700
|
-
return
|
|
984
|
+
return structuredText(res);
|
|
701
985
|
}
|
|
702
986
|
});
|
|
703
987
|
server2.addTool({
|
|
@@ -714,7 +998,8 @@ Permanently delete a monitor by ID and stop its future schedule. This operation
|
|
|
714
998
|
description: `
|
|
715
999
|
Queue an immediate check for a monitor outside its normal schedule. This starts network work for the monitor's configured targets and returns the queued check.
|
|
716
1000
|
`,
|
|
717
|
-
|
|
1001
|
+
outputSchema: monitorRunOutputSchema,
|
|
1002
|
+
parameters: z5.object({ id: z5.string() }),
|
|
718
1003
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
719
1004
|
const { id } = args2;
|
|
720
1005
|
const res = await monitorRequest(
|
|
@@ -723,7 +1008,7 @@ Queue an immediate check for a monitor outside its normal schedule. This starts
|
|
|
723
1008
|
`/monitor/${encodeURIComponent(id)}/run`,
|
|
724
1009
|
{ method: "POST" }
|
|
725
1010
|
);
|
|
726
|
-
return
|
|
1011
|
+
return structuredText(res);
|
|
727
1012
|
}
|
|
728
1013
|
});
|
|
729
1014
|
server2.addTool({
|
|
@@ -740,10 +1025,11 @@ Queue an immediate check for a monitor outside its normal schedule. This starts
|
|
|
740
1025
|
description: `
|
|
741
1026
|
List historical checks for a monitor, optionally filtered by status and bounded by a result limit. Returns one page of check summaries and pagination metadata.
|
|
742
1027
|
`,
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
1028
|
+
outputSchema: monitorChecksOutputSchema,
|
|
1029
|
+
parameters: z5.object({
|
|
1030
|
+
id: z5.string(),
|
|
1031
|
+
limit: z5.number().int().positive().optional(),
|
|
1032
|
+
offset: z5.number().int().nonnegative().optional(),
|
|
747
1033
|
status: checkStatusSchema.optional()
|
|
748
1034
|
}),
|
|
749
1035
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
@@ -754,7 +1040,7 @@ List historical checks for a monitor, optionally filtered by status and bounded
|
|
|
754
1040
|
`/monitor/${encodeURIComponent(id)}/checks`,
|
|
755
1041
|
{ query: { limit, offset, status } }
|
|
756
1042
|
);
|
|
757
|
-
return
|
|
1043
|
+
return structuredText(res);
|
|
758
1044
|
}
|
|
759
1045
|
});
|
|
760
1046
|
server2.addTool({
|
|
@@ -773,11 +1059,12 @@ Retrieve one monitor check and its page-level results, optionally filtered by pa
|
|
|
773
1059
|
|
|
774
1060
|
Markdown tracking returns a unified text diff, JSON tracking returns field paths with previous/current values and a current snapshot, and mixed tracking returns both. Returns one page of results plus a \`next\` URL when more pages exist.
|
|
775
1061
|
`,
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1062
|
+
outputSchema: monitorCheckOutputSchema,
|
|
1063
|
+
parameters: z5.object({
|
|
1064
|
+
id: z5.string(),
|
|
1065
|
+
checkId: z5.string(),
|
|
1066
|
+
limit: z5.number().int().positive().optional(),
|
|
1067
|
+
skip: z5.number().int().nonnegative().optional(),
|
|
781
1068
|
pageStatus: pageStatusSchema.optional()
|
|
782
1069
|
}),
|
|
783
1070
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
@@ -788,13 +1075,13 @@ Markdown tracking returns a unified text diff, JSON tracking returns field paths
|
|
|
788
1075
|
`/monitor/${encodeURIComponent(id)}/checks/${encodeURIComponent(checkId)}`,
|
|
789
1076
|
{ query: { limit, skip, status: pageStatus } }
|
|
790
1077
|
);
|
|
791
|
-
return
|
|
1078
|
+
return structuredText(res);
|
|
792
1079
|
}
|
|
793
1080
|
});
|
|
794
1081
|
}
|
|
795
1082
|
|
|
796
1083
|
// src/research.ts
|
|
797
|
-
import { z as
|
|
1084
|
+
import { z as z6 } from "zod";
|
|
798
1085
|
import { UserError } from "fastmcp";
|
|
799
1086
|
var BASE2 = "/v2/search/research";
|
|
800
1087
|
function appendParam(params, key, value) {
|
|
@@ -910,21 +1197,22 @@ Several distinct framings of the same question surface different papers than a s
|
|
|
910
1197
|
|
|
911
1198
|
Returns ranked papers with canonical IDs, titles, authors, and abstracts.
|
|
912
1199
|
`,
|
|
913
|
-
|
|
914
|
-
|
|
1200
|
+
outputSchema: researchSearchOutputSchema,
|
|
1201
|
+
parameters: z6.object({
|
|
1202
|
+
query: z6.string().min(1).describe(
|
|
915
1203
|
"Natural-language research topic or question, including methods, systems, conditions, populations, interventions, or outcomes when relevant."
|
|
916
1204
|
),
|
|
917
|
-
k:
|
|
918
|
-
authors:
|
|
1205
|
+
k: z6.number().int().min(1).max(500).optional().describe("Number of ranked papers to return (default 40)."),
|
|
1206
|
+
authors: z6.array(z6.string()).optional().describe(
|
|
919
1207
|
"Author substring filter(s); ALL must match (case-insensitive)."
|
|
920
1208
|
),
|
|
921
|
-
categories:
|
|
1209
|
+
categories: z6.array(z6.string()).optional().describe(
|
|
922
1210
|
"Paper category filter(s) (e.g. `cs.LG`); ALL provided values must match."
|
|
923
1211
|
),
|
|
924
|
-
from:
|
|
1212
|
+
from: z6.string().optional().describe(
|
|
925
1213
|
"Inclusive lower bound on created/updated date (`YYYY-MM-DD`)."
|
|
926
1214
|
),
|
|
927
|
-
to:
|
|
1215
|
+
to: z6.string().optional().describe(
|
|
928
1216
|
"Inclusive upper bound on created/updated date (`YYYY-MM-DD`)."
|
|
929
1217
|
)
|
|
930
1218
|
}),
|
|
@@ -942,7 +1230,8 @@ Returns ranked papers with canonical IDs, titles, authors, and abstracts.
|
|
|
942
1230
|
withQuery(`${BASE2}/papers`, params),
|
|
943
1231
|
originHeaders(requestOrigin(mcpClient, session))
|
|
944
1232
|
);
|
|
945
|
-
|
|
1233
|
+
const results = res.data?.results ?? [];
|
|
1234
|
+
return withStructured(fmtHits(results), { results });
|
|
946
1235
|
}
|
|
947
1236
|
});
|
|
948
1237
|
server2.addTool({
|
|
@@ -959,8 +1248,9 @@ Returns ranked papers with canonical IDs, titles, authors, and abstracts.
|
|
|
959
1248
|
description: `
|
|
960
1249
|
Retrieve canonical metadata for one paper ID, such as an arXiv, PMC, PMID, or DOI identifier. Returns the title, abstract, authors, categories, source IDs, and dates as markdown.
|
|
961
1250
|
`,
|
|
962
|
-
|
|
963
|
-
|
|
1251
|
+
outputSchema: researchPaperOutputSchema,
|
|
1252
|
+
parameters: z6.object({
|
|
1253
|
+
paperId: z6.string().min(1).describe(
|
|
964
1254
|
"Canonical paperId or primaryId such as `arxiv:1706.03762`, `pmcid:PMC12530322`, `pmid:40953549`, or `doi:10.1016/j.neunet.2025.108095`."
|
|
965
1255
|
)
|
|
966
1256
|
}),
|
|
@@ -971,7 +1261,11 @@ Retrieve canonical metadata for one paper ID, such as an arXiv, PMC, PMID, or DO
|
|
|
971
1261
|
`${BASE2}/papers/${encodeURIComponent(paperId)}`,
|
|
972
1262
|
originHeaders(requestOrigin(mcpClient, session))
|
|
973
1263
|
);
|
|
974
|
-
|
|
1264
|
+
const paper = res.data?.paper;
|
|
1265
|
+
return withStructured(
|
|
1266
|
+
fmtPaperMetadata(paper),
|
|
1267
|
+
paper ? { paper } : {}
|
|
1268
|
+
);
|
|
975
1269
|
}
|
|
976
1270
|
});
|
|
977
1271
|
server2.addTool({
|
|
@@ -990,12 +1284,13 @@ Find citation-graph candidates from one to ten \`seed_ids\`; the first ID is the
|
|
|
990
1284
|
|
|
991
1285
|
Returns ranked candidates and the evaluated pool size.
|
|
992
1286
|
`,
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1287
|
+
outputSchema: researchRelatedOutputSchema,
|
|
1288
|
+
parameters: z6.object({
|
|
1289
|
+
seed_ids: z6.array(z6.string()).min(1).max(10),
|
|
1290
|
+
intent: z6.string().min(1),
|
|
1291
|
+
mode: z6.enum(["similar", "citers", "references"]).optional(),
|
|
1292
|
+
k: z6.number().int().min(1).max(500).optional(),
|
|
1293
|
+
rerank: z6.boolean().optional().describe("Apply an additional rerank over the fused candidates.")
|
|
999
1294
|
}),
|
|
1000
1295
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
1001
1296
|
const { seed_ids, intent, mode, k, rerank } = args2;
|
|
@@ -1014,10 +1309,15 @@ Returns ranked candidates and the evaluated pool size.
|
|
|
1014
1309
|
),
|
|
1015
1310
|
originHeaders(requestOrigin(mcpClient, session))
|
|
1016
1311
|
);
|
|
1312
|
+
const results = res.data?.results ?? [];
|
|
1313
|
+
const poolSize = res.data?.poolSize ?? 0;
|
|
1017
1314
|
const note = res.data?.note ? `
|
|
1018
1315
|
note: ${res.data.note}` : "";
|
|
1019
|
-
return
|
|
1020
|
-
(
|
|
1316
|
+
return withStructured(
|
|
1317
|
+
`${fmtHits(results)}
|
|
1318
|
+
(poolSize=${poolSize})${note}`,
|
|
1319
|
+
{ results, poolSize, ...res.data?.note ? { note: res.data.note } : {} }
|
|
1320
|
+
);
|
|
1021
1321
|
}
|
|
1022
1322
|
});
|
|
1023
1323
|
server2.addTool({
|
|
@@ -1036,12 +1336,13 @@ Retrieve in-body passages from one paper that are relevant to a specific questio
|
|
|
1036
1336
|
|
|
1037
1337
|
Returns matching passages or a notice when full text is unavailable.
|
|
1038
1338
|
`,
|
|
1039
|
-
|
|
1040
|
-
|
|
1339
|
+
outputSchema: researchReadOutputSchema,
|
|
1340
|
+
parameters: z6.object({
|
|
1341
|
+
paperId: z6.string().min(1).describe(
|
|
1041
1342
|
"Canonical paperId or primaryId such as `arxiv:1706.03762`, `pmcid:PMC12530322`, `pmid:40953549`, or `doi:10.1016/j.neunet.2025.108095`."
|
|
1042
1343
|
),
|
|
1043
|
-
question:
|
|
1044
|
-
k:
|
|
1344
|
+
question: z6.string().min(1),
|
|
1345
|
+
k: z6.number().int().min(1).max(50).optional().describe("Number of passages to return (default 4).")
|
|
1045
1346
|
}),
|
|
1046
1347
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
1047
1348
|
const { paperId, question, k } = args2;
|
|
@@ -1054,7 +1355,10 @@ Returns matching passages or a notice when full text is unavailable.
|
|
|
1054
1355
|
originHeaders(requestOrigin(mcpClient, session))
|
|
1055
1356
|
);
|
|
1056
1357
|
const passages = res.data?.passages ?? [];
|
|
1057
|
-
return
|
|
1358
|
+
return withStructured(
|
|
1359
|
+
passages.length ? passages.map((p) => p.text).join("\n---\n") : "(no full-text passages available for this paper)",
|
|
1360
|
+
{ passages }
|
|
1361
|
+
);
|
|
1058
1362
|
}
|
|
1059
1363
|
});
|
|
1060
1364
|
server2.addTool({
|
|
@@ -1068,9 +1372,10 @@ Returns matching passages or a notice when full text is unavailable.
|
|
|
1068
1372
|
description: `
|
|
1069
1373
|
Deprecated compatibility entry point. Use firecrawl_developer_search for GitHub issues, pull requests, and READMEs, plus code documentation, returned as matched passages.
|
|
1070
1374
|
`,
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1375
|
+
outputSchema: deprecatedToolOutputSchema,
|
|
1376
|
+
parameters: z6.object({
|
|
1377
|
+
query: z6.string().min(1),
|
|
1378
|
+
k: z6.number().int().min(1).max(100).optional()
|
|
1074
1379
|
}),
|
|
1075
1380
|
canList: () => false,
|
|
1076
1381
|
beforeValidate: () => {
|
|
@@ -1089,10 +1394,7 @@ Deprecated compatibility entry point. Use firecrawl_developer_search for GitHub
|
|
|
1089
1394
|
}
|
|
1090
1395
|
|
|
1091
1396
|
// src/usage.ts
|
|
1092
|
-
import { z as
|
|
1093
|
-
function asText2(data) {
|
|
1094
|
-
return JSON.stringify(data, null, 2);
|
|
1095
|
-
}
|
|
1397
|
+
import { z as z7 } from "zod";
|
|
1096
1398
|
function assertSuccessful(status, response, operation) {
|
|
1097
1399
|
if (status === 200 && response.success) return;
|
|
1098
1400
|
throw new Error(response.error || `Failed to ${operation}`);
|
|
@@ -1113,11 +1415,12 @@ The current view returns \`remainingCredits\`, \`planCredits\`, \`billingPeriodS
|
|
|
1113
1415
|
|
|
1114
1416
|
The historical view returns periods sorted by start date with \`startDate\`, \`endDate\`, and \`creditsUsed\`. Set \`byApiKey\` to include separate periods per API key, identified by the optional \`apiKey\` field. The newest period's \`endDate\` can be null.
|
|
1115
1417
|
`,
|
|
1116
|
-
|
|
1117
|
-
|
|
1418
|
+
outputSchema: creditUsageOutputSchema,
|
|
1419
|
+
parameters: z7.object({
|
|
1420
|
+
view: z7.enum(["current", "historical"]).optional().describe(
|
|
1118
1421
|
"Select current balance or historical monthly usage. Defaults to current."
|
|
1119
1422
|
),
|
|
1120
|
-
byApiKey:
|
|
1423
|
+
byApiKey: z7.boolean().optional().describe(
|
|
1121
1424
|
'Break historical usage down by API key. When view is omitted, true selects the historical view; it cannot be combined with view "current".'
|
|
1122
1425
|
)
|
|
1123
1426
|
}),
|
|
@@ -1142,7 +1445,7 @@ The historical view returns periods sorted by start date with \`startDate\`, \`e
|
|
|
1142
1445
|
response2.data,
|
|
1143
1446
|
"get historical credit usage"
|
|
1144
1447
|
);
|
|
1145
|
-
return
|
|
1448
|
+
return structuredText(response2.data);
|
|
1146
1449
|
}
|
|
1147
1450
|
const response = await client.http.get(
|
|
1148
1451
|
"/v2/team/credit-usage",
|
|
@@ -1150,7 +1453,7 @@ The historical view returns periods sorted by start date with \`startDate\`, \`e
|
|
|
1150
1453
|
);
|
|
1151
1454
|
assertSuccessful(response.status, response.data, "get credit usage");
|
|
1152
1455
|
const data = response.data.data ?? response.data;
|
|
1153
|
-
return
|
|
1456
|
+
return structuredText({
|
|
1154
1457
|
remainingCredits: data.remainingCredits ?? data.remaining_credits ?? 0,
|
|
1155
1458
|
planCredits: data.planCredits ?? data.plan_credits,
|
|
1156
1459
|
billingPeriodStart: data.billingPeriodStart ?? data.billing_period_start ?? null,
|
|
@@ -1248,11 +1551,11 @@ function getOAuthProtectedResourceMetadataUrl(profile) {
|
|
|
1248
1551
|
const base = `${resource.origin}/.well-known/oauth-protected-resource`;
|
|
1249
1552
|
return profile.id === "full" ? base : `${base}${resource.pathname}`;
|
|
1250
1553
|
}
|
|
1251
|
-
function createOAuthChallengeResponse(
|
|
1554
|
+
function createOAuthChallengeResponse(error2, profile, details = {}) {
|
|
1252
1555
|
if (!isMcpOAuthEnabled()) {
|
|
1253
1556
|
return void 0;
|
|
1254
1557
|
}
|
|
1255
|
-
const errorMessage =
|
|
1558
|
+
const errorMessage = error2 instanceof Error ? error2.message : String(error2 || "Unauthorized");
|
|
1256
1559
|
const wwwAuthenticateDescription = errorMessage.replace(/[\r\n]+/g, " ").trim();
|
|
1257
1560
|
const wwwAuthenticate = [
|
|
1258
1561
|
...profile.advertiseOAuth ? [
|
|
@@ -1304,8 +1607,8 @@ function createInvalidOAuthRecoveryResponse(recovery) {
|
|
|
1304
1607
|
);
|
|
1305
1608
|
}
|
|
1306
1609
|
var CREDENTIAL_VALIDATION_RETRY_AFTER_SECONDS = 5;
|
|
1307
|
-
function createCredentialValidationUnavailableResponse(
|
|
1308
|
-
return new Response(
|
|
1610
|
+
function createCredentialValidationUnavailableResponse(error2) {
|
|
1611
|
+
return new Response(error2.message, {
|
|
1309
1612
|
headers: {
|
|
1310
1613
|
"Content-Type": "text/plain; charset=utf-8",
|
|
1311
1614
|
"Retry-After": String(CREDENTIAL_VALIDATION_RETRY_AFTER_SECONDS)
|
|
@@ -1601,13 +1904,13 @@ function makeAuthenticate(profile) {
|
|
|
1601
1904
|
session
|
|
1602
1905
|
);
|
|
1603
1906
|
return session;
|
|
1604
|
-
}).catch((
|
|
1907
|
+
}).catch((error2) => {
|
|
1605
1908
|
emitSearchCompanionAuthTelemetry(profile, request, "rejected");
|
|
1606
1909
|
emitLegacyKeyPathTelemetry(profile, request, "rejected");
|
|
1607
|
-
if (
|
|
1608
|
-
throw createInvalidCredentialResponse(
|
|
1910
|
+
if (error2 instanceof InvalidFirecrawlCredentialError) {
|
|
1911
|
+
throw createInvalidCredentialResponse(error2);
|
|
1609
1912
|
}
|
|
1610
|
-
if (
|
|
1913
|
+
if (error2 instanceof InvalidOAuthCredentialError) {
|
|
1611
1914
|
const recovery = invalidOAuthRecoveryPayload();
|
|
1612
1915
|
const oauthChallenge2 = createOAuthChallengeResponse(
|
|
1613
1916
|
new Error(recovery.message),
|
|
@@ -1616,15 +1919,15 @@ function makeAuthenticate(profile) {
|
|
|
1616
1919
|
);
|
|
1617
1920
|
throw oauthChallenge2 ?? createInvalidOAuthRecoveryResponse(recovery);
|
|
1618
1921
|
}
|
|
1619
|
-
if (
|
|
1620
|
-
throw createCredentialValidationUnavailableResponse(
|
|
1922
|
+
if (error2 instanceof CredentialValidationUnavailableError) {
|
|
1923
|
+
throw createCredentialValidationUnavailableResponse(error2);
|
|
1621
1924
|
}
|
|
1622
1925
|
const shouldChallenge = requestShouldReceiveOAuthChallenge(request, profile);
|
|
1623
|
-
const oauthChallenge = shouldChallenge ? createOAuthChallengeResponse(
|
|
1926
|
+
const oauthChallenge = shouldChallenge ? createOAuthChallengeResponse(error2, profile) : void 0;
|
|
1624
1927
|
if (oauthChallenge) {
|
|
1625
1928
|
throw oauthChallenge;
|
|
1626
1929
|
}
|
|
1627
|
-
throw
|
|
1930
|
+
throw error2;
|
|
1628
1931
|
});
|
|
1629
1932
|
if (request) {
|
|
1630
1933
|
request[authResultByRequest] = authResult;
|
|
@@ -1644,30 +1947,30 @@ function removeEmptyTopLevel(obj) {
|
|
|
1644
1947
|
}
|
|
1645
1948
|
return out;
|
|
1646
1949
|
}
|
|
1647
|
-
var searchDomainSchema =
|
|
1950
|
+
var searchDomainSchema = z8.string().trim().toLowerCase().min(1).max(253).regex(
|
|
1648
1951
|
/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/,
|
|
1649
1952
|
"Domain must be a valid hostname without protocol or path"
|
|
1650
1953
|
);
|
|
1651
1954
|
var searchToolBaseFields = {
|
|
1652
|
-
toolDetail:
|
|
1653
|
-
query:
|
|
1654
|
-
domainTools:
|
|
1955
|
+
toolDetail: z8.enum(["compact", "summary", "full"]).optional().describe("Compact by default. Compact returns only provider, capability and description; full includes contracts. Inspect selected compact tools with firecrawl_find_tools providers and capabilities."),
|
|
1956
|
+
query: z8.string().min(1).describe("Query for web and semantic tool discovery. Operators include quoted phrases, `-term`, `site:host`, `inurl:term`, `intitle:term`, and `related:host`; the set is non-exhaustive. Catalogue browsing is available through firecrawl_find_tools."),
|
|
1957
|
+
domainTools: z8.boolean().optional().describe(
|
|
1655
1958
|
"Include domain-matched tools for result URLs. Defaults to true when Alexandria is combined with web, news or images; semantic-only search leaves domain matching off."
|
|
1656
1959
|
),
|
|
1657
|
-
highlights:
|
|
1960
|
+
highlights: z8.boolean().optional().describe(
|
|
1658
1961
|
"Return query-relevant page excerpts for web and news results when available (default). Highlights appear in web `description` and news `snippet`; otherwise, original snippets are returned. Set to false to keep the original search snippets."
|
|
1659
1962
|
),
|
|
1660
|
-
limit:
|
|
1661
|
-
tbs:
|
|
1662
|
-
filter:
|
|
1663
|
-
location:
|
|
1664
|
-
includeDomains:
|
|
1665
|
-
excludeDomains:
|
|
1666
|
-
sources:
|
|
1667
|
-
categories:
|
|
1963
|
+
limit: z8.number().int().min(1).max(100).optional(),
|
|
1964
|
+
tbs: z8.string().optional(),
|
|
1965
|
+
filter: z8.string().optional(),
|
|
1966
|
+
location: z8.string().optional(),
|
|
1967
|
+
includeDomains: z8.array(searchDomainSchema).optional().describe("Hostnames to restrict results to. Mutually exclusive with excludeDomains."),
|
|
1968
|
+
excludeDomains: z8.array(searchDomainSchema).optional().describe("Hostnames to leave out of results. Mutually exclusive with includeDomains."),
|
|
1969
|
+
sources: z8.array(searchSourceSchema).optional().describe("Search sources; authenticated sessions default to web + alexandria, keyless sessions to web only. " + ALEXANDRIA_SOURCES_OPT_OUT + ' Use ["alexandria"] alone for provider discovery without web results.'),
|
|
1970
|
+
categories: z8.array(z8.enum(["research", "pdf", "developer"])).optional().describe(
|
|
1668
1971
|
'Limit results to specific source types. `research` restricts ordinary web results to research-affiliated websites and returns page snippets, which is separate from the `firecrawl_research_*` tools that search paper abstracts and full text across biomedical (PubMed, bioRxiv, medRxiv) and arXiv literature; `pdf` searches PDF results; `developer` searches an index built for coding agents over public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation. `developer` returns hits in `data.web` with `category: "developer"`; the other categories also filter `data.web`.'
|
|
1669
1972
|
),
|
|
1670
|
-
enterprise:
|
|
1973
|
+
enterprise: z8.array(z8.enum(["default", "anon", "zdr"])).optional()
|
|
1671
1974
|
};
|
|
1672
1975
|
function searchDomainsAreExclusive(args2) {
|
|
1673
1976
|
return !(args2.includeDomains?.length && args2.excludeDomains?.length);
|
|
@@ -1675,15 +1978,15 @@ function searchDomainsAreExclusive(args2) {
|
|
|
1675
1978
|
var SEARCH_DOMAINS_CONFLICT_MESSAGE = "includeDomains and excludeDomains cannot both be specified";
|
|
1676
1979
|
var EXCHANGE_KEY_REQUIRED_MESSAGE = "Alexandria requires an API key on a team with Alexandria access";
|
|
1677
1980
|
var EXCHANGE_MAX_CALLS = 10;
|
|
1678
|
-
var exchangeCallSchema =
|
|
1679
|
-
provider:
|
|
1680
|
-
capability:
|
|
1981
|
+
var exchangeCallSchema = z8.object({
|
|
1982
|
+
provider: z8.string().min(1).describe('Provider slug, e.g. "fred".'),
|
|
1983
|
+
capability: z8.string().min(1).describe(
|
|
1681
1984
|
'Capability address as returned by search or discover, e.g. "series/observations".'
|
|
1682
1985
|
),
|
|
1683
|
-
version:
|
|
1684
|
-
options:
|
|
1986
|
+
version: z8.string().trim().min(1).max(128).optional().describe("Optional published workflow version. Omit to use the latest version."),
|
|
1987
|
+
options: z8.record(z8.string(), z8.any()).optional().describe("Capability options as declared by its contract.")
|
|
1685
1988
|
});
|
|
1686
|
-
var exchangeCallsSchema =
|
|
1989
|
+
var exchangeCallsSchema = z8.array(exchangeCallSchema).min(1).max(EXCHANGE_MAX_CALLS);
|
|
1687
1990
|
function assertExchangeCredential(session) {
|
|
1688
1991
|
if (hasCredential(session)) return;
|
|
1689
1992
|
const payload = {
|
|
@@ -1749,26 +2052,26 @@ Do not retry until acceptance is confirmed.`,
|
|
|
1749
2052
|
}
|
|
1750
2053
|
);
|
|
1751
2054
|
}
|
|
1752
|
-
function throwIfTermsRequired(
|
|
1753
|
-
const source =
|
|
2055
|
+
function throwIfTermsRequired(error2, context) {
|
|
2056
|
+
const source = error2;
|
|
1754
2057
|
const action = termsRequiredAction(source?.response?.data ?? source?.details);
|
|
1755
2058
|
if (action) throw termsRequiredError(action, context);
|
|
1756
2059
|
}
|
|
1757
2060
|
async function relayTermsRequired(run, context) {
|
|
1758
2061
|
try {
|
|
1759
2062
|
return await run();
|
|
1760
|
-
} catch (
|
|
1761
|
-
throwIfTermsRequired(
|
|
1762
|
-
throw
|
|
2063
|
+
} catch (error2) {
|
|
2064
|
+
throwIfTermsRequired(error2, context);
|
|
2065
|
+
throw error2;
|
|
1763
2066
|
}
|
|
1764
2067
|
}
|
|
1765
2068
|
async function relayExchangeError(run, context) {
|
|
1766
2069
|
try {
|
|
1767
2070
|
return await run();
|
|
1768
|
-
} catch (
|
|
1769
|
-
throwIfTermsRequired(
|
|
1770
|
-
const response =
|
|
1771
|
-
if (!response || response.status === 401) throw
|
|
2071
|
+
} catch (error2) {
|
|
2072
|
+
throwIfTermsRequired(error2, context);
|
|
2073
|
+
const response = error2?.response;
|
|
2074
|
+
if (!response || response.status === 401) throw error2;
|
|
1772
2075
|
const data = response.data;
|
|
1773
2076
|
const message = typeof data?.error === "string" ? data.error : `Alexandria request failed (HTTP ${response.status})`;
|
|
1774
2077
|
const disabledProvider = response.status === 403 ? context.providers?.find((provider) => message === `Access to ${provider} is disabled for this organization.`) : void 0;
|
|
@@ -1799,14 +2102,14 @@ async function relayExchangeError(run, context) {
|
|
|
1799
2102
|
async function postSearchWithFallback(client, body, implicitTools) {
|
|
1800
2103
|
try {
|
|
1801
2104
|
return await client.http.post("/v2/search", body);
|
|
1802
|
-
} catch (
|
|
1803
|
-
const response =
|
|
2105
|
+
} catch (error2) {
|
|
2106
|
+
const response = error2?.response;
|
|
1804
2107
|
const unavailable = [
|
|
1805
2108
|
"Provider discovery requires access and does not support zero data retention.",
|
|
1806
2109
|
"This endpoint is not enabled for this team.",
|
|
1807
2110
|
"Exchange is not enabled for this team."
|
|
1808
2111
|
].includes(response?.data?.error);
|
|
1809
|
-
if (!implicitTools || response?.status !== 403 || !unavailable) throw
|
|
2112
|
+
if (!implicitTools || response?.status !== 403 || !unavailable) throw error2;
|
|
1810
2113
|
return client.http.post("/v2/search", {
|
|
1811
2114
|
...body,
|
|
1812
2115
|
sources: ["web"],
|
|
@@ -1845,10 +2148,9 @@ var ConsoleLogger = class {
|
|
|
1845
2148
|
var openAiAppsChallengeToken = normalizeHeader(
|
|
1846
2149
|
process.env.OPENAI_APPS_CHALLENGE_TOKEN
|
|
1847
2150
|
);
|
|
1848
|
-
var FULL_PROFILE_INSTRUCTIONS = `Firecrawl provides web search, page retrieval, site URL discovery, multi-page collection, structured page data, monitoring, and multi-source research that returns structured data. Match the requested operation to the tool boundary: firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema, firecrawl_map enumerates URLs under a site without retrieving their content, and firecrawl_agent runs multi-source research and returns structured data when the URLs are not known or the answer spans several sites (an entity plus its fields, a list, a dataset); its result is read with firecrawl_agent_status. Authenticated firecrawl_search returns web results together with matching Alexandria providers in data.tools. ${ALEXANDRIA_CATALOGUE_SENTENCE}
|
|
1849
|
-
${ALEXANDRIA_FEEDBACK_GUIDANCE}`;
|
|
2151
|
+
var FULL_PROFILE_INSTRUCTIONS = `Firecrawl provides web search, page retrieval, site URL discovery, multi-page collection, structured page data, monitoring, and multi-source research that returns structured data. Match the requested operation to the tool boundary: firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema, firecrawl_map enumerates URLs under a site without retrieving their content, and firecrawl_agent runs multi-source research and returns structured data when the URLs are not known or the answer spans several sites (an entity plus its fields, a list, a dataset); its result is read with firecrawl_agent_status. Authenticated firecrawl_search returns web results together with matching Alexandria providers in data.tools. ${ALEXANDRIA_CATALOGUE_SENTENCE} A matching provider can return the same fields across several entities, provenance, exact figures or timestamps, or a large set of records through firecrawl_scrape with its published contract. If web results already answer the question, use them. For the same fields across multiple pages, firecrawl_find_tools offers free provider discovery. Use firecrawl_find_tools to read a contract that was not returned in full or to browse the catalogue by category. ${ALEXANDRIA_SOURCES_OPT_OUT} If no provider fits, continue with web search or firecrawl_agent. For biomedical, life-science, clinical, or arXiv literature, the firecrawl_research_* tools search a paper index of abstracts and full text; firecrawl_search with categories: ["research"] is a website filter over ordinary web results and reaches different sources. For a programming question (code behaviour, a library or framework, an API contract, an error message, or a known bug), firecrawl_developer_search (or firecrawl_search with categories: ["developer"]) searches an index of public repositories, GitHub issues, merged pull requests, READMEs, and code documentation. Alexandria requestId identifies one logical execution; repeated attempts of the identical payload require the same ID, including pending or uncertain executions. firecrawl_search with sources: [{type: "alexandria"}] returns compact tool summaries in data.tools; toolDetail: "full" includes contracts, firecrawl_find_tools starts with categories, lists providers, then compact tools, and expands the selected full contract, and firecrawl_scrape with alexandria: [{provider, capability, options}] executes up to ten capabilities and returns their results. Alexandria access needs an API key on a team with it enabled. A terms-gated Alexandria provider fails with code THIRD_PARTY_DATA_TERMS_REQUIRED and a requiresAction.url. Through firecrawl_scrape, terms/show displays the agreement and terms/accept records acceptance; acceptance requires explicit user authorization for the reviewed version and digest and confirmed:true. A data request does not authorize acceptance. The dashboard URL provides an alternative for an organization admin. A retained result provides nextTool access without repeating a successful provider call. Provide only the required inputs and account for stated network or external side effects.`;
|
|
1850
2152
|
var KEYLESS_PROFILE_INSTRUCTIONS = `Hosted keyless sessions expose firecrawl_search, firecrawl_scrape, and firecrawl_parse with usage limits. firecrawl_search searches the web. For programming questions, firecrawl_search with categories: ["developer"] searches indexed public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation. For biomedical, life-science, clinical, or arXiv literature, firecrawl_search with categories: ["research"] filters ordinary web results to research-affiliated websites. firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema. firecrawl_parse processes supported local files through its two-phase upload flow. An Authorization bearer API key can provide higher usage limits and expose additional tools, subject to plan, deployment, and team policy, including firecrawl_map for site URL discovery, firecrawl_agent and firecrawl_agent_status for multi-source research that returns structured data when the URLs are not known, firecrawl_research_* for paper-index and repository research, and firecrawl_find_tools as the progressive Alexandria catalogue lookup alongside the Alexandria options of firecrawl_search and firecrawl_scrape for catalogued data providers.`;
|
|
1851
|
-
var SEARCH_PROFILE_INSTRUCTIONS = ALEXANDRIA_SEARCH_INSTRUCTIONS + ` Firecrawl provides web, developer, and research search, and executes catalogued Alexandria data providers. Use firecrawl_search to find relevant results across the web and specialized indexes; authenticated searches also return matching Alexandria providers in data.tools.
|
|
2153
|
+
var SEARCH_PROFILE_INSTRUCTIONS = ALEXANDRIA_SEARCH_INSTRUCTIONS + ` Firecrawl provides web, developer, and research search, and executes catalogued Alexandria data providers. Use firecrawl_search to find relevant results across the web and specialized indexes; authenticated searches also return matching Alexandria providers in data.tools. firecrawl_find_tools provides catalogue browsing and provider contracts; firecrawl_scrape with an alexandria body executes a selected capability; firecrawl_scrape with a url retrieves one supplied page. For a programming question, firecrawl_developer_search searches indexed public repositories, GitHub issues, merged pull requests, READMEs, and code documentation and returns the matched passages, and skills: "only" narrows it to agent-skill files; firecrawl_search with categories: ["developer"] reaches the same index beside ordinary web results, returning the hits in the web group rather than as passages and offering no skills filter. For a biomedical, life-science, clinical, or arXiv literature question, the firecrawl_research_* tools search the paper index, while categories: ["research"] on firecrawl_search filters ordinary web results to research-affiliated websites. Use the firecrawl_research_* tools to search academic and research literature, expand from anchor papers via the citation graph, and read full-text passages from a specific paper. Search and discovery tools are read-only and return ranked results. Billing: web, developer and research search are billed per request; Alexandria discovery (firecrawl_search with sources ["alexandria"] alone, and firecrawl_find_tools) is free; firecrawl_scrape is billed, as a page retrieval in url mode or at each executed capability's listed price in alexandria mode.`;
|
|
1852
2154
|
var SEARCH_PROFILE_TOOLS = /* @__PURE__ */ new Set([
|
|
1853
2155
|
"firecrawl_search",
|
|
1854
2156
|
"firecrawl_developer_search",
|
|
@@ -1992,17 +2294,17 @@ function invalidOAuthRecoveryPayload() {
|
|
|
1992
2294
|
message: INVALID_OAUTH_MESSAGE
|
|
1993
2295
|
});
|
|
1994
2296
|
}
|
|
1995
|
-
function isCoreCredentialRejection(
|
|
1996
|
-
if (!(
|
|
1997
|
-
const candidate =
|
|
2297
|
+
function isCoreCredentialRejection(error2) {
|
|
2298
|
+
if (!(error2 instanceof Error)) return false;
|
|
2299
|
+
const candidate = error2;
|
|
1998
2300
|
return candidate.status === 401 || candidate.response?.status === 401;
|
|
1999
2301
|
}
|
|
2000
2302
|
async function runWithCredentialRecovery(run, requestId, session) {
|
|
2001
2303
|
try {
|
|
2002
2304
|
return await run();
|
|
2003
|
-
} catch (
|
|
2004
|
-
if (session?.authType !== "api-key" || !isCoreCredentialRejection(
|
|
2005
|
-
throw
|
|
2305
|
+
} catch (error2) {
|
|
2306
|
+
if (session?.authType !== "api-key" || !isCoreCredentialRejection(error2)) {
|
|
2307
|
+
throw error2;
|
|
2006
2308
|
}
|
|
2007
2309
|
const payload = recoveryPayload("CREDENTIAL_INVALID", requestId);
|
|
2008
2310
|
throw new UserError2(String(payload.message), payload);
|
|
@@ -2055,7 +2357,7 @@ function deprecatedExtractPayload() {
|
|
|
2055
2357
|
docs_url: "https://docs.firecrawl.dev/developer-guides/usage-guides/choosing-the-data-extractor"
|
|
2056
2358
|
};
|
|
2057
2359
|
}
|
|
2058
|
-
function emitActionLog(toolName, status, session,
|
|
2360
|
+
function emitActionLog(toolName, status, session, error2, requestId = randomUUID(), code) {
|
|
2059
2361
|
if (process.env.CLOUD_SERVICE !== "true") return;
|
|
2060
2362
|
const payload = {
|
|
2061
2363
|
team_id: session?.teamId,
|
|
@@ -2067,7 +2369,7 @@ function emitActionLog(toolName, status, session, error, requestId = randomUUID(
|
|
|
2067
2369
|
status,
|
|
2068
2370
|
request_id: requestId,
|
|
2069
2371
|
resource: primaryProfile.resourceUrl,
|
|
2070
|
-
...
|
|
2372
|
+
...error2 ? { error_class: error2 instanceof Error ? error2.name : typeof error2 } : {},
|
|
2071
2373
|
...code ? { code } : {}
|
|
2072
2374
|
};
|
|
2073
2375
|
console.error("[MCP_ACTION]", JSON.stringify(payload));
|
|
@@ -2167,9 +2469,9 @@ function guardHostedTool(tool, { logActions }) {
|
|
|
2167
2469
|
const result = await runTool();
|
|
2168
2470
|
emitActionLog(tool.name, "success", invocationSession, void 0, requestId);
|
|
2169
2471
|
return result;
|
|
2170
|
-
} catch (
|
|
2171
|
-
emitActionLog(tool.name, "error", invocationSession,
|
|
2172
|
-
throw
|
|
2472
|
+
} catch (error2) {
|
|
2473
|
+
emitActionLog(tool.name, "error", invocationSession, error2, requestId);
|
|
2474
|
+
throw error2;
|
|
2173
2475
|
}
|
|
2174
2476
|
}
|
|
2175
2477
|
};
|
|
@@ -2261,12 +2563,9 @@ function getClient(session) {
|
|
|
2261
2563
|
});
|
|
2262
2564
|
return client;
|
|
2263
2565
|
}
|
|
2264
|
-
function
|
|
2566
|
+
function asText(data) {
|
|
2265
2567
|
return JSON.stringify(data, null, 2);
|
|
2266
2568
|
}
|
|
2267
|
-
function compactText(data) {
|
|
2268
|
-
return JSON.stringify(data);
|
|
2269
|
-
}
|
|
2270
2569
|
var safeActionTypes = ["wait", "screenshot", "scroll", "scrape"];
|
|
2271
2570
|
var otherActions = [
|
|
2272
2571
|
"click",
|
|
@@ -2332,10 +2631,10 @@ function transformScrapeParams(args2) {
|
|
|
2332
2631
|
delete out.pdfOptions;
|
|
2333
2632
|
return out;
|
|
2334
2633
|
}
|
|
2335
|
-
var scrapeParamsSchema =
|
|
2336
|
-
url:
|
|
2337
|
-
formats:
|
|
2338
|
-
|
|
2634
|
+
var scrapeParamsSchema = z8.object({
|
|
2635
|
+
url: z8.string().url(),
|
|
2636
|
+
formats: z8.array(
|
|
2637
|
+
z8.enum([
|
|
2339
2638
|
"markdown",
|
|
2340
2639
|
"html",
|
|
2341
2640
|
"rawHtml",
|
|
@@ -2349,87 +2648,93 @@ var scrapeParamsSchema = z7.object({
|
|
|
2349
2648
|
"audio"
|
|
2350
2649
|
])
|
|
2351
2650
|
).optional(),
|
|
2352
|
-
jsonOptions:
|
|
2353
|
-
prompt:
|
|
2354
|
-
schema:
|
|
2651
|
+
jsonOptions: z8.object({
|
|
2652
|
+
prompt: z8.string().optional(),
|
|
2653
|
+
schema: z8.record(z8.string(), z8.any()).optional()
|
|
2355
2654
|
}).optional(),
|
|
2356
|
-
queryOptions:
|
|
2357
|
-
prompt:
|
|
2358
|
-
mode:
|
|
2655
|
+
queryOptions: z8.object({
|
|
2656
|
+
prompt: z8.string().max(1e4),
|
|
2657
|
+
mode: z8.enum(["directQuote", "freeform"]).default("freeform")
|
|
2359
2658
|
}).optional(),
|
|
2360
|
-
screenshotOptions:
|
|
2361
|
-
fullPage:
|
|
2362
|
-
quality:
|
|
2363
|
-
viewport:
|
|
2659
|
+
screenshotOptions: z8.object({
|
|
2660
|
+
fullPage: z8.boolean().optional(),
|
|
2661
|
+
quality: z8.number().optional(),
|
|
2662
|
+
viewport: z8.object({ width: z8.number(), height: z8.number() }).optional()
|
|
2364
2663
|
}).optional(),
|
|
2365
|
-
parsers:
|
|
2366
|
-
pdfOptions:
|
|
2367
|
-
maxPages:
|
|
2664
|
+
parsers: z8.array(z8.enum(["pdf"])).optional(),
|
|
2665
|
+
pdfOptions: z8.object({
|
|
2666
|
+
maxPages: z8.number().int().min(1).max(1e4).optional()
|
|
2368
2667
|
}).optional(),
|
|
2369
|
-
onlyMainContent:
|
|
2370
|
-
redactPII:
|
|
2371
|
-
includeTags:
|
|
2372
|
-
excludeTags:
|
|
2373
|
-
waitFor:
|
|
2668
|
+
onlyMainContent: z8.boolean().optional(),
|
|
2669
|
+
redactPII: z8.boolean().optional(),
|
|
2670
|
+
includeTags: z8.array(z8.string()).optional(),
|
|
2671
|
+
excludeTags: z8.array(z8.string()).optional(),
|
|
2672
|
+
waitFor: z8.number().optional(),
|
|
2374
2673
|
...SAFE_MODE ? {} : {
|
|
2375
|
-
actions:
|
|
2376
|
-
|
|
2377
|
-
type:
|
|
2378
|
-
selector:
|
|
2379
|
-
milliseconds:
|
|
2380
|
-
text:
|
|
2381
|
-
key:
|
|
2382
|
-
direction:
|
|
2383
|
-
script:
|
|
2384
|
-
fullPage:
|
|
2674
|
+
actions: z8.array(
|
|
2675
|
+
z8.object({
|
|
2676
|
+
type: z8.enum(allowedActionTypes),
|
|
2677
|
+
selector: z8.string().optional(),
|
|
2678
|
+
milliseconds: z8.number().optional(),
|
|
2679
|
+
text: z8.string().optional(),
|
|
2680
|
+
key: z8.string().optional(),
|
|
2681
|
+
direction: z8.enum(["up", "down"]).optional(),
|
|
2682
|
+
script: z8.string().optional(),
|
|
2683
|
+
fullPage: z8.boolean().optional()
|
|
2385
2684
|
})
|
|
2386
2685
|
).optional()
|
|
2387
2686
|
},
|
|
2388
|
-
mobile:
|
|
2389
|
-
skipTlsVerification:
|
|
2390
|
-
removeBase64Images:
|
|
2391
|
-
location:
|
|
2392
|
-
country:
|
|
2393
|
-
languages:
|
|
2687
|
+
mobile: z8.boolean().optional(),
|
|
2688
|
+
skipTlsVerification: z8.boolean().optional(),
|
|
2689
|
+
removeBase64Images: z8.boolean().optional(),
|
|
2690
|
+
location: z8.object({
|
|
2691
|
+
country: z8.string().optional(),
|
|
2692
|
+
languages: z8.array(z8.string()).optional()
|
|
2394
2693
|
}).optional(),
|
|
2395
|
-
storeInCache:
|
|
2396
|
-
zeroDataRetention:
|
|
2397
|
-
maxAge:
|
|
2398
|
-
lockdown:
|
|
2399
|
-
proxy:
|
|
2400
|
-
profile:
|
|
2401
|
-
name:
|
|
2402
|
-
saveChanges:
|
|
2694
|
+
storeInCache: z8.boolean().optional(),
|
|
2695
|
+
zeroDataRetention: z8.boolean().optional(),
|
|
2696
|
+
maxAge: z8.number().optional(),
|
|
2697
|
+
lockdown: z8.boolean().optional(),
|
|
2698
|
+
proxy: z8.enum(["basic", "stealth", "enhanced", "auto"]).optional(),
|
|
2699
|
+
profile: z8.object({
|
|
2700
|
+
name: z8.string(),
|
|
2701
|
+
saveChanges: z8.boolean().optional()
|
|
2403
2702
|
}).optional()
|
|
2404
2703
|
});
|
|
2704
|
+
var ALEXANDRIA_IGNORED_SCRAPE_OPTIONS = /* @__PURE__ */ new Set(["toolDetail", "domainTools"]);
|
|
2405
2705
|
var scrapeToolParamsSchema = scrapeParamsSchema.extend({
|
|
2406
|
-
url:
|
|
2407
|
-
timeout:
|
|
2408
|
-
requestId:
|
|
2409
|
-
"Alexandria execution
|
|
2706
|
+
url: z8.string().url().optional(),
|
|
2707
|
+
timeout: z8.number().int().positive().optional().describe("Execution timeout in milliseconds."),
|
|
2708
|
+
requestId: z8.string().regex(/^[A-Za-z0-9._:-]{1,128}$/).optional().describe(
|
|
2709
|
+
"Identifies one logical Alexandria execution; generated when omitted and returned with the result. Repeated attempts of the identical payload require the same ID. A new ID cannot reconcile a pending or uncertain execution. A caller-supplied ID supports recovery if no response is received. Errors relay a code and may include chargeId. request_in_flight (409) means the execution is pending; request_unresolved (503) requires reconciliation under the same ID; duplicate_request (409) means the ID belongs to a different payload; unknown_provider (404), insufficient_credits (402) and billing_unavailable (503) mean nothing executed."
|
|
2410
2710
|
),
|
|
2411
|
-
alexandria:
|
|
2412
|
-
"
|
|
2711
|
+
alexandria: z8.union([exchangeCallSchema, exchangeCallsSchema]).optional().describe(
|
|
2712
|
+
"Catalogued Alexandria capability invocation, mutually exclusive with url. One {provider, capability, options} object or an array of 1-10, with contracts available through firecrawl_search or firecrawl_find_tools. Each call may include version to pin a published workflow; omitting it uses latest. Only requestId and timeout are supported alongside alexandria. " + ALEXANDRIA_CONTRACT_GUIDANCE + " Returns per-capability results in data.alexandria with data, records, or an error with a code; individual capabilities can fail even when the outer response succeeds. nextTool identifies access to retained results without repeating a successful provider call. Requires an API key on a team with Alexandria enabled. THIRD_PARTY_DATA_TERMS_REQUIRED (403) means execution is blocked by provider terms, with requiresAction.url for review by an organization admin. Through this tool, terms/show displays the agreement and terms/accept records acceptance; acceptance requires explicit user authorization for the reviewed version and digest and confirmed:true. A data request does not authorize acceptance. Provider execution remains blocked until acceptance is confirmed."
|
|
2413
2713
|
),
|
|
2414
|
-
toolDetail:
|
|
2415
|
-
domainTools:
|
|
2416
|
-
"URL mode only: include domain-matched Alexandria tools for the page in tools on the returned document."
|
|
2714
|
+
toolDetail: z8.enum(["compact", "summary", "full"]).optional().describe("URL mode only: domain discovery detail, summary by default; compact returns provider/capability/description, full includes contracts. Ignored with alexandria."),
|
|
2715
|
+
domainTools: z8.boolean().optional().describe(
|
|
2716
|
+
"URL mode only: include domain-matched Alexandria tools for the page in tools on the returned document. Ignored with alexandria."
|
|
2417
2717
|
)
|
|
2418
2718
|
}).refine(
|
|
2419
2719
|
(args2) => Boolean(args2.url) !== Boolean(args2.alexandria),
|
|
2420
2720
|
"Provide exactly one of url or alexandria"
|
|
2421
2721
|
).refine(
|
|
2422
2722
|
(args2) => !args2.alexandria || Object.entries(args2).every(
|
|
2423
|
-
([key, value]) => key === "alexandria" || key === "requestId" || key === "timeout" ||
|
|
2723
|
+
([key, value]) => key === "alexandria" || key === "requestId" || key === "timeout" || // URL-mode discovery options that mean nothing when executing a
|
|
2724
|
+
// provider. Agents carry toolDetail over from firecrawl_search (where
|
|
2725
|
+
// it selects contract detail), so accept and ignore them rather than
|
|
2726
|
+
// failing the call: Codex sent toolDetail on 18 of 315 Alexandria
|
|
2727
|
+
// executions across the AX EXP-058 runs, each one a wasted round trip.
|
|
2728
|
+
ALEXANDRIA_IGNORED_SCRAPE_OPTIONS.has(key) || value === void 0
|
|
2424
2729
|
),
|
|
2425
2730
|
"alexandria cannot be combined with url or other scrape options"
|
|
2426
2731
|
).refine(
|
|
2427
2732
|
(args2) => !args2.requestId || !!args2.alexandria,
|
|
2428
2733
|
"requestId applies to Alexandria execution only"
|
|
2429
2734
|
);
|
|
2430
|
-
var parseOptionParamsSchema =
|
|
2431
|
-
formats:
|
|
2432
|
-
|
|
2735
|
+
var parseOptionParamsSchema = z8.object({
|
|
2736
|
+
formats: z8.array(
|
|
2737
|
+
z8.enum([
|
|
2433
2738
|
"markdown",
|
|
2434
2739
|
"html",
|
|
2435
2740
|
"rawHtml",
|
|
@@ -2439,48 +2744,48 @@ var parseOptionParamsSchema = z7.object({
|
|
|
2439
2744
|
"query"
|
|
2440
2745
|
])
|
|
2441
2746
|
).optional(),
|
|
2442
|
-
jsonOptions:
|
|
2443
|
-
prompt:
|
|
2444
|
-
schema:
|
|
2747
|
+
jsonOptions: z8.object({
|
|
2748
|
+
prompt: z8.string().optional(),
|
|
2749
|
+
schema: z8.record(z8.string(), z8.any()).optional()
|
|
2445
2750
|
}).optional(),
|
|
2446
|
-
queryOptions:
|
|
2447
|
-
prompt:
|
|
2448
|
-
mode:
|
|
2751
|
+
queryOptions: z8.object({
|
|
2752
|
+
prompt: z8.string().max(1e4),
|
|
2753
|
+
mode: z8.enum(["directQuote", "freeform"]).default("freeform")
|
|
2449
2754
|
}).optional(),
|
|
2450
|
-
parsers:
|
|
2451
|
-
pdfOptions:
|
|
2452
|
-
maxPages:
|
|
2755
|
+
parsers: z8.array(z8.enum(["pdf"])).optional(),
|
|
2756
|
+
pdfOptions: z8.object({
|
|
2757
|
+
maxPages: z8.number().int().min(1).max(1e4).optional()
|
|
2453
2758
|
}).optional(),
|
|
2454
|
-
onlyMainContent:
|
|
2455
|
-
redactPII:
|
|
2456
|
-
includeTags:
|
|
2457
|
-
excludeTags:
|
|
2458
|
-
removeBase64Images:
|
|
2459
|
-
skipTlsVerification:
|
|
2460
|
-
storeInCache:
|
|
2461
|
-
zeroDataRetention:
|
|
2462
|
-
maxAge:
|
|
2463
|
-
proxy:
|
|
2759
|
+
onlyMainContent: z8.boolean().optional(),
|
|
2760
|
+
redactPII: z8.boolean().optional(),
|
|
2761
|
+
includeTags: z8.array(z8.string()).optional(),
|
|
2762
|
+
excludeTags: z8.array(z8.string()).optional(),
|
|
2763
|
+
removeBase64Images: z8.boolean().optional(),
|
|
2764
|
+
skipTlsVerification: z8.boolean().optional(),
|
|
2765
|
+
storeInCache: z8.boolean().optional(),
|
|
2766
|
+
zeroDataRetention: z8.boolean().optional(),
|
|
2767
|
+
maxAge: z8.number().optional().describe("Ignored: parse never reuses or stores indexed content."),
|
|
2768
|
+
proxy: z8.enum(["basic", "auto"]).optional()
|
|
2464
2769
|
});
|
|
2465
2770
|
var localParseParamsSchema = parseOptionParamsSchema.extend({
|
|
2466
|
-
filePath:
|
|
2771
|
+
filePath: z8.string().min(1).describe(
|
|
2467
2772
|
"Absolute or relative path to a local file to parse. Supported: .html, .htm, .pdf, .docx, .doc, .odt, .rtf, .xlsx, .xls"
|
|
2468
2773
|
),
|
|
2469
|
-
contentType:
|
|
2774
|
+
contentType: z8.string().optional().describe(
|
|
2470
2775
|
"Optional MIME type override. If omitted, the server infers the file kind from the extension."
|
|
2471
2776
|
)
|
|
2472
2777
|
});
|
|
2473
2778
|
var hostedParseParamsSchema = parseOptionParamsSchema.extend({
|
|
2474
|
-
filePath:
|
|
2779
|
+
filePath: z8.string().min(1).optional().describe(
|
|
2475
2780
|
"Phase 1 only: path to the local file on the caller/harness machine. Hosted MCP will not read or stat this path; it is used only to produce upload instructions."
|
|
2476
2781
|
),
|
|
2477
|
-
uploadRef:
|
|
2782
|
+
uploadRef: z8.string().min(1).optional().describe(
|
|
2478
2783
|
"Phase 2 only: short-lived upload reference returned by phase 1 after the local PUT upload completes."
|
|
2479
2784
|
),
|
|
2480
|
-
contentType:
|
|
2785
|
+
contentType: z8.string().optional().describe(
|
|
2481
2786
|
"Phase 1 MIME type override. If omitted, the server infers it from the file extension without reading the file."
|
|
2482
2787
|
),
|
|
2483
|
-
declaredSizeBytes:
|
|
2788
|
+
declaredSizeBytes: z8.number().int().positive().optional().describe(
|
|
2484
2789
|
"Optional phase 1 size declaration. Hosted MCP does not stat the file; provide this only if the caller already knows it."
|
|
2485
2790
|
)
|
|
2486
2791
|
}).superRefine((value, ctx) => {
|
|
@@ -2488,7 +2793,7 @@ var hostedParseParamsSchema = parseOptionParamsSchema.extend({
|
|
|
2488
2793
|
const hasUploadRef = typeof value.uploadRef === "string" && value.uploadRef.length > 0;
|
|
2489
2794
|
if (hasFilePath === hasUploadRef) {
|
|
2490
2795
|
ctx.addIssue({
|
|
2491
|
-
code:
|
|
2796
|
+
code: z8.ZodIssueCode.custom,
|
|
2492
2797
|
message: "Hosted firecrawl_parse requires exactly one of filePath (phase 1) or uploadRef (phase 2).",
|
|
2493
2798
|
path: hasFilePath && hasUploadRef ? ["uploadRef"] : ["filePath"]
|
|
2494
2799
|
});
|
|
@@ -2609,7 +2914,7 @@ async function executeHostedParse(args2, session, log, origin) {
|
|
|
2609
2914
|
);
|
|
2610
2915
|
}
|
|
2611
2916
|
if (!hasCredential(session) && !isKeylessMode(session)) {
|
|
2612
|
-
return
|
|
2917
|
+
return asText({
|
|
2613
2918
|
success: false,
|
|
2614
2919
|
mode: "hosted-upload-ref-auth-required",
|
|
2615
2920
|
message: "Hosted firecrawl_parse requires an authenticated Firecrawl session or keyless eligibility before a local file upload URL can be minted. Connect a Firecrawl account, provide an API key, or use keyless hosted MCP while eligible, then call firecrawl_parse again."
|
|
@@ -2647,7 +2952,7 @@ async function executeHostedParse(args2, session, log, origin) {
|
|
|
2647
2952
|
}
|
|
2648
2953
|
const uploadHeaders = upload.headers && Object.keys(upload.headers).length > 0 ? upload.headers : (upload.method ?? "PUT").toUpperCase() === "POST" ? {} : { "Content-Type": contentType };
|
|
2649
2954
|
const uploadForCommand = { ...upload, headers: uploadHeaders };
|
|
2650
|
-
return
|
|
2955
|
+
return asText({
|
|
2651
2956
|
success: true,
|
|
2652
2957
|
mode: "hosted-upload-ref-awaiting-upload",
|
|
2653
2958
|
message: "Hosted MCP cannot read local files. Run the local upload command, then call firecrawl_parse again with uploadRef. No Firecrawl API key is included in this command.",
|
|
@@ -2683,7 +2988,7 @@ async function executeHostedParse(args2, session, log, origin) {
|
|
|
2683
2988
|
session,
|
|
2684
2989
|
origin
|
|
2685
2990
|
);
|
|
2686
|
-
return
|
|
2991
|
+
return asText(parseJson);
|
|
2687
2992
|
}
|
|
2688
2993
|
var scrapeTool = {
|
|
2689
2994
|
name: "firecrawl_scrape",
|
|
@@ -2697,14 +3002,15 @@ var scrapeTool = {
|
|
|
2697
3002
|
// Does not modify, delete, or write to external websites.
|
|
2698
3003
|
},
|
|
2699
3004
|
description: `
|
|
2700
|
-
Scrape one URL and return its content: markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema. Use it when the request identifies a page and needs its content or defined fields. Use \`firecrawl_search\` when additional web sources are needed, \`firecrawl_map\`
|
|
3005
|
+
Scrape one URL and return its content: markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema. Use it when the request identifies a page and needs its content or defined fields. Use \`firecrawl_search\` when additional web sources are needed; on an authenticated session, \`firecrawl_map\` lists a site's URLs and \`firecrawl_crawl\` collects a set of pages.
|
|
2701
3006
|
|
|
2702
3007
|
Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch or a smaller \`maxAge\` to bound staleness. A successful response does not by itself confirm the page is still current. Browser actions can change the live page when interactive actions are enabled. Authenticated responses can include a \`metadata.scrapeId\` for optional scrape feedback.
|
|
2703
3008
|
|
|
2704
|
-
On an authenticated session with Alexandria access,
|
|
3009
|
+
On an authenticated session with Alexandria access, \`firecrawl_search\` with \`sources\` unset and \`firecrawl_find_tools\` can discover providers for the same fields across several pages; a matching provider returns typed records in one call. Keyless sessions have no provider matches.
|
|
2705
3010
|
|
|
2706
|
-
Alexandria mode:
|
|
3011
|
+
Alexandria mode, on an authenticated session with Alexandria access: \`alexandria\` selects catalogued capability execution and is mutually exclusive with \`url\`; the \`alexandria\` and \`requestId\` parameters describe batching, retries, errors and provider terms.
|
|
2707
3012
|
`,
|
|
3013
|
+
outputSchema: scrapeOutputSchema,
|
|
2708
3014
|
parameters: scrapeToolParamsSchema,
|
|
2709
3015
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
2710
3016
|
const origin = requestOrigin(mcpClient, session);
|
|
@@ -2719,7 +3025,9 @@ Alexandria mode: pass \`alexandria\` instead of \`url\` to execute catalogued ca
|
|
|
2719
3025
|
log.info("Executing Alexandria capabilities", {
|
|
2720
3026
|
count: Array.isArray(alexandria) ? alexandria.length : 1
|
|
2721
3027
|
});
|
|
2722
|
-
return
|
|
3028
|
+
return structuredJsonText(
|
|
3029
|
+
await executeExchangeCalls(session, alexandria, suppliedRequestId, options.timeout, origin)
|
|
3030
|
+
);
|
|
2723
3031
|
}
|
|
2724
3032
|
const transformed = transformScrapeParams(
|
|
2725
3033
|
options
|
|
@@ -2740,7 +3048,7 @@ Alexandria mode: pass \`alexandria\` instead of \`url\` to execute catalogued ca
|
|
|
2740
3048
|
},
|
|
2741
3049
|
session
|
|
2742
3050
|
);
|
|
2743
|
-
return
|
|
3051
|
+
return structuredText(json?.data ?? json);
|
|
2744
3052
|
}
|
|
2745
3053
|
const client = getClient(session);
|
|
2746
3054
|
const res = await relayTermsRequired(
|
|
@@ -2750,10 +3058,13 @@ Alexandria mode: pass \`alexandria\` instead of \`url\` to execute catalogued ca
|
|
|
2750
3058
|
}),
|
|
2751
3059
|
{ tool: "firecrawl_scrape" }
|
|
2752
3060
|
);
|
|
2753
|
-
return
|
|
3061
|
+
return structuredText(res);
|
|
2754
3062
|
}
|
|
2755
3063
|
};
|
|
2756
|
-
server.addTool(
|
|
3064
|
+
server.addTool({
|
|
3065
|
+
...scrapeTool,
|
|
3066
|
+
_meta: { "anthropic/alwaysLoad": true }
|
|
3067
|
+
});
|
|
2757
3068
|
server.addTool({
|
|
2758
3069
|
name: "firecrawl_map",
|
|
2759
3070
|
annotations: {
|
|
@@ -2770,13 +3081,14 @@ Enumerate URLs indexed under one website through Firecrawl without fetching each
|
|
|
2770
3081
|
|
|
2771
3082
|
Returns matching URLs rather than page bodies. Retrieve one page with \`firecrawl_scrape\`; collect content across multiple pages with \`firecrawl_crawl\`. Authenticated responses can include an \`id\` for optional map feedback.
|
|
2772
3083
|
`,
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
3084
|
+
outputSchema: mapOutputSchema,
|
|
3085
|
+
parameters: z8.object({
|
|
3086
|
+
url: z8.string().url(),
|
|
3087
|
+
search: z8.string().optional(),
|
|
3088
|
+
sitemap: z8.enum(["include", "skip", "only"]).optional(),
|
|
3089
|
+
includeSubdomains: z8.boolean().optional(),
|
|
3090
|
+
limit: z8.number().optional(),
|
|
3091
|
+
ignoreQueryParameters: z8.boolean().optional()
|
|
2780
3092
|
}),
|
|
2781
3093
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
2782
3094
|
const { url, ...options } = args2;
|
|
@@ -2787,11 +3099,12 @@ Returns matching URLs rather than page bodies. Retrieve one page with \`firecraw
|
|
|
2787
3099
|
...cleaned,
|
|
2788
3100
|
origin: requestOrigin(mcpClient, session)
|
|
2789
3101
|
});
|
|
2790
|
-
return
|
|
3102
|
+
return structuredText(res);
|
|
2791
3103
|
}
|
|
2792
3104
|
});
|
|
2793
3105
|
server.addTool({
|
|
2794
3106
|
name: "firecrawl_search",
|
|
3107
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
2795
3108
|
annotations: {
|
|
2796
3109
|
title: "Firecrawl web search",
|
|
2797
3110
|
readOnlyHint: true,
|
|
@@ -2806,11 +3119,12 @@ Search web, news, or image sources and return ranked results with query-relevant
|
|
|
2806
3119
|
|
|
2807
3120
|
${ALEXANDRIA_SEARCH_LEAD}
|
|
2808
3121
|
|
|
2809
|
-
|
|
3122
|
+
On an authenticated session, tool matches describe available capabilities; \`firecrawl_find_tools\` returns their contracts and \`firecrawl_scrape\` with an \`alexandria\` body executes a selected capability. Keyless sessions get no Alexandria matches in data.tools.
|
|
2810
3123
|
|
|
2811
3124
|
For a programming question, add \`categories: ["developer"]\`; its hits return in \`data.web\` with \`category: "developer"\`. \`categories: ["research"]\` restricts web results to research-affiliated websites; the \`firecrawl_research_*\` tools are a separate surface over paper abstracts and full text (PubMed, bioRxiv, medRxiv, arXiv). Query operators, domain filters, \`categories\`, \`toolDetail\` and \`scrapeOptions\` are described on their parameters. Returns source-type result groups and usage metadata. Authenticated responses can include an \`id\` for optional search feedback.
|
|
2812
3125
|
`,
|
|
2813
|
-
|
|
3126
|
+
outputSchema: searchOutputSchema,
|
|
3127
|
+
parameters: z8.object({
|
|
2814
3128
|
...searchToolBaseFields,
|
|
2815
3129
|
scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional().describe("Attach page content for web results in the same call. These fetches ignore maxAge, so use firecrawl_scrape when you need a live fetch. scrapeOptions fetches web pages, never Alexandria provider tools.")
|
|
2816
3130
|
}).refine(searchDomainsAreExclusive, SEARCH_DOMAINS_CONFLICT_MESSAGE).refine(
|
|
@@ -2847,7 +3161,7 @@ For a programming question, add \`categories: ["developer"]\`; its hits return i
|
|
|
2847
3161
|
const json = await keylessPost("/v2/search", searchBody, session);
|
|
2848
3162
|
const keylessResponse = { ...json ?? {} };
|
|
2849
3163
|
delete keylessResponse.id;
|
|
2850
|
-
return
|
|
3164
|
+
return structuredCompact(keylessResponse);
|
|
2851
3165
|
}
|
|
2852
3166
|
const client = getClient(session);
|
|
2853
3167
|
const postSearch = () => postSearchWithFallback(
|
|
@@ -2857,7 +3171,7 @@ For a programming question, add \`categories: ["developer"]\`; its hits return i
|
|
|
2857
3171
|
);
|
|
2858
3172
|
const context = { tool: "firecrawl_search" };
|
|
2859
3173
|
const httpRes = exchangeSource ? await relayExchangeError(postSearch, context) : await relayTermsRequired(postSearch, context);
|
|
2860
|
-
return
|
|
3174
|
+
return structuredCompact(httpRes?.data ?? {});
|
|
2861
3175
|
}
|
|
2862
3176
|
});
|
|
2863
3177
|
async function executeExchangeCalls(session, alexandria, suppliedRequestId, timeout, origin = requestOrigin(void 0, session)) {
|
|
@@ -2895,11 +3209,11 @@ async function executeExchangeCalls(session, alexandria, suppliedRequestId, time
|
|
|
2895
3209
|
randomUUID(),
|
|
2896
3210
|
alexandriaFeedbackAvailable(session) && alexandriaCallsWarrantFeedback(calls) ? { feedbackTool: ALEXANDRIA_FEEDBACK_HINT } : {}
|
|
2897
3211
|
);
|
|
2898
|
-
} catch (
|
|
2899
|
-
if (
|
|
3212
|
+
} catch (error2) {
|
|
3213
|
+
if (error2?.response?.status === 401) throw error2;
|
|
2900
3214
|
throw new UserError2(
|
|
2901
|
-
`${
|
|
2902
|
-
{ ...
|
|
3215
|
+
`${error2 instanceof Error ? error2.message : String(error2)} Request ID: ${requestId}. Reuse this ID only with the identical payload.`,
|
|
3216
|
+
{ ...error2 instanceof UserError2 ? error2.extras : {}, requestId }
|
|
2903
3217
|
);
|
|
2904
3218
|
}
|
|
2905
3219
|
}
|
|
@@ -2912,15 +3226,16 @@ var findToolsTool = {
|
|
|
2912
3226
|
destructiveHint: false,
|
|
2913
3227
|
idempotentHint: true
|
|
2914
3228
|
},
|
|
2915
|
-
description: "Browse Alexandria data providers and workflows or read a selected contract. Alexandria covers " + ALEXANDRIA_CATALOGUE_VERTICALS + ": typed, sourced records through published contracts. Prefer normal firecrawl_search for a data task; it already returns matching providers. Use this tool when the contract you need was not returned in full, to browse a category when search found nothing, or before scraping the same fields from several pages. Discovery is free. Use query for semantic discovery or urls to find providers for a website. With no arguments, browse categories, then providers and tools.
|
|
3229
|
+
description: "Browse Alexandria data providers and workflows or read a selected contract. Alexandria covers " + ALEXANDRIA_CATALOGUE_VERTICALS + ": typed, sourced records through published contracts. Prefer normal firecrawl_search for a data task; it already returns matching providers. Use this tool when the contract you need was not returned in full, to browse a category when search found nothing, or before scraping the same fields from several pages. Discovery is free. Use query for semantic discovery or urls to find providers for a website. With no arguments, browse categories, then providers and tools. Contracts describe the inputs and outputs for execution through firecrawl_scrape. nextTool identifies further discovery or pagination. Discovery does not execute providers. Use firecrawl_search when you also need web results.",
|
|
3230
|
+
outputSchema: findToolsOutputSchema,
|
|
2916
3231
|
parameters: findToolsSchema,
|
|
2917
3232
|
execute: async (args2, { session, client: mcpClient }) => {
|
|
2918
3233
|
const origin = requestOrigin(mcpClient, session);
|
|
2919
3234
|
let options;
|
|
2920
3235
|
try {
|
|
2921
3236
|
options = findToolsOptions(args2);
|
|
2922
|
-
} catch (
|
|
2923
|
-
throw new UserError2(
|
|
3237
|
+
} catch (error2) {
|
|
3238
|
+
throw new UserError2(error2 instanceof Error ? error2.message : String(error2));
|
|
2924
3239
|
}
|
|
2925
3240
|
if (options.level === "categories") {
|
|
2926
3241
|
assertExchangeCredential(session);
|
|
@@ -2940,25 +3255,26 @@ var findToolsTool = {
|
|
|
2940
3255
|
}));
|
|
2941
3256
|
const page = { level: "categories", items, total: rows.length };
|
|
2942
3257
|
if (offset + options.limit < rows.length) page.nextTool = { name: "firecrawl_find_tools", arguments: { level: "categories", limit: options.limit, offset: offset + options.limit } };
|
|
2943
|
-
return
|
|
3258
|
+
return structuredCompact(withAlexandriaFeedbackHint(withFindToolsNavigation({ success: true, data: { creditsCost: 0, alexandria: [{ provider: "firecrawl", capability: "find-tools", creditsCost: 0, data: page }] } }), alexandriaFeedbackAvailable(session)));
|
|
2944
3259
|
}
|
|
2945
3260
|
const result = await executeExchangeCalls(session, { provider: "firecrawl", capability: "find-tools", options }, void 0, void 0, origin);
|
|
2946
|
-
return
|
|
3261
|
+
return structuredCompact(withFindToolsNavigation(JSON.parse(result)));
|
|
2947
3262
|
}
|
|
2948
3263
|
};
|
|
2949
3264
|
server.addTool(findToolsTool);
|
|
2950
3265
|
var SEARCH_SURFACE_SCRAPE_DESCRIPTION = `
|
|
2951
3266
|
Scrape one URL and return its content, or execute catalogued Alexandria capabilities. URL mode returns markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema, plus page metadata. Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch. A successful response does not by itself confirm the page is still current. Browser actions can change the live page when interactive actions are enabled, and a named browser profile can load saved session data and overwrite its stored state.
|
|
2952
3267
|
|
|
2953
|
-
|
|
3268
|
+
\`firecrawl_search\` with \`sources\` unset and \`firecrawl_find_tools\` can discover providers for the same fields across several pages; a matching Alexandria provider returns typed records in one call.
|
|
2954
3269
|
|
|
2955
|
-
Alexandria mode:
|
|
3270
|
+
Alexandria mode: \`alexandria\` selects catalogued capability execution and is mutually exclusive with \`url\`; the \`alexandria\` and \`requestId\` parameters describe batching, retries, errors and provider terms. Alexandria execution is billed at the capability's listed price and needs a team with Alexandria enabled.
|
|
2956
3271
|
`;
|
|
2957
3272
|
var searchSurfaceScrapeTool = {
|
|
2958
3273
|
...scrapeTool,
|
|
3274
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
2959
3275
|
description: SEARCH_SURFACE_SCRAPE_DESCRIPTION
|
|
2960
3276
|
};
|
|
2961
|
-
var SEARCH_SURFACE_FIND_TOOLS_DESCRIPTION = "Browse Alexandria data providers and workflows or read a selected contract. Alexandria covers " + ALEXANDRIA_CATALOGUE_VERTICALS + ": typed, sourced records through published contracts. Prefer normal firecrawl_search for a data task; it already returns matching providers. Use this tool when the contract you need was not returned in full, to browse a category when search found nothing, or before scraping the same fields from several pages. Discovery is free. Use query for semantic discovery or urls to find providers for a website. With no arguments, browse categories, then providers and tools.
|
|
3277
|
+
var SEARCH_SURFACE_FIND_TOOLS_DESCRIPTION = "Browse Alexandria data providers and workflows or read a selected contract. Alexandria covers " + ALEXANDRIA_CATALOGUE_VERTICALS + ": typed, sourced records through published contracts. Prefer normal firecrawl_search for a data task; it already returns matching providers. Use this tool when the contract you need was not returned in full, to browse a category when search found nothing, or before scraping the same fields from several pages. Discovery is free. Use query for semantic discovery or urls to find providers for a website. With no arguments, browse categories, then providers and tools. Contracts describe the inputs and outputs for execution through firecrawl_scrape. nextTool identifies further discovery or pagination. Discovery does not execute providers. Use firecrawl_search when you also need web results.";
|
|
2962
3278
|
var searchSurfaceFindToolsTool = {
|
|
2963
3279
|
...findToolsTool,
|
|
2964
3280
|
description: SEARCH_SURFACE_FIND_TOOLS_DESCRIPTION
|
|
@@ -3109,17 +3425,17 @@ async function waitForCrawlCompletionWithOrigin(client, jobId, origin, pollInter
|
|
|
3109
3425
|
);
|
|
3110
3426
|
}
|
|
3111
3427
|
}
|
|
3112
|
-
var feedbackIssueSchema =
|
|
3428
|
+
var feedbackIssueSchema = z8.string().trim().min(1).max(80).regex(
|
|
3113
3429
|
/^[a-z0-9][a-z0-9_-]*$/,
|
|
3114
3430
|
"Issue codes must use lowercase letters, numbers, underscores, or hyphens"
|
|
3115
3431
|
);
|
|
3116
|
-
var valuableSourceSchema =
|
|
3117
|
-
url:
|
|
3118
|
-
reason:
|
|
3432
|
+
var valuableSourceSchema = z8.object({
|
|
3433
|
+
url: z8.string().url(),
|
|
3434
|
+
reason: z8.string().max(1e3).optional()
|
|
3119
3435
|
});
|
|
3120
|
-
var missingContentSchema =
|
|
3121
|
-
topic:
|
|
3122
|
-
description:
|
|
3436
|
+
var missingContentSchema = z8.object({
|
|
3437
|
+
topic: z8.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
|
|
3438
|
+
description: z8.string().max(2e3).optional()
|
|
3123
3439
|
});
|
|
3124
3440
|
var FEEDBACK_DISABLED_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
3125
3441
|
function feedbackEnvEnabled(...keys) {
|
|
@@ -3157,24 +3473,25 @@ Records schema-validated quality feedback for a prior \`firecrawl_search\` UUID
|
|
|
3157
3473
|
|
|
3158
3474
|
Eligibility is limited to successful searches within the feedback age window. The record is idempotent per search ID. Eligible first feedback for a search can refund 1 credit; refunds are subject to the team's daily cap. The response reports whether a refund was applied, along with submission and daily-cap status.
|
|
3159
3475
|
`,
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3476
|
+
outputSchema: feedbackOutputSchema,
|
|
3477
|
+
parameters: z8.object({
|
|
3478
|
+
searchId: z8.string().uuid("searchId must be the UUID returned by firecrawl_search"),
|
|
3479
|
+
rating: z8.enum(["good", "bad", "partial"]),
|
|
3480
|
+
valuableSources: z8.array(
|
|
3481
|
+
z8.object({
|
|
3482
|
+
url: z8.string().url(),
|
|
3483
|
+
reason: z8.string().max(1e3).optional()
|
|
3167
3484
|
})
|
|
3168
3485
|
).max(50).optional(),
|
|
3169
|
-
missingContent:
|
|
3170
|
-
|
|
3171
|
-
topic:
|
|
3172
|
-
description:
|
|
3486
|
+
missingContent: z8.array(
|
|
3487
|
+
z8.object({
|
|
3488
|
+
topic: z8.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
|
|
3489
|
+
description: z8.string().max(2e3).optional()
|
|
3173
3490
|
})
|
|
3174
3491
|
).max(20).optional().describe(
|
|
3175
3492
|
"Array of specific pieces of content the agent expected to find but did not. One entry per distinct topic. Each entry has a short `topic` and optional longer `description`."
|
|
3176
3493
|
),
|
|
3177
|
-
querySuggestions:
|
|
3494
|
+
querySuggestions: z8.string().max(2e3).optional()
|
|
3178
3495
|
}),
|
|
3179
3496
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3180
3497
|
const origin = requestOrigin(mcpClient, session);
|
|
@@ -3234,7 +3551,7 @@ Eligibility is limited to successful searches within the feedback age window. Th
|
|
|
3234
3551
|
status: response.status,
|
|
3235
3552
|
feedbackErrorCode: parsed?.feedbackErrorCode
|
|
3236
3553
|
});
|
|
3237
|
-
return
|
|
3554
|
+
return structuredText({
|
|
3238
3555
|
success: false,
|
|
3239
3556
|
status: response.status,
|
|
3240
3557
|
feedbackErrorCode: parsed?.feedbackErrorCode,
|
|
@@ -3242,7 +3559,7 @@ Eligibility is limited to successful searches within the feedback age window. Th
|
|
|
3242
3559
|
retryable: response.status >= 500
|
|
3243
3560
|
});
|
|
3244
3561
|
}
|
|
3245
|
-
return
|
|
3562
|
+
return structuredText(parsed);
|
|
3246
3563
|
}
|
|
3247
3564
|
});
|
|
3248
3565
|
}
|
|
@@ -3273,20 +3590,21 @@ For an Alexandria session, set endpoint to \`alexandria\`, omit jobId, and provi
|
|
|
3273
3590
|
|
|
3274
3591
|
Returns submission status, feedback ID, and accounting fields.
|
|
3275
3592
|
`,
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3593
|
+
outputSchema: feedbackOutputSchema,
|
|
3594
|
+
parameters: z8.object({
|
|
3595
|
+
endpoint: z8.enum(["search", "scrape", "parse", "map", "alexandria"]),
|
|
3596
|
+
jobId: z8.string().uuid("jobId must be the UUID returned by Firecrawl").optional(),
|
|
3279
3597
|
...alexandriaFeedbackFields,
|
|
3280
|
-
rating:
|
|
3281
|
-
issues:
|
|
3282
|
-
tags:
|
|
3283
|
-
note:
|
|
3284
|
-
valuableSources:
|
|
3285
|
-
missingContent:
|
|
3286
|
-
querySuggestions:
|
|
3287
|
-
url:
|
|
3288
|
-
pageNumbers:
|
|
3289
|
-
metadata:
|
|
3598
|
+
rating: z8.enum(["good", "bad", "partial"]),
|
|
3599
|
+
issues: z8.array(feedbackIssueSchema).max(20).optional(),
|
|
3600
|
+
tags: z8.array(feedbackIssueSchema).max(20).optional(),
|
|
3601
|
+
note: z8.string().max(4e3).optional(),
|
|
3602
|
+
valuableSources: z8.array(valuableSourceSchema).max(50).optional(),
|
|
3603
|
+
missingContent: z8.array(missingContentSchema).max(50).optional(),
|
|
3604
|
+
querySuggestions: z8.string().max(2e3).optional(),
|
|
3605
|
+
url: z8.string().url().optional(),
|
|
3606
|
+
pageNumbers: z8.array(z8.number().int().positive()).max(100).optional(),
|
|
3607
|
+
metadata: z8.record(z8.string(), z8.unknown()).optional()
|
|
3290
3608
|
}).superRefine((value, ctx) => {
|
|
3291
3609
|
if (value.endpoint === "alexandria") {
|
|
3292
3610
|
const parsed = alexandriaSessionFeedbackSchema.safeParse(value);
|
|
@@ -3368,7 +3686,7 @@ Returns submission status, feedback ID, and accounting fields.
|
|
|
3368
3686
|
status: response.status,
|
|
3369
3687
|
feedbackErrorCode: parsed?.feedbackErrorCode
|
|
3370
3688
|
});
|
|
3371
|
-
return
|
|
3689
|
+
return structuredText({
|
|
3372
3690
|
success: false,
|
|
3373
3691
|
status: response.status,
|
|
3374
3692
|
feedbackErrorCode: parsed?.feedbackErrorCode,
|
|
@@ -3376,7 +3694,7 @@ Returns submission status, feedback ID, and accounting fields.
|
|
|
3376
3694
|
retryable: response.status >= 500
|
|
3377
3695
|
});
|
|
3378
3696
|
}
|
|
3379
|
-
return
|
|
3697
|
+
return structuredText(parsed);
|
|
3380
3698
|
}
|
|
3381
3699
|
});
|
|
3382
3700
|
}
|
|
@@ -3396,27 +3714,28 @@ Start a multi-page crawl at a website URL, poll it to a terminal state, and retu
|
|
|
3396
3714
|
|
|
3397
3715
|
Crawl results can be large; use conservative limits when full-site coverage is unnecessary. Webhooks and interactive scrape actions are unavailable in safe mode. Returns the crawl ID, status, and page data.
|
|
3398
3716
|
`,
|
|
3399
|
-
parameters:
|
|
3400
|
-
url:
|
|
3401
|
-
prompt:
|
|
3402
|
-
excludePaths:
|
|
3403
|
-
includePaths:
|
|
3404
|
-
maxDiscoveryDepth:
|
|
3405
|
-
sitemap:
|
|
3406
|
-
limit:
|
|
3407
|
-
allowExternalLinks:
|
|
3408
|
-
allowSubdomains:
|
|
3409
|
-
crawlEntireDomain:
|
|
3410
|
-
delay:
|
|
3411
|
-
maxConcurrency:
|
|
3717
|
+
parameters: z8.object({
|
|
3718
|
+
url: z8.string(),
|
|
3719
|
+
prompt: z8.string().optional(),
|
|
3720
|
+
excludePaths: z8.array(z8.string()).optional(),
|
|
3721
|
+
includePaths: z8.array(z8.string()).optional(),
|
|
3722
|
+
maxDiscoveryDepth: z8.number().optional(),
|
|
3723
|
+
sitemap: z8.enum(["skip", "include", "only"]).optional(),
|
|
3724
|
+
limit: z8.number().optional(),
|
|
3725
|
+
allowExternalLinks: z8.boolean().optional(),
|
|
3726
|
+
allowSubdomains: z8.boolean().optional(),
|
|
3727
|
+
crawlEntireDomain: z8.boolean().optional(),
|
|
3728
|
+
delay: z8.number().optional(),
|
|
3729
|
+
maxConcurrency: z8.number().optional(),
|
|
3412
3730
|
...SAFE_MODE ? {} : {
|
|
3413
|
-
webhook:
|
|
3414
|
-
webhookHeaders:
|
|
3731
|
+
webhook: z8.string().optional(),
|
|
3732
|
+
webhookHeaders: z8.record(z8.string(), z8.string()).optional()
|
|
3415
3733
|
},
|
|
3416
|
-
deduplicateSimilarURLs:
|
|
3417
|
-
ignoreQueryParameters:
|
|
3734
|
+
deduplicateSimilarURLs: z8.boolean().optional(),
|
|
3735
|
+
ignoreQueryParameters: z8.boolean().optional(),
|
|
3418
3736
|
scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional()
|
|
3419
3737
|
}),
|
|
3738
|
+
outputSchema: crawlOutputSchema,
|
|
3420
3739
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3421
3740
|
const origin = requestOrigin(mcpClient, session);
|
|
3422
3741
|
const { url, ...options } = args2;
|
|
@@ -3443,7 +3762,7 @@ Crawl results can be large; use conservative limits when full-site coverage is u
|
|
|
3443
3762
|
});
|
|
3444
3763
|
const crawlId = started?.data?.id;
|
|
3445
3764
|
if (!crawlId) {
|
|
3446
|
-
return
|
|
3765
|
+
return structuredText(started?.data ?? {});
|
|
3447
3766
|
}
|
|
3448
3767
|
const res = await waitForCrawlCompletionWithOrigin(
|
|
3449
3768
|
client,
|
|
@@ -3452,7 +3771,7 @@ Crawl results can be large; use conservative limits when full-site coverage is u
|
|
|
3452
3771
|
pollInterval,
|
|
3453
3772
|
timeout
|
|
3454
3773
|
);
|
|
3455
|
-
return
|
|
3774
|
+
return structuredText(res);
|
|
3456
3775
|
}
|
|
3457
3776
|
});
|
|
3458
3777
|
server.addTool({
|
|
@@ -3469,7 +3788,8 @@ server.addTool({
|
|
|
3469
3788
|
description: `
|
|
3470
3789
|
Retrieve the current status, progress, and available results for an existing crawl ID. This only reads Firecrawl job state and does not start or modify the crawl.
|
|
3471
3790
|
`,
|
|
3472
|
-
|
|
3791
|
+
outputSchema: crawlOutputSchema,
|
|
3792
|
+
parameters: z8.object({ id: z8.string() }),
|
|
3473
3793
|
execute: async (args2, {
|
|
3474
3794
|
session,
|
|
3475
3795
|
client: mcpClient
|
|
@@ -3481,7 +3801,7 @@ Retrieve the current status, progress, and available results for an existing cra
|
|
|
3481
3801
|
id,
|
|
3482
3802
|
requestOrigin(mcpClient, session)
|
|
3483
3803
|
);
|
|
3484
|
-
return
|
|
3804
|
+
return structuredText(res);
|
|
3485
3805
|
}
|
|
3486
3806
|
});
|
|
3487
3807
|
server.addTool({
|
|
@@ -3495,13 +3815,14 @@ server.addTool({
|
|
|
3495
3815
|
description: `
|
|
3496
3816
|
Deprecated compatibility entry point. Use firecrawl_scrape once per known URL with formats: ["json"] and jsonOptions containing the prompt and schema. Use firecrawl_agent for multi-source research when the URLs are not known or the data spans several sites.
|
|
3497
3817
|
`,
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3818
|
+
outputSchema: deprecatedToolOutputSchema,
|
|
3819
|
+
parameters: z8.object({
|
|
3820
|
+
urls: z8.array(z8.string()),
|
|
3821
|
+
prompt: z8.string().optional(),
|
|
3822
|
+
schema: z8.record(z8.string(), z8.any()).optional(),
|
|
3823
|
+
allowExternalLinks: z8.boolean().optional(),
|
|
3824
|
+
enableWebSearch: z8.boolean().optional(),
|
|
3825
|
+
includeSubdomains: z8.boolean().optional()
|
|
3505
3826
|
}),
|
|
3506
3827
|
canList: () => false,
|
|
3507
3828
|
beforeValidate: () => {
|
|
@@ -3533,10 +3854,11 @@ Run web research that returns structured data when the URLs are not known or the
|
|
|
3533
3854
|
|
|
3534
3855
|
This call returns only a job ID, not the research result. Read the job with \`firecrawl_agent_status\` until it reaches \`completed\` or \`failed\`; a typical research run takes one to three minutes. For one known URL use \`firecrawl_scrape\` (with formats: ["json"] for structured output); for a plain lookup that a results page answers, use \`firecrawl_search\`.
|
|
3535
3856
|
`,
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3857
|
+
outputSchema: agentOutputSchema,
|
|
3858
|
+
parameters: z8.object({
|
|
3859
|
+
prompt: z8.string().min(1).max(1e4),
|
|
3860
|
+
urls: z8.array(z8.string().url()).optional(),
|
|
3861
|
+
schema: z8.record(z8.string(), z8.any()).optional()
|
|
3540
3862
|
}),
|
|
3541
3863
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3542
3864
|
const client = getClient(session);
|
|
@@ -3554,7 +3876,7 @@ This call returns only a job ID, not the research result. Read the job with \`fi
|
|
|
3554
3876
|
...agentBody,
|
|
3555
3877
|
origin: requestOrigin(mcpClient, session)
|
|
3556
3878
|
});
|
|
3557
|
-
return
|
|
3879
|
+
return structuredText(res);
|
|
3558
3880
|
}
|
|
3559
3881
|
});
|
|
3560
3882
|
server.addTool({
|
|
@@ -3573,7 +3895,8 @@ Retrieve progress or final results for a \`firecrawl_agent\` job ID. A \`process
|
|
|
3573
3895
|
|
|
3574
3896
|
Returns job status, progress information, and result data when completed.
|
|
3575
3897
|
`,
|
|
3576
|
-
|
|
3898
|
+
outputSchema: agentStatusOutputSchema,
|
|
3899
|
+
parameters: z8.object({ id: z8.string() }),
|
|
3577
3900
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3578
3901
|
const client = getClient(session);
|
|
3579
3902
|
const { id } = args2;
|
|
@@ -3582,7 +3905,7 @@ Returns job status, progress information, and result data when completed.
|
|
|
3582
3905
|
`/v2/agent/${encodeURIComponent(id)}`,
|
|
3583
3906
|
originHeaders(requestOrigin(mcpClient, session))
|
|
3584
3907
|
);
|
|
3585
|
-
return
|
|
3908
|
+
return structuredText(res?.data ?? {});
|
|
3586
3909
|
}
|
|
3587
3910
|
});
|
|
3588
3911
|
server.addTool({
|
|
@@ -3601,13 +3924,14 @@ Open or reuse a live browser session to navigate a page, click controls, fill fi
|
|
|
3601
3924
|
|
|
3602
3925
|
This acts on the live site, so actions such as form submission can create persistent external side effects. Returns execution output, stdout/stderr, exit status, and session viewing URLs.
|
|
3603
3926
|
`,
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3927
|
+
outputSchema: interactOutputSchema,
|
|
3928
|
+
parameters: z8.object({
|
|
3929
|
+
scrapeId: z8.string().trim().min(1).optional(),
|
|
3930
|
+
url: z8.string().trim().url().optional(),
|
|
3931
|
+
prompt: z8.string().trim().min(1).optional(),
|
|
3932
|
+
code: z8.string().trim().min(1).optional(),
|
|
3933
|
+
language: z8.enum(["bash", "python", "node"]).optional(),
|
|
3934
|
+
timeout: z8.number().min(1).max(300).optional(),
|
|
3611
3935
|
scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional()
|
|
3612
3936
|
}).refine((data) => Boolean(data.scrapeId) !== Boolean(data.url), {
|
|
3613
3937
|
message: "Provide either 'url' (interact directly) or 'scrapeId' (reuse a previous scrape), not both."
|
|
@@ -3639,14 +3963,14 @@ This acts on the live site, so actions such as form submission can create persis
|
|
|
3639
3963
|
});
|
|
3640
3964
|
scrapeId = scraped?.metadata?.scrapeId;
|
|
3641
3965
|
if (!scrapeId) {
|
|
3642
|
-
return
|
|
3966
|
+
return structuredText({
|
|
3643
3967
|
error: "Could not open an interact session: the scrape did not return a scrapeId. Try firecrawl_scrape first, then pass its scrapeId.",
|
|
3644
3968
|
url
|
|
3645
3969
|
});
|
|
3646
3970
|
}
|
|
3647
3971
|
}
|
|
3648
3972
|
if (!scrapeId) {
|
|
3649
|
-
return
|
|
3973
|
+
return structuredText({
|
|
3650
3974
|
error: "Could not open an interact session: missing scrapeId.",
|
|
3651
3975
|
url
|
|
3652
3976
|
});
|
|
@@ -3660,15 +3984,15 @@ This acts on the live site, so actions such as form submission can create persis
|
|
|
3660
3984
|
if (timeout != null) interactArgs.timeout = timeout;
|
|
3661
3985
|
const res = await client.interact(activeScrapeId, interactArgs);
|
|
3662
3986
|
if (openedFromUrl && res && typeof res === "object" && !Array.isArray(res)) {
|
|
3663
|
-
return
|
|
3987
|
+
return structuredText({
|
|
3664
3988
|
...res,
|
|
3665
3989
|
scrapeId: activeScrapeId
|
|
3666
3990
|
});
|
|
3667
3991
|
}
|
|
3668
3992
|
if (openedFromUrl) {
|
|
3669
|
-
return
|
|
3993
|
+
return structuredText({ scrapeId: activeScrapeId, result: res });
|
|
3670
3994
|
}
|
|
3671
|
-
return
|
|
3995
|
+
return structuredText(res);
|
|
3672
3996
|
}
|
|
3673
3997
|
});
|
|
3674
3998
|
server.addTool({
|
|
@@ -3685,8 +4009,9 @@ server.addTool({
|
|
|
3685
4009
|
description: `
|
|
3686
4010
|
Stop the live interact session associated with a \`scrapeId\` and release its resources. Returns a success confirmation.
|
|
3687
4011
|
`,
|
|
3688
|
-
|
|
3689
|
-
|
|
4012
|
+
outputSchema: interactStopOutputSchema,
|
|
4013
|
+
parameters: z8.object({
|
|
4014
|
+
scrapeId: z8.string()
|
|
3690
4015
|
}),
|
|
3691
4016
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3692
4017
|
const client = getClient(session);
|
|
@@ -3696,7 +4021,7 @@ Stop the live interact session associated with a \`scrapeId\` and release its re
|
|
|
3696
4021
|
`/v2/scrape/${encodeURIComponent(scrapeId)}/interact`,
|
|
3697
4022
|
originHeaders(requestOrigin(mcpClient, session))
|
|
3698
4023
|
);
|
|
3699
|
-
return
|
|
4024
|
+
return structuredText(res?.data ?? {});
|
|
3700
4025
|
}
|
|
3701
4026
|
});
|
|
3702
4027
|
server.addTool({
|
|
@@ -3717,11 +4042,14 @@ Local MCP reads \`filePath\` from the server filesystem. Hosted MCP uses two cal
|
|
|
3717
4042
|
|
|
3718
4043
|
Set \`redactPII\` to request redaction of personally identifiable information in the returned content. \`zeroDataRetention\` requires an eligible authenticated account; omit it for anonymous keyless use. Returns upload instructions for hosted phase one or parsed document content for the final call. Authenticated final responses can include a \`data.metadata.scrapeId\` for optional parse feedback.
|
|
3719
4044
|
`,
|
|
4045
|
+
outputSchema: parseOutputSchema,
|
|
3720
4046
|
parameters: parseParamsSchema,
|
|
3721
4047
|
execute: async (args2, { session, log, client: mcpClient }) => {
|
|
3722
4048
|
const origin = requestOrigin(mcpClient, session);
|
|
3723
4049
|
if (process.env.CLOUD_SERVICE === "true") {
|
|
3724
|
-
return
|
|
4050
|
+
return structuredJsonText(
|
|
4051
|
+
await executeHostedParse(args2, session, log, origin)
|
|
4052
|
+
);
|
|
3725
4053
|
}
|
|
3726
4054
|
const apiUrl = process.env.FIRECRAWL_API_URL;
|
|
3727
4055
|
if (!apiUrl) {
|
|
@@ -3771,15 +4099,16 @@ Set \`redactPII\` to request redaction of personally identifiable information in
|
|
|
3771
4099
|
);
|
|
3772
4100
|
}
|
|
3773
4101
|
try {
|
|
3774
|
-
return
|
|
4102
|
+
return structuredText(JSON.parse(responseText));
|
|
3775
4103
|
} catch {
|
|
3776
|
-
return responseText;
|
|
4104
|
+
return withStructured(responseText, { raw: responseText });
|
|
3777
4105
|
}
|
|
3778
4106
|
}
|
|
3779
4107
|
});
|
|
3780
4108
|
function registerMarketplaceSearchTool(registrar, getClientFn) {
|
|
3781
4109
|
registrar.addTool({
|
|
3782
4110
|
name: "firecrawl_search",
|
|
4111
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
3783
4112
|
annotations: {
|
|
3784
4113
|
title: "Firecrawl web search",
|
|
3785
4114
|
readOnlyHint: true,
|
|
@@ -3795,7 +4124,8 @@ ${ALEXANDRIA_SEARCH_INSTRUCTIONS}
|
|
|
3795
4124
|
|
|
3796
4125
|
Returns result groups in \`data\` and an operation \`id\`.
|
|
3797
4126
|
`,
|
|
3798
|
-
|
|
4127
|
+
outputSchema: searchOutputSchema,
|
|
4128
|
+
parameters: z8.object({ ...searchToolBaseFields }).strict().refine(searchDomainsAreExclusive, SEARCH_DOMAINS_CONFLICT_MESSAGE).refine(
|
|
3799
4129
|
searchQueryIsValid,
|
|
3800
4130
|
"A query is required. Use firecrawl_find_tools to browse the catalogue."
|
|
3801
4131
|
),
|
|
@@ -3846,7 +4176,7 @@ Returns result groups in \`data\` and an operation \`id\`.
|
|
|
3846
4176
|
);
|
|
3847
4177
|
const context = { tool: "firecrawl_search" };
|
|
3848
4178
|
const httpRes = exchangeSource ? await relayExchangeError(postSearch, context) : await relayTermsRequired(postSearch, context);
|
|
3849
|
-
return
|
|
4179
|
+
return structuredCompact(httpRes?.data ?? {});
|
|
3850
4180
|
}
|
|
3851
4181
|
});
|
|
3852
4182
|
}
|
|
@@ -3918,10 +4248,10 @@ if (searchProfileEnabled) {
|
|
|
3918
4248
|
stateless: true
|
|
3919
4249
|
}
|
|
3920
4250
|
});
|
|
3921
|
-
} catch (
|
|
4251
|
+
} catch (error2) {
|
|
3922
4252
|
console.error(
|
|
3923
4253
|
`[search-profile] failed to start on port ${searchProfile.port}; the full surface is unaffected`,
|
|
3924
|
-
|
|
4254
|
+
error2
|
|
3925
4255
|
);
|
|
3926
4256
|
}
|
|
3927
4257
|
}
|