Skip to content

Add server info, idempotent hints and input descriptions - #540

Closed
JuampiHernandez wants to merge 1 commit into
mainfrom
feat/mcp-server-metadata-and-schema-docs
Closed

JuampiHernandez wants to merge 1 commit into
mainfrom
feat/mcp-server-metadata-and-schema-docs

Conversation

@JuampiHernandez

Copy link
Copy Markdown

Hey, I'm Juampi from the Firecrawl team. MCP directories and quality scanners (Manufact's catalog score is the one that prompted this) read the server's handshake metadata and each tool's input documentation, and ours was thin. Our hosted server scored 84/100 there, mostly because of three gaps: the server sends no title, description, website or icon; only find_tools declares idempotentHint; and most inputs of firecrawl_scrape, firecrawl_search and firecrawl_parse have no description.

This PR fills those in. It changes no tool, input name, default, output or behaviour.

Changes

  • patches/fastmcp@4.3.2.patch: fastmcp builds the initialize result with only name and version, so the existing patch now also accepts title, description, websiteUrl and icons server options. title, websiteUrl and icons use the same names as upstream fastmcp, so the hunks drop out on upgrade. description is not upstream. pnpm-lock.yaml only gets the new patch hash.
  • src/server.ts: sets those four fields next to name (name stays firecrawl-fastmcp, origin attribution keys on it).
  • idempotentHint on every tool (src/server.ts, monitor.ts, research.ts, developer.ts, gov.ts, usage.ts, service/search-surface.ts). true for lookups, search, map, parse, status checks and the monitor/interact operations that are safe to repeat; false for tools that start a crawl, agent, monitor run or interact session or record feedback. firecrawl_scrape follows safe mode: true on the hosted surface, false outside it because browser actions exist there.
  • Input descriptions for firecrawl_scrape, firecrawl_parse and firecrawl_search (src/server.ts, src/tool-helpers.ts, src/alexandria.ts). Defaults were checked against the API source and docs. Where an input does nothing on a tool (parse storeInCache, parse proxy, search scrapeOptions.maxAge), the description says so. Search reuses the scrape option descriptions through scrapeOptions; crawl and interact say "Same fields as firecrawl_scrape." instead of repeating them, to keep tools/list small.
  • Contract snapshots regenerated, CHANGELOG.md entry, version bump to 3.30.2 in package.json, server.json and gemini-extension.json.
  • tests/server-info-and-schema-docs.test.mjs: pins the four server fields, idempotentHint on every tool, and description coverage for every input of the three tools.

Heads up

How I tested it

  • pnpm install --frozen-lockfile, pnpm run build, pnpm test: 171/171 pass (168 on main plus the 3 new tests), on a fresh clone as well.
  • Compared tools/list and initialize between main and this branch on all 8 contract surfaces. With descriptions and idempotentHint removed, the input and output schemas are identical, and tool descriptions, names, order and counts are unchanged.
  • Live runs with a real Firecrawl key over stdio and streamable HTTP, and with CLOUD_SERVICE=true: scrape (markdown, JSON, screenshot, waitFor, location, maxAge: 0), search (limit, tbs, location), parse against a mock API. Results match main. A request without a credential on the hosted setup still gets a 401.
  • About 50 invalid-input calls per build (bad URL, negative waitFor, limit 0 and 1000, unknown format and proxy, url plus alexandria) return identical errors, and the requests sent upstream are byte-identical.
  • The official MCP SDK client connects and parses the new server fields on stdio and HTTP.
  • Re-ran Manufact's published checks against the built hosted surface: 100% of scrape, search and parse inputs are documented, idempotentHint is on every tool, and the metadata fields are all present. I expect the hosted score to land in the mid-nineties after the redeploy; I have not seen the real number yet.

