domain-search-mcp 1.1.5 → 1.2.1

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