scrapercity 1.0.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/AGENT_DOCS.txt +205 -0
- package/README.md +128 -0
- package/SKILL.md +98 -0
- package/bin/cli.mjs +518 -0
- package/bin/mcp.mjs +474 -0
- package/lib/client.mjs +215 -0
- package/package.json +30 -0
package/bin/mcp.mjs
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/mcp.mjs - ScraperCity MCP Server (stdio transport)
|
|
3
|
+
// Connect from Claude Code / Cursor / any MCP client
|
|
4
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
6
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
7
|
+
import * as sc from '../lib/client.mjs'
|
|
8
|
+
|
|
9
|
+
const server = new Server(
|
|
10
|
+
{ name: 'scrapercity', version: '1.0.0' },
|
|
11
|
+
{ capabilities: { tools: {} } }
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// ── Tool definitions ──────────────────────────────────────────
|
|
15
|
+
const TOOLS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'check_wallet',
|
|
18
|
+
description: 'Check account balance, plan, and billing info. Call this FIRST to verify credits before running any scrape.',
|
|
19
|
+
inputSchema: { type: 'object', properties: {} }
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'list_runs',
|
|
23
|
+
description: 'List recent scraper runs with status, lead counts, and costs.',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
hours: { type: 'number', description: 'How many hours back to look (default 24, max 168)', default: 24 },
|
|
28
|
+
limit: { type: 'number', description: 'Max runs to return (default 20, max 100)', default: 20 }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'scrape_apollo',
|
|
34
|
+
description: 'Scrape leads from Apollo.io using a search URL. IMPORTANT: Apollo delivery takes up to 4 days. Use webhooks (configure at app.scrapercity.com/dashboard/webhooks) instead of polling. Returns a runId to check status later.',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
url: { type: 'string', description: 'Full Apollo.io search URL (from the People search page)' },
|
|
39
|
+
count: { type: 'number', description: 'Number of leads to pull (min 500, max 50000)', default: 1000 },
|
|
40
|
+
fileName: { type: 'string', description: 'Name for this export', default: '' }
|
|
41
|
+
},
|
|
42
|
+
required: ['url']
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'scrape_apollo_filters',
|
|
47
|
+
description: 'Scrape leads from Apollo.io using filter parameters instead of a URL. Same 4-day delivery - use webhooks. At least one filter required.',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
seniorityLevel: { type: 'string', description: 'e.g. "director", "vp", "c_suite", "manager", "senior"' },
|
|
52
|
+
functionDept: { type: 'string', description: 'e.g. "sales", "marketing", "engineering", "finance"' },
|
|
53
|
+
companyIndustry: { type: 'string', description: 'e.g. "computer software", "financial services"' },
|
|
54
|
+
personCountry: { type: 'string', description: 'e.g. "United States", "United Kingdom"' },
|
|
55
|
+
personState: { type: 'string', description: 'e.g. "California", "New York"' },
|
|
56
|
+
companyCountry: { type: 'string', description: 'Company HQ country' },
|
|
57
|
+
companyState: { type: 'string', description: 'Company HQ state' },
|
|
58
|
+
companySize: { type: 'string', description: 'e.g. "11-50", "51-200", "201-500", "501-1000", "1001-5000"' },
|
|
59
|
+
personTitles: { type: 'array', items: { type: 'string' }, description: 'Specific job titles to target' },
|
|
60
|
+
companyDomains: { type: 'array', items: { type: 'string' }, description: 'Specific company domains' },
|
|
61
|
+
companyKeywords: { type: 'array', items: { type: 'string' }, description: 'Company description keywords' },
|
|
62
|
+
personCities: { type: 'array', items: { type: 'string' }, description: 'Person city filter' },
|
|
63
|
+
companyCities: { type: 'array', items: { type: 'string' }, description: 'Company city filter' },
|
|
64
|
+
hasPhone: { type: 'boolean', description: 'Only return contacts with phone numbers', default: false },
|
|
65
|
+
count: { type: 'number', description: 'Number of leads (min 500, max 50000)', default: 1000 },
|
|
66
|
+
fileName: { type: 'string', description: 'Export name', default: 'Apollo Export' }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'scrape_maps',
|
|
72
|
+
description: 'Scrape businesses from Google Maps. Returns businesses with names, addresses, phone numbers, websites, emails, ratings. Typically completes in 5-30 minutes.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
query: { type: 'string', description: 'Search keyword, e.g. "plumbers", "dentists", "restaurants"' },
|
|
77
|
+
location: { type: 'string', description: 'City/area, e.g. "Denver, CO", "London, UK"' },
|
|
78
|
+
limit: { type: 'number', description: 'Max places to scrape (default 500)', default: 500 }
|
|
79
|
+
},
|
|
80
|
+
required: ['query', 'location']
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'validate_emails',
|
|
85
|
+
description: 'Validate a list of email addresses. Returns deliverability status, catch-all detection, MX records, and company info. Typically completes in 1-10 minutes.',
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
emails: { type: 'array', items: { type: 'string' }, description: 'Email addresses to validate' }
|
|
90
|
+
},
|
|
91
|
+
required: ['emails']
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'find_emails',
|
|
96
|
+
description: 'Find business email addresses given a person name and company. Provide first/last name + domain or company name.',
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
contacts: {
|
|
101
|
+
type: 'array',
|
|
102
|
+
items: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties: {
|
|
105
|
+
first_name: { type: 'string' },
|
|
106
|
+
last_name: { type: 'string' },
|
|
107
|
+
full_name: { type: 'string', description: 'Alternative to first/last' },
|
|
108
|
+
domain: { type: 'string', description: 'Company domain, e.g. "acme.com"' },
|
|
109
|
+
company_name: { type: 'string', description: 'Alternative to domain' }
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
description: 'Contacts to find emails for. Each needs a name AND a company/domain.'
|
|
113
|
+
},
|
|
114
|
+
autoValidateEmails: { type: 'boolean', description: 'Auto-validate found emails', default: false },
|
|
115
|
+
autoFindMobiles: { type: 'boolean', description: 'Auto-find mobile numbers', default: false }
|
|
116
|
+
},
|
|
117
|
+
required: ['contacts']
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'find_mobiles',
|
|
122
|
+
description: 'Find mobile phone numbers from LinkedIn URLs or work emails.',
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
inputs: { type: 'array', items: { type: 'string' }, description: 'LinkedIn profile URLs or work email addresses' }
|
|
127
|
+
},
|
|
128
|
+
required: ['inputs']
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'find_people',
|
|
133
|
+
description: 'Skip trace / people finder. Look up personal info by name, email, phone, or address.',
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
name: { type: 'array', items: { type: 'string' }, description: 'Full names to search' },
|
|
138
|
+
email: { type: 'array', items: { type: 'string' }, description: 'Email addresses to search' },
|
|
139
|
+
phone_number: { type: 'array', items: { type: 'string' }, description: 'Phone numbers to search' },
|
|
140
|
+
street_citystatezip: { type: 'array', items: { type: 'string' }, description: 'Addresses (format: "123 Main St, City, ST 12345")' },
|
|
141
|
+
max_results: { type: 'number', description: 'Results per search (default 1)', default: 1 }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'scrape_store_leads',
|
|
147
|
+
description: 'Get ecommerce store data (Shopify, WooCommerce, etc). Returns domains, emails, phones, social profiles, revenue estimates. Instant results from cached database.',
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
platform: { type: 'string', description: 'e.g. "shopify", "woocommerce", "bigcommerce"', default: 'shopify' },
|
|
152
|
+
countryCode: { type: 'string', description: 'e.g. "US", "GB", "CA"' },
|
|
153
|
+
category: { type: 'string', description: 'Store category filter' },
|
|
154
|
+
city: { type: 'string', description: 'City filter' },
|
|
155
|
+
technologies: { type: 'string', description: 'Technology filter' },
|
|
156
|
+
apps: { type: 'string', description: 'Installed app filter' },
|
|
157
|
+
emails: { type: 'boolean', description: 'Only stores with emails', default: false },
|
|
158
|
+
phones: { type: 'boolean', description: 'Only stores with phones', default: false },
|
|
159
|
+
instagram: { type: 'boolean', description: 'Only stores with Instagram' },
|
|
160
|
+
facebook: { type: 'boolean', description: 'Only stores with Facebook' },
|
|
161
|
+
totalLeads: { type: 'number', description: 'Number of leads to pull', default: 1000 }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'scrape_builtwith',
|
|
167
|
+
description: 'Find all websites using a specific technology. Returns domains with contact info. $4.99 per search.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
technology: { type: 'string', description: 'Technology name, e.g. "Shopify", "Stripe", "HubSpot"' },
|
|
172
|
+
fileName: { type: 'string', description: 'Export name' }
|
|
173
|
+
},
|
|
174
|
+
required: ['technology']
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'search_criminal_records',
|
|
179
|
+
description: 'Search criminal records by name. $1 per search, only charged if records found.',
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
name: { type: 'string', description: 'Full name to search' },
|
|
184
|
+
state: { type: 'string', description: 'US state code, e.g. "CA", "TX" (optional, searches all if omitted)' },
|
|
185
|
+
dob: { type: 'string', description: 'Date of birth "MM/DD/YYYY" (optional, improves accuracy)' }
|
|
186
|
+
},
|
|
187
|
+
required: ['name']
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'scrape_airbnb',
|
|
192
|
+
description: 'Scrape Airbnb host emails by city or listing URL. Returns host contact info including email addresses.',
|
|
193
|
+
inputSchema: {
|
|
194
|
+
type: 'object',
|
|
195
|
+
properties: {
|
|
196
|
+
mode: { type: 'string', enum: ['city', 'single', 'bulk'], description: 'Search mode', default: 'city' },
|
|
197
|
+
city: { type: 'array', items: { type: 'string' }, description: 'Cities to search (for city mode), e.g. ["Miami, FL"]' },
|
|
198
|
+
listingUrl: { type: 'string', description: 'Single Airbnb listing URL (for single mode)' },
|
|
199
|
+
bulkListings: { type: 'array', items: { type: 'string' }, description: 'Multiple listing URLs (for bulk mode)' },
|
|
200
|
+
maxResults: { type: 'number', description: 'Max results for billing cap', default: 100 },
|
|
201
|
+
checkin: { type: 'string', description: 'Check-in date YYYY-MM-DD' },
|
|
202
|
+
checkout: { type: 'string', description: 'Check-out date YYYY-MM-DD' },
|
|
203
|
+
maxPages: { type: 'number', description: 'Max pages to crawl per city', default: 1 },
|
|
204
|
+
onlyUniqueEmails: { type: 'boolean', description: 'Deduplicate by email', default: false },
|
|
205
|
+
onlyProHosts: { type: 'boolean', description: 'Only professional hosts', default: false }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'scrape_youtube_email',
|
|
211
|
+
description: 'Find business emails for YouTube channels. Pass channel handles (@ChannelName) or URLs.',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
channels: { type: 'array', items: { type: 'string' }, description: 'YouTube channel handles or URLs, e.g. ["@MrBeast", "https://youtube.com/@Channel"]' }
|
|
216
|
+
},
|
|
217
|
+
required: ['channels']
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'scrape_website_finder',
|
|
222
|
+
description: 'Find contact info (emails, phones, social links) from a list of website domains. Optionally filter by job title.',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
domains: { type: 'array', items: { type: 'string' }, description: 'Website domains, e.g. ["acme.com", "example.com"]' },
|
|
227
|
+
jobTitle: { type: 'string', description: 'Filter contacts by job title, e.g. "CEO"' }
|
|
228
|
+
},
|
|
229
|
+
required: ['domains']
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'scrape_yelp',
|
|
234
|
+
description: 'Scrape business listings from Yelp. Search by keyword + location, or provide direct Yelp URLs.',
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
searchTerms: { type: 'array', items: { type: 'string' }, description: 'Search keywords, e.g. ["plumbers", "dentists"]' },
|
|
239
|
+
locations: { type: 'array', items: { type: 'string' }, description: 'Locations, e.g. ["Denver, CO"]' },
|
|
240
|
+
directUrls: { type: 'array', items: { type: 'string' }, description: 'Direct Yelp search/business URLs' },
|
|
241
|
+
searchLimit: { type: 'number', description: 'Max results per search', default: 10 }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'scrape_angi',
|
|
247
|
+
description: 'Scrape service provider listings from Angi (Angie\'s List). Search by keyword and zip codes.',
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: 'object',
|
|
250
|
+
properties: {
|
|
251
|
+
keyword: { type: 'string', description: 'Service type, e.g. "plumbers", "electricians"' },
|
|
252
|
+
zipCodes: { type: 'array', items: { type: 'string' }, description: 'ZIP codes to search, e.g. ["80202", "80203"]' },
|
|
253
|
+
maxItems: { type: 'number', description: 'Max results', default: 100 }
|
|
254
|
+
},
|
|
255
|
+
required: ['keyword']
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: 'scrape_zillow_agents',
|
|
260
|
+
description: 'Scrape real estate agent listings from Zillow by location.',
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
properties: {
|
|
264
|
+
location: { type: 'string', description: 'City or area, e.g. "Denver, CO"' },
|
|
265
|
+
specialty: { type: 'string', description: 'Agent specialty, e.g. "buyer", "seller"' },
|
|
266
|
+
language: { type: 'string', description: 'Language filter' },
|
|
267
|
+
searchLimit: { type: 'number', description: 'Max results', default: 10 }
|
|
268
|
+
},
|
|
269
|
+
required: ['location']
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'scrape_bizbuysell',
|
|
274
|
+
description: 'Scrape business-for-sale listings from BizBuySell. Provide search result URLs.',
|
|
275
|
+
inputSchema: {
|
|
276
|
+
type: 'object',
|
|
277
|
+
properties: {
|
|
278
|
+
startUrls: { type: 'array', items: { type: 'string' }, description: 'BizBuySell search URLs' },
|
|
279
|
+
maxItems: { type: 'number', description: 'Max listings to scrape', default: 100 }
|
|
280
|
+
},
|
|
281
|
+
required: ['startUrls']
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'scrape_crexi',
|
|
286
|
+
description: 'Scrape commercial real estate listings from Crexi. Provide search result URLs.',
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
startUrls: { type: 'array', items: { type: 'string' }, description: 'Crexi search URLs' }
|
|
291
|
+
},
|
|
292
|
+
required: ['startUrls']
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'scrape_property_lookup',
|
|
297
|
+
description: 'Look up property data by address. Optionally include owner contact information. $0.15 per address.',
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {
|
|
301
|
+
addresses: { type: 'array', items: { type: 'string' }, description: 'Full addresses, e.g. ["123 Main St, Denver, CO 80202"]' },
|
|
302
|
+
includeOwnerContact: { type: 'boolean', description: 'Include property owner contact info', default: false }
|
|
303
|
+
},
|
|
304
|
+
required: ['addresses']
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: 'check_run_status',
|
|
309
|
+
description: 'Check the status of a scraper run. Returns status (RUNNING/SUCCEEDED/FAILED/CANCELLED), lead count, and download URL when complete.',
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
properties: {
|
|
313
|
+
runId: { type: 'string', description: 'The run ID returned when starting a scrape' }
|
|
314
|
+
},
|
|
315
|
+
required: ['runId']
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: 'download_results',
|
|
320
|
+
description: 'Download the CSV results of a completed scraper run. Only works when status is SUCCEEDED.',
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: 'object',
|
|
323
|
+
properties: {
|
|
324
|
+
runId: { type: 'string', description: 'The run ID to download results for' },
|
|
325
|
+
outputPath: { type: 'string', description: 'File path to save CSV (default: {runId}.csv)' }
|
|
326
|
+
},
|
|
327
|
+
required: ['runId']
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: 'cancel_run',
|
|
332
|
+
description: 'Cancel a running scraper job.',
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: {
|
|
336
|
+
runId: { type: 'string', description: 'The run ID to cancel' }
|
|
337
|
+
},
|
|
338
|
+
required: ['runId']
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'query_lead_database',
|
|
343
|
+
description: 'Query the B2B lead database directly (requires $649/mo plan). Returns contacts with names, emails, phones, titles, companies. 100 per request, paginate with page param.',
|
|
344
|
+
inputSchema: {
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties: {
|
|
347
|
+
title: { type: 'string', description: 'Job title filter' },
|
|
348
|
+
industry: { type: 'string', description: 'Company industry' },
|
|
349
|
+
country: { type: 'string', description: 'Person country' },
|
|
350
|
+
state: { type: 'string', description: 'Person state' },
|
|
351
|
+
city: { type: 'string', description: 'Person city' },
|
|
352
|
+
companyName: { type: 'string', description: 'Company name' },
|
|
353
|
+
companyDomain: { type: 'string', description: 'Company domain' },
|
|
354
|
+
seniority: { type: 'string', description: 'e.g. "vp", "director", "c_suite"' },
|
|
355
|
+
department: { type: 'string', description: 'e.g. "sales", "engineering"' },
|
|
356
|
+
hasEmail: { type: 'boolean', description: 'Only contacts with email' },
|
|
357
|
+
hasPhone: { type: 'boolean', description: 'Only contacts with phone' },
|
|
358
|
+
page: { type: 'number', description: 'Page number (default 1)', default: 1 },
|
|
359
|
+
limit: { type: 'number', description: 'Results per page (max 100)', default: 50 }
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
// ── Register tool list handler ────────────────────────────────
|
|
366
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
367
|
+
return { tools: TOOLS }
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
// ── Tool execution handler ────────────────────────────────────
|
|
371
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
372
|
+
const { name, arguments: args } = request.params
|
|
373
|
+
try {
|
|
374
|
+
let result
|
|
375
|
+
switch (name) {
|
|
376
|
+
case 'check_wallet':
|
|
377
|
+
result = await sc.wallet()
|
|
378
|
+
break
|
|
379
|
+
case 'list_runs':
|
|
380
|
+
result = await sc.runs(args?.hours || 24, args?.limit || 20)
|
|
381
|
+
break
|
|
382
|
+
case 'scrape_apollo':
|
|
383
|
+
result = await sc.apollo(args.url, args.count || 1000, args.fileName || '')
|
|
384
|
+
result._note = 'Apollo takes up to 4 days. Configure webhook at app.scrapercity.com/dashboard/webhooks instead of polling.'
|
|
385
|
+
break
|
|
386
|
+
case 'scrape_apollo_filters':
|
|
387
|
+
result = await sc.apolloFilters(args)
|
|
388
|
+
result._note = 'Apollo takes up to 4 days. Configure webhook at app.scrapercity.com/dashboard/webhooks instead of polling.'
|
|
389
|
+
break
|
|
390
|
+
case 'scrape_maps':
|
|
391
|
+
result = await sc.maps(args.query, args.location, args.limit || 500)
|
|
392
|
+
break
|
|
393
|
+
case 'validate_emails':
|
|
394
|
+
result = await sc.emailValidate(args.emails)
|
|
395
|
+
break
|
|
396
|
+
case 'find_emails':
|
|
397
|
+
result = await sc.emailFind(args.contacts, {
|
|
398
|
+
validate: args.autoValidateEmails || false,
|
|
399
|
+
mobiles: args.autoFindMobiles || false
|
|
400
|
+
})
|
|
401
|
+
break
|
|
402
|
+
case 'find_mobiles':
|
|
403
|
+
result = await sc.mobileFinder(args.inputs)
|
|
404
|
+
break
|
|
405
|
+
case 'find_people':
|
|
406
|
+
result = await sc.peopleFinder(args)
|
|
407
|
+
break
|
|
408
|
+
case 'scrape_store_leads':
|
|
409
|
+
result = await sc.storeLeads(args)
|
|
410
|
+
break
|
|
411
|
+
case 'scrape_builtwith':
|
|
412
|
+
result = await sc.builtwith(args.technology, args.fileName)
|
|
413
|
+
break
|
|
414
|
+
case 'search_criminal_records':
|
|
415
|
+
result = await sc.criminal(args.name, args.state, args.dob)
|
|
416
|
+
break
|
|
417
|
+
case 'scrape_airbnb':
|
|
418
|
+
result = await sc.airbnb(args)
|
|
419
|
+
break
|
|
420
|
+
case 'scrape_youtube_email':
|
|
421
|
+
result = await sc.youtubeEmail(args.channels)
|
|
422
|
+
break
|
|
423
|
+
case 'scrape_website_finder':
|
|
424
|
+
result = await sc.websiteFinder(args.domains, args.jobTitle)
|
|
425
|
+
break
|
|
426
|
+
case 'scrape_yelp':
|
|
427
|
+
result = await sc.yelp(args)
|
|
428
|
+
break
|
|
429
|
+
case 'scrape_angi':
|
|
430
|
+
result = await sc.angi(args.keyword, args.zipCodes || [], args.maxItems || 100)
|
|
431
|
+
break
|
|
432
|
+
case 'scrape_zillow_agents':
|
|
433
|
+
result = await sc.zillowAgents(args.location, { specialty: args.specialty, language: args.language, searchLimit: args.searchLimit || 10 })
|
|
434
|
+
break
|
|
435
|
+
case 'scrape_bizbuysell':
|
|
436
|
+
result = await sc.bizbuysell(args.startUrls, args.maxItems || 100)
|
|
437
|
+
break
|
|
438
|
+
case 'scrape_crexi':
|
|
439
|
+
result = await sc.crexi(args.startUrls)
|
|
440
|
+
break
|
|
441
|
+
case 'scrape_property_lookup':
|
|
442
|
+
result = await sc.propertyLookup(args.addresses, args.includeOwnerContact || false)
|
|
443
|
+
break
|
|
444
|
+
case 'check_run_status':
|
|
445
|
+
result = await sc.status(args.runId)
|
|
446
|
+
break
|
|
447
|
+
case 'download_results': {
|
|
448
|
+
const dl = await sc.download(args.runId, args.outputPath)
|
|
449
|
+
result = { success: true, path: dl.path, sizeKB: Math.round(dl.bytes / 1024) }
|
|
450
|
+
break
|
|
451
|
+
}
|
|
452
|
+
case 'cancel_run':
|
|
453
|
+
result = await sc.cancel(args.runId)
|
|
454
|
+
break
|
|
455
|
+
case 'query_lead_database': {
|
|
456
|
+
const params = { ...args }
|
|
457
|
+
if (params.hasEmail) { params.hasEmail = 'true'; }
|
|
458
|
+
if (params.hasPhone) { params.hasPhone = 'true'; }
|
|
459
|
+
result = await sc.dbLeads(params)
|
|
460
|
+
break
|
|
461
|
+
}
|
|
462
|
+
default:
|
|
463
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true }
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
|
|
467
|
+
} catch (e) {
|
|
468
|
+
return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true }
|
|
469
|
+
}
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// ── Start ─────────────────────────────────────────────────────
|
|
473
|
+
const transport = new StdioServerTransport()
|
|
474
|
+
await server.connect(transport)
|