firecrawl-mcp 1.5.0 → 1.7.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.
Files changed (2) hide show
  1. package/dist/index.js +151 -125
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -513,11 +513,33 @@ const DEEP_RESEARCH_TOOL = {
513
513
  maxUrls: {
514
514
  type: 'number',
515
515
  description: 'Maximum number of URLs to analyze (1-1000)',
516
- }
516
+ },
517
517
  },
518
518
  required: ['query'],
519
519
  },
520
520
  };
521
+ const GENERATE_LLMSTXT_TOOL = {
522
+ name: 'firecrawl_generate_llmstxt',
523
+ description: 'Generate standardized LLMs.txt file for a given URL, which provides context about how LLMs should interact with the website.',
524
+ inputSchema: {
525
+ type: 'object',
526
+ properties: {
527
+ url: {
528
+ type: 'string',
529
+ description: 'The URL to generate LLMs.txt from',
530
+ },
531
+ maxUrls: {
532
+ type: 'number',
533
+ description: 'Maximum number of URLs to process (1-100, default: 10)',
534
+ },
535
+ showFullText: {
536
+ type: 'boolean',
537
+ description: 'Whether to show the full LLMs-full.txt in the response',
538
+ },
539
+ },
540
+ required: ['url'],
541
+ },
542
+ };
521
543
  // Type guards
