domain-search-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.env.example +52 -0
  2. package/Dockerfile +15 -0
  3. package/LICENSE +21 -0
  4. package/README.md +426 -0
  5. package/SECURITY.md +252 -0
  6. package/dist/config.d.ts +25 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +117 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/fallbacks/index.d.ts +6 -0
  11. package/dist/fallbacks/index.d.ts.map +1 -0
  12. package/dist/fallbacks/index.js +14 -0
  13. package/dist/fallbacks/index.js.map +1 -0
  14. package/dist/fallbacks/rdap.d.ts +18 -0
  15. package/dist/fallbacks/rdap.d.ts.map +1 -0
  16. package/dist/fallbacks/rdap.js +339 -0
  17. package/dist/fallbacks/rdap.js.map +1 -0
  18. package/dist/fallbacks/whois.d.ts +27 -0
  19. package/dist/fallbacks/whois.d.ts.map +1 -0
  20. package/dist/fallbacks/whois.js +219 -0
  21. package/dist/fallbacks/whois.js.map +1 -0
  22. package/dist/registrars/base.d.ts +89 -0
  23. package/dist/registrars/base.d.ts.map +1 -0
  24. package/dist/registrars/base.js +203 -0
  25. package/dist/registrars/base.js.map +1 -0
  26. package/dist/registrars/index.d.ts +7 -0
  27. package/dist/registrars/index.d.ts.map +1 -0
  28. package/dist/registrars/index.js +15 -0
  29. package/dist/registrars/index.js.map +1 -0
  30. package/dist/registrars/namecheap.d.ts +69 -0
  31. package/dist/registrars/namecheap.d.ts.map +1 -0
  32. package/dist/registrars/namecheap.js +307 -0
  33. package/dist/registrars/namecheap.js.map +1 -0
  34. package/dist/registrars/porkbun.d.ts +63 -0
  35. package/dist/registrars/porkbun.d.ts.map +1 -0
  36. package/dist/registrars/porkbun.js +299 -0
  37. package/dist/registrars/porkbun.js.map +1 -0
  38. package/dist/server.d.ts +19 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +209 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/services/domain-search.d.ts +40 -0
  43. package/dist/services/domain-search.d.ts.map +1 -0
  44. package/dist/services/domain-search.js +438 -0
  45. package/dist/services/domain-search.js.map +1 -0
  46. package/dist/services/index.d.ts +5 -0
  47. package/dist/services/index.d.ts.map +1 -0
  48. package/dist/services/index.js +11 -0
  49. package/dist/services/index.js.map +1 -0
  50. package/dist/tools/bulk_search.d.ts +72 -0
  51. package/dist/tools/bulk_search.d.ts.map +1 -0
  52. package/dist/tools/bulk_search.js +108 -0
  53. package/dist/tools/bulk_search.js.map +1 -0
  54. package/dist/tools/check_socials.d.ts +71 -0
  55. package/dist/tools/check_socials.d.ts.map +1 -0
  56. package/dist/tools/check_socials.js +357 -0
  57. package/dist/tools/check_socials.js.map +1 -0
  58. package/dist/tools/compare_registrars.d.ts +80 -0
  59. package/dist/tools/compare_registrars.d.ts.map +1 -0
  60. package/dist/tools/compare_registrars.js +116 -0
  61. package/dist/tools/compare_registrars.js.map +1 -0
  62. package/dist/tools/index.d.ts +10 -0
  63. package/dist/tools/index.d.ts.map +1 -0
  64. package/dist/tools/index.js +31 -0
  65. package/dist/tools/index.js.map +1 -0
  66. package/dist/tools/search_domain.d.ts +61 -0
  67. package/dist/tools/search_domain.d.ts.map +1 -0
  68. package/dist/tools/search_domain.js +81 -0
  69. package/dist/tools/search_domain.js.map +1 -0
  70. package/dist/tools/suggest_domains.d.ts +82 -0
  71. package/dist/tools/suggest_domains.d.ts.map +1 -0
  72. package/dist/tools/suggest_domains.js +227 -0
  73. package/dist/tools/suggest_domains.js.map +1 -0
  74. package/dist/tools/tld_info.d.ts +56 -0
  75. package/dist/tools/tld_info.d.ts.map +1 -0
  76. package/dist/tools/tld_info.js +273 -0
  77. package/dist/tools/tld_info.js.map +1 -0
  78. package/dist/types.d.ts +193 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +9 -0
  81. package/dist/types.js.map +1 -0
  82. package/dist/utils/cache.d.ts +81 -0
  83. package/dist/utils/cache.d.ts.map +1 -0
  84. package/dist/utils/cache.js +192 -0
  85. package/dist/utils/cache.js.map +1 -0
  86. package/dist/utils/errors.d.ts +87 -0
  87. package/dist/utils/errors.d.ts.map +1 -0
  88. package/dist/utils/errors.js +191 -0
  89. package/dist/utils/errors.js.map +1 -0
  90. package/dist/utils/index.d.ts +8 -0
  91. package/dist/utils/index.d.ts.map +1 -0
  92. package/dist/utils/index.js +24 -0
  93. package/dist/utils/index.js.map +1 -0
  94. package/dist/utils/logger.d.ts +27 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +132 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/premium-analyzer.d.ts +33 -0
  99. package/dist/utils/premium-analyzer.d.ts.map +1 -0
  100. package/dist/utils/premium-analyzer.js +273 -0
  101. package/dist/utils/premium-analyzer.js.map +1 -0
  102. package/dist/utils/validators.d.ts +53 -0
  103. package/dist/utils/validators.d.ts.map +1 -0
  104. package/dist/utils/validators.js +159 -0
  105. package/dist/utils/validators.js.map +1 -0
  106. package/docs/marketing/devto-post.md +135 -0
  107. package/docs/marketing/hackernews.md +42 -0
  108. package/docs/marketing/producthunt.md +109 -0
  109. package/docs/marketing/reddit-post.md +59 -0
  110. package/docs/marketing/twitter-thread.md +105 -0
  111. package/examples/bulk-search-50-domains.ts +131 -0
  112. package/examples/cli-interactive.ts +280 -0
  113. package/examples/compare-registrars.ts +78 -0
  114. package/examples/search-single-domain.ts +54 -0
  115. package/examples/suggest-names.ts +110 -0
  116. package/glama.json +6 -0
  117. package/jest.config.js +35 -0
  118. package/package.json +62 -0
  119. package/smithery.yaml +36 -0
  120. package/src/config.ts +121 -0
  121. package/src/fallbacks/index.ts +6 -0
  122. package/src/fallbacks/rdap.ts +407 -0
  123. package/src/fallbacks/whois.ts +250 -0
  124. package/src/registrars/base.ts +264 -0
  125. package/src/registrars/index.ts +7 -0
  126. package/src/registrars/namecheap.ts +378 -0
  127. package/src/registrars/porkbun.ts +380 -0
  128. package/src/server.ts +276 -0
  129. package/src/services/domain-search.ts +567 -0
  130. package/src/services/index.ts +9 -0
  131. package/src/tools/bulk_search.ts +142 -0
  132. package/src/tools/check_socials.ts +467 -0
  133. package/src/tools/compare_registrars.ts +162 -0
  134. package/src/tools/index.ts +45 -0
  135. package/src/tools/search_domain.ts +93 -0
  136. package/src/tools/suggest_domains.ts +284 -0
  137. package/src/tools/tld_info.ts +294 -0
  138. package/src/types.ts +289 -0
  139. package/src/utils/cache.ts +238 -0
  140. package/src/utils/errors.ts +262 -0
  141. package/src/utils/index.ts +8 -0
  142. package/src/utils/logger.ts +162 -0
  143. package/src/utils/premium-analyzer.ts +303 -0
  144. package/src/utils/validators.ts +193 -0
  145. package/tests/premium-analyzer.test.ts +310 -0
  146. package/tests/unit/cache.test.ts +123 -0
  147. package/tests/unit/errors.test.ts +190 -0
  148. package/tests/unit/tld-info.test.ts +62 -0
  149. package/tests/unit/tools.test.ts +200 -0
  150. package/tests/unit/validators.test.ts +146 -0
  151. package/tsconfig.json +25 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Unit Tests for Tools.
