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.
- package/.env.example +52 -0
- package/Dockerfile +15 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/SECURITY.md +252 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/fallbacks/index.d.ts +6 -0
- package/dist/fallbacks/index.d.ts.map +1 -0
- package/dist/fallbacks/index.js +14 -0
- package/dist/fallbacks/index.js.map +1 -0
- package/dist/fallbacks/rdap.d.ts +18 -0
- package/dist/fallbacks/rdap.d.ts.map +1 -0
- package/dist/fallbacks/rdap.js +339 -0
- package/dist/fallbacks/rdap.js.map +1 -0
- package/dist/fallbacks/whois.d.ts +27 -0
- package/dist/fallbacks/whois.d.ts.map +1 -0
- package/dist/fallbacks/whois.js +219 -0
- package/dist/fallbacks/whois.js.map +1 -0
- package/dist/registrars/base.d.ts +89 -0
- package/dist/registrars/base.d.ts.map +1 -0
- package/dist/registrars/base.js +203 -0
- package/dist/registrars/base.js.map +1 -0
- package/dist/registrars/index.d.ts +7 -0
- package/dist/registrars/index.d.ts.map +1 -0
- package/dist/registrars/index.js +15 -0
- package/dist/registrars/index.js.map +1 -0
- package/dist/registrars/namecheap.d.ts +69 -0
- package/dist/registrars/namecheap.d.ts.map +1 -0
- package/dist/registrars/namecheap.js +307 -0
- package/dist/registrars/namecheap.js.map +1 -0
- package/dist/registrars/porkbun.d.ts +63 -0
- package/dist/registrars/porkbun.d.ts.map +1 -0
- package/dist/registrars/porkbun.js +299 -0
- package/dist/registrars/porkbun.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +209 -0
- package/dist/server.js.map +1 -0
- package/dist/services/domain-search.d.ts +40 -0
- package/dist/services/domain-search.d.ts.map +1 -0
- package/dist/services/domain-search.js +438 -0
- package/dist/services/domain-search.js.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +11 -0
- package/dist/services/index.js.map +1 -0
- package/dist/tools/bulk_search.d.ts +72 -0
- package/dist/tools/bulk_search.d.ts.map +1 -0
- package/dist/tools/bulk_search.js +108 -0
- package/dist/tools/bulk_search.js.map +1 -0
- package/dist/tools/check_socials.d.ts +71 -0
- package/dist/tools/check_socials.d.ts.map +1 -0
- package/dist/tools/check_socials.js +357 -0
- package/dist/tools/check_socials.js.map +1 -0
- package/dist/tools/compare_registrars.d.ts +80 -0
- package/dist/tools/compare_registrars.d.ts.map +1 -0
- package/dist/tools/compare_registrars.js +116 -0
- package/dist/tools/compare_registrars.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/search_domain.d.ts +61 -0
- package/dist/tools/search_domain.d.ts.map +1 -0
- package/dist/tools/search_domain.js +81 -0
- package/dist/tools/search_domain.js.map +1 -0
- package/dist/tools/suggest_domains.d.ts +82 -0
- package/dist/tools/suggest_domains.d.ts.map +1 -0
- package/dist/tools/suggest_domains.js +227 -0
- package/dist/tools/suggest_domains.js.map +1 -0
- package/dist/tools/tld_info.d.ts +56 -0
- package/dist/tools/tld_info.d.ts.map +1 -0
- package/dist/tools/tld_info.js +273 -0
- package/dist/tools/tld_info.js.map +1 -0
- package/dist/types.d.ts +193 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cache.d.ts +81 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +192 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/errors.d.ts +87 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +191 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +24 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +27 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +132 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/premium-analyzer.d.ts +33 -0
- package/dist/utils/premium-analyzer.d.ts.map +1 -0
- package/dist/utils/premium-analyzer.js +273 -0
- package/dist/utils/premium-analyzer.js.map +1 -0
- package/dist/utils/validators.d.ts +53 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +159 -0
- package/dist/utils/validators.js.map +1 -0
- package/docs/marketing/devto-post.md +135 -0
- package/docs/marketing/hackernews.md +42 -0
- package/docs/marketing/producthunt.md +109 -0
- package/docs/marketing/reddit-post.md +59 -0
- package/docs/marketing/twitter-thread.md +105 -0
- package/examples/bulk-search-50-domains.ts +131 -0
- package/examples/cli-interactive.ts +280 -0
- package/examples/compare-registrars.ts +78 -0
- package/examples/search-single-domain.ts +54 -0
- package/examples/suggest-names.ts +110 -0
- package/glama.json +6 -0
- package/jest.config.js +35 -0
- package/package.json +62 -0
- package/smithery.yaml +36 -0
- package/src/config.ts +121 -0
- package/src/fallbacks/index.ts +6 -0
- package/src/fallbacks/rdap.ts +407 -0
- package/src/fallbacks/whois.ts +250 -0
- package/src/registrars/base.ts +264 -0
- package/src/registrars/index.ts +7 -0
- package/src/registrars/namecheap.ts +378 -0
- package/src/registrars/porkbun.ts +380 -0
- package/src/server.ts +276 -0
- package/src/services/domain-search.ts +567 -0
- package/src/services/index.ts +9 -0
- package/src/tools/bulk_search.ts +142 -0
- package/src/tools/check_socials.ts +467 -0
- package/src/tools/compare_registrars.ts +162 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/search_domain.ts +93 -0
- package/src/tools/suggest_domains.ts +284 -0
- package/src/tools/tld_info.ts +294 -0
- package/src/types.ts +289 -0
- package/src/utils/cache.ts +238 -0
- package/src/utils/errors.ts +262 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +162 -0
- package/src/utils/premium-analyzer.ts +303 -0
- package/src/utils/validators.ts +193 -0
- package/tests/premium-analyzer.test.ts +310 -0
- package/tests/unit/cache.test.ts +123 -0
- package/tests/unit/errors.test.ts +190 -0
- package/tests/unit/tld-info.test.ts +62 -0
- package/tests/unit/tools.test.ts +200 -0
- package/tests/unit/validators.test.ts +146 -0
- 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
|
+
};
|