522
544
  function isScrapeOptions(args) {
523
545
  return (typeof args === 'object' &&
@@ -563,10 +585,16 @@ function isExtractOptions(args) {
563
585
  return (Array.isArray(urls) &&
564
586
  urls.every((url) => typeof url === 'string'));
565
587
  }
588
+ function isGenerateLLMsTextOptions(args) {
589
+ return (typeof args === 'object' &&
590
+ args !== null &&
591
+ 'url' in args &&
592
+ typeof args.url === 'string');
593
+ }
566
594
  // Server implementation
567
595
  const server = new Server({
568
596
  name: 'firecrawl-mcp',
569
- version: '1.3.2',
597
+ version: '1.7.0',
570
598
  }, {
571
599
  capabilities: {
572
600
  tools: {},
@@ -607,6 +635,17 @@ const creditUsage = {
607
635
  function delay(ms) {
608
636
  return new Promise((resolve) => setTimeout(resolve, ms));
609
637
  }
638
+ let isStdioTransport = false;
639
+ function safeLog(level, data) {
640
+ if (isStdioTransport) {
641
+ // For stdio transport, log to stderr to avoid protocol interference
642
+ console.error(`[${level}] ${typeof data === 'object' ? JSON.stringify(data) : data}`);
643
+ }
644
+ else {
645
+ // For other transport types, use the normal logging mechanism
646
+ server.sendLoggingMessage({ level, data });
647
+ }
648
+ }
610
649
  // Add retry logic with exponential backoff
611
650
  async function withRetry(operation, context, attempt = 1) {
612
651
  try {
@@ -618,10 +657,7 @@ async function withRetry(operation, context, attempt = 1) {
618
657
  if (isRateLimit && attempt < CONFIG.retry.maxAttempts) {
619
658
  const delayMs = Math.min(CONFIG.retry.initialDelay *
620
659
  Math.pow(CONFIG.retry.backoffFactor, attempt - 1), CONFIG.retry.maxDelay);
621
- server.sendLoggingMessage({
622
- level: 'warning',
623
- data: `Rate limit hit for ${context}. Attempt ${attempt}/${CONFIG.retry.maxAttempts}. Retrying in ${delayMs}ms`,
624
- });
660
+ safeLog('warning', `Rate limit hit for ${context}. Attempt ${attempt}/${CONFIG.retry.maxAttempts}. Retrying in ${delayMs}ms`);
625
661
  await delay(delayMs);
626
662
  return withRetry(operation, context, attempt + 1);
627
663
  }
@@ -632,22 +668,13 @@ async function withRetry(operation, context, attempt = 1) {
632
668
  async function updateCreditUsage(creditsUsed) {
633
669
  creditUsage.total += creditsUsed;
634
670
  // Log credit usage
635
- server.sendLoggingMessage({
636
- level: 'info',
637
- data: `Credit usage: ${creditUsage.total} credits used total`,
638
- });
671
+ safeLog('info', `Credit usage: ${creditUsage.total} credits used total`);
639
672
  // Check thresholds
640
673
  if (creditUsage.total >= CONFIG.credit.criticalThreshold) {
641
- server.sendLoggingMessage({
642
- level: 'error',
643
- data: `CRITICAL: Credit usage has reached ${creditUsage.total}`,
644
- });
674
+ safeLog('error', `CRITICAL: Credit usage has reached ${creditUsage.total}`);
645
675
  }
646
676
  else if (creditUsage.total >= CONFIG.credit.warningThreshold) {
647
- server.sendLoggingMessage({
648
- level: 'warning',
649
- data: `WARNING: Credit usage has reached ${creditUsage.total}`,
650
- });
677
+ safeLog('warning', `WARNING: Credit usage has reached ${creditUsage.total}`);
651
678
  }
652
679
  }
653
680
  // Initialize queue system
@@ -672,19 +699,13 @@ async function processBatchOperation(operation) {
672
699
  operation.result = response;
673
700
  // Log final credit usage for the batch
674
701
  if (!FIRECRAWL_API_URL) {
675
- server.sendLoggingMessage({
676
- level: 'info',
677
- data: `Batch ${operation.id} completed. Total credits used: ${totalCreditsUsed}`,
678
- });
702
+ safeLog('info', `Batch ${operation.id} completed. Total credits used: ${totalCreditsUsed}`);
679
703
  }
680
704
  }
681
705
  catch (error) {
682
706
  operation.status = 'failed';
683
707
  operation.error = error instanceof Error ? error.message : String(error);
684
- server.sendLoggingMessage({
685
- level: 'error',
686
- data: `Batch ${operation.id} failed: ${operation.error}`,
687
- });
708
+ safeLog('error', `Batch ${operation.id} failed: ${operation.error}`);
688
709
  }
689
710
  }
690
711
  // Tool handlers
@@ -699,6 +720,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
699
720
  SEARCH_TOOL,
700
721
  EXTRACT_TOOL,
701
722
  DEEP_RESEARCH_TOOL,
723
+ GENERATE_LLMSTXT_TOOL,
702
724
  ],
703
725
  }));
704
726
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -706,10 +728,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
706
728
  try {
707
729
  const { name, arguments: args } = request.params;
708
730
  // Log incoming request with timestamp
709
- server.sendLoggingMessage({
710
- level: 'info',
711
- data: `[${new Date().toISOString()}] Received request for tool: ${name}`,
712
- });
731
+ safeLog('info', `[${new Date().toISOString()}] Received request for tool: ${name}`);
713
732
  if (!args) {
714
733
  throw new Error('No arguments provided');
715
734
  }
@@ -721,16 +740,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
721
740
  const { url, ...options } = args;
722
741
  try {
723
742
  const scrapeStartTime = Date.now();
724
- server.sendLoggingMessage({
725
- level: 'info',
726
- data: `Starting scrape for URL: ${url} with options: ${JSON.stringify(options)}`,
727
- });
743
+ safeLog('info', `Starting scrape for URL: ${url} with options: ${JSON.stringify(options)}`);
728
744
  const response = await client.scrapeUrl(url, options);
729
745
  // Log performance metrics
730
- server.sendLoggingMessage({
731
- level: 'info',
732
- data: `Scrape completed in ${Date.now() - scrapeStartTime}ms`,
733
- });
746
+ safeLog('info', `Scrape completed in ${Date.now() - scrapeStartTime}ms`);
734
747
  if ('success' in response && !response.success) {
735
748
  throw new Error(response.error || 'Scraping failed');
736
749
  }
@@ -756,14 +769,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
756
769
  }
757
770
  // Add warning to response if present
758
771
  if (response.warning) {
759
- server.sendLoggingMessage({
760
- level: 'warning',
761
- data: response.warning,
762
- });
772
+ safeLog('warning', response.warning);
763
773
  }
764
774
  return {
765
775
  content: [
766
- { type: 'text', text: contentParts.join('\n\n') || 'No content available' },
776
+ {
777
+ type: 'text',
778
+ text: trimResponseText(contentParts.join('\n\n') || 'No content available'),
779
+ },
767
780
  ],
768
781
  isError: false,
769
782
  };
@@ -771,7 +784,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
771
784
  catch (error) {
772
785
  const errorMessage = error instanceof Error ? error.message : String(error);
773
786
  return {
774
- content: [{ type: 'text', text: errorMessage }],
787
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
775
788
  isError: true,
776
789
  };
777
790
  }
@@ -789,7 +802,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
789
802
  throw new Error('No links received from FireCrawl API');
790
803
  }
791
804
  return {
792
- content: [{ type: 'text', text: response.links.join('\n') }],
805
+ content: [
806
+ { type: 'text', text: trimResponseText(response.links.join('\n')) },
807
+ ],
793
808
  isError: false,
794
809
  };
795
810
  }
@@ -812,15 +827,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
812
827
  batchOperations.set(operationId, operation);
813
828
  // Queue the operation
814
829
  batchQueue.add(() => processBatchOperation(operation));
815
- server.sendLoggingMessage({
816
- level: 'info',
817
- data: `Queued batch operation ${operationId} with ${args.urls.length} URLs`,
818
- });
830
+ safeLog('info', `Queued batch operation ${operationId} with ${args.urls.length} URLs`);
819
831
  return {
820
832
  content: [
821
833
  {
822
834
  type: 'text',
823
- text: `Batch operation queued with ID: ${operationId}. Use firecrawl_check_batch_status to check progress.`,
835
+ text: trimResponseText(`Batch operation queued with ID: ${operationId}. Use firecrawl_check_batch_status to check progress.`),
824
836
  },
825
837
  ],
826
838
  isError: false,
@@ -831,7 +843,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
831
843
  ? error.message
832
844
  : `Batch operation failed: ${JSON.stringify(error)}`;
833
845
  return {
834
- content: [{ type: 'text', text: errorMessage }],
846
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
835
847
  isError: true,
836
848
  };
837
849
  }
@@ -846,7 +858,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
846
858
  content: [
847
859
  {
848
860
  type: 'text',
849
- text: `No batch operation found with ID: ${args.id}`,
861
+ text: trimResponseText(`No batch operation found with ID: ${args.id}`),
850
862
  },
851
863
  ],
852
864
  isError: true,
@@ -860,7 +872,7 @@ ${operation.result
860
872
  ? `Results: ${JSON.stringify(operation.result, null, 2)}`
861
873
  : ''}`;
862
874
  return {
863
- content: [{ type: 'text', text: status }],
875
+ content: [{ type: 'text', text: trimResponseText(status) }],
864
876
  isError: false,
865
877
  };
866
878
  }
@@ -881,7 +893,7 @@ ${operation.result
881
893
  content: [
882
894
  {
883
895
  type: 'text',
884
- text: `Started crawl for ${url} with job ID: ${response.id}`,
896
+ text: trimResponseText(`Started crawl for ${url} with job ID: ${response.id}`),
885
897
  },
886
898
  ],
887
899
  isError: false,
@@ -902,7 +914,7 @@ Credits Used: ${response.creditsUsed}
902
914
  Expires At: ${response.expiresAt}
903
915
  ${response.data.length > 0 ? '\nResults:\n' + formatResults(response.data) : ''}`;
904
916
  return {
905
- content: [{ type: 'text', text: status }],
917
+ content: [{ type: 'text', text: trimResponseText(status) }],
906
918
  isError: false,
907
919
  };
908
920
  }
@@ -927,7 +939,7 @@ Description: ${result.description || 'No description'}
927
939
  ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
928
940
  .join('\n\n');
929
941
  return {
930
- content: [{ type: 'text', text: results }],
942
+ content: [{ type: 'text', text: trimResponseText(results) }],
931
943
  isError: false,
932
944
  };
933
945
  }
@@ -936,7 +948,7 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
936
948
  ? error.message
937
949
  : `Search failed: ${JSON.stringify(error)}`;
938
950
  return {
939
- content: [{ type: 'text', text: errorMessage }],
951
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
940
952
  isError: true,
941
953
  };
942
954
  }
@@ -947,16 +959,10 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
947
959
  }
