maybethisone 0.1.0 → 0.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # maybethisone
2
2
 
3
- A CLI tool to check name availability across multiple platforms - GitHub, npm, Twitter/X, LinkedIn, and domains.
3
+ A CLI tool and MCP server to check name availability across multiple platforms - GitHub, npm, Twitter/X, LinkedIn, and domains.
4
4
 
5
5
  ## Installation
6
6
 
@@ -73,11 +73,64 @@ Results for: myproject
73
73
  | `~ No DNS` | No DNS record found (domain may still be registered - verify manually) |
74
74
  | `? Error` | Check failed |
75
75
 
76
+ ## MCP Server (for AI Agents)
77
+
78
+ maybethisone includes an MCP (Model Context Protocol) server that allows AI agents like Claude to check name availability.
79
+
80
+ ### Tools Available
81
+
82
+ | Tool | Description |
83
+ | ---------------------------- | -------------------------------------------------------------- |
84
+ | `check_name_availability` | Full check - all platforms including social media (uses browser) |
85
+ | `check_name_availability_quick` | Quick check - GitHub, npm package, and .com domain only (no browser) |
86
+
87
+ ### Claude Desktop Configuration
88
+
89
+ Add to your `claude_desktop_config.json`:
90
+
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "maybethisone": {
95
+ "command": "npx",
96
+ "args": ["maybethisone-mcp"]
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ Or if installed locally:
103
+
104
+ ```json
105
+ {
106
+ "mcpServers": {
107
+ "maybethisone": {
108
+ "command": "node",
109
+ "args": ["/path/to/maybethisone/dist/mcp.js"]
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Example Response
116
+
117
+ ```json
118
+ {
119
+ "name": "myproject",
120
+ "results": [
121
+ { "resource": "github.com/orgs/myproject", "status": "available" },
122
+ { "resource": "npm package: myproject", "status": "taken" },
123
+ { "resource": "myproject.com", "status": "taken" }
124
+ ]
125
+ }
126
+ ```
127
+
76
128
  ## Project Structure
77
129
 
78
130
  ```
79
131
  src/
80
- ├── index.ts # Main entry point
132
+ ├── index.ts # CLI entry point
133
+ ├── mcp.ts # MCP server entry point
81
134
  ├── types.ts # TypeScript type definitions
82
135
  ├── checkers/
83
136
  │ ├── index.ts # Re-exports all checkers
package/dist/index.js CHANGED
@@ -8,11 +8,19 @@ import Table from "cli-table3";
8
8
  // src/checkers/github.ts
9
9
  async function checkGitHubOrg(name) {
10
10
  try {
11
- const response = await fetch(`https://github.com/orgs/${name}`, {
12
- method: "HEAD",
13
- redirect: "manual"
14
- });
15
- return { available: response.status === 404 };
11
+ const [orgResponse, userResponse] = await Promise.all([
12
+ fetch(`https://github.com/orgs/${name}`, {
13
+ method: "HEAD",
14
+ redirect: "manual"
15
+ }),
16
+ fetch(`https://github.com/${name}`, {
17
+ method: "HEAD",
18
+ redirect: "manual"
19
+ })
20
+ ]);
21
+ const orgExists = orgResponse.status !== 404;
22
+ const userExists = userResponse.status !== 404;
23
+ return { available: !orgExists && !userExists };
16
24
  } catch (error) {
17
25
  return { available: null, error: error.message };
18
26
  }
@@ -33,6 +41,13 @@ async function getBrowserContext() {
33
41
  }
34
42
  return browserContext;
35
43
  }
44
+ async function closeBrowser() {
45
+ if (browser) {
46
+ await browser.close();
47
+ browser = null;
48
+ browserContext = null;
49
+ }
50
+ }
36
51
 
37
52
  // src/checkers/npm.ts
38
53
  async function checkNpmPackage(name) {
@@ -145,7 +160,7 @@ async function checkDomain(name, extension) {
145
160
  import { chromium as chromium2 } from "playwright";
146
161
  var browser2 = null;
147
162
  var browserContext2 = null;
148
- async function closeBrowser() {
163
+ async function closeBrowser2() {
149
164
  if (browser2) {
150
165
  await browser2.close();
151
166
  browser2 = null;
@@ -190,30 +205,34 @@ function formatStatus(result) {
190
205
  return chalk2.red("\u2717 Taken");
191
206
  }
192
207
  async function checkAllForName(name) {
193
- const results = [];
194
- logProgress("Checking GitHub organization...");
195
- const githubResult = await checkGitHubOrg(name);
196
- results.push({ resource: `github.com/orgs/${name}`, ...githubResult });
197
- logProgress("Checking npm package...");
198
- const npmPkgResult = await checkNpmPackage(name);
199
- results.push({ resource: `npm package: ${name}`, ...npmPkgResult });
200
- logProgress("Checking npm org (loading browser)...");
201
- const npmOrgResult = await checkNpmOrg(name);
202
- results.push({ resource: `npm org: @${name}`, ...npmOrgResult });
203
- logProgress("Checking Twitter/X...");
204
- const twitterResult = await checkTwitter(name);
205
- results.push({ resource: `x.com/${name}`, ...twitterResult });
206
- logProgress("Checking LinkedIn...");
207
- const linkedinResult = await checkLinkedIn(name);
208
- results.push({ resource: `linkedin.com/company/${name}`, ...linkedinResult });
209
- logProgress("Checking domains...");
210
- const domainResults = await Promise.all(DOMAIN_EXTENSIONS.map(async (ext) => {
211
- const result = await checkDomain(name, ext);
212
- return { resource: `${name}${ext}`, ...result };
213
- }));
214
- results.push(...domainResults);
208
+ logProgress("Checking all platforms...");
209
+ const [
210
+ githubResult,
211
+ npmPkgResult,
212
+ npmOrgResult,
213
+ twitterResult,
214
+ linkedinResult,
215
+ ...domainResults
216
+ ] = await Promise.all([
217
+ checkGitHubOrg(name),
218
+ checkNpmPackage(name),
219
+ checkNpmOrg(name),
220
+ checkTwitter(name),
221
+ checkLinkedIn(name),
222
+ ...DOMAIN_EXTENSIONS.map((ext) => checkDomain(name, ext))
223
+ ]);
215
224
  clearProgress();
216
- return results;
225
+ return [
226
+ { resource: `github.com/orgs/${name}`, ...githubResult },
227
+ { resource: `npm package: ${name}`, ...npmPkgResult },
228
+ { resource: `npm org: @${name}`, ...npmOrgResult },
229
+ { resource: `x.com/${name}`, ...twitterResult },
230
+ { resource: `linkedin.com/company/${name}`, ...linkedinResult },
231
+ ...DOMAIN_EXTENSIONS.map((ext, i) => ({
232
+ resource: `${name}${ext}`,
233
+ ...domainResults[i]
234
+ }))
235
+ ];
217
236
  }
218
237
  function displayResults(name, results) {
219
238
  console.log(chalk2.bold(`
@@ -247,6 +266,12 @@ function showUsage() {
247
266
  console.log(" \u2022 Twitter/X username");
248
267
  console.log(" \u2022 LinkedIn company page");
249
268
  console.log(` \u2022 Domains (${DOMAIN_EXTENSIONS.join(", ")})
269
+ `);
270
+ console.log(chalk2.bold("MCP Server (for AI agents):"));
271
+ console.log(` maybethisone-mcp
272
+ `);
273
+ console.log(" Add to Claude Desktop config:");
274
+ console.log(` { "mcpServers": { "maybethisone": { "command": "npx", "args": ["maybethisone-mcp"] } } }
250
275
  `);
251
276
  }
252
277
  function isValidName(name) {
@@ -275,10 +300,11 @@ async function main() {
275
300
  await Bun.sleep(500);
276
301
  }
277
302
  }
278
- await closeBrowser();
303
+ await closeBrowser2();
304
+ process.exit(0);
279
305
  }
280
306
  main().catch(async (err) => {
281
307
  console.error(err);
282
- await closeBrowser();
308
+ await closeBrowser2();
283
309
  process.exit(1);
284
310
  });
package/dist/mcp.js ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/mcp.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+
8
+ // src/checkers/github.ts
9
+ async function checkGitHubOrg(name) {
10
+ try {
11
+ const [orgResponse, userResponse] = await Promise.all([
12
+ fetch(`https://github.com/orgs/${name}`, {
13
+ method: "HEAD",
14
+ redirect: "manual"
15
+ }),
16
+ fetch(`https://github.com/${name}`, {
17
+ method: "HEAD",
18
+ redirect: "manual"
19
+ })
20
+ ]);
21
+ const orgExists = orgResponse.status !== 404;
22
+ const userExists = userResponse.status !== 404;
23
+ return { available: !orgExists && !userExists };
24
+ } catch (error) {
25
+ return { available: null, error: error.message };
26
+ }
27
+ }
28
+ // src/utils/browser.ts
29
+ import { chromium } from "playwright";
30
+ var browser = null;
31
+ var browserContext = null;
32
+ async function getBrowserContext() {
33
+ if (!browser) {
34
+ browser = await chromium.launch({
35
+ headless: true,
36
+ args: ["--disable-blink-features=AutomationControlled"]
37
+ });
38
+ browserContext = await browser.newContext({
39
+ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
40
+ });
41
+ }
42
+ return browserContext;
43
+ }
44
+ async function closeBrowser() {
45
+ if (browser) {
46
+ await browser.close();
47
+ browser = null;
48
+ browserContext = null;
49
+ }
50
+ }
51
+
52
+ // src/checkers/npm.ts
53
+ async function checkNpmPackage(name) {
54
+ try {
55
+ const response = await fetch(`https://registry.npmjs.org/${name}`, {
56
+ method: "HEAD"
57
+ });
58
+ const isAvailable = response.status === 404;
59
+ return { available: isAvailable, maybeAvailable: isAvailable };
60
+ } catch (error) {
61
+ return { available: null, error: error.message };
62
+ }
63
+ }
64
+ async function checkNpmOrg(name) {
65
+ try {
66
+ const context = await getBrowserContext();
67
+ const page = await context.newPage();
68
+ await page.addInitScript(() => {
69
+ Object.defineProperty(navigator, "webdriver", { get: () => false });
70
+ });
71
+ await page.goto(`https://www.npmjs.com/org/${name}`, {
72
+ waitUntil: "load",
73
+ timeout: 30000
74
+ });
75
+ try {
76
+ await page.waitForFunction(() => !document.title.includes("moment"), { timeout: 15000 });
77
+ } catch {}
78
+ await page.waitForTimeout(1000);
79
+ const bodyText = await page.evaluate(() => document.body.innerText);
80
+ await page.close();
81
+ const notFoundPatterns = [
82
+ /scope not found/i,
83
+ /NotFoundError/i,
84
+ /couldn't find.*org/i
85
+ ];
86
+ const isNotFound = notFoundPatterns.some((pattern) => pattern.test(bodyText));
87
+ return { available: isNotFound };
88
+ } catch (error) {
89
+ return { available: null, error: error.message };
90
+ }
91
+ }
92
+ // src/checkers/twitter.ts
93
+ async function checkTwitter(name) {
94
+ try {
95
+ const context = await getBrowserContext();
96
+ const page = await context.newPage();
97
+ await page.addInitScript(() => {
98
+ Object.defineProperty(navigator, "webdriver", { get: () => false });
99
+ });
100
+ await page.goto(`https://x.com/${name}`, {
101
+ waitUntil: "domcontentloaded",
102
+ timeout: 45000
103
+ });
104
+ await page.waitForTimeout(3000);
105
+ const bodyText = await page.evaluate(() => document.body.innerText);
106
+ await page.close();
107
+ const notFoundPatterns = [
108
+ /this account doesn.t exist/i,
109
+ /account suspended/i,
110
+ /doesn.t exist/i,
111
+ /hmm.*this page doesn.t exist/i
112
+ ];
113
+ const isNotFound = notFoundPatterns.some((pattern) => pattern.test(bodyText));
114
+ return { available: isNotFound };
115
+ } catch (error) {
116
+ return { available: null, error: error.message };
117
+ }
118
+ }
119
+ // src/checkers/linkedin.ts
120
+ async function checkLinkedIn(name) {
121
+ try {
122
+ const context = await getBrowserContext();
123
+ const page = await context.newPage();
124
+ await page.addInitScript(() => {
125
+ Object.defineProperty(navigator, "webdriver", { get: () => false });
126
+ });
127
+ await page.goto(`https://www.linkedin.com/company/${name}`, {
128
+ waitUntil: "load",
129
+ timeout: 30000
130
+ });
131
+ await page.waitForTimeout(2000);
132
+ const bodyText = await page.evaluate(() => document.body.innerText);
133
+ const url = page.url();
134
+ await page.close();
135
+ const notFoundPatterns = [
136
+ /page not found/i,
137
+ /this page doesn.t exist/i,
138
+ /couldn.t find/i
139
+ ];
140
+ const isNotFound = notFoundPatterns.some((pattern) => pattern.test(bodyText)) || url.includes("/404") || url.includes("pagenotfound");
141
+ return { available: isNotFound };
142
+ } catch (error) {
143
+ return { available: null, error: error.message };
144
+ }
145
+ }
146
+ // src/checkers/domain.ts
147
+ import { resolve } from "dns/promises";
148
+ async function checkDomain(name, extension) {
149
+ try {
150
+ await resolve(`${name}${extension}`);
151
+ return { available: false };
152
+ } catch (error) {
153
+ if (error.code === "ENOTFOUND" || error.code === "ENODATA") {
154
+ return { available: true, dnsOnly: true };
155
+ }
156
+ return { available: null, error: error.message };
157
+ }
158
+ }
159
+ // src/types.ts
160
+ var DOMAIN_EXTENSIONS = [".com", ".dev", ".io", ".org", ".net", ".co", ".app"];
161
+
162
+ // src/mcp.ts
163
+ var server = new McpServer({
164
+ name: "maybethisone",
165
+ version: "0.1.0"
166
+ });
167
+ function formatResult(result) {
168
+ if (result.error) {
169
+ return "error";
170
+ }
171
+ if (result.available) {
172
+ if (result.dnsOnly) {
173
+ return "no_dns";
174
+ }
175
+ if (result.maybeAvailable) {
176
+ return "maybe";
177
+ }
178
+ return "available";
179
+ }
180
+ return "taken";
181
+ }
182
+ async function checkAllForName(name) {
183
+ const [
184
+ githubResult,
185
+ npmPkgResult,
186
+ npmOrgResult,
187
+ twitterResult,
188
+ linkedinResult,
189
+ ...domainResults
190
+ ] = await Promise.all([
191
+ checkGitHubOrg(name),
192
+ checkNpmPackage(name),
193
+ checkNpmOrg(name),
194
+ checkTwitter(name),
195
+ checkLinkedIn(name),
196
+ ...DOMAIN_EXTENSIONS.map((ext) => checkDomain(name, ext))
197
+ ]);
198
+ return [
199
+ { resource: `github.com/orgs/${name}`, ...githubResult },
200
+ { resource: `npm package: ${name}`, ...npmPkgResult },
201
+ { resource: `npm org: @${name}`, ...npmOrgResult },
202
+ { resource: `x.com/${name}`, ...twitterResult },
203
+ { resource: `linkedin.com/company/${name}`, ...linkedinResult },
204
+ ...DOMAIN_EXTENSIONS.map((ext, i) => ({
205
+ resource: `${name}${ext}`,
206
+ ...domainResults[i]
207
+ }))
208
+ ];
209
+ }
210
+ server.tool("check_name_availability", "Check if a name is available across GitHub orgs, npm packages/orgs, Twitter/X, LinkedIn, and domains (.com, .dev, .io, .org, .net, .co, .app)", {
211
+ name: z.string().describe('The name to check availability for (e.g., "myproject")')
212
+ }, async ({ name }) => {
213
+ const validNamePattern = /^[a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/;
214
+ if (!validNamePattern.test(name)) {
215
+ return {
216
+ content: [{
217
+ type: "text",
218
+ text: JSON.stringify({
219
+ error: "Invalid name format. Name must start and end with alphanumeric characters and can contain hyphens.",
220
+ name
221
+ }, null, 2)
222
+ }]
223
+ };
224
+ }
225
+ try {
226
+ const results = await checkAllForName(name.toLowerCase());
227
+ await closeBrowser();
228
+ const formattedResults = results.map((r) => ({
229
+ resource: r.resource,
230
+ status: formatResult(r),
231
+ error: r.error || undefined
232
+ }));
233
+ const summary = {
234
+ name,
235
+ results: formattedResults,
236
+ legend: {
237
+ available: "Confirmed available",
238
+ taken: "Confirmed taken",
239
+ maybe: "npm package - may conflict with normalized names or private packages",
240
+ no_dns: "No DNS record found (domain may still be registered)",
241
+ error: "Check failed"
242
+ }
243
+ };
244
+ return {
245
+ content: [{
246
+ type: "text",
247
+ text: JSON.stringify(summary, null, 2)
248
+ }]
249
+ };
250
+ } catch (error) {
251
+ await closeBrowser();
252
+ return {
253
+ content: [{
254
+ type: "text",
255
+ text: JSON.stringify({
256
+ error: `Failed to check availability: ${error.message}`,
257
+ name
258
+ }, null, 2)
259
+ }],
260
+ isError: true
261
+ };
262
+ }
263
+ });
264
+ server.tool("check_name_availability_quick", "Quick check for name availability - only checks GitHub org, npm package, and .com domain (faster, no browser needed)", {
265
+ name: z.string().describe("The name to check availability for")
266
+ }, async ({ name }) => {
267
+ const validNamePattern = /^[a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/;
268
+ if (!validNamePattern.test(name)) {
269
+ return {
270
+ content: [{
271
+ type: "text",
272
+ text: JSON.stringify({
273
+ error: "Invalid name format",
274
+ name
275
+ }, null, 2)
276
+ }]
277
+ };
278
+ }
279
+ try {
280
+ const results = [];
281
+ const lowerName = name.toLowerCase();
282
+ const githubResult = await checkGitHubOrg(lowerName);
283
+ results.push({ resource: `github.com/orgs/${lowerName}`, ...githubResult });
284
+ const npmPkgResult = await checkNpmPackage(lowerName);
285
+ results.push({ resource: `npm package: ${lowerName}`, ...npmPkgResult });
286
+ const domainResult = await checkDomain(lowerName, ".com");
287
+ results.push({ resource: `${lowerName}.com`, ...domainResult });
288
+ const formattedResults = results.map((r) => ({
289
+ resource: r.resource,
290
+ status: formatResult(r),
291
+ error: r.error || undefined
292
+ }));
293
+ return {
294
+ content: [{
295
+ type: "text",
296
+ text: JSON.stringify({
297
+ name,
298
+ results: formattedResults
299
+ }, null, 2)
300
+ }]
301
+ };
302
+ } catch (error) {
303
+ return {
304
+ content: [{
305
+ type: "text",
306
+ text: JSON.stringify({
307
+ error: `Failed to check: ${error.message}`,
308
+ name
309
+ }, null, 2)
310
+ }],
311
+ isError: true
312
+ };
313
+ }
314
+ });
315
+ function logStartup() {
316
+ const msg = (text) => console.error(text);
317
+ const cwd = process.cwd();
318
+ msg("");
319
+ msg("✓ maybethisone MCP server running");
320
+ msg("");
321
+ msg("Available tools:");
322
+ msg(" • check_name_availability - Full check (all platforms, uses browser)");
323
+ msg(" • check_name_availability_quick - Quick check (GitHub, npm, .com only)");
324
+ msg("");
325
+ msg("Claude Desktop config (~/.config/claude/claude_desktop_config.json):");
326
+ msg("");
327
+ msg(" // If installed via npm");
328
+ msg(" {");
329
+ msg(' "mcpServers": {');
330
+ msg(' "maybethisone": {');
331
+ msg(' "command": "npx",');
332
+ msg(' "args": ["maybethisone-mcp"]');
333
+ msg(" }");
334
+ msg(" }");
335
+ msg(" }");
336
+ msg("");
337
+ msg(" // For local development");
338
+ msg(" {");
339
+ msg(' "mcpServers": {');
340
+ msg(' "maybethisone": {');
341
+ msg(' "command": "bun",');
342
+ msg(' "args": ["run", "src/mcp.ts"],');
343
+ msg(` "cwd": "${cwd}"`);
344
+ msg(" }");
345
+ msg(" }");
346
+ msg(" }");
347
+ msg("");
348
+ msg("Note: You don't need to run this manually.");
349
+ msg("Claude Desktop will start it automatically when configured.");
350
+ msg("");
351
+ }
352
+ async function main() {
353
+ logStartup();
354
+ const transport = new StdioServerTransport;
355
+ await server.connect(transport);
356
+ }
357
+ main().catch((error) => {
358
+ console.error("MCP server error:", error);
359
+ process.exit(1);
360
+ });
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "maybethisone",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Check availability of GitHub orgs, npm packages, social media handles, and domains",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "maybethisone": "dist/index.js"
8
+ "maybethisone": "dist/index.js",
9
+ "maybethisone-mcp": "dist/mcp.js"
9
10
  },
10
11
  "files": [
11
12
  "dist"
@@ -13,7 +14,8 @@
13
14
  "scripts": {
14
15
  "search": "bun run src/index.ts",
15
16
  "dev": "bun --watch src/index.ts",
16
- "build": "bun build src/index.ts --outdir dist --target node --format esm --external playwright --external chalk --external cli-table3",
17
+ "mcp": "bun run src/mcp.ts",
18
+ "build": "bun build src/index.ts src/mcp.ts --outdir dist --target node --format esm --external playwright --external chalk --external cli-table3 --external @modelcontextprotocol/sdk --external zod",
17
19
  "clean": "rm -rf dist",
18
20
  "prebuild": "bun run clean",
19
21
  "prepublishOnly": "bun run build",
@@ -37,14 +39,18 @@
37
39
  "twitter",
38
40
  "linkedin",
39
41
  "cli",
42
+ "mcp",
43
+ "model-context-protocol",
40
44
  "maybethisone"
41
45
  ],
42
46
  "author": "",
43
47
  "license": "MIT",
44
48
  "dependencies": {
49
+ "@modelcontextprotocol/sdk": "^1.25.2",
45
50
  "chalk": "^5.4.1",
46
51
  "cli-table3": "^0.6.5",
47
- "playwright": "^1.57.0"
52
+ "playwright": "^1.57.0",
53
+ "zod": "^4.3.5"
48
54
  },
49
55
  "devDependencies": {
50
56
  "@types/bun": "latest",