domain-search-mcp 1.1.5 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +394 -1
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -89,6 +89,7 @@ nano .env
89
89
  |-----------|----------|---------|-------|
90
90
  | **Porkbun** | JSON | Free | Fast, includes WHOIS privacy |
91
91
  | **Namecheap** | XML | Free | Requires IP whitelist |
92
+ | **GoDaddy** | MCP | Free | Via GoDaddy MCP server (no API key needed) |
92
93
 
93
94
  ### Fallback Protocols
94
95
 
@@ -257,6 +258,7 @@ AI-powered domain suggestions using semantic analysis:
257
258
  ```
258
259
 
259
260
  **Features:**
261
+ - **Dual-Source Suggestions**: Combines semantic analysis + GoDaddy AI recommendations
260
262
  - Understands natural language queries ("coffee shop in seattle")
261
263
  - Auto-detects industry for contextual suggestions
262
264
  - Generates portmanteau/blended names
@@ -404,7 +406,8 @@ domain-search-mcp/
404
406
  │ ├── registrars/ # Registrar adapters
405
407
  │ │ ├── base.ts
406
408
  │ │ ├── porkbun.ts
407
- │ │ └── namecheap.ts
409
+ │ │ ├── namecheap.ts
410
+ │ │ └── godaddy-mcp.ts # GoDaddy via MCP server
408
411
  │ ├── fallbacks/ # RDAP and WHOIS fallbacks
409
412
  │ │ ├── rdap.ts
410
413
  │ │ └── whois.ts
@@ -451,6 +454,396 @@ The server provides user-friendly error messages with suggested actions:
451
454
  | `NO_SOURCE_AVAILABLE` | All sources failed | Yes |
452
455
  | `TIMEOUT` | Request timed out | Yes |
453
456
 
457
+ ## Rate Limiting & Performance Optimization
458
+
459
+ ### Understanding WHOIS/RDAP Rate Limits
460
+
461
+ When operating without API keys, Domain Search MCP uses WHOIS and RDAP protocols as fallbacks. These protocols have important rate limiting considerations:
462
+
463
+ | Protocol | Typical Rate Limit | Behavior When Exceeded |
464
+ |----------|-------------------|------------------------|
465
+ | **RDAP** | 10-50 req/min per TLD | Returns 429 or connection refused |
466
+ | **WHOIS** | 5-20 req/min per server | Connection timeout or ban |
467
+
468
+ ### Automatic Rate Limit Handling
469
+
470
+ The server implements intelligent rate limit handling:
471
+
472
+ ```typescript
473
+ // Built-in protections
474
+ {
475
+ // Automatic exponential backoff
476
+ retryStrategy: {
477
+ initialDelay: 1000, // Start with 1 second
478
+ maxDelay: 30000, // Cap at 30 seconds
479
+ backoffMultiplier: 2, // Double each retry
480
+ maxRetries: 3 // Give up after 3 attempts
481
+ },
482
+
483
+ // Per-source rate limiting
484
+ rateLimits: {
485
+ rdap: { requestsPerMinute: 30, burstLimit: 5 },
486
+ whois: { requestsPerMinute: 10, burstLimit: 3 }
487
+ }
488
+ }
489
+ ```
490
+
491
+ ### Strategies for High-Volume Searches
492
+
493
+ When performing bulk searches without API keys, use these optimization strategies:
494
+
495
+ #### 1. Use Caching Effectively
496
+
497
+ ```typescript
498
+ // Results are cached automatically
499
+ // - Availability: 5 minutes (CACHE_TTL_AVAILABILITY)
500
+ // - Pricing: 1 hour (CACHE_TTL_PRICING)
501
+ // - TLD info: 24 hours
502
+
503
+ // Subsequent checks for the same domain are instant
504
+ const first = await searchDomain("example.com"); // API call
505
+ const second = await searchDomain("example.com"); // Cache hit (no API call)
506
+ ```
507
+
508
+ #### 2. Batch Domains by TLD
509
+
510
+ ```typescript
511
+ // GOOD: Group by TLD to minimize server switches
512
+ const comDomains = ["app1", "app2", "app3"];
513
+ const ioDomains = ["startup1", "startup2"];
514
+
515
+ await bulkSearch({ domains: comDomains, tld: "com" }); // One .com server
516
+ await bulkSearch({ domains: ioDomains, tld: "io" }); // One .io server
517
+
518
+ // BAD: Mixed TLDs cause more server connections
519
+ await Promise.all([
520
+ searchDomain("app1.com"),
521
+ searchDomain("startup1.io"),
522
+ searchDomain("app2.com"), // Back to .com server
523
+ ]);
524
+ ```
525
+
526
+ #### 3. Control Concurrency
527
+
528
+ ```typescript
529
+ // bulk_search has built-in concurrency control
530
+ {
531
+ "domains": ["name1", "name2", ..., "name50"],
532
+ "tld": "com",
533
+ "concurrency": 5 // Process 5 at a time (default)
534
+ }
535
+ ```
536
+
537
+ #### 4. Implement Request Queuing
538
+
539
+ For very high volumes, implement client-side queuing:
540
+
541
+ ```typescript
542
+ // Example: Rate-limited queue for 100+ domains
543
+ async function queuedBulkSearch(domains: string[], tld: string) {
544
+ const BATCH_SIZE = 25;
545
+ const DELAY_BETWEEN_BATCHES = 5000; // 5 seconds
546
+
547
+ const results = [];
548
+ for (let i = 0; i < domains.length; i += BATCH_SIZE) {
549
+ const batch = domains.slice(i, i + BATCH_SIZE);
550
+ const batchResults = await bulkSearch({ domains: batch, tld });
551
+ results.push(...batchResults.results);
552
+
553
+ // Wait between batches to avoid rate limits
554
+ if (i + BATCH_SIZE < domains.length) {
555
+ await new Promise(r => setTimeout(r, DELAY_BETWEEN_BATCHES));
556
+ }
557
+ }
558
+ return results;
559
+ }
560
+ ```
561
+
562
+ ### Why API Keys Are Recommended
563
+
564
+ | Feature | Without API Keys | With API Keys | With GoDaddy MCP |
565
+ |---------|-----------------|---------------|------------------|
566
+ | Speed | 2-5 sec/domain | 100-200ms/domain | 200-500ms/domain |
567
+ | Rate Limits | Strict (10-50/min) | Generous (1000+/min) | Moderate |
568
+ | Pricing Data | Not available | Full pricing info | Full pricing info |
569
+ | Reliability | Varies by server | Consistent | Consistent |
570
+ | Bulk Operations | Limited to ~50/batch | Up to 100/batch | Supported |
571
+ | Setup Required | None | API key setup | MCP server only |
572
+
573
+ > **Tip**: GoDaddy MCP provides a middle ground - no API key needed but still gives pricing data!
574
+
575
+ ### Handling Rate Limit Errors
576
+
577
+ ```typescript
578
+ // The server returns structured errors for rate limits
579
+ {
580
+ "error": true,
581
+ "code": "RATE_LIMIT",
582
+ "message": "WHOIS rate limit exceeded for .com TLD",
583
+ "retryable": true,
584
+ "retryAfter": 30, // Seconds to wait
585
+ "suggestedAction": "Wait 30 seconds or use Porkbun API for faster results"
586
+ }
587
+
588
+ // Your code should handle these gracefully
589
+ try {
590
+ const result = await searchDomain("example.com");
591
+ } catch (error) {
592
+ if (error.code === "RATE_LIMIT" && error.retryable) {
593
+ await sleep(error.retryAfter * 1000);
594
+ return searchDomain("example.com"); // Retry
595
+ }
596
+ throw error;
597
+ }
598
+ ```
599
+
600
+ ## Workflow Examples
601
+
602
+ ### Workflow 1: Domain Suggestion When Preferred Name is Taken
603
+
604
+ When a user's preferred domain is unavailable, use `suggest_domains` to find alternatives:
605
+
606
+ ```typescript
607
+ // Step 1: Check if preferred domain is available
608
+ const preferred = await searchDomain({
609
+ domain_name: "techapp",
610
+ tlds: ["com"]
611
+ });
612
+
613
+ // Step 2: If taken, generate suggestions
614
+ if (!preferred.results[0].available) {
615
+ const suggestions = await suggestDomains({
616
+ base_name: "techapp",
617
+ tld: "com",
618
+ max_suggestions: 10,
619
+ variants: ["prefixes", "suffixes", "hyphen", "abbreviations"]
620
+ });
621
+
622
+ // Step 3: Present alternatives to user
623
+ console.log("techapp.com is taken. Available alternatives:");
624
+ suggestions.suggestions.forEach(s => {
625
+ console.log(` ${s.domain} - $${s.price_first_year}/year`);
626
+ });
627
+
628
+ // Output:
629
+ // techapp.com is taken. Available alternatives:
630
+ // gettechapp.com - $8.95/year
631
+ // techappnow.com - $8.95/year
632
+ // techapp-io.com - $8.95/year
633
+ // mytechapp.com - $8.95/year
634
+ }
635
+ ```
636
+
637
+ ### Workflow 2: Simultaneous Social Media Verification
638
+
639
+ Check username availability across multiple platforms at once:
640
+
641
+ ```typescript
642
+ // Check if "myproject" is available on GitHub, Twitter, and Instagram
643
+ const socialCheck = await checkSocials({
644
+ name: "myproject",
645
+ platforms: ["github", "twitter", "instagram"]
646
+ });
647
+
648
+ // Handle results by confidence level
649
+ const highConfidence = socialCheck.results.filter(r => r.confidence === "high");
650
+ const mediumConfidence = socialCheck.results.filter(r => r.confidence === "medium");
651
+ const lowConfidence = socialCheck.results.filter(r => r.confidence === "low");
652
+
653
+ // Report findings
654
+ console.log("Verified available:", highConfidence.filter(r => r.available).map(r => r.platform));
655
+ console.log("Likely available:", mediumConfidence.filter(r => r.available).map(r => r.platform));
656
+ console.log("Check manually:", lowConfidence.map(r => r.platform));
657
+
658
+ // Output:
659
+ // Verified available: ["github"]
660
+ // Likely available: ["twitter"]
661
+ // Check manually: ["instagram"]
662
+ ```
663
+
664
+ ### Workflow 3: Complete Brand Validation Pipeline
665
+
666
+ Comprehensive brand name validation across domains and social media:
667
+
668
+ ```typescript
669
+ async function validateBrandName(brandName: string) {
670
+ // Run domain and social checks in parallel
671
+ const [domainResults, socialResults] = await Promise.all([
672
+ searchDomain({
673
+ domain_name: brandName,
674
+ tlds: ["com", "io", "dev", "app"]
675
+ }),
676
+ checkSocials({
677
+ name: brandName,
678
+ platforms: ["github", "twitter", "instagram", "linkedin"]
679
+ })
680
+ ]);
681
+
682
+ // Analyze domain availability
683
+ const availableDomains = domainResults.results.filter(r => r.available);
684
+ const bestDomain = availableDomains.sort((a, b) =>
685
+ a.price_first_year - b.price_first_year
686
+ )[0];
687
+
688
+ // Analyze social availability
689
+ const availableSocials = socialResults.results.filter(r =>
690
+ r.available && r.confidence !== "low"
691
+ );
692
+
693
+ // Calculate brand score
694
+ const domainScore = availableDomains.length / domainResults.results.length;
695
+ const socialScore = availableSocials.length / socialResults.results.length;
696
+ const overallScore = (domainScore + socialScore) / 2;
697
+
698
+ return {
699
+ brandName,
700
+ overallScore: Math.round(overallScore * 100),
701
+ domains: {
702
+ available: availableDomains.map(d => d.domain),
703
+ bestOption: bestDomain?.domain,
704
+ bestPrice: bestDomain?.price_first_year
705
+ },
706
+ socials: {
707
+ available: availableSocials.map(s => s.platform),
708
+ needsManualCheck: socialResults.results
709
+ .filter(r => r.confidence === "low")
710
+ .map(s => s.platform)
711
+ },
712
+ recommendation: overallScore > 0.7
713
+ ? "Strong brand availability - proceed with registration"
714
+ : overallScore > 0.4
715
+ ? "Partial availability - consider alternatives"
716
+ : "Limited availability - try a different name"
717
+ };
718
+ }
719
+
720
+ // Usage
721
+ const result = await validateBrandName("vibecoding");
722
+ // Output:
723
+ // {
724
+ // brandName: "vibecoding",
725
+ // overallScore: 85,
726
+ // domains: {
727
+ // available: ["vibecoding.com", "vibecoding.io", "vibecoding.dev"],
728
+ // bestOption: "vibecoding.com",
729
+ // bestPrice: 8.95
730
+ // },
731
+ // socials: {
732
+ // available: ["github", "twitter"],
733
+ // needsManualCheck: ["instagram", "linkedin"]
734
+ // },
735
+ // recommendation: "Strong brand availability - proceed with registration"
736
+ // }
737
+ ```
738
+
739
+ ### Workflow 4: Handling Partial Availability Scenarios
740
+
741
+ When some sources succeed and others fail:
742
+
743
+ ```typescript
744
+ async function robustDomainSearch(domainName: string, tlds: string[]) {
745
+ const results = await searchDomain({ domain_name: domainName, tlds });
746
+
747
+ // Separate successful and failed checks
748
+ const successful = results.results.filter(r => !r.error);
749
+ const failed = results.results.filter(r => r.error);
750
+
751
+ // Handle partial failures
752
+ if (failed.length > 0) {
753
+ console.log(`Warning: ${failed.length} TLDs could not be checked:`);
754
+ failed.forEach(f => console.log(` ${f.domain}: ${f.error}`));
755
+
756
+ // Retry failed ones with exponential backoff
757
+ for (const failedResult of failed) {
758
+ const tld = failedResult.domain.split('.').pop();
759
+ let retryDelay = 1000;
760
+
761
+ for (let attempt = 1; attempt <= 3; attempt++) {
762
+ await new Promise(r => setTimeout(r, retryDelay));
763
+ try {
764
+ const retry = await searchDomain({
765
+ domain_name: domainName,
766
+ tlds: [tld]
767
+ });
768
+ if (!retry.results[0].error) {
769
+ successful.push(retry.results[0]);
770
+ break;
771
+ }
772
+ } catch (e) {
773
+ retryDelay *= 2; // Exponential backoff
774
+ }
775
+ }
776
+ }
777
+ }
778
+
779
+ return {
780
+ results: successful,
781
+ partialFailure: failed.length > 0,
782
+ failedTlds: failed.map(f => f.domain.split('.').pop())
783
+ };
784
+ }
785
+ ```
786
+
787
+ ### Workflow 5: Domain Research Pipeline
788
+
789
+ Comprehensive domain research combining multiple tools:
790
+
791
+ ```typescript
792
+ async function domainResearchPipeline(businessIdea: string) {
793
+ // Step 1: Generate smart suggestions from business description
794
+ const suggestions = await suggestDomainsSmart({
795
+ query: businessIdea,
796
+ tld: "com",
797
+ style: "brandable",
798
+ max_suggestions: 15
799
+ });
800
+
801
+ // Step 2: Get TLD information for context
802
+ const tldInfo = await getTldInfo({ tld: "com", detailed: true });
803
+
804
+ // Step 3: For top suggestions, compare registrar pricing
805
+ const topDomains = suggestions.results.available.slice(0, 5);
806
+ const priceComparisons = await Promise.all(
807
+ topDomains.map(d => compareRegistrars({
808
+ domain: d.domain.replace('.com', ''),
809
+ tld: "com"
810
+ }))
811
+ );
812
+
813
+ // Step 4: Check social media for top picks
814
+ const socialChecks = await Promise.all(
815
+ topDomains.slice(0, 3).map(d => {
816
+ const name = d.domain.replace('.com', '');
817
+ return checkSocials({
818
+ name,
819
+ platforms: ["github", "twitter", "npm"]
820
+ });
821
+ })
822
+ );
823
+
824
+ // Compile research report
825
+ return {
826
+ businessIdea,
827
+ tldContext: {
828
+ description: tldInfo.description,
829
+ priceRange: tldInfo.price_range,
830
+ recommendation: tldInfo.recommendation
831
+ },
832
+ topRecommendations: topDomains.map((d, i) => ({
833
+ domain: d.domain,
834
+ price: d.price_first_year,
835
+ bestRegistrar: priceComparisons[i]?.best_first_year?.registrar,
836
+ socialAvailability: socialChecks[i]?.summary
837
+ })),
838
+ allSuggestions: suggestions.results.available,
839
+ relatedTerms: suggestions.related_terms
840
+ };
841
+ }
842
+
843
+ // Usage
844
+ const research = await domainResearchPipeline("ai-powered code review tool");
845
+ ```
846
+
454
847
  ## Security
455
848
 
456
849
  - API keys are never logged (automatic secret masking)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domain-search-mcp",
3
- "version": "1.1.5",
3
+ "version": "1.2.0",
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",