948
960
  try {
949
961
  const extractStartTime = Date.now();
950
- server.sendLoggingMessage({
951
- level: 'info',
952
- data: `Starting extraction for URLs: ${args.urls.join(', ')}`,
953
- });
962
+ safeLog('info', `Starting extraction for URLs: ${args.urls.join(', ')}`);
954
963
  // Log if using self-hosted instance
955
964
  if (FIRECRAWL_API_URL) {
956
- server.sendLoggingMessage({
957
- level: 'info',
958
- data: 'Using self-hosted instance for extraction',
959
- });
965
+ safeLog('info', 'Using self-hosted instance for extraction');
960
966
  }
961
967
  const extractResponse = await withRetry(async () => client.extract(args.urls, {
962
968
  prompt: args.prompt,
@@ -977,25 +983,19 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
977
983
  await updateCreditUsage(response.creditsUsed || 0);
978
984
  }
979
985
  // Log performance metrics
980
- server.sendLoggingMessage({
981
- level: 'info',
982
- data: `Extraction completed in ${Date.now() - extractStartTime}ms`,
983
- });
986
+ safeLog('info', `Extraction completed in ${Date.now() - extractStartTime}ms`);
984
987
  // Add warning to response if present
985
988
  const result = {
986
989
  content: [
987
990
  {
988
991
  type: 'text',
989
- text: JSON.stringify(response.data, null, 2),
992
+ text: trimResponseText(JSON.stringify(response.data, null, 2)),
990
993
  },
991
994
  ],
992
995
  isError: false,
993
996
  };
994
997
  if (response.warning) {
995
- server.sendLoggingMessage({
996
- level: 'warning',
997
- data: response.warning,
998
- });
998
+ safeLog('warning', response.warning);
999
999
  }
1000
1000
  return result;
1001
1001
  }
@@ -1004,22 +1004,19 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
1004
1004
  // Special handling for self-hosted instance errors
1005
1005
  if (FIRECRAWL_API_URL &&
1006
1006
  errorMessage.toLowerCase().includes('not supported')) {
1007
- server.sendLoggingMessage({
1008
- level: 'error',
1009
- data: 'Extraction is not supported by this self-hosted instance',
1010
- });
1007
+ safeLog('error', 'Extraction is not supported by this self-hosted instance');
1011
1008
  return {
1012
1009
  content: [
1013
1010
  {
1014
1011
  type: 'text',
1015
- text: 'Extraction is not supported by this self-hosted instance. Please ensure LLM support is configured.',
1012
+ text: trimResponseText('Extraction is not supported by this self-hosted instance. Please ensure LLM support is configured.'),
1016
1013
  },
1017
1014
  ],
1018
1015
  isError: true,
1019
1016
  };
1020
1017
  }
1021
1018
  return {
1022
- content: [{ type: 'text', text: errorMessage }],
1019
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
1023
1020
  isError: true,
1024
1021
  };
1025
1022
  }
@@ -1030,10 +1027,7 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
1030
1027
  }
