domain-search-mcp 1.1.4 → 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.
- package/README.md +394 -1
- package/dist/registrars/godaddy-mcp.d.ts +22 -2
- package/dist/registrars/godaddy-mcp.d.ts.map +1 -1
- package/dist/registrars/godaddy-mcp.js +118 -0
- package/dist/registrars/godaddy-mcp.js.map +1 -1
- package/dist/registrars/index.d.ts +1 -1
- package/dist/registrars/index.d.ts.map +1 -1
- package/dist/registrars/index.js.map +1 -1
- package/dist/tools/suggest_domains_smart.d.ts +7 -2
- package/dist/tools/suggest_domains_smart.d.ts.map +1 -1
- package/dist/tools/suggest_domains_smart.js +171 -56
- package/dist/tools/suggest_domains_smart.js.map +1 -1
- package/package.json +1 -1
- package/src/registrars/godaddy-mcp.ts +152 -1
- package/src/registrars/index.ts +6 -1
- package/src/tools/suggest_domains_smart.ts +187 -59
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
getSynonyms,
|
|
19
19
|
getIndustryTerms,
|
|
20
20
|
} from '../utils/semantic-engine.js';
|
|
21
|
+
import { godaddyMcpAdapter, type GodaddySuggestion } from '../registrars/index.js';
|
|
22
|
+
import { logger } from '../utils/logger.js';
|
|
21
23
|
import type { DomainResult } from '../types.js';
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -101,14 +103,16 @@ export const suggestDomainsSmartTool = {
|
|
|
101
103
|
description: `AI-powered domain name suggestion engine.
|
|
102
104
|
|
|
103
105
|
Generate creative, brandable domain names from keywords or business descriptions.
|
|
104
|
-
|
|
106
|
+
Combines our semantic engine with GoDaddy's AI suggestions for maximum coverage.
|
|
105
107
|
|
|
106
108
|
Features:
|
|
109
|
+
- Dual-source suggestions: Our semantic engine + GoDaddy AI
|
|
107
110
|
- Understands natural language queries ("coffee shop in seattle")
|
|
108
111
|
- Auto-detects industry for contextual suggestions
|
|
109
112
|
- Generates portmanteau/blended names (instagram = instant + telegram)
|
|
110
113
|
- Applies modern naming patterns (ly, ify, io, hub, etc.)
|
|
111
114
|
- Filters premium domains by default
|
|
115
|
+
- Pre-verified availability via GoDaddy
|
|
112
116
|
|
|
113
117
|
Examples:
|
|
114
118
|
- suggest_domains_smart("ai customer service") → AI-themed suggestions
|
|
@@ -206,6 +210,7 @@ interface SmartSuggestion {
|
|
|
206
210
|
privacy_included: boolean;
|
|
207
211
|
score: number;
|
|
208
212
|
category: 'standard' | 'premium' | 'auction' | 'unavailable';
|
|
213
|
+
source: 'semantic_engine' | 'godaddy_suggest' | 'both';
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
/**
|
|
@@ -217,7 +222,11 @@ interface SuggestDomainsSmartResponse {
|
|
|
217
222
|
detected_industry: string | null;
|
|
218
223
|
tld: string;
|
|
219
224
|
style: string;
|
|
220
|
-
|
|
225
|
+
sources: {
|
|
226
|
+
semantic_engine: number;
|
|
227
|
+
godaddy_suggest: number;
|
|
228
|
+
merged: number;
|
|
229
|
+
};
|
|
221
230
|
total_checked: number;
|
|
222
231
|
results: {
|
|
223
232
|
available: SmartSuggestion[];
|
|
@@ -243,97 +252,215 @@ export async function executeSuggestDomainsSmart(
|
|
|
243
252
|
const detectedWords = segmentWords(normalizedQuery);
|
|
244
253
|
const detectedIndustry = industry || detectIndustry(detectedWords);
|
|
245
254
|
|
|
246
|
-
//
|
|
247
|
-
const
|
|
248
|
-
|
|
255
|
+
// Track source statistics
|
|
256
|
+
const sourceStats = {
|
|
257
|
+
semantic_engine: 0,
|
|
258
|
+
godaddy_suggest: 0,
|
|
259
|
+
merged: 0,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// ========================================
|
|
263
|
+
// STEP 1: Generate suggestions from BOTH sources in parallel
|
|
264
|
+
// ========================================
|
|
265
|
+
|
|
266
|
+
// Source 1: Our semantic engine
|
|
267
|
+
const semanticSuggestions = generateSmartSuggestions(normalizedQuery, {
|
|
268
|
+
maxSuggestions: max_suggestions * 3,
|
|
249
269
|
includePortmanteau: style === 'creative' || style === 'brandable',
|
|
250
270
|
includeSynonyms: style !== 'short',
|
|
251
271
|
includeIndustryTerms: !!detectedIndustry,
|
|
252
272
|
industry: detectedIndustry || undefined,
|
|
253
273
|
});
|
|
274
|
+
sourceStats.semantic_engine = semanticSuggestions.length;
|
|
275
|
+
|
|
276
|
+
// Source 2: GoDaddy's AI suggestions (parallel call)
|
|
277
|
+
let godaddySuggestions: GodaddySuggestion[] = [];
|
|
278
|
+
try {
|
|
279
|
+
godaddySuggestions = await godaddyMcpAdapter.suggestDomains(query, {
|
|
280
|
+
tlds: [tld],
|
|
281
|
+
limit: max_suggestions * 2,
|
|
282
|
+
});
|
|
283
|
+
sourceStats.godaddy_suggest = godaddySuggestions.length;
|
|
284
|
+
logger.debug('GoDaddy suggestions received', {
|
|
285
|
+
count: godaddySuggestions.length,
|
|
286
|
+
sample: godaddySuggestions.slice(0, 3).map(s => s.domain),
|
|
287
|
+
});
|
|
288
|
+
} catch (error) {
|
|
289
|
+
// GoDaddy might fail - continue with just semantic suggestions
|
|
290
|
+
logger.warn('GoDaddy suggestions failed, using semantic engine only', {
|
|
291
|
+
error: error instanceof Error ? error.message : 'unknown',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
254
294
|
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Limit candidates for availability check
|
|
259
|
-
const candidates = styledSuggestions.slice(0, max_suggestions * 2);
|
|
295
|
+
// ========================================
|
|
296
|
+
// STEP 2: Merge and deduplicate suggestions
|
|
297
|
+
// ========================================
|
|
260
298
|
|
|
261
|
-
//
|
|
262
|
-
const
|
|
263
|
-
const results: Array<{ name: string; result: DomainResult | null }> = [];
|
|
299
|
+
// Track which domains came from which source
|
|
300
|
+
const domainSources = new Map<string, 'semantic_engine' | 'godaddy_suggest' | 'both'>();
|
|
264
301
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const result = response.results.find((r) => r.domain === `${name}.${tld}`);
|
|
272
|
-
return { name, result: result || null };
|
|
273
|
-
} catch {
|
|
274
|
-
return { name, result: null };
|
|
275
|
-
}
|
|
276
|
-
}),
|
|
277
|
-
);
|
|
278
|
-
results.push(...batchResults);
|
|
302
|
+
// Add semantic suggestions (need availability check)
|
|
303
|
+
const styledSuggestions = applyStyleFilter(semanticSuggestions, style, normalizedQuery);
|
|
304
|
+
for (const name of styledSuggestions) {
|
|
305
|
+
const fullDomain = `${name}.${tld}`.toLowerCase();
|
|
306
|
+
domainSources.set(fullDomain, 'semantic_engine');
|
|
307
|
+
}
|
|
279
308
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
309
|
+
// Add GoDaddy suggestions (already have availability)
|
|
310
|
+
for (const gs of godaddySuggestions) {
|
|
311
|
+
const fullDomain = gs.domain.toLowerCase();
|
|
312
|
+
if (domainSources.has(fullDomain)) {
|
|
313
|
+
domainSources.set(fullDomain, 'both'); // Found in both sources
|
|
314
|
+
sourceStats.merged++;
|
|
315
|
+
} else {
|
|
316
|
+
domainSources.set(fullDomain, 'godaddy_suggest');
|
|
284
317
|
}
|
|
285
318
|
}
|
|
286
319
|
|
|
287
|
-
//
|
|
320
|
+
// ========================================
|
|
321
|
+
// STEP 3: Check availability for semantic suggestions
|
|
322
|
+
// (GoDaddy suggestions already have availability info)
|
|
323
|
+
// ========================================
|
|
324
|
+
|
|
288
325
|
const available: SmartSuggestion[] = [];
|
|
289
326
|
const premium: SmartSuggestion[] = [];
|
|
290
327
|
let unavailableCount = 0;
|
|
328
|
+
let totalChecked = 0;
|
|
291
329
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const isPremium = result.premium || isPremiumPrice(tld, result.price_first_year);
|
|
330
|
+
// First, add pre-checked GoDaddy suggestions (no API call needed!)
|
|
331
|
+
for (const gs of godaddySuggestions) {
|
|
332
|
+
const fullDomain = gs.domain.toLowerCase();
|
|
333
|
+
const source = domainSources.get(fullDomain) || 'godaddy_suggest';
|
|
334
|
+
const name = fullDomain.replace(`.${tld}`, '');
|
|
299
335
|
|
|
300
336
|
const suggestion: SmartSuggestion = {
|
|
301
|
-
domain:
|
|
302
|
-
available:
|
|
303
|
-
price_first_year:
|
|
304
|
-
price_renewal:
|
|
305
|
-
registrar:
|
|
306
|
-
premium:
|
|
307
|
-
premium_detected:
|
|
308
|
-
privacy_included:
|
|
337
|
+
domain: fullDomain,
|
|
338
|
+
available: gs.available,
|
|
339
|
+
price_first_year: null, // GoDaddy doesn't provide pricing
|
|
340
|
+
price_renewal: null,
|
|
341
|
+
registrar: 'godaddy',
|
|
342
|
+
premium: gs.premium,
|
|
343
|
+
premium_detected: gs.premium,
|
|
344
|
+
privacy_included: false,
|
|
309
345
|
score: scoreDomainName(name, normalizedQuery),
|
|
310
|
-
category: !
|
|
346
|
+
category: !gs.available
|
|
311
347
|
? 'unavailable'
|
|
312
|
-
:
|
|
348
|
+
: gs.premium
|
|
313
349
|
? 'premium'
|
|
350
|
+
: gs.auction
|
|
351
|
+
? 'auction'
|
|
314
352
|
: 'standard',
|
|
353
|
+
source,
|
|
315
354
|
};
|
|
316
355
|
|
|
317
|
-
if (!
|
|
356
|
+
if (!gs.available) {
|
|
318
357
|
unavailableCount++;
|
|
319
|
-
} else if (
|
|
320
|
-
|
|
358
|
+
} else if (gs.premium || gs.auction) {
|
|
359
|
+
if (include_premium) {
|
|
360
|
+
premium.push(suggestion);
|
|
361
|
+
}
|
|
321
362
|
} else {
|
|
322
363
|
available.push(suggestion);
|
|
323
364
|
}
|
|
324
365
|
}
|
|
325
366
|
|
|
326
|
-
//
|
|
327
|
-
|
|
367
|
+
// Then, check semantic suggestions that weren't in GoDaddy results
|
|
368
|
+
const semanticOnlyCandidates = styledSuggestions
|
|
369
|
+
.filter(name => {
|
|
370
|
+
const fullDomain = `${name}.${tld}`.toLowerCase();
|
|
371
|
+
return domainSources.get(fullDomain) === 'semantic_engine';
|
|
372
|
+
})
|
|
373
|
+
.slice(0, max_suggestions); // Limit API calls
|
|
374
|
+
|
|
375
|
+
const BATCH_SIZE = 5;
|
|
376
|
+
for (let i = 0; i < semanticOnlyCandidates.length; i += BATCH_SIZE) {
|
|
377
|
+
const batch = semanticOnlyCandidates.slice(i, i + BATCH_SIZE);
|
|
378
|
+
const batchResults = await Promise.all(
|
|
379
|
+
batch.map(async (name) => {
|
|
380
|
+
try {
|
|
381
|
+
const response = await searchDomain(name, [tld]);
|
|
382
|
+
const result = response.results.find((r) => r.domain === `${name}.${tld}`);
|
|
383
|
+
return { name, result: result || null };
|
|
384
|
+
} catch {
|
|
385
|
+
return { name, result: null };
|
|
386
|
+
}
|
|
387
|
+
}),
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
for (const { name, result } of batchResults) {
|
|
391
|
+
totalChecked++;
|
|
392
|
+
if (!result) {
|
|
393
|
+
unavailableCount++;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const isPremiumDomain = result.premium || isPremiumPrice(tld, result.price_first_year);
|
|
398
|
+
const fullDomain = `${name}.${tld}`.toLowerCase();
|
|
399
|
+
|
|
400
|
+
const suggestion: SmartSuggestion = {
|
|
401
|
+
domain: fullDomain,
|
|
402
|
+
available: result.available,
|
|
403
|
+
price_first_year: result.price_first_year,
|
|
404
|
+
price_renewal: result.price_renewal,
|
|
405
|
+
registrar: result.registrar,
|
|
406
|
+
premium: result.premium || false,
|
|
407
|
+
premium_detected: isPremiumPrice(tld, result.price_first_year),
|
|
408
|
+
privacy_included: result.privacy_included || false,
|
|
409
|
+
score: scoreDomainName(name, normalizedQuery),
|
|
410
|
+
category: !result.available
|
|
411
|
+
? 'unavailable'
|
|
412
|
+
: isPremiumDomain
|
|
413
|
+
? 'premium'
|
|
414
|
+
: 'standard',
|
|
415
|
+
source: 'semantic_engine',
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
if (!result.available) {
|
|
419
|
+
unavailableCount++;
|
|
420
|
+
} else if (isPremiumDomain) {
|
|
421
|
+
if (include_premium) {
|
|
422
|
+
premium.push(suggestion);
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
available.push(suggestion);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Early exit if we have enough available
|
|
430
|
+
if (available.length >= max_suggestions && !include_premium) {
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ========================================
|
|
436
|
+
// STEP 4: Sort and finalize results
|
|
437
|
+
// ========================================
|
|
438
|
+
|
|
439
|
+
// Sort by score, prefer 'both' source items (validated by multiple sources)
|
|
440
|
+
available.sort((a, b) => {
|
|
441
|
+
// Boost 'both' source items
|
|
442
|
+
const aBoost = a.source === 'both' ? 2 : 0;
|
|
443
|
+
const bBoost = b.source === 'both' ? 2 : 0;
|
|
444
|
+
return (b.score + bBoost) - (a.score + aBoost);
|
|
445
|
+
});
|
|
328
446
|
premium.sort((a, b) => b.score - a.score);
|
|
329
447
|
|
|
330
448
|
// Limit results
|
|
331
449
|
const finalAvailable = available.slice(0, max_suggestions);
|
|
332
450
|
const finalPremium = include_premium ? premium.slice(0, Math.floor(max_suggestions / 2)) : [];
|
|
333
451
|
|
|
334
|
-
//
|
|
452
|
+
// ========================================
|
|
453
|
+
// STEP 5: Generate insights
|
|
454
|
+
// ========================================
|
|
455
|
+
|
|
335
456
|
const insights: string[] = [];
|
|
336
457
|
|
|
458
|
+
// Source info
|
|
459
|
+
insights.push(`🔍 Sources: Semantic Engine (${sourceStats.semantic_engine}) + GoDaddy AI (${sourceStats.godaddy_suggest})`);
|
|
460
|
+
if (sourceStats.merged > 0) {
|
|
461
|
+
insights.push(`🔗 ${sourceStats.merged} suggestions found in both sources`);
|
|
462
|
+
}
|
|
463
|
+
|
|
337
464
|
if (detectedIndustry) {
|
|
338
465
|
insights.push(`🎯 Detected industry: ${detectedIndustry}`);
|
|
339
466
|
}
|
|
@@ -345,8 +472,9 @@ export async function executeSuggestDomainsSmart(
|
|
|
345
472
|
if (finalAvailable.length > 0) {
|
|
346
473
|
insights.push(`✅ Found ${finalAvailable.length} available domain${finalAvailable.length > 1 ? 's' : ''}`);
|
|
347
474
|
const best = finalAvailable[0]!;
|
|
348
|
-
const priceStr = best.price_first_year !== null ? `$${best.price_first_year}/yr` : '
|
|
349
|
-
|
|
475
|
+
const priceStr = best.price_first_year !== null ? `$${best.price_first_year}/yr` : 'via ' + best.registrar;
|
|
476
|
+
const sourceStr = best.source === 'both' ? ' (verified by both sources)' : '';
|
|
477
|
+
insights.push(`⭐ Top pick: ${best.domain} (${priceStr})${sourceStr}`);
|
|
350
478
|
} else {
|
|
351
479
|
insights.push(`❌ No standard-priced domains available`);
|
|
352
480
|
}
|
|
@@ -376,8 +504,8 @@ export async function executeSuggestDomainsSmart(
|
|
|
376
504
|
detected_industry: detectedIndustry,
|
|
377
505
|
tld,
|
|
378
506
|
style,
|
|
379
|
-
|
|
380
|
-
total_checked:
|
|
507
|
+
sources: sourceStats,
|
|
508
|
+
total_checked: totalChecked + godaddySuggestions.length,
|
|
381
509
|
results: {
|
|
382
510
|
available: finalAvailable,
|
|
383
511
|
premium: finalPremium,
|