domain-search-mcp 1.0.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 (151) hide show
  1. package/.env.example +52 -0
  2. package/Dockerfile +15 -0
  3. package/LICENSE +21 -0
  4. package/README.md +426 -0
  5. package/SECURITY.md +252 -0
  6. package/dist/config.d.ts +25 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +117 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/fallbacks/index.d.ts +6 -0
  11. package/dist/fallbacks/index.d.ts.map +1 -0
  12. package/dist/fallbacks/index.js +14 -0
  13. package/dist/fallbacks/index.js.map +1 -0
  14. package/dist/fallbacks/rdap.d.ts +18 -0
  15. package/dist/fallbacks/rdap.d.ts.map +1 -0
  16. package/dist/fallbacks/rdap.js +339 -0
  17. package/dist/fallbacks/rdap.js.map +1 -0
  18. package/dist/fallbacks/whois.d.ts +27 -0
  19. package/dist/fallbacks/whois.d.ts.map +1 -0
  20. package/dist/fallbacks/whois.js +219 -0
  21. package/dist/fallbacks/whois.js.map +1 -0
  22. package/dist/registrars/base.d.ts +89 -0
  23. package/dist/registrars/base.d.ts.map +1 -0
  24. package/dist/registrars/base.js +203 -0
  25. package/dist/registrars/base.js.map +1 -0
  26. package/dist/registrars/index.d.ts +7 -0
  27. package/dist/registrars/index.d.ts.map +1 -0
  28. package/dist/registrars/index.js +15 -0
  29. package/dist/registrars/index.js.map +1 -0
  30. package/dist/registrars/namecheap.d.ts +69 -0
  31. package/dist/registrars/namecheap.d.ts.map +1 -0
  32. package/dist/registrars/namecheap.js +307 -0
  33. package/dist/registrars/namecheap.js.map +1 -0
  34. package/dist/registrars/porkbun.d.ts +63 -0
  35. package/dist/registrars/porkbun.d.ts.map +1 -0
  36. package/dist/registrars/porkbun.js +299 -0
  37. package/dist/registrars/porkbun.js.map +1 -0
  38. package/dist/server.d.ts +19 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +209 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/services/domain-search.d.ts +40 -0
  43. package/dist/services/domain-search.d.ts.map +1 -0
  44. package/dist/services/domain-search.js +438 -0
  45. package/dist/services/domain-search.js.map +1 -0
  46. package/dist/services/index.d.ts +5 -0
  47. package/dist/services/index.d.ts.map +1 -0
  48. package/dist/services/index.js +11 -0
  49. package/dist/services/index.js.map +1 -0
  50. package/dist/tools/bulk_search.d.ts +72 -0
  51. package/dist/tools/bulk_search.d.ts.map +1 -0
  52. package/dist/tools/bulk_search.js +108 -0
  53. package/dist/tools/bulk_search.js.map +1 -0
  54. package/dist/tools/check_socials.d.ts +71 -0
  55. package/dist/tools/check_socials.d.ts.map +1 -0
  56. package/dist/tools/check_socials.js +357 -0
  57. package/dist/tools/check_socials.js.map +1 -0
  58. package/dist/tools/compare_registrars.d.ts +80 -0
  59. package/dist/tools/compare_registrars.d.ts.map +1 -0
  60. package/dist/tools/compare_registrars.js +116 -0
  61. package/dist/tools/compare_registrars.js.map +1 -0
  62. package/dist/tools/index.d.ts +10 -0
  63. package/dist/tools/index.d.ts.map +1 -0
  64. package/dist/tools/index.js +31 -0
  65. package/dist/tools/index.js.map +1 -0
  66. package/dist/tools/search_domain.d.ts +61 -0
  67. package/dist/tools/search_domain.d.ts.map +1 -0
  68. package/dist/tools/search_domain.js +81 -0
  69. package/dist/tools/search_domain.js.map +1 -0
  70. package/dist/tools/suggest_domains.d.ts +82 -0
  71. package/dist/tools/suggest_domains.d.ts.map +1 -0
  72. package/dist/tools/suggest_domains.js +227 -0
  73. package/dist/tools/suggest_domains.js.map +1 -0
  74. package/dist/tools/tld_info.d.ts +56 -0
  75. package/dist/tools/tld_info.d.ts.map +1 -0
  76. package/dist/tools/tld_info.js +273 -0
  77. package/dist/tools/tld_info.js.map +1 -0
  78. package/dist/types.d.ts +193 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +9 -0
  81. package/dist/types.js.map +1 -0
  82. package/dist/utils/cache.d.ts +81 -0
  83. package/dist/utils/cache.d.ts.map +1 -0
  84. package/dist/utils/cache.js +192 -0
  85. package/dist/utils/cache.js.map +1 -0
  86. package/dist/utils/errors.d.ts +87 -0
  87. package/dist/utils/errors.d.ts.map +1 -0
  88. package/dist/utils/errors.js +191 -0
  89. package/dist/utils/errors.js.map +1 -0
  90. package/dist/utils/index.d.ts +8 -0
  91. package/dist/utils/index.d.ts.map +1 -0
  92. package/dist/utils/index.js +24 -0
  93. package/dist/utils/index.js.map +1 -0
  94. package/dist/utils/logger.d.ts +27 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +132 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/premium-analyzer.d.ts +33 -0
  99. package/dist/utils/premium-analyzer.d.ts.map +1 -0
  100. package/dist/utils/premium-analyzer.js +273 -0
  101. package/dist/utils/premium-analyzer.js.map +1 -0
  102. package/dist/utils/validators.d.ts +53 -0
  103. package/dist/utils/validators.d.ts.map +1 -0
  104. package/dist/utils/validators.js +159 -0
  105. package/dist/utils/validators.js.map +1 -0
  106. package/docs/marketing/devto-post.md +135 -0
  107. package/docs/marketing/hackernews.md +42 -0
  108. package/docs/marketing/producthunt.md +109 -0
  109. package/docs/marketing/reddit-post.md +59 -0
  110. package/docs/marketing/twitter-thread.md +105 -0
  111. package/examples/bulk-search-50-domains.ts +131 -0
  112. package/examples/cli-interactive.ts +280 -0
  113. package/examples/compare-registrars.ts +78 -0
  114. package/examples/search-single-domain.ts +54 -0
  115. package/examples/suggest-names.ts +110 -0
  116. package/glama.json +6 -0
  117. package/jest.config.js +35 -0
  118. package/package.json +62 -0
  119. package/smithery.yaml +36 -0
  120. package/src/config.ts +121 -0
  121. package/src/fallbacks/index.ts +6 -0
  122. package/src/fallbacks/rdap.ts +407 -0
  123. package/src/fallbacks/whois.ts +250 -0
  124. package/src/registrars/base.ts +264 -0
  125. package/src/registrars/index.ts +7 -0
  126. package/src/registrars/namecheap.ts +378 -0
  127. package/src/registrars/porkbun.ts +380 -0
  128. package/src/server.ts +276 -0
  129. package/src/services/domain-search.ts +567 -0
  130. package/src/services/index.ts +9 -0
  131. package/src/tools/bulk_search.ts +142 -0
  132. package/src/tools/check_socials.ts +467 -0
  133. package/src/tools/compare_registrars.ts +162 -0
  134. package/src/tools/index.ts +45 -0
  135. package/src/tools/search_domain.ts +93 -0
  136. package/src/tools/suggest_domains.ts +284 -0
  137. package/src/tools/tld_info.ts +294 -0
  138. package/src/types.ts +289 -0
  139. package/src/utils/cache.ts +238 -0
  140. package/src/utils/errors.ts +262 -0
  141. package/src/utils/index.ts +8 -0
  142. package/src/utils/logger.ts +162 -0
  143. package/src/utils/premium-analyzer.ts +303 -0
  144. package/src/utils/validators.ts +193 -0
  145. package/tests/premium-analyzer.test.ts +310 -0
  146. package/tests/unit/cache.test.ts +123 -0
  147. package/tests/unit/errors.test.ts +190 -0
  148. package/tests/unit/tld-info.test.ts +62 -0
  149. package/tests/unit/tools.test.ts +200 -0
  150. package/tests/unit/validators.test.ts +146 -0
  151. package/tsconfig.json +25 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Example: Bulk Search 50 Domains