Send title, description, website and icons in the initialize result
(the bundled fastmcp patch adds title, description, websiteUrl and icons
server options), declare idempotentHint on every tool, and describe the
inputs of firecrawl_scrape, firecrawl_parse and firecrawl_search. Search
shares the scrape option descriptions through scrapeOptions; crawl and
interact point to firecrawl_scrape instead of repeating them. Defaults in
the descriptions match the Firecrawl API.

No tool, input name, default, output or behaviour changes. Contract
snapshots regenerated; a new test pins the server info, the hints and
description coverage. Bump to 3.30.2.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 25 files

Confidence score: 3/5

  • src/research.ts and src/service/search-surface.ts expose billed searches without warning clients that retries can incur another charge. Mark both tools non-idempotent.
  • In src/server.ts, hosted phase one creates a new upload reference on each call, so retrying the same filePath can repeat the side effect. Mark the tool non-idempotent or make reference creation idempotent.
  • In src/server.ts, the feedback tool is marked non-idempotent even though repeated calls with the same searchId are documented as idempotent; clients may retry and create duplicate feedback records. Set the hint to true.
  • tests/contract-snapshots/hosted-search-primary.json describes tools and API-key auth that this search-only endpoint does not support. Replace it with a surface-specific description.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/research.ts">

<violation number="1" location="src/research.ts:211">
P2: `firecrawl_research_search_papers` incurs a charge on every request, so repeating the same arguments has an additional effect. Mark this tool non-idempotent so clients do not treat retries as cost-free.

