firecrawl-mcp 3.6.2 → 3.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.
@@ -1,255 +0,0 @@
1
- import FirecrawlApp from '@mendable/firecrawl-js';
2
- import { describe, expect, jest, test, beforeEach, afterEach, } from '@jest/globals';
3
- import { mock } from 'jest-mock-extended';
4
- // Mock FirecrawlApp
5
- jest.mock('@mendable/firecrawl-js');
6
- describe('Firecrawl Tool Tests', () => {
7
- let mockClient;
8
- let requestHandler;
9
- beforeEach(() => {
10
- jest.clearAllMocks();
11
- mockClient = mock();
12
- // Set up mock implementations
13
- const mockInstance = new FirecrawlApp({ apiKey: 'test' });
14
- Object.assign(mockInstance, mockClient);
15
- // Create request handler
16
- requestHandler = async (request) => {
17
- const { name, arguments: args } = request.params;
18
- if (!args) {
19
- throw new Error('No arguments provided');
20
- }
21
- return handleRequest(name, args, mockClient);
22
- };
23
- });
24
- afterEach(() => {
25
- jest.clearAllMocks();
26
- });
27
- // Test scrape functionality
28
- test('should handle scrape request', async () => {
29
- const url = 'https://example.com';
30
- const options = { formats: ['markdown'] };
31
- const mockResponse = {
32
- success: true,
33
- markdown: '# Test Content',
34
- html: undefined,
35
- rawHtml: undefined,
36
- url: 'https://example.com',
37
- actions: undefined,
38
- };
39
- mockClient.scrapeUrl.mockResolvedValueOnce(mockResponse);
40
- const response = await requestHandler({
41
- method: 'call_tool',
42
- params: {
43
- name: 'firecrawl_scrape',
44
- arguments: { url, ...options },
45
- },
46
- });
47
- expect(response).toEqual({
48
- content: [{ type: 'text', text: '# Test Content' }],
49
- isError: false,
50
- });
51
- expect(mockClient.scrapeUrl).toHaveBeenCalledWith(url, {
52
- formats: ['markdown'],
53
- url,
54
- });
55
- });
56
- // Test scrape with maxAge parameter
57
- test('should handle scrape request with maxAge parameter', async () => {
58
- const url = 'https://example.com';
59
- const options = { formats: ['markdown'], maxAge: 3600000 };
60
- const mockResponse = {
61
- success: true,
62
- markdown: '# Test Content',
63
- html: undefined,
64
- rawHtml: undefined,
65
- url: 'https://example.com',
66
- actions: undefined,
67
- };
68
- mockClient.scrapeUrl.mockResolvedValueOnce(mockResponse);
69
- const response = await requestHandler({
70
- method: 'call_tool',
71
- params: {
72
- name: 'firecrawl_scrape',
73
- arguments: { url, ...options },
74
- },
75
- });
76
- expect(response).toEqual({
77
- content: [{ type: 'text', text: '# Test Content' }],
78
- isError: false,
79
- });
80
- expect(mockClient.scrapeUrl).toHaveBeenCalledWith(url, {
81
- formats: ['markdown'],
82
- maxAge: 3600000,
83
- url,
84
- });
85
- });
86
- // Test batch scrape functionality
87
- test('should handle batch scrape request', async () => {
88
- const urls = ['https://example.com'];
89
- const options = { formats: ['markdown'] };
90
- mockClient.asyncBatchScrapeUrls.mockResolvedValueOnce({
91
- success: true,
92
- id: 'test-batch-id',
93
- });
94
- const response = await requestHandler({
95
- method: 'call_tool',
96
- params: {
97
- name: 'firecrawl_batch_scrape',
98
- arguments: { urls, options },
99
- },
100
- });
101
- expect(response.content[0].text).toContain('Batch operation queued with ID: batch_');
102
- expect(mockClient.asyncBatchScrapeUrls).toHaveBeenCalledWith(urls, options);
103
- });
104
- // Test search functionality
105
- test('should handle search request', async () => {
106
- const query = 'test query';
107
- const scrapeOptions = { formats: ['markdown'] };
108
- const mockSearchResponse = {
109
- success: true,
110
- data: [
111
- {
112
- url: 'https://example.com',
113
- title: 'Test Page',
114
- description: 'Test Description',
115
- markdown: '# Test Content',
116
- actions: undefined,
117
- },
118
- ],
119
- };
120
- mockClient.search.mockResolvedValueOnce(mockSearchResponse);
121
- const response = await requestHandler({
122
- method: 'call_tool',
123
- params: {
124
- name: 'firecrawl_search',
125
- arguments: { query, scrapeOptions },
126
- },
127
- });
128
- expect(response.isError).toBe(false);
129
- expect(response.content[0].text).toContain('Test Page');
130
- expect(mockClient.search).toHaveBeenCalledWith(query, scrapeOptions);
131
- });
132
- // Test crawl functionality
133
- test('should handle crawl request', async () => {
134
- const url = 'https://example.com';
135
- const options = { maxDepth: 2 };
136
- mockClient.asyncCrawlUrl.mockResolvedValueOnce({
137
- success: true,
138
- id: 'test-crawl-id',
139
- });
140
- const response = await requestHandler({
141
- method: 'call_tool',
142
- params: {
143
- name: 'firecrawl_crawl',
144
- arguments: { url, ...options },
145
- },
146
- });
147
- expect(response.isError).toBe(false);
148
- expect(response.content[0].text).toContain('test-crawl-id');
149
- expect(mockClient.asyncCrawlUrl).toHaveBeenCalledWith(url, {
150
- maxDepth: 2,
151
- url,
152
- });
153
- });
154
- // Test error handling
155
- test('should handle API errors', async () => {
156
- const url = 'https://example.com';
157
- mockClient.scrapeUrl.mockRejectedValueOnce(new Error('API Error'));
158
- const response = await requestHandler({
159
- method: 'call_tool',
160
- params: {
161
- name: 'firecrawl_scrape',
162
- arguments: { url },
163
- },
164
- });
165
- expect(response.isError).toBe(true);
166
- expect(response.content[0].text).toContain('API Error');
167
- });
168
- // Test rate limiting
169
- test('should handle rate limits', async () => {
170
- const url = 'https://example.com';
171
- // Mock rate limit error
172
- mockClient.scrapeUrl.mockRejectedValueOnce(new Error('rate limit exceeded'));
173
- const response = await requestHandler({
174
- method: 'call_tool',
175
- params: {
176
- name: 'firecrawl_scrape',
177
- arguments: { url },
178
- },
179
- });
180
- expect(response.isError).toBe(true);
181
- expect(response.content[0].text).toContain('rate limit exceeded');
182
- });
183
- });
184
- // Helper function to simulate request handling
185
- async function handleRequest(name, args, client) {
186
- try {
187
- switch (name) {
188
- case 'firecrawl_scrape': {
189
- const response = await client.scrapeUrl(args.url, args);
190
- if (!response.success) {
191
- throw new Error(response.error || 'Scraping failed');
192
- }
193
- return {
194
- content: [
195
- { type: 'text', text: response.markdown || 'No content available' },
196
- ],
197
- isError: false,
198
- };
199
- }
200
- case 'firecrawl_batch_scrape': {
201
- const response = await client.asyncBatchScrapeUrls(args.urls, args.options);
202
- return {
203
- content: [
204
- {
205
- type: 'text',
206
- text: `Batch operation queued with ID: batch_1. Use firecrawl_check_batch_status to check progress.`,
207
- },
208
- ],
209
- isError: false,
210
- };
211
- }
212
- case 'firecrawl_search': {
213
- const response = await client.search(args.query, args.scrapeOptions);
214
- if (!response.success) {
215
- throw new Error(response.error || 'Search failed');
216
- }
217
- const results = response.data
218
- .map((result) => `URL: ${result.url}\nTitle: ${result.title || 'No title'}\nDescription: ${result.description || 'No description'}\n${result.markdown ? `\nContent:\n${result.markdown}` : ''}`)
219
- .join('\n\n');
220
- return {
221
- content: [{ type: 'text', text: results }],
222
- isError: false,
223
- };
224
- }
225
- case 'firecrawl_crawl': {
226
- const response = await client.asyncCrawlUrl(args.url, args);
227
- if (!response.success) {
228
- throw new Error(response.error);
229
- }
230
- return {
231
- content: [
232
- {
233
- type: 'text',
234
- text: `Started crawl for ${args.url} with job ID: ${response.id}`,
235
- },
236
- ],
237
- isError: false,
238
- };
239
- }
240
- default:
241
- throw new Error(`Unknown tool: ${name}`);
242
- }
243
- }
244
- catch (error) {
245
- return {
246
- content: [
247
- {
248
- type: 'text',
249
- text: error instanceof Error ? error.message : String(error),
250
- },
251
- ],
252
- isError: true,
253
- };
254
- }
255
- }
@@ -1,58 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- // Set test timeout
3
- jest.setTimeout(30000);
4
- // Create mock responses
5
- const mockSearchResponse = {
6
- success: true,
7
- data: [
8
- {
9
- url: 'https://example.com',
10
- title: 'Test Page',
11
- description: 'Test Description',
12
- markdown: '# Test Content',
13
- actions: null,
14
- },
15
- ],
16
- };
17
- const mockBatchScrapeResponse = {
18
- success: true,
19
- id: 'test-batch-id',
20
- };
21
- const mockBatchStatusResponse = {
22
- success: true,
23
- status: 'completed',
24
- completed: 1,
25
- total: 1,
26
- creditsUsed: 1,
27
- expiresAt: new Date(),
28
- data: [
29
- {
30
- url: 'https://example.com',
31
- title: 'Test Page',
32
- description: 'Test Description',
33
- markdown: '# Test Content',
34
- actions: null,
35
- },
36
- ],
37
- };
38
- // Create mock instance methods
39
- const mockSearch = jest.fn().mockImplementation(async () => mockSearchResponse);
40
- const mockAsyncBatchScrapeUrls = jest
41
- .fn()
42
- .mockImplementation(async () => mockBatchScrapeResponse);
43
- const mockCheckBatchScrapeStatus = jest
44
- .fn()
45
- .mockImplementation(async () => mockBatchStatusResponse);
46
- // Create mock instance
47
- const mockInstance = {
48
- apiKey: 'test-api-key',
49
- apiUrl: 'test-api-url',
50
- search: mockSearch,
51
- asyncBatchScrapeUrls: mockAsyncBatchScrapeUrls,
52
- checkBatchScrapeStatus: mockCheckBatchScrapeStatus,
53
- };
54
- // Mock the module
55
- jest.mock('@mendable/firecrawl-js', () => ({
56
- __esModule: true,
57
- default: jest.fn().mockImplementation(() => mockInstance),
58
- }));