1031
1028
  try {
1032
1029
  const researchStartTime = Date.now();
1033
- server.sendLoggingMessage({
1034
- level: 'info',
1035
- data: `Starting deep research for query: ${args.query}`,
1036
- });
1030
+ safeLog('info', `Starting deep research for query: ${args.query}`);
1037
1031
  const response = await client.deepResearch(args.query, {
1038
1032
  maxDepth: args.maxDepth,
1039
1033
  timeLimit: args.timeLimit,
@@ -1041,23 +1035,14 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
1041
1035
  },
1042
1036
  // Activity callback
1043
1037
  (activity) => {
1044
- server.sendLoggingMessage({
1045
- level: 'info',
1046
- data: `Research activity: ${activity.message} (Depth: ${activity.depth})`,
1047
- });
1038
+ safeLog('info', `Research activity: ${activity.message} (Depth: ${activity.depth})`);
1048
1039
  },
1049
1040
  // Source callback
1050
1041
  (source) => {
1051
- server.sendLoggingMessage({
1052
- level: 'info',
1053
- data: `Research source found: ${source.url}${source.title ? ` - ${source.title}` : ''}`,
1054
- });
1042
+ safeLog('info', `Research source found: ${source.url}${source.title ? ` - ${source.title}` : ''}`);
1055
1043
  });
1056
1044
  // Log performance metrics
1057
- server.sendLoggingMessage({
1058
- level: 'info',
1059
- data: `Deep research completed in ${Date.now() - researchStartTime}ms`,
1060
- });
1045
+ safeLog('info', `Deep research completed in ${Date.now() - researchStartTime}ms`);
1061
1046
  if (!response.success) {
1062
1047
  throw new Error(response.error || 'Deep research failed');
1063
1048
  }
@@ -1068,42 +1053,82 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
1068
1053
  sources: response.data.sources,
1069
1054
  };