3
+ *
4
+ * This example demonstrates the bulk_search tool for checking
5
+ * many domains at once efficiently.
6
+ */
7
+
8
+ import { bulkSearch } from '../src/services/domain-search.js';
9
+
10
+ // Sample domain names to check
11
+ const DOMAIN_NAMES = [
12
+ 'vibecoding',
13
+ 'codevibes',
14
+ 'devmagic',
15
+ 'buildfast',
16
+ 'shipit',
17
+ 'hackflow',
18
+ 'codealchemy',
19
+ 'pixelperfect',
20
+ 'bytesize',
21
+ 'stackflow',
22
+ 'gitflow',
23
+ 'deployfast',
24
+ 'cloudnine',
25
+ 'serverless',
26
+ 'microstack',
27
+ 'apicraft',
28
+ 'dataflow',
29
+ 'mlops',
30
+ 'aicraft',
31
+ 'neuralnet',
32
+ 'tensorflow',
33
+ 'deeplearn',
34
+ 'automate',
35
+ 'botcraft',
36
+ 'chatops',
37
+ 'devtools',
38
+ 'codebase',
39
+ 'repocraft',
40
+ 'gitmagic',
41
+ 'versionctl',
42
+ 'cicdpipe',
43
+ 'testcraft',
44
+ 'debugflow',
45
+ 'logstream',
46
+ 'metrics',
47
+ 'dashcraft',
48
+ 'uimagic',
49
+ 'csscraft',
50
+ 'reactflow',
51
+ 'vuemagic',
52
+ 'sveltekit',
53
+ 'nextcraft',
54
+ 'nuxtmagic',
55
+ 'astrosite',
56
+ 'remixapp',
57
+ 'solidstart',
58
+ 'qwiksite',
59
+ 'htmxcraft',
60
+ 'alpinejs',
61
+ 'tailwindui',
62
+ ];
63
+
64
+ async function main() {
65
+ console.log('Domain Search MCP - Bulk Search Example\n');
66
+ console.log('=======================================\n');
67
+
68
+ console.log(`Checking ${DOMAIN_NAMES.length} domains across .io TLD...\n`);
69
+
70
+ try {
71
+ const startTime = Date.now();
72
+ const result = await bulkSearch(DOMAIN_NAMES, 'io');
73
+ const duration = Date.now() - startTime;
74
+
75
+ // Display summary
76
+ console.log('Summary:');
77
+ console.log('--------');
78
+ console.log(`Total checked: ${result.summary.total}`);
79
+ console.log(`Available: ${result.summary.available}`);
80
+ console.log(`Taken: ${result.summary.taken}`);
81
+ console.log(`Errors: ${result.summary.errors}`);
82
+ console.log(`Duration: ${duration}ms\n`);
83
+
84
+ // Display available domains
85
+ const available = result.results.filter((r) => r.available);
86
+ if (available.length > 0) {
87
+ console.log('Available Domains:');
88
+ console.log('------------------');
89
+ for (const domain of available.slice(0, 10)) {
90
+ const price = domain.price_first_year
91
+ ? `$${domain.price_first_year}/year`
92
+ : 'Price unknown';
93
+ console.log(` ${domain.domain} - ${price}`);
94
+ }
95
+ if (available.length > 10) {
96
+ console.log(` ... and ${available.length - 10} more`);
97
+ }
98
+ }
99
+
100
+ // Display taken domains
101
+ const taken = result.results.filter((r) => !r.available && !r.error);
102
+ if (taken.length > 0) {
103
+ console.log('\nTaken Domains:');
104
+ console.log('--------------');
105
+ for (const domain of taken.slice(0, 5)) {
106
+ console.log(` ${domain.domain}`);
107
+ }
108
+ if (taken.length > 5) {
109
+ console.log(` ... and ${taken.length - 5} more`);
110
+ }
111
+ }
112
+
113
+ // Display insights
114
+ console.log('\nInsights:');
115
+ console.log('---------');
116
+ for (const insight of result.insights) {
117
+ console.log(insight);
118
+ }
119
+
120
+ // Display next steps
121
+ console.log('\nNext Steps:');
122
+ console.log('-----------');
123
+ for (const step of result.next_steps) {
124
+ console.log(`- ${step}`);
125
+ }
126
+ } catch (error) {
127
+ console.error('Error:', error instanceof Error ? error.message : error);
128
+ }
129
+ }
130
+
131
+ main();
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Example: Interactive CLI
3
+ *
4
+ * This example demonstrates an interactive command-line interface
5
+ * for domain searching using readline.
6
+ */
7
+
8
+ import * as readline from 'readline';
9
+ import {
10
+ searchDomain,
11
+ bulkSearch,
12
+ compareRegistrars,
13
+ suggestDomains,
14
+ getTldInfo,
15
+ checkSocials,
16
+ } from '../src/services/domain-search.js';
17
+
18
+ const rl = readline.createInterface({
19
+ input: process.stdin,
20
+ output: process.stdout,
21
+ });
22
+
23
+ function prompt(question: string): Promise<string> {
24
+ return new Promise((resolve) => {
25
+ rl.question(question, (answer) => {
26
+ resolve(answer.trim());
27
+ });
28
+ });
29
+ }
30
+
31
+ function printHelp() {
32
+ console.log(`
33
+ Available Commands:
34
+ -------------------
35
+ search <name> [tlds] - Search domain across TLDs (default: com,io,dev)
36
+ bulk <names> [tld] - Check multiple names (comma-separated)
37
+ compare <name> <tld> - Compare registrar prices
38
+ suggest <name> [tld] - Get available variations
39
+ tld <tld> - Get TLD information
40
+ socials <name> - Check social media availability
41
+ help - Show this help
42
+ quit - Exit
43
+
44
+ Examples:
45
+ search vibecoding
46
+ search vibecoding com,io,dev,app
47
+ bulk vibecoding,coolapp,mysite io
48
+ compare vibecoding com
49
+ suggest vibecoding com
50
+ tld dev
51
+ socials vibecoding
52
+ `);
53
+ }
54
+
55
+ async function handleSearch(args: string[]) {
56
+ const name = args[0];
57
+ const tlds = args[1]?.split(',') || ['com', 'io', 'dev'];
58
+
59
+ if (!name) {
60
+ console.log('Usage: search <name> [tlds]');
61
+ return;
62
+ }
63
+
64
+ console.log(`\nSearching for ${name} across ${tlds.join(', ')}...\n`);
65
+
66
+ try {
67
+ const result = await searchDomain(name, tlds);
68
+
69
+ for (const domain of result.results) {
70
+ const status = domain.available ? 'Available' : 'Taken';
71
+ const price = domain.price_first_year
72
+ ? `$${domain.price_first_year}/year`
73
+ : '';
74
+ console.log(` ${domain.domain}: ${status} ${price}`);
75
+ }
76
+
77
+ console.log('\nInsights:');
78
+ for (const insight of result.insights) {
79
+ console.log(` ${insight}`);
80
+ }
81
+ } catch (error) {
82
+ console.error('Error:', error instanceof Error ? error.message : error);
83
+ }
84
+ }
85
+
86
+ async function handleBulk(args: string[]) {
87
+ const names = args[0]?.split(',') || [];
88
+ const tld = args[1] || 'com';
89
+
90
+ if (names.length === 0) {
91
+ console.log('Usage: bulk <names> [tld]');
92
+ return;
93
+ }
94
+
95
+ console.log(`\nChecking ${names.length} names for .${tld}...\n`);
96
+
97
+ try {
98
+ const result = await bulkSearch(names, tld);
99
+
100
+ console.log(`Available: ${result.summary.available}/${result.summary.total}`);
101
+
102
+ const available = result.results.filter((r) => r.available);
103
+ if (available.length > 0) {
104
+ console.log('\nAvailable:');
105
+ for (const d of available) {
106
+ const price = d.price_first_year ? `$${d.price_first_year}/year` : '';
107
+ console.log(` ${d.domain} ${price}`);
108
+ }
109
+ }
110
+ } catch (error) {
111
+ console.error('Error:', error instanceof Error ? error.message : error);
112
+ }
113
+ }
114
+
115
+ async function handleCompare(args: string[]) {
116
+ const name = args[0];
117
+ const tld = args[1] || 'com';
118
+
119
+ if (!name) {
120
+ console.log('Usage: compare <name> <tld>');
121
+ return;
122
+ }
123
+
124
+ console.log(`\nComparing prices for ${name}.${tld}...\n`);
125
+
126
+ try {
127
+ const result = await compareRegistrars(name, tld);
128
+
129
+ for (const price of result.prices) {
130
+ const first = price.price_first_year ? `$${price.price_first_year}` : 'N/A';
131
+ console.log(` ${price.registrar}: ${first}/year`);
132
+ }
133
+
134
+ if (result.recommendation) {
135
+ console.log(`\nRecommendation: ${result.recommendation}`);
136
+ }
137
+ } catch (error) {
138
+ console.error('Error:', error instanceof Error ? error.message : error);
139
+ }
140
+ }
141
+
142
+ async function handleSuggest(args: string[]) {
143
+ const name = args[0];
144
+ const tld = args[1] || 'com';
145
+
146
+ if (!name) {
147
+ console.log('Usage: suggest <name> [tld]');
148
+ return;
149
+ }
150
+
151
+ console.log(`\nGetting suggestions for ${name}.${tld}...\n`);
152
+
153
+ try {
154
+ const result = await suggestDomains(name, tld, 10);
155
+
156
+ const available = result.suggestions.filter((s) => s.available);
157
+ console.log(`Found ${available.length} available variations:\n`);
158
+
159
+ for (const s of available.slice(0, 10)) {
160
+ const price = s.price_first_year ? `$${s.price_first_year}/year` : '';
161
+ console.log(` ${s.domain} ${price}`);
162
+ }
163
+ } catch (error) {
164
+ console.error('Error:', error instanceof Error ? error.message : error);
165
+ }
166
+ }
167
+
168
+ async function handleTld(args: string[]) {
169
+ const tld = args[0];
170
+
171
+ if (!tld) {
172
+ console.log('Usage: tld <tld>');
173
+ return;
174
+ }
175
+
176
+ console.log(`\nGetting info for .${tld}...\n`);
177
+
178
+ try {
179
+ const result = await getTldInfo(tld);
180
+
181
+ console.log(` TLD: .${result.tld}`);
182
+ console.log(` Category: ${result.category}`);
183
+ console.log(` Description: ${result.description}`);
184
+ console.log(` Popularity: ${result.popularity}`);
185
+
186
+ if (result.price_range) {
187
+ console.log(
188
+ ` Price Range: $${result.price_range.min} - $${result.price_range.max}/year`,
189
+ );
190
+ }
191
+
192
+ if (result.restrictions.length > 0) {
193
+ console.log(` Restrictions: ${result.restrictions.join(', ')}`);
194
+ }
195
+
196
+ if (result.recommendation) {
197
+ console.log(`\n Recommendation: ${result.recommendation}`);
198
+ }
199
+ } catch (error) {
200
+ console.error('Error:', error instanceof Error ? error.message : error);
201
+ }
202
+ }
203
+
204
+ async function handleSocials(args: string[]) {
205
+ const name = args[0];
206
+
207
+ if (!name) {
208
+ console.log('Usage: socials <name>');
209
+ return;
210
+ }
211
+
212
+ console.log(`\nChecking social media for "${name}"...\n`);
213
+
214
+ try {
215
+ const result = await checkSocials(name);
216
+
217
+ for (const platform of result.results) {
218
+ const status = platform.available ? 'Available' : 'Taken';
219
+ const confidence =
220
+ platform.confidence === 'high'
221
+ ? ''
222
+ : ` (${platform.confidence} confidence)`;
223
+ console.log(` ${platform.platform}: ${status}${confidence}`);
224
+ }
225
+
226
+ console.log('\nInsights:');
227
+ for (const insight of result.insights) {
228
+ console.log(` ${insight}`);
229
+ }
230
+ } catch (error) {
231
+ console.error('Error:', error instanceof Error ? error.message : error);
232
+ }
233
+ }
234
+
235
+ async function main() {
236
+ console.log('Domain Search MCP - Interactive CLI\n');
237
+ console.log('====================================\n');
238
+ console.log('Type "help" for available commands, "quit" to exit.\n');
239
+
240
+ while (true) {
241
+ const input = await prompt('\ndomain> ');
242
+
243
+ if (!input) continue;
244
+
245
+ const [command, ...args] = input.split(' ');
246
+
247
+ switch (command.toLowerCase()) {
248
+ case 'search':
249
+ await handleSearch(args);
250
+ break;
251
+ case 'bulk':
252
+ await handleBulk(args);
253
+ break;
254
+ case 'compare':
255
+ await handleCompare(args);
256
+ break;
257
+ case 'suggest':
258
+ await handleSuggest(args);
259
+ break;
260
+ case 'tld':
261
+ await handleTld(args);
262
+ break;
263
+ case 'socials':
264
+ await handleSocials(args);
265
+ break;
266
+ case 'help':
267
+ printHelp();
268
+ break;
269
+ case 'quit':
270
+ case 'exit':
271
+ console.log('\nGoodbye!');
272
+ rl.close();
273
+ process.exit(0);
274
+ default:
275
+ console.log(`Unknown command: ${command}. Type "help" for available commands.`);
276
+ }
277
+ }
278
+ }
279
+
280
+ main();
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Example: Compare Registrar Pricing
3
+ *
4
+ * This example demonstrates comparing prices across different
5
+ * registrars to find the best deal for a domain.
6
+ */
7
+
8
+ import { compareRegistrars } from '../src/services/domain-search.js';
9
+
10
+ async function main() {
11
+ console.log('Domain Search MCP - Compare Registrars Example\n');
12
+ console.log('===============================================\n');
13
+
14
+ const domains = [
15
+ { name: 'vibecoding', tld: 'com' },
16
+ { name: 'myawesomeapp', tld: 'io' },
17
+ { name: 'coolstartup', tld: 'dev' },
18
+ ];
19
+
20
+ for (const { name, tld } of domains) {
21
+ console.log(`\nComparing prices for ${name}.${tld}:`);
22
+ console.log('-'.repeat(40));
23
+
24
+ try {
25
+ const result = await compareRegistrars(name, tld);
26
+
27
+ // Show what happened
28
+ console.log(`Status: ${result.what_happened}`);
29
+
30
+ // Display prices from each registrar
31
+ console.log('\nPrices by Registrar:');
32
+ for (const price of result.prices) {
33
+ const firstYear = price.price_first_year
34
+ ? `$${price.price_first_year}`
35
+ : 'N/A';
36
+ const renewal = price.price_renewal ? `$${price.price_renewal}` : 'N/A';
37
+ const privacy = price.privacy_included ? '(privacy included)' : '';
38
+
39
+ console.log(` ${price.registrar}:`);
40
+ console.log(` First year: ${firstYear} ${privacy}`);
41
+ console.log(` Renewal: ${renewal}`);
42
+ }
43
+
44
+ // Show best options
45
+ if (result.best_first_year) {
46
+ console.log(
47
+ `\n Best first year: ${result.best_first_year.registrar} at $${result.best_first_year.price}`,
48
+ );
49
+ }
50
+ if (result.best_renewal) {
51
+ console.log(
52
+ ` Best renewal: ${result.best_renewal.registrar} at $${result.best_renewal.price}`,
53
+ );
54
+ }
55
+
56
+ // Show recommendation
57
+ console.log(`\nRecommendation: ${result.recommendation}`);
58
+
59
+ // Show insights
60
+ if (result.insights.length > 0) {
61
+ console.log('\nInsights:');
62
+ for (const insight of result.insights) {
63
+ console.log(` ${insight}`);
64
+ }
65
+ }
66
+ } catch (error) {
67
+ console.error(
68
+ `Error checking ${name}.${tld}:`,
69
+ error instanceof Error ? error.message : error,
70
+ );
71
+ }
72
+ }
73
+
74
+ console.log('\n' + '='.repeat(40));
75
+ console.log('Comparison complete!');
76
+ }
77
+
78
+ main();
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Example: Search for a Single Domain
3
+ *
4
+ * This example demonstrates the basic usage of the search_domain tool.
5
+ * It checks availability across multiple TLDs and shows pricing.
6
+ */
7
+
8
+ import { searchDomain } from '../src/services/domain-search.js';
9
+
10
+ async function main() {
11
+ console.log('Domain Search MCP - Single Domain Example\n');
12
+ console.log('==========================================\n');
13
+
14
+ // Search for a domain across default TLDs (com, io, dev)
15
+ console.log('Searching for "vibecoding" across .com, .io, .dev...\n');
16
+
17
+ try {
18
+ const result = await searchDomain('vibecoding');
19
+
20
+ // Display results
21
+ console.log('Results:');
22
+ console.log('--------');
23
+
24
+ for (const domain of result.results) {
25
+ const status = domain.available ? '✅ Available' : '❌ Taken';
26
+ const price = domain.price_first_year
27
+ ? `$${domain.price_first_year}/year`
28
+ : 'Price unknown';
29
+ const privacy = domain.privacy_included ? '(privacy included)' : '';
30
+
31
+ console.log(`${domain.domain}: ${status} - ${price} ${privacy}`);
32
+ }
33
+
34
+ // Display insights
35
+ console.log('\nInsights:');
36
+ console.log('---------');
37
+ for (const insight of result.insights) {
38
+ console.log(insight);
39
+ }
40
+
41
+ // Display next steps
42
+ console.log('\nNext Steps:');
43
+ console.log('-----------');
44
+ for (const step of result.next_steps) {
45
+ console.log(`• ${step}`);
46
+ }
47
+
48
+ console.log(`\n(Completed in ${result.duration_ms}ms)`);
49
+ } catch (error) {
50
+ console.error('Error:', error instanceof Error ? error.message : error);
51
+ }
52
+ }
53
+
54
+ main();
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Example: Domain Name Suggestions
3
+ *
4
+ * This example demonstrates getting available domain variations
5
+ * when your preferred domain name is taken.
6
+ */
7
+
8
+ import { suggestDomains } from '../src/services/domain-search.js';
9
+
10
+ async function main() {
11
+ console.log('Domain Search MCP - Domain Suggestions Example\n');
12
+ console.log('===============================================\n');
13
+
14
+ const baseNames = ['vibecoding', 'coolapp', 'techstart'];
15
+
16
+ for (const baseName of baseNames) {
17
+ console.log(`\nSuggestions for "${baseName}":`);
18
+ console.log('-'.repeat(40));
19
+
20
+ try {
21
+ // Get suggestions for .com
22
+ const result = await suggestDomains(baseName, 'com', 10);
23
+
24
+ console.log(`Original: ${baseName}.com`);
25
+ console.log(`Strategy: ${result.strategy}\n`);
26
+
27
+ // Display suggestions grouped by type
28
+ const prefixed = result.suggestions.filter((s) =>
29
+ s.domain.startsWith('get') ||
30
+ s.domain.startsWith('try') ||
31
+ s.domain.startsWith('use') ||
32
+ s.domain.startsWith('my')
33
+ );
34
+
35
+ const suffixed = result.suggestions.filter((s) =>
36
+ s.domain.includes('app') ||
37
+ s.domain.includes('hq') ||
38
+ s.domain.includes('io') ||
39
+ s.domain.includes('now')
40
+ );
41
+
42
+ const others = result.suggestions.filter(
43
+ (s) => !prefixed.includes(s) && !suffixed.includes(s),
44
+ );
45
+
46
+ if (prefixed.length > 0) {
47
+ console.log('With Prefix:');
48
+ for (const s of prefixed.slice(0, 3)) {
49
+ const price = s.price_first_year
50
+ ? `$${s.price_first_year}/year`
51
+ : 'Price unknown';
52
+ const status = s.available ? 'Available' : 'Taken';
53
+ console.log(` ${s.domain} - ${status} - ${price}`);
54
+ }
55
+ }
56
+
57
+ if (suffixed.length > 0) {
58
+ console.log('\nWith Suffix:');
59
+ for (const s of suffixed.slice(0, 3)) {
60
+ const price = s.price_first_year
61
+ ? `$${s.price_first_year}/year`
62
+ : 'Price unknown';
63
+ const status = s.available ? 'Available' : 'Taken';
64
+ console.log(` ${s.domain} - ${status} - ${price}`);
65
+ }
66
+ }
67
+
68
+ if (others.length > 0) {
69
+ console.log('\nOther Variations:');
70
+ for (const s of others.slice(0, 3)) {
71
+ const price = s.price_first_year
72
+ ? `$${s.price_first_year}/year`
73
+ : 'Price unknown';
74
+ const status = s.available ? 'Available' : 'Taken';
75
+ console.log(` ${s.domain} - ${status} - ${price}`);
76
+ }
77
+ }
78
+
79
+ // Summary
80
+ const availableCount = result.suggestions.filter((s) => s.available).length;
81
+ console.log(`\nFound ${availableCount} available out of ${result.suggestions.length} checked`);
82
+
83
+ // Insights
84
+ if (result.insights.length > 0) {
85
+ console.log('\nInsights:');
86
+ for (const insight of result.insights) {
87
+ console.log(` ${insight}`);
88
+ }
89
+ }
90
+
91
+ // Next steps
92
+ if (result.next_steps.length > 0) {
93
+ console.log('\nNext Steps:');
94
+ for (const step of result.next_steps) {
95
+ console.log(` - ${step}`);
96
+ }
97
+ }
98
+ } catch (error) {
99
+ console.error(
100
+ `Error getting suggestions for ${baseName}:`,
101
+ error instanceof Error ? error.message : error,
102
+ );
103
+ }
104
+ }
105
+
106
+ console.log('\n' + '='.repeat(40));
107
+ console.log('Suggestions complete!');
108
+ }
109
+
110
+ main();
package/glama.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": ["dorukardahan"],
4
+ "name": "Domain Search MCP",
5
+ "description": "Check domain availability and pricing across multiple registrars (Porkbun, Namecheap, RDAP, WHOIS) in real-time. Includes social handle checking for 10 platforms."
6
+ }
package/jest.config.js ADDED
@@ -0,0 +1,35 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/tests'],
6
+ testMatch: ['**/*.test.ts'],
7
+ collectCoverageFrom: [
8
+ 'src/**/*.ts',
9
+ '!src/**/*.d.ts',
10
+ ],
11
+ coverageDirectory: 'coverage',
12
+ coverageReporters: ['text', 'lcov', 'html'],
13
+ coverageThreshold: {
14
+ global: {
15
+ branches: 60,
16
+ functions: 60,
17
+ lines: 60,
18
+ statements: 60,
19
+ },
20
+ },
21
+ moduleNameMapper: {
22
+ '^(\\.{1,2}/.*)\\.js$': '$1', // Handle .js extensions
23
+ },
24
+ transform: {
25
+ '^.+\\.tsx?$': [
26
+ 'ts-jest',
27
+ {
28
+ useESM: false,
29
+ isolatedModules: true,
30
+ },
31
+ ],
32
+ },
33
+ verbose: true,
34
+ testTimeout: 10000,
35
+ };