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.
Files changed (2) hide show
  1. package/README.md +935 -58
  2. 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
- { "platform": "github", "available": true, "confidence": "high" },
1218
- { "platform": "twitter", "available": false, "confidence": "high" },
1219
- { "platform": "instagram", "available": true, "confidence": "low" }
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
- ```bash
1443
- # Porkbun (Recommended - Free, Fast, Reliable)
1444
- # Visit: https://porkbun.com/account/api
1445
- # Click "Create API Key" → Copy both API Key and Secret Key
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
- # Namecheap (Optional - Requires IP Whitelist)
1448
- # Visit: https://ap.www.namecheap.com/settings/tools/apiaccess
1449
- # Enable API → Add your IP to whitelist → Copy credentials
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=pk1_abc123def456...
1465
- PORKBUN_SECRET_KEY=sk1_xyz789ghi012...
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 handles scenarios where domains are available on some registrars but not others, or when some checks succeed while others fail:
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
- async function completeDomainAcquisition(brandName: string) {
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
- checkSocials({
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 - some TLDs available, some taken
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; // Exponential backoff
3010
+ delay *= 2;
2338
3011
  }
2339
3012
  }
2340
3013
  }
2341
3014
 
2342
- // Step 4: If preferred .com is taken, generate alternatives
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: Compile comprehensive report
2364
- return {
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: socialResults.results.filter(r => r.available).length
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 => s.domain)
2382
- },
2383
- socials: {
2384
- available: socialResults.results
2385
- .filter(r => r.available && r.confidence !== "low")
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
- bestPrice: p.best_first_year,
2397
- recommendation: p.recommendation
3068
+ bestRegistrar: p.best_first_year?.registrar,
3069
+ price: p.best_first_year?.price
2398
3070
  })),
2399
- nextSteps: generateNextSteps(available, socialResults, suggestions)
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
- function generateNextSteps(available, socialResults, suggestions) {
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
- steps.push(`Register ${available[0].domain} at ${available[0].registrar}`);
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
- const availableSocials = socialResults.results.filter(r => r.available);
2411
- if (availableSocials.length > 0) {
2412
- steps.push(`Secure username on: ${availableSocials.map(r => r.platform).join(', ')}`);
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
- // Usage
2418
- const acquisition = await completeDomainAcquisition("techstartup");
2419
- // Returns comprehensive report with partial availability handled
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.4",
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",