1070
1055
  return {
1071
- content: [{ type: 'text', text: formattedResponse.finalAnalysis }],
1056
+ content: [
1057
+ {
1058
+ type: 'text',
1059
+ text: trimResponseText(formattedResponse.finalAnalysis),
1060
+ },
1061
+ ],
1072
1062
  isError: false,
1073
1063
  };
1074
1064
  }
1075
1065
  catch (error) {
1076
1066
  const errorMessage = error instanceof Error ? error.message : String(error);
1077
1067
  return {
1078
- content: [{ type: 'text', text: errorMessage }],
1068
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
1069
+ isError: true,
1070
+ };
1071
+ }
1072
+ }
1073
+ case 'firecrawl_generate_llmstxt': {
1074
+ if (!isGenerateLLMsTextOptions(args)) {
1075
+ throw new Error('Invalid arguments for firecrawl_generate_llmstxt');
1076
+ }
1077
+ try {
1078
+ const { url, ...params } = args;
1079
+ const generateStartTime = Date.now();
1080
+ safeLog('info', `Starting LLMs.txt generation for URL: ${url}`);
1081
+ // Start the generation process
1082
+ const response = await withRetry(async () => client.generateLLMsText(url, params), 'LLMs.txt generation');
1083
+ if (!response.success) {
1084
+ throw new Error(response.error || 'LLMs.txt generation failed');
1085
+ }
1086
+ // Log performance metrics
1087
+ safeLog('info', `LLMs.txt generation completed in ${Date.now() - generateStartTime}ms`);
1088
+ // Format the response
1089
+ let resultText = '';
1090
+ if ('data' in response) {
1091
+ resultText = `LLMs.txt content:\n\n${response.data.llmstxt}`;
1092
+ if (args.showFullText && response.data.llmsfulltxt) {
1093
+ resultText += `\n\nLLMs-full.txt content:\n\n${response.data.llmsfulltxt}`;
1094
+ }
1095
+ }
1096
+ return {
1097
+ content: [{ type: 'text', text: trimResponseText(resultText) }],
1098
+ isError: false,
1099
+ };
1100
+ }
1101
+ catch (error) {
1102
+ const errorMessage = error instanceof Error ? error.message : String(error);
1103
+ return {
1104
+ content: [{ type: 'text', text: trimResponseText(errorMessage) }],
1079
1105
  isError: true,
1080
1106
  };
1081
1107
  }
1082
1108
  }
1083
1109
  default:
1084
1110
  return {
1085
- content: [{ type: 'text', text: `Unknown tool: ${name}` }],
1111
+ content: [
1112
+ { type: 'text', text: trimResponseText(`Unknown tool: ${name}`) },
1113
+ ],
1086
1114
  isError: true,
1087
1115
  };
1088
1116
  }
1089
1117
  }
