mcphosting-cli 0.1.0 → 0.1.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/package.json +10 -5
- package/CONTRIBUTING.md +0 -156
- package/src/commands/auth.ts +0 -186
- package/src/commands/connect.ts +0 -172
- package/src/commands/import.ts +0 -213
- package/src/commands/proxy.ts +0 -144
- package/src/commands/search.ts +0 -135
- package/src/commands/servers.ts +0 -131
- package/src/index.ts +0 -130
- package/src/lib/api.ts +0 -137
- package/src/lib/clients.ts +0 -188
- package/src/lib/config.ts +0 -90
- package/src/lib/logger.ts +0 -63
- package/src/types.ts +0 -47
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -14
package/src/index.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createAuthCommands, createLegacyAuthCommands } from './commands/auth.js';
|
|
3
|
-
import {
|
|
4
|
-
createConnectCommands,
|
|
5
|
-
createDisconnectCommand,
|
|
6
|
-
createListCommand
|
|
7
|
-
} from './commands/connect.js';
|
|
8
|
-
import { createImportCommand } from './commands/import.js';
|
|
9
|
-
import { createProxyCommand } from './commands/proxy.js';
|
|
10
|
-
import { createSearchCommand, createInfoCommand } from './commands/search.js';
|
|
11
|
-
import {
|
|
12
|
-
createServersCommand,
|
|
13
|
-
createKeysCommand,
|
|
14
|
-
createPublishCommand
|
|
15
|
-
} from './commands/servers.js';
|
|
16
|
-
import { Logger } from './lib/logger.js';
|
|
17
|
-
import chalk from 'chalk';
|
|
18
|
-
|
|
19
|
-
const program = new Command();
|
|
20
|
-
|
|
21
|
-
program
|
|
22
|
-
.name('mcphost')
|
|
23
|
-
.description('Connect AI agents to MCP servers. Browse, install, and manage Model Context Protocol servers.')
|
|
24
|
-
.version('0.1.0')
|
|
25
|
-
.configureOutput({
|
|
26
|
-
outputError: (str, write) => {
|
|
27
|
-
// Ensure errors go to stderr
|
|
28
|
-
write(chalk.red(str));
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Add the main commands
|
|
33
|
-
program.addCommand(createConnectCommands());
|
|
34
|
-
program.addCommand(createDisconnectCommand());
|
|
35
|
-
program.addCommand(createListCommand());
|
|
36
|
-
program.addCommand(createImportCommand());
|
|
37
|
-
program.addCommand(createProxyCommand());
|
|
38
|
-
program.addCommand(createSearchCommand());
|
|
39
|
-
program.addCommand(createInfoCommand());
|
|
40
|
-
|
|
41
|
-
// Add server management commands
|
|
42
|
-
program.addCommand(createServersCommand());
|
|
43
|
-
program.addCommand(createKeysCommand());
|
|
44
|
-
program.addCommand(createPublishCommand());
|
|
45
|
-
|
|
46
|
-
// Add auth commands both as subcommands and top-level (backwards compatibility)
|
|
47
|
-
program.addCommand(createAuthCommands());
|
|
48
|
-
const [login, logout, whoami] = createLegacyAuthCommands();
|
|
49
|
-
program.addCommand(login);
|
|
50
|
-
program.addCommand(logout);
|
|
51
|
-
program.addCommand(whoami);
|
|
52
|
-
|
|
53
|
-
// Custom help
|
|
54
|
-
program.configureHelp({
|
|
55
|
-
subcommandTerm: (cmd) => chalk.cyan(cmd.name()),
|
|
56
|
-
commandUsage: (cmd) => {
|
|
57
|
-
const usage = cmd.usage();
|
|
58
|
-
return chalk.yellow(usage);
|
|
59
|
-
},
|
|
60
|
-
commandDescription: (cmd) => {
|
|
61
|
-
return chalk.dim(cmd.description());
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Add custom help examples
|
|
66
|
-
program.addHelpText('after', `
|
|
67
|
-
${chalk.bold('Examples:')}
|
|
68
|
-
${chalk.cyan('mcphost connect github')} ${chalk.dim('Connect to GitHub MCP server')}
|
|
69
|
-
${chalk.cyan('mcphost connect https://my-mcp.example.com')} ${chalk.dim('Connect to custom MCP server')}
|
|
70
|
-
${chalk.cyan('mcphost connect slack --client claude')} ${chalk.dim('Connect Slack MCP only to Claude')}
|
|
71
|
-
${chalk.cyan('mcphost list')} ${chalk.dim('List all connected MCP servers')}
|
|
72
|
-
${chalk.cyan('mcphost search github')} ${chalk.dim('Search marketplace for MCP servers')}
|
|
73
|
-
${chalk.cyan('mcphost info notion')} ${chalk.dim('Get details about Notion MCP')}
|
|
74
|
-
${chalk.cyan('mcphost import --from smithery')} ${chalk.dim('Import connections from Smithery')}
|
|
75
|
-
${chalk.cyan('mcphost disconnect github')} ${chalk.dim('Remove GitHub MCP connection')}
|
|
76
|
-
|
|
77
|
-
${chalk.bold('Supported AI Clients:')}
|
|
78
|
-
• ${chalk.green('Claude Desktop')} - Auto-configured via claude_desktop_config.json
|
|
79
|
-
• ${chalk.green('Cursor')} - Auto-configured via .cursor/mcp.json
|
|
80
|
-
• ${chalk.green('VS Code')} - Configured via .vscode/mcp.json
|
|
81
|
-
• ${chalk.green('OpenClaw')} - Auto-configured via ~/.openclaw/mcp.json
|
|
82
|
-
• ${chalk.green('ChatGPT')} - Manual setup with web instructions
|
|
83
|
-
|
|
84
|
-
${chalk.bold('Get Started:')}
|
|
85
|
-
${chalk.dim('1.')} ${chalk.cyan('mcphost search <topic>')} ${chalk.dim('Find MCP servers')}
|
|
86
|
-
${chalk.dim('2.')} ${chalk.cyan('mcphost connect <slug>')} ${chalk.dim('Connect to your AI clients')}
|
|
87
|
-
${chalk.dim('3.')} ${chalk.cyan('mcphost list')} ${chalk.dim('View your connections')}
|
|
88
|
-
|
|
89
|
-
${chalk.yellow('⭐ Star us:')} ${chalk.blue('https://github.com/gorlomi-enzo/mcphosting-cli')}
|
|
90
|
-
${chalk.yellow('📚 Docs:')} ${chalk.blue('https://mcphosting.com/docs')}
|
|
91
|
-
`);
|
|
92
|
-
|
|
93
|
-
// Global error handling
|
|
94
|
-
process.on('uncaughtException', (error) => {
|
|
95
|
-
Logger.error(`Uncaught error: ${error.message}`);
|
|
96
|
-
if (process.env.DEBUG) {
|
|
97
|
-
console.error(error.stack);
|
|
98
|
-
}
|
|
99
|
-
process.exit(1);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
process.on('unhandledRejection', (reason) => {
|
|
103
|
-
Logger.error(`Unhandled rejection: ${reason}`);
|
|
104
|
-
if (process.env.DEBUG) {
|
|
105
|
-
console.error(reason);
|
|
106
|
-
}
|
|
107
|
-
process.exit(1);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Handle Ctrl+C gracefully
|
|
111
|
-
process.on('SIGINT', () => {
|
|
112
|
-
console.log('\n');
|
|
113
|
-
Logger.info('Goodbye! 👋');
|
|
114
|
-
process.exit(0);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Parse CLI arguments
|
|
118
|
-
async function main() {
|
|
119
|
-
try {
|
|
120
|
-
await program.parseAsync();
|
|
121
|
-
} catch (error) {
|
|
122
|
-
Logger.error(`Command failed: ${error}`);
|
|
123
|
-
if (process.env.DEBUG) {
|
|
124
|
-
console.error(error);
|
|
125
|
-
}
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
main();
|
package/src/lib/api.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { MCPServer } from '../types.js';
|
|
2
|
-
|
|
3
|
-
export class MCPHostingAPI {
|
|
4
|
-
private baseUrl = 'https://mcphosting.com/api';
|
|
5
|
-
private token?: string;
|
|
6
|
-
|
|
7
|
-
constructor(token?: string) {
|
|
8
|
-
this.token = token;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
private async request(endpoint: string, options: RequestInit = {}): Promise<any> {
|
|
12
|
-
// For MVP, always throw to use static fallback
|
|
13
|
-
// In production, this would make actual HTTP requests
|
|
14
|
-
throw new Error(`API not available - using static fallback`);
|
|
15
|
-
|
|
16
|
-
// Commented out for MVP - uncomment for production API calls
|
|
17
|
-
/*
|
|
18
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
19
|
-
const headers: Record<string, string> = {
|
|
20
|
-
'Content-Type': 'application/json',
|
|
21
|
-
...options.headers as Record<string, string>
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
if (this.token) {
|
|
25
|
-
headers.Authorization = `Bearer ${this.token}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const response = await fetch(url, {
|
|
29
|
-
...options,
|
|
30
|
-
headers
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (!response.ok) {
|
|
34
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return await response.json();
|
|
38
|
-
*/
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async searchMCPs(query: string): Promise<MCPServer[]> {
|
|
42
|
-
try {
|
|
43
|
-
const results = await this.request(`/marketplace/search?q=${encodeURIComponent(query)}`);
|
|
44
|
-
return results.mcps || [];
|
|
45
|
-
} catch (error) {
|
|
46
|
-
// Always fallback to static data for now
|
|
47
|
-
const staticMCPs = this.getStaticMCPs();
|
|
48
|
-
return staticMCPs.filter(mcp =>
|
|
49
|
-
mcp.name.toLowerCase().includes(query.toLowerCase()) ||
|
|
50
|
-
mcp.description.toLowerCase().includes(query.toLowerCase()) ||
|
|
51
|
-
mcp.tools.some(tool => tool.toLowerCase().includes(query.toLowerCase()))
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async getMCPInfo(slug: string): Promise<MCPServer | null> {
|
|
57
|
-
try {
|
|
58
|
-
const result = await this.request(`/marketplace/mcp/${slug}`);
|
|
59
|
-
return result.mcp || null;
|
|
60
|
-
} catch {
|
|
61
|
-
// Fallback to static data
|
|
62
|
-
const staticMCPs = this.getStaticMCPs();
|
|
63
|
-
return staticMCPs.find(mcp => mcp.slug === slug) || null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async whoami(): Promise<{ email: string; org?: string } | null> {
|
|
68
|
-
if (!this.token) return null;
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const result = await this.request('/auth/whoami');
|
|
72
|
-
return result.user;
|
|
73
|
-
} catch {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private getStaticMCPs(): MCPServer[] {
|
|
79
|
-
// Curated list of popular MCP servers for fallback
|
|
80
|
-
return [
|
|
81
|
-
{
|
|
82
|
-
slug: 'github',
|
|
83
|
-
name: 'GitHub MCP',
|
|
84
|
-
description: 'Access GitHub repositories, issues, and pull requests',
|
|
85
|
-
url: 'https://github.mcphost.dev',
|
|
86
|
-
tools: ['read_file', 'list_repos', 'create_issue', 'list_issues'],
|
|
87
|
-
installs: 15420,
|
|
88
|
-
author: 'MCPHosting'
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
slug: 'slack',
|
|
92
|
-
name: 'Slack MCP',
|
|
93
|
-
description: 'Send messages and manage Slack workspaces',
|
|
94
|
-
url: 'https://slack.mcphost.dev',
|
|
95
|
-
tools: ['send_message', 'list_channels', 'get_history'],
|
|
96
|
-
installs: 8930,
|
|
97
|
-
author: 'MCPHosting'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
slug: 'notion',
|
|
101
|
-
name: 'Notion MCP',
|
|
102
|
-
description: 'Read and write Notion pages and databases',
|
|
103
|
-
url: 'https://notion.mcphost.dev',
|
|
104
|
-
tools: ['read_page', 'create_page', 'query_database'],
|
|
105
|
-
installs: 12450,
|
|
106
|
-
author: 'MCPHosting'
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
slug: 'stripe',
|
|
110
|
-
name: 'Stripe MCP',
|
|
111
|
-
description: 'Access Stripe payment and customer data',
|
|
112
|
-
url: 'https://stripe.mcphost.dev',
|
|
113
|
-
tools: ['get_customer', 'list_payments', 'create_invoice'],
|
|
114
|
-
installs: 6720,
|
|
115
|
-
author: 'MCPHosting'
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
slug: 'postgres',
|
|
119
|
-
name: 'PostgreSQL MCP',
|
|
120
|
-
description: 'Query PostgreSQL databases safely',
|
|
121
|
-
url: 'https://postgres.mcphost.dev',
|
|
122
|
-
tools: ['query', 'describe_table', 'list_tables'],
|
|
123
|
-
installs: 9180,
|
|
124
|
-
author: 'MCPHosting'
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
slug: 'filesystem',
|
|
128
|
-
name: 'Filesystem MCP',
|
|
129
|
-
description: 'Read and write files securely',
|
|
130
|
-
url: 'https://filesystem.mcphost.dev',
|
|
131
|
-
tools: ['read_file', 'write_file', 'list_directory'],
|
|
132
|
-
installs: 18950,
|
|
133
|
-
author: 'MCPHosting'
|
|
134
|
-
}
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
}
|
package/src/lib/clients.ts
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, access } from 'fs/promises';
|
|
2
|
-
import { join, dirname } from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import { mkdir } from 'fs/promises';
|
|
5
|
-
import { ClientConfig, ClaudeConfig, CursorConfig, MCPServerEntry, SupportedClient } from '../types.js';
|
|
6
|
-
import { Logger } from './logger.js';
|
|
7
|
-
|
|
8
|
-
export class ClientManager {
|
|
9
|
-
private static getClientPaths(): Record<SupportedClient, string> {
|
|
10
|
-
const home = homedir();
|
|
11
|
-
const platform = process.platform;
|
|
12
|
-
|
|
13
|
-
const paths: Record<SupportedClient, string> = {
|
|
14
|
-
claude: platform === 'win32'
|
|
15
|
-
? join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json')
|
|
16
|
-
: join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
17
|
-
cursor: join(home, '.cursor', 'mcp.json'),
|
|
18
|
-
vscode: join(process.cwd(), '.vscode', 'mcp.json'),
|
|
19
|
-
openclaw: join(home, '.openclaw', 'mcp.json'),
|
|
20
|
-
chatgpt: '' // Web-based, no local config
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return paths;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static async detectInstalledClients(): Promise<ClientConfig[]> {
|
|
27
|
-
const paths = this.getClientPaths();
|
|
28
|
-
const clients: ClientConfig[] = [];
|
|
29
|
-
|
|
30
|
-
for (const [name, path] of Object.entries(paths)) {
|
|
31
|
-
if (name === 'chatgpt') continue; // Skip web-based clients
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
await access(path);
|
|
35
|
-
clients.push({ name, configPath: path, exists: true });
|
|
36
|
-
} catch {
|
|
37
|
-
clients.push({ name, configPath: path, exists: false });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return clients;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
static async addToClient(
|
|
45
|
-
client: SupportedClient,
|
|
46
|
-
slug: string,
|
|
47
|
-
url: string
|
|
48
|
-
): Promise<boolean> {
|
|
49
|
-
if (client === 'chatgpt') {
|
|
50
|
-
Logger.info('ChatGPT Setup Instructions:');
|
|
51
|
-
console.log(`
|
|
52
|
-
1. Open ChatGPT in your browser
|
|
53
|
-
2. Go to Settings → Apps → Connect an app
|
|
54
|
-
3. Enter this URL: ${url}
|
|
55
|
-
4. Follow the prompts to authorize the MCP server
|
|
56
|
-
`);
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const paths = this.getClientPaths();
|
|
61
|
-
const configPath = paths[client];
|
|
62
|
-
|
|
63
|
-
if (!configPath) {
|
|
64
|
-
throw new Error(`Unsupported client: ${client}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const serverEntry: MCPServerEntry = {
|
|
68
|
-
command: 'npx',
|
|
69
|
-
args: ['-y', '@mcphosting/cli', 'proxy', url],
|
|
70
|
-
env: {}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
// Ensure directory exists
|
|
75
|
-
await mkdir(dirname(configPath), { recursive: true });
|
|
76
|
-
|
|
77
|
-
let config: ClaudeConfig | CursorConfig = { mcpServers: {} };
|
|
78
|
-
|
|
79
|
-
// Read existing config if it exists
|
|
80
|
-
try {
|
|
81
|
-
const configContent = await readFile(configPath, 'utf-8');
|
|
82
|
-
config = JSON.parse(configContent);
|
|
83
|
-
if (!config.mcpServers) {
|
|
84
|
-
config.mcpServers = {};
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
// File doesn't exist or is invalid, use default config
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Add the MCP server
|
|
91
|
-
config.mcpServers[slug] = serverEntry;
|
|
92
|
-
|
|
93
|
-
// Write back to file
|
|
94
|
-
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
95
|
-
return true;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
Logger.error(`Failed to configure ${client}: ${error}`);
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
static async removeFromClient(
|
|
103
|
-
client: SupportedClient,
|
|
104
|
-
slug: string
|
|
105
|
-
): Promise<boolean> {
|
|
106
|
-
if (client === 'chatgpt') {
|
|
107
|
-
Logger.info('To remove from ChatGPT:');
|
|
108
|
-
console.log(`
|
|
109
|
-
1. Open ChatGPT in your browser
|
|
110
|
-
2. Go to Settings → Apps
|
|
111
|
-
3. Find and disconnect the MCP server: ${slug}
|
|
112
|
-
`);
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const paths = this.getClientPaths();
|
|
117
|
-
const configPath = paths[client];
|
|
118
|
-
|
|
119
|
-
if (!configPath) {
|
|
120
|
-
throw new Error(`Unsupported client: ${client}`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const configContent = await readFile(configPath, 'utf-8');
|
|
125
|
-
const config: ClaudeConfig | CursorConfig = JSON.parse(configContent);
|
|
126
|
-
|
|
127
|
-
if (config.mcpServers && config.mcpServers[slug]) {
|
|
128
|
-
delete config.mcpServers[slug];
|
|
129
|
-
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return false;
|
|
134
|
-
} catch (error) {
|
|
135
|
-
Logger.error(`Failed to remove from ${client}: ${error}`);
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
static async removeFromAllClients(slug: string): Promise<string[]> {
|
|
141
|
-
const clients = await this.detectInstalledClients();
|
|
142
|
-
const removed: string[] = [];
|
|
143
|
-
|
|
144
|
-
for (const client of clients) {
|
|
145
|
-
if (!client.exists) continue;
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const success = await this.removeFromClient(client.name as SupportedClient, slug);
|
|
149
|
-
if (success) {
|
|
150
|
-
removed.push(client.name);
|
|
151
|
-
}
|
|
152
|
-
} catch (error) {
|
|
153
|
-
Logger.warning(`Failed to remove from ${client.name}: ${error}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return removed;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
static resolveUrl(urlOrSlug: string): string {
|
|
161
|
-
// If it's already a full URL, return as-is
|
|
162
|
-
if (urlOrSlug.startsWith('http://') || urlOrSlug.startsWith('https://')) {
|
|
163
|
-
return urlOrSlug;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Otherwise, assume it's a slug and construct the mcphost.dev URL
|
|
167
|
-
return `https://${urlOrSlug}.mcphost.dev`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
static extractSlug(url: string): string {
|
|
171
|
-
// Extract slug from URLs like https://myserver.mcphost.dev
|
|
172
|
-
if (url.includes('.mcphost.dev')) {
|
|
173
|
-
const match = url.match(/https?:\/\/([^.]+)\.mcphost\.dev/);
|
|
174
|
-
if (match) {
|
|
175
|
-
return match[1];
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// For other URLs, use the hostname or a hash of the URL
|
|
180
|
-
try {
|
|
181
|
-
const parsed = new URL(url);
|
|
182
|
-
return parsed.hostname.replace(/\./g, '-');
|
|
183
|
-
} catch {
|
|
184
|
-
// If URL parsing fails, create a simple hash
|
|
185
|
-
return url.replace(/[^a-zA-Z0-9]/g, '-').slice(0, 20);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
package/src/lib/config.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import Conf from 'conf';
|
|
2
|
-
import { MCPConfig, MCPConnection } from '../types.js';
|
|
3
|
-
|
|
4
|
-
export class Config {
|
|
5
|
-
private conf: Conf<MCPConfig>;
|
|
6
|
-
private connectionsConf: Conf<{ connections: MCPConnection[] }>;
|
|
7
|
-
|
|
8
|
-
constructor() {
|
|
9
|
-
this.conf = new Conf<MCPConfig>({
|
|
10
|
-
projectName: 'mcphosting',
|
|
11
|
-
defaults: {}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
this.connectionsConf = new Conf<{ connections: MCPConnection[] }>({
|
|
15
|
-
projectName: 'mcphosting',
|
|
16
|
-
configName: 'connections',
|
|
17
|
-
defaults: { connections: [] }
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
get token(): string | undefined {
|
|
22
|
-
return this.conf.get('token');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
set token(value: string | undefined) {
|
|
26
|
-
if (value) {
|
|
27
|
-
this.conf.set('token', value);
|
|
28
|
-
} else {
|
|
29
|
-
this.conf.delete('token');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get user() {
|
|
34
|
-
return this.conf.get('user');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
set user(value: { email: string; org?: string } | undefined) {
|
|
38
|
-
if (value) {
|
|
39
|
-
this.conf.set('user', value);
|
|
40
|
-
} else {
|
|
41
|
-
this.conf.delete('user');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get connections(): MCPConnection[] {
|
|
46
|
-
return this.connectionsConf.get('connections', []);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addConnection(connection: Omit<MCPConnection, 'id' | 'addedAt'>): MCPConnection {
|
|
50
|
-
const connections = this.connections;
|
|
51
|
-
const newConnection: MCPConnection = {
|
|
52
|
-
...connection,
|
|
53
|
-
id: Math.random().toString(36).substr(2, 9),
|
|
54
|
-
addedAt: Date.now()
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
connections.push(newConnection);
|
|
58
|
-
this.connectionsConf.set('connections', connections);
|
|
59
|
-
return newConnection;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
removeConnection(id: string): boolean {
|
|
63
|
-
const connections = this.connections;
|
|
64
|
-
const index = connections.findIndex(c => c.id === id || c.slug === id);
|
|
65
|
-
if (index === -1) return false;
|
|
66
|
-
|
|
67
|
-
connections.splice(index, 1);
|
|
68
|
-
this.connectionsConf.set('connections', connections);
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
findConnection(slugOrId: string): MCPConnection | undefined {
|
|
73
|
-
return this.connections.find(c => c.id === slugOrId || c.slug === slugOrId);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
updateConnection(id: string, updates: Partial<MCPConnection>): boolean {
|
|
77
|
-
const connections = this.connections;
|
|
78
|
-
const index = connections.findIndex(c => c.id === id);
|
|
79
|
-
if (index === -1) return false;
|
|
80
|
-
|
|
81
|
-
connections[index] = { ...connections[index], ...updates };
|
|
82
|
-
this.connectionsConf.set('connections', connections);
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
clear(): void {
|
|
87
|
-
this.conf.clear();
|
|
88
|
-
this.connectionsConf.clear();
|
|
89
|
-
}
|
|
90
|
-
}
|
package/src/lib/logger.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora, { Ora } from 'ora';
|
|
3
|
-
|
|
4
|
-
export class Logger {
|
|
5
|
-
static info(message: string): void {
|
|
6
|
-
console.log(chalk.blue('ℹ'), message);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
static success(message: string): void {
|
|
10
|
-
console.log(chalk.green('✓'), message);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static warning(message: string): void {
|
|
14
|
-
console.log(chalk.yellow('⚠'), message);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static error(message: string): void {
|
|
18
|
-
console.error(chalk.red('✗'), message);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static dim(message: string): void {
|
|
22
|
-
console.log(chalk.dim(message));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static bold(message: string): void {
|
|
26
|
-
console.log(chalk.bold(message));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static spinner(text: string): Ora {
|
|
30
|
-
return ora(text).start();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static table(data: Array<Record<string, any>>, headers?: string[]): void {
|
|
34
|
-
if (data.length === 0) {
|
|
35
|
-
Logger.dim('No data to display');
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const keys = headers || Object.keys(data[0]);
|
|
40
|
-
const maxWidths = keys.map(key =>
|
|
41
|
-
Math.max(key.length, ...data.map(row => String(row[key] || '').length))
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
// Header
|
|
45
|
-
const headerRow = keys.map((key, i) =>
|
|
46
|
-
chalk.bold(key.padEnd(maxWidths[i]))
|
|
47
|
-
).join(' | ');
|
|
48
|
-
console.log(headerRow);
|
|
49
|
-
console.log(keys.map((_, i) => '-'.repeat(maxWidths[i])).join('-|-'));
|
|
50
|
-
|
|
51
|
-
// Data rows
|
|
52
|
-
data.forEach(row => {
|
|
53
|
-
const dataRow = keys.map((key, i) =>
|
|
54
|
-
String(row[key] || '').padEnd(maxWidths[i])
|
|
55
|
-
).join(' | ');
|
|
56
|
-
console.log(dataRow);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
static json(data: any): void {
|
|
61
|
-
console.log(JSON.stringify(data, null, 2));
|
|
62
|
-
}
|
|
63
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export interface MCPConfig {
|
|
2
|
-
token?: string;
|
|
3
|
-
user?: {
|
|
4
|
-
email: string;
|
|
5
|
-
org?: string;
|
|
6
|
-
};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface MCPConnection {
|
|
10
|
-
id: string;
|
|
11
|
-
slug: string;
|
|
12
|
-
url: string;
|
|
13
|
-
clients: string[];
|
|
14
|
-
addedAt: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ClientConfig {
|
|
18
|
-
name: string;
|
|
19
|
-
configPath: string;
|
|
20
|
-
exists: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface MCPServer {
|
|
24
|
-
slug: string;
|
|
25
|
-
name: string;
|
|
26
|
-
description: string;
|
|
27
|
-
url?: string;
|
|
28
|
-
tools: string[];
|
|
29
|
-
installs: number;
|
|
30
|
-
author: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface MCPServerEntry {
|
|
34
|
-
command: string;
|
|
35
|
-
args: string[];
|
|
36
|
-
env: Record<string, string>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ClaudeConfig {
|
|
40
|
-
mcpServers: Record<string, MCPServerEntry>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface CursorConfig {
|
|
44
|
-
mcpServers: Record<string, MCPServerEntry>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export type SupportedClient = 'claude' | 'cursor' | 'vscode' | 'openclaw' | 'chatgpt';
|
package/tsconfig.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"allowSyntheticDefaultImports": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"allowJs": true,
|
|
9
|
-
"outDir": "./dist",
|
|
10
|
-
"rootDir": "./src",
|
|
11
|
-
"strict": true,
|
|
12
|
-
"moduleDetection": "force",
|
|
13
|
-
"skipLibCheck": true,
|
|
14
|
-
"forceConsistentCasingInFileNames": true,
|
|
15
|
-
"resolveJsonModule": true,
|
|
16
|
-
"isolatedModules": true,
|
|
17
|
-
"verbatimModuleSyntax": false
|
|
18
|
-
},
|
|
19
|
-
"include": [
|
|
20
|
-
"src/**/*"
|
|
21
|
-
],
|
|
22
|
-
"exclude": [
|
|
23
|
-
"node_modules",
|
|
24
|
-
"dist"
|
|
25
|
-
]
|
|
26
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ['src/index.ts'],
|
|
5
|
-
format: ['esm', 'cjs'],
|
|
6
|
-
dts: true,
|
|
7
|
-
splitting: false,
|
|
8
|
-
sourcemap: true,
|
|
9
|
-
clean: true,
|
|
10
|
-
shims: true,
|
|
11
|
-
banner: {
|
|
12
|
-
js: '#!/usr/bin/env node'
|
|
13
|
-
}
|
|
14
|
-
});
|