mcphosting-cli 0.4.0 → 0.5.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 (2) hide show
  1. package/index.js +97 -43
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  // MCPHosting CLI — file tasks and link local AI tools to your command center.
3
3
  import { execSync } from 'node:child_process'
4
- import { readFileSync, writeFileSync, existsSync } from 'node:fs'
4
+ import { readFileSync, writeFileSync } from 'node:fs'
5
5
  import { homedir } from 'node:os'
6
6
  import { join } from 'node:path'
7
7
  import { createInterface } from 'node:readline/promises'
8
8
 
9
9
  const CONFIG_PATH = join(homedir(), '.mcphosting.json')
10
- const DEFAULT_SITE = 'https://mcphosting.com'
10
+ // Custom domain (mcphosting.com) not yet attached — default to the live deployment.
11
+ const DEFAULT_SITE = 'https://mcphosting-com-ux-ui.vercel.app'
11
12
 
12
13
  function loadConfig() {
13
14
  try {
@@ -30,70 +31,114 @@ function requireAuth() {
30
31
  return config
31
32
  }
32
33
 
34
+ function siteOf(config) {
35
+ return config.siteUrl ?? DEFAULT_SITE
36
+ }
37
+
38
+ // Parse `--flag value` pairs out of an argv slice; returns { flags, positionals }.
39
+ function parseArgs(argv) {
40
+ const flags = {}
41
+ const positionals = []
42
+ for (let i = 0; i < argv.length; i++) {
43
+ if (argv[i].startsWith('--')) {
44
+ const key = argv[i].slice(2)
45
+ const val = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[(i += 1)] : 'true'
46
+ flags[key] = val
47
+ } else {
48
+ positionals.push(argv[i])
49
+ }
50
+ }
51
+ return { flags, positionals }
52
+ }
53
+
33
54
  async function api(config, path, options = {}) {
34
- const res = await fetch(`${config.siteUrl ?? DEFAULT_SITE}${path}`, {
35
- ...options,
36
- headers: {
37
- Authorization: `Bearer ${config.apiKey}`,
38
- 'Content-Type': 'application/json',
39
- ...options.headers,
40
- },
41
- })
55
+ let res
56
+ try {
57
+ res = await fetch(`${siteOf(config)}${path}`, {
58
+ ...options,
59
+ headers: {
60
+ Authorization: `Bearer ${config.apiKey}`,
61
+ 'Content-Type': 'application/json',
62
+ ...options.headers,
63
+ },
64
+ })
65
+ } catch {
66
+ console.error(`Could not reach ${siteOf(config)} — check your connection or pass --url <site>.`)
67
+ process.exit(1)
68
+ }
69
+ if (res.status === 401) {
70
+ console.error('Unauthorized — your API key is invalid or revoked. Run: mcphosting-cli login')
71
+ process.exit(1)
72
+ }
42
73
  if (!res.ok) {
43
- console.error(`API error ${res.status}: ${await res.text()}`)
74
+ console.error(`API error ${res.status}: ${(await res.text()).slice(0, 300)}`)
44
75
  process.exit(1)
45
76
  }
46
77
  return res.json()
47
78
  }
48
79
 
49
80
  const [, , command, ...args] = process.argv
81
+ const { flags, positionals } = parseArgs(args)
50
82
 
51
83
  switch (command) {
52
84
  case 'login': {
53
- const rl = createInterface({ input: process.stdin, output: process.stdout })
54
- const apiKey = (await rl.question('Paste your MCPHosting API key (mcph_...): ')).trim()
55
- const siteUrl =
56
- (await rl.question(`Site URL [${DEFAULT_SITE}]: `)).trim() || DEFAULT_SITE
57
- rl.close()
58
- if (!apiKey.startsWith('mcph_')) {
85
+ let apiKey = flags.key
86
+ let siteUrl = flags.url
87
+ if (!apiKey) {
88
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
89
+ apiKey = (await rl.question('Paste your MCPHosting API key (mcph_...): ')).trim()
90
+ siteUrl = siteUrl || (await rl.question(`Site URL [${DEFAULT_SITE}]: `)).trim()
91
+ rl.close()
92
+ }
93
+ siteUrl = (siteUrl || DEFAULT_SITE).trim().replace(/\/$/, '')
94
+ if (!apiKey || !apiKey.startsWith('mcph_')) {
59
95
  console.error('That does not look like an MCPHosting key (expected mcph_...).')
60
96
  process.exit(1)
61
97
  }
62
98
  saveConfig({ apiKey, siteUrl })
99
+ // Verify against a real endpoint before declaring success.
100
+ const me = await api({ apiKey, siteUrl }, '/api/v1/me')
101
+ console.log(`Logged in to "${me.workspace}" (${me.plan} plan). Config saved to ${CONFIG_PATH}`)
102
+ break
103
+ }
104
+
105
+ case 'whoami': {
106
+ const config = requireAuth()
107
+ const me = await api(config, '/api/v1/me')
108
+ console.log(`Workspace: ${me.workspace}`)
109
+ console.log(`Plan: ${me.plan}`)
110
+ console.log(`Key: ${config.apiKey.slice(0, 12)}…`)
111
+ console.log(`Site: ${siteOf(config)}`)
112
+ break
113
+ }
114
+
115
+ case 'open': {
63
116
  const config = loadConfig()
64
- await api(config, '/api/v1/tasks?status=all') // verify
65
- console.log(`Logged in. Config saved to ${CONFIG_PATH}`)
117
+ console.log(`${siteOf(config)}/dashboard`)
66
118
  break
67
119
  }
68
120
 
69
121
  case 'link': {
70
122
  const config = requireAuth()
71
- const mcpUrl = `${config.siteUrl ?? DEFAULT_SITE}/api/mcp/mcp`
72
- const target = args[0] ?? 'claude'
123
+ const mcpUrl = `${siteOf(config)}/api/mcp/mcp`
124
+ const target = positionals[0] ?? 'claude'
73
125
  if (target === 'claude') {
74
126
  try {
75
127
  execSync(
76
128
  `claude mcp add mcphosting --transport http "${mcpUrl}" -H "Authorization: Bearer ${config.apiKey}"`,
77
129
  { stdio: 'inherit' },
78
130
  )
79
- console.log('Linked: Claude Code can now file tasks into your MCPHosting dashboard.')
131
+ console.log('Linked Claude Code can now drive your MCPHosting dashboard.')
80
132
  } catch {
81
- console.error('Failed to run the claude CLI is Claude Code installed?')
133
+ console.error('Failed to run the `claude` CLI. Install Claude Code first, or run: mcphosting-cli link config')
82
134
  process.exit(1)
83
135
  }
84
136
  } else {
85
- // Print a generic MCP config snippet (Cursor and other clients).
86
- console.log('Add this MCP server to your client config:\n')
137
+ // Cursor / Windsurf / any MCP client — print a config snippet to paste.
138
+ console.log('Add this MCP server to your client config (Cursor: .cursor/mcp.json):\n')
87
139
  console.log(
88
140
  JSON.stringify(
89
- {
90
- mcpServers: {
91
- mcphosting: {
92
- url: mcpUrl,
93
- headers: { Authorization: `Bearer ${config.apiKey}` },
94
- },
95
- },
96
- },
141
+ { mcpServers: { mcphosting: { url: mcpUrl, headers: { Authorization: `Bearer ${config.apiKey}` } } } },
97
142
  null,
98
143
  2,
99
144
  ),
@@ -104,22 +149,27 @@ switch (command) {
104
149
 
105
150
  case 'task': {
106
151
  const config = requireAuth()
107
- const sub = args[0]
152
+ const sub = positionals[0]
108
153
  if (sub === 'create') {
109
- const title = args.slice(1).join(' ')
154
+ const title = positionals.slice(1).join(' ')
110
155
  if (!title) {
111
- console.error('Usage: mcphosting-cli task create <title>')
156
+ console.error('Usage: mcphosting-cli task create <title> [--dept Sales|Marketing|...] [--message "..."]')
112
157
  process.exit(1)
113
158
  }
114
159
  const result = await api(config, '/api/v1/tasks', {
115
160
  method: 'POST',
116
- body: JSON.stringify({ title }),
161
+ body: JSON.stringify({
162
+ title,
163
+ ...(flags.dept ? { dept: flags.dept } : {}),
164
+ ...(flags.message ? { message: flags.message } : {}),
165
+ }),
117
166
  })
118
167
  console.log(`Task created: ${result.task_id}`)
168
+ console.log(`${siteOf(config)}/dashboard/tasks`)
119
169
  } else if (sub === 'list' || !sub) {
120
170
  const result = await api(config, '/api/v1/tasks')
121
171
  for (const t of result.tasks) {
122
- console.log(`[${t.status.padEnd(12)}] ${t.dept.padEnd(10)} ${t.title}`)
172
+ console.log(`[${t.status.padEnd(12)}] ${(t.dept ?? '').padEnd(10)} ${t.title}`)
123
173
  }
124
174
  if (result.tasks.length === 0) console.log('No open tasks.')
125
175
  } else {
@@ -143,9 +193,13 @@ switch (command) {
143
193
  console.log(`MCPHosting CLI
144
194
 
145
195
  Usage:
146
- mcphosting-cli login Save your API key (Settings -> API & Vault)
147
- mcphosting-cli link [claude|config] Register the MCPHosting MCP server with Claude Code
148
- mcphosting-cli task create <title> File a task into your dashboard
149
- mcphosting-cli task list List open tasks
150
- mcphosting-cli status Task counts by status`)
196
+ mcphosting-cli login [--key mcph_...] [--url <site>] Save + verify your API key
197
+ mcphosting-cli whoami Show the workspace behind your key
198
+ mcphosting-cli link [claude|cursor|config] Bridge a local AI tool to your dashboard
199
+ mcphosting-cli task create <title> [--dept D] [--message "..."]
200
+ mcphosting-cli task list List open tasks
201
+ mcphosting-cli status Task counts by status
202
+ mcphosting-cli open Print your dashboard URL
203
+
204
+ Get an API key: dashboard → Settings → API & Vault.`)
151
205
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcphosting-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for MCPHosting.com \u2014 file tasks, link Claude Code/Cursor to your AI command center",
5
5
  "bin": {
6
6
  "mcphosting-cli": "./index.js"