@mcp-use/cli 2.2.1 → 2.2.2-canary.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/README.md +62 -0
- package/dist/commands/auth.d.ts +13 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/deploy.d.ts +13 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/index.js +1214 -74
- package/dist/index.mjs +1209 -67
- package/dist/utils/api.d.ts +103 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/config.d.ts +34 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/git.d.ts +45 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -6,12 +6,1133 @@ import { Command } from "commander";
|
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import { readFileSync } from "fs";
|
|
8
8
|
import { access } from "fs/promises";
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import path3 from "path";
|
|
10
|
+
import open3 from "open";
|
|
11
|
+
import chalk3 from "chalk";
|
|
12
|
+
|
|
13
|
+
// src/commands/auth.ts
|
|
11
14
|
import chalk from "chalk";
|
|
15
|
+
import {
|
|
16
|
+
createServer
|
|
17
|
+
} from "http";
|
|
18
|
+
import open from "open";
|
|
19
|
+
|
|
20
|
+
// src/utils/config.ts
|
|
21
|
+
import { promises as fs } from "fs";
|
|
22
|
+
import path from "path";
|
|
23
|
+
import os from "os";
|
|
24
|
+
var CONFIG_DIR = path.join(os.homedir(), ".mcp-use");
|
|
25
|
+
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
26
|
+
var DEFAULT_API_URL = process.env.MCP_API_URL ? process.env.MCP_API_URL.replace(/\/api\/v1$/, "") + "/api/v1" : "https://cloud.mcp-use.com/api/v1";
|
|
27
|
+
var DEFAULT_WEB_URL = process.env.MCP_WEB_URL ? process.env.MCP_WEB_URL : "https://mcp-use.com";
|
|
28
|
+
async function ensureConfigDir() {
|
|
29
|
+
try {
|
|
30
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
31
|
+
} catch (error) {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function readConfig() {
|
|
35
|
+
try {
|
|
36
|
+
const content = await fs.readFile(CONFIG_FILE, "utf-8");
|
|
37
|
+
return JSON.parse(content);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function writeConfig(config) {
|
|
43
|
+
await ensureConfigDir();
|
|
44
|
+
await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
45
|
+
}
|
|
46
|
+
async function deleteConfig() {
|
|
47
|
+
try {
|
|
48
|
+
await fs.unlink(CONFIG_FILE);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function getApiUrl() {
|
|
53
|
+
const config = await readConfig();
|
|
54
|
+
return config.apiUrl || DEFAULT_API_URL;
|
|
55
|
+
}
|
|
56
|
+
async function getApiKey() {
|
|
57
|
+
const config = await readConfig();
|
|
58
|
+
return config.apiKey || null;
|
|
59
|
+
}
|
|
60
|
+
async function isLoggedIn() {
|
|
61
|
+
const apiKey = await getApiKey();
|
|
62
|
+
return !!apiKey;
|
|
63
|
+
}
|
|
64
|
+
async function getWebUrl() {
|
|
65
|
+
return DEFAULT_WEB_URL;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/utils/api.ts
|
|
69
|
+
var McpUseAPI = class _McpUseAPI {
|
|
70
|
+
baseUrl;
|
|
71
|
+
apiKey;
|
|
72
|
+
constructor(baseUrl, apiKey) {
|
|
73
|
+
this.baseUrl = baseUrl || "";
|
|
74
|
+
this.apiKey = apiKey;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Initialize API client with config
|
|
78
|
+
*/
|
|
79
|
+
static async create() {
|
|
80
|
+
const baseUrl = await getApiUrl();
|
|
81
|
+
const apiKey = await getApiKey();
|
|
82
|
+
return new _McpUseAPI(baseUrl, apiKey ?? void 0);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Make authenticated request
|
|
86
|
+
*/
|
|
87
|
+
async request(endpoint, options = {}) {
|
|
88
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
89
|
+
const headers = {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
...options.headers || {}
|
|
92
|
+
};
|
|
93
|
+
if (this.apiKey) {
|
|
94
|
+
headers["x-api-key"] = this.apiKey;
|
|
95
|
+
}
|
|
96
|
+
const response = await fetch(url, {
|
|
97
|
+
...options,
|
|
98
|
+
headers
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const error = await response.text();
|
|
102
|
+
throw new Error(`API request failed: ${response.status} ${error}`);
|
|
103
|
+
}
|
|
104
|
+
return response.json();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create API key using JWT token
|
|
108
|
+
*/
|
|
109
|
+
async createApiKey(jwtToken, name = "CLI") {
|
|
110
|
+
const url = `${this.baseUrl}/api-key`;
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: {
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
Authorization: `Bearer ${jwtToken}`
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify({ name })
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const error = await response.text();
|
|
121
|
+
throw new Error(`Failed to create API key: ${response.status} ${error}`);
|
|
122
|
+
}
|
|
123
|
+
return response.json();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Test authentication
|
|
127
|
+
*/
|
|
128
|
+
async testAuth() {
|
|
129
|
+
return this.request("/test-auth");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create deployment
|
|
133
|
+
*/
|
|
134
|
+
async createDeployment(request) {
|
|
135
|
+
return this.request("/deployments", {
|
|
136
|
+
method: "POST",
|
|
137
|
+
body: JSON.stringify(request)
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get deployment by ID
|
|
142
|
+
*/
|
|
143
|
+
async getDeployment(deploymentId) {
|
|
144
|
+
return this.request(`/deployments/${deploymentId}`);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Stream deployment logs
|
|
148
|
+
*/
|
|
149
|
+
async *streamDeploymentLogs(deploymentId) {
|
|
150
|
+
const url = `${this.baseUrl}/deployments/${deploymentId}/logs/stream`;
|
|
151
|
+
const headers = {};
|
|
152
|
+
if (this.apiKey) {
|
|
153
|
+
headers["x-api-key"] = this.apiKey;
|
|
154
|
+
}
|
|
155
|
+
const response = await fetch(url, { headers });
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
throw new Error(`Failed to stream logs: ${response.status}`);
|
|
158
|
+
}
|
|
159
|
+
if (!response.body) {
|
|
160
|
+
throw new Error("Response body is null");
|
|
161
|
+
}
|
|
162
|
+
const reader = response.body.getReader();
|
|
163
|
+
const decoder = new TextDecoder();
|
|
164
|
+
let buffer = "";
|
|
165
|
+
try {
|
|
166
|
+
while (true) {
|
|
167
|
+
const { done, value } = await reader.read();
|
|
168
|
+
if (done) break;
|
|
169
|
+
buffer += decoder.decode(value, { stream: true });
|
|
170
|
+
const lines = buffer.split("\n");
|
|
171
|
+
buffer = lines.pop() || "";
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
if (line.startsWith("data: ")) {
|
|
174
|
+
const data = line.slice(6);
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(data);
|
|
177
|
+
if (parsed.log) {
|
|
178
|
+
yield parsed.log;
|
|
179
|
+
} else if (parsed.error) {
|
|
180
|
+
throw new Error(parsed.error);
|
|
181
|
+
}
|
|
182
|
+
} catch (e) {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} finally {
|
|
188
|
+
reader.releaseLock();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create deployment with source code upload
|
|
193
|
+
*/
|
|
194
|
+
async createDeploymentWithUpload(request, filePath) {
|
|
195
|
+
const { readFile } = await import("fs/promises");
|
|
196
|
+
const { basename } = await import("path");
|
|
197
|
+
const { stat } = await import("fs/promises");
|
|
198
|
+
const stats = await stat(filePath);
|
|
199
|
+
const maxSize = 2 * 1024 * 1024;
|
|
200
|
+
if (stats.size > maxSize) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`File size (${(stats.size / 1024 / 1024).toFixed(2)}MB) exceeds maximum of 2MB`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const fileBuffer = await readFile(filePath);
|
|
206
|
+
const filename = basename(filePath);
|
|
207
|
+
const formData = new FormData();
|
|
208
|
+
const blob = new Blob([fileBuffer], { type: "application/gzip" });
|
|
209
|
+
formData.append("source_file", blob, filename);
|
|
210
|
+
formData.append("name", request.name);
|
|
211
|
+
formData.append("source_type", "upload");
|
|
212
|
+
if (request.source.type === "upload") {
|
|
213
|
+
formData.append("runtime", request.source.runtime || "node");
|
|
214
|
+
formData.append("port", String(request.source.port || 3e3));
|
|
215
|
+
if (request.source.startCommand) {
|
|
216
|
+
formData.append("startCommand", request.source.startCommand);
|
|
217
|
+
}
|
|
218
|
+
if (request.source.buildCommand) {
|
|
219
|
+
formData.append("buildCommand", request.source.buildCommand);
|
|
220
|
+
}
|
|
221
|
+
if (request.source.env && Object.keys(request.source.env).length > 0) {
|
|
222
|
+
formData.append("env", JSON.stringify(request.source.env));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (request.customDomain) {
|
|
226
|
+
formData.append("customDomain", request.customDomain);
|
|
227
|
+
}
|
|
228
|
+
if (request.healthCheckPath) {
|
|
229
|
+
formData.append("healthCheckPath", request.healthCheckPath);
|
|
230
|
+
}
|
|
231
|
+
const url = `${this.baseUrl}/deployments`;
|
|
232
|
+
const headers = {};
|
|
233
|
+
if (this.apiKey) {
|
|
234
|
+
headers["x-api-key"] = this.apiKey;
|
|
235
|
+
}
|
|
236
|
+
const response = await fetch(url, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers,
|
|
239
|
+
body: formData
|
|
240
|
+
});
|
|
241
|
+
if (!response.ok) {
|
|
242
|
+
const error = await response.text();
|
|
243
|
+
throw new Error(`Deployment failed: ${error}`);
|
|
244
|
+
}
|
|
245
|
+
return response.json();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/commands/auth.ts
|
|
250
|
+
var LOGIN_TIMEOUT = 3e5;
|
|
251
|
+
async function findAvailablePort(startPort = 8765) {
|
|
252
|
+
for (let port = startPort; port < startPort + 100; port++) {
|
|
253
|
+
try {
|
|
254
|
+
await new Promise((resolve, reject) => {
|
|
255
|
+
const server = createServer();
|
|
256
|
+
server.once("error", reject);
|
|
257
|
+
server.once("listening", () => {
|
|
258
|
+
server.close();
|
|
259
|
+
resolve();
|
|
260
|
+
});
|
|
261
|
+
server.listen(port);
|
|
262
|
+
});
|
|
263
|
+
return port;
|
|
264
|
+
} catch {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
throw new Error("No available ports found");
|
|
269
|
+
}
|
|
270
|
+
async function startCallbackServer(port) {
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
let tokenResolver = null;
|
|
273
|
+
const tokenPromise = new Promise((res) => {
|
|
274
|
+
tokenResolver = res;
|
|
275
|
+
});
|
|
276
|
+
const server = createServer((req, res) => {
|
|
277
|
+
if (req.url?.startsWith("/callback")) {
|
|
278
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
279
|
+
const token = url.searchParams.get("token");
|
|
280
|
+
if (token && tokenResolver) {
|
|
281
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
282
|
+
res.end(`
|
|
283
|
+
<!DOCTYPE html>
|
|
284
|
+
<html>
|
|
285
|
+
<head>
|
|
286
|
+
<title>Login Successful</title>
|
|
287
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
288
|
+
<style>
|
|
289
|
+
* {
|
|
290
|
+
margin: 0;
|
|
291
|
+
padding: 0;
|
|
292
|
+
box-sizing: border-box;
|
|
293
|
+
}
|
|
294
|
+
body {
|
|
295
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
296
|
+
display: flex;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
align-items: center;
|
|
299
|
+
min-height: 100vh;
|
|
300
|
+
background: #000;
|
|
301
|
+
padding: 1rem;
|
|
302
|
+
}
|
|
303
|
+
.container {
|
|
304
|
+
width: 100%;
|
|
305
|
+
max-width: 28rem;
|
|
306
|
+
padding: 3rem;
|
|
307
|
+
text-align: center;
|
|
308
|
+
-webkit-backdrop-filter: blur(40px);
|
|
309
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
310
|
+
border-radius: 1.5rem;
|
|
311
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
312
|
+
}
|
|
313
|
+
.icon-container {
|
|
314
|
+
display: inline-flex;
|
|
315
|
+
align-items: center;
|
|
316
|
+
justify-content: center;
|
|
317
|
+
width: 6rem;
|
|
318
|
+
height: 6rem;
|
|
319
|
+
margin-bottom: 2rem;
|
|
320
|
+
background: rgba(255, 255, 255, 0.1);
|
|
321
|
+
backdrop-filter: blur(10px);
|
|
322
|
+
-webkit-backdrop-filter: blur(10px);
|
|
323
|
+
border-radius: 50%;
|
|
324
|
+
}
|
|
325
|
+
.checkmark {
|
|
326
|
+
font-size: 4rem;
|
|
327
|
+
color: #fff;
|
|
328
|
+
line-height: 1;
|
|
329
|
+
animation: scaleIn 0.5s ease-out;
|
|
330
|
+
}
|
|
331
|
+
@keyframes scaleIn {
|
|
332
|
+
from {
|
|
333
|
+
transform: scale(0);
|
|
334
|
+
opacity: 0;
|
|
335
|
+
}
|
|
336
|
+
to {
|
|
337
|
+
transform: scale(1);
|
|
338
|
+
opacity: 1;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
h1 {
|
|
342
|
+
color: #fff;
|
|
343
|
+
margin: 0 0 1rem 0;
|
|
344
|
+
font-size: 2.5rem;
|
|
345
|
+
font-weight: 700;
|
|
346
|
+
letter-spacing: -0.025em;
|
|
347
|
+
}
|
|
348
|
+
p {
|
|
349
|
+
color: rgba(255, 255, 255, 0.8);
|
|
350
|
+
margin: 0 0 2rem 0;
|
|
351
|
+
font-size: 1.125rem;
|
|
352
|
+
line-height: 1.5;
|
|
353
|
+
}
|
|
354
|
+
.spinner {
|
|
355
|
+
display: inline-block;
|
|
356
|
+
width: 2rem;
|
|
357
|
+
height: 2rem;
|
|
358
|
+
border: 3px solid rgba(255, 255, 255, 0.3);
|
|
359
|
+
border-top-color: #fff;
|
|
360
|
+
border-radius: 50%;
|
|
361
|
+
animation: spin 0.8s linear infinite;
|
|
362
|
+
}
|
|
363
|
+
@keyframes spin {
|
|
364
|
+
to { transform: rotate(360deg); }
|
|
365
|
+
}
|
|
366
|
+
.footer {
|
|
367
|
+
margin-top: 2rem;
|
|
368
|
+
color: rgba(255, 255, 255, 0.6);
|
|
369
|
+
font-size: 0.875rem;
|
|
370
|
+
}
|
|
371
|
+
</style>
|
|
372
|
+
</head>
|
|
373
|
+
<body>
|
|
374
|
+
<div class="container">
|
|
375
|
+
<h1>Authentication Successful!</h1>
|
|
376
|
+
<p>You can now close this window and return to the CLI.</p>
|
|
377
|
+
</div>
|
|
378
|
+
</body>
|
|
379
|
+
</html>
|
|
380
|
+
`);
|
|
381
|
+
tokenResolver(token);
|
|
382
|
+
} else {
|
|
383
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
384
|
+
res.end(`
|
|
385
|
+
<!DOCTYPE html>
|
|
386
|
+
<html>
|
|
387
|
+
<head>
|
|
388
|
+
<title>Login Failed</title>
|
|
389
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
390
|
+
<style>
|
|
391
|
+
* {
|
|
392
|
+
margin: 0;
|
|
393
|
+
padding: 0;
|
|
394
|
+
box-sizing: border-box;
|
|
395
|
+
}
|
|
396
|
+
body {
|
|
397
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
398
|
+
display: flex;
|
|
399
|
+
justify-content: center;
|
|
400
|
+
align-items: center;
|
|
401
|
+
min-height: 100vh;
|
|
402
|
+
background: #000;
|
|
403
|
+
padding: 1rem;
|
|
404
|
+
}
|
|
405
|
+
.container {
|
|
406
|
+
width: 100%;
|
|
407
|
+
max-width: 28rem;
|
|
408
|
+
padding: 3rem;
|
|
409
|
+
text-align: center;
|
|
410
|
+
background: rgba(255, 255, 255, 0.1);
|
|
411
|
+
backdrop-filter: blur(40px);
|
|
412
|
+
-webkit-backdrop-filter: blur(40px);
|
|
413
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
414
|
+
border-radius: 1.5rem;
|
|
415
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
416
|
+
}
|
|
417
|
+
.icon-container {
|
|
418
|
+
display: inline-flex;
|
|
419
|
+
align-items: center;
|
|
420
|
+
justify-content: center;
|
|
421
|
+
width: 6rem;
|
|
422
|
+
height: 6rem;
|
|
423
|
+
margin-bottom: 2rem;
|
|
424
|
+
background: rgba(255, 255, 255, 0.1);
|
|
425
|
+
backdrop-filter: blur(10px);
|
|
426
|
+
-webkit-backdrop-filter: blur(10px);
|
|
427
|
+
border-radius: 50%;
|
|
428
|
+
}
|
|
429
|
+
.cross {
|
|
430
|
+
font-size: 4rem;
|
|
431
|
+
color: #fff;
|
|
432
|
+
line-height: 1;
|
|
433
|
+
}
|
|
434
|
+
h1 {
|
|
435
|
+
color: #fff;
|
|
436
|
+
margin: 0 0 1rem 0;
|
|
437
|
+
font-size: 2.5rem;
|
|
438
|
+
font-weight: 700;
|
|
439
|
+
letter-spacing: -0.025em;
|
|
440
|
+
}
|
|
441
|
+
p {
|
|
442
|
+
color: rgba(255, 255, 255, 0.8);
|
|
443
|
+
margin: 0;
|
|
444
|
+
font-size: 1.125rem;
|
|
445
|
+
line-height: 1.5;
|
|
446
|
+
}
|
|
447
|
+
</style>
|
|
448
|
+
</head>
|
|
449
|
+
<body>
|
|
450
|
+
<div class="container">
|
|
451
|
+
<div class="icon-container">
|
|
452
|
+
<div class="cross">\u2717</div>
|
|
453
|
+
</div>
|
|
454
|
+
<h1>Login Failed</h1>
|
|
455
|
+
<p>No token received. Please try again.</p>
|
|
456
|
+
</div>
|
|
457
|
+
</body>
|
|
458
|
+
</html>
|
|
459
|
+
`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
server.listen(port, () => {
|
|
464
|
+
resolve({ server, token: tokenPromise });
|
|
465
|
+
});
|
|
466
|
+
server.on("error", reject);
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
async function loginCommand() {
|
|
470
|
+
try {
|
|
471
|
+
if (await isLoggedIn()) {
|
|
472
|
+
console.log(
|
|
473
|
+
chalk.yellow(
|
|
474
|
+
"\u26A0\uFE0F You are already logged in. Run 'mcp-use logout' first if you want to login with a different account."
|
|
475
|
+
)
|
|
476
|
+
);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
console.log(chalk.cyan.bold("\u{1F510} Logging in to mcp-use cloud...\n"));
|
|
480
|
+
const port = await findAvailablePort();
|
|
481
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
482
|
+
console.log(chalk.gray(`Starting local server on port ${port}...`));
|
|
483
|
+
const { server, token } = await startCallbackServer(port);
|
|
484
|
+
const webUrl = await getWebUrl();
|
|
485
|
+
const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
486
|
+
console.log(chalk.gray(`Opening browser to ${webUrl}/auth/cli...
|
|
487
|
+
`));
|
|
488
|
+
console.log(
|
|
489
|
+
chalk.white(
|
|
490
|
+
"If the browser doesn't open automatically, please visit:\n" + chalk.cyan(loginUrl)
|
|
491
|
+
)
|
|
492
|
+
);
|
|
493
|
+
await open(loginUrl);
|
|
494
|
+
console.log(
|
|
495
|
+
chalk.gray("\nWaiting for authentication... (this may take a moment)")
|
|
496
|
+
);
|
|
497
|
+
const jwtToken = await Promise.race([
|
|
498
|
+
token,
|
|
499
|
+
new Promise(
|
|
500
|
+
(_, reject) => setTimeout(
|
|
501
|
+
() => reject(new Error("Login timeout - please try again")),
|
|
502
|
+
LOGIN_TIMEOUT
|
|
503
|
+
)
|
|
504
|
+
)
|
|
505
|
+
]);
|
|
506
|
+
server.close();
|
|
507
|
+
console.log(
|
|
508
|
+
chalk.gray("Received authentication token, creating API key...")
|
|
509
|
+
);
|
|
510
|
+
const api = await McpUseAPI.create();
|
|
511
|
+
const apiKeyResponse = await api.createApiKey(jwtToken, "CLI");
|
|
512
|
+
await writeConfig({
|
|
513
|
+
apiKey: apiKeyResponse.api_key
|
|
514
|
+
});
|
|
515
|
+
console.log(chalk.green.bold("\n\u2713 Successfully logged in!"));
|
|
516
|
+
console.log(
|
|
517
|
+
chalk.gray(
|
|
518
|
+
`
|
|
519
|
+
Your API key has been saved to ${chalk.white("~/.mcp-use/config.json")}`
|
|
520
|
+
)
|
|
521
|
+
);
|
|
522
|
+
console.log(
|
|
523
|
+
chalk.gray(
|
|
524
|
+
"You can now deploy your MCP servers with " + chalk.white("mcp-use deploy")
|
|
525
|
+
)
|
|
526
|
+
);
|
|
527
|
+
process.exit(0);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.error(
|
|
530
|
+
chalk.red.bold("\n\u2717 Login failed:"),
|
|
531
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
532
|
+
);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
async function logoutCommand() {
|
|
537
|
+
try {
|
|
538
|
+
if (!await isLoggedIn()) {
|
|
539
|
+
console.log(chalk.yellow("\u26A0\uFE0F You are not logged in."));
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
console.log(chalk.cyan.bold("\u{1F513} Logging out...\n"));
|
|
543
|
+
await deleteConfig();
|
|
544
|
+
console.log(chalk.green.bold("\u2713 Successfully logged out!"));
|
|
545
|
+
console.log(
|
|
546
|
+
chalk.gray(
|
|
547
|
+
"\nYour local config has been deleted. The API key will remain active until revoked from the web interface."
|
|
548
|
+
)
|
|
549
|
+
);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error(
|
|
552
|
+
chalk.red.bold("\n\u2717 Logout failed:"),
|
|
553
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
554
|
+
);
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function whoamiCommand() {
|
|
559
|
+
try {
|
|
560
|
+
if (!await isLoggedIn()) {
|
|
561
|
+
console.log(chalk.yellow("\u26A0\uFE0F You are not logged in."));
|
|
562
|
+
console.log(
|
|
563
|
+
chalk.gray("Run " + chalk.white("mcp-use login") + " to get started.")
|
|
564
|
+
);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
console.log(chalk.cyan.bold("\u{1F464} Current user:\n"));
|
|
568
|
+
const api = await McpUseAPI.create();
|
|
569
|
+
const authInfo = await api.testAuth();
|
|
570
|
+
console.log(chalk.white("Email: ") + chalk.cyan(authInfo.email));
|
|
571
|
+
console.log(chalk.white("User ID: ") + chalk.gray(authInfo.user_id));
|
|
572
|
+
const apiKey = await getApiKey();
|
|
573
|
+
if (apiKey) {
|
|
574
|
+
const masked = apiKey.substring(0, 8) + "..." + apiKey.substring(apiKey.length - 4);
|
|
575
|
+
console.log(chalk.white("API Key: ") + chalk.gray(masked));
|
|
576
|
+
}
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.error(
|
|
579
|
+
chalk.red.bold("\n\u2717 Failed to get user info:"),
|
|
580
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
581
|
+
);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/commands/deploy.ts
|
|
587
|
+
import chalk2 from "chalk";
|
|
588
|
+
import { promises as fs2 } from "fs";
|
|
589
|
+
import path2 from "path";
|
|
590
|
+
import os2 from "os";
|
|
591
|
+
import { exec as exec2 } from "child_process";
|
|
592
|
+
import { promisify as promisify2 } from "util";
|
|
593
|
+
|
|
594
|
+
// src/utils/git.ts
|
|
595
|
+
import { exec } from "child_process";
|
|
596
|
+
import { promisify } from "util";
|
|
597
|
+
var execAsync = promisify(exec);
|
|
598
|
+
async function gitCommand(command, cwd = process.cwd()) {
|
|
599
|
+
try {
|
|
600
|
+
const { stdout } = await execAsync(command, { cwd });
|
|
601
|
+
return stdout.trim();
|
|
602
|
+
} catch (error) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function isGitRepo(cwd = process.cwd()) {
|
|
607
|
+
const result = await gitCommand("git rev-parse --is-inside-work-tree", cwd);
|
|
608
|
+
return result === "true";
|
|
609
|
+
}
|
|
610
|
+
async function getRemoteUrl(cwd = process.cwd()) {
|
|
611
|
+
return gitCommand("git config --get remote.origin.url", cwd);
|
|
612
|
+
}
|
|
613
|
+
function parseGitHubUrl(url) {
|
|
614
|
+
const sshMatch = url.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
615
|
+
const httpsMatch = url.match(
|
|
616
|
+
/https:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/
|
|
617
|
+
);
|
|
618
|
+
const match = sshMatch || httpsMatch;
|
|
619
|
+
if (!match) return null;
|
|
620
|
+
return {
|
|
621
|
+
owner: match[1],
|
|
622
|
+
repo: match[2]
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
async function getCurrentBranch(cwd = process.cwd()) {
|
|
626
|
+
return gitCommand("git rev-parse --abbrev-ref HEAD", cwd);
|
|
627
|
+
}
|
|
628
|
+
async function getCommitSha(cwd = process.cwd()) {
|
|
629
|
+
return gitCommand("git rev-parse HEAD", cwd);
|
|
630
|
+
}
|
|
631
|
+
async function getCommitMessage(cwd = process.cwd()) {
|
|
632
|
+
return gitCommand("git log -1 --pretty=%B", cwd);
|
|
633
|
+
}
|
|
634
|
+
async function getGitInfo(cwd = process.cwd()) {
|
|
635
|
+
const isRepo = await isGitRepo(cwd);
|
|
636
|
+
if (!isRepo) {
|
|
637
|
+
return { isGitRepo: false };
|
|
638
|
+
}
|
|
639
|
+
const remoteUrl = await getRemoteUrl(cwd);
|
|
640
|
+
const branch = await getCurrentBranch(cwd);
|
|
641
|
+
const commitSha = await getCommitSha(cwd);
|
|
642
|
+
const commitMessage = await getCommitMessage(cwd);
|
|
643
|
+
let owner;
|
|
644
|
+
let repo;
|
|
645
|
+
if (remoteUrl) {
|
|
646
|
+
const parsed = parseGitHubUrl(remoteUrl);
|
|
647
|
+
if (parsed) {
|
|
648
|
+
owner = parsed.owner;
|
|
649
|
+
repo = parsed.repo;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return {
|
|
653
|
+
isGitRepo: true,
|
|
654
|
+
remoteUrl: remoteUrl || void 0,
|
|
655
|
+
owner,
|
|
656
|
+
repo,
|
|
657
|
+
branch: branch || void 0,
|
|
658
|
+
commitSha: commitSha || void 0,
|
|
659
|
+
commitMessage: commitMessage || void 0
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function isGitHubUrl(url) {
|
|
663
|
+
return url.includes("github.com");
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// src/commands/deploy.ts
|
|
667
|
+
import open2 from "open";
|
|
668
|
+
var execAsync2 = promisify2(exec2);
|
|
669
|
+
async function isMcpProject(cwd = process.cwd()) {
|
|
670
|
+
try {
|
|
671
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
672
|
+
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
673
|
+
const packageJson2 = JSON.parse(content);
|
|
674
|
+
const hasMcpDeps = packageJson2.dependencies?.["mcp-use"] || packageJson2.dependencies?.["@modelcontextprotocol/sdk"] || packageJson2.devDependencies?.["mcp-use"] || packageJson2.devDependencies?.["@modelcontextprotocol/sdk"];
|
|
675
|
+
const hasMcpScripts = packageJson2.scripts?.mcp || packageJson2.scripts?.["mcp:dev"];
|
|
676
|
+
return !!(hasMcpDeps || hasMcpScripts);
|
|
677
|
+
} catch {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
async function getProjectName(cwd = process.cwd()) {
|
|
682
|
+
try {
|
|
683
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
684
|
+
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
685
|
+
const packageJson2 = JSON.parse(content);
|
|
686
|
+
if (packageJson2.name) {
|
|
687
|
+
return packageJson2.name;
|
|
688
|
+
}
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
return path2.basename(cwd);
|
|
692
|
+
}
|
|
693
|
+
async function detectBuildCommand(cwd = process.cwd()) {
|
|
694
|
+
try {
|
|
695
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
696
|
+
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
697
|
+
const packageJson2 = JSON.parse(content);
|
|
698
|
+
if (packageJson2.scripts?.build) {
|
|
699
|
+
return "npm run build";
|
|
700
|
+
}
|
|
701
|
+
} catch {
|
|
702
|
+
}
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
async function detectStartCommand(cwd = process.cwd()) {
|
|
706
|
+
try {
|
|
707
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
708
|
+
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
709
|
+
const packageJson2 = JSON.parse(content);
|
|
710
|
+
if (packageJson2.scripts?.start) {
|
|
711
|
+
return "npm start";
|
|
712
|
+
}
|
|
713
|
+
if (packageJson2.main) {
|
|
714
|
+
return `node ${packageJson2.main}`;
|
|
715
|
+
}
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
return void 0;
|
|
719
|
+
}
|
|
720
|
+
async function detectRuntime(cwd = process.cwd()) {
|
|
721
|
+
try {
|
|
722
|
+
const pythonFiles = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
723
|
+
for (const file of pythonFiles) {
|
|
724
|
+
try {
|
|
725
|
+
await fs2.access(path2.join(cwd, file));
|
|
726
|
+
return "python";
|
|
727
|
+
} catch {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
try {
|
|
732
|
+
await fs2.access(path2.join(cwd, "package.json"));
|
|
733
|
+
return "node";
|
|
734
|
+
} catch {
|
|
735
|
+
}
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
return "node";
|
|
739
|
+
}
|
|
740
|
+
async function prompt(question) {
|
|
741
|
+
const readline = await import("readline");
|
|
742
|
+
const rl = readline.createInterface({
|
|
743
|
+
input: process.stdin,
|
|
744
|
+
output: process.stdout
|
|
745
|
+
});
|
|
746
|
+
return new Promise((resolve) => {
|
|
747
|
+
rl.question(question, (answer) => {
|
|
748
|
+
rl.close();
|
|
749
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
async function createTarball(cwd) {
|
|
754
|
+
const tmpDir = os2.tmpdir();
|
|
755
|
+
const tarballPath = path2.join(tmpDir, `mcp-deploy-${Date.now()}.tar.gz`);
|
|
756
|
+
const excludePatterns = [
|
|
757
|
+
"node_modules",
|
|
758
|
+
".git",
|
|
759
|
+
"dist",
|
|
760
|
+
"build",
|
|
761
|
+
".next",
|
|
762
|
+
".venv",
|
|
763
|
+
"__pycache__",
|
|
764
|
+
"*.pyc",
|
|
765
|
+
".DS_Store",
|
|
766
|
+
".env",
|
|
767
|
+
".env.local",
|
|
768
|
+
"*.log"
|
|
769
|
+
];
|
|
770
|
+
const excludeFlags = excludePatterns.map((pattern) => `--exclude='${pattern}'`).join(" ");
|
|
771
|
+
const command = `tar ${excludeFlags} -czf "${tarballPath}" -C "${cwd}" .`;
|
|
772
|
+
try {
|
|
773
|
+
await execAsync2(command);
|
|
774
|
+
return tarballPath;
|
|
775
|
+
} catch (error) {
|
|
776
|
+
throw new Error(
|
|
777
|
+
`Failed to create tarball: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function formatFileSize(bytes) {
|
|
782
|
+
if (bytes === 0) return "0 B";
|
|
783
|
+
const k = 1024;
|
|
784
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
785
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
786
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
787
|
+
}
|
|
788
|
+
async function displayDeploymentProgress(api, deployment) {
|
|
789
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
790
|
+
let frameIndex = 0;
|
|
791
|
+
let spinnerInterval = null;
|
|
792
|
+
let lastStep = "";
|
|
793
|
+
const startSpinner = (message) => {
|
|
794
|
+
if (spinnerInterval) {
|
|
795
|
+
clearInterval(spinnerInterval);
|
|
796
|
+
}
|
|
797
|
+
process.stdout.write("\r\x1B[K");
|
|
798
|
+
spinnerInterval = setInterval(() => {
|
|
799
|
+
const frame = frames[frameIndex];
|
|
800
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
801
|
+
process.stdout.write(
|
|
802
|
+
"\r" + chalk2.cyan(frame) + " " + chalk2.gray(message)
|
|
803
|
+
);
|
|
804
|
+
}, 80);
|
|
805
|
+
};
|
|
806
|
+
const stopSpinner = () => {
|
|
807
|
+
if (spinnerInterval) {
|
|
808
|
+
clearInterval(spinnerInterval);
|
|
809
|
+
spinnerInterval = null;
|
|
810
|
+
process.stdout.write("\r\x1B[K");
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
console.log();
|
|
814
|
+
startSpinner("Deploying...");
|
|
815
|
+
try {
|
|
816
|
+
for await (const log of api.streamDeploymentLogs(deployment.id)) {
|
|
817
|
+
try {
|
|
818
|
+
const logData = JSON.parse(log);
|
|
819
|
+
if (logData.step && logData.step !== lastStep) {
|
|
820
|
+
lastStep = logData.step;
|
|
821
|
+
const stepMessages = {
|
|
822
|
+
clone: "Preparing source code...",
|
|
823
|
+
analyze: "Analyzing project...",
|
|
824
|
+
build: "Building container image...",
|
|
825
|
+
deploy: "Deploying to cloud..."
|
|
826
|
+
};
|
|
827
|
+
const message = stepMessages[logData.step] || "Deploying...";
|
|
828
|
+
startSpinner(message);
|
|
829
|
+
}
|
|
830
|
+
if (logData.line) {
|
|
831
|
+
stopSpinner();
|
|
832
|
+
const levelColor = logData.level === "error" ? chalk2.red : logData.level === "warn" ? chalk2.yellow : chalk2.gray;
|
|
833
|
+
const stepPrefix = logData.step ? chalk2.cyan(`[${logData.step}]`) + " " : "";
|
|
834
|
+
console.log(stepPrefix + levelColor(logData.line));
|
|
835
|
+
}
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
} catch (error) {
|
|
840
|
+
stopSpinner();
|
|
841
|
+
}
|
|
842
|
+
let checkCount = 0;
|
|
843
|
+
const maxChecks = 60;
|
|
844
|
+
let delay = 3e3;
|
|
845
|
+
const maxDelay = 1e4;
|
|
846
|
+
let lastDisplayedLogLength = 0;
|
|
847
|
+
while (checkCount < maxChecks) {
|
|
848
|
+
const currentDelay = delay;
|
|
849
|
+
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
850
|
+
const finalDeployment = await api.getDeployment(deployment.id);
|
|
851
|
+
if (finalDeployment.buildLogs && finalDeployment.buildLogs.length > lastDisplayedLogLength) {
|
|
852
|
+
const newLogs = finalDeployment.buildLogs.substring(
|
|
853
|
+
lastDisplayedLogLength
|
|
854
|
+
);
|
|
855
|
+
const logLines = newLogs.split("\n").filter((l) => l.trim());
|
|
856
|
+
for (const line of logLines) {
|
|
857
|
+
try {
|
|
858
|
+
const logData = JSON.parse(line);
|
|
859
|
+
if (logData.line) {
|
|
860
|
+
stopSpinner();
|
|
861
|
+
const levelColor = logData.level === "error" ? chalk2.red : logData.level === "warn" ? chalk2.yellow : chalk2.gray;
|
|
862
|
+
const stepPrefix = logData.step ? chalk2.cyan(`[${logData.step}]`) + " " : "";
|
|
863
|
+
console.log(stepPrefix + levelColor(logData.line));
|
|
864
|
+
}
|
|
865
|
+
} catch {
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
lastDisplayedLogLength = finalDeployment.buildLogs.length;
|
|
869
|
+
}
|
|
870
|
+
if (finalDeployment.status === "running") {
|
|
871
|
+
const mcpUrl = `https://${finalDeployment.domain}/mcp`;
|
|
872
|
+
const inspectorUrl = `https://inspector.mcp-use.com/inspect?autoConnect=${encodeURIComponent(mcpUrl)}`;
|
|
873
|
+
console.log(chalk2.green.bold("\u2713 Deployment successful!\n"));
|
|
874
|
+
console.log(chalk2.white("\u{1F310} MCP Server URL:"));
|
|
875
|
+
console.log(chalk2.cyan.bold(` ${mcpUrl}
|
|
876
|
+
`));
|
|
877
|
+
console.log(chalk2.white("\u{1F50D} Inspector URL:"));
|
|
878
|
+
console.log(chalk2.cyan.bold(` ${inspectorUrl}
|
|
879
|
+
`));
|
|
880
|
+
if (finalDeployment.customDomain) {
|
|
881
|
+
const customMcpUrl = `https://${finalDeployment.customDomain}/mcp`;
|
|
882
|
+
const customInspectorUrl = `https://inspector.mcp-use.com/inspect?autoConnect=${encodeURIComponent(customMcpUrl)}`;
|
|
883
|
+
console.log(chalk2.white("\u{1F517} Custom Domain:"));
|
|
884
|
+
console.log(chalk2.cyan.bold(` ${customMcpUrl}
|
|
885
|
+
`));
|
|
886
|
+
console.log(chalk2.white("\u{1F50D} Custom Inspector:"));
|
|
887
|
+
console.log(chalk2.cyan.bold(` ${customInspectorUrl}
|
|
888
|
+
`));
|
|
889
|
+
}
|
|
890
|
+
console.log(
|
|
891
|
+
chalk2.gray("Deployment ID: ") + chalk2.white(finalDeployment.id)
|
|
892
|
+
);
|
|
893
|
+
return;
|
|
894
|
+
} else if (finalDeployment.status === "failed") {
|
|
895
|
+
console.log(chalk2.red.bold("\u2717 Deployment failed\n"));
|
|
896
|
+
if (finalDeployment.error) {
|
|
897
|
+
console.log(chalk2.red("Error: ") + finalDeployment.error);
|
|
898
|
+
}
|
|
899
|
+
if (finalDeployment.buildLogs) {
|
|
900
|
+
console.log(chalk2.gray("\nBuild logs:"));
|
|
901
|
+
try {
|
|
902
|
+
const logs = finalDeployment.buildLogs.split("\n").filter((l) => l.trim());
|
|
903
|
+
for (const log of logs) {
|
|
904
|
+
try {
|
|
905
|
+
const logData = JSON.parse(log);
|
|
906
|
+
if (logData.line) {
|
|
907
|
+
console.log(chalk2.gray(` ${logData.line}`));
|
|
908
|
+
}
|
|
909
|
+
} catch {
|
|
910
|
+
console.log(chalk2.gray(` ${log}`));
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
} catch {
|
|
914
|
+
console.log(chalk2.gray(finalDeployment.buildLogs));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
process.exit(1);
|
|
918
|
+
} else if (finalDeployment.status === "building") {
|
|
919
|
+
startSpinner("Building and deploying...");
|
|
920
|
+
checkCount++;
|
|
921
|
+
delay = Math.min(delay * 1.2, maxDelay);
|
|
922
|
+
} else {
|
|
923
|
+
console.log(
|
|
924
|
+
chalk2.yellow("\u26A0\uFE0F Deployment status: ") + finalDeployment.status
|
|
925
|
+
);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
stopSpinner();
|
|
930
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Deployment is taking longer than expected."));
|
|
931
|
+
console.log(
|
|
932
|
+
chalk2.gray("Check status with: ") + chalk2.white(`mcp-use status ${deployment.id}`)
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
async function deployCommand(options) {
|
|
936
|
+
try {
|
|
937
|
+
const cwd = process.cwd();
|
|
938
|
+
if (!await isLoggedIn()) {
|
|
939
|
+
console.log(chalk2.red("\u2717 You are not logged in."));
|
|
940
|
+
console.log(
|
|
941
|
+
chalk2.gray("Run " + chalk2.white("mcp-use login") + " to get started.")
|
|
942
|
+
);
|
|
943
|
+
process.exit(1);
|
|
944
|
+
}
|
|
945
|
+
console.log(chalk2.cyan.bold("\u{1F680} Deploying to mcp-use cloud...\n"));
|
|
946
|
+
const isMcp = await isMcpProject(cwd);
|
|
947
|
+
if (!isMcp) {
|
|
948
|
+
console.log(
|
|
949
|
+
chalk2.yellow(
|
|
950
|
+
"\u26A0\uFE0F This doesn't appear to be an MCP server project (no mcp-use or @modelcontextprotocol/sdk dependency found)."
|
|
951
|
+
)
|
|
952
|
+
);
|
|
953
|
+
const shouldContinue = await prompt(
|
|
954
|
+
chalk2.white("Continue anyway? (y/n): ")
|
|
955
|
+
);
|
|
956
|
+
if (!shouldContinue) {
|
|
957
|
+
console.log(chalk2.gray("Deployment cancelled."));
|
|
958
|
+
process.exit(0);
|
|
959
|
+
}
|
|
960
|
+
console.log();
|
|
961
|
+
}
|
|
962
|
+
const gitInfo = await getGitInfo(cwd);
|
|
963
|
+
if (!options.fromSource && gitInfo.isGitRepo && gitInfo.remoteUrl && isGitHubUrl(gitInfo.remoteUrl)) {
|
|
964
|
+
if (!gitInfo.owner || !gitInfo.repo) {
|
|
965
|
+
console.log(
|
|
966
|
+
chalk2.red(
|
|
967
|
+
"\u2717 Could not parse GitHub repository information from remote URL."
|
|
968
|
+
)
|
|
969
|
+
);
|
|
970
|
+
process.exit(1);
|
|
971
|
+
}
|
|
972
|
+
console.log(chalk2.white("GitHub repository detected:"));
|
|
973
|
+
console.log(
|
|
974
|
+
chalk2.gray(` Repository: `) + chalk2.cyan(`${gitInfo.owner}/${gitInfo.repo}`)
|
|
975
|
+
);
|
|
976
|
+
console.log(
|
|
977
|
+
chalk2.gray(` Branch: `) + chalk2.cyan(gitInfo.branch || "main")
|
|
978
|
+
);
|
|
979
|
+
if (gitInfo.commitSha) {
|
|
980
|
+
console.log(
|
|
981
|
+
chalk2.gray(` Commit: `) + chalk2.gray(gitInfo.commitSha.substring(0, 7))
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
if (gitInfo.commitMessage) {
|
|
985
|
+
console.log(
|
|
986
|
+
chalk2.gray(` Message: `) + chalk2.gray(gitInfo.commitMessage.split("\n")[0])
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
console.log();
|
|
990
|
+
const shouldDeploy = await prompt(
|
|
991
|
+
chalk2.white(
|
|
992
|
+
`Deploy from GitHub repository ${gitInfo.owner}/${gitInfo.repo}? (y/n): `
|
|
993
|
+
)
|
|
994
|
+
);
|
|
995
|
+
if (!shouldDeploy) {
|
|
996
|
+
console.log(chalk2.gray("Deployment cancelled."));
|
|
997
|
+
process.exit(0);
|
|
998
|
+
}
|
|
999
|
+
const projectName = options.name || await getProjectName(cwd);
|
|
1000
|
+
const runtime = options.runtime || await detectRuntime(cwd);
|
|
1001
|
+
const port = options.port || 3e3;
|
|
1002
|
+
const buildCommand = await detectBuildCommand(cwd);
|
|
1003
|
+
const startCommand = await detectStartCommand(cwd);
|
|
1004
|
+
console.log();
|
|
1005
|
+
console.log(chalk2.white("Deployment configuration:"));
|
|
1006
|
+
console.log(chalk2.gray(` Name: `) + chalk2.cyan(projectName));
|
|
1007
|
+
console.log(chalk2.gray(` Runtime: `) + chalk2.cyan(runtime));
|
|
1008
|
+
console.log(chalk2.gray(` Port: `) + chalk2.cyan(port));
|
|
1009
|
+
if (buildCommand) {
|
|
1010
|
+
console.log(chalk2.gray(` Build command: `) + chalk2.cyan(buildCommand));
|
|
1011
|
+
}
|
|
1012
|
+
if (startCommand) {
|
|
1013
|
+
console.log(chalk2.gray(` Start command: `) + chalk2.cyan(startCommand));
|
|
1014
|
+
}
|
|
1015
|
+
console.log();
|
|
1016
|
+
const deploymentRequest = {
|
|
1017
|
+
name: projectName,
|
|
1018
|
+
source: {
|
|
1019
|
+
type: "github",
|
|
1020
|
+
repo: `${gitInfo.owner}/${gitInfo.repo}`,
|
|
1021
|
+
branch: gitInfo.branch || "main",
|
|
1022
|
+
runtime,
|
|
1023
|
+
port,
|
|
1024
|
+
buildCommand,
|
|
1025
|
+
startCommand
|
|
1026
|
+
},
|
|
1027
|
+
healthCheckPath: "/healthz"
|
|
1028
|
+
};
|
|
1029
|
+
console.log(chalk2.gray("Creating deployment..."));
|
|
1030
|
+
const api = await McpUseAPI.create();
|
|
1031
|
+
const deployment = await api.createDeployment(deploymentRequest);
|
|
1032
|
+
console.log(
|
|
1033
|
+
chalk2.green("\u2713 Deployment created: ") + chalk2.gray(deployment.id)
|
|
1034
|
+
);
|
|
1035
|
+
await displayDeploymentProgress(api, deployment);
|
|
1036
|
+
if (options.open && deployment.domain) {
|
|
1037
|
+
console.log();
|
|
1038
|
+
console.log(chalk2.gray("Opening deployment in browser..."));
|
|
1039
|
+
await open2(`https://${deployment.domain}`);
|
|
1040
|
+
}
|
|
1041
|
+
} else {
|
|
1042
|
+
if (options.fromSource) {
|
|
1043
|
+
console.log(
|
|
1044
|
+
chalk2.white("\u{1F4E6} Deploying from local source code (--from-source)...")
|
|
1045
|
+
);
|
|
1046
|
+
} else {
|
|
1047
|
+
console.log(
|
|
1048
|
+
chalk2.yellow(
|
|
1049
|
+
"\u26A0\uFE0F This is not a GitHub repository or no remote is configured."
|
|
1050
|
+
)
|
|
1051
|
+
);
|
|
1052
|
+
console.log(chalk2.white("Deploying from local source code instead..."));
|
|
1053
|
+
}
|
|
1054
|
+
console.log();
|
|
1055
|
+
const projectName = options.name || await getProjectName(cwd);
|
|
1056
|
+
const runtime = options.runtime || await detectRuntime(cwd);
|
|
1057
|
+
const port = options.port || 3e3;
|
|
1058
|
+
const buildCommand = await detectBuildCommand(cwd);
|
|
1059
|
+
const startCommand = await detectStartCommand(cwd);
|
|
1060
|
+
console.log(chalk2.white("Deployment configuration:"));
|
|
1061
|
+
console.log(chalk2.gray(` Name: `) + chalk2.cyan(projectName));
|
|
1062
|
+
console.log(chalk2.gray(` Runtime: `) + chalk2.cyan(runtime));
|
|
1063
|
+
console.log(chalk2.gray(` Port: `) + chalk2.cyan(port));
|
|
1064
|
+
if (buildCommand) {
|
|
1065
|
+
console.log(chalk2.gray(` Build command: `) + chalk2.cyan(buildCommand));
|
|
1066
|
+
}
|
|
1067
|
+
if (startCommand) {
|
|
1068
|
+
console.log(chalk2.gray(` Start command: `) + chalk2.cyan(startCommand));
|
|
1069
|
+
}
|
|
1070
|
+
console.log();
|
|
1071
|
+
const shouldDeploy = await prompt(
|
|
1072
|
+
chalk2.white("Deploy from local source? (y/n): ")
|
|
1073
|
+
);
|
|
1074
|
+
if (!shouldDeploy) {
|
|
1075
|
+
console.log(chalk2.gray("Deployment cancelled."));
|
|
1076
|
+
process.exit(0);
|
|
1077
|
+
}
|
|
1078
|
+
console.log();
|
|
1079
|
+
console.log(chalk2.gray("Packaging source code..."));
|
|
1080
|
+
const tarballPath = await createTarball(cwd);
|
|
1081
|
+
const stats = await fs2.stat(tarballPath);
|
|
1082
|
+
console.log(
|
|
1083
|
+
chalk2.green("\u2713 Packaged: ") + chalk2.gray(formatFileSize(stats.size))
|
|
1084
|
+
);
|
|
1085
|
+
const maxSize = 2 * 1024 * 1024;
|
|
1086
|
+
if (stats.size > maxSize) {
|
|
1087
|
+
console.log(
|
|
1088
|
+
chalk2.red(
|
|
1089
|
+
`\u2717 File size (${formatFileSize(stats.size)}) exceeds maximum of 2MB`
|
|
1090
|
+
)
|
|
1091
|
+
);
|
|
1092
|
+
await fs2.unlink(tarballPath);
|
|
1093
|
+
process.exit(1);
|
|
1094
|
+
}
|
|
1095
|
+
const deploymentRequest = {
|
|
1096
|
+
name: projectName,
|
|
1097
|
+
source: {
|
|
1098
|
+
type: "upload",
|
|
1099
|
+
runtime,
|
|
1100
|
+
port,
|
|
1101
|
+
buildCommand,
|
|
1102
|
+
startCommand
|
|
1103
|
+
},
|
|
1104
|
+
healthCheckPath: "/healthz"
|
|
1105
|
+
};
|
|
1106
|
+
console.log(chalk2.gray("Creating deployment..."));
|
|
1107
|
+
const api = await McpUseAPI.create();
|
|
1108
|
+
const deployment = await api.createDeploymentWithUpload(
|
|
1109
|
+
deploymentRequest,
|
|
1110
|
+
tarballPath
|
|
1111
|
+
);
|
|
1112
|
+
await fs2.unlink(tarballPath);
|
|
1113
|
+
console.log(
|
|
1114
|
+
chalk2.green("\u2713 Deployment created: ") + chalk2.gray(deployment.id)
|
|
1115
|
+
);
|
|
1116
|
+
await displayDeploymentProgress(api, deployment);
|
|
1117
|
+
if (options.open && deployment.domain) {
|
|
1118
|
+
console.log();
|
|
1119
|
+
console.log(chalk2.gray("Opening deployment in browser..."));
|
|
1120
|
+
await open2(`https://${deployment.domain}`);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
console.error(
|
|
1125
|
+
chalk2.red.bold("\n\u2717 Deployment failed:"),
|
|
1126
|
+
chalk2.red(error instanceof Error ? error.message : "Unknown error")
|
|
1127
|
+
);
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// src/index.ts
|
|
12
1133
|
var program = new Command();
|
|
13
1134
|
var packageContent = readFileSync(
|
|
14
|
-
|
|
1135
|
+
path3.join(__dirname, "../package.json"),
|
|
15
1136
|
"utf-8"
|
|
16
1137
|
);
|
|
17
1138
|
var packageJson = JSON.parse(packageContent);
|
|
@@ -25,7 +1146,7 @@ async function isPortAvailable(port, host = "localhost") {
|
|
|
25
1146
|
return true;
|
|
26
1147
|
}
|
|
27
1148
|
}
|
|
28
|
-
async function
|
|
1149
|
+
async function findAvailablePort2(startPort, host = "localhost") {
|
|
29
1150
|
for (let port = startPort; port < startPort + 100; port++) {
|
|
30
1151
|
if (await isPortAvailable(port, host)) {
|
|
31
1152
|
return port;
|
|
@@ -75,7 +1196,7 @@ function runCommand(command, args, cwd, env, filterStderr = false) {
|
|
|
75
1196
|
}
|
|
76
1197
|
async function startTunnel(port) {
|
|
77
1198
|
return new Promise((resolve, reject) => {
|
|
78
|
-
console.log(
|
|
1199
|
+
console.log(chalk3.gray(`Starting tunnel for port ${port}...`));
|
|
79
1200
|
const proc = spawn("npx", ["--yes", "@mcp-use/tunnel", String(port)], {
|
|
80
1201
|
stdio: ["ignore", "pipe", "pipe"],
|
|
81
1202
|
shell: false
|
|
@@ -90,7 +1211,7 @@ async function startTunnel(port) {
|
|
|
90
1211
|
const subdomain = url;
|
|
91
1212
|
resolved = true;
|
|
92
1213
|
clearTimeout(setupTimeout);
|
|
93
|
-
console.log(
|
|
1214
|
+
console.log(chalk3.green.bold(`\u2713 Tunnel established: ${url}/mcp`));
|
|
94
1215
|
resolve({ url, subdomain, process: proc });
|
|
95
1216
|
}
|
|
96
1217
|
});
|
|
@@ -121,7 +1242,7 @@ async function findServerFile(projectPath) {
|
|
|
121
1242
|
const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
|
|
122
1243
|
for (const candidate of candidates) {
|
|
123
1244
|
try {
|
|
124
|
-
await access(
|
|
1245
|
+
await access(path3.join(projectPath, candidate));
|
|
125
1246
|
return candidate;
|
|
126
1247
|
} catch {
|
|
127
1248
|
continue;
|
|
@@ -130,18 +1251,18 @@ async function findServerFile(projectPath) {
|
|
|
130
1251
|
throw new Error("No server file found");
|
|
131
1252
|
}
|
|
132
1253
|
async function buildWidgets(projectPath) {
|
|
133
|
-
const { promises:
|
|
1254
|
+
const { promises: fs3 } = await import("fs");
|
|
134
1255
|
const { build } = await import("vite");
|
|
135
|
-
const resourcesDir =
|
|
1256
|
+
const resourcesDir = path3.join(projectPath, "resources");
|
|
136
1257
|
const mcpUrl = process.env.MCP_URL;
|
|
137
1258
|
if (!mcpUrl) {
|
|
138
1259
|
console.log(
|
|
139
|
-
|
|
1260
|
+
chalk3.yellow(
|
|
140
1261
|
"\u26A0\uFE0F MCP_URL not set - using relative paths (widgets may not work correctly)"
|
|
141
1262
|
)
|
|
142
1263
|
);
|
|
143
1264
|
console.log(
|
|
144
|
-
|
|
1265
|
+
chalk3.gray(
|
|
145
1266
|
" Set MCP_URL environment variable for production builds (e.g., https://myserver.com)"
|
|
146
1267
|
)
|
|
147
1268
|
);
|
|
@@ -150,39 +1271,39 @@ async function buildWidgets(projectPath) {
|
|
|
150
1271
|
await access(resourcesDir);
|
|
151
1272
|
} catch {
|
|
152
1273
|
console.log(
|
|
153
|
-
|
|
1274
|
+
chalk3.gray("No resources/ directory found - skipping widget build")
|
|
154
1275
|
);
|
|
155
1276
|
return [];
|
|
156
1277
|
}
|
|
157
1278
|
let entries = [];
|
|
158
1279
|
try {
|
|
159
|
-
const files = await
|
|
160
|
-
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) =>
|
|
1280
|
+
const files = await fs3.readdir(resourcesDir);
|
|
1281
|
+
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => path3.join(resourcesDir, f));
|
|
161
1282
|
} catch (error) {
|
|
162
|
-
console.log(
|
|
1283
|
+
console.log(chalk3.gray("No widgets found in resources/ directory"));
|
|
163
1284
|
return [];
|
|
164
1285
|
}
|
|
165
1286
|
if (entries.length === 0) {
|
|
166
|
-
console.log(
|
|
1287
|
+
console.log(chalk3.gray("No widgets found in resources/ directory"));
|
|
167
1288
|
return [];
|
|
168
1289
|
}
|
|
169
|
-
console.log(
|
|
1290
|
+
console.log(chalk3.gray(`Building ${entries.length} widget(s)...`));
|
|
170
1291
|
const react = (await import("@vitejs/plugin-react")).default;
|
|
171
1292
|
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
172
1293
|
const builtWidgets = [];
|
|
173
1294
|
for (const entry of entries) {
|
|
174
|
-
const baseName =
|
|
1295
|
+
const baseName = path3.basename(entry).replace(/\.tsx?$/, "");
|
|
175
1296
|
const widgetName = baseName;
|
|
176
|
-
console.log(
|
|
177
|
-
const tempDir =
|
|
178
|
-
await
|
|
179
|
-
const relativeResourcesPath =
|
|
1297
|
+
console.log(chalk3.gray(` - Building ${widgetName}...`));
|
|
1298
|
+
const tempDir = path3.join(projectPath, ".mcp-use", widgetName);
|
|
1299
|
+
await fs3.mkdir(tempDir, { recursive: true });
|
|
1300
|
+
const relativeResourcesPath = path3.relative(tempDir, resourcesDir).replace(/\\/g, "/");
|
|
180
1301
|
const cssContent = `@import "tailwindcss";
|
|
181
1302
|
|
|
182
1303
|
/* Configure Tailwind to scan the resources directory */
|
|
183
1304
|
@source "${relativeResourcesPath}";
|
|
184
1305
|
`;
|
|
185
|
-
await
|
|
1306
|
+
await fs3.writeFile(path3.join(tempDir, "styles.css"), cssContent, "utf8");
|
|
186
1307
|
const entryContent = `import React from 'react'
|
|
187
1308
|
import { createRoot } from 'react-dom/client'
|
|
188
1309
|
import './styles.css'
|
|
@@ -206,9 +1327,9 @@ if (container && Component) {
|
|
|
206
1327
|
<script type="module" src="/entry.tsx"></script>
|
|
207
1328
|
</body>
|
|
208
1329
|
</html>`;
|
|
209
|
-
await
|
|
210
|
-
await
|
|
211
|
-
const outDir =
|
|
1330
|
+
await fs3.writeFile(path3.join(tempDir, "entry.tsx"), entryContent, "utf8");
|
|
1331
|
+
await fs3.writeFile(path3.join(tempDir, "index.html"), htmlContent, "utf8");
|
|
1332
|
+
const outDir = path3.join(
|
|
212
1333
|
projectPath,
|
|
213
1334
|
"dist",
|
|
214
1335
|
"resources",
|
|
@@ -218,16 +1339,16 @@ if (container && Component) {
|
|
|
218
1339
|
const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
|
|
219
1340
|
let widgetMetadata = {};
|
|
220
1341
|
try {
|
|
221
|
-
const metadataTempDir =
|
|
1342
|
+
const metadataTempDir = path3.join(
|
|
222
1343
|
projectPath,
|
|
223
1344
|
".mcp-use",
|
|
224
1345
|
`${widgetName}-metadata`
|
|
225
1346
|
);
|
|
226
|
-
await
|
|
227
|
-
const { createServer } = await import("vite");
|
|
228
|
-
const metadataServer = await
|
|
1347
|
+
await fs3.mkdir(metadataTempDir, { recursive: true });
|
|
1348
|
+
const { createServer: createServer2 } = await import("vite");
|
|
1349
|
+
const metadataServer = await createServer2({
|
|
229
1350
|
root: metadataTempDir,
|
|
230
|
-
cacheDir:
|
|
1351
|
+
cacheDir: path3.join(metadataTempDir, ".vite-cache"),
|
|
231
1352
|
plugins: [tailwindcss(), react()],
|
|
232
1353
|
resolve: {
|
|
233
1354
|
alias: {
|
|
@@ -267,12 +1388,12 @@ if (container && Component) {
|
|
|
267
1388
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
268
1389
|
} catch (error) {
|
|
269
1390
|
console.warn(
|
|
270
|
-
|
|
1391
|
+
chalk3.yellow(` \u26A0 Could not extract metadata for ${widgetName}`)
|
|
271
1392
|
);
|
|
272
1393
|
} finally {
|
|
273
1394
|
await metadataServer.close();
|
|
274
1395
|
try {
|
|
275
|
-
await
|
|
1396
|
+
await fs3.rm(metadataTempDir, { recursive: true, force: true });
|
|
276
1397
|
} catch {
|
|
277
1398
|
}
|
|
278
1399
|
}
|
|
@@ -303,7 +1424,7 @@ if (container && Component) {
|
|
|
303
1424
|
outDir,
|
|
304
1425
|
emptyOutDir: true,
|
|
305
1426
|
rollupOptions: {
|
|
306
|
-
input:
|
|
1427
|
+
input: path3.join(tempDir, "index.html")
|
|
307
1428
|
}
|
|
308
1429
|
}
|
|
309
1430
|
});
|
|
@@ -311,23 +1432,23 @@ if (container && Component) {
|
|
|
311
1432
|
name: widgetName,
|
|
312
1433
|
metadata: widgetMetadata
|
|
313
1434
|
});
|
|
314
|
-
console.log(
|
|
1435
|
+
console.log(chalk3.green(` \u2713 Built ${widgetName}`));
|
|
315
1436
|
} catch (error) {
|
|
316
|
-
console.error(
|
|
1437
|
+
console.error(chalk3.red(` \u2717 Failed to build ${widgetName}:`), error);
|
|
317
1438
|
}
|
|
318
1439
|
}
|
|
319
1440
|
return builtWidgets;
|
|
320
1441
|
}
|
|
321
1442
|
program.command("build").description("Build TypeScript and MCP UI widgets").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--with-inspector", "Include inspector in production build").action(async (options) => {
|
|
322
1443
|
try {
|
|
323
|
-
const projectPath =
|
|
324
|
-
const { promises:
|
|
325
|
-
console.log(
|
|
1444
|
+
const projectPath = path3.resolve(options.path);
|
|
1445
|
+
const { promises: fs3 } = await import("fs");
|
|
1446
|
+
console.log(chalk3.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
326
1447
|
const builtWidgets = await buildWidgets(projectPath);
|
|
327
|
-
console.log(
|
|
1448
|
+
console.log(chalk3.gray("Building TypeScript..."));
|
|
328
1449
|
await runCommand("npx", ["tsc"], projectPath);
|
|
329
|
-
console.log(
|
|
330
|
-
const manifestPath =
|
|
1450
|
+
console.log(chalk3.green("\u2713 TypeScript build complete!"));
|
|
1451
|
+
const manifestPath = path3.join(projectPath, "dist", "mcp-use.json");
|
|
331
1452
|
const widgetsData = {};
|
|
332
1453
|
for (const widget of builtWidgets) {
|
|
333
1454
|
widgetsData[widget.name] = widget.metadata;
|
|
@@ -338,36 +1459,36 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
338
1459
|
buildTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
1460
|
widgets: widgetsData
|
|
340
1461
|
};
|
|
341
|
-
await
|
|
342
|
-
await
|
|
1462
|
+
await fs3.mkdir(path3.dirname(manifestPath), { recursive: true });
|
|
1463
|
+
await fs3.writeFile(
|
|
343
1464
|
manifestPath,
|
|
344
1465
|
JSON.stringify(manifest, null, 2),
|
|
345
1466
|
"utf8"
|
|
346
1467
|
);
|
|
347
|
-
console.log(
|
|
348
|
-
console.log(
|
|
1468
|
+
console.log(chalk3.green("\u2713 Build manifest created"));
|
|
1469
|
+
console.log(chalk3.green.bold(`
|
|
349
1470
|
\u2713 Build complete!`));
|
|
350
1471
|
if (builtWidgets.length > 0) {
|
|
351
|
-
console.log(
|
|
1472
|
+
console.log(chalk3.gray(` ${builtWidgets.length} widget(s) built`));
|
|
352
1473
|
}
|
|
353
1474
|
if (options.withInspector) {
|
|
354
|
-
console.log(
|
|
1475
|
+
console.log(chalk3.gray(" Inspector included"));
|
|
355
1476
|
}
|
|
356
1477
|
} catch (error) {
|
|
357
|
-
console.error(
|
|
1478
|
+
console.error(chalk3.red("Build failed:"), error);
|
|
358
1479
|
process.exit(1);
|
|
359
1480
|
}
|
|
360
1481
|
});
|
|
361
1482
|
program.command("dev").description("Run development server with auto-reload and inspector").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option("--host <host>", "Server host", "localhost").option("--no-open", "Do not auto-open inspector").action(async (options) => {
|
|
362
1483
|
try {
|
|
363
|
-
const projectPath =
|
|
1484
|
+
const projectPath = path3.resolve(options.path);
|
|
364
1485
|
let port = parseInt(options.port, 10);
|
|
365
1486
|
const host = options.host;
|
|
366
|
-
console.log(
|
|
1487
|
+
console.log(chalk3.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
367
1488
|
if (!await isPortAvailable(port, host)) {
|
|
368
|
-
console.log(
|
|
369
|
-
const availablePort = await
|
|
370
|
-
console.log(
|
|
1489
|
+
console.log(chalk3.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
|
|
1490
|
+
const availablePort = await findAvailablePort2(port, host);
|
|
1491
|
+
console.log(chalk3.green.bold(`\u2713 Using port ${availablePort} instead`));
|
|
371
1492
|
port = availablePort;
|
|
372
1493
|
}
|
|
373
1494
|
let mcpUrl;
|
|
@@ -399,20 +1520,20 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
399
1520
|
inspectorUrl += `&tunnelUrl=${encodeURIComponent(mcpUrl)}`;
|
|
400
1521
|
}
|
|
401
1522
|
const readyTime = Date.now() - startTime;
|
|
402
|
-
console.log(
|
|
403
|
-
console.log(
|
|
404
|
-
console.log(
|
|
1523
|
+
console.log(chalk3.green.bold(`\u2713 Ready in ${readyTime}ms`));
|
|
1524
|
+
console.log(chalk3.whiteBright(`Local: http://${host}:${port}`));
|
|
1525
|
+
console.log(chalk3.whiteBright(`Network: http://${host}:${port}`));
|
|
405
1526
|
if (mcpUrl) {
|
|
406
|
-
console.log(
|
|
1527
|
+
console.log(chalk3.whiteBright(`Tunnel: ${mcpUrl}`));
|
|
407
1528
|
}
|
|
408
|
-
console.log(
|
|
409
|
-
console.log(
|
|
1529
|
+
console.log(chalk3.whiteBright(`MCP: ${mcpEndpoint}`));
|
|
1530
|
+
console.log(chalk3.whiteBright(`Inspector: ${inspectorUrl}
|
|
410
1531
|
`));
|
|
411
|
-
await
|
|
1532
|
+
await open3(inspectorUrl);
|
|
412
1533
|
}
|
|
413
1534
|
}
|
|
414
1535
|
const cleanup = () => {
|
|
415
|
-
console.log(
|
|
1536
|
+
console.log(chalk3.gray("\n\nShutting down..."));
|
|
416
1537
|
const processesToKill = processes.length;
|
|
417
1538
|
let killedCount = 0;
|
|
418
1539
|
const checkAndExit = () => {
|
|
@@ -443,13 +1564,13 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
443
1564
|
await new Promise(() => {
|
|
444
1565
|
});
|
|
445
1566
|
} catch (error) {
|
|
446
|
-
console.error(
|
|
1567
|
+
console.error(chalk3.red("Dev mode failed:"), error);
|
|
447
1568
|
process.exit(1);
|
|
448
1569
|
}
|
|
449
1570
|
});
|
|
450
1571
|
program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
|
|
451
1572
|
try {
|
|
452
|
-
const projectPath =
|
|
1573
|
+
const projectPath = path3.resolve(options.path);
|
|
453
1574
|
const port = parseInt(options.port, 10);
|
|
454
1575
|
console.log(
|
|
455
1576
|
`\x1B[36m\x1B[1mmcp-use\x1B[0m \x1B[90mVersion: ${packageJson.version}\x1B[0m
|
|
@@ -463,13 +1584,13 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
463
1584
|
mcpUrl = tunnelInfo.subdomain;
|
|
464
1585
|
tunnelProcess = tunnelInfo.process;
|
|
465
1586
|
} catch (error) {
|
|
466
|
-
console.error(
|
|
1587
|
+
console.error(chalk3.red("Failed to start tunnel:"), error);
|
|
467
1588
|
process.exit(1);
|
|
468
1589
|
}
|
|
469
1590
|
}
|
|
470
1591
|
let serverFile = "dist/index.js";
|
|
471
1592
|
try {
|
|
472
|
-
await access(
|
|
1593
|
+
await access(path3.join(projectPath, serverFile));
|
|
473
1594
|
} catch {
|
|
474
1595
|
serverFile = "dist/server.js";
|
|
475
1596
|
}
|
|
@@ -481,7 +1602,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
481
1602
|
};
|
|
482
1603
|
if (mcpUrl) {
|
|
483
1604
|
env.MCP_URL = mcpUrl;
|
|
484
|
-
console.log(
|
|
1605
|
+
console.log(chalk3.whiteBright(`Tunnel: ${mcpUrl}`));
|
|
485
1606
|
}
|
|
486
1607
|
const serverProc = spawn("node", [serverFile], {
|
|
487
1608
|
cwd: projectPath,
|
|
@@ -526,4 +1647,25 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
526
1647
|
process.exit(1);
|
|
527
1648
|
}
|
|
528
1649
|
});
|
|
1650
|
+
program.command("login").description("Login to mcp-use cloud").action(async () => {
|
|
1651
|
+
await loginCommand();
|
|
1652
|
+
});
|
|
1653
|
+
program.command("logout").description("Logout from mcp-use cloud").action(async () => {
|
|
1654
|
+
await logoutCommand();
|
|
1655
|
+
});
|
|
1656
|
+
program.command("whoami").description("Show current user information").action(async () => {
|
|
1657
|
+
await whoamiCommand();
|
|
1658
|
+
});
|
|
1659
|
+
program.command("deploy").description("Deploy MCP server to mcp-use cloud").option("--open", "Open deployment in browser after successful deploy").option("--name <name>", "Custom deployment name").option("--port <port>", "Server port", "3000").option("--runtime <runtime>", "Runtime (node or python)").option(
|
|
1660
|
+
"--from-source",
|
|
1661
|
+
"Deploy from local source code (even for GitHub repos)"
|
|
1662
|
+
).action(async (options) => {
|
|
1663
|
+
await deployCommand({
|
|
1664
|
+
open: options.open,
|
|
1665
|
+
name: options.name,
|
|
1666
|
+
port: options.port ? parseInt(options.port, 10) : void 0,
|
|
1667
|
+
runtime: options.runtime,
|
|
1668
|
+
fromSource: options.fromSource
|
|
1669
|
+
});
|
|
1670
|
+
});
|
|
529
1671
|
program.parse();
|