firecrawl-mcp 3.32.0 → 3.33.0

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 CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  # Firecrawl MCP Server
10
10
 
11
- A Model Context Protocol (MCP) server that brings [Firecrawl](https://github.com/firecrawl/firecrawl) to MCP-compatible AI agents — search, scrape, and interact with the live web for clean, agent-ready context.
11
+ A Model Context Protocol (MCP) server that brings [Firecrawl](https://github.com/firecrawl/firecrawl), the context API for AI agents, to MCP-compatible clients: search, scrape, and interact with the live web and get back clean Markdown or structured data.
12
12
 
13
13
  > Big thanks to [@vrknetha](https://github.com/vrknetha), [@knacklabs](https://www.knacklabs.ai) for the initial implementation!
14
14
 
package/dist/index.js CHANGED
@@ -2637,25 +2637,64 @@ function initializeClientSlug(name2) {
2637
2637
  const slug = slugOf(name2);
2638
2638
  return isOwnSlug(slug) || GENERIC_CLIENT_NAMES.has(slug) ? "" : slug;
2639
2639
  }
2640
- function referrerSlug(referrer) {
2641
- let host;
2640
+ function publicHost(url) {
2641
+ let parsed;
2642
2642
  try {
2643
- host = new URL(referrer ?? "").hostname.toLowerCase().replace(/\.$/, "");
2643
+ parsed = new URL(url ?? "");
2644
2644
  } catch {
2645
2645
  return "";
2646
2646
  }
2647
+ const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
2647
2648
  if (!host.includes(".") || host.endsWith(".localhost") || host.endsWith(".local") || /^[\d.]+$/.test(host) || host.startsWith("[")) {
2648
2649
  return "";
2649
2650
  }
2650
- return slugOf(host.replace(/^www\./, ""));
2651
+ return host.replace(/^www\./, "");
2652
+ }
2653
+ function referrerSlug(referrer) {
2654
+ return slugOf(publicHost(referrer));
2655
+ }
2656
+ var SHARED_STATIC_HOSTS = [
2657
+ "github.io",
2658
+ "gitlab.io",
2659
+ "pages.dev",
2660
+ "netlify.app",
2661
+ "vercel.app"
2662
+ ];
2663
+ function isHttpUrl(value) {
2664
+ try {
2665
+ return /^https?:$/.test(new URL(value ?? "").protocol);
2666
+ } catch {
2667
+ return false;
2668
+ }
2669
+ }
2670
+ function oauthClientSlug(clientId, clientName) {
2671
+ if (!isHttpUrl(clientId)) return initializeClientSlug(clientName);
2672
+ const host = publicHost(clientId);
2673
+ if (!host || host === "firecrawl.dev" || host.endsWith(".firecrawl.dev")) {
2674
+ return "";
2675
+ }
2676
+ if (!SHARED_STATIC_HOSTS.some(
2677
+ (shared) => host === shared || host.endsWith(`.${shared}`)
2678
+ )) {
2679
+ return slugOf(host);
2680
+ }
2681
+ const [first = ""] = new URL(clientId ?? "").pathname.split("/").filter(Boolean);
2682
+ const segment = slugOf(first).slice(0, NAME_MAX / 2);
2683
+ if (!segment) return slugOf(host);
2684
+ const room = NAME_MAX - segment.length - 1;
2685
+ return slugOf(`${slugOf(host).slice(0, room)}-${segment}`);
2651
2686
  }
2652
2687
  function originForClient(client, serverVersion2, userAgent, signals = {}) {
2653
2688
  const declared = slugOf(signals.declaredOrigin);
2654
2689
  const slug = slugOf(client?.name);
2655
2690
  const product = userAgentProduct(userAgent);
2656
2691
  const remembered = initializeClientSlug(signals.initializeClient?.name);
2692
+ const oauthClient = oauthClientSlug(
2693
+ signals.oauthClientId,
2694
+ signals.oauthClientName
2695
+ );
2657
2696
  const referrer = referrerSlug(signals.referrer);
2658
- const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
2697
+ const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : oauthClient ? `mcp-oauth-${oauthClient}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
2659
2698
  return serverVersion2 ? `${base}@${serverVersion2}` : base;
2660
2699
  }
2661
2700
  function originHeaders(origin) {
@@ -2672,6 +2711,8 @@ function requestOrigin(client, session) {
2672
2711
  {
2673
2712
  declaredOrigin: session?.clientOrigin,
2674
2713
  initializeClient: session?.initializeClient,
2714
+ oauthClientId: session?.oauthClientId,
2715
+ oauthClientName: session?.oauthClientName,
2675
2716
  referrer: session?.clientReferrer
2676
2717
  }
2677
2718
  );
@@ -6222,6 +6263,7 @@ function credentialMetadata(data) {
6222
6263
  userId: typeof data.sub === "string" ? data.sub : void 0,
6223
6264
  apiKeyId: typeof data.api_key_id === "string" ? data.api_key_id : void 0,
6224
6265
  oauthClientId: typeof data.client_id === "string" ? data.client_id : void 0,
6266
+ oauthClientName: typeof data.client_name === "string" ? data.client_name : void 0,
6225
6267
  resource: typeof data.aud === "string" ? data.aud : void 0
6226
6268
  };
6227
6269
  }
package/dist/origin.js CHANGED
@@ -59,25 +59,64 @@ function initializeClientSlug(name) {
59
59
  const slug = slugOf(name);
60
60
  return isOwnSlug(slug) || GENERIC_CLIENT_NAMES.has(slug) ? "" : slug;
61
61
  }
62
- function referrerSlug(referrer) {
63
- let host;
62
+ function publicHost(url) {
63
+ let parsed;
64
64
  try {
65
- host = new URL(referrer ?? "").hostname.toLowerCase().replace(/\.$/, "");
65
+ parsed = new URL(url ?? "");
66
66
  } catch {
67
67
  return "";
68
68
  }
69
+ const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
69
70
  if (!host.includes(".") || host.endsWith(".localhost") || host.endsWith(".local") || /^[\d.]+$/.test(host) || host.startsWith("[")) {
70
71
  return "";
71
72
  }
72
- return slugOf(host.replace(/^www\./, ""));
73
+ return host.replace(/^www\./, "");
74
+ }
75
+ function referrerSlug(referrer) {
76
+ return slugOf(publicHost(referrer));
77
+ }
78
+ var SHARED_STATIC_HOSTS = [
79
+ "github.io",
80
+ "gitlab.io",
81
+ "pages.dev",
82
+ "netlify.app",
83
+ "vercel.app"
84
+ ];
85
+ function isHttpUrl(value) {
86
+ try {
87
+ return /^https?:$/.test(new URL(value ?? "").protocol);
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+ function oauthClientSlug(clientId, clientName) {
93
+ if (!isHttpUrl(clientId)) return initializeClientSlug(clientName);
94
+ const host = publicHost(clientId);
95
+ if (!host || host === "firecrawl.dev" || host.endsWith(".firecrawl.dev")) {
96
+ return "";
97
+ }
98
+ if (!SHARED_STATIC_HOSTS.some(
99
+ (shared) => host === shared || host.endsWith(`.${shared}`)
100
+ )) {
101
+ return slugOf(host);
102
+ }
103
+ const [first = ""] = new URL(clientId ?? "").pathname.split("/").filter(Boolean);
104
+ const segment = slugOf(first).slice(0, NAME_MAX / 2);
105
+ if (!segment) return slugOf(host);
106
+ const room = NAME_MAX - segment.length - 1;
107
+ return slugOf(`${slugOf(host).slice(0, room)}-${segment}`);
73
108
  }
74
109
  function originForClient(client, serverVersion2, userAgent, signals = {}) {
75
110
  const declared = slugOf(signals.declaredOrigin);
76
111
  const slug = slugOf(client?.name);
77
112
  const product = userAgentProduct(userAgent);
78
113
  const remembered = initializeClientSlug(signals.initializeClient?.name);
114
+ const oauthClient = oauthClientSlug(
115
+ signals.oauthClientId,
116
+ signals.oauthClientName
117
+ );
79
118
  const referrer = referrerSlug(signals.referrer);
80
- const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
119
+ const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : oauthClient ? `mcp-oauth-${oauthClient}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
81
120
  return serverVersion2 ? `${base}@${serverVersion2}` : base;
82
121
  }
83
122
  function originHeaders(origin) {
@@ -94,6 +133,8 @@ function requestOrigin(client, session) {
94
133
  {
95
134
  declaredOrigin: session?.clientOrigin,
96
135
  initializeClient: session?.initializeClient,
136
+ oauthClientId: session?.oauthClientId,
137
+ oauthClientName: session?.oauthClientName,
97
138
  referrer: session?.clientReferrer
98
139
  }
99
140
  );
package/dist/server.d.ts CHANGED
@@ -35,6 +35,8 @@ type OriginSession = {
35
35
  clientOrigin?: string;
36
36
  clientReferrer?: string;
37
37
  initializeClient?: ClientImplementation;
38
+ oauthClientId?: string;
39
+ oauthClientName?: string;
38
40
  };
39
41
  /**
40
42
  * The origin of every request a tool call makes: the call's client, the
@@ -241,6 +243,13 @@ interface SessionData {
241
243
  name?: string;
242
244
  version?: string;
243
245
  };
246
+ /**
247
+ * The OAuth client id the access token was issued to. A metadata URL id
248
+ * names the product; read only when the User-Agent names a runtime.
249
+ */
250
+ oauthClientId?: string;
251
+ /** The registered name of that client; read when the id is not a URL. */
252
+ oauthClientName?: string;
244
253
  authType?: 'api-key' | 'oauth' | 'env' | 'keyless' | 'none';
245
254
  /** Admitted, but the supplied credential cannot be used. */
246
255
  credentialError?: 'CREDENTIAL_INVALID';
package/dist/server.js CHANGED
@@ -2636,25 +2636,64 @@ function initializeClientSlug(name2) {
2636
2636
  const slug = slugOf(name2);
2637
2637
  return isOwnSlug(slug) || GENERIC_CLIENT_NAMES.has(slug) ? "" : slug;
2638
2638
  }
2639
- function referrerSlug(referrer) {
2640
- let host;
2639
+ function publicHost(url) {
2640
+ let parsed;
2641
2641
  try {
2642
- host = new URL(referrer ?? "").hostname.toLowerCase().replace(/\.$/, "");
2642
+ parsed = new URL(url ?? "");
2643
2643
  } catch {
2644
2644
  return "";
2645
2645
  }
2646
+ const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
2646
2647
  if (!host.includes(".") || host.endsWith(".localhost") || host.endsWith(".local") || /^[\d.]+$/.test(host) || host.startsWith("[")) {
2647
2648
  return "";
2648
2649
  }
2649
- return slugOf(host.replace(/^www\./, ""));
2650
+ return host.replace(/^www\./, "");
2651
+ }
2652
+ function referrerSlug(referrer) {
2653
+ return slugOf(publicHost(referrer));
2654
+ }
2655
+ var SHARED_STATIC_HOSTS = [
2656
+ "github.io",
2657
+ "gitlab.io",
2658
+ "pages.dev",
2659
+ "netlify.app",
2660
+ "vercel.app"
2661
+ ];
2662
+ function isHttpUrl(value) {
2663
+ try {
2664
+ return /^https?:$/.test(new URL(value ?? "").protocol);
2665
+ } catch {
2666
+ return false;
2667
+ }
2668
+ }
2669
+ function oauthClientSlug(clientId, clientName) {
2670
+ if (!isHttpUrl(clientId)) return initializeClientSlug(clientName);
2671
+ const host = publicHost(clientId);
2672
+ if (!host || host === "firecrawl.dev" || host.endsWith(".firecrawl.dev")) {
2673
+ return "";
2674
+ }
2675
+ if (!SHARED_STATIC_HOSTS.some(
2676
+ (shared) => host === shared || host.endsWith(`.${shared}`)
2677
+ )) {
2678
+ return slugOf(host);
2679
+ }
2680
+ const [first = ""] = new URL(clientId ?? "").pathname.split("/").filter(Boolean);
2681
+ const segment = slugOf(first).slice(0, NAME_MAX / 2);
2682
+ if (!segment) return slugOf(host);
2683
+ const room = NAME_MAX - segment.length - 1;
2684
+ return slugOf(`${slugOf(host).slice(0, room)}-${segment}`);
2650
2685
  }
2651
2686
  function originForClient(client, serverVersion2, userAgent, signals = {}) {
2652
2687
  const declared = slugOf(signals.declaredOrigin);
2653
2688
  const slug = slugOf(client?.name);
2654
2689
  const product = userAgentProduct(userAgent);
2655
2690
  const remembered = initializeClientSlug(signals.initializeClient?.name);
2691
+ const oauthClient = oauthClientSlug(
2692
+ signals.oauthClientId,
2693
+ signals.oauthClientName
2694
+ );
2656
2695
  const referrer = referrerSlug(signals.referrer);
2657
- const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
2696
+ const base = !isOwnSlug(declared) ? `mcp-${declared}` : !isOwnSlug(slug) ? `mcp-${slug}` : product && !RUNTIME_PRODUCTS.has(product) ? `mcp-ua-${product}` : remembered ? `mcp-${remembered}` : oauthClient ? `mcp-oauth-${oauthClient}` : referrer ? `mcp-ref-${referrer}` : product ? `mcp-ua-${product}` : ORIGIN_SERVER;
2658
2697
  return serverVersion2 ? `${base}@${serverVersion2}` : base;
2659
2698
  }
2660
2699
  function originHeaders(origin) {
@@ -2671,6 +2710,8 @@ function requestOrigin(client, session) {
2671
2710
  {
2672
2711
  declaredOrigin: session?.clientOrigin,
2673
2712
  initializeClient: session?.initializeClient,
2713
+ oauthClientId: session?.oauthClientId,
2714
+ oauthClientName: session?.oauthClientName,
2674
2715
  referrer: session?.clientReferrer
2675
2716
  }
2676
2717
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "firecrawl-mcp",
3
- "version": "3.32.0",
4
- "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.",
3
+ "version": "3.33.0",
4
+ "description": "Firecrawl MCP server, the context API for AI agents: search, scrape, parse, crawl, map, and interact with the web, run Agent and Monitor jobs, and search scientific papers (PubMed, bioRxiv, medRxiv, arXiv). Works with Firecrawl cloud or a self-hosted instance.",
5
5
  "type": "module",
6
6
  "mcpName": "io.github.firecrawl/firecrawl-mcp-server",
7
7
  "bin": {