(Based on your team's feedback about per-request research-search billing.)</violation>
</file>

<file name="src/server.ts">

<violation number="1" location="src/server.ts:1727">
P2: Hosted phase one mints an upload reference on each call, so repeating the same `filePath` does more than repeat a read. Mark this tool non-idempotent, or make upload-reference creation idempotent before advertising it as repeat-safe.</violation>

<violation number="2" location="src/server.ts:2148">
P2: Repeated calls with the same `searchId` are explicitly documented as idempotent, so this false hint tells clients retries may add another feedback record. Set it to `true` to match the per-search behavior.</violation>
</file>

<file name="CHANGELOG.md">

<violation number="1" location="CHANGELOG.md:11">
P3: This says there is no output change, but the first bullet adds fields to the MCP `initialize` result. Clarify that tool result shapes remain unchanged so readers do not infer protocol responses are unchanged.</violation>
</file>

<file name="tests/contract-snapshots/hosted-search-primary.json">

<violation number="1" location="tests/contract-snapshots/hosted-search-primary.json:14">
P2: This search-only endpoint advertises `parse` and API-key-enabled crawl/map/interact/extract, but exposes none of those tools and rejects API-key auth; use a surface-specific description.</violation>
</file>

<file name="src/service/search-surface.ts">

<violation number="1" location="src/service/search-surface.ts:64">
P2: The default search includes web results, and each web search is billed per request, so repeating identical arguments incurs another charge. Mark this tool non-idempotent so clients do not treat retries as effect-free.</violation>
</file>

<file name="tests/server-info-and-schema-docs.test.mjs">

<violation number="1" location="tests/server-info-and-schema-docs.test.mjs:19">
P2: This walker never checks array `items`, so fields in `firecrawl_scrape.actions[]` can lose their descriptions without failing the test. Recurse into `prop.items` as well.</violation>
</file>

Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/research.ts
readOnlyHint: true, // Semantic search over indexed paper metadata; returns ranked results only.
openWorldHint: true, // Searches the Firecrawl research paper index.
destructiveHint: false, // Query-only; no writes to external sources or the research index.
idempotentHint: true,

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: firecrawl_research_search_papers incurs a charge on every request, so repeating the same arguments has an additional effect. Mark this tool non-idempotent so clients do not treat retries as cost-free.

(Based on your team's feedback about per-request research-search billing.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/research.ts, line 211:

<comment>`firecrawl_research_search_papers` incurs a charge on every request, so repeating the same arguments has an additional effect. Mark this tool non-idempotent so clients do not treat retries as cost-free.

(Based on your team's feedback about per-request research-search billing.) </comment>

<file context>
@@ -208,6 +208,7 @@ export function registerResearchTools(
       readOnlyHint: true, // Semantic search over indexed paper metadata; returns ranked results only.
       openWorldHint: true, // Searches the Firecrawl research paper index.
       destructiveHint: false, // Query-only; no writes to external sources or the research index.
+      idempotentHint: true,
     },
     description: `
</file context>
Suggested change
idempotentHint: true,
idempotentHint: false,
Fix with cubic

Comment thread src/server.ts
readOnlyHint: true, // Discovers and returns indexed URLs; does not modify the target site.
openWorldHint: true, // Operates against arbitrary user-supplied web domains.
destructiveHint: false, // Read-only discovery; no deletion or destructive updates.
idempotentHint: true,

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Hosted phase one mints an upload reference on each call, so repeating the same filePath does more than repeat a read. Mark this tool non-idempotent, or make upload-reference creation idempotent before advertising it as repeat-safe.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/server.ts, line 1727:

<comment>Hosted phase one mints an upload reference on each call, so repeating the same `filePath` does more than repeat a read. Mark this tool non-idempotent, or make upload-reference creation idempotent before advertising it as repeat-safe.</comment>

<file context>
@@ -1516,6 +1724,7 @@ Alexandria mode, on an authenticated session with Alexandria access: \`alexandri
       readOnlyHint: true, // Discovers and returns indexed URLs; does not modify the target site.
       openWorldHint: true, // Operates against arbitrary user-supplied web domains.
       destructiveHint: false, // Read-only discovery; no deletion or destructive updates.
+      idempotentHint: true,
     },
     description: `
</file context>
Suggested change
idempotentHint: true,
idempotentHint: false,
Fix with cubic

Comment thread src/server.ts
readOnlyHint: false, // POSTs structured feedback to the API, creating a server-side record.
openWorldHint: true, // Feedback references open-web search results and external URLs.
destructiveHint: false, // Additive only; records feedback and may refund credits, does not delete data.
idempotentHint: false,

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Repeated calls with the same searchId are explicitly documented as idempotent, so this false hint tells clients retries may add another feedback record. Set it to true to match the per-search behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/server.ts, line 2148:

<comment>Repeated calls with the same `searchId` are explicitly documented as idempotent, so this false hint tells clients retries may add another feedback record. Set it to `true` to match the per-search behavior.</comment>

<file context>
@@ -1928,6 +2145,7 @@ For a programming question, add \`categories: ["developer"]\`; its hits return i
         readOnlyHint: false, // POSTs structured feedback to the API, creating a server-side record.
         openWorldHint: true, // Feedback references open-web search results and external URLs.
         destructiveHint: false, // Additive only; records feedback and may refund credits, does not delete data.
+        idempotentHint: false,
       },
       description: `
</file context>
Suggested change
idempotentHint: false,
idempotentHint: true,
Fix with cubic

"name": "firecrawl-fastmcp",
"version": "<version>"
"version": "<version>",
"description": "Search the web and scrape pages or parse documents; with an API key, also crawl, map, interact and extract structured data. Built for AI agents.",

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This search-only endpoint advertises parse and API-key-enabled crawl/map/interact/extract, but exposes none of those tools and rejects API-key auth; use a surface-specific description.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/contract-snapshots/hosted-search-primary.json, line 14:

<comment>This search-only endpoint advertises `parse` and API-key-enabled crawl/map/interact/extract, but exposes none of those tools and rejects API-key auth; use a surface-specific description.</comment>

<file context>
@@ -10,19 +10,38 @@
         "name": "firecrawl-fastmcp",
-        "version": "<version>"
+        "version": "<version>",
+        "description": "Search the web and scrape pages or parse documents; with an API key, also crawl, map, interact and extract structured data. Built for AI agents.",
+        "icons": [
+          {
</file context>
Fix with cubic

readOnlyHint: true,
openWorldHint: true,
destructiveHint: false,
idempotentHint: true,

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The default search includes web results, and each web search is billed per request, so repeating identical arguments incurs another charge. Mark this tool non-idempotent so clients do not treat retries as effect-free.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/service/search-surface.ts, line 64:

<comment>The default search includes web results, and each web search is billed per request, so repeating identical arguments incurs another charge. Mark this tool non-idempotent so clients do not treat retries as effect-free.</comment>

<file context>
@@ -61,6 +61,7 @@ function registerMarketplaceSearchTool(
       readOnlyHint: true,
       openWorldHint: true,
       destructiveHint: false,
+      idempotentHint: true,
     },
     description: `
</file context>
Suggested change
idempotentHint: true,
idempotentHint: false,
Fix with cubic

) {
missing.push(here);
}
missing.push(...undocumented(prop, here));

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This walker never checks array items, so fields in firecrawl_scrape.actions[] can lose their descriptions without failing the test. Recurse into prop.items as well.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/server-info-and-schema-docs.test.mjs, line 19:

<comment>This walker never checks array `items`, so fields in `firecrawl_scrape.actions[]` can lose their descriptions without failing the test. Recurse into `prop.items` as well.</comment>

<file context>
@@ -0,0 +1,92 @@
+    ) {
+      missing.push(here);
+    }
+    missing.push(...undocumented(prop, here));
+  }
+  return missing;
</file context>
Suggested change
missing.push(...undocumented(prop, here));
missing.push(...undocumented(prop, here));
if (prop.items) {
missing.push(...undocumented(prop.items, `${here}[]`));
}
Fix with cubic

Comment thread CHANGELOG.md
- Every tool declares `idempotentHint`: `true` for read-only lookups, status checks, search, map, parse, and monitor/interact-stop operations that are safe to repeat, `false` for tools that start a crawl, agent, monitor run or interact session or record feedback. `firecrawl_scrape` follows safe mode, since browser `actions` are only offered outside it.
- Inputs of `firecrawl_scrape`, `firecrawl_parse` and `firecrawl_search` carry descriptions, including the scrape options reused as `scrapeOptions` by search (crawl and interact point to `firecrawl_scrape` instead of repeating them): formats, JSON and query options, screenshot and PDF settings, wait, cache, location, proxy and profile fields, and search `limit`, `tbs`, `location` and `enterprise`.

No tool, input name, default, output or behaviour changes.

@cubic-dev-ai cubic-dev-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This says there is no output change, but the first bullet adds fields to the MCP initialize result. Clarify that tool result shapes remain unchanged so readers do not infer protocol responses are unchanged.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At CHANGELOG.md, line 11:

<comment>This says there is no output change, but the first bullet adds fields to the MCP `initialize` result. Clarify that tool result shapes remain unchanged so readers do not infer protocol responses are unchanged.</comment>

<file context>
@@ -1,5 +1,15 @@
+- Every tool declares `idempotentHint`: `true` for read-only lookups, status checks, search, map, parse, and monitor/interact-stop operations that are safe to repeat, `false` for tools that start a crawl, agent, monitor run or interact session or record feedback. `firecrawl_scrape` follows safe mode, since browser `actions` are only offered outside it.
+- Inputs of `firecrawl_scrape`, `firecrawl_parse` and `firecrawl_search` carry descriptions, including the scrape options reused as `scrapeOptions` by search (crawl and interact point to `firecrawl_scrape` instead of repeating them): formats, JSON and query options, screenshot and PDF settings, wait, cache, location, proxy and profile fields, and search `limit`, `tbs`, `location` and `enterprise`.
+
+No tool, input name, default, output or behaviour changes.
+
 ## [3.30.1] - 2026-10-14
</file context>
Suggested change
No tool, input name, default, output or behaviour changes.
Tool names, input names and defaults, tool result shapes, and runtime behaviour are unchanged.
Fix with cubic

@JuampiHernandez

Copy link
Copy Markdown
Author

Split into smaller PRs, with Cubic's comments addressed:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant