domain-search-mcp 1.2.4 ā 1.2.6
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 +935 -58
- 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:**
|
|
@@ -501,6 +573,132 @@ interface CompareRegistrarsResponse {
|
|
|
501
573
|
}
|
|
502
574
|
```
|
|
503
575
|
|
|
576
|
+
#### Example: Finding the Cheapest Registrar for 'startup.io'
|
|
577
|
+
|
|
578
|
+
This is the complete workflow for finding and presenting the cheapest registrar for registering `startup.io`:
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
// Find the cheapest registrar for startup.io and present pricing comparison
|
|
582
|
+
|
|
583
|
+
async function findCheapestRegistrarForStartupIO() {
|
|
584
|
+
// Step 1: Compare prices across all available registrars
|
|
585
|
+
const comparison = await compareRegistrars({
|
|
586
|
+
domain: "startup",
|
|
587
|
+
tld: "io",
|
|
588
|
+
registrars: ["porkbun", "namecheap"]
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// Step 2: Check if domain is available
|
|
592
|
+
if (!comparison.available) {
|
|
593
|
+
console.log("startup.io is not available for registration");
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Step 3: Extract pricing information
|
|
598
|
+
const prices = comparison.registrar_prices;
|
|
599
|
+
console.log("\nš PRICING COMPARISON FOR startup.io\n");
|
|
600
|
+
console.log("ā".repeat(50));
|
|
601
|
+
console.log("REGISTRAR FIRST YEAR RENEWAL PRIVACY");
|
|
602
|
+
console.log("ā".repeat(50));
|
|
603
|
+
|
|
604
|
+
for (const [registrar, pricing] of Object.entries(prices)) {
|
|
605
|
+
if (pricing.error) {
|
|
606
|
+
console.log(`${registrar.padEnd(15)} Error: ${pricing.error}`);
|
|
607
|
+
} else {
|
|
608
|
+
const firstYear = `$${pricing.first_year}`.padEnd(13);
|
|
609
|
+
const renewal = `$${pricing.renewal}`.padEnd(13);
|
|
610
|
+
const privacy = pricing.privacy_included ? "ā
Included" : "ā Extra";
|
|
611
|
+
console.log(`${registrar.padEnd(15)} ${firstYear} ${renewal} ${privacy}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
console.log("ā".repeat(50));
|
|
616
|
+
|
|
617
|
+
// Step 4: Present the cheapest option
|
|
618
|
+
const cheapest = comparison.best_first_year;
|
|
619
|
+
const bestRenewal = comparison.best_renewal;
|
|
620
|
+
|
|
621
|
+
console.log(`\n⨠CHEAPEST FIRST YEAR: ${cheapest.registrar} at $${cheapest.price}`);
|
|
622
|
+
console.log(`š BEST RENEWAL RATE: ${bestRenewal.registrar} at $${bestRenewal.price}`);
|
|
623
|
+
console.log(`\nš° 5-YEAR SAVINGS: $${comparison.savings.over_5_years}`);
|
|
624
|
+
console.log(`\nš” ${comparison.recommendation}`);
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
cheapestRegistrar: cheapest.registrar,
|
|
628
|
+
firstYearPrice: cheapest.price,
|
|
629
|
+
renewalPrice: prices[cheapest.registrar].renewal,
|
|
630
|
+
recommendation: comparison.recommendation,
|
|
631
|
+
allPrices: prices
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Usage
|
|
636
|
+
const result = await findCheapestRegistrarForStartupIO();
|
|
637
|
+
|
|
638
|
+
// Output:
|
|
639
|
+
// š PRICING COMPARISON FOR startup.io
|
|
640
|
+
//
|
|
641
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
642
|
+
// REGISTRAR FIRST YEAR RENEWAL PRIVACY
|
|
643
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
644
|
+
// porkbun $29.88 $29.88 ā
Included
|
|
645
|
+
// namecheap $32.98 $32.98 ā
Included
|
|
646
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
647
|
+
//
|
|
648
|
+
// ⨠CHEAPEST FIRST YEAR: porkbun at $29.88
|
|
649
|
+
// š BEST RENEWAL RATE: porkbun at $29.88
|
|
650
|
+
//
|
|
651
|
+
// š° 5-YEAR SAVINGS: $15.50
|
|
652
|
+
//
|
|
653
|
+
// š” Porkbun offers the best price for both first year and renewal
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**JavaScript Example for startup.io:**
|
|
657
|
+
|
|
658
|
+
```javascript
|
|
659
|
+
// Using fetch API to compare registrars for startup.io
|
|
660
|
+
async function compareStartupIO() {
|
|
661
|
+
const response = await fetch('http://localhost:3000/compare_registrars', {
|
|
662
|
+
method: 'POST',
|
|
663
|
+
headers: { 'Content-Type': 'application/json' },
|
|
664
|
+
body: JSON.stringify({
|
|
665
|
+
domain: 'startup',
|
|
666
|
+
tld: 'io'
|
|
667
|
+
})
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
const data = await response.json();
|
|
671
|
+
|
|
672
|
+
if (!data.available) {
|
|
673
|
+
console.log('startup.io is taken');
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Display comparison to user
|
|
678
|
+
console.log(`\nPricing for startup.io:\n`);
|
|
679
|
+
|
|
680
|
+
Object.entries(data.registrar_prices).forEach(([registrar, prices]) => {
|
|
681
|
+
console.log(`${registrar}: $${prices.first_year}/yr (renewal: $${prices.renewal}/yr)`);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
console.log(`\nā
Best price: ${data.best_first_year.registrar} at $${data.best_first_year.price}/yr`);
|
|
685
|
+
console.log(`š” ${data.recommendation}`);
|
|
686
|
+
|
|
687
|
+
return data;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Run comparison
|
|
691
|
+
const comparison = await compareStartupIO();
|
|
692
|
+
// Output:
|
|
693
|
+
// Pricing for startup.io:
|
|
694
|
+
//
|
|
695
|
+
// porkbun: $29.88/yr (renewal: $29.88/yr)
|
|
696
|
+
// namecheap: $32.98/yr (renewal: $32.98/yr)
|
|
697
|
+
//
|
|
698
|
+
// ā
Best price: porkbun at $29.88/yr
|
|
699
|
+
// š” Porkbun offers the best price for both first year and renewal
|
|
700
|
+
```
|
|
701
|
+
|
|
504
702
|
**Handling Edge Cases:**
|
|
505
703
|
|
|
506
704
|
```typescript
|
|
@@ -1201,7 +1399,152 @@ console.log(`Price range: $${tldData.price_range.min} - $${tldData.price_range.m
|
|
|
1201
1399
|
|
|
1202
1400
|
### check_socials
|
|
1203
1401
|
|
|
1204
|
-
Verify username availability across 10 platforms
|
|
1402
|
+
Verify username availability across 10 platforms simultaneously.
|
|
1403
|
+
|
|
1404
|
+
**API Endpoint:** `POST /check_socials`
|
|
1405
|
+
|
|
1406
|
+
**Request Parameters:**
|
|
1407
|
+
|
|
1408
|
+
| Parameter | Type | Required | Default | Description |
|
|
1409
|
+
|-----------|------|----------|---------|-------------|
|
|
1410
|
+
| `name` | string | Yes | - | Username to check |
|
|
1411
|
+
| `platforms` | string[] | No | ["github", "twitter", "reddit", "npm"] | Platforms to check |
|
|
1412
|
+
|
|
1413
|
+
**Supported Platforms:**
|
|
1414
|
+
|
|
1415
|
+
| Platform | ID | Confidence | Detection Method |
|
|
1416
|
+
|----------|-----|------------|------------------|
|
|
1417
|
+
| GitHub | `github` | High | Public API |
|
|
1418
|
+
| Twitter/X | `twitter` | High | oembed API |
|
|
1419
|
+
| npm | `npm` | High | Registry API |
|
|
1420
|
+
| PyPI | `pypi` | High | Package API |
|
|
1421
|
+
| Reddit | `reddit` | High | Profile check |
|
|
1422
|
+
| YouTube | `youtube` | Medium | Channel page |
|
|
1423
|
+
| ProductHunt | `producthunt` | Medium | Profile page |
|
|
1424
|
+
| Instagram | `instagram` | Low | Blocks automation |
|
|
1425
|
+
| LinkedIn | `linkedin` | Low | Blocks automation |
|
|
1426
|
+
| TikTok | `tiktok` | Low | Blocks automation |
|
|
1427
|
+
|
|
1428
|
+
**Response Type:**
|
|
1429
|
+
|
|
1430
|
+
```typescript
|
|
1431
|
+
interface CheckSocialsResponse {
|
|
1432
|
+
name: string; // Username checked
|
|
1433
|
+
results: Array<{
|
|
1434
|
+
platform: string; // Platform ID (github, twitter, etc.)
|
|
1435
|
+
available: boolean; // Whether username is available
|
|
1436
|
+
confidence: "high" | "medium" | "low";
|
|
1437
|
+
url: string; // Direct URL to profile/claim page
|
|
1438
|
+
error?: string; // Error message if check failed
|
|
1439
|
+
errorCode?: string; // RATE_LIMIT, TIMEOUT, BLOCKED, etc.
|
|
1440
|
+
responseTime?: number; // Check duration in ms
|
|
1441
|
+
}>;
|
|
1442
|
+
insights: string[]; // Human-readable summary
|
|
1443
|
+
summary: {
|
|
1444
|
+
available: number; // Count of available platforms
|
|
1445
|
+
taken: number; // Count of taken platforms
|
|
1446
|
+
unknown: number; // Count of low-confidence/error platforms
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
```
|
|
1450
|
+
|
|
1451
|
+
---
|
|
1452
|
+
|
|
1453
|
+
#### Quick Start: Check GitHub, Twitter, and Instagram
|
|
1454
|
+
|
|
1455
|
+
This is the most common use case - verify username on the three main platforms:
|
|
1456
|
+
|
|
1457
|
+
```typescript
|
|
1458
|
+
// Quick Start: Check 'myproject' on GitHub, Twitter, and Instagram simultaneously
|
|
1459
|
+
|
|
1460
|
+
import { checkSocials } from 'domain-search-mcp';
|
|
1461
|
+
|
|
1462
|
+
async function checkUsernameOnMainPlatforms(username: string) {
|
|
1463
|
+
// Call check_socials with the three platforms
|
|
1464
|
+
const result = await checkSocials({
|
|
1465
|
+
name: username,
|
|
1466
|
+
platforms: ["github", "twitter", "instagram"]
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
// Process results
|
|
1470
|
+
console.log(`\nChecking "${username}" on GitHub, Twitter, Instagram:\n`);
|
|
1471
|
+
|
|
1472
|
+
for (const platform of result.results) {
|
|
1473
|
+
const status = platform.available ? "ā
Available" : "ā Taken";
|
|
1474
|
+
const confidence = platform.confidence === "low" ? " (verify manually)" : "";
|
|
1475
|
+
console.log(` ${platform.platform.padEnd(12)} ${status}${confidence}`);
|
|
1476
|
+
console.log(` āā ${platform.url}`);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Summary
|
|
1480
|
+
const available = result.results.filter(r => r.available && r.confidence !== "low");
|
|
1481
|
+
const taken = result.results.filter(r => !r.available);
|
|
1482
|
+
const manual = result.results.filter(r => r.confidence === "low");
|
|
1483
|
+
|
|
1484
|
+
console.log(`\nSummary: ${available.length} available, ${taken.length} taken, ${manual.length} need manual check`);
|
|
1485
|
+
|
|
1486
|
+
return {
|
|
1487
|
+
available: available.map(r => ({ platform: r.platform, url: r.url })),
|
|
1488
|
+
taken: taken.map(r => r.platform),
|
|
1489
|
+
verifyManually: manual.map(r => ({ platform: r.platform, url: r.url }))
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Usage
|
|
1494
|
+
const result = await checkUsernameOnMainPlatforms("myproject");
|
|
1495
|
+
|
|
1496
|
+
// Output:
|
|
1497
|
+
// Checking "myproject" on GitHub, Twitter, Instagram:
|
|
1498
|
+
//
|
|
1499
|
+
// github ā
Available
|
|
1500
|
+
// āā https://github.com/myproject
|
|
1501
|
+
// twitter ā Taken
|
|
1502
|
+
// āā https://twitter.com/myproject
|
|
1503
|
+
// instagram ā
Available (verify manually)
|
|
1504
|
+
// āā https://instagram.com/myproject
|
|
1505
|
+
//
|
|
1506
|
+
// Summary: 1 available, 1 taken, 1 need manual check
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
**JavaScript Quick Start:**
|
|
1510
|
+
|
|
1511
|
+
```javascript
|
|
1512
|
+
// Check GitHub, Twitter, Instagram for a project name
|
|
1513
|
+
async function checkProjectSocials(projectName) {
|
|
1514
|
+
const response = await fetch('http://localhost:3000/check_socials', {
|
|
1515
|
+
method: 'POST',
|
|
1516
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1517
|
+
body: JSON.stringify({
|
|
1518
|
+
name: projectName,
|
|
1519
|
+
platforms: ['github', 'twitter', 'instagram']
|
|
1520
|
+
})
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
const data = await response.json();
|
|
1524
|
+
|
|
1525
|
+
// Display results
|
|
1526
|
+
console.log(`Social media check for "${projectName}":`);
|
|
1527
|
+
data.results.forEach(r => {
|
|
1528
|
+
const icon = r.available ? 'ā
' : 'ā';
|
|
1529
|
+
const note = r.confidence === 'low' ? ' (verify manually)' : '';
|
|
1530
|
+
console.log(` ${icon} ${r.platform}: ${r.available ? 'Available' : 'Taken'}${note}`);
|
|
1531
|
+
});
|
|
1532
|
+
|
|
1533
|
+
return data;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// Example
|
|
1537
|
+
const socials = await checkProjectSocials('myproject');
|
|
1538
|
+
// Output:
|
|
1539
|
+
// Social media check for "myproject":
|
|
1540
|
+
// ā
github: Available
|
|
1541
|
+
// ā twitter: Taken
|
|
1542
|
+
// ā
instagram: Available (verify manually)
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
---
|
|
1546
|
+
|
|
1547
|
+
**Basic Example:**
|
|
1205
1548
|
|
|
1206
1549
|
```typescript
|
|
1207
1550
|
// Input
|
|
@@ -1214,15 +1557,31 @@ Verify username availability across 10 platforms:
|
|
|
1214
1557
|
{
|
|
1215
1558
|
"name": "vibecoding",
|
|
1216
1559
|
"results": [
|
|
1217
|
-
{
|
|
1218
|
-
|
|
1219
|
-
|
|
1560
|
+
{
|
|
1561
|
+
"platform": "github",
|
|
1562
|
+
"available": true,
|
|
1563
|
+
"confidence": "high",
|
|
1564
|
+
"url": "https://github.com/vibecoding"
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
"platform": "twitter",
|
|
1568
|
+
"available": false,
|
|
1569
|
+
"confidence": "high",
|
|
1570
|
+
"url": "https://twitter.com/vibecoding"
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
"platform": "instagram",
|
|
1574
|
+
"available": true,
|
|
1575
|
+
"confidence": "low",
|
|
1576
|
+
"url": "https://instagram.com/vibecoding"
|
|
1577
|
+
}
|
|
1220
1578
|
],
|
|
1221
1579
|
"insights": [
|
|
1222
1580
|
"ā
vibecoding is available on: github",
|
|
1223
1581
|
"ā vibecoding is taken on: twitter",
|
|
1224
1582
|
"ā ļø Could not reliably check: instagram (check manually)"
|
|
1225
|
-
]
|
|
1583
|
+
],
|
|
1584
|
+
"summary": { "available": 2, "taken": 1, "unknown": 0 }
|
|
1226
1585
|
}
|
|
1227
1586
|
```
|
|
1228
1587
|
|
|
@@ -1437,16 +1796,57 @@ With Porkbun API:
|
|
|
1437
1796
|
|
|
1438
1797
|
#### Initializing Domain Search MCP with API Keys
|
|
1439
1798
|
|
|
1440
|
-
**Step 1: Get API Keys**
|
|
1799
|
+
**Step 1: Get Porkbun API Keys (Free, Recommended)**
|
|
1441
1800
|
|
|
1442
|
-
```
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1801
|
+
```
|
|
1802
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1803
|
+
ā HOW TO GET PORKBUN API KEYS (5 minutes) ā
|
|
1804
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
|
|
1805
|
+
ā ā
|
|
1806
|
+
ā 1. Go to: https://porkbun.com/account/api ā
|
|
1807
|
+
ā ā
|
|
1808
|
+
ā 2. Log in or create a free account ā
|
|
1809
|
+
ā - No credit card required ā
|
|
1810
|
+
ā - Email verification needed ā
|
|
1811
|
+
ā ā
|
|
1812
|
+
ā 3. Click "Create API Key" ā
|
|
1813
|
+
ā ā
|
|
1814
|
+
ā 4. You'll receive TWO keys: ā
|
|
1815
|
+
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
|
|
1816
|
+
ā ā API Key: pk1_abc123def456ghi789jkl012mno345... ā ā
|
|
1817
|
+
ā ā Secret Key: sk1_xyz789abc123def456ghi789jkl012... ā ā
|
|
1818
|
+
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
|
|
1819
|
+
ā ā ļø SAVE BOTH - Secret Key is shown only once! ā
|
|
1820
|
+
ā ā
|
|
1821
|
+
ā 5. Done! No IP whitelist or domain ownership required ā
|
|
1822
|
+
ā ā
|
|
1823
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1824
|
+
```
|
|
1825
|
+
|
|
1826
|
+
**Step 1b: Get Namecheap API Keys (Optional)**
|
|
1446
1827
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1828
|
+
```
|
|
1829
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1830
|
+
ā HOW TO GET NAMECHEAP API KEYS ā
|
|
1831
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
|
|
1832
|
+
ā ā
|
|
1833
|
+
ā Prerequisites: ā
|
|
1834
|
+
ā - Active Namecheap account with at least 1 domain OR ā
|
|
1835
|
+
ā - $50+ account balance ā
|
|
1836
|
+
ā ā
|
|
1837
|
+
ā 1. Go to: https://ap.www.namecheap.com/settings/tools/apiaccessā
|
|
1838
|
+
ā ā
|
|
1839
|
+
ā 2. Enable API Access (toggle ON) ā
|
|
1840
|
+
ā ā
|
|
1841
|
+
ā 3. Whitelist your IP address: ā
|
|
1842
|
+
ā - Find your IP: curl ifconfig.me ā
|
|
1843
|
+
ā - Add it to the whitelist ā
|
|
1844
|
+
ā ā
|
|
1845
|
+
ā 4. Copy your API Key and Username ā
|
|
1846
|
+
ā ā
|
|
1847
|
+
ā ā ļø Note: Namecheap requires IP whitelisting for security ā
|
|
1848
|
+
ā ā
|
|
1849
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1450
1850
|
```
|
|
1451
1851
|
|
|
1452
1852
|
**Step 2: Create .env File**
|
|
@@ -1460,16 +1860,54 @@ cp .env.example .env
|
|
|
1460
1860
|
# .env file contents
|
|
1461
1861
|
# ==================
|
|
1462
1862
|
|
|
1463
|
-
# Porkbun API (Recommended - Free)
|
|
1464
|
-
PORKBUN_API_KEY=
|
|
1465
|
-
PORKBUN_SECRET_KEY=
|
|
1863
|
+
# Porkbun API (Recommended - Free, no restrictions)
|
|
1864
|
+
PORKBUN_API_KEY=pk1_abc123def456ghi789jkl012mno345pqr678stu901vwx234
|
|
1865
|
+
PORKBUN_SECRET_KEY=sk1_xyz789abc123def456ghi789jkl012mno345pqr678stu901
|
|
1466
1866
|
|
|
1467
|
-
# Namecheap API (Optional)
|
|
1867
|
+
# Namecheap API (Optional - requires IP whitelist)
|
|
1468
1868
|
NAMECHEAP_API_KEY=your_api_key_here
|
|
1469
1869
|
NAMECHEAP_API_USER=your_username_here
|
|
1470
1870
|
NAMECHEAP_CLIENT_IP=auto # Optional, auto-detected if omitted
|
|
1471
1871
|
```
|
|
1472
1872
|
|
|
1873
|
+
**Why Porkbun is Recommended:**
|
|
1874
|
+
|
|
1875
|
+
| Feature | Porkbun | Namecheap |
|
|
1876
|
+
|---------|---------|-----------|
|
|
1877
|
+
| **Cost** | Free | Free |
|
|
1878
|
+
| **Setup Time** | 2 minutes | 10+ minutes |
|
|
1879
|
+
| **IP Whitelist** | Not required | Required |
|
|
1880
|
+
| **Domain Ownership** | Not required | Required ($50 balance alternative) |
|
|
1881
|
+
| **Rate Limit** | 1000+ req/min | 500+ req/min |
|
|
1882
|
+
| **Response Time** | ~100ms | ~150ms |
|
|
1883
|
+
|
|
1884
|
+
**Performance Benefits of API Keys (Detailed):**
|
|
1885
|
+
|
|
1886
|
+
```
|
|
1887
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1888
|
+
ā PERFORMANCE COMPARISON: 100 Domain Batch Search ā
|
|
1889
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
|
|
1890
|
+
ā ā
|
|
1891
|
+
ā WITHOUT API KEYS (RDAP/WHOIS fallback): ā
|
|
1892
|
+
ā āā Total time: 3-5 minutes ā
|
|
1893
|
+
ā āā Rate limit errors: 5-15 retries needed ā
|
|
1894
|
+
ā āā Pricing data: ā Not available ā
|
|
1895
|
+
ā āā WHOIS privacy info: ā Not available ā
|
|
1896
|
+
ā āā Success rate: ~85% (some TLDs fail) ā
|
|
1897
|
+
ā ā
|
|
1898
|
+
ā WITH PORKBUN API: ā
|
|
1899
|
+
ā āā Total time: 8-15 seconds (14x faster) ā
|
|
1900
|
+
ā āā Rate limit errors: 0 ā
|
|
1901
|
+
ā āā Pricing data: ā
First year + renewal prices ā
|
|
1902
|
+
ā āā WHOIS privacy info: ā
Included ā
|
|
1903
|
+
ā āā Success rate: 99.9% ā
|
|
1904
|
+
ā ā
|
|
1905
|
+
ā Time saved per 100 domains: ~4 minutes ā
|
|
1906
|
+
ā Time saved per 1000 domains: ~40 minutes ā
|
|
1907
|
+
ā ā
|
|
1908
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1909
|
+
```
|
|
1910
|
+
|
|
1473
1911
|
**Step 3: Verify Configuration**
|
|
1474
1912
|
|
|
1475
1913
|
```typescript
|
|
@@ -1881,6 +2319,215 @@ Add to `.cursor/mcp.json`:
|
|
|
1881
2319
|
|
|
1882
2320
|
Add to your Cline settings to enable MCP servers.
|
|
1883
2321
|
|
|
2322
|
+
### MCP Tool Invocation Patterns
|
|
2323
|
+
|
|
2324
|
+
When using Domain Search MCP through an MCP-compatible client (Claude Desktop, Cursor, etc.), tools are invoked using the standard MCP tool call pattern:
|
|
2325
|
+
|
|
2326
|
+
#### Basic MCP Tool Call Structure
|
|
2327
|
+
|
|
2328
|
+
```typescript
|
|
2329
|
+
// MCP tool invocation pattern (as handled by the MCP client)
|
|
2330
|
+
// The client sends a tool_call request to the MCP server:
|
|
2331
|
+
|
|
2332
|
+
// Tool: search_domain
|
|
2333
|
+
{
|
|
2334
|
+
"tool": "search_domain",
|
|
2335
|
+
"arguments": {
|
|
2336
|
+
"domain_name": "vibecoding",
|
|
2337
|
+
"tlds": ["com", "io", "dev"]
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
// Tool: check_socials
|
|
2342
|
+
{
|
|
2343
|
+
"tool": "check_socials",
|
|
2344
|
+
"arguments": {
|
|
2345
|
+
"name": "myproject",
|
|
2346
|
+
"platforms": ["github", "twitter", "instagram"]
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
// Tool: suggest_domains
|
|
2351
|
+
{
|
|
2352
|
+
"tool": "suggest_domains",
|
|
2353
|
+
"arguments": {
|
|
2354
|
+
"base_name": "techapp",
|
|
2355
|
+
"tld": "com",
|
|
2356
|
+
"max_suggestions": 10,
|
|
2357
|
+
"variants": ["prefixes", "suffixes", "hyphen"]
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
```
|
|
2361
|
+
|
|
2362
|
+
#### MCP Tool Invocation via Claude Desktop
|
|
2363
|
+
|
|
2364
|
+
When using Claude Desktop, simply describe what you want in natural language:
|
|
2365
|
+
|
|
2366
|
+
```
|
|
2367
|
+
User: "Check if 'myproject' is available as a domain and on GitHub"
|
|
2368
|
+
|
|
2369
|
+
Claude invokes MCP tools:
|
|
2370
|
+
1. search_domain({ domain_name: "myproject", tlds: ["com", "io", "dev"] })
|
|
2371
|
+
2. check_socials({ name: "myproject", platforms: ["github"] })
|
|
2372
|
+
|
|
2373
|
+
Response: Shows domain availability with pricing and GitHub username status
|
|
2374
|
+
```
|
|
2375
|
+
|
|
2376
|
+
#### Quick Reference: All MCP Tool Names
|
|
2377
|
+
|
|
2378
|
+
| MCP Tool Name | Purpose | Primary Arguments |
|
|
2379
|
+
|---------------|---------|-------------------|
|
|
2380
|
+
| `search_domain` | Check availability + pricing | `domain_name`, `tlds[]` |
|
|
2381
|
+
| `bulk_search` | Check up to 100 domains | `domains[]`, `tld` |
|
|
2382
|
+
| `compare_registrars` | Compare prices | `domain`, `tld` |
|
|
2383
|
+
| `suggest_domains` | Get variations when taken | `base_name`, `tld`, `variants[]` |
|
|
2384
|
+
| `suggest_domains_smart` | AI-powered suggestions | `query`, `tld`, `style` |
|
|
2385
|
+
| `tld_info` | TLD details + restrictions | `tld`, `detailed` |
|
|
2386
|
+
| `check_socials` | Social username check | `name`, `platforms[]` |
|
|
2387
|
+
|
|
2388
|
+
#### Programmatic MCP Tool Invocation (via MCP SDK)
|
|
2389
|
+
|
|
2390
|
+
```typescript
|
|
2391
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2392
|
+
|
|
2393
|
+
// Initialize MCP client
|
|
2394
|
+
const client = new Client({
|
|
2395
|
+
name: "domain-search-client",
|
|
2396
|
+
version: "1.0.0"
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
// Connect to the domain-search-mcp server
|
|
2400
|
+
await client.connect(transport);
|
|
2401
|
+
|
|
2402
|
+
// Invoke MCP tools programmatically
|
|
2403
|
+
const domainResult = await client.callTool({
|
|
2404
|
+
name: "search_domain",
|
|
2405
|
+
arguments: {
|
|
2406
|
+
domain_name: "myproject",
|
|
2407
|
+
tlds: ["com", "io"]
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
|
|
2411
|
+
const socialResult = await client.callTool({
|
|
2412
|
+
name: "check_socials",
|
|
2413
|
+
arguments: {
|
|
2414
|
+
name: "myproject",
|
|
2415
|
+
platforms: ["github", "twitter", "instagram"]
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2419
|
+
console.log("Domain results:", domainResult);
|
|
2420
|
+
console.log("Social results:", socialResult);
|
|
2421
|
+
```
|
|
2422
|
+
|
|
2423
|
+
### Running as Local HTTP Server
|
|
2424
|
+
|
|
2425
|
+
Domain Search MCP can also run as a standalone HTTP server for direct API access:
|
|
2426
|
+
|
|
2427
|
+
#### Quick Start: Local Server Setup
|
|
2428
|
+
|
|
2429
|
+
```bash
|
|
2430
|
+
# Step 1: Clone and install
|
|
2431
|
+
git clone https://github.com/dorukardahan/domain-search-mcp.git
|
|
2432
|
+
cd domain-search-mcp
|
|
2433
|
+
npm install
|
|
2434
|
+
|
|
2435
|
+
# Step 2: Build the server
|
|
2436
|
+
npm run build
|
|
2437
|
+
|
|
2438
|
+
# Step 3: Start the HTTP server
|
|
2439
|
+
npm start
|
|
2440
|
+
|
|
2441
|
+
# Server runs at http://localhost:3000
|
|
2442
|
+
```
|
|
2443
|
+
|
|
2444
|
+
#### Local Server Configuration
|
|
2445
|
+
|
|
2446
|
+
```bash
|
|
2447
|
+
# Optional: Configure environment before starting
|
|
2448
|
+
cp .env.example .env
|
|
2449
|
+
|
|
2450
|
+
# Edit .env to add API keys (optional but recommended)
|
|
2451
|
+
# PORKBUN_API_KEY=pk1_...
|
|
2452
|
+
# PORKBUN_SECRET_KEY=sk1_...
|
|
2453
|
+
|
|
2454
|
+
# Set custom port (default: 3000)
|
|
2455
|
+
export PORT=3001
|
|
2456
|
+
|
|
2457
|
+
# Start server
|
|
2458
|
+
npm start
|
|
2459
|
+
```
|
|
2460
|
+
|
|
2461
|
+
#### Making API Requests to Local Server
|
|
2462
|
+
|
|
2463
|
+
Once the server is running, you can make HTTP requests:
|
|
2464
|
+
|
|
2465
|
+
```bash
|
|
2466
|
+
# Check domain availability
|
|
2467
|
+
curl -X POST http://localhost:3000/search_domain \
|
|
2468
|
+
-H "Content-Type: application/json" \
|
|
2469
|
+
-d '{"domain_name": "myproject", "tlds": ["com", "io"]}'
|
|
2470
|
+
|
|
2471
|
+
# Check social media usernames
|
|
2472
|
+
curl -X POST http://localhost:3000/check_socials \
|
|
2473
|
+
-H "Content-Type: application/json" \
|
|
2474
|
+
-d '{"name": "myproject", "platforms": ["github", "twitter"]}'
|
|
2475
|
+
|
|
2476
|
+
# Bulk search 50 domains
|
|
2477
|
+
curl -X POST http://localhost:3000/bulk_search \
|
|
2478
|
+
-H "Content-Type: application/json" \
|
|
2479
|
+
-d '{"domains": ["name1", "name2", "name3"], "tld": "com"}'
|
|
2480
|
+
```
|
|
2481
|
+
|
|
2482
|
+
#### JavaScript/Node.js Local Server Integration
|
|
2483
|
+
|
|
2484
|
+
```javascript
|
|
2485
|
+
// Complete local server integration example
|
|
2486
|
+
|
|
2487
|
+
const BASE_URL = 'http://localhost:3000';
|
|
2488
|
+
|
|
2489
|
+
// Initialize connection to local domain-search server
|
|
2490
|
+
async function initializeDomainSearch() {
|
|
2491
|
+
try {
|
|
2492
|
+
// Test server connection
|
|
2493
|
+
const response = await fetch(`${BASE_URL}/search_domain`, {
|
|
2494
|
+
method: 'POST',
|
|
2495
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2496
|
+
body: JSON.stringify({
|
|
2497
|
+
domain_name: 'test-connection',
|
|
2498
|
+
tlds: ['com']
|
|
2499
|
+
})
|
|
2500
|
+
});
|
|
2501
|
+
|
|
2502
|
+
if (response.ok) {
|
|
2503
|
+
console.log('ā
Domain Search server connected');
|
|
2504
|
+
return true;
|
|
2505
|
+
}
|
|
2506
|
+
throw new Error('Server not responding');
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
console.error('ā Failed to connect to server:', error.message);
|
|
2509
|
+
console.error('Make sure to run: npm start');
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// Usage
|
|
2515
|
+
const connected = await initializeDomainSearch();
|
|
2516
|
+
if (connected) {
|
|
2517
|
+
// Proceed with domain searches
|
|
2518
|
+
const result = await fetch(`${BASE_URL}/search_domain`, {
|
|
2519
|
+
method: 'POST',
|
|
2520
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2521
|
+
body: JSON.stringify({
|
|
2522
|
+
domain_name: 'vibecoding',
|
|
2523
|
+
tlds: ['com', 'io', 'dev']
|
|
2524
|
+
})
|
|
2525
|
+
}).then(r => r.json());
|
|
2526
|
+
|
|
2527
|
+
console.log(`Found ${result.results.filter(r => r.available).length} available domains`);
|
|
2528
|
+
}
|
|
2529
|
+
```
|
|
2530
|
+
|
|
1884
2531
|
## Development
|
|
1885
2532
|
|
|
1886
2533
|
### Setup
|
|
@@ -2295,28 +2942,54 @@ try {
|
|
|
2295
2942
|
|
|
2296
2943
|
### Workflow 1: Complete Domain Acquisition with Partial Availability Handling
|
|
2297
2944
|
|
|
2298
|
-
A comprehensive workflow that
|
|
2945
|
+
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:
|
|
2299
2946
|
|
|
2300
2947
|
```typescript
|
|
2301
|
-
|
|
2948
|
+
// Types for the workflow response
|
|
2949
|
+
interface AcquisitionReport {
|
|
2950
|
+
brandName: string;
|
|
2951
|
+
summary: {
|
|
2952
|
+
domainsChecked: number;
|
|
2953
|
+
available: number;
|
|
2954
|
+
taken: number;
|
|
2955
|
+
failedChecks: number;
|
|
2956
|
+
socialsAvailable: number;
|
|
2957
|
+
socialsTaken: number;
|
|
2958
|
+
socialsUnverified: number;
|
|
2959
|
+
};
|
|
2960
|
+
domains: {
|
|
2961
|
+
available: Array<{ domain: string; price: number; registrar: string }>;
|
|
2962
|
+
taken: string[];
|
|
2963
|
+
alternatives: Array<{ domain: string; price: number; variant: string }>;
|
|
2964
|
+
};
|
|
2965
|
+
socials: {
|
|
2966
|
+
available: Array<{ platform: string; confidence: string; url: string }>;
|
|
2967
|
+
taken: Array<{ platform: string; url: string }>;
|
|
2968
|
+
needsManualCheck: Array<{ platform: string; url: string; reason: string }>;
|
|
2969
|
+
};
|
|
2970
|
+
pricing: Array<{ domain: string; bestRegistrar: string; price: number }>;
|
|
2971
|
+
nextSteps: string[];
|
|
2972
|
+
presentation: string; // Formatted for user display
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
async function completeDomainAcquisition(brandName: string): Promise<AcquisitionReport> {
|
|
2976
|
+
console.log(`\nš Starting brand validation for "${brandName}"...\n`);
|
|
2977
|
+
|
|
2302
2978
|
// Step 1: Run parallel checks across domains and social media
|
|
2303
2979
|
const [domainResults, socialResults] = await Promise.all([
|
|
2304
2980
|
searchDomain({
|
|
2305
2981
|
domain_name: brandName,
|
|
2306
2982
|
tlds: ["com", "io", "dev", "app", "co"]
|
|
2307
2983
|
}),
|
|
2308
|
-
|
|
2309
|
-
name: brandName,
|
|
2310
|
-
platforms: ["github", "twitter", "instagram", "npm", "linkedin"]
|
|
2311
|
-
})
|
|
2984
|
+
checkSocialsWithErrorHandling(brandName) // Use wrapper for better error handling
|
|
2312
2985
|
]);
|
|
2313
2986
|
|
|
2314
|
-
// Step 2: Handle partial availability
|
|
2987
|
+
// Step 2: Handle partial domain availability
|
|
2315
2988
|
const available = domainResults.results.filter(r => r.available && !r.error);
|
|
2316
2989
|
const taken = domainResults.results.filter(r => !r.available && !r.error);
|
|
2317
2990
|
const failed = domainResults.results.filter(r => r.error);
|
|
2318
2991
|
|
|
2319
|
-
// Step 3: Retry failed checks with exponential backoff
|
|
2992
|
+
// Step 3: Retry failed domain checks with exponential backoff
|
|
2320
2993
|
const retriedResults = [];
|
|
2321
2994
|
for (const failedDomain of failed) {
|
|
2322
2995
|
const tld = failedDomain.domain.split('.').pop();
|
|
@@ -2334,25 +3007,26 @@ async function completeDomainAcquisition(brandName: string) {
|
|
|
2334
3007
|
break;
|
|
2335
3008
|
}
|
|
2336
3009
|
} catch (e) {
|
|
2337
|
-
delay *= 2;
|
|
3010
|
+
delay *= 2;
|
|
2338
3011
|
}
|
|
2339
3012
|
}
|
|
2340
3013
|
}
|
|
2341
3014
|
|
|
2342
|
-
// Step 4: If preferred .com is taken,
|
|
3015
|
+
// Step 4: If preferred .com is taken, use suggest_domains for alternatives
|
|
2343
3016
|
let suggestions = [];
|
|
2344
3017
|
const comDomain = [...available, ...retriedResults].find(d => d.domain.endsWith('.com'));
|
|
2345
3018
|
if (!comDomain) {
|
|
3019
|
+
// Use suggest_domains (not smart) to get variations of the exact name
|
|
2346
3020
|
const suggestResult = await suggestDomains({
|
|
2347
3021
|
base_name: brandName,
|
|
2348
3022
|
tld: "com",
|
|
2349
3023
|
max_suggestions: 10,
|
|
2350
|
-
variants: ["prefixes", "suffixes", "hyphen"]
|
|
3024
|
+
variants: ["prefixes", "suffixes", "hyphen"] // Most common patterns
|
|
2351
3025
|
});
|
|
2352
3026
|
suggestions = suggestResult.suggestions;
|
|
2353
3027
|
}
|
|
2354
3028
|
|
|
2355
|
-
// Step 5: Compare pricing for available domains
|
|
3029
|
+
// Step 5: Compare pricing for top available domains
|
|
2356
3030
|
const priceComparisons = await Promise.all(
|
|
2357
3031
|
available.slice(0, 3).map(d => {
|
|
2358
3032
|
const [name, tld] = d.domain.split('.');
|
|
@@ -2360,63 +3034,266 @@ async function completeDomainAcquisition(brandName: string) {
|
|
|
2360
3034
|
})
|
|
2361
3035
|
);
|
|
2362
3036
|
|
|
2363
|
-
// Step 6:
|
|
2364
|
-
|
|
3037
|
+
// Step 6: Process social media results with platform-specific handling
|
|
3038
|
+
const socialReport = processSocialResults(socialResults, brandName);
|
|
3039
|
+
|
|
3040
|
+
// Step 7: Compile comprehensive report
|
|
3041
|
+
const report: AcquisitionReport = {
|
|
2365
3042
|
brandName,
|
|
2366
3043
|
summary: {
|
|
2367
3044
|
domainsChecked: domainResults.results.length,
|
|
2368
3045
|
available: available.length + retriedResults.length,
|
|
2369
3046
|
taken: taken.length,
|
|
2370
3047
|
failedChecks: failed.length - retriedResults.length,
|
|
2371
|
-
socialsAvailable:
|
|
3048
|
+
socialsAvailable: socialReport.available.length,
|
|
3049
|
+
socialsTaken: socialReport.taken.length,
|
|
3050
|
+
socialsUnverified: socialReport.needsManualCheck.length
|
|
2372
3051
|
},
|
|
2373
3052
|
domains: {
|
|
2374
3053
|
available: [...available, ...retriedResults].map(d => ({
|
|
2375
3054
|
domain: d.domain,
|
|
2376
3055
|
price: d.price_first_year,
|
|
2377
|
-
registrar: d.registrar
|
|
2378
|
-
source: d.source
|
|
3056
|
+
registrar: d.registrar
|
|
2379
3057
|
})),
|
|
2380
3058
|
taken: taken.map(d => d.domain),
|
|
2381
|
-
alternatives: suggestions.map(s =>
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
.map(r => r.platform),
|
|
2387
|
-
taken: socialResults.results
|
|
2388
|
-
.filter(r => !r.available)
|
|
2389
|
-
.map(r => r.platform),
|
|
2390
|
-
needsManualCheck: socialResults.results
|
|
2391
|
-
.filter(r => r.confidence === "low")
|
|
2392
|
-
.map(r => r.platform)
|
|
3059
|
+
alternatives: suggestions.map(s => ({
|
|
3060
|
+
domain: s.domain,
|
|
3061
|
+
price: s.price_first_year,
|
|
3062
|
+
variant: s.variant_type
|
|
3063
|
+
}))
|
|
2393
3064
|
},
|
|
3065
|
+
socials: socialReport,
|
|
2394
3066
|
pricing: priceComparisons.filter(Boolean).map(p => ({
|
|
2395
3067
|
domain: p.domain,
|
|
2396
|
-
|
|
2397
|
-
|
|
3068
|
+
bestRegistrar: p.best_first_year?.registrar,
|
|
3069
|
+
price: p.best_first_year?.price
|
|
2398
3070
|
})),
|
|
2399
|
-
nextSteps: generateNextSteps(available,
|
|
3071
|
+
nextSteps: generateNextSteps(available, socialReport, suggestions),
|
|
3072
|
+
presentation: "" // Will be filled below
|
|
2400
3073
|
};
|
|
3074
|
+
|
|
3075
|
+
// Step 8: Generate formatted presentation for user
|
|
3076
|
+
report.presentation = formatReportForPresentation(report);
|
|
3077
|
+
|
|
3078
|
+
return report;
|
|
2401
3079
|
}
|
|
2402
3080
|
|
|
2403
|
-
|
|
3081
|
+
// Social media check with comprehensive error handling per platform
|
|
3082
|
+
async function checkSocialsWithErrorHandling(brandName: string) {
|
|
3083
|
+
try {
|
|
3084
|
+
const result = await checkSocials({
|
|
3085
|
+
name: brandName,
|
|
3086
|
+
platforms: ["github", "twitter", "instagram", "npm", "linkedin"]
|
|
3087
|
+
});
|
|
3088
|
+
return result;
|
|
3089
|
+
} catch (error) {
|
|
3090
|
+
// Return partial results even if some platforms fail
|
|
3091
|
+
return {
|
|
3092
|
+
results: [
|
|
3093
|
+
{ platform: "github", available: null, error: error.message, confidence: "low" },
|
|
3094
|
+
{ platform: "twitter", available: null, error: error.message, confidence: "low" },
|
|
3095
|
+
{ platform: "instagram", available: null, error: "Platform blocks automated checks", confidence: "low" },
|
|
3096
|
+
{ platform: "npm", available: null, error: error.message, confidence: "low" },
|
|
3097
|
+
{ platform: "linkedin", available: null, error: "Platform blocks automated checks", confidence: "low" }
|
|
3098
|
+
]
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
// Process social results with platform-specific error handling
|
|
3104
|
+
function processSocialResults(socialResults: any, brandName: string) {
|
|
3105
|
+
const available = [];
|
|
3106
|
+
const taken = [];
|
|
3107
|
+
const needsManualCheck = [];
|
|
3108
|
+
|
|
3109
|
+
for (const result of socialResults.results) {
|
|
3110
|
+
const platformUrl = getPlatformUrl(result.platform, brandName);
|
|
3111
|
+
|
|
3112
|
+
if (result.error) {
|
|
3113
|
+
// Platform-specific error handling
|
|
3114
|
+
needsManualCheck.push({
|
|
3115
|
+
platform: result.platform,
|
|
3116
|
+
url: platformUrl,
|
|
3117
|
+
reason: getErrorReason(result.platform, result.error)
|
|
3118
|
+
});
|
|
3119
|
+
} else if (result.confidence === "low") {
|
|
3120
|
+
// Low confidence results need manual verification
|
|
3121
|
+
needsManualCheck.push({
|
|
3122
|
+
platform: result.platform,
|
|
3123
|
+
url: platformUrl,
|
|
3124
|
+
reason: "Automated check unreliable - verify manually"
|
|
3125
|
+
});
|
|
3126
|
+
} else if (result.available) {
|
|
3127
|
+
available.push({
|
|
3128
|
+
platform: result.platform,
|
|
3129
|
+
confidence: result.confidence,
|
|
3130
|
+
url: platformUrl
|
|
3131
|
+
});
|
|
3132
|
+
} else {
|
|
3133
|
+
taken.push({
|
|
3134
|
+
platform: result.platform,
|
|
3135
|
+
url: platformUrl
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
return { available, taken, needsManualCheck };
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
// Get direct URL for each platform
|
|
3144
|
+
function getPlatformUrl(platform: string, username: string): string {
|
|
3145
|
+
const urls = {
|
|
3146
|
+
github: `https://github.com/${username}`,
|
|
3147
|
+
twitter: `https://twitter.com/${username}`,
|
|
3148
|
+
instagram: `https://instagram.com/${username}`,
|
|
3149
|
+
npm: `https://www.npmjs.com/~${username}`,
|
|
3150
|
+
linkedin: `https://linkedin.com/in/${username}`,
|
|
3151
|
+
reddit: `https://reddit.com/user/${username}`,
|
|
3152
|
+
youtube: `https://youtube.com/@${username}`,
|
|
3153
|
+
tiktok: `https://tiktok.com/@${username}`,
|
|
3154
|
+
producthunt: `https://producthunt.com/@${username}`,
|
|
3155
|
+
pypi: `https://pypi.org/user/${username}`
|
|
3156
|
+
};
|
|
3157
|
+
return urls[platform] || `https://${platform}.com/${username}`;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
// Get human-readable error reason per platform
|
|
3161
|
+
function getErrorReason(platform: string, error: string): string {
|
|
3162
|
+
const reasons = {
|
|
3163
|
+
instagram: "Instagram blocks automated checks - visit link to verify",
|
|
3164
|
+
linkedin: "LinkedIn requires login to check profiles",
|
|
3165
|
+
tiktok: "TikTok blocks automated checks - visit link to verify",
|
|
3166
|
+
twitter: error.includes("rate") ? "Twitter rate limited - try again in 15 min" : "Twitter check failed",
|
|
3167
|
+
github: error.includes("rate") ? "GitHub rate limited - try again later" : "GitHub check failed"
|
|
3168
|
+
};
|
|
3169
|
+
return reasons[platform] || `Check failed: ${error}`;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
function generateNextSteps(available, socialReport, suggestions) {
|
|
2404
3173
|
const steps = [];
|
|
3174
|
+
|
|
3175
|
+
// Domain recommendations
|
|
2405
3176
|
if (available.length > 0) {
|
|
2406
|
-
|
|
3177
|
+
const best = available.sort((a, b) => a.price - b.price)[0];
|
|
3178
|
+
steps.push(`1. Register ${best.domain} at ${best.registrar} ($${best.price}/yr)`);
|
|
2407
3179
|
} else if (suggestions.length > 0) {
|
|
2408
|
-
steps.push(`Consider alternative: ${suggestions[0].domain}`);
|
|
3180
|
+
steps.push(`1. Consider alternative: ${suggestions[0].domain} ($${suggestions[0].price}/yr)`);
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
// Social media recommendations
|
|
3184
|
+
if (socialReport.available.length > 0) {
|
|
3185
|
+
const platforms = socialReport.available.map(s => s.platform).join(", ");
|
|
3186
|
+
steps.push(`2. Secure username on: ${platforms}`);
|
|
2409
3187
|
}
|
|
2410
|
-
|
|
2411
|
-
if (
|
|
2412
|
-
steps.push(`
|
|
3188
|
+
|
|
3189
|
+
if (socialReport.needsManualCheck.length > 0) {
|
|
3190
|
+
steps.push(`3. Manually verify: ${socialReport.needsManualCheck.map(s => s.platform).join(", ")}`);
|
|
2413
3191
|
}
|
|
3192
|
+
|
|
2414
3193
|
return steps;
|
|
2415
3194
|
}
|
|
2416
3195
|
|
|
2417
|
-
//
|
|
2418
|
-
|
|
2419
|
-
|
|
3196
|
+
// Format report for user-friendly presentation
|
|
3197
|
+
function formatReportForPresentation(report: AcquisitionReport): string {
|
|
3198
|
+
const lines = [
|
|
3199
|
+
``,
|
|
3200
|
+
`${"ā".repeat(60)}`,
|
|
3201
|
+
` BRAND VALIDATION REPORT: ${report.brandName.toUpperCase()}`,
|
|
3202
|
+
`${"ā".repeat(60)}`,
|
|
3203
|
+
``,
|
|
3204
|
+
`š SUMMARY`,
|
|
3205
|
+
`${"ā".repeat(40)}`,
|
|
3206
|
+
` Domains: ${report.summary.available} available / ${report.summary.taken} taken`,
|
|
3207
|
+
` Socials: ${report.summary.socialsAvailable} available / ${report.summary.socialsTaken} taken`,
|
|
3208
|
+
` Unverified: ${report.summary.socialsUnverified} platforms need manual check`,
|
|
3209
|
+
``
|
|
3210
|
+
];
|
|
3211
|
+
|
|
3212
|
+
// Available domains section
|
|
3213
|
+
if (report.domains.available.length > 0) {
|
|
3214
|
+
lines.push(`ā
AVAILABLE DOMAINS`);
|
|
3215
|
+
lines.push(`${"ā".repeat(40)}`);
|
|
3216
|
+
report.domains.available.forEach(d => {
|
|
3217
|
+
lines.push(` ${d.domain.padEnd(25)} $${d.price}/yr (${d.registrar})`);
|
|
3218
|
+
});
|
|
3219
|
+
lines.push(``);
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// Alternatives section
|
|
3223
|
+
if (report.domains.alternatives.length > 0) {
|
|
3224
|
+
lines.push(`š” SUGGESTED ALTERNATIVES`);
|
|
3225
|
+
lines.push(`${"ā".repeat(40)}`);
|
|
3226
|
+
report.domains.alternatives.slice(0, 5).forEach(d => {
|
|
3227
|
+
lines.push(` ${d.domain.padEnd(25)} $${d.price}/yr (${d.variant})`);
|
|
3228
|
+
});
|
|
3229
|
+
lines.push(``);
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
// Social media section
|
|
3233
|
+
lines.push(`š± SOCIAL MEDIA`);
|
|
3234
|
+
lines.push(`${"ā".repeat(40)}`);
|
|
3235
|
+
report.socials.available.forEach(s => {
|
|
3236
|
+
lines.push(` ā
${s.platform.padEnd(12)} Available (${s.confidence} confidence)`);
|
|
3237
|
+
});
|
|
3238
|
+
report.socials.taken.forEach(s => {
|
|
3239
|
+
lines.push(` ā ${s.platform.padEnd(12)} Taken`);
|
|
3240
|
+
});
|
|
3241
|
+
report.socials.needsManualCheck.forEach(s => {
|
|
3242
|
+
lines.push(` ā ļø ${s.platform.padEnd(12)} ${s.reason}`);
|
|
3243
|
+
});
|
|
3244
|
+
lines.push(``);
|
|
3245
|
+
|
|
3246
|
+
// Next steps
|
|
3247
|
+
lines.push(`š NEXT STEPS`);
|
|
3248
|
+
lines.push(`${"ā".repeat(40)}`);
|
|
3249
|
+
report.nextSteps.forEach(step => lines.push(` ${step}`));
|
|
3250
|
+
lines.push(``);
|
|
3251
|
+
lines.push(`${"ā".repeat(60)}`);
|
|
3252
|
+
|
|
3253
|
+
return lines.join("\n");
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
// Usage example
|
|
3257
|
+
const report = await completeDomainAcquisition("techstartup");
|
|
3258
|
+
console.log(report.presentation);
|
|
3259
|
+
|
|
3260
|
+
// Example output:
|
|
3261
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3262
|
+
// BRAND VALIDATION REPORT: TECHSTARTUP
|
|
3263
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3264
|
+
//
|
|
3265
|
+
// š SUMMARY
|
|
3266
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3267
|
+
// Domains: 3 available / 2 taken
|
|
3268
|
+
// Socials: 2 available / 1 taken
|
|
3269
|
+
// Unverified: 2 platforms need manual check
|
|
3270
|
+
//
|
|
3271
|
+
// ā
AVAILABLE DOMAINS
|
|
3272
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3273
|
+
// techstartup.io $29.88/yr (porkbun)
|
|
3274
|
+
// techstartup.dev $10.18/yr (porkbun)
|
|
3275
|
+
// techstartup.app $12.00/yr (porkbun)
|
|
3276
|
+
//
|
|
3277
|
+
// š” SUGGESTED ALTERNATIVES
|
|
3278
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3279
|
+
// gettechstartup.com $8.95/yr (prefixes)
|
|
3280
|
+
// techstartupnow.com $8.95/yr (suffixes)
|
|
3281
|
+
//
|
|
3282
|
+
// š± SOCIAL MEDIA
|
|
3283
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3284
|
+
// ā
github Available (high confidence)
|
|
3285
|
+
// ā
npm Available (high confidence)
|
|
3286
|
+
// ā twitter Taken
|
|
3287
|
+
// ā ļø instagram Instagram blocks automated checks - visit link to verify
|
|
3288
|
+
// ā ļø linkedin LinkedIn requires login to check profiles
|
|
3289
|
+
//
|
|
3290
|
+
// š NEXT STEPS
|
|
3291
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3292
|
+
// 1. Register techstartup.dev at porkbun ($10.18/yr)
|
|
3293
|
+
// 2. Secure username on: github, npm
|
|
3294
|
+
// 3. Manually verify: instagram, linkedin
|
|
3295
|
+
//
|
|
3296
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
2420
3297
|
```
|
|
2421
3298
|
|
|
2422
3299
|
### Workflow 2: Domain Suggestion When Preferred Name is Taken
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domain-search-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
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",
|