3
+ */
4
+
5
+ import {
6
+ searchDomainSchema,
7
+ bulkSearchSchema,
8
+ compareRegistrarsSchema,
9
+ suggestDomainsSchema,
10
+ tldInfoSchema,
11
+ checkSocialsSchema,
12
+ } from '../../src/tools';
13
+
14
+ describe('searchDomainSchema', () => {
15
+ it('should accept valid input with domain only', () => {
16
+ const result = searchDomainSchema.parse({
17
+ domain_name: 'vibecoding',
18
+ });
19
+
20
+ expect(result.domain_name).toBe('vibecoding');
21
+ expect(result.tlds).toBeUndefined();
22
+ expect(result.registrars).toBeUndefined();
23
+ });
24
+
25
+ it('should accept valid input with all fields', () => {
26
+ const result = searchDomainSchema.parse({
27
+ domain_name: 'vibecoding',
28
+ tlds: ['com', 'io', 'dev'],
29
+ registrars: ['porkbun'],
30
+ });
31
+
32
+ expect(result.domain_name).toBe('vibecoding');
33
+ expect(result.tlds).toEqual(['com', 'io', 'dev']);
34
+ expect(result.registrars).toEqual(['porkbun']);
35
+ });
36
+
37
+ it('should reject empty domain name', () => {
38
+ expect(() =>
39
+ searchDomainSchema.parse({ domain_name: '' }),
40
+ ).toThrow();
41
+ });
42
+
43
+ it('should reject domain name too long', () => {
44
+ expect(() =>
45
+ searchDomainSchema.parse({ domain_name: 'a'.repeat(64) }),
46
+ ).toThrow();
47
+ });
48
+ });
49
+
50
+ describe('bulkSearchSchema', () => {
51
+ it('should accept valid input', () => {
52
+ const result = bulkSearchSchema.parse({
53
+ domains: ['vibecoding', 'myapp', 'coolstartup'],
54
+ tld: 'com',
55
+ });
56
+
57
+ expect(result.domains).toHaveLength(3);
58
+ expect(result.tld).toBe('com');
59
+ });
60
+
61
+ it('should use default TLD', () => {
62
+ const result = bulkSearchSchema.parse({
63
+ domains: ['vibecoding'],
64
+ });
65
+
66
+ expect(result.tld).toBe('com');
67
+ });
68
+
69
+ it('should reject empty domains array', () => {
70
+ expect(() =>
71
+ bulkSearchSchema.parse({ domains: [] }),
72
+ ).toThrow();
73
+ });
74
+
75
+ it('should reject too many domains', () => {
76
+ const domains = Array.from({ length: 101 }, (_, i) => `domain${i}`);
77
+ expect(() =>
78
+ bulkSearchSchema.parse({ domains }),
79
+ ).toThrow();
80
+ });
81
+ });
82
+
83
+ describe('compareRegistrarsSchema', () => {
84
+ it('should accept valid input', () => {
85
+ const result = compareRegistrarsSchema.parse({
86
+ domain: 'vibecoding',
87
+ tld: 'com',
88
+ });
89
+
90
+ expect(result.domain).toBe('vibecoding');
91
+ expect(result.tld).toBe('com');
92
+ });
93
+
94
+ it('should accept specific registrars', () => {
95
+ const result = compareRegistrarsSchema.parse({
96
+ domain: 'vibecoding',
97
+ tld: 'com',
98
+ registrars: ['porkbun', 'namecheap'],
99
+ });
100
+
101
+ expect(result.registrars).toEqual(['porkbun', 'namecheap']);
102
+ });
103
+ });
104
+
105
+ describe('suggestDomainsSchema', () => {
106
+ it('should accept valid input', () => {
107
+ const result = suggestDomainsSchema.parse({
108
+ base_name: 'vibecoding',
109
+ });
110
+
111
+ expect(result.base_name).toBe('vibecoding');
112
+ expect(result.tld).toBe('com');
113
+ expect(result.max_suggestions).toBe(10);
114
+ });
115
+
116
+ it('should accept custom options', () => {
117
+ const result = suggestDomainsSchema.parse({
118
+ base_name: 'vibecoding',
119
+ tld: 'io',
120
+ variants: ['hyphen', 'prefixes'],
121
+ max_suggestions: 20,
122
+ });
123
+
124
+ expect(result.tld).toBe('io');
125
+ expect(result.variants).toEqual(['hyphen', 'prefixes']);
126
+ expect(result.max_suggestions).toBe(20);
127
+ });
128
+
129
+ it('should reject invalid max_suggestions', () => {
130
+ expect(() =>
131
+ suggestDomainsSchema.parse({
132
+ base_name: 'vibecoding',
133
+ max_suggestions: 100,
134
+ }),
135
+ ).toThrow();
136
+ });
137
+ });
138
+
139
+ describe('tldInfoSchema', () => {
140
+ it('should accept valid TLD', () => {
141
+ const result = tldInfoSchema.parse({
142
+ tld: 'com',
143
+ });
144
+
145
+ expect(result.tld).toBe('com');
146
+ expect(result.detailed).toBe(false);
147
+ });
148
+
149
+ it('should accept detailed flag', () => {
150
+ const result = tldInfoSchema.parse({
151
+ tld: 'io',
152
+ detailed: true,
153
+ });
154
+
155
+ expect(result.detailed).toBe(true);
156
+ });
157
+
158
+ it('should reject TLD too short', () => {
159
+ expect(() =>
160
+ tldInfoSchema.parse({ tld: 'a' }),
161
+ ).toThrow();
162
+ });
163
+ });
164
+
165
+ describe('checkSocialsSchema', () => {
166
+ it('should accept valid input', () => {
167
+ const result = checkSocialsSchema.parse({
168
+ name: 'vibecoding',
169
+ });
170
+
171
+ expect(result.name).toBe('vibecoding');
172
+ expect(result.platforms).toBeUndefined();
173
+ });
174
+
175
+ it('should accept specific platforms', () => {
176
+ const result = checkSocialsSchema.parse({
177
+ name: 'vibecoding',
178
+ platforms: ['github', 'twitter'],
179
+ });
180
+
181
+ expect(result.platforms).toEqual(['github', 'twitter']);
182
+ });
183
+
184
+ it('should reject invalid platform', () => {
185
+ expect(() =>
186
+ checkSocialsSchema.parse({
187
+ name: 'vibecoding',
188
+ platforms: ['invalid'],
189
+ }),
190
+ ).toThrow();
191
+ });
192
+
193
+ it('should reject name too long', () => {
194
+ expect(() =>
195
+ checkSocialsSchema.parse({
196
+ name: 'a'.repeat(31),
197
+ }),
198
+ ).toThrow();
199
+ });
200
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Unit Tests for Domain Validators.
3
+ */
4
+
5
+ import {
6
+ validateDomainName,
7
+ validateTld,
8
+ validateTlds,
9
+ parseDomain,
10
+ isFullDomain,
11
+ buildDomain,
12
+ } from '../../src/utils/validators';
13
+ import { InvalidDomainError, UnsupportedTldError } from '../../src/utils/errors';
14
+
15
+ describe('validateDomainName', () => {
16
+ it('should accept valid domain names', () => {
17
+ expect(validateDomainName('vibecoding')).toBe('vibecoding');
18
+ expect(validateDomainName('my-app')).toBe('my-app');
19
+ expect(validateDomainName('test123')).toBe('test123');
20
+ expect(validateDomainName('a')).toBe('a');
21
+ });
22
+
23
+ it('should normalize to lowercase', () => {
24
+ expect(validateDomainName('VibeCoding')).toBe('vibecoding');
25
+ expect(validateDomainName('MyApp')).toBe('myapp');
26
+ });
27
+
28
+ it('should trim whitespace', () => {
29
+ expect(validateDomainName(' vibecoding ')).toBe('vibecoding');
30
+ });
31
+
32
+ it('should reject empty names', () => {
33
+ expect(() => validateDomainName('')).toThrow(InvalidDomainError);
34
+ expect(() => validateDomainName(' ')).toThrow(InvalidDomainError);
35
+ });
36
+
37
+ it('should reject names starting with hyphen', () => {
38
+ expect(() => validateDomainName('-invalid')).toThrow(InvalidDomainError);
39
+ });
40
+
41
+ it('should reject names ending with hyphen', () => {
42
+ expect(() => validateDomainName('invalid-')).toThrow(InvalidDomainError);
43
+ });
44
+
45
+ it('should reject names with invalid characters', () => {
46
+ expect(() => validateDomainName('my_app')).toThrow(InvalidDomainError);
47
+ expect(() => validateDomainName('my.app')).toThrow(InvalidDomainError);
48
+ expect(() => validateDomainName('my app')).toThrow(InvalidDomainError);
49
+ expect(() => validateDomainName('my@app')).toThrow(InvalidDomainError);
50
+ });
51
+
52
+ it('should reject names that are too long', () => {
53
+ const longName = 'a'.repeat(64);
54
+ expect(() => validateDomainName(longName)).toThrow(InvalidDomainError);
55
+ });
56
+
57
+ it('should accept maximum length names', () => {
58
+ const maxName = 'a'.repeat(63);
59
+ expect(validateDomainName(maxName)).toBe(maxName);
60
+ });
61
+ });
62
+
63
+ describe('validateTld', () => {
64
+ it('should accept valid TLDs', () => {
65
+ expect(validateTld('com')).toBe('com');
66
+ expect(validateTld('io')).toBe('io');
67
+ expect(validateTld('dev')).toBe('dev');
68
+ });
69
+
70
+ it('should normalize to lowercase', () => {
71
+ expect(validateTld('COM')).toBe('com');
72
+ expect(validateTld('IO')).toBe('io');
73
+ });
74
+
75
+ it('should remove leading dot', () => {
76
+ expect(validateTld('.com')).toBe('com');
77
+ expect(validateTld('.io')).toBe('io');
78
+ });
79
+
80
+ it('should reject empty TLDs', () => {
81
+ expect(() => validateTld('')).toThrow(UnsupportedTldError);
82
+ });
83
+
84
+ it('should reject denied TLDs', () => {
85
+ expect(() => validateTld('localhost')).toThrow(UnsupportedTldError);
86
+ expect(() => validateTld('internal')).toThrow(UnsupportedTldError);
87
+ });
88
+ });
89
+
90
+ describe('validateTlds', () => {
91
+ it('should return defaults for empty array', () => {
92
+ expect(validateTlds([])).toEqual(['com', 'io', 'dev']);
93
+ });
94
+
95
+ it('should validate and normalize array of TLDs', () => {
96
+ expect(validateTlds(['com', 'IO', '.dev'])).toEqual(['com', 'io', 'dev']);
97
+ });
98
+ });
99
+
100
+ describe('parseDomain', () => {
101
+ it('should parse full domain names', () => {
102
+ expect(parseDomain('vibecoding.com')).toEqual({
103
+ name: 'vibecoding',
104
+ tld: 'com',
105
+ });
106
+ expect(parseDomain('my-app.io')).toEqual({
107
+ name: 'my-app',
108
+ tld: 'io',
109
+ });
110
+ });
111
+
112
+ it('should normalize the parsed result', () => {
113
+ expect(parseDomain('VibeCoding.COM')).toEqual({
114
+ name: 'vibecoding',
115
+ tld: 'com',
116
+ });
117
+ });
118
+
119
+ it('should reject domains without TLD', () => {
120
+ expect(() => parseDomain('vibecoding')).toThrow(InvalidDomainError);
121
+ });
122
+ });
123
+
124
+ describe('isFullDomain', () => {
125
+ it('should detect full domains', () => {
126
+ expect(isFullDomain('vibecoding.com')).toBe(true);
127
+ expect(isFullDomain('my-app.io')).toBe(true);
128
+ });
129
+
130
+ it('should detect partial domains', () => {
131
+ expect(isFullDomain('vibecoding')).toBe(false);
132
+ expect(isFullDomain('myapp')).toBe(false);
133
+ });
134
+ });
135
+
136
+ describe('buildDomain', () => {
137
+ it('should build full domain from parts', () => {
138
+ expect(buildDomain('vibecoding', 'com')).toBe('vibecoding.com');
139
+ expect(buildDomain('my-app', 'io')).toBe('my-app.io');
140
+ });
141
+
142
+ it('should validate parts during build', () => {
143
+ expect(() => buildDomain('-invalid', 'com')).toThrow(InvalidDomainError);
144
+ expect(() => buildDomain('valid', 'localhost')).toThrow(UnsupportedTldError);
145
+ });
146
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "noImplicitReturns": true,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "noUncheckedIndexedAccess": true,
20
+ "noImplicitOverride": true,
21
+ "exactOptionalPropertyTypes": false
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist", "tests"]
25
+ }