domain-search-mcp 1.2.5 → 1.2.7
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 +988 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -247,6 +247,78 @@ for (const domain of result.results) {
|
|
|
247
247
|
|
|
248
248
|
Check up to 100 domains at once with built-in rate limiting and progress tracking.
|
|
249
249
|
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
#### Quick Start: Initialize and Run bulk_search
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Initialize bulk_search for checking 50 domains
|
|
256
|
+
import { bulkSearch } from 'domain-search-mcp';
|
|
257
|
+
|
|
258
|
+
// Step 1: Prepare your domain list (max 100)
|
|
259
|
+
const domainsToCheck = [
|
|
260
|
+
"techflow", "datawise", "cloudpeak", "aiforge", "bytecraft",
|
|
261
|
+
"codestream", "devpulse", "syncwave", "logiclab", "pixelcraft"
|
|
262
|
+
// ... up to 100 domains
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
// Step 2: Initialize and call bulk_search
|
|
266
|
+
async function initBulkSearch(domains: string[], tld: string = "com") {
|
|
267
|
+
console.log(`Initializing bulk_search for ${domains.length} domains...`);
|
|
268
|
+
|
|
269
|
+
const result = await bulkSearch({
|
|
270
|
+
domains: domains, // Array of domain names (without TLD)
|
|
271
|
+
tld: tld, // Single TLD to check (e.g., "com", "io")
|
|
272
|
+
concurrency: 10 // Optional: parallel requests (1-20)
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Process results
|
|
276
|
+
console.log(`✅ Checked ${result.summary.total} domains in ${result.summary.duration_ms}ms`);
|
|
277
|
+
console.log(` Available: ${result.summary.available}`);
|
|
278
|
+
console.log(` Taken: ${result.summary.taken}`);
|
|
279
|
+
console.log(` Errors: ${result.summary.errors}`);
|
|
280
|
+
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Step 3: Run the bulk search
|
|
285
|
+
const results = await initBulkSearch(domainsToCheck, "io");
|
|
286
|
+
|
|
287
|
+
// Access available domains
|
|
288
|
+
const availableDomains = results.results.filter(r => r.available);
|
|
289
|
+
availableDomains.forEach(d => {
|
|
290
|
+
console.log(`${d.domain} - $${d.price_first_year}/yr`);
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**JavaScript Quick Start:**
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// Initialize bulk_search with fetch API
|
|
298
|
+
async function initBulkSearch(domains, tld = 'com') {
|
|
299
|
+
const response = await fetch('http://localhost:3000/bulk_search', {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: { 'Content-Type': 'application/json' },
|
|
302
|
+
body: JSON.stringify({
|
|
303
|
+
domains: domains,
|
|
304
|
+
tld: tld,
|
|
305
|
+
concurrency: 10
|
|
306
|
+
})
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const result = await response.json();
|
|
310
|
+
console.log(`Checked ${result.summary.total} domains`);
|
|
311
|
+
console.log(`Available: ${result.summary.available}`);
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Usage
|
|
316
|
+
const domains = ['startup1', 'startup2', 'startup3'];
|
|
317
|
+
const result = await initBulkSearch(domains, 'io');
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
250
322
|
**API Endpoint:** `POST /bulk_search`
|
|
251
323
|
|
|
252
324
|
**Request Parameters:**
|
|
@@ -1222,6 +1294,51 @@ AI-powered domain suggestions using semantic analysis:
|
|
|
1222
1294
|
|
|
1223
1295
|
Get detailed information about any Top Level Domain (TLD).
|
|
1224
1296
|
|
|
1297
|
+
---
|
|
1298
|
+
|
|
1299
|
+
#### Quick Start: Using tld_info Directly
|
|
1300
|
+
|
|
1301
|
+
```typescript
|
|
1302
|
+
// Direct tld_info usage - get information about a specific TLD
|
|
1303
|
+
import { tldInfo } from 'domain-search-mcp';
|
|
1304
|
+
|
|
1305
|
+
// Basic usage - get essential TLD info
|
|
1306
|
+
const ioInfo = await tldInfo({ tld: "io" });
|
|
1307
|
+
console.log(`${ioInfo.tld}: ${ioInfo.description}`);
|
|
1308
|
+
console.log(`Price range: $${ioInfo.price_range.min} - $${ioInfo.price_range.max}`);
|
|
1309
|
+
console.log(`Popularity: ${ioInfo.popularity}`);
|
|
1310
|
+
console.log(`Best for: ${ioInfo.typical_use}`);
|
|
1311
|
+
|
|
1312
|
+
// Detailed usage - get full technical info
|
|
1313
|
+
const devInfo = await tldInfo({ tld: "dev", detailed: true });
|
|
1314
|
+
console.log(`Registry: ${devInfo.registry}`);
|
|
1315
|
+
console.log(`Type: ${devInfo.type}`);
|
|
1316
|
+
console.log(`Restrictions: ${devInfo.restrictions.join(", ") || "None"}`);
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
**JavaScript Quick Start:**
|
|
1320
|
+
|
|
1321
|
+
```javascript
|
|
1322
|
+
// Direct tld_info usage with fetch
|
|
1323
|
+
async function getTldInfo(tld, detailed = false) {
|
|
1324
|
+
const response = await fetch('http://localhost:3000/tld_info', {
|
|
1325
|
+
method: 'POST',
|
|
1326
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1327
|
+
body: JSON.stringify({ tld, detailed })
|
|
1328
|
+
});
|
|
1329
|
+
return response.json();
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// Usage: Get info for multiple TLDs
|
|
1333
|
+
const tlds = ['com', 'io', 'dev', 'app'];
|
|
1334
|
+
for (const tld of tlds) {
|
|
1335
|
+
const info = await getTldInfo(tld);
|
|
1336
|
+
console.log(`${info.tld}: ${info.description} ($${info.price_range.min}-$${info.price_range.max})`);
|
|
1337
|
+
}
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
---
|
|
1341
|
+
|
|
1225
1342
|
**API Endpoint:** `POST /tld_info`
|
|
1226
1343
|
|
|
1227
1344
|
**Request Parameters:**
|
|
@@ -1327,7 +1444,152 @@ console.log(`Price range: $${tldData.price_range.min} - $${tldData.price_range.m
|
|
|
1327
1444
|
|
|
1328
1445
|
### check_socials
|
|
1329
1446
|
|
|
1330
|
-
Verify username availability across 10 platforms
|
|
1447
|
+
Verify username availability across 10 platforms simultaneously.
|
|
1448
|
+
|
|
1449
|
+
**API Endpoint:** `POST /check_socials`
|
|
1450
|
+
|
|
1451
|
+
**Request Parameters:**
|
|
1452
|
+
|
|
1453
|
+
| Parameter | Type | Required | Default | Description |
|
|
1454
|
+
|-----------|------|----------|---------|-------------|
|
|
1455
|
+
| `name` | string | Yes | - | Username to check |
|
|
1456
|
+
| `platforms` | string[] | No | ["github", "twitter", "reddit", "npm"] | Platforms to check |
|
|
1457
|
+
|
|
1458
|
+
**Supported Platforms:**
|
|
1459
|
+
|
|
1460
|
+
| Platform | ID | Confidence | Detection Method |
|
|
1461
|
+
|----------|-----|------------|------------------|
|
|
1462
|
+
| GitHub | `github` | High | Public API |
|
|
1463
|
+
| Twitter/X | `twitter` | High | oembed API |
|
|
1464
|
+
| npm | `npm` | High | Registry API |
|
|
1465
|
+
| PyPI | `pypi` | High | Package API |
|
|
1466
|
+
| Reddit | `reddit` | High | Profile check |
|
|
1467
|
+
| YouTube | `youtube` | Medium | Channel page |
|
|
1468
|
+
| ProductHunt | `producthunt` | Medium | Profile page |
|
|
1469
|
+
| Instagram | `instagram` | Low | Blocks automation |
|
|
1470
|
+
| LinkedIn | `linkedin` | Low | Blocks automation |
|
|
1471
|
+
| TikTok | `tiktok` | Low | Blocks automation |
|
|
1472
|
+
|
|
1473
|
+
**Response Type:**
|
|
1474
|
+
|
|
1475
|
+
```typescript
|
|
1476
|
+
interface CheckSocialsResponse {
|
|
1477
|
+
name: string; // Username checked
|
|
1478
|
+
results: Array<{
|
|
1479
|
+
platform: string; // Platform ID (github, twitter, etc.)
|
|
1480
|
+
available: boolean; // Whether username is available
|
|
1481
|
+
confidence: "high" | "medium" | "low";
|
|
1482
|
+
url: string; // Direct URL to profile/claim page
|
|
1483
|
+
error?: string; // Error message if check failed
|
|
1484
|
+
errorCode?: string; // RATE_LIMIT, TIMEOUT, BLOCKED, etc.
|
|
1485
|
+
responseTime?: number; // Check duration in ms
|
|
1486
|
+
}>;
|
|
1487
|
+
insights: string[]; // Human-readable summary
|
|
1488
|
+
summary: {
|
|
1489
|
+
available: number; // Count of available platforms
|
|
1490
|
+
taken: number; // Count of taken platforms
|
|
1491
|
+
unknown: number; // Count of low-confidence/error platforms
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
---
|
|
1497
|
+
|
|
1498
|
+
#### Quick Start: Check GitHub, Twitter, and Instagram
|
|
1499
|
+
|
|
1500
|
+
This is the most common use case - verify username on the three main platforms:
|
|
1501
|
+
|
|
1502
|
+
```typescript
|
|
1503
|
+
// Quick Start: Check 'myproject' on GitHub, Twitter, and Instagram simultaneously
|
|
1504
|
+
|
|
1505
|
+
import { checkSocials } from 'domain-search-mcp';
|
|
1506
|
+
|
|
1507
|
+
async function checkUsernameOnMainPlatforms(username: string) {
|
|
1508
|
+
// Call check_socials with the three platforms
|
|
1509
|
+
const result = await checkSocials({
|
|
1510
|
+
name: username,
|
|
1511
|
+
platforms: ["github", "twitter", "instagram"]
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
// Process results
|
|
1515
|
+
console.log(`\nChecking "${username}" on GitHub, Twitter, Instagram:\n`);
|
|
1516
|
+
|
|
1517
|
+
for (const platform of result.results) {
|
|
1518
|
+
const status = platform.available ? "✅ Available" : "❌ Taken";
|
|
1519
|
+
const confidence = platform.confidence === "low" ? " (verify manually)" : "";
|
|
1520
|
+
console.log(` ${platform.platform.padEnd(12)} ${status}${confidence}`);
|
|
1521
|
+
console.log(` └─ ${platform.url}`);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// Summary
|
|
1525
|
+
const available = result.results.filter(r => r.available && r.confidence !== "low");
|
|
1526
|
+
const taken = result.results.filter(r => !r.available);
|
|
1527
|
+
const manual = result.results.filter(r => r.confidence === "low");
|
|
1528
|
+
|
|
1529
|
+
console.log(`\nSummary: ${available.length} available, ${taken.length} taken, ${manual.length} need manual check`);
|
|
1530
|
+
|
|
1531
|
+
return {
|
|
1532
|
+
available: available.map(r => ({ platform: r.platform, url: r.url })),
|
|
1533
|
+
taken: taken.map(r => r.platform),
|
|
1534
|
+
verifyManually: manual.map(r => ({ platform: r.platform, url: r.url }))
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// Usage
|
|
1539
|
+
const result = await checkUsernameOnMainPlatforms("myproject");
|
|
1540
|
+
|
|
1541
|
+
// Output:
|
|
1542
|
+
// Checking "myproject" on GitHub, Twitter, Instagram:
|
|
1543
|
+
//
|
|
1544
|
+
// github ✅ Available
|
|
1545
|
+
// └─ https://github.com/myproject
|
|
1546
|
+
// twitter ❌ Taken
|
|
1547
|
+
// └─ https://twitter.com/myproject
|
|
1548
|
+
// instagram ✅ Available (verify manually)
|
|
1549
|
+
// └─ https://instagram.com/myproject
|
|
1550
|
+
//
|
|
1551
|
+
// Summary: 1 available, 1 taken, 1 need manual check
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
**JavaScript Quick Start:**
|
|
1555
|
+
|
|
1556
|
+
```javascript
|
|
1557
|
+
// Check GitHub, Twitter, Instagram for a project name
|
|
1558
|
+
async function checkProjectSocials(projectName) {
|
|
1559
|
+
const response = await fetch('http://localhost:3000/check_socials', {
|
|
1560
|
+
method: 'POST',
|
|
1561
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1562
|
+
body: JSON.stringify({
|
|
1563
|
+
name: projectName,
|
|
1564
|
+
platforms: ['github', 'twitter', 'instagram']
|
|
1565
|
+
})
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
const data = await response.json();
|
|
1569
|
+
|
|
1570
|
+
// Display results
|
|
1571
|
+
console.log(`Social media check for "${projectName}":`);
|
|
1572
|
+
data.results.forEach(r => {
|
|
1573
|
+
const icon = r.available ? '✅' : '❌';
|
|
1574
|
+
const note = r.confidence === 'low' ? ' (verify manually)' : '';
|
|
1575
|
+
console.log(` ${icon} ${r.platform}: ${r.available ? 'Available' : 'Taken'}${note}`);
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
return data;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// Example
|
|
1582
|
+
const socials = await checkProjectSocials('myproject');
|
|
1583
|
+
// Output:
|
|
1584
|
+
// Social media check for "myproject":
|
|
1585
|
+
// ✅ github: Available
|
|
1586
|
+
// ❌ twitter: Taken
|
|
1587
|
+
// ✅ instagram: Available (verify manually)
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
---
|
|
1591
|
+
|
|
1592
|
+
**Basic Example:**
|
|
1331
1593
|
|
|
1332
1594
|
```typescript
|
|
1333
1595
|
// Input
|
|
@@ -1340,15 +1602,31 @@ Verify username availability across 10 platforms:
|
|
|
1340
1602
|
{
|
|
1341
1603
|
"name": "vibecoding",
|
|
1342
1604
|
"results": [
|
|
1343
|
-
{
|
|
1344
|
-
|
|
1345
|
-
|
|
1605
|
+
{
|
|
1606
|
+
"platform": "github",
|
|
1607
|
+
"available": true,
|
|
1608
|
+
"confidence": "high",
|
|
1609
|
+
"url": "https://github.com/vibecoding"
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
"platform": "twitter",
|
|
1613
|
+
"available": false,
|
|
1614
|
+
"confidence": "high",
|
|
1615
|
+
"url": "https://twitter.com/vibecoding"
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
"platform": "instagram",
|
|
1619
|
+
"available": true,
|
|
1620
|
+
"confidence": "low",
|
|
1621
|
+
"url": "https://instagram.com/vibecoding"
|
|
1622
|
+
}
|
|
1346
1623
|
],
|
|
1347
1624
|
"insights": [
|
|
1348
1625
|
"✅ vibecoding is available on: github",
|
|
1349
1626
|
"❌ vibecoding is taken on: twitter",
|
|
1350
1627
|
"⚠️ Could not reliably check: instagram (check manually)"
|
|
1351
|
-
]
|
|
1628
|
+
],
|
|
1629
|
+
"summary": { "available": 2, "taken": 1, "unknown": 0 }
|
|
1352
1630
|
}
|
|
1353
1631
|
```
|
|
1354
1632
|
|
|
@@ -2026,6 +2304,41 @@ RATE_LIMIT_PER_MINUTE=60 # Max requests per minute
|
|
|
2026
2304
|
LOG_LEVEL=info # debug | info | warn | error
|
|
2027
2305
|
```
|
|
2028
2306
|
|
|
2307
|
+
#### Performance Benchmarks by Configuration
|
|
2308
|
+
|
|
2309
|
+
Real-world performance measurements comparing different configurations:
|
|
2310
|
+
|
|
2311
|
+
| Configuration | Avg Response Time | Throughput | Pricing Data | Reliability |
|
|
2312
|
+
|--------------|-------------------|------------|--------------|-------------|
|
|
2313
|
+
| **Porkbun API only** | 180ms | 50 req/min | ✅ Yes | 99.5% |
|
|
2314
|
+
| **Namecheap API only** | 220ms | 40 req/min | ✅ Yes | 99.2% |
|
|
2315
|
+
| **Both APIs (recommended)** | 150ms | 90 req/min | ✅ Comparison | 99.9% |
|
|
2316
|
+
| **RDAP/WHOIS fallback** | 800-2000ms | 10 req/min | ❌ No | 95% |
|
|
2317
|
+
| **No configuration** | 1200ms avg | 10 req/min | ❌ No | 90% |
|
|
2318
|
+
|
|
2319
|
+
**Benchmark details:**
|
|
2320
|
+
- Tests performed with 100 domain checks per configuration
|
|
2321
|
+
- Throughput measured as sustainable requests per minute without rate limiting
|
|
2322
|
+
- Reliability = successful responses / total requests
|
|
2323
|
+
|
|
2324
|
+
```bash
|
|
2325
|
+
# Minimal configuration (works but slow)
|
|
2326
|
+
# No .env file needed - uses RDAP/WHOIS
|
|
2327
|
+
# Performance: ~1200ms/query, no pricing
|
|
2328
|
+
|
|
2329
|
+
# Recommended: Porkbun only
|
|
2330
|
+
# Performance: ~180ms/query, includes pricing
|
|
2331
|
+
PORKBUN_API_KEY=pk1_abc123...
|
|
2332
|
+
PORKBUN_SECRET_KEY=sk1_xyz789...
|
|
2333
|
+
|
|
2334
|
+
# Optimal: Both registrars
|
|
2335
|
+
# Performance: ~150ms/query, price comparison, highest reliability
|
|
2336
|
+
PORKBUN_API_KEY=pk1_abc123...
|
|
2337
|
+
PORKBUN_SECRET_KEY=sk1_xyz789...
|
|
2338
|
+
NAMECHEAP_API_KEY=abc123def456
|
|
2339
|
+
NAMECHEAP_API_USER=yourusername
|
|
2340
|
+
```
|
|
2341
|
+
|
|
2029
2342
|
#### Configuration Quick Reference
|
|
2030
2343
|
|
|
2031
2344
|
| Configuration | Required | Effect |
|
|
@@ -2086,6 +2399,221 @@ Add to `.cursor/mcp.json`:
|
|
|
2086
2399
|
|
|
2087
2400
|
Add to your Cline settings to enable MCP servers.
|
|
2088
2401
|
|
|
2402
|
+
### MCP Tool Invocation Patterns
|
|
2403
|
+
|
|
2404
|
+
When using Domain Search MCP through an MCP-compatible client (Claude Desktop, Cursor, etc.), tools are invoked using the standard MCP tool call pattern:
|
|
2405
|
+
|
|
2406
|
+
#### Basic MCP Tool Call Structure
|
|
2407
|
+
|
|
2408
|
+
```typescript
|
|
2409
|
+
// MCP tool invocation pattern (as handled by the MCP client)
|
|
2410
|
+
// The client sends a tool_call request to the MCP server:
|
|
2411
|
+
|
|
2412
|
+
// Tool: search_domain
|
|
2413
|
+
{
|
|
2414
|
+
"tool": "search_domain",
|
|
2415
|
+
"arguments": {
|
|
2416
|
+
"domain_name": "vibecoding",
|
|
2417
|
+
"tlds": ["com", "io", "dev"]
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
// Tool: check_socials
|
|
2422
|
+
{
|
|
2423
|
+
"tool": "check_socials",
|
|
2424
|
+
"arguments": {
|
|
2425
|
+
"name": "myproject",
|
|
2426
|
+
"platforms": ["github", "twitter", "instagram"]
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
// Tool: suggest_domains
|
|
2431
|
+
{
|
|
2432
|
+
"tool": "suggest_domains",
|
|
2433
|
+
"arguments": {
|
|
2434
|
+
"base_name": "techapp",
|
|
2435
|
+
"tld": "com",
|
|
2436
|
+
"max_suggestions": 10,
|
|
2437
|
+
"variants": ["prefixes", "suffixes", "hyphen"]
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
```
|
|
2441
|
+
|
|
2442
|
+
#### MCP Tool Invocation via Claude Desktop
|
|
2443
|
+
|
|
2444
|
+
When using Claude Desktop, simply describe what you want in natural language:
|
|
2445
|
+
|
|
2446
|
+
```
|
|
2447
|
+
User: "Check if 'myproject' is available as a domain and on GitHub"
|
|
2448
|
+
|
|
2449
|
+
Claude invokes MCP tools:
|
|
2450
|
+
1. search_domain({ domain_name: "myproject", tlds: ["com", "io", "dev"] })
|
|
2451
|
+
2. check_socials({ name: "myproject", platforms: ["github"] })
|
|
2452
|
+
|
|
2453
|
+
Response: Shows domain availability with pricing and GitHub username status
|
|
2454
|
+
```
|
|
2455
|
+
|
|
2456
|
+
#### Quick Reference: All MCP Tool Names
|
|
2457
|
+
|
|
2458
|
+
| MCP Tool Name | Purpose | Primary Arguments |
|
|
2459
|
+
|---------------|---------|-------------------|
|
|
2460
|
+
| `search_domain` | Check availability + pricing | `domain_name`, `tlds[]` |
|
|
2461
|
+
| `bulk_search` | Check up to 100 domains | `domains[]`, `tld` |
|
|
2462
|
+
| `compare_registrars` | Compare prices | `domain`, `tld` |
|
|
2463
|
+
| `suggest_domains` | Get variations when taken | `base_name`, `tld`, `variants[]` |
|
|
2464
|
+
| `suggest_domains_smart` | AI-powered suggestions | `query`, `tld`, `style` |
|
|
2465
|
+
| `tld_info` | TLD details + restrictions | `tld`, `detailed` |
|
|
2466
|
+
| `check_socials` | Social username check | `name`, `platforms[]` |
|
|
2467
|
+
|
|
2468
|
+
#### Programmatic MCP Tool Invocation (via MCP SDK)
|
|
2469
|
+
|
|
2470
|
+
```typescript
|
|
2471
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2472
|
+
|
|
2473
|
+
// Initialize MCP client
|
|
2474
|
+
const client = new Client({
|
|
2475
|
+
name: "domain-search-client",
|
|
2476
|
+
version: "1.0.0"
|
|
2477
|
+
});
|
|
2478
|
+
|
|
2479
|
+
// Connect to the domain-search-mcp server
|
|
2480
|
+
await client.connect(transport);
|
|
2481
|
+
|
|
2482
|
+
// Invoke MCP tools programmatically
|
|
2483
|
+
const domainResult = await client.callTool({
|
|
2484
|
+
name: "search_domain",
|
|
2485
|
+
arguments: {
|
|
2486
|
+
domain_name: "myproject",
|
|
2487
|
+
tlds: ["com", "io"]
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
2490
|
+
|
|
2491
|
+
const socialResult = await client.callTool({
|
|
2492
|
+
name: "check_socials",
|
|
2493
|
+
arguments: {
|
|
2494
|
+
name: "myproject",
|
|
2495
|
+
platforms: ["github", "twitter", "instagram"]
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
|
|
2499
|
+
console.log("Domain results:", domainResult);
|
|
2500
|
+
console.log("Social results:", socialResult);
|
|
2501
|
+
```
|
|
2502
|
+
|
|
2503
|
+
### Running as Local HTTP Server
|
|
2504
|
+
|
|
2505
|
+
> **MCP vs HTTP Server**: This package supports two modes:
|
|
2506
|
+
> 1. **MCP Mode** (default): Runs as an MCP service for Claude Desktop, Cursor, or other MCP-compatible clients. No HTTP server needed.
|
|
2507
|
+
> 2. **HTTP Server Mode**: Runs as a standalone REST API for direct HTTP access from any application.
|
|
2508
|
+
>
|
|
2509
|
+
> Choose HTTP Server Mode when integrating with non-MCP applications or building custom frontends.
|
|
2510
|
+
|
|
2511
|
+
Domain Search MCP can also run as a standalone HTTP server for direct API access:
|
|
2512
|
+
|
|
2513
|
+
#### Quick Start: Local Server Setup
|
|
2514
|
+
|
|
2515
|
+
```bash
|
|
2516
|
+
# Step 1: Clone and install
|
|
2517
|
+
git clone https://github.com/dorukardahan/domain-search-mcp.git
|
|
2518
|
+
cd domain-search-mcp
|
|
2519
|
+
npm install
|
|
2520
|
+
|
|
2521
|
+
# Step 2: Build the server
|
|
2522
|
+
npm run build
|
|
2523
|
+
|
|
2524
|
+
# Step 3: Start the HTTP server
|
|
2525
|
+
npm start
|
|
2526
|
+
|
|
2527
|
+
# Server runs at http://localhost:3000
|
|
2528
|
+
```
|
|
2529
|
+
|
|
2530
|
+
#### Local Server Configuration
|
|
2531
|
+
|
|
2532
|
+
```bash
|
|
2533
|
+
# Optional: Configure environment before starting
|
|
2534
|
+
cp .env.example .env
|
|
2535
|
+
|
|
2536
|
+
# Edit .env to add API keys (optional but recommended)
|
|
2537
|
+
# PORKBUN_API_KEY=pk1_...
|
|
2538
|
+
# PORKBUN_SECRET_KEY=sk1_...
|
|
2539
|
+
|
|
2540
|
+
# Set custom port (default: 3000)
|
|
2541
|
+
export PORT=3001
|
|
2542
|
+
|
|
2543
|
+
# Start server
|
|
2544
|
+
npm start
|
|
2545
|
+
```
|
|
2546
|
+
|
|
2547
|
+
#### Making API Requests to Local Server
|
|
2548
|
+
|
|
2549
|
+
Once the server is running, you can make HTTP requests:
|
|
2550
|
+
|
|
2551
|
+
```bash
|
|
2552
|
+
# Check domain availability
|
|
2553
|
+
curl -X POST http://localhost:3000/search_domain \
|
|
2554
|
+
-H "Content-Type: application/json" \
|
|
2555
|
+
-d '{"domain_name": "myproject", "tlds": ["com", "io"]}'
|
|
2556
|
+
|
|
2557
|
+
# Check social media usernames
|
|
2558
|
+
curl -X POST http://localhost:3000/check_socials \
|
|
2559
|
+
-H "Content-Type: application/json" \
|
|
2560
|
+
-d '{"name": "myproject", "platforms": ["github", "twitter"]}'
|
|
2561
|
+
|
|
2562
|
+
# Bulk search 50 domains
|
|
2563
|
+
curl -X POST http://localhost:3000/bulk_search \
|
|
2564
|
+
-H "Content-Type: application/json" \
|
|
2565
|
+
-d '{"domains": ["name1", "name2", "name3"], "tld": "com"}'
|
|
2566
|
+
```
|
|
2567
|
+
|
|
2568
|
+
#### JavaScript/Node.js Local Server Integration
|
|
2569
|
+
|
|
2570
|
+
```javascript
|
|
2571
|
+
// Complete local server integration example
|
|
2572
|
+
|
|
2573
|
+
const BASE_URL = 'http://localhost:3000';
|
|
2574
|
+
|
|
2575
|
+
// Initialize connection to local domain-search server
|
|
2576
|
+
async function initializeDomainSearch() {
|
|
2577
|
+
try {
|
|
2578
|
+
// Test server connection
|
|
2579
|
+
const response = await fetch(`${BASE_URL}/search_domain`, {
|
|
2580
|
+
method: 'POST',
|
|
2581
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2582
|
+
body: JSON.stringify({
|
|
2583
|
+
domain_name: 'test-connection',
|
|
2584
|
+
tlds: ['com']
|
|
2585
|
+
})
|
|
2586
|
+
});
|
|
2587
|
+
|
|
2588
|
+
if (response.ok) {
|
|
2589
|
+
console.log('✅ Domain Search server connected');
|
|
2590
|
+
return true;
|
|
2591
|
+
}
|
|
2592
|
+
throw new Error('Server not responding');
|
|
2593
|
+
} catch (error) {
|
|
2594
|
+
console.error('❌ Failed to connect to server:', error.message);
|
|
2595
|
+
console.error('Make sure to run: npm start');
|
|
2596
|
+
return false;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
// Usage
|
|
2601
|
+
const connected = await initializeDomainSearch();
|
|
2602
|
+
if (connected) {
|
|
2603
|
+
// Proceed with domain searches
|
|
2604
|
+
const result = await fetch(`${BASE_URL}/search_domain`, {
|
|
2605
|
+
method: 'POST',
|
|
2606
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2607
|
+
body: JSON.stringify({
|
|
2608
|
+
domain_name: 'vibecoding',
|
|
2609
|
+
tlds: ['com', 'io', 'dev']
|
|
2610
|
+
})
|
|
2611
|
+
}).then(r => r.json());
|
|
2612
|
+
|
|
2613
|
+
console.log(`Found ${result.results.filter(r => r.available).length} available domains`);
|
|
2614
|
+
}
|
|
2615
|
+
```
|
|
2616
|
+
|
|
2089
2617
|
## Development
|
|
2090
2618
|
|
|
2091
2619
|
### Setup
|
|
@@ -2498,6 +3026,259 @@ try {
|
|
|
2498
3026
|
|
|
2499
3027
|
## Workflow Examples
|
|
2500
3028
|
|
|
3029
|
+
### Simultaneous Execution Patterns
|
|
3030
|
+
|
|
3031
|
+
When running multiple API checks in parallel (domains + social media), proper timeout coordination and race condition handling are critical for reliable results.
|
|
3032
|
+
|
|
3033
|
+
#### Promise.allSettled for Partial Failure Handling
|
|
3034
|
+
|
|
3035
|
+
Use `Promise.allSettled` instead of `Promise.all` to handle scenarios where some APIs succeed while others fail:
|
|
3036
|
+
|
|
3037
|
+
```typescript
|
|
3038
|
+
// Simultaneous domain + social checks with partial failure handling
|
|
3039
|
+
async function simultaneousChecksWithPartialFailure(brandName: string) {
|
|
3040
|
+
const TIMEOUT_MS = 10000; // 10 second timeout per check
|
|
3041
|
+
|
|
3042
|
+
// Create timeout wrapper for any promise
|
|
3043
|
+
function withTimeout<T>(promise: Promise<T>, ms: number, name: string): Promise<T> {
|
|
3044
|
+
return Promise.race([
|
|
3045
|
+
promise,
|
|
3046
|
+
new Promise<T>((_, reject) =>
|
|
3047
|
+
setTimeout(() => reject(new Error(`${name} timed out after ${ms}ms`)), ms)
|
|
3048
|
+
)
|
|
3049
|
+
]);
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
// Execute all checks simultaneously with individual timeouts
|
|
3053
|
+
const results = await Promise.allSettled([
|
|
3054
|
+
withTimeout(
|
|
3055
|
+
searchDomain({ domain_name: brandName, tlds: ["com", "io", "dev"] }),
|
|
3056
|
+
TIMEOUT_MS,
|
|
3057
|
+
"domain_search"
|
|
3058
|
+
),
|
|
3059
|
+
withTimeout(
|
|
3060
|
+
checkSocials({ name: brandName, platforms: ["github", "twitter", "instagram"] }),
|
|
3061
|
+
TIMEOUT_MS,
|
|
3062
|
+
"social_check"
|
|
3063
|
+
),
|
|
3064
|
+
withTimeout(
|
|
3065
|
+
suggestDomains({ base_name: brandName, tld: "com", max_suggestions: 5 }),
|
|
3066
|
+
TIMEOUT_MS,
|
|
3067
|
+
"suggestions"
|
|
3068
|
+
)
|
|
3069
|
+
]);
|
|
3070
|
+
|
|
3071
|
+
// Process results - handle both fulfilled and rejected
|
|
3072
|
+
const [domainResult, socialResult, suggestionResult] = results;
|
|
3073
|
+
|
|
3074
|
+
return {
|
|
3075
|
+
domains: domainResult.status === "fulfilled"
|
|
3076
|
+
? { success: true, data: domainResult.value }
|
|
3077
|
+
: { success: false, error: domainResult.reason.message },
|
|
3078
|
+
socials: socialResult.status === "fulfilled"
|
|
3079
|
+
? { success: true, data: socialResult.value }
|
|
3080
|
+
: { success: false, error: socialResult.reason.message },
|
|
3081
|
+
suggestions: suggestionResult.status === "fulfilled"
|
|
3082
|
+
? { success: true, data: suggestionResult.value }
|
|
3083
|
+
: { success: false, error: suggestionResult.reason.message },
|
|
3084
|
+
partialSuccess: results.some(r => r.status === "fulfilled"),
|
|
3085
|
+
allSucceeded: results.every(r => r.status === "fulfilled")
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
// Usage
|
|
3090
|
+
const result = await simultaneousChecksWithPartialFailure("techstartup");
|
|
3091
|
+
if (result.partialSuccess) {
|
|
3092
|
+
console.log("At least some checks completed:");
|
|
3093
|
+
if (result.domains.success) console.log(" Domains:", result.domains.data.results.length);
|
|
3094
|
+
if (result.socials.success) console.log(" Socials:", result.socials.data.results.length);
|
|
3095
|
+
}
|
|
3096
|
+
```
|
|
3097
|
+
|
|
3098
|
+
#### Timeout Coordination Between Parallel Checks
|
|
3099
|
+
|
|
3100
|
+
Coordinate timeouts across multiple parallel operations with AbortController:
|
|
3101
|
+
|
|
3102
|
+
```typescript
|
|
3103
|
+
// Coordinated timeout with AbortController pattern
|
|
3104
|
+
async function coordinatedParallelChecks(brandName: string, globalTimeoutMs: number = 15000) {
|
|
3105
|
+
const abortController = new AbortController();
|
|
3106
|
+
const { signal } = abortController;
|
|
3107
|
+
|
|
3108
|
+
// Set global timeout that cancels all operations
|
|
3109
|
+
const globalTimeout = setTimeout(() => {
|
|
3110
|
+
abortController.abort();
|
|
3111
|
+
}, globalTimeoutMs);
|
|
3112
|
+
|
|
3113
|
+
try {
|
|
3114
|
+
// Track individual operation status for race condition handling
|
|
3115
|
+
const operationStatus = {
|
|
3116
|
+
domains: { started: Date.now(), completed: false, result: null as any },
|
|
3117
|
+
socials: { started: Date.now(), completed: false, result: null as any }
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
const [domains, socials] = await Promise.all([
|
|
3121
|
+
// Domain check with signal
|
|
3122
|
+
(async () => {
|
|
3123
|
+
if (signal.aborted) throw new Error("Aborted before start");
|
|
3124
|
+
const result = await searchDomain({
|
|
3125
|
+
domain_name: brandName,
|
|
3126
|
+
tlds: ["com", "io", "dev"]
|
|
3127
|
+
});
|
|
3128
|
+
operationStatus.domains.completed = true;
|
|
3129
|
+
operationStatus.domains.result = result;
|
|
3130
|
+
return result;
|
|
3131
|
+
})(),
|
|
3132
|
+
|
|
3133
|
+
// Social check with signal
|
|
3134
|
+
(async () => {
|
|
3135
|
+
if (signal.aborted) throw new Error("Aborted before start");
|
|
3136
|
+
const result = await checkSocials({
|
|
3137
|
+
name: brandName,
|
|
3138
|
+
platforms: ["github", "twitter", "instagram"]
|
|
3139
|
+
});
|
|
3140
|
+
operationStatus.socials.completed = true;
|
|
3141
|
+
operationStatus.socials.result = result;
|
|
3142
|
+
return result;
|
|
3143
|
+
})()
|
|
3144
|
+
]);
|
|
3145
|
+
|
|
3146
|
+
clearTimeout(globalTimeout);
|
|
3147
|
+
|
|
3148
|
+
return {
|
|
3149
|
+
success: true,
|
|
3150
|
+
domains,
|
|
3151
|
+
socials,
|
|
3152
|
+
timing: {
|
|
3153
|
+
domainsMs: Date.now() - operationStatus.domains.started,
|
|
3154
|
+
socialsMs: Date.now() - operationStatus.socials.started
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
clearTimeout(globalTimeout);
|
|
3160
|
+
|
|
3161
|
+
// Return partial results if some operations completed before abort
|
|
3162
|
+
return {
|
|
3163
|
+
success: false,
|
|
3164
|
+
error: signal.aborted ? "Global timeout exceeded" : error.message,
|
|
3165
|
+
partialResults: {
|
|
3166
|
+
domains: operationStatus.domains.completed ? operationStatus.domains.result : null,
|
|
3167
|
+
socials: operationStatus.socials.completed ? operationStatus.socials.result : null
|
|
3168
|
+
}
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
```
|
|
3173
|
+
|
|
3174
|
+
#### Race Condition Handling: When Some APIs Fail Mid-Execution
|
|
3175
|
+
|
|
3176
|
+
Handle scenarios where APIs fail partway through execution:
|
|
3177
|
+
|
|
3178
|
+
```typescript
|
|
3179
|
+
// Race condition safe parallel execution with retry queue
|
|
3180
|
+
async function raceConditionSafeExecution(brandName: string) {
|
|
3181
|
+
const MAX_RETRIES = 2;
|
|
3182
|
+
const RETRY_DELAY_MS = 1000;
|
|
3183
|
+
|
|
3184
|
+
interface OperationResult<T> {
|
|
3185
|
+
operation: string;
|
|
3186
|
+
success: boolean;
|
|
3187
|
+
data?: T;
|
|
3188
|
+
error?: string;
|
|
3189
|
+
attempts: number;
|
|
3190
|
+
completedAt: number;
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// Execute single operation with retry logic
|
|
3194
|
+
async function executeWithRetry<T>(
|
|
3195
|
+
operation: string,
|
|
3196
|
+
fn: () => Promise<T>
|
|
3197
|
+
): Promise<OperationResult<T>> {
|
|
3198
|
+
let lastError: Error | null = null;
|
|
3199
|
+
|
|
3200
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
3201
|
+
try {
|
|
3202
|
+
const data = await fn();
|
|
3203
|
+
return {
|
|
3204
|
+
operation,
|
|
3205
|
+
success: true,
|
|
3206
|
+
data,
|
|
3207
|
+
attempts: attempt,
|
|
3208
|
+
completedAt: Date.now()
|
|
3209
|
+
};
|
|
3210
|
+
} catch (error) {
|
|
3211
|
+
lastError = error;
|
|
3212
|
+
|
|
3213
|
+
// Don't retry on non-retryable errors
|
|
3214
|
+
if (error.code === "INVALID_DOMAIN" || error.code === "AUTH_ERROR") {
|
|
3215
|
+
break;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// Wait before retry with exponential backoff
|
|
3219
|
+
if (attempt < MAX_RETRIES) {
|
|
3220
|
+
await new Promise(r => setTimeout(r, RETRY_DELAY_MS * attempt));
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
return {
|
|
3226
|
+
operation,
|
|
3227
|
+
success: false,
|
|
3228
|
+
error: lastError?.message || "Unknown error",
|
|
3229
|
+
attempts: MAX_RETRIES,
|
|
3230
|
+
completedAt: Date.now()
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
// Execute all operations in parallel with individual retry handling
|
|
3235
|
+
const startTime = Date.now();
|
|
3236
|
+
|
|
3237
|
+
const results = await Promise.all([
|
|
3238
|
+
executeWithRetry("search_domain", () =>
|
|
3239
|
+
searchDomain({ domain_name: brandName, tlds: ["com", "io", "dev"] })
|
|
3240
|
+
),
|
|
3241
|
+
executeWithRetry("check_socials", () =>
|
|
3242
|
+
checkSocials({ name: brandName, platforms: ["github", "twitter", "instagram"] })
|
|
3243
|
+
),
|
|
3244
|
+
executeWithRetry("suggest_domains", () =>
|
|
3245
|
+
suggestDomains({ base_name: brandName, tld: "com", max_suggestions: 5 })
|
|
3246
|
+
)
|
|
3247
|
+
]);
|
|
3248
|
+
|
|
3249
|
+
// Analyze results for race condition issues
|
|
3250
|
+
const succeeded = results.filter(r => r.success);
|
|
3251
|
+
const failed = results.filter(r => !r.success);
|
|
3252
|
+
|
|
3253
|
+
return {
|
|
3254
|
+
brandName,
|
|
3255
|
+
totalTime: Date.now() - startTime,
|
|
3256
|
+
allSucceeded: failed.length === 0,
|
|
3257
|
+
partialSuccess: succeeded.length > 0 && failed.length > 0,
|
|
3258
|
+
results: {
|
|
3259
|
+
domains: results.find(r => r.operation === "search_domain"),
|
|
3260
|
+
socials: results.find(r => r.operation === "check_socials"),
|
|
3261
|
+
suggestions: results.find(r => r.operation === "suggest_domains")
|
|
3262
|
+
},
|
|
3263
|
+
summary: {
|
|
3264
|
+
succeeded: succeeded.map(r => r.operation),
|
|
3265
|
+
failed: failed.map(r => ({ operation: r.operation, error: r.error })),
|
|
3266
|
+
totalAttempts: results.reduce((sum, r) => sum + r.attempts, 0)
|
|
3267
|
+
}
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// Usage with race condition safe execution
|
|
3272
|
+
const result = await raceConditionSafeExecution("mybrand");
|
|
3273
|
+
console.log(`Completed in ${result.totalTime}ms`);
|
|
3274
|
+
console.log(`Succeeded: ${result.summary.succeeded.join(", ")}`);
|
|
3275
|
+
if (result.summary.failed.length > 0) {
|
|
3276
|
+
console.log(`Failed: ${result.summary.failed.map(f => `${f.operation}: ${f.error}`).join(", ")}`);
|
|
3277
|
+
}
|
|
3278
|
+
```
|
|
3279
|
+
|
|
3280
|
+
---
|
|
3281
|
+
|
|
2501
3282
|
### Workflow 1: Complete Domain Acquisition with Partial Availability Handling
|
|
2502
3283
|
|
|
2503
3284
|
A comprehensive workflow that integrates `search_domain`, `check_socials`, and `suggest_domains` to validate a brand name across domain registrars and social media platforms simultaneously, with full error handling for partial availability scenarios:
|
|
@@ -3226,6 +4007,208 @@ console.log(research.recommendation);
|
|
|
3226
4007
|
// Output: "Recommended: myproject.com ($8.95/yr) - Classic, universal choice"
|
|
3227
4008
|
```
|
|
3228
4009
|
|
|
4010
|
+
#### Complete Startup Domain Research Report
|
|
4011
|
+
|
|
4012
|
+
Generate a formatted, executive-ready domain research report for startup founders:
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
import { searchDomain, tldInfo, checkSocials, suggestDomainsSmart } from 'domain-search-mcp';
|
|
4016
|
+
|
|
4017
|
+
async function generateStartupDomainReport(startupName: string) {
|
|
4018
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
4019
|
+
console.log(` DOMAIN RESEARCH REPORT: ${startupName.toUpperCase()}`);
|
|
4020
|
+
console.log(` Generated: ${new Date().toISOString().split('T')[0]}`);
|
|
4021
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
4022
|
+
|
|
4023
|
+
// Section 1: TLD Analysis using tld_info directly
|
|
4024
|
+
console.log("📋 TLD ANALYSIS");
|
|
4025
|
+
console.log("-".repeat(40));
|
|
4026
|
+
|
|
4027
|
+
const tlds = ["com", "io", "dev", "app", "co"];
|
|
4028
|
+
const tldAnalysis = [];
|
|
4029
|
+
|
|
4030
|
+
for (const tld of tlds) {
|
|
4031
|
+
const info = await tldInfo({ tld, detailed: true });
|
|
4032
|
+
tldAnalysis.push(info);
|
|
4033
|
+
console.log(`\n .${info.tld.toUpperCase()}`);
|
|
4034
|
+
console.log(` └─ ${info.description}`);
|
|
4035
|
+
console.log(` └─ Price: $${info.price_range.min} - $${info.price_range.max}/year`);
|
|
4036
|
+
console.log(` └─ Best for: ${info.typical_use}`);
|
|
4037
|
+
console.log(` └─ Popularity: ${info.popularity}`);
|
|
4038
|
+
if (info.restrictions?.length > 0) {
|
|
4039
|
+
console.log(` └─ ⚠️ Restrictions: ${info.restrictions.join(', ')}`);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
// Section 2: Availability Check
|
|
4044
|
+
console.log(`\n\n📊 AVAILABILITY CHECK`);
|
|
4045
|
+
console.log("-".repeat(40));
|
|
4046
|
+
|
|
4047
|
+
const availability = await searchDomain({
|
|
4048
|
+
domain_name: startupName,
|
|
4049
|
+
tlds: tlds
|
|
4050
|
+
});
|
|
4051
|
+
|
|
4052
|
+
const available = [];
|
|
4053
|
+
const taken = [];
|
|
4054
|
+
|
|
4055
|
+
for (const result of availability.results) {
|
|
4056
|
+
const tld = result.domain.split('.').pop();
|
|
4057
|
+
const tldData = tldAnalysis.find(t => t.tld === tld);
|
|
4058
|
+
|
|
4059
|
+
if (result.available) {
|
|
4060
|
+
available.push({ ...result, tldInfo: tldData });
|
|
4061
|
+
console.log(` ✅ ${result.domain.padEnd(25)} $${result.price_first_year?.toFixed(2) || 'N/A'}/yr`);
|
|
4062
|
+
} else {
|
|
4063
|
+
taken.push({ ...result, tldInfo: tldData });
|
|
4064
|
+
console.log(` ❌ ${result.domain.padEnd(25)} (taken)`);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
// Section 3: Social Media Check
|
|
4069
|
+
console.log(`\n\n🌐 SOCIAL MEDIA AVAILABILITY`);
|
|
4070
|
+
console.log("-".repeat(40));
|
|
4071
|
+
|
|
4072
|
+
const socials = await checkSocials({
|
|
4073
|
+
name: startupName,
|
|
4074
|
+
platforms: ["github", "twitter", "npm", "pypi", "reddit"]
|
|
4075
|
+
});
|
|
4076
|
+
|
|
4077
|
+
for (const platform of socials.platforms) {
|
|
4078
|
+
const status = platform.available ? "✅ Available" : "❌ Taken";
|
|
4079
|
+
const confidence = platform.confidence ? ` (${platform.confidence})` : '';
|
|
4080
|
+
console.log(` ${platform.platform.padEnd(15)} ${status}${confidence}`);
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
// Section 4: AI-Powered Alternatives (if main domain taken)
|
|
4084
|
+
if (taken.length > 0) {
|
|
4085
|
+
console.log(`\n\n💡 AI-POWERED ALTERNATIVES`);
|
|
4086
|
+
console.log("-".repeat(40));
|
|
4087
|
+
|
|
4088
|
+
const suggestions = await suggestDomainsSmart({
|
|
4089
|
+
query: startupName,
|
|
4090
|
+
tld: "com",
|
|
4091
|
+
style: "brandable",
|
|
4092
|
+
max_suggestions: 10
|
|
4093
|
+
});
|
|
4094
|
+
|
|
4095
|
+
console.log("\n Top 10 Available Alternatives:");
|
|
4096
|
+
for (let i = 0; i < suggestions.suggestions.length; i++) {
|
|
4097
|
+
const s = suggestions.suggestions[i];
|
|
4098
|
+
console.log(` ${(i+1).toString().padStart(2)}. ${s.domain.padEnd(25)} $${s.price?.toFixed(2) || 'N/A'}/yr`);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// Section 5: Recommendation Summary
|
|
4103
|
+
console.log(`\n\n📌 RECOMMENDATION`);
|
|
4104
|
+
console.log("-".repeat(40));
|
|
4105
|
+
|
|
4106
|
+
if (available.length > 0) {
|
|
4107
|
+
// Sort by price to find best value
|
|
4108
|
+
const bestValue = available.sort((a, b) =>
|
|
4109
|
+
(a.price_first_year || 999) - (b.price_first_year || 999)
|
|
4110
|
+
)[0];
|
|
4111
|
+
|
|
4112
|
+
// Find tech-focused option
|
|
4113
|
+
const techOption = available.find(a =>
|
|
4114
|
+
['io', 'dev', 'app'].includes(a.domain.split('.').pop())
|
|
4115
|
+
);
|
|
4116
|
+
|
|
4117
|
+
console.log(`\n 🏆 BEST VALUE: ${bestValue.domain}`);
|
|
4118
|
+
console.log(` Price: $${bestValue.price_first_year}/year`);
|
|
4119
|
+
console.log(` Why: ${bestValue.tldInfo?.recommendation || 'Lowest cost option'}`);
|
|
4120
|
+
|
|
4121
|
+
if (techOption && techOption.domain !== bestValue.domain) {
|
|
4122
|
+
console.log(`\n 💻 TECH STARTUP PICK: ${techOption.domain}`);
|
|
4123
|
+
console.log(` Price: $${techOption.price_first_year}/year`);
|
|
4124
|
+
console.log(` Why: ${techOption.tldInfo?.recommendation || 'Popular with tech startups'}`);
|
|
4125
|
+
}
|
|
4126
|
+
} else {
|
|
4127
|
+
console.log("\n ⚠️ Primary domain unavailable across all TLDs.");
|
|
4128
|
+
console.log(" → Consider alternatives listed above");
|
|
4129
|
+
console.log(" → Check social media availability for brand consistency");
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
console.log(`\n${'='.repeat(60)}\n`);
|
|
4133
|
+
|
|
4134
|
+
return {
|
|
4135
|
+
startupName,
|
|
4136
|
+
generatedAt: new Date().toISOString(),
|
|
4137
|
+
tldAnalysis,
|
|
4138
|
+
availability: { available, taken },
|
|
4139
|
+
socialMedia: socials.platforms,
|
|
4140
|
+
recommendations: available.length > 0
|
|
4141
|
+
? available.sort((a, b) => (a.price_first_year || 999) - (b.price_first_year || 999))
|
|
4142
|
+
: []
|
|
4143
|
+
};
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
// Generate report for a startup
|
|
4147
|
+
const report = await generateStartupDomainReport("nexaflow");
|
|
4148
|
+
```
|
|
4149
|
+
|
|
4150
|
+
**Sample Output:**
|
|
4151
|
+
```
|
|
4152
|
+
============================================================
|
|
4153
|
+
DOMAIN RESEARCH REPORT: NEXAFLOW
|
|
4154
|
+
Generated: 2024-01-15
|
|
4155
|
+
============================================================
|
|
4156
|
+
|
|
4157
|
+
📋 TLD ANALYSIS
|
|
4158
|
+
----------------------------------------
|
|
4159
|
+
|
|
4160
|
+
.COM
|
|
4161
|
+
└─ The original and most recognized domain extension
|
|
4162
|
+
└─ Price: $8.95 - $12.95/year
|
|
4163
|
+
└─ Best for: Any business or personal website
|
|
4164
|
+
└─ Popularity: Very High
|
|
4165
|
+
|
|
4166
|
+
.IO
|
|
4167
|
+
└─ Popular with tech startups and SaaS companies
|
|
4168
|
+
└─ Price: $32.95 - $44.95/year
|
|
4169
|
+
└─ Best for: Tech startups, APIs, developer tools
|
|
4170
|
+
└─ Popularity: High
|
|
4171
|
+
|
|
4172
|
+
.DEV
|
|
4173
|
+
└─ Google-operated TLD for developers
|
|
4174
|
+
└─ Price: $12.95 - $16.95/year
|
|
4175
|
+
└─ Best for: Developer portfolios, open source projects
|
|
4176
|
+
└─ Popularity: Medium
|
|
4177
|
+
└─ ⚠️ Restrictions: Requires HTTPS
|
|
4178
|
+
|
|
4179
|
+
|
|
4180
|
+
📊 AVAILABILITY CHECK
|
|
4181
|
+
----------------------------------------
|
|
4182
|
+
✅ nexaflow.com $9.95/yr
|
|
4183
|
+
❌ nexaflow.io (taken)
|
|
4184
|
+
✅ nexaflow.dev $14.95/yr
|
|
4185
|
+
✅ nexaflow.app $14.95/yr
|
|
4186
|
+
✅ nexaflow.co $29.95/yr
|
|
4187
|
+
|
|
4188
|
+
|
|
4189
|
+
🌐 SOCIAL MEDIA AVAILABILITY
|
|
4190
|
+
----------------------------------------
|
|
4191
|
+
github ✅ Available (high)
|
|
4192
|
+
twitter ❌ Taken (high)
|
|
4193
|
+
npm ✅ Available (high)
|
|
4194
|
+
pypi ✅ Available (high)
|
|
4195
|
+
reddit ✅ Available (high)
|
|
4196
|
+
|
|
4197
|
+
|
|
4198
|
+
📌 RECOMMENDATION
|
|
4199
|
+
----------------------------------------
|
|
4200
|
+
|
|
4201
|
+
🏆 BEST VALUE: nexaflow.com
|
|
4202
|
+
Price: $9.95/year
|
|
4203
|
+
Why: Classic, universal choice - trusted by all audiences
|
|
4204
|
+
|
|
4205
|
+
💻 TECH STARTUP PICK: nexaflow.dev
|
|
4206
|
+
Price: $14.95/year
|
|
4207
|
+
Why: Signals technical focus, requires HTTPS
|
|
4208
|
+
|
|
4209
|
+
============================================================
|
|
4210
|
+
```
|
|
4211
|
+
|
|
3229
4212
|
### Workflow 7: Validate 50 Domains with Result Aggregation
|
|
3230
4213
|
|
|
3231
4214
|
End-to-end workflow for validating exactly 50 domain names with comprehensive result handling:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domain-search-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Fast domain availability aggregator MCP server. Check availability across Porkbun, Namecheap, RDAP, and WHOIS. Compare pricing. Get suggestions.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"types": "dist/server.d.ts",
|