1090
1118
  catch (error) {
1091
1119
  // Log detailed error information
1092
- server.sendLoggingMessage({
1093
- level: 'error',
1094
- data: {
1095
- message: `Request failed: ${error instanceof Error ? error.message : String(error)}`,
1096
- tool: request.params.name,
1097
- arguments: request.params.arguments,
1098
- timestamp: new Date().toISOString(),
1099
- duration: Date.now() - startTime,
1100
- },
1120
+ safeLog('error', {
1121
+ message: `Request failed: ${error instanceof Error ? error.message : String(error)}`,
1122
+ tool: request.params.name,
1123
+ arguments: request.params.arguments,
1124
+ timestamp: new Date().toISOString(),
1125
+ duration: Date.now() - startTime,
1101
1126
  });
1102
1127
  return {
1103
1128
  content: [
1104
1129
  {
1105
1130
  type: 'text',
1106
- text: `Error: ${error instanceof Error ? error.message : String(error)}`,
1131
+ text: trimResponseText(`Error: ${error instanceof Error ? error.message : String(error)}`),
1107
1132
  },
1108
1133
  ],
1109
1134
  isError: true,
@@ -1111,10 +1136,7 @@ ${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
1111
1136
  }
1112
1137
  finally {
1113
1138
  // Log request completion with performance metrics
1114
- server.sendLoggingMessage({
1115
- level: 'info',
1116
- data: `Request completed in ${Date.now() - startTime}ms`,
1117
- });
1139
+ safeLog('info', `Request completed in ${Date.now() - startTime}ms`);
1118
1140
  }
1119
1141
  });
1120
1142
  // Helper function to format results
@@ -1128,21 +1150,29 @@ ${doc.metadata?.title ? `Title: ${doc.metadata.title}` : ''}`;
1128
1150
  })
1129
1151
  .join('\n\n');
1130
1152
  }
1153
+ // Add type guard for credit usage
1154
+ function hasCredits(response) {
1155
+ return 'creditsUsed' in response && typeof response.creditsUsed === 'number';
1156
+ }
1157
+ // Utility function to trim trailing whitespace from text responses
1158
+ // This prevents Claude API errors with "final assistant content cannot end with trailing whitespace"
1159
+ function trimResponseText(text) {
1160
+ return text.trim();
1161
+ }
1131
1162
  // Server startup
1132
1163
  async function runServer() {
1133
1164
  try {
1134
1165
  console.error('Initializing FireCrawl MCP Server...');
1135
1166
  const transport = new StdioServerTransport();
1167
+ // Detect if we're using stdio transport
1168
+ isStdioTransport = transport instanceof StdioServerTransport;
1169
+ if (isStdioTransport) {
1170
+ console.error('Running in stdio mode, logging will be directed to stderr');
1171
+ }
1136
1172
  await server.connect(transport);
1137
1173
  // Now that we're connected, we can send logging messages
1138
- server.sendLoggingMessage({
1139
- level: 'info',
1140
- data: 'FireCrawl MCP Server initialized successfully',
1141
- });
1142
- server.sendLoggingMessage({
1143
- level: 'info',
1144
- data: `Configuration: API URL: ${FIRECRAWL_API_URL || 'default'}`,
1145
- });
1174
+ safeLog('info', 'FireCrawl MCP Server initialized successfully');
1175
+ safeLog('info', `Configuration: API URL: ${FIRECRAWL_API_URL || 'default'}`);
1146
1176
  console.error('FireCrawl MCP Server running on stdio');
1147
1177
  }
1148
1178
  catch (error) {
@@ -1154,7 +1184,3 @@ runServer().catch((error) => {
1154
1184
  console.error('Fatal error running server:', error);
1155
1185
  process.exit(1);
1156
1186
  });
1157
- // Add type guard for credit usage
1158
- function hasCredits(response) {
1159
- return 'creditsUsed' in response && typeof response.creditsUsed === 'number';
1160
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firecrawl-mcp",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "MCP server for FireCrawl web scraping integration. Supports both cloud and self-hosted instances. Features include web scraping, batch processing, structured data extraction, and LLM-powered content analysis.",
5
5
  "type": "module",
6
6
  "bin": {