domain-search-mcp 1.2.6 → 1.2.8
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 +1020 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1294,6 +1294,51 @@ AI-powered domain suggestions using semantic analysis:
|
|
|
1294
1294
|
|
|
1295
1295
|
Get detailed information about any Top Level Domain (TLD).
|
|
1296
1296
|
|
|
1297
|
+
---
|
|
1298
|
+
|
|
1299
|
+
#### Quick Start: Using tld_info Directly
|
|
1300
|
+
|
|
1301
|
+
```typescript
|
|
1302
|
+
// Direct tld_info usage - get information about a specific TLD
|
|
1303
|
+
import { tldInfo } from 'domain-search-mcp';
|
|
1304
|
+
|
|
1305
|
+
// Basic usage - get essential TLD info
|
|
1306
|
+
const ioInfo = await tldInfo({ tld: "io" });
|
|
1307
|
+
console.log(`${ioInfo.tld}: ${ioInfo.description}`);
|
|
1308
|
+
console.log(`Price range: $${ioInfo.price_range.min} - $${ioInfo.price_range.max}`);
|
|
1309
|
+
console.log(`Popularity: ${ioInfo.popularity}`);
|
|
1310
|
+
console.log(`Best for: ${ioInfo.typical_use}`);
|
|
1311
|
+
|
|
1312
|
+
// Detailed usage - get full technical info
|
|
1313
|
+
const devInfo = await tldInfo({ tld: "dev", detailed: true });
|
|
1314
|
+
console.log(`Registry: ${devInfo.registry}`);
|
|
1315
|
+
console.log(`Type: ${devInfo.type}`);
|
|
1316
|
+
console.log(`Restrictions: ${devInfo.restrictions.join(", ") || "None"}`);
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
**JavaScript Quick Start:**
|
|
1320
|
+
|
|
1321
|
+
```javascript
|
|
1322
|
+
// Direct tld_info usage with fetch
|
|
1323
|
+
async function getTldInfo(tld, detailed = false) {
|
|
1324
|
+
const response = await fetch('http://localhost:3000/tld_info', {
|
|
1325
|
+
method: 'POST',
|
|
1326
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1327
|
+
body: JSON.stringify({ tld, detailed })
|
|
1328
|
+
});
|
|
1329
|
+
return response.json();
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// Usage: Get info for multiple TLDs
|
|
1333
|
+
const tlds = ['com', 'io', 'dev', 'app'];
|
|
1334
|
+
for (const tld of tlds) {
|
|
1335
|
+
const info = await getTldInfo(tld);
|
|
1336
|
+
console.log(`${info.tld}: ${info.description} ($${info.price_range.min}-$${info.price_range.max})`);
|
|
1337
|
+
}
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
---
|
|
1341
|
+
|
|
1297
1342
|
**API Endpoint:** `POST /tld_info`
|
|
1298
1343
|
|
|
1299
1344
|
**Request Parameters:**
|
|
@@ -2259,6 +2304,41 @@ RATE_LIMIT_PER_MINUTE=60 # Max requests per minute
|
|
|
2259
2304
|
LOG_LEVEL=info # debug | info | warn | error
|
|
2260
2305
|
```
|
|
2261
2306
|
|
|
2307
|
+
#### Performance Benchmarks by Configuration
|
|
2308
|
+
|
|
2309
|
+
Real-world performance measurements comparing different configurations:
|
|
2310
|
+
|
|
2311
|
+
| Configuration | Avg Response Time | Throughput | Pricing Data | Reliability |
|
|
2312
|
+
|--------------|-------------------|------------|--------------|-------------|
|
|
2313
|
+
| **Porkbun API only** | 180ms | 50 req/min | ✅ Yes | 99.5% |
|
|
2314
|
+
| **Namecheap API only** | 220ms | 40 req/min | ✅ Yes | 99.2% |
|
|
2315
|
+
| **Both APIs (recommended)** | 150ms | 90 req/min | ✅ Comparison | 99.9% |
|
|
2316
|
+
| **RDAP/WHOIS fallback** | 800-2000ms | 10 req/min | ❌ No | 95% |
|
|
2317
|
+
| **No configuration** | 1200ms avg | 10 req/min | ❌ No | 90% |
|
|
2318
|
+
|
|
2319
|
+
**Benchmark details:**
|
|
2320
|
+
- Tests performed with 100 domain checks per configuration
|
|
2321
|
+
- Throughput measured as sustainable requests per minute without rate limiting
|
|
2322
|
+
- Reliability = successful responses / total requests
|
|
2323
|
+
|
|
2324
|
+
```bash
|
|
2325
|
+
# Minimal configuration (works but slow)
|
|
2326
|
+
# No .env file needed - uses RDAP/WHOIS
|
|
2327
|
+
# Performance: ~1200ms/query, no pricing
|
|
2328
|
+
|
|
2329
|
+
# Recommended: Porkbun only
|
|
2330
|
+
# Performance: ~180ms/query, includes pricing
|
|
2331
|
+
PORKBUN_API_KEY=pk1_abc123...
|
|
2332
|
+
PORKBUN_SECRET_KEY=sk1_xyz789...
|
|
2333
|
+
|
|
2334
|
+
# Optimal: Both registrars
|
|
2335
|
+
# Performance: ~150ms/query, price comparison, highest reliability
|
|
2336
|
+
PORKBUN_API_KEY=pk1_abc123...
|
|
2337
|
+
PORKBUN_SECRET_KEY=sk1_xyz789...
|
|
2338
|
+
NAMECHEAP_API_KEY=abc123def456
|
|
2339
|
+
NAMECHEAP_API_USER=yourusername
|
|
2340
|
+
```
|
|
2341
|
+
|
|
2262
2342
|
#### Configuration Quick Reference
|
|
2263
2343
|
|
|
2264
2344
|
| Configuration | Required | Effect |
|
|
@@ -2422,6 +2502,12 @@ console.log("Social results:", socialResult);
|
|
|
2422
2502
|
|
|
2423
2503
|
### Running as Local HTTP Server
|
|
2424
2504
|
|
|
2505
|
+
> **MCP vs HTTP Server**: This package supports two modes:
|
|
2506
|
+
> 1. **MCP Mode** (default): Runs as an MCP service for Claude Desktop, Cursor, or other MCP-compatible clients. No HTTP server needed.
|
|
2507
|
+
> 2. **HTTP Server Mode**: Runs as a standalone REST API for direct HTTP access from any application.
|
|
2508
|
+
>
|
|
2509
|
+
> Choose HTTP Server Mode when integrating with non-MCP applications or building custom frontends.
|
|
2510
|
+
|
|
2425
2511
|
Domain Search MCP can also run as a standalone HTTP server for direct API access:
|
|
2426
2512
|
|
|
2427
2513
|
#### Quick Start: Local Server Setup
|
|
@@ -2940,6 +3026,259 @@ try {
|
|
|
2940
3026
|
|
|
2941
3027
|
## Workflow Examples
|
|
2942
3028
|
|
|
3029
|
+
### Simultaneous Execution Patterns
|
|
3030
|
+
|
|
3031
|
+
When running multiple API checks in parallel (domains + social media), proper timeout coordination and race condition handling are critical for reliable results.
|
|
3032
|
+
|
|
3033
|
+
#### Promise.allSettled for Partial Failure Handling
|
|
3034
|
+
|
|
3035
|
+
Use `Promise.allSettled` instead of `Promise.all` to handle scenarios where some APIs succeed while others fail:
|
|
3036
|
+
|
|
3037
|
+
```typescript
|
|
3038
|
+
// Simultaneous domain + social checks with partial failure handling
|
|
3039
|
+
async function simultaneousChecksWithPartialFailure(brandName: string) {
|
|
3040
|
+
const TIMEOUT_MS = 10000; // 10 second timeout per check
|
|
3041
|
+
|
|
3042
|
+
// Create timeout wrapper for any promise
|
|
3043
|
+
function withTimeout<T>(promise: Promise<T>, ms: number, name: string): Promise<T> {
|
|
3044
|
+
return Promise.race([
|
|
3045
|
+
promise,
|
|
3046
|
+
new Promise<T>((_, reject) =>
|
|
3047
|
+
setTimeout(() => reject(new Error(`${name} timed out after ${ms}ms`)), ms)
|
|
3048
|
+
)
|
|
3049
|
+
]);
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
// Execute all checks simultaneously with individual timeouts
|
|
3053
|
+
const results = await Promise.allSettled([
|
|
3054
|
+
withTimeout(
|
|
3055
|
+
searchDomain({ domain_name: brandName, tlds: ["com", "io", "dev"] }),
|
|
3056
|
+
TIMEOUT_MS,
|
|
3057
|
+
"domain_search"
|
|
3058
|
+
),
|
|
3059
|
+
withTimeout(
|
|
3060
|
+
checkSocials({ name: brandName, platforms: ["github", "twitter", "instagram"] }),
|
|
3061
|
+
TIMEOUT_MS,
|
|
3062
|
+
"social_check"
|
|
3063
|
+
),
|
|
3064
|
+
withTimeout(
|
|
3065
|
+
suggestDomains({ base_name: brandName, tld: "com", max_suggestions: 5 }),
|
|
3066
|
+
TIMEOUT_MS,
|
|
3067
|
+
"suggestions"
|
|
3068
|
+
)
|
|
3069
|
+
]);
|
|
3070
|
+
|
|
3071
|
+
// Process results - handle both fulfilled and rejected
|
|
3072
|
+
const [domainResult, socialResult, suggestionResult] = results;
|
|
3073
|
+
|
|
3074
|
+
return {
|
|
3075
|
+
domains: domainResult.status === "fulfilled"
|
|
3076
|
+
? { success: true, data: domainResult.value }
|
|
3077
|
+
: { success: false, error: domainResult.reason.message },
|
|
3078
|
+
socials: socialResult.status === "fulfilled"
|
|
3079
|
+
? { success: true, data: socialResult.value }
|
|
3080
|
+
: { success: false, error: socialResult.reason.message },
|
|
3081
|
+
suggestions: suggestionResult.status === "fulfilled"
|
|
3082
|
+
? { success: true, data: suggestionResult.value }
|
|
3083
|
+
: { success: false, error: suggestionResult.reason.message },
|
|
3084
|
+
partialSuccess: results.some(r => r.status === "fulfilled"),
|
|
3085
|
+
allSucceeded: results.every(r => r.status === "fulfilled")
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
// Usage
|
|
3090
|
+
const result = await simultaneousChecksWithPartialFailure("techstartup");
|
|
3091
|
+
if (result.partialSuccess) {
|
|
3092
|
+
console.log("At least some checks completed:");
|
|
3093
|
+
if (result.domains.success) console.log(" Domains:", result.domains.data.results.length);
|
|
3094
|
+
if (result.socials.success) console.log(" Socials:", result.socials.data.results.length);
|
|
3095
|
+
}
|
|
3096
|
+
```
|
|
3097
|
+
|
|
3098
|
+
#### Timeout Coordination Between Parallel Checks
|
|
3099
|
+
|
|
3100
|
+
Coordinate timeouts across multiple parallel operations with AbortController:
|
|
3101
|
+
|
|
3102
|
+
```typescript
|
|
3103
|
+
// Coordinated timeout with AbortController pattern
|
|
3104
|
+
async function coordinatedParallelChecks(brandName: string, globalTimeoutMs: number = 15000) {
|
|
3105
|
+
const abortController = new AbortController();
|
|
3106
|
+
const { signal } = abortController;
|
|
3107
|
+
|
|
3108
|
+
// Set global timeout that cancels all operations
|
|
3109
|
+
const globalTimeout = setTimeout(() => {
|
|
3110
|
+
abortController.abort();
|
|
3111
|
+
}, globalTimeoutMs);
|
|
3112
|
+
|
|
3113
|
+
try {
|
|
3114
|
+
// Track individual operation status for race condition handling
|
|
3115
|
+
const operationStatus = {
|
|
3116
|
+
domains: { started: Date.now(), completed: false, result: null as any },
|
|
3117
|
+
socials: { started: Date.now(), completed: false, result: null as any }
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
const [domains, socials] = await Promise.all([
|
|
3121
|
+
// Domain check with signal
|
|
3122
|
+
(async () => {
|
|
3123
|
+
if (signal.aborted) throw new Error("Aborted before start");
|
|
3124
|
+
const result = await searchDomain({
|
|
3125
|
+
domain_name: brandName,
|
|
3126
|
+
tlds: ["com", "io", "dev"]
|
|
3127
|
+
});
|
|
3128
|
+
operationStatus.domains.completed = true;
|
|
3129
|
+
operationStatus.domains.result = result;
|
|
3130
|
+
return result;
|
|
3131
|
+
})(),
|
|
3132
|
+
|
|
3133
|
+
// Social check with signal
|
|
3134
|
+
(async () => {
|
|
3135
|
+
if (signal.aborted) throw new Error("Aborted before start");
|
|
3136
|
+
const result = await checkSocials({
|
|
3137
|
+
name: brandName,
|
|
3138
|
+
platforms: ["github", "twitter", "instagram"]
|
|
3139
|
+
});
|
|
3140
|
+
operationStatus.socials.completed = true;
|
|
3141
|
+
operationStatus.socials.result = result;
|
|
3142
|
+
return result;
|
|
3143
|
+
})()
|
|
3144
|
+
]);
|
|
3145
|
+
|
|
3146
|
+
clearTimeout(globalTimeout);
|
|
3147
|
+
|
|
3148
|
+
return {
|
|
3149
|
+
success: true,
|
|
3150
|
+
domains,
|
|
3151
|
+
socials,
|
|
3152
|
+
timing: {
|
|
3153
|
+
domainsMs: Date.now() - operationStatus.domains.started,
|
|
3154
|
+
socialsMs: Date.now() - operationStatus.socials.started
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
clearTimeout(globalTimeout);
|
|
3160
|
+
|
|
3161
|
+
// Return partial results if some operations completed before abort
|
|
3162
|
+
return {
|
|
3163
|
+
success: false,
|
|
3164
|
+
error: signal.aborted ? "Global timeout exceeded" : error.message,
|
|
3165
|
+
partialResults: {
|
|
3166
|
+
domains: operationStatus.domains.completed ? operationStatus.domains.result : null,
|
|
3167
|
+
socials: operationStatus.socials.completed ? operationStatus.socials.result : null
|
|
3168
|
+
}
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
```
|
|
3173
|
+
|
|
3174
|
+
#### Race Condition Handling: When Some APIs Fail Mid-Execution
|
|
3175
|
+
|
|
3176
|
+
Handle scenarios where APIs fail partway through execution:
|
|
3177
|
+
|
|
3178
|
+
```typescript
|
|
3179
|
+
// Race condition safe parallel execution with retry queue
|
|
3180
|
+
async function raceConditionSafeExecution(brandName: string) {
|
|
3181
|
+
const MAX_RETRIES = 2;
|
|
3182
|
+
const RETRY_DELAY_MS = 1000;
|
|
3183
|
+
|
|
3184
|
+
interface OperationResult<T> {
|
|
3185
|
+
operation: string;
|
|
3186
|
+
success: boolean;
|
|
3187
|
+
data?: T;
|
|
3188
|
+
error?: string;
|
|
3189
|
+
attempts: number;
|
|
3190
|
+
completedAt: number;
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// Execute single operation with retry logic
|
|
3194
|
+
async function executeWithRetry<T>(
|
|
3195
|
+
operation: string,
|
|
3196
|
+
fn: () => Promise<T>
|
|
3197
|
+
): Promise<OperationResult<T>> {
|
|
3198
|
+
let lastError: Error | null = null;
|
|
3199
|
+
|
|
3200
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
3201
|
+
try {
|
|
3202
|
+
const data = await fn();
|
|
3203
|
+
return {
|
|
3204
|
+
operation,
|
|
3205
|
+
success: true,
|
|
3206
|
+
data,
|
|
3207
|
+
attempts: attempt,
|
|
3208
|
+
completedAt: Date.now()
|
|
3209
|
+
};
|
|
3210
|
+
} catch (error) {
|
|
3211
|
+
lastError = error;
|
|
3212
|
+
|
|
3213
|
+
// Don't retry on non-retryable errors
|
|
3214
|
+
if (error.code === "INVALID_DOMAIN" || error.code === "AUTH_ERROR") {
|
|
3215
|
+
break;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// Wait before retry with exponential backoff
|
|
3219
|
+
if (attempt < MAX_RETRIES) {
|
|
3220
|
+
await new Promise(r => setTimeout(r, RETRY_DELAY_MS * attempt));
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
return {
|
|
3226
|
+
operation,
|
|
3227
|
+
success: false,
|
|
3228
|
+
error: lastError?.message || "Unknown error",
|
|
3229
|
+
attempts: MAX_RETRIES,
|
|
3230
|
+
completedAt: Date.now()
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
// Execute all operations in parallel with individual retry handling
|
|
3235
|
+
const startTime = Date.now();
|
|
3236
|
+
|
|
3237
|
+
const results = await Promise.all([
|
|
3238
|
+
executeWithRetry("search_domain", () =>
|
|
3239
|
+
searchDomain({ domain_name: brandName, tlds: ["com", "io", "dev"] })
|
|
3240
|
+
),
|
|
3241
|
+
executeWithRetry("check_socials", () =>
|
|
3242
|
+
checkSocials({ name: brandName, platforms: ["github", "twitter", "instagram"] })
|
|
3243
|
+
),
|
|
3244
|
+
executeWithRetry("suggest_domains", () =>
|
|
3245
|
+
suggestDomains({ base_name: brandName, tld: "com", max_suggestions: 5 })
|
|
3246
|
+
)
|
|
3247
|
+
]);
|
|
3248
|
+
|
|
3249
|
+
// Analyze results for race condition issues
|
|
3250
|
+
const succeeded = results.filter(r => r.success);
|
|
3251
|
+
const failed = results.filter(r => !r.success);
|
|
3252
|
+
|
|
3253
|
+
return {
|
|
3254
|
+
brandName,
|
|
3255
|
+
totalTime: Date.now() - startTime,
|
|
3256
|
+
allSucceeded: failed.length === 0,
|
|
3257
|
+
partialSuccess: succeeded.length > 0 && failed.length > 0,
|
|
3258
|
+
results: {
|
|
3259
|
+
domains: results.find(r => r.operation === "search_domain"),
|
|
3260
|
+
socials: results.find(r => r.operation === "check_socials"),
|
|
3261
|
+
suggestions: results.find(r => r.operation === "suggest_domains")
|
|
3262
|
+
},
|
|
3263
|
+
summary: {
|
|
3264
|
+
succeeded: succeeded.map(r => r.operation),
|
|
3265
|
+
failed: failed.map(r => ({ operation: r.operation, error: r.error })),
|
|
3266
|
+
totalAttempts: results.reduce((sum, r) => sum + r.attempts, 0)
|
|
3267
|
+
}
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// Usage with race condition safe execution
|
|
3272
|
+
const result = await raceConditionSafeExecution("mybrand");
|
|
3273
|
+
console.log(`Completed in ${result.totalTime}ms`);
|
|
3274
|
+
console.log(`Succeeded: ${result.summary.succeeded.join(", ")}`);
|
|
3275
|
+
if (result.summary.failed.length > 0) {
|
|
3276
|
+
console.log(`Failed: ${result.summary.failed.map(f => `${f.operation}: ${f.error}`).join(", ")}`);
|
|
3277
|
+
}
|
|
3278
|
+
```
|
|
3279
|
+
|
|
3280
|
+
---
|
|
3281
|
+
|
|
2943
3282
|
### Workflow 1: Complete Domain Acquisition with Partial Availability Handling
|
|
2944
3283
|
|
|
2945
3284
|
A comprehensive workflow that integrates `search_domain`, `check_socials`, and `suggest_domains` to validate a brand name across domain registrars and social media platforms simultaneously, with full error handling for partial availability scenarios:
|
|
@@ -3668,6 +4007,208 @@ console.log(research.recommendation);
|
|
|
3668
4007
|
// Output: "Recommended: myproject.com ($8.95/yr) - Classic, universal choice"
|
|
3669
4008
|
```
|
|
3670
4009
|
|
|
4010
|
+
#### Complete Startup Domain Research Report
|
|
4011
|
+
|
|
4012
|
+
Generate a formatted, executive-ready domain research report for startup founders:
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
import { searchDomain, tldInfo, checkSocials, suggestDomainsSmart } from 'domain-search-mcp';
|
|
4016
|
+
|
|
4017
|
+
async function generateStartupDomainReport(startupName: string) {
|
|
4018
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
4019
|
+
console.log(` DOMAIN RESEARCH REPORT: ${startupName.toUpperCase()}`);
|
|
4020
|
+
console.log(` Generated: ${new Date().toISOString().split('T')[0]}`);
|
|
4021
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
4022
|
+
|
|
4023
|
+
// Section 1: TLD Analysis using tld_info directly
|
|
4024
|
+
console.log("📋 TLD ANALYSIS");
|
|
4025
|
+
console.log("-".repeat(40));
|
|
4026
|
+
|
|
4027
|
+
const tlds = ["com", "io", "dev", "app", "co"];
|
|
4028
|
+
const tldAnalysis = [];
|
|
4029
|
+
|
|
4030
|
+
for (const tld of tlds) {
|
|
4031
|
+
const info = await tldInfo({ tld, detailed: true });
|
|
4032
|
+
tldAnalysis.push(info);
|
|
4033
|
+
console.log(`\n .${info.tld.toUpperCase()}`);
|
|
4034
|
+
console.log(` └─ ${info.description}`);
|
|
4035
|
+
console.log(` └─ Price: $${info.price_range.min} - $${info.price_range.max}/year`);
|
|
4036
|
+
console.log(` └─ Best for: ${info.typical_use}`);
|
|
4037
|
+
console.log(` └─ Popularity: ${info.popularity}`);
|
|
4038
|
+
if (info.restrictions?.length > 0) {
|
|
4039
|
+
console.log(` └─ ⚠️ Restrictions: ${info.restrictions.join(', ')}`);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
// Section 2: Availability Check
|
|
4044
|
+
console.log(`\n\n📊 AVAILABILITY CHECK`);
|
|
4045
|
+
console.log("-".repeat(40));
|
|
4046
|
+
|
|
4047
|
+
const availability = await searchDomain({
|
|
4048
|
+
domain_name: startupName,
|
|
4049
|
+
tlds: tlds
|
|
4050
|
+
});
|
|
4051
|
+
|
|
4052
|
+
const available = [];
|
|
4053
|
+
const taken = [];
|
|
4054
|
+
|
|
4055
|
+
for (const result of availability.results) {
|
|
4056
|
+
const tld = result.domain.split('.').pop();
|
|
4057
|
+
const tldData = tldAnalysis.find(t => t.tld === tld);
|
|
4058
|
+
|
|
4059
|
+
if (result.available) {
|
|
4060
|
+
available.push({ ...result, tldInfo: tldData });
|
|
4061
|
+
console.log(` ✅ ${result.domain.padEnd(25)} $${result.price_first_year?.toFixed(2) || 'N/A'}/yr`);
|
|
4062
|
+
} else {
|
|
4063
|
+
taken.push({ ...result, tldInfo: tldData });
|
|
4064
|
+
console.log(` ❌ ${result.domain.padEnd(25)} (taken)`);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
// Section 3: Social Media Check
|
|
4069
|
+
console.log(`\n\n🌐 SOCIAL MEDIA AVAILABILITY`);
|
|
4070
|
+
console.log("-".repeat(40));
|
|
4071
|
+
|
|
4072
|
+
const socials = await checkSocials({
|
|
4073
|
+
name: startupName,
|
|
4074
|
+
platforms: ["github", "twitter", "npm", "pypi", "reddit"]
|
|
4075
|
+
});
|
|
4076
|
+
|
|
4077
|
+
for (const platform of socials.platforms) {
|
|
4078
|
+
const status = platform.available ? "✅ Available" : "❌ Taken";
|
|
4079
|
+
const confidence = platform.confidence ? ` (${platform.confidence})` : '';
|
|
4080
|
+
console.log(` ${platform.platform.padEnd(15)} ${status}${confidence}`);
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
// Section 4: AI-Powered Alternatives (if main domain taken)
|
|
4084
|
+
if (taken.length > 0) {
|
|
4085
|
+
console.log(`\n\n💡 AI-POWERED ALTERNATIVES`);
|
|
4086
|
+
console.log("-".repeat(40));
|
|
4087
|
+
|
|
4088
|
+
const suggestions = await suggestDomainsSmart({
|
|
4089
|
+
query: startupName,
|
|
4090
|
+
tld: "com",
|
|
4091
|
+
style: "brandable",
|
|
4092
|
+
max_suggestions: 10
|
|
4093
|
+
});
|
|
4094
|
+
|
|
4095
|
+
console.log("\n Top 10 Available Alternatives:");
|
|
4096
|
+
for (let i = 0; i < suggestions.suggestions.length; i++) {
|
|
4097
|
+
const s = suggestions.suggestions[i];
|
|
4098
|
+
console.log(` ${(i+1).toString().padStart(2)}. ${s.domain.padEnd(25)} $${s.price?.toFixed(2) || 'N/A'}/yr`);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// Section 5: Recommendation Summary
|
|
4103
|
+
console.log(`\n\n📌 RECOMMENDATION`);
|
|
4104
|
+
console.log("-".repeat(40));
|
|
4105
|
+
|
|
4106
|
+
if (available.length > 0) {
|
|
4107
|
+
// Sort by price to find best value
|
|
4108
|
+
const bestValue = available.sort((a, b) =>
|
|
4109
|
+
(a.price_first_year || 999) - (b.price_first_year || 999)
|
|
4110
|
+
)[0];
|
|
4111
|
+
|
|
4112
|
+
// Find tech-focused option
|
|
4113
|
+
const techOption = available.find(a =>
|
|
4114
|
+
['io', 'dev', 'app'].includes(a.domain.split('.').pop())
|
|
4115
|
+
);
|
|
4116
|
+
|
|
4117
|
+
console.log(`\n 🏆 BEST VALUE: ${bestValue.domain}`);
|
|
4118
|
+
console.log(` Price: $${bestValue.price_first_year}/year`);
|
|
4119
|
+
console.log(` Why: ${bestValue.tldInfo?.recommendation || 'Lowest cost option'}`);
|
|
4120
|
+
|
|
4121
|
+
if (techOption && techOption.domain !== bestValue.domain) {
|
|
4122
|
+
console.log(`\n 💻 TECH STARTUP PICK: ${techOption.domain}`);
|
|
4123
|
+
console.log(` Price: $${techOption.price_first_year}/year`);
|
|
4124
|
+
console.log(` Why: ${techOption.tldInfo?.recommendation || 'Popular with tech startups'}`);
|
|
4125
|
+
}
|
|
4126
|
+
} else {
|
|
4127
|
+
console.log("\n ⚠️ Primary domain unavailable across all TLDs.");
|
|
4128
|
+
console.log(" → Consider alternatives listed above");
|
|
4129
|
+
console.log(" → Check social media availability for brand consistency");
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
console.log(`\n${'='.repeat(60)}\n`);
|
|
4133
|
+
|
|
4134
|
+
return {
|
|
4135
|
+
startupName,
|
|
4136
|
+
generatedAt: new Date().toISOString(),
|
|
4137
|
+
tldAnalysis,
|
|
4138
|
+
availability: { available, taken },
|
|
4139
|
+
socialMedia: socials.platforms,
|
|
4140
|
+
recommendations: available.length > 0
|
|
4141
|
+
? available.sort((a, b) => (a.price_first_year || 999) - (b.price_first_year || 999))
|
|
4142
|
+
: []
|
|
4143
|
+
};
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
// Generate report for a startup
|
|
4147
|
+
const report = await generateStartupDomainReport("nexaflow");
|
|
4148
|
+
```
|
|
4149
|
+
|
|
4150
|
+
**Sample Output:**
|
|
4151
|
+
```
|
|
4152
|
+
============================================================
|
|
4153
|
+
DOMAIN RESEARCH REPORT: NEXAFLOW
|
|
4154
|
+
Generated: 2024-01-15
|
|
4155
|
+
============================================================
|
|
4156
|
+
|
|
4157
|
+
📋 TLD ANALYSIS
|
|
4158
|
+
----------------------------------------
|
|
4159
|
+
|
|
4160
|
+
.COM
|
|
4161
|
+
└─ The original and most recognized domain extension
|
|
4162
|
+
└─ Price: $8.95 - $12.95/year
|
|
4163
|
+
└─ Best for: Any business or personal website
|
|
4164
|
+
└─ Popularity: Very High
|
|
4165
|
+
|
|
4166
|
+
.IO
|
|
4167
|
+
└─ Popular with tech startups and SaaS companies
|
|
4168
|
+
└─ Price: $32.95 - $44.95/year
|
|
4169
|
+
└─ Best for: Tech startups, APIs, developer tools
|
|
4170
|
+
└─ Popularity: High
|
|
4171
|
+
|
|
4172
|
+
.DEV
|
|
4173
|
+
└─ Google-operated TLD for developers
|
|
4174
|
+
└─ Price: $12.95 - $16.95/year
|
|
4175
|
+
└─ Best for: Developer portfolios, open source projects
|
|
4176
|
+
└─ Popularity: Medium
|
|
4177
|
+
└─ ⚠️ Restrictions: Requires HTTPS
|
|
4178
|
+
|
|
4179
|
+
|
|
4180
|
+
📊 AVAILABILITY CHECK
|
|
4181
|
+
----------------------------------------
|
|
4182
|
+
✅ nexaflow.com $9.95/yr
|
|
4183
|
+
❌ nexaflow.io (taken)
|
|
4184
|
+
✅ nexaflow.dev $14.95/yr
|
|
4185
|
+
✅ nexaflow.app $14.95/yr
|
|
4186
|
+
✅ nexaflow.co $29.95/yr
|
|
4187
|
+
|
|
4188
|
+
|
|
4189
|
+
🌐 SOCIAL MEDIA AVAILABILITY
|
|
4190
|
+
----------------------------------------
|
|
4191
|
+
github ✅ Available (high)
|
|
4192
|
+
twitter ❌ Taken (high)
|
|
4193
|
+
npm ✅ Available (high)
|
|
4194
|
+
pypi ✅ Available (high)
|
|
4195
|
+
reddit ✅ Available (high)
|
|
4196
|
+
|
|
4197
|
+
|
|
4198
|
+
📌 RECOMMENDATION
|
|
4199
|
+
----------------------------------------
|
|
4200
|
+
|
|
4201
|
+
🏆 BEST VALUE: nexaflow.com
|
|
4202
|
+
Price: $9.95/year
|
|
4203
|
+
Why: Classic, universal choice - trusted by all audiences
|
|
4204
|
+
|
|
4205
|
+
💻 TECH STARTUP PICK: nexaflow.dev
|
|
4206
|
+
Price: $14.95/year
|
|
4207
|
+
Why: Signals technical focus, requires HTTPS
|
|
4208
|
+
|
|
4209
|
+
============================================================
|
|
4210
|
+
```
|
|
4211
|
+
|
|
3671
4212
|
### Workflow 7: Validate 50 Domains with Result Aggregation
|
|
3672
4213
|
|
|
3673
4214
|
End-to-end workflow for validating exactly 50 domain names with comprehensive result handling:
|
|
@@ -3883,6 +4424,485 @@ async function domainResearchPipeline(businessIdea: string) {
|
|
|
3883
4424
|
const research = await domainResearchPipeline("ai-powered code review tool");
|
|
3884
4425
|
```
|
|
3885
4426
|
|
|
4427
|
+
#### Startup-Type Optimized Parameters
|
|
4428
|
+
|
|
4429
|
+
Different startup types require different tool parameter configurations for optimal results:
|
|
4430
|
+
|
|
4431
|
+
```typescript
|
|
4432
|
+
// Startup type definitions with optimized tool parameters
|
|
4433
|
+
const STARTUP_TYPE_CONFIGS = {
|
|
4434
|
+
tech: {
|
|
4435
|
+
// Tech startups: developer tools, SaaS, APIs
|
|
4436
|
+
tlds: ["io", "dev", "app", "com", "sh"],
|
|
4437
|
+
suggestStyle: "brandable" as const,
|
|
4438
|
+
socialPlatforms: ["github", "twitter", "npm", "pypi", "producthunt"],
|
|
4439
|
+
priceThreshold: 50, // Tech startups accept higher TLD costs
|
|
4440
|
+
industryHint: "tech" as const
|
|
4441
|
+
},
|
|
4442
|
+
ecommerce: {
|
|
4443
|
+
// E-commerce: retail, marketplace, DTC brands
|
|
4444
|
+
tlds: ["com", "co", "shop", "store", "io"],
|
|
4445
|
+
suggestStyle: "brandable" as const,
|
|
4446
|
+
socialPlatforms: ["instagram", "twitter", "tiktok", "pinterest"],
|
|
4447
|
+
priceThreshold: 25, // Lower threshold, .com preferred
|
|
4448
|
+
industryHint: "ecommerce" as const
|
|
4449
|
+
},
|
|
4450
|
+
creative: {
|
|
4451
|
+
// Creative agencies: design, media, content
|
|
4452
|
+
tlds: ["design", "studio", "io", "co", "com"],
|
|
4453
|
+
suggestStyle: "creative" as const,
|
|
4454
|
+
socialPlatforms: ["instagram", "twitter", "dribbble", "behance"],
|
|
4455
|
+
priceThreshold: 35,
|
|
4456
|
+
industryHint: "creative" as const
|
|
4457
|
+
},
|
|
4458
|
+
fintech: {
|
|
4459
|
+
// Financial technology: payments, banking, crypto
|
|
4460
|
+
tlds: ["com", "io", "finance", "money", "app"],
|
|
4461
|
+
suggestStyle: "brandable" as const,
|
|
4462
|
+
socialPlatforms: ["twitter", "linkedin", "github"],
|
|
4463
|
+
priceThreshold: 100, // Premium domains acceptable
|
|
4464
|
+
industryHint: "finance" as const
|
|
4465
|
+
},
|
|
4466
|
+
healthcare: {
|
|
4467
|
+
// Healthcare & wellness startups
|
|
4468
|
+
tlds: ["health", "care", "com", "io", "app"],
|
|
4469
|
+
suggestStyle: "descriptive" as const,
|
|
4470
|
+
socialPlatforms: ["twitter", "linkedin", "facebook"],
|
|
4471
|
+
priceThreshold: 40,
|
|
4472
|
+
industryHint: "health" as const
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4475
|
+
|
|
4476
|
+
type StartupType = keyof typeof STARTUP_TYPE_CONFIGS;
|
|
4477
|
+
```
|
|
4478
|
+
|
|
4479
|
+
#### Complete Startup Research Pipeline with Error Recovery
|
|
4480
|
+
|
|
4481
|
+
Production-ready pipeline with comprehensive error handling and retry logic:
|
|
4482
|
+
|
|
4483
|
+
```typescript
|
|
4484
|
+
import {
|
|
4485
|
+
searchDomain,
|
|
4486
|
+
tldInfo,
|
|
4487
|
+
suggestDomainsSmart,
|
|
4488
|
+
checkSocials,
|
|
4489
|
+
compareRegistrars
|
|
4490
|
+
} from 'domain-search-mcp';
|
|
4491
|
+
|
|
4492
|
+
interface PipelineResult {
|
|
4493
|
+
success: boolean;
|
|
4494
|
+
startupName: string;
|
|
4495
|
+
startupType: StartupType;
|
|
4496
|
+
domains: DomainRecommendation[];
|
|
4497
|
+
socialAnalysis: SocialAnalysis[];
|
|
4498
|
+
tldContext: TldContext[];
|
|
4499
|
+
errors: PipelineError[];
|
|
4500
|
+
executionTime: number;
|
|
4501
|
+
}
|
|
4502
|
+
|
|
4503
|
+
interface PipelineError {
|
|
4504
|
+
stage: string;
|
|
4505
|
+
error: string;
|
|
4506
|
+
recoverable: boolean;
|
|
4507
|
+
fallbackUsed?: string;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
// Retry wrapper with exponential backoff
|
|
4511
|
+
async function withRetry<T>(
|
|
4512
|
+
fn: () => Promise<T>,
|
|
4513
|
+
options: { maxRetries: number; baseDelay: number; stageName: string }
|
|
4514
|
+
): Promise<{ result: T | null; error: PipelineError | null }> {
|
|
4515
|
+
let lastError: Error | null = null;
|
|
4516
|
+
|
|
4517
|
+
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
|
|
4518
|
+
try {
|
|
4519
|
+
const result = await fn();
|
|
4520
|
+
return { result, error: null };
|
|
4521
|
+
} catch (err) {
|
|
4522
|
+
lastError = err as Error;
|
|
4523
|
+
|
|
4524
|
+
if (attempt < options.maxRetries) {
|
|
4525
|
+
const delay = options.baseDelay * Math.pow(2, attempt);
|
|
4526
|
+
console.log(`⚠️ ${options.stageName} failed, retrying in ${delay}ms...`);
|
|
4527
|
+
await new Promise(r => setTimeout(r, delay));
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4532
|
+
return {
|
|
4533
|
+
result: null,
|
|
4534
|
+
error: {
|
|
4535
|
+
stage: options.stageName,
|
|
4536
|
+
error: lastError?.message || 'Unknown error',
|
|
4537
|
+
recoverable: false
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
async function startupDomainResearchPipeline(
|
|
4543
|
+
startupName: string,
|
|
4544
|
+
startupType: StartupType = 'tech'
|
|
4545
|
+
): Promise<PipelineResult> {
|
|
4546
|
+
const startTime = Date.now();
|
|
4547
|
+
const config = STARTUP_TYPE_CONFIGS[startupType];
|
|
4548
|
+
const errors: PipelineError[] = [];
|
|
4549
|
+
|
|
4550
|
+
console.log(`\n🚀 Starting domain research for "${startupName}" (${startupType} startup)`);
|
|
4551
|
+
console.log(` Preferred TLDs: ${config.tlds.join(', ')}`);
|
|
4552
|
+
console.log(` Social platforms: ${config.socialPlatforms.join(', ')}`);
|
|
4553
|
+
|
|
4554
|
+
// Stage 1: Get TLD context with error recovery
|
|
4555
|
+
console.log('\n📋 Stage 1: Fetching TLD information...');
|
|
4556
|
+
const tldResults: TldContext[] = [];
|
|
4557
|
+
|
|
4558
|
+
for (const tld of config.tlds) {
|
|
4559
|
+
const { result, error } = await withRetry(
|
|
4560
|
+
() => tldInfo({ tld, detailed: true }),
|
|
4561
|
+
{ maxRetries: 2, baseDelay: 1000, stageName: `tld_info(${tld})` }
|
|
4562
|
+
);
|
|
4563
|
+
|
|
4564
|
+
if (result) {
|
|
4565
|
+
tldResults.push({
|
|
4566
|
+
tld: result.tld,
|
|
4567
|
+
description: result.description,
|
|
4568
|
+
priceRange: result.price_range,
|
|
4569
|
+
typicalUse: result.typical_use,
|
|
4570
|
+
restrictions: result.restrictions,
|
|
4571
|
+
recommendation: result.recommendation
|
|
4572
|
+
});
|
|
4573
|
+
console.log(` ✅ .${tld}: ${result.description.substring(0, 50)}...`);
|
|
4574
|
+
} else if (error) {
|
|
4575
|
+
errors.push(error);
|
|
4576
|
+
console.log(` ⚠️ .${tld}: Failed to fetch info`);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
// Stage 2: Generate smart suggestions with startup context
|
|
4581
|
+
console.log('\n💡 Stage 2: Generating domain suggestions...');
|
|
4582
|
+
|
|
4583
|
+
const { result: suggestions, error: suggestError } = await withRetry(
|
|
4584
|
+
() => suggestDomainsSmart({
|
|
4585
|
+
query: startupName,
|
|
4586
|
+
tld: config.tlds[0], // Primary TLD
|
|
4587
|
+
style: config.suggestStyle,
|
|
4588
|
+
industry: config.industryHint,
|
|
4589
|
+
max_suggestions: 20,
|
|
4590
|
+
include_premium: config.priceThreshold > 50
|
|
4591
|
+
}),
|
|
4592
|
+
{ maxRetries: 2, baseDelay: 1500, stageName: 'suggest_domains_smart' }
|
|
4593
|
+
);
|
|
4594
|
+
|
|
4595
|
+
if (suggestError) {
|
|
4596
|
+
errors.push(suggestError);
|
|
4597
|
+
console.log(' ⚠️ Smart suggestions failed, falling back to basic search');
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
// Stage 3: Check availability across all preferred TLDs
|
|
4601
|
+
console.log('\n🔍 Stage 3: Checking domain availability...');
|
|
4602
|
+
|
|
4603
|
+
const { result: availability, error: searchError } = await withRetry(
|
|
4604
|
+
() => searchDomain({
|
|
4605
|
+
domain_name: startupName,
|
|
4606
|
+
tlds: config.tlds
|
|
4607
|
+
}),
|
|
4608
|
+
{ maxRetries: 3, baseDelay: 1000, stageName: 'search_domain' }
|
|
4609
|
+
);
|
|
4610
|
+
|
|
4611
|
+
if (searchError) {
|
|
4612
|
+
errors.push(searchError);
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
// Stage 4: Social media availability with platform-specific handling
|
|
4616
|
+
console.log('\n🌐 Stage 4: Checking social media availability...');
|
|
4617
|
+
|
|
4618
|
+
const { result: socialResults, error: socialError } = await withRetry(
|
|
4619
|
+
() => checkSocials({
|
|
4620
|
+
name: startupName.toLowerCase().replace(/[^a-z0-9]/g, ''),
|
|
4621
|
+
platforms: config.socialPlatforms
|
|
4622
|
+
}),
|
|
4623
|
+
{ maxRetries: 2, baseDelay: 2000, stageName: 'check_socials' }
|
|
4624
|
+
);
|
|
4625
|
+
|
|
4626
|
+
// Process social results with confidence levels
|
|
4627
|
+
const socialAnalysis: SocialAnalysis[] = [];
|
|
4628
|
+
if (socialResults) {
|
|
4629
|
+
for (const platform of socialResults.platforms) {
|
|
4630
|
+
socialAnalysis.push({
|
|
4631
|
+
platform: platform.platform,
|
|
4632
|
+
available: platform.available,
|
|
4633
|
+
confidence: platform.confidence || 'unknown',
|
|
4634
|
+
url: platform.profile_url,
|
|
4635
|
+
recommendation: getSocialRecommendation(platform, startupType)
|
|
4636
|
+
});
|
|
4637
|
+
|
|
4638
|
+
const status = platform.available ? '✅' : '❌';
|
|
4639
|
+
const conf = platform.confidence ? ` (${platform.confidence})` : '';
|
|
4640
|
+
console.log(` ${status} ${platform.platform}${conf}`);
|
|
4641
|
+
}
|
|
4642
|
+
} else if (socialError) {
|
|
4643
|
+
errors.push({
|
|
4644
|
+
...socialError,
|
|
4645
|
+
fallbackUsed: 'Manual verification recommended'
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
|
|
4649
|
+
// Stage 5: Price comparison for available domains
|
|
4650
|
+
console.log('\n💰 Stage 5: Comparing registrar pricing...');
|
|
4651
|
+
|
|
4652
|
+
const domains: DomainRecommendation[] = [];
|
|
4653
|
+
const availableDomains = availability?.results.filter(r => r.available) || [];
|
|
4654
|
+
|
|
4655
|
+
for (const domain of availableDomains.slice(0, 5)) {
|
|
4656
|
+
const domainName = domain.domain.split('.')[0];
|
|
4657
|
+
const tld = domain.domain.split('.').pop()!;
|
|
4658
|
+
|
|
4659
|
+
const { result: pricing } = await withRetry(
|
|
4660
|
+
() => compareRegistrars({ domain: domainName, tld }),
|
|
4661
|
+
{ maxRetries: 1, baseDelay: 1000, stageName: `compare_registrars(${domain.domain})` }
|
|
4662
|
+
);
|
|
4663
|
+
|
|
4664
|
+
const tldContext = tldResults.find(t => t.tld === tld);
|
|
4665
|
+
|
|
4666
|
+
domains.push({
|
|
4667
|
+
domain: domain.domain,
|
|
4668
|
+
available: true,
|
|
4669
|
+
priceFirstYear: pricing?.best_first_year?.price || domain.price_first_year,
|
|
4670
|
+
priceRenewal: pricing?.best_renewal?.price,
|
|
4671
|
+
bestRegistrar: pricing?.best_first_year?.registrar || domain.registrar,
|
|
4672
|
+
tldInfo: tldContext,
|
|
4673
|
+
withinBudget: (domain.price_first_year || 0) <= config.priceThreshold,
|
|
4674
|
+
score: calculateDomainScore(domain, tldContext, socialAnalysis, config)
|
|
4675
|
+
});
|
|
4676
|
+
|
|
4677
|
+
console.log(` ${domain.domain}: $${domain.price_first_year}/yr`);
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// Sort by score
|
|
4681
|
+
domains.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
4682
|
+
|
|
4683
|
+
const executionTime = Date.now() - startTime;
|
|
4684
|
+
|
|
4685
|
+
console.log(`\n✨ Research complete in ${executionTime}ms`);
|
|
4686
|
+
console.log(` ${domains.length} available domains found`);
|
|
4687
|
+
console.log(` ${errors.length} errors encountered`);
|
|
4688
|
+
|
|
4689
|
+
return {
|
|
4690
|
+
success: errors.length === 0,
|
|
4691
|
+
startupName,
|
|
4692
|
+
startupType,
|
|
4693
|
+
domains,
|
|
4694
|
+
socialAnalysis,
|
|
4695
|
+
tldContext: tldResults,
|
|
4696
|
+
errors,
|
|
4697
|
+
executionTime
|
|
4698
|
+
};
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4701
|
+
// Helper: Calculate domain score based on multiple factors
|
|
4702
|
+
function calculateDomainScore(
|
|
4703
|
+
domain: any,
|
|
4704
|
+
tldContext: TldContext | undefined,
|
|
4705
|
+
social: SocialAnalysis[],
|
|
4706
|
+
config: typeof STARTUP_TYPE_CONFIGS[StartupType]
|
|
4707
|
+
): number {
|
|
4708
|
+
let score = 50; // Base score
|
|
4709
|
+
|
|
4710
|
+
// Price factor (lower is better, up to threshold)
|
|
4711
|
+
const price = domain.price_first_year || 0;
|
|
4712
|
+
if (price <= config.priceThreshold * 0.5) score += 20;
|
|
4713
|
+
else if (price <= config.priceThreshold) score += 10;
|
|
4714
|
+
else score -= 10;
|
|
4715
|
+
|
|
4716
|
+
// TLD preference factor
|
|
4717
|
+
const tldIndex = config.tlds.indexOf(domain.domain.split('.').pop());
|
|
4718
|
+
if (tldIndex === 0) score += 15; // Primary TLD
|
|
4719
|
+
else if (tldIndex <= 2) score += 10;
|
|
4720
|
+
else if (tldIndex > -1) score += 5;
|
|
4721
|
+
|
|
4722
|
+
// Social availability factor
|
|
4723
|
+
const availableSocials = social.filter(s => s.available).length;
|
|
4724
|
+
score += availableSocials * 5;
|
|
4725
|
+
|
|
4726
|
+
// Length factor (shorter is better)
|
|
4727
|
+
const nameLength = domain.domain.split('.')[0].length;
|
|
4728
|
+
if (nameLength <= 6) score += 10;
|
|
4729
|
+
else if (nameLength <= 10) score += 5;
|
|
4730
|
+
else if (nameLength > 15) score -= 5;
|
|
4731
|
+
|
|
4732
|
+
return Math.max(0, Math.min(100, score));
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
// Helper: Get platform-specific recommendations
|
|
4736
|
+
function getSocialRecommendation(
|
|
4737
|
+
platform: any,
|
|
4738
|
+
startupType: StartupType
|
|
4739
|
+
): string {
|
|
4740
|
+
if (platform.available) {
|
|
4741
|
+
return `Register @${platform.name} immediately to secure brand consistency`;
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
const alternatives: Record<string, string> = {
|
|
4745
|
+
twitter: 'Consider @get[name], @[name]app, or @[name]hq',
|
|
4746
|
+
github: 'Use organization account or add suffix like -app, -io',
|
|
4747
|
+
instagram: 'Try [name].official, get[name], or [name]_app',
|
|
4748
|
+
npm: 'Use scoped package @[org]/[name]',
|
|
4749
|
+
linkedin: 'Create company page with full business name'
|
|
4750
|
+
};
|
|
4751
|
+
|
|
4752
|
+
return alternatives[platform.platform] || 'Consider alternative naming';
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4755
|
+
// Usage with different startup types
|
|
4756
|
+
const techStartupReport = await startupDomainResearchPipeline("codeflow", "tech");
|
|
4757
|
+
const ecommerceReport = await startupDomainResearchPipeline("shopwave", "ecommerce");
|
|
4758
|
+
const creativeReport = await startupDomainResearchPipeline("pixelcraft", "creative");
|
|
4759
|
+
```
|
|
4760
|
+
|
|
4761
|
+
#### Formatted Report Output by Startup Type
|
|
4762
|
+
|
|
4763
|
+
Generate customized reports based on startup category:
|
|
4764
|
+
|
|
4765
|
+
```typescript
|
|
4766
|
+
function formatStartupReport(result: PipelineResult): string {
|
|
4767
|
+
const { startupName, startupType, domains, socialAnalysis, tldContext, errors } = result;
|
|
4768
|
+
|
|
4769
|
+
let report = `
|
|
4770
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
4771
|
+
║ DOMAIN RESEARCH REPORT: ${startupName.toUpperCase().padEnd(35)}║
|
|
4772
|
+
║ Type: ${startupType.toUpperCase()} STARTUP${' '.repeat(45 - startupType.length)}║
|
|
4773
|
+
║ Generated: ${new Date().toISOString().split('T')[0]}${' '.repeat(40)}║
|
|
4774
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
4775
|
+
`;
|
|
4776
|
+
|
|
4777
|
+
// TLD Analysis Section
|
|
4778
|
+
report += `\n┌─ TLD ANALYSIS ${'─'.repeat(46)}┐\n`;
|
|
4779
|
+
for (const tld of tldContext) {
|
|
4780
|
+
report += `│ .${tld.tld.padEnd(6)} │ ${tld.description.substring(0, 40).padEnd(40)} │\n`;
|
|
4781
|
+
report += `│ │ Price: $${tld.priceRange.min}-$${tld.priceRange.max}/yr`.padEnd(52) + `│\n`;
|
|
4782
|
+
}
|
|
4783
|
+
report += `└${'─'.repeat(60)}┘\n`;
|
|
4784
|
+
|
|
4785
|
+
// Domain Recommendations Section
|
|
4786
|
+
report += `\n┌─ TOP DOMAIN RECOMMENDATIONS ${'─'.repeat(31)}┐\n`;
|
|
4787
|
+
report += `│ Rank │ Domain${' '.repeat(20)}│ Price │ Score │\n`;
|
|
4788
|
+
report += `├──────┼${'-'.repeat(26)}┼────────┼───────┤\n`;
|
|
4789
|
+
|
|
4790
|
+
domains.slice(0, 5).forEach((d, i) => {
|
|
4791
|
+
const rank = `#${i + 1}`.padEnd(4);
|
|
4792
|
+
const domain = d.domain.padEnd(25);
|
|
4793
|
+
const price = `$${d.priceFirstYear}`.padEnd(6);
|
|
4794
|
+
const score = `${d.score}/100`;
|
|
4795
|
+
report += `│ ${rank} │ ${domain} │ ${price} │ ${score.padEnd(5)} │\n`;
|
|
4796
|
+
});
|
|
4797
|
+
report += `└${'─'.repeat(60)}┘\n`;
|
|
4798
|
+
|
|
4799
|
+
// Social Media Section
|
|
4800
|
+
report += `\n┌─ SOCIAL MEDIA AVAILABILITY ${'─'.repeat(32)}┐\n`;
|
|
4801
|
+
for (const social of socialAnalysis) {
|
|
4802
|
+
const status = social.available ? '✅ Available' : '❌ Taken';
|
|
4803
|
+
const conf = social.confidence !== 'unknown' ? ` (${social.confidence})` : '';
|
|
4804
|
+
report += `│ ${social.platform.padEnd(12)} │ ${status}${conf}`.padEnd(48) + `│\n`;
|
|
4805
|
+
if (!social.available) {
|
|
4806
|
+
report += `│ │ → ${social.recommendation.substring(0, 35)}`.padEnd(48) + `│\n`;
|
|
4807
|
+
}
|
|
4808
|
+
}
|
|
4809
|
+
report += `└${'─'.repeat(60)}┘\n`;
|
|
4810
|
+
|
|
4811
|
+
// Recommendations by startup type
|
|
4812
|
+
report += `\n┌─ ${startupType.toUpperCase()} STARTUP RECOMMENDATIONS ${'─'.repeat(26)}┐\n`;
|
|
4813
|
+
|
|
4814
|
+
const typeRecommendations: Record<StartupType, string[]> = {
|
|
4815
|
+
tech: [
|
|
4816
|
+
'→ Prioritize .io or .dev for developer credibility',
|
|
4817
|
+
'→ Secure GitHub org and npm package name',
|
|
4818
|
+
'→ Consider .sh for CLI tools'
|
|
4819
|
+
],
|
|
4820
|
+
ecommerce: [
|
|
4821
|
+
'→ .com is essential for consumer trust',
|
|
4822
|
+
'→ Secure Instagram and TikTok handles',
|
|
4823
|
+
'→ Consider .shop or .store as secondary'
|
|
4824
|
+
],
|
|
4825
|
+
creative: [
|
|
4826
|
+
'→ .design or .studio signals creative focus',
|
|
4827
|
+
'→ Instagram presence is critical',
|
|
4828
|
+
'→ Shorter names work better for branding'
|
|
4829
|
+
],
|
|
4830
|
+
fintech: [
|
|
4831
|
+
'→ .com required for financial credibility',
|
|
4832
|
+
'→ Avoid hyphens - trust signals matter',
|
|
4833
|
+
'→ LinkedIn company page essential'
|
|
4834
|
+
],
|
|
4835
|
+
healthcare: [
|
|
4836
|
+
'→ .health TLD builds trust',
|
|
4837
|
+
'→ Avoid playful names - professionalism matters',
|
|
4838
|
+
'→ Verify regulatory compliance for domain use'
|
|
4839
|
+
]
|
|
4840
|
+
};
|
|
4841
|
+
|
|
4842
|
+
for (const rec of typeRecommendations[startupType]) {
|
|
4843
|
+
report += `│ ${rec.padEnd(58)} │\n`;
|
|
4844
|
+
}
|
|
4845
|
+
report += `└${'─'.repeat(60)}┘\n`;
|
|
4846
|
+
|
|
4847
|
+
// Error summary if any
|
|
4848
|
+
if (errors.length > 0) {
|
|
4849
|
+
report += `\n⚠️ WARNINGS: ${errors.length} issues encountered during research\n`;
|
|
4850
|
+
errors.forEach(e => {
|
|
4851
|
+
report += ` • ${e.stage}: ${e.error}\n`;
|
|
4852
|
+
});
|
|
4853
|
+
}
|
|
4854
|
+
|
|
4855
|
+
return report;
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4858
|
+
// Generate and print formatted report
|
|
4859
|
+
const result = await startupDomainResearchPipeline("nexaflow", "tech");
|
|
4860
|
+
console.log(formatStartupReport(result));
|
|
4861
|
+
```
|
|
4862
|
+
|
|
4863
|
+
**Sample Output:**
|
|
4864
|
+
```
|
|
4865
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
4866
|
+
║ DOMAIN RESEARCH REPORT: NEXAFLOW ║
|
|
4867
|
+
║ Type: TECH STARTUP ║
|
|
4868
|
+
║ Generated: 2024-01-15 ║
|
|
4869
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
4870
|
+
|
|
4871
|
+
┌─ TLD ANALYSIS ──────────────────────────────────────────────┐
|
|
4872
|
+
│ .io │ Popular with tech startups and SaaS │
|
|
4873
|
+
│ │ Price: $32-$44/yr │
|
|
4874
|
+
│ .dev │ Google TLD for developers │
|
|
4875
|
+
│ │ Price: $12-$16/yr │
|
|
4876
|
+
│ .app │ Mobile and web applications │
|
|
4877
|
+
│ │ Price: $14-$18/yr │
|
|
4878
|
+
└──────────────────────────────────────────────────────────────┘
|
|
4879
|
+
|
|
4880
|
+
┌─ TOP DOMAIN RECOMMENDATIONS ─────────────────────────────────┐
|
|
4881
|
+
│ Rank │ Domain │ Price │ Score │
|
|
4882
|
+
├──────┼──────────────────────────┼────────┼───────┤
|
|
4883
|
+
│ #1 │ nexaflow.dev │ $14.95 │ 85/100│
|
|
4884
|
+
│ #2 │ nexaflow.io │ $39.95 │ 78/100│
|
|
4885
|
+
│ #3 │ nexaflow.app │ $14.95 │ 75/100│
|
|
4886
|
+
│ #4 │ nexaflow.com │ $9.95 │ 72/100│
|
|
4887
|
+
│ #5 │ nexaflow.sh │ $24.95 │ 65/100│
|
|
4888
|
+
└──────────────────────────────────────────────────────────────┘
|
|
4889
|
+
|
|
4890
|
+
┌─ SOCIAL MEDIA AVAILABILITY ──────────────────────────────────┐
|
|
4891
|
+
│ github │ ✅ Available (high) │
|
|
4892
|
+
│ twitter │ ❌ Taken (high) │
|
|
4893
|
+
│ │ → Consider @getnexaflow, @nexaflowapp │
|
|
4894
|
+
│ npm │ ✅ Available (high) │
|
|
4895
|
+
│ pypi │ ✅ Available (high) │
|
|
4896
|
+
│ producthunt │ ✅ Available (medium) │
|
|
4897
|
+
└──────────────────────────────────────────────────────────────┘
|
|
4898
|
+
|
|
4899
|
+
┌─ TECH STARTUP RECOMMENDATIONS ───────────────────────────────┐
|
|
4900
|
+
│ → Prioritize .io or .dev for developer credibility │
|
|
4901
|
+
│ → Secure GitHub org and npm package name │
|
|
4902
|
+
│ → Consider .sh for CLI tools │
|
|
4903
|
+
└──────────────────────────────────────────────────────────────┘
|
|
4904
|
+
```
|
|
4905
|
+
|
|
3886
4906
|
## Security
|
|
3887
4907
|
|
|
3888
4908
|
- 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.2.
|
|
3
|
+
"version": "1.2.8",
|
|
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",
|