@mcpcloud/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.
- package/README.md +40 -10
- package/dist/index.js +111 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,10 +4,14 @@ The official command-line interface for [MCPCloud](https://mcpcloud.sh) — mana
|
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npm install -g @mcpcloud/cli
|
|
7
|
+
export MCPCLOUD_BASE_URL=https://your-deployment.example.com # ask your admin
|
|
7
8
|
mcpsh login
|
|
8
9
|
mcpsh whoami
|
|
9
10
|
```
|
|
10
11
|
|
|
12
|
+
> **Note**
|
|
13
|
+
> MCPCloud is in private beta. The CLI does **not** ship with a default API base URL — set `MCPCLOUD_BASE_URL` (or `mcpsh config set-url <url>`) using the URL provided by your MCPCloud admin. A built-in default will be added when the platform launches publicly.
|
|
14
|
+
|
|
11
15
|
The package installs two equivalent binaries: `mcpsh` (short) and `mcpcloud` (full). Use whichever you prefer.
|
|
12
16
|
|
|
13
17
|
## Requirements
|
|
@@ -27,20 +31,39 @@ npx -y @mcpcloud/cli whoami
|
|
|
27
31
|
bunx --bun @mcpcloud/cli whoami
|
|
28
32
|
```
|
|
29
33
|
|
|
30
|
-
##
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
The CLI needs **two** values to talk to your MCPCloud deployment: an API base URL and an API key. Either or both can come from environment variables, the saved config file, or per-invocation flags.
|
|
37
|
+
|
|
38
|
+
### 1. Set the API base URL
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
The CLI does not assume any default base URL. Use whichever method fits your environment:
|
|
33
41
|
|
|
34
42
|
```sh
|
|
35
|
-
#
|
|
43
|
+
# environment variable (recommended for CI / scripts)
|
|
44
|
+
export MCPCLOUD_BASE_URL=https://your-deployment.example.com
|
|
45
|
+
|
|
46
|
+
# persist for your user account (writes to ~/.mcpcloud/config.json)
|
|
47
|
+
mcpsh config set-url https://your-deployment.example.com
|
|
48
|
+
|
|
49
|
+
# one-off override
|
|
50
|
+
mcpsh --base-url https://your-deployment.example.com whoami
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The lookup order is: `--base-url` flag → `MCPCLOUD_BASE_URL` env var → saved `baseUrl` in `~/.mcpcloud/config.json`. If none are set the CLI exits with code `3` and a clear message.
|
|
54
|
+
|
|
55
|
+
### 2. Authenticate
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
# Save a key to ~/.mcpcloud/config.json (mode 0600)
|
|
36
59
|
mcpsh login # interactive prompt (recommended)
|
|
37
60
|
mcpsh login --key mck_xxx # non-interactive (avoid: writes to shell history)
|
|
38
61
|
|
|
39
|
-
#
|
|
62
|
+
# Or use an environment variable (recommended for CI)
|
|
40
63
|
export MCPCLOUD_API_KEY=mck_xxx
|
|
41
64
|
```
|
|
42
65
|
|
|
43
|
-
`mcpsh logout` removes the saved key.
|
|
66
|
+
`mcpsh logout` removes the saved key. `mcpsh config show` prints the current resolved config (with the API key redacted).
|
|
44
67
|
|
|
45
68
|
## Quick start
|
|
46
69
|
|
|
@@ -75,7 +98,7 @@ Run `mcpsh <command> --help` on any command for the full option list.
|
|
|
75
98
|
| Variable | Description |
|
|
76
99
|
|---|---|
|
|
77
100
|
| `MCPCLOUD_API_KEY` | API key used for authentication (overrides the saved config). |
|
|
78
|
-
| `MCPCLOUD_BASE_URL` |
|
|
101
|
+
| `MCPCLOUD_BASE_URL` | API base URL. **Required** — there is no built-in default. |
|
|
79
102
|
| `MCPCLOUD_ORG_ID` | Default organization ID for commands that require one. |
|
|
80
103
|
|
|
81
104
|
The organization for each command is resolved in this order:
|
|
@@ -87,12 +110,18 @@ The organization for each command is resolved in this order:
|
|
|
87
110
|
|
|
88
111
|
## Commands
|
|
89
112
|
|
|
90
|
-
### Authentication
|
|
113
|
+
### Authentication & config
|
|
91
114
|
|
|
92
115
|
```sh
|
|
93
116
|
mcpsh login [--key <secret>] # save an API key locally
|
|
94
117
|
mcpsh logout # remove the saved key
|
|
95
118
|
mcpsh whoami # show current key, base URL, and accessible orgs
|
|
119
|
+
|
|
120
|
+
mcpsh config show # print resolved config (key redacted)
|
|
121
|
+
mcpsh config set-url <url> # persist the API base URL
|
|
122
|
+
mcpsh config clear-url # remove the saved base URL
|
|
123
|
+
mcpsh config set-org <orgId> # persist a default organization
|
|
124
|
+
mcpsh config clear-org # remove the saved default organization
|
|
96
125
|
```
|
|
97
126
|
|
|
98
127
|
### Projects
|
|
@@ -158,6 +187,7 @@ mcpsh --json skills connect skl_123 --agent cursor \
|
|
|
158
187
|
| `0` | Success. |
|
|
159
188
|
| `1` | Generic failure (API error, validation error, network error). |
|
|
160
189
|
| `2` | No API key configured. Run `mcpsh login` or set `MCPCLOUD_API_KEY`. |
|
|
190
|
+
| `3` | No API base URL configured. Set `MCPCLOUD_BASE_URL` or run `mcpsh config set-url <url>`. |
|
|
161
191
|
|
|
162
192
|
When an API call fails, the CLI prints the response code, status, and request id (when present) to make debugging easier:
|
|
163
193
|
|
|
@@ -173,9 +203,9 @@ The CLI stores its config at `~/.mcpcloud/config.json` with mode `0600`. Recogni
|
|
|
173
203
|
|
|
174
204
|
```jsonc
|
|
175
205
|
{
|
|
176
|
-
"apiKey": "mck_...",
|
|
177
|
-
"baseUrl": "https://
|
|
178
|
-
"defaultOrganizationId": "org_..."
|
|
206
|
+
"apiKey": "mck_...", // saved by `mcpsh login`
|
|
207
|
+
"baseUrl": "https://your-deployment...", // saved by `mcpsh config set-url`
|
|
208
|
+
"defaultOrganizationId": "org_..." // saved by `mcpsh config set-org`
|
|
179
209
|
}
|
|
180
210
|
```
|
|
181
211
|
|
package/dist/index.js
CHANGED
|
@@ -1878,7 +1878,6 @@ var {
|
|
|
1878
1878
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from "node:fs";
|
|
1879
1879
|
import { homedir } from "node:os";
|
|
1880
1880
|
import { join } from "node:path";
|
|
1881
|
-
var DEFAULT_BASE_URL = "https://mcpcloud.sh";
|
|
1882
1881
|
function configDir() {
|
|
1883
1882
|
return join(homedir(), ".mcpcloud");
|
|
1884
1883
|
}
|
|
@@ -1892,6 +1891,13 @@ class MissingApiKeyError extends Error {
|
|
|
1892
1891
|
this.name = "MissingApiKeyError";
|
|
1893
1892
|
}
|
|
1894
1893
|
}
|
|
1894
|
+
|
|
1895
|
+
class MissingBaseUrlError extends Error {
|
|
1896
|
+
constructor() {
|
|
1897
|
+
super("No API base URL configured. Set MCPCLOUD_BASE_URL, pass --base-url, or save one with `mcpsh config set-url <url>`.");
|
|
1898
|
+
this.name = "MissingBaseUrlError";
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1895
1901
|
function readConfig() {
|
|
1896
1902
|
if (!existsSync(configFile()))
|
|
1897
1903
|
return {};
|
|
@@ -1923,7 +1929,10 @@ function setBaseUrlOverride(url) {
|
|
|
1923
1929
|
baseUrlOverride = url;
|
|
1924
1930
|
}
|
|
1925
1931
|
function getBaseUrl() {
|
|
1926
|
-
|
|
1932
|
+
const url = baseUrlOverride ?? process.env["MCPCLOUD_BASE_URL"] ?? readConfig().baseUrl;
|
|
1933
|
+
if (!url)
|
|
1934
|
+
throw new MissingBaseUrlError;
|
|
1935
|
+
return url;
|
|
1927
1936
|
}
|
|
1928
1937
|
function requireApiKey() {
|
|
1929
1938
|
const key = getApiKey();
|
|
@@ -1968,6 +1977,9 @@ function buildUrl(path, params) {
|
|
|
1968
1977
|
function shouldRetry(attempt, err) {
|
|
1969
1978
|
if (attempt >= MAX_RETRIES)
|
|
1970
1979
|
return false;
|
|
1980
|
+
if (err instanceof Error && (err.name === "MissingApiKeyError" || err.name === "MissingBaseUrlError")) {
|
|
1981
|
+
return false;
|
|
1982
|
+
}
|
|
1971
1983
|
if (err instanceof McpCloudNetworkError)
|
|
1972
1984
|
return true;
|
|
1973
1985
|
if (err instanceof McpCloudApiError && RETRYABLE_STATUSES.has(err.status))
|
|
@@ -2032,9 +2044,10 @@ async function performRequest(method, path, options) {
|
|
|
2032
2044
|
init.body = JSON.stringify(options.body);
|
|
2033
2045
|
}
|
|
2034
2046
|
try {
|
|
2047
|
+
const url = buildUrl(path, options.params);
|
|
2035
2048
|
let res;
|
|
2036
2049
|
try {
|
|
2037
|
-
res = await fetch(
|
|
2050
|
+
res = await fetch(url, init);
|
|
2038
2051
|
} catch (err) {
|
|
2039
2052
|
if (controller.signal.aborted && !externalSignal?.aborted) {
|
|
2040
2053
|
throw new McpCloudNetworkError(`Request to ${path} timed out after ${timeoutMs}ms.`, err);
|
|
@@ -2144,6 +2157,9 @@ function describeError(err) {
|
|
|
2144
2157
|
if (err instanceof MissingApiKeyError) {
|
|
2145
2158
|
return { message: err.message, code: "missing_api_key" };
|
|
2146
2159
|
}
|
|
2160
|
+
if (err instanceof MissingBaseUrlError) {
|
|
2161
|
+
return { message: err.message, code: "missing_base_url" };
|
|
2162
|
+
}
|
|
2147
2163
|
if (err instanceof Error) {
|
|
2148
2164
|
return { message: err.message };
|
|
2149
2165
|
}
|
|
@@ -2165,7 +2181,11 @@ function handleError(err) {
|
|
|
2165
2181
|
if (info.docsUrl)
|
|
2166
2182
|
console.error(` docs: ${info.docsUrl}`);
|
|
2167
2183
|
}
|
|
2168
|
-
|
|
2184
|
+
if (info.code === "missing_api_key")
|
|
2185
|
+
process.exit(2);
|
|
2186
|
+
if (info.code === "missing_base_url")
|
|
2187
|
+
process.exit(3);
|
|
2188
|
+
process.exit(1);
|
|
2169
2189
|
}
|
|
2170
2190
|
|
|
2171
2191
|
// src/lib/version.ts
|
|
@@ -2812,29 +2832,112 @@ function registerApiKeyCommands(program2) {
|
|
|
2812
2832
|
}));
|
|
2813
2833
|
}
|
|
2814
2834
|
|
|
2835
|
+
// src/commands/config.ts
|
|
2836
|
+
import { homedir as homedir2 } from "node:os";
|
|
2837
|
+
import { join as join3 } from "node:path";
|
|
2838
|
+
function configFilePath() {
|
|
2839
|
+
return join3(homedir2(), ".mcpcloud", "config.json");
|
|
2840
|
+
}
|
|
2841
|
+
function previewKey2(key) {
|
|
2842
|
+
if (!key)
|
|
2843
|
+
return;
|
|
2844
|
+
if (key.length <= 12)
|
|
2845
|
+
return `${key.slice(0, 4)}…`;
|
|
2846
|
+
return `${key.slice(0, 8)}…${key.slice(-4)}`;
|
|
2847
|
+
}
|
|
2848
|
+
function registerConfigCommands(program2) {
|
|
2849
|
+
const config = program2.command("config").description("Manage CLI configuration in ~/.mcpcloud/config.json");
|
|
2850
|
+
config.command("show").description("Print the current saved config (API key is redacted)").action(runAction(() => {
|
|
2851
|
+
const c = readConfig();
|
|
2852
|
+
const safe = {
|
|
2853
|
+
path: configFilePath(),
|
|
2854
|
+
baseUrl: c.baseUrl ?? null,
|
|
2855
|
+
defaultOrganizationId: c.defaultOrganizationId ?? null,
|
|
2856
|
+
apiKeyPreview: previewKey2(c.apiKey) ?? null
|
|
2857
|
+
};
|
|
2858
|
+
if (isJsonMode()) {
|
|
2859
|
+
printJson(safe);
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
printKeyValue({
|
|
2863
|
+
path: safe.path,
|
|
2864
|
+
"base url": safe.baseUrl ?? "—",
|
|
2865
|
+
"default org": safe.defaultOrganizationId ?? "—",
|
|
2866
|
+
"api key": safe.apiKeyPreview ?? "—"
|
|
2867
|
+
});
|
|
2868
|
+
}));
|
|
2869
|
+
config.command("set-url <url>").description("Save the API base URL to ~/.mcpcloud/config.json").action(runAction((url) => {
|
|
2870
|
+
try {
|
|
2871
|
+
new URL(url);
|
|
2872
|
+
} catch {
|
|
2873
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
2874
|
+
}
|
|
2875
|
+
const c = readConfig();
|
|
2876
|
+
c.baseUrl = url;
|
|
2877
|
+
writeConfig(c);
|
|
2878
|
+
if (isJsonMode()) {
|
|
2879
|
+
printJson({ baseUrl: url });
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
printSuccess(`Saved base URL: ${url}`);
|
|
2883
|
+
}));
|
|
2884
|
+
config.command("clear-url").description("Remove the saved base URL (falls back to MCPCLOUD_BASE_URL)").action(runAction(() => {
|
|
2885
|
+
const c = readConfig();
|
|
2886
|
+
delete c.baseUrl;
|
|
2887
|
+
writeConfig(c);
|
|
2888
|
+
if (isJsonMode()) {
|
|
2889
|
+
printJson({ baseUrl: null });
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
printSuccess("Cleared saved base URL.");
|
|
2893
|
+
}));
|
|
2894
|
+
config.command("set-org <organizationId>").description("Save a default organization ID").action(runAction((organizationId) => {
|
|
2895
|
+
const c = readConfig();
|
|
2896
|
+
c.defaultOrganizationId = organizationId;
|
|
2897
|
+
writeConfig(c);
|
|
2898
|
+
if (isJsonMode()) {
|
|
2899
|
+
printJson({ defaultOrganizationId: organizationId });
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
printSuccess(`Saved default organization: ${organizationId}`);
|
|
2903
|
+
}));
|
|
2904
|
+
config.command("clear-org").description("Remove the saved default organization ID").action(runAction(() => {
|
|
2905
|
+
const c = readConfig();
|
|
2906
|
+
delete c.defaultOrganizationId;
|
|
2907
|
+
writeConfig(c);
|
|
2908
|
+
if (isJsonMode()) {
|
|
2909
|
+
printJson({ defaultOrganizationId: null });
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
printSuccess("Cleared saved default organization.");
|
|
2913
|
+
}));
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2815
2916
|
// src/create-program.ts
|
|
2816
2917
|
function createProgram() {
|
|
2817
2918
|
const program2 = new Command;
|
|
2818
|
-
program2.name("mcpsh").description("The official CLI for MCPCloud — manage projects, MCP servers, skills, and API keys from the terminal").version(getCliVersion(), "-v, --version", "Print the CLI version").option("--json", "Output raw JSON (useful for scripting)").option("--base-url <url>", "
|
|
2919
|
+
program2.name("mcpsh").description("The official CLI for MCPCloud — manage projects, MCP servers, skills, and API keys from the terminal").version(getCliVersion(), "-v, --version", "Print the CLI version").option("--json", "Output raw JSON (useful for scripting)").option("--base-url <url>", "API base URL for this invocation. Falls back to MCPCLOUD_BASE_URL or `mcpsh config set-url`.").hook("preAction", (thisCommand) => {
|
|
2819
2920
|
const opts = thisCommand.optsWithGlobals();
|
|
2820
2921
|
setJsonMode(Boolean(opts.json));
|
|
2821
2922
|
setBaseUrlOverride(opts.baseUrl);
|
|
2822
2923
|
}).addHelpText("after", [
|
|
2823
2924
|
"",
|
|
2824
2925
|
"Environment variables:",
|
|
2825
|
-
" MCPCLOUD_API_KEY API key used for authentication",
|
|
2826
|
-
" MCPCLOUD_BASE_URL
|
|
2926
|
+
" MCPCLOUD_API_KEY API key used for authentication (required)",
|
|
2927
|
+
" MCPCLOUD_BASE_URL API base URL (required — no default is shipped)",
|
|
2827
2928
|
" MCPCLOUD_ORG_ID Default organization ID",
|
|
2828
2929
|
"",
|
|
2829
2930
|
"Examples:",
|
|
2931
|
+
" $ export MCPCLOUD_BASE_URL=https://your-deployment.example.com",
|
|
2932
|
+
" $ mcpsh config set-url https://your-deployment.example.com # persist it",
|
|
2830
2933
|
" $ mcpsh login",
|
|
2831
2934
|
" $ mcpsh whoami",
|
|
2832
|
-
" $ mcpsh projects list",
|
|
2833
2935
|
" $ mcpsh skills connect skill_123 --agent claude-code --apply",
|
|
2834
2936
|
" $ mcpsh --json servers list"
|
|
2835
2937
|
].join(`
|
|
2836
2938
|
`));
|
|
2837
2939
|
registerAuthCommands(program2);
|
|
2940
|
+
registerConfigCommands(program2);
|
|
2838
2941
|
registerProjectCommands(program2);
|
|
2839
2942
|
registerServerCommands(program2);
|
|
2840
2943
|
registerToolCommands(program2);
|