mcphosting-cli 0.1.0 → 0.1.2

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.
@@ -1,144 +0,0 @@
1
- import { Command } from 'commander';
2
- import { Logger } from '../lib/logger.js';
3
- import { createReadStream, createWriteStream } from 'fs';
4
- import { Readable, Writable } from 'stream';
5
-
6
- export function createProxyCommand(): Command {
7
- return new Command('proxy')
8
- .description('Start a local STDIO MCP proxy to a remote server')
9
- .argument('<url>', 'Remote MCP server URL')
10
- .option('--quiet', 'Suppress startup messages')
11
- .action(async (url: string, options) => {
12
- if (!options.quiet) {
13
- // Log to stderr so it doesn't interfere with STDIO MCP protocol
14
- console.error('🔗 Proxying via mcphosting.com');
15
- console.error(`📡 Remote server: ${url}`);
16
- }
17
-
18
- try {
19
- await startProxy(url, options.quiet);
20
- } catch (error) {
21
- if (!options.quiet) {
22
- console.error(`❌ Proxy error: ${error}`);
23
- }
24
- process.exit(1);
25
- }
26
- });
27
- }
28
-
29
- async function startProxy(url: string, quiet: boolean = false): Promise<void> {
30
- // For MVP, implement a simple HTTP proxy that translates STDIO JSON-RPC to HTTP
31
- // In production, this should use WebSocket or SSE for real-time communication
32
-
33
- let buffer = '';
34
-
35
- // Set up JSON-RPC message parsing from stdin
36
- process.stdin.setEncoding('utf8');
37
- process.stdin.on('data', async (chunk) => {
38
- buffer += chunk;
39
-
40
- // Process complete JSON-RPC messages (newline delimited)
41
- const lines = buffer.split('\n');
42
- buffer = lines.pop() || ''; // Keep incomplete line in buffer
43
-
44
- for (const line of lines) {
45
- if (line.trim()) {
46
- try {
47
- await forwardMessage(url, line, quiet);
48
- } catch (error) {
49
- if (!quiet) {
50
- console.error(`Proxy error: ${error}`);
51
- }
52
- // Send error response back to client
53
- const errorResponse = {
54
- jsonrpc: '2.0',
55
- id: null,
56
- error: {
57
- code: -32603,
58
- message: 'Proxy error',
59
- data: error.toString()
60
- }
61
- };
62
- process.stdout.write(JSON.stringify(errorResponse) + '\n');
63
- }
64
- }
65
- }
66
- });
67
-
68
- process.stdin.on('end', () => {
69
- if (!quiet) {
70
- console.error('📴 Proxy connection closed');
71
- }
72
- process.exit(0);
73
- });
74
-
75
- process.stdin.on('error', (error) => {
76
- if (!quiet) {
77
- console.error(`Stdin error: ${error}`);
78
- }
79
- process.exit(1);
80
- });
81
-
82
- // Keep the process alive
83
- await new Promise(() => {});
84
- }
85
-
86
- async function forwardMessage(url: string, message: string, quiet: boolean): Promise<void> {
87
- try {
88
- const jsonrpcMessage = JSON.parse(message);
89
-
90
- // Forward the JSON-RPC message to the remote server via HTTP POST
91
- const response = await fetch(url, {
92
- method: 'POST',
93
- headers: {
94
- 'Content-Type': 'application/json',
95
- 'Accept': 'application/json',
96
- 'User-Agent': '@mcphosting/cli-proxy'
97
- },
98
- body: message
99
- });
100
-
101
- if (!response.ok) {
102
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
103
- }
104
-
105
- const responseData = await response.text();
106
-
107
- // Forward the response back to stdout
108
- process.stdout.write(responseData);
109
-
110
- // Ensure newline for JSON-RPC protocol
111
- if (!responseData.endsWith('\n')) {
112
- process.stdout.write('\n');
113
- }
114
-
115
- } catch (error) {
116
- // If we can't parse the original message, send a generic error
117
- if (error instanceof SyntaxError) {
118
- const errorResponse = {
119
- jsonrpc: '2.0',
120
- id: null,
121
- error: {
122
- code: -32700,
123
- message: 'Parse error',
124
- data: 'Invalid JSON-RPC message'
125
- }
126
- };
127
- process.stdout.write(JSON.stringify(errorResponse) + '\n');
128
- } else {
129
- throw error;
130
- }
131
- }
132
- }
133
-
134
- // Alternative WebSocket-based proxy for real-time communication
135
- async function startWebSocketProxy(url: string, quiet: boolean = false): Promise<void> {
136
- // This would use WebSocket for bidirectional communication
137
- // Implementation would depend on the remote server supporting WebSocket
138
-
139
- if (!quiet) {
140
- console.error('WebSocket proxy not yet implemented, using HTTP proxy');
141
- }
142
-
143
- return startProxy(url, quiet);
144
- }
@@ -1,135 +0,0 @@
1
- import { Command } from 'commander';
2
- import { MCPHostingAPI } from '../lib/api.js';
3
- import { Config } from '../lib/config.js';
4
- import { Logger } from '../lib/logger.js';
5
- import chalk from 'chalk';
6
-
7
- export function createSearchCommand(): Command {
8
- return new Command('search')
9
- .description('Search MCP servers in the marketplace')
10
- .argument('<query>', 'Search query')
11
- .option('--limit <number>', 'Maximum number of results', '10')
12
- .option('--json', 'Output as JSON')
13
- .action(async (query: string, options) => {
14
- const config = new Config();
15
- const api = new MCPHostingAPI(config.token);
16
- const spinner = Logger.spinner(`Searching for "${query}"...`);
17
-
18
- try {
19
- const results = await api.searchMCPs(query);
20
- spinner.succeed(`Found ${results.length} MCP server(s)`);
21
-
22
- if (results.length === 0) {
23
- Logger.info('No MCP servers found matching your query');
24
- Logger.dim('Try searching for: github, slack, notion, stripe, postgres, filesystem');
25
- return;
26
- }
27
-
28
- const limit = parseInt(options.limit);
29
- const limitedResults = results.slice(0, limit);
30
-
31
- if (options.json) {
32
- Logger.json(limitedResults);
33
- return;
34
- }
35
-
36
- console.log('');
37
- Logger.bold(`🔍 MCP Marketplace Search Results`);
38
- console.log('');
39
-
40
- limitedResults.forEach((server, index) => {
41
- console.log(chalk.cyan(`${index + 1}. ${server.name}`));
42
- console.log(` ${chalk.dim(server.description)}`);
43
- console.log(` ${chalk.yellow('Slug:')} ${server.slug} | ${chalk.yellow('Installs:')} ${server.installs.toLocaleString()} | ${chalk.yellow('Author:')} ${server.author}`);
44
- console.log(` ${chalk.green('Tools:')} ${server.tools.join(', ')}`);
45
-
46
- if (server.url) {
47
- console.log(` ${chalk.blue('Connect:')} ${chalk.dim(`mcphost connect ${server.slug}`)}`);
48
- }
49
-
50
- console.log('');
51
- });
52
-
53
- if (results.length > limit) {
54
- Logger.dim(`Showing ${limit} of ${results.length} results. Use --limit to see more.`);
55
- }
56
-
57
- Logger.info('Use `mcphost info <slug>` for detailed information');
58
- Logger.info('Use `mcphost connect <slug>` to install');
59
-
60
- } catch (error) {
61
- spinner.fail('Search failed');
62
- Logger.error(`Error: ${error}`);
63
- process.exit(1);
64
- }
65
- });
66
- }
67
-
68
- export function createInfoCommand(): Command {
69
- return new Command('info')
70
- .description('Show detailed information about an MCP server')
71
- .argument('<slug>', 'MCP server slug')
72
- .option('--json', 'Output as JSON')
73
- .action(async (slug: string, options) => {
74
- const config = new Config();
75
- const api = new MCPHostingAPI(config.token);
76
- const spinner = Logger.spinner(`Getting info for ${slug}...`);
77
-
78
- try {
79
- const server = await api.getMCPInfo(slug);
80
-
81
- if (!server) {
82
- spinner.fail(`MCP server not found: ${slug}`);
83
- Logger.info('Use `mcphost search <query>` to find servers');
84
- return;
85
- }
86
-
87
- spinner.succeed('Found server info');
88
-
89
- if (options.json) {
90
- Logger.json(server);
91
- return;
92
- }
93
-
94
- console.log('');
95
- console.log(chalk.bold.cyan(`📦 ${server.name}`));
96
- console.log(chalk.dim(server.description));
97
- console.log('');
98
-
99
- const infoTable = [
100
- { Property: 'Slug', Value: server.slug },
101
- { Property: 'Author', Value: server.author },
102
- { Property: 'Installs', Value: server.installs.toLocaleString() },
103
- { Property: 'Tools', Value: server.tools.length.toString() }
104
- ];
105
-
106
- if (server.url) {
107
- infoTable.push({ Property: 'URL', Value: server.url });
108
- }
109
-
110
- Logger.table(infoTable);
111
-
112
- console.log('');
113
- console.log(chalk.yellow('🔧 Available Tools:'));
114
- server.tools.forEach(tool => {
115
- console.log(` • ${tool}`);
116
- });
117
-
118
- console.log('');
119
- console.log(chalk.green('💡 Quick Start:'));
120
- console.log(chalk.dim(` mcphost connect ${server.slug}`));
121
-
122
- // Check if already connected
123
- const connection = config.findConnection(slug);
124
- if (connection) {
125
- console.log('');
126
- console.log(chalk.blue('ℹ️ Already connected to:'), connection.clients.join(', '));
127
- }
128
-
129
- } catch (error) {
130
- spinner.fail('Failed to get server info');
131
- Logger.error(`Error: ${error}`);
132
- process.exit(1);
133
- }
134
- });
135
- }
@@ -1,131 +0,0 @@
1
- import { Command } from 'commander';
2
- import { Config } from '../lib/config.js';
3
- import { Logger } from '../lib/logger.js';
4
- import chalk from 'chalk';
5
-
6
- export function createServersCommand(): Command {
7
- const servers = new Command('servers');
8
- servers.description('Manage your hosted MCP servers');
9
-
10
- servers
11
- .command('list')
12
- .description('List your MCP servers')
13
- .action(async () => {
14
- const config = new Config();
15
-
16
- if (!config.token) {
17
- Logger.warning('Authentication required');
18
- Logger.info('Use `mcphost login` to authenticate with MCPHosting');
19
- return;
20
- }
21
-
22
- Logger.info('🚧 Server management coming soon!');
23
- console.log('');
24
- console.log('This feature will let you:');
25
- console.log('• List your hosted MCP servers');
26
- console.log('• View usage analytics');
27
- console.log('• Manage API keys');
28
- console.log('• Publish to marketplace');
29
- console.log('');
30
- Logger.info('Visit https://mcphosting.com to manage servers in the web dashboard');
31
- });
32
-
33
- return servers;
34
- }
35
-
36
- export function createKeysCommand(): Command {
37
- const keys = new Command('keys');
38
- keys.description('Manage API keys for your MCP servers');
39
-
40
- keys
41
- .command('list')
42
- .argument('[server-id]', 'Server ID')
43
- .description('List API keys')
44
- .action(async (serverId?: string) => {
45
- const config = new Config();
46
-
47
- if (!config.token) {
48
- Logger.warning('Authentication required');
49
- Logger.info('Use `mcphost login` to authenticate');
50
- return;
51
- }
52
-
53
- Logger.info('🚧 API key management coming soon!');
54
- Logger.info('Visit https://mcphosting.com to manage keys in the web dashboard');
55
- });
56
-
57
- keys
58
- .command('create')
59
- .argument('<server-id>', 'Server ID')
60
- .option('--name <name>', 'Key name', 'CLI Key')
61
- .description('Create a new API key')
62
- .action(async (serverId: string, options) => {
63
- const config = new Config();
64
-
65
- if (!config.token) {
66
- Logger.warning('Authentication required');
67
- Logger.info('Use `mcphost login` to authenticate');
68
- return;
69
- }
70
-
71
- Logger.info('🚧 API key creation coming soon!');
72
- Logger.info('Visit https://mcphosting.com to create keys in the web dashboard');
73
- });
74
-
75
- keys
76
- .command('revoke')
77
- .argument('<key-id>', 'API key ID')
78
- .description('Revoke an API key')
79
- .action(async (keyId: string) => {
80
- const config = new Config();
81
-
82
- if (!config.token) {
83
- Logger.warning('Authentication required');
84
- Logger.info('Use `mcphost login` to authenticate');
85
- return;
86
- }
87
-
88
- Logger.info('🚧 API key revocation coming soon!');
89
- Logger.info('Visit https://mcphosting.com to revoke keys in the web dashboard');
90
- });
91
-
92
- return keys;
93
- }
94
-
95
- export function createPublishCommand(): Command {
96
- return new Command('publish')
97
- .description('Publish MCP server to marketplace')
98
- .argument('[server-id]', 'Server ID to publish')
99
- .option('--private', 'Keep server private')
100
- .option('--description <desc>', 'Server description')
101
- .option('--tags <tags>', 'Comma-separated tags')
102
- .action(async (serverId?: string, options = {}) => {
103
- const config = new Config();
104
-
105
- if (!config.token) {
106
- Logger.warning('Authentication required');
107
- Logger.info('Use `mcphost login` to authenticate');
108
- return;
109
- }
110
-
111
- Logger.info('🚧 Marketplace publishing coming soon!');
112
- console.log('');
113
- console.log('This feature will let you:');
114
- console.log('• Publish your MCP servers to the marketplace');
115
- console.log('• Set descriptions and tags');
116
- console.log('• Configure public/private visibility');
117
- console.log('• Track usage analytics');
118
- console.log('');
119
- Logger.info('Visit https://mcphosting.com to publish servers via the web dashboard');
120
-
121
- if (serverId) {
122
- Logger.dim(`Would publish server: ${serverId}`);
123
- }
124
- if (options.description) {
125
- Logger.dim(`Description: ${options.description}`);
126
- }
127
- if (options.tags) {
128
- Logger.dim(`Tags: ${options.tags}`);
129
- }
130
- });
131
- }
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
- }