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