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.
- package/index.js +97 -43
- 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
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
const target =
|
|
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
|
|
131
|
+
console.log('Linked — Claude Code can now drive your MCPHosting dashboard.')
|
|
80
132
|
} catch {
|
|
81
|
-
console.error('Failed to run the claude CLI
|
|
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
|
-
//
|
|
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 =
|
|
152
|
+
const sub = positionals[0]
|
|
108
153
|
if (sub === 'create') {
|
|
109
|
-
const title =
|
|
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({
|
|
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
|
|
147
|
-
mcphosting-cli
|
|
148
|
-
mcphosting-cli
|
|
149
|
-
mcphosting-cli task
|
|
150
|
-
mcphosting-cli
|
|
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
|
}
|