Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1211,3 +1211,5 @@ Use the existing `firecrawl_feedback` tool with `endpoint: "alexandria"`:
```

This uses authenticated `POST /v2/feedback`, without a job ID, job-age deadline, or credit refund. Optional `providerFeedback` and `capabilityFeedback` arrays describe coverage gaps and execution issues; the tool schema lists supported issue values. A `new_capability_request` requires `requestedFunctionality`; `missing_capability` (the provider exists but lacks the capability) does not. Existing feedback opt-out and authentication controls apply.

Agents are pointed at this loop from three places: the server instructions, the `firecrawl_scrape` and `firecrawl_find_tools` descriptions, and a `feedbackTool` object attached to every Alexandria execution and discovery result (with the tool name and a skeleton of the arguments). The hint is omitted for Firecrawl-internal calls such as `bash` and when `firecrawl_feedback` is not registered (`FIRECRAWL_NO_ENDPOINT_FEEDBACK` or keyless startup).
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firecrawl-mcp",
"version": "3.25.0",
"version": "3.25.1",
"description": "MCP server for Firecrawl — search, scrape, and interact with the web, and search scientific papers. Supports both cloud and self-hosted instances. Features include web search, scraping, page interaction, batch processing, LLM-powered content analysis, and research paper search over biomedical and arXiv literature (PubMed, bioRxiv, medRxiv, arXiv) with citation-graph expansion and full-text reading.",
"type": "module",
"mcpName": "io.github.firecrawl/firecrawl-mcp-server",
Expand Down
46 changes: 46 additions & 0 deletions src/alexandria-feedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,52 @@ export const alexandriaFeedbackFields = {
.optional(),
};

/**
* Shared by the server instructions and the Alexandria tool descriptions so an
* agent that only reads one of them still learns the feedback loop exists.
* Wording is checked by scripts/agent-metadata-policy.mjs.
*/
export const 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.';

/** Appended to Alexandria results so the pointer travels with the data the agent is reading. */
export const ALEXANDRIA_FEEDBACK_HINT = {
name: 'firecrawl_feedback',
when: 'Once per website after the task is complete, including when no provider covered the site. Free; no job ID or deadline.',
arguments: {
endpoint: 'alexandria',
rating: '<good | partial | bad>',
requestedWebsite: {
url: '<website the user needed data from>',
requestedFunctionality: '<what they needed from it>',
},
rationale: '<why this rating, from observed results>',
providerFeedback:
'<optional [{name, issue, why}]: providers that were missing, thin, or unavailable>',
capabilityFeedback:
'<optional [{name, provider, issue, why, requestedFunctionality?}]: capabilities that were missing, wrong, or failed>',
},
} as const;

type AlexandriaCall = { provider: string; capability: string };

/** Discovery and provider executions get the hint; Firecrawl-internal calls such as bash or terms do not. */
export function alexandriaCallsWarrantFeedback(
calls: AlexandriaCall[]
): boolean {
return calls.some(
(call) => call.provider !== 'firecrawl' || call.capability === 'find-tools'
);
}

export function withAlexandriaFeedbackHint<T>(
envelope: T,
enabled: boolean
): T {
if (!enabled || !envelope || typeof envelope !== 'object') return envelope;
return { ...envelope, feedbackTool: ALEXANDRIA_FEEDBACK_HINT };
}

export const alexandriaSessionFeedbackSchema = z.strictObject({
endpoint: z.literal('alexandria'),
rating: z.enum(['good', 'bad', 'partial']),
Expand Down
7 changes: 5 additions & 2 deletions src/alexandria-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export async function alexandriaOutput(
payload: any,
calls: Call[],
post: Post,
recoveryRequestId: string
recoveryRequestId: string,
// Merged into whichever envelope is returned, inline or retained.
extras: Record<string, unknown> = {}
): Promise<string> {
const serialized = JSON.stringify(payload);
const serialized = JSON.stringify({ ...payload, ...extras });
const responseBytes = Buffer.byteLength(serialized, 'utf8');
const estimatedTokens = Math.ceil(responseBytes / 4);
const items = payload?.data?.alexandria;
Expand Down Expand Up @@ -83,6 +85,7 @@ export async function alexandriaOutput(
},
},
},
...extras,
});
} catch {
return serialized;
Expand Down
4 changes: 3 additions & 1 deletion src/alexandria.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from 'zod';
import { ALEXANDRIA_FEEDBACK_GUIDANCE } from './alexandria-feedback.js';

const catalogueTypes = ['alexandria', 'exchange'] as const;
export const searchSourceSchema = z.union([
Expand Down Expand Up @@ -67,7 +68,8 @@ export const ALEXANDRIA_INSTRUCTIONS =
'Use sources: ["alexandria"] for semantic tools only, sources: ["web"] for web only, or sources: ["web"], domainTools: true for web plus domain tools. domainTools: false disables domain matching. Results in data.tools describe available tools, not executed data. Search defaults to toolDetail: "compact", returning only provider, capability and description; "summary" adds metadata and navigation. Inspect selected tools with firecrawl_find_tools using their providers and capabilities plus expand:["options","response"]; request examples only when the input shape is unclear. Batch related contract inspections and reuse complete contracts. Alternatively set toolDetail: \"full\" for contracts upfront. ' +
'On the full MCP surface, firecrawl_find_tools supports semantic query lookup and is the list equivalent: {} lists categories; {categories:["<category-id>"]} lists providers; {providers:["<provider-id>"]} lists compact tools; adding capabilities:["<capability-id>"] expands the selected contract. Avoid expanding the entire catalogue. ' +
'Read required inputs and requiresOneOf groups (at least one member per group), example.request/example.response when present, and response.key in the selected contract. Do not assume records is the result key. Follow the declared pagination input and response cursor, preserving filters; catalogue next is separate from provider pagination. ' +
'Execute through firecrawl_scrape with alexandria:{provider,capability,options}. Search scrapeOptions fetches web pages, never provider tools. Use web results when sufficient, tools when they offer a direct route to deeper data.';
'Execute through firecrawl_scrape with alexandria:{provider,capability,options}. Search scrapeOptions fetches web pages, never provider tools. Use web results when sufficient, tools when they offer a direct route to deeper data. ' +
ALEXANDRIA_FEEDBACK_GUIDANCE;

export function findToolsOptions(args: z.infer<typeof findToolsSchema>) {
const level = args.level ?? (args.query || args.capabilities?.length || args.providers?.length || args.groups?.length || args.urls?.length ? 'tools' : args.categories?.length ? 'providers' : 'categories');
Expand Down
Loading
Loading