@mcp-use/cli 2.1.18 → 2.1.19-canary.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +232 -238
- package/dist/index.mjs +231 -237
- package/package.json +11 -3
- package/dist/build.d.ts +0 -2
- package/dist/build.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -24,205 +24,39 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
+
var import_config = require("dotenv/config");
|
|
27
28
|
var import_commander = require("commander");
|
|
28
|
-
|
|
29
|
-
// src/build.ts
|
|
30
|
-
var import_node_fs = require("fs");
|
|
31
|
-
var import_node_path = __toESM(require("path"));
|
|
32
|
-
var import_esbuild = require("esbuild");
|
|
33
|
-
var import_globby = require("globby");
|
|
34
|
-
var ROUTE_PREFIX = "/mcp-use/widgets";
|
|
35
|
-
var SRC_DIR = "resources";
|
|
36
|
-
var OUT_DIR = "dist/resources";
|
|
37
|
-
function toRoute(file) {
|
|
38
|
-
const rel = file.replace(new RegExp(`^${SRC_DIR}/`), "").replace(/\.tsx?$/, "");
|
|
39
|
-
return `${ROUTE_PREFIX}/${rel}`;
|
|
40
|
-
}
|
|
41
|
-
function outDirForRoute(route) {
|
|
42
|
-
return import_node_path.default.join(OUT_DIR, route.replace(/^\//, ""));
|
|
43
|
-
}
|
|
44
|
-
function htmlTemplate({ title, scriptPath }) {
|
|
45
|
-
return `<!doctype html>
|
|
46
|
-
<html lang="en">
|
|
47
|
-
<head>
|
|
48
|
-
<meta charset="UTF-8" />
|
|
49
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
50
|
-
<title>${title} Widget</title>
|
|
51
|
-
<style>
|
|
52
|
-
body {
|
|
53
|
-
margin: 0;
|
|
54
|
-
padding: 20px;
|
|
55
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
56
|
-
background: #f5f5f5;
|
|
57
|
-
}
|
|
58
|
-
#widget-root {
|
|
59
|
-
max-width: 1200px;
|
|
60
|
-
margin: 0 auto;
|
|
61
|
-
}
|
|
62
|
-
</style>
|
|
63
|
-
</head>
|
|
64
|
-
<body>
|
|
65
|
-
<div id="widget-root"></div>
|
|
66
|
-
<script type="module" src="${scriptPath}"></script>
|
|
67
|
-
</body>
|
|
68
|
-
</html>`;
|
|
69
|
-
}
|
|
70
|
-
async function buildWidget(entry, projectPath, minify = true) {
|
|
71
|
-
const relativePath = import_node_path.default.relative(projectPath, entry);
|
|
72
|
-
const route = toRoute(relativePath);
|
|
73
|
-
const pageOutDir = import_node_path.default.join(projectPath, outDirForRoute(route));
|
|
74
|
-
const baseName = import_node_path.default.parse(entry).name;
|
|
75
|
-
await (0, import_esbuild.build)({
|
|
76
|
-
entryPoints: [entry],
|
|
77
|
-
bundle: true,
|
|
78
|
-
splitting: true,
|
|
79
|
-
format: "esm",
|
|
80
|
-
platform: "browser",
|
|
81
|
-
target: "es2018",
|
|
82
|
-
sourcemap: !minify,
|
|
83
|
-
minify,
|
|
84
|
-
outdir: import_node_path.default.join(pageOutDir, "assets"),
|
|
85
|
-
logLevel: "silent",
|
|
86
|
-
loader: {
|
|
87
|
-
".svg": "file",
|
|
88
|
-
".png": "file",
|
|
89
|
-
".jpg": "file",
|
|
90
|
-
".jpeg": "file",
|
|
91
|
-
".gif": "file",
|
|
92
|
-
".css": "css"
|
|
93
|
-
},
|
|
94
|
-
entryNames: `[name]-[hash]`,
|
|
95
|
-
chunkNames: `chunk-[hash]`,
|
|
96
|
-
assetNames: `asset-[hash]`,
|
|
97
|
-
define: {
|
|
98
|
-
"process.env.NODE_ENV": minify ? '"production"' : '"development"'
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
const files = await import_node_fs.promises.readdir(import_node_path.default.join(pageOutDir, "assets"));
|
|
102
|
-
const mainJs = files.find((f) => f.startsWith(`${baseName}-`) && f.endsWith(".js"));
|
|
103
|
-
if (!mainJs)
|
|
104
|
-
throw new Error(`Failed to locate entry JS for ${entry}`);
|
|
105
|
-
await import_node_fs.promises.mkdir(pageOutDir, { recursive: true });
|
|
106
|
-
await import_node_fs.promises.writeFile(
|
|
107
|
-
import_node_path.default.join(pageOutDir, "index.html"),
|
|
108
|
-
htmlTemplate({
|
|
109
|
-
title: baseName,
|
|
110
|
-
scriptPath: `./assets/${mainJs}`
|
|
111
|
-
}),
|
|
112
|
-
"utf8"
|
|
113
|
-
);
|
|
114
|
-
return { baseName, route };
|
|
115
|
-
}
|
|
116
|
-
async function buildWidgets(projectPath, watch = false) {
|
|
117
|
-
const srcDir = import_node_path.default.join(projectPath, SRC_DIR);
|
|
118
|
-
const outDir = import_node_path.default.join(projectPath, OUT_DIR);
|
|
119
|
-
await import_node_fs.promises.rm(outDir, { recursive: true, force: true });
|
|
120
|
-
const entries = await (0, import_globby.globby)([`${srcDir}/**/*.tsx`]);
|
|
121
|
-
if (!watch) {
|
|
122
|
-
console.log(`Building ${entries.length} widget files...`);
|
|
123
|
-
}
|
|
124
|
-
if (watch) {
|
|
125
|
-
const contexts = [];
|
|
126
|
-
for (const entry of entries) {
|
|
127
|
-
const relativePath = import_node_path.default.relative(projectPath, entry);
|
|
128
|
-
const route = toRoute(relativePath);
|
|
129
|
-
const pageOutDir = import_node_path.default.join(projectPath, outDirForRoute(route));
|
|
130
|
-
const baseName = import_node_path.default.parse(entry).name;
|
|
131
|
-
const ctx = await (0, import_esbuild.context)({
|
|
132
|
-
entryPoints: [entry],
|
|
133
|
-
bundle: true,
|
|
134
|
-
splitting: true,
|
|
135
|
-
format: "esm",
|
|
136
|
-
platform: "browser",
|
|
137
|
-
target: "es2018",
|
|
138
|
-
sourcemap: true,
|
|
139
|
-
minify: false,
|
|
140
|
-
outdir: import_node_path.default.join(pageOutDir, "assets"),
|
|
141
|
-
logLevel: "silent",
|
|
142
|
-
loader: {
|
|
143
|
-
".svg": "file",
|
|
144
|
-
".png": "file",
|
|
145
|
-
".jpg": "file",
|
|
146
|
-
".jpeg": "file",
|
|
147
|
-
".gif": "file",
|
|
148
|
-
".css": "css"
|
|
149
|
-
},
|
|
150
|
-
entryNames: `[name]-[hash]`,
|
|
151
|
-
chunkNames: `chunk-[hash]`,
|
|
152
|
-
assetNames: `asset-[hash]`,
|
|
153
|
-
define: {
|
|
154
|
-
"process.env.NODE_ENV": '"development"'
|
|
155
|
-
},
|
|
156
|
-
plugins: [{
|
|
157
|
-
name: "html-writer",
|
|
158
|
-
setup(buildPlugin) {
|
|
159
|
-
buildPlugin.onEnd(async () => {
|
|
160
|
-
try {
|
|
161
|
-
const files = await import_node_fs.promises.readdir(import_node_path.default.join(pageOutDir, "assets"));
|
|
162
|
-
const mainJs = files.find((f) => f.startsWith(`${baseName}-`) && f.endsWith(".js"));
|
|
163
|
-
if (mainJs) {
|
|
164
|
-
await import_node_fs.promises.mkdir(pageOutDir, { recursive: true });
|
|
165
|
-
await import_node_fs.promises.writeFile(
|
|
166
|
-
import_node_path.default.join(pageOutDir, "index.html"),
|
|
167
|
-
htmlTemplate({
|
|
168
|
-
title: baseName,
|
|
169
|
-
scriptPath: `./assets/${mainJs}`
|
|
170
|
-
}),
|
|
171
|
-
"utf8"
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
} catch (err) {
|
|
175
|
-
console.error(`Error writing HTML for ${baseName}:`, err);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}]
|
|
180
|
-
});
|
|
181
|
-
contexts.push(ctx);
|
|
182
|
-
}
|
|
183
|
-
for (const ctx of contexts) {
|
|
184
|
-
await ctx.watch();
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
for (const entry of entries) {
|
|
188
|
-
const { baseName, route } = await buildWidget(entry, projectPath);
|
|
189
|
-
console.log(`\x1B[32m\u2713\x1B[0m Built ${baseName} -> ${route}`);
|
|
190
|
-
}
|
|
191
|
-
console.log("Build complete!");
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// src/index.ts
|
|
196
29
|
var import_node_child_process = require("child_process");
|
|
197
|
-
var
|
|
30
|
+
var import_node_fs = require("fs");
|
|
198
31
|
var import_promises = require("fs/promises");
|
|
199
|
-
var
|
|
32
|
+
var import_node_path = __toESM(require("path"));
|
|
200
33
|
var import_open = __toESM(require("open"));
|
|
34
|
+
var import_chalk = __toESM(require("chalk"));
|
|
201
35
|
var program = new import_commander.Command();
|
|
202
|
-
var packageContent = (0,
|
|
36
|
+
var packageContent = (0, import_node_fs.readFileSync)(import_node_path.default.join(__dirname, "../package.json"), "utf-8");
|
|
203
37
|
var packageJson = JSON.parse(packageContent);
|
|
204
38
|
var packageVersion = packageJson.version || "unknown";
|
|
205
|
-
program.name("mcp-use").description("MCP
|
|
206
|
-
async function isPortAvailable(port) {
|
|
39
|
+
program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion);
|
|
40
|
+
async function isPortAvailable(port, host = "localhost") {
|
|
207
41
|
try {
|
|
208
|
-
await fetch(`http
|
|
42
|
+
await fetch(`http://${host}:${port}`);
|
|
209
43
|
return false;
|
|
210
44
|
} catch {
|
|
211
45
|
return true;
|
|
212
46
|
}
|
|
213
47
|
}
|
|
214
|
-
async function findAvailablePort(startPort) {
|
|
48
|
+
async function findAvailablePort(startPort, host = "localhost") {
|
|
215
49
|
for (let port = startPort; port < startPort + 100; port++) {
|
|
216
|
-
if (await isPortAvailable(port)) {
|
|
50
|
+
if (await isPortAvailable(port, host)) {
|
|
217
51
|
return port;
|
|
218
52
|
}
|
|
219
53
|
}
|
|
220
54
|
throw new Error("No available ports found");
|
|
221
55
|
}
|
|
222
|
-
async function waitForServer(port, maxAttempts = 30) {
|
|
56
|
+
async function waitForServer(port, host = "localhost", maxAttempts = 30) {
|
|
223
57
|
for (let i = 0; i < maxAttempts; i++) {
|
|
224
58
|
try {
|
|
225
|
-
const response = await fetch(`http
|
|
59
|
+
const response = await fetch(`http://${host}:${port}/inspector`);
|
|
226
60
|
if (response.ok) {
|
|
227
61
|
return true;
|
|
228
62
|
}
|
|
@@ -232,12 +66,13 @@ async function waitForServer(port, maxAttempts = 30) {
|
|
|
232
66
|
}
|
|
233
67
|
return false;
|
|
234
68
|
}
|
|
235
|
-
function runCommand(command, args, cwd) {
|
|
69
|
+
function runCommand(command, args, cwd, env) {
|
|
236
70
|
return new Promise((resolve, reject) => {
|
|
237
71
|
const proc = (0, import_node_child_process.spawn)(command, args, {
|
|
238
72
|
cwd,
|
|
239
73
|
stdio: "inherit",
|
|
240
|
-
shell: false
|
|
74
|
+
shell: false,
|
|
75
|
+
env: env ? { ...process.env, ...env } : process.env
|
|
241
76
|
});
|
|
242
77
|
proc.on("error", reject);
|
|
243
78
|
proc.on("exit", (code) => {
|
|
@@ -249,81 +84,240 @@ function runCommand(command, args, cwd) {
|
|
|
249
84
|
});
|
|
250
85
|
});
|
|
251
86
|
}
|
|
252
|
-
|
|
87
|
+
async function findServerFile(projectPath) {
|
|
88
|
+
const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
try {
|
|
91
|
+
await (0, import_promises.access)(import_node_path.default.join(projectPath, candidate));
|
|
92
|
+
return candidate;
|
|
93
|
+
} catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
throw new Error("No server file found");
|
|
98
|
+
}
|
|
99
|
+
async function buildWidgets(projectPath) {
|
|
100
|
+
const { promises: fs } = await import("fs");
|
|
101
|
+
const { build } = await import("vite");
|
|
102
|
+
const resourcesDir = import_node_path.default.join(projectPath, "resources");
|
|
103
|
+
const mcpUrl = process.env.MCP_URL;
|
|
104
|
+
if (!mcpUrl) {
|
|
105
|
+
console.log(import_chalk.default.yellow("\u26A0\uFE0F MCP_URL not set - using relative paths (widgets may not work correctly)"));
|
|
106
|
+
console.log(import_chalk.default.gray(" Set MCP_URL environment variable for production builds (e.g., https://myserver.com)"));
|
|
107
|
+
}
|
|
253
108
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
109
|
+
await (0, import_promises.access)(resourcesDir);
|
|
110
|
+
} catch {
|
|
111
|
+
console.log(import_chalk.default.gray("No resources/ directory found - skipping widget build"));
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
let entries = [];
|
|
115
|
+
try {
|
|
116
|
+
const files = await fs.readdir(resourcesDir);
|
|
117
|
+
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => import_node_path.default.join(resourcesDir, f));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.log(import_chalk.default.gray("No widgets found in resources/ directory"));
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
if (entries.length === 0) {
|
|
123
|
+
console.log(import_chalk.default.gray("No widgets found in resources/ directory"));
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
console.log(import_chalk.default.gray(`Building ${entries.length} widget(s)...`));
|
|
127
|
+
const react = (await import("@vitejs/plugin-react")).default;
|
|
128
|
+
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
129
|
+
const builtWidgets = [];
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
const baseName = import_node_path.default.basename(entry).replace(/\.tsx?$/, "");
|
|
132
|
+
const widgetName = baseName;
|
|
133
|
+
console.log(import_chalk.default.gray(` - Building ${widgetName}...`));
|
|
134
|
+
const tempDir = import_node_path.default.join(projectPath, ".mcp-use", widgetName);
|
|
135
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
136
|
+
const relativeResourcesPath = import_node_path.default.relative(tempDir, resourcesDir).replace(/\\/g, "/");
|
|
137
|
+
const cssContent = `@import "tailwindcss";
|
|
138
|
+
|
|
139
|
+
/* Configure Tailwind to scan the resources directory */
|
|
140
|
+
@source "${relativeResourcesPath}";
|
|
141
|
+
`;
|
|
142
|
+
await fs.writeFile(import_node_path.default.join(tempDir, "styles.css"), cssContent, "utf8");
|
|
143
|
+
const entryContent = `import React from 'react'
|
|
144
|
+
import { createRoot } from 'react-dom/client'
|
|
145
|
+
import './styles.css'
|
|
146
|
+
import Component from '${entry}'
|
|
147
|
+
|
|
148
|
+
const container = document.getElementById('widget-root')
|
|
149
|
+
if (container && Component) {
|
|
150
|
+
const root = createRoot(container)
|
|
151
|
+
root.render(<Component />)
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
const htmlContent = `<!doctype html>
|
|
155
|
+
<html lang="en">
|
|
156
|
+
<head>
|
|
157
|
+
<meta charset="UTF-8" />
|
|
158
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
159
|
+
<title>${widgetName} Widget</title>
|
|
160
|
+
</head>
|
|
161
|
+
<body>
|
|
162
|
+
<div id="widget-root"></div>
|
|
163
|
+
<script type="module" src="/entry.tsx"></script>
|
|
164
|
+
</body>
|
|
165
|
+
</html>`;
|
|
166
|
+
await fs.writeFile(import_node_path.default.join(tempDir, "entry.tsx"), entryContent, "utf8");
|
|
167
|
+
await fs.writeFile(import_node_path.default.join(tempDir, "index.html"), htmlContent, "utf8");
|
|
168
|
+
const outDir = import_node_path.default.join(projectPath, "dist", "resources", "widgets", widgetName);
|
|
169
|
+
const baseUrl = mcpUrl ? `${mcpUrl}/mcp-use/widgets/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
|
|
170
|
+
let widgetMetadata = {};
|
|
171
|
+
try {
|
|
172
|
+
const metadataTempDir = import_node_path.default.join(projectPath, ".mcp-use", `${widgetName}-metadata`);
|
|
173
|
+
await fs.mkdir(metadataTempDir, { recursive: true });
|
|
174
|
+
const { createServer } = await import("vite");
|
|
175
|
+
const metadataServer = await createServer({
|
|
176
|
+
root: metadataTempDir,
|
|
177
|
+
cacheDir: import_node_path.default.join(metadataTempDir, ".vite-cache"),
|
|
178
|
+
plugins: [tailwindcss(), react()],
|
|
179
|
+
resolve: {
|
|
180
|
+
alias: {
|
|
181
|
+
"@": resourcesDir
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
server: {
|
|
185
|
+
middlewareMode: true
|
|
186
|
+
},
|
|
187
|
+
clearScreen: false,
|
|
188
|
+
logLevel: "silent",
|
|
189
|
+
customLogger: {
|
|
190
|
+
info: () => {
|
|
191
|
+
},
|
|
192
|
+
warn: () => {
|
|
193
|
+
},
|
|
194
|
+
error: () => {
|
|
195
|
+
},
|
|
196
|
+
clearScreen: () => {
|
|
197
|
+
},
|
|
198
|
+
hasErrorLogged: () => false,
|
|
199
|
+
hasWarned: false,
|
|
200
|
+
warnOnce: () => {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
try {
|
|
205
|
+
const mod = await metadataServer.ssrLoadModule(entry);
|
|
206
|
+
if (mod.widgetMetadata) {
|
|
207
|
+
widgetMetadata = {
|
|
208
|
+
description: mod.widgetMetadata.description,
|
|
209
|
+
inputs: mod.widgetMetadata.inputs?.shape || {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.warn(import_chalk.default.yellow(` \u26A0 Could not extract metadata for ${widgetName}`));
|
|
215
|
+
} finally {
|
|
216
|
+
await metadataServer.close();
|
|
217
|
+
try {
|
|
218
|
+
await fs.rm(metadataTempDir, { recursive: true, force: true });
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
await build({
|
|
226
|
+
root: tempDir,
|
|
227
|
+
base: baseUrl,
|
|
228
|
+
plugins: [tailwindcss(), react()],
|
|
229
|
+
resolve: {
|
|
230
|
+
alias: {
|
|
231
|
+
"@": resourcesDir
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
build: {
|
|
235
|
+
outDir,
|
|
236
|
+
emptyOutDir: true,
|
|
237
|
+
rollupOptions: {
|
|
238
|
+
input: import_node_path.default.join(tempDir, "index.html")
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
const metadataPath = import_node_path.default.join(outDir, "metadata.json");
|
|
243
|
+
await fs.writeFile(metadataPath, JSON.stringify(widgetMetadata, null, 2), "utf8");
|
|
244
|
+
builtWidgets.push(widgetName);
|
|
245
|
+
console.log(import_chalk.default.green(` \u2713 Built ${widgetName}`));
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(import_chalk.default.red(` \u2717 Failed to build ${widgetName}:`), error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return builtWidgets;
|
|
251
|
+
}
|
|
252
|
+
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", false).action(async (options) => {
|
|
253
|
+
try {
|
|
254
|
+
const projectPath = import_node_path.default.resolve(options.path);
|
|
255
|
+
const { promises: fs } = await import("fs");
|
|
256
|
+
console.log(import_chalk.default.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
257
|
+
const builtWidgets = await buildWidgets(projectPath);
|
|
258
|
+
console.log(import_chalk.default.gray("Building TypeScript..."));
|
|
258
259
|
await runCommand("npx", ["tsc"], projectPath);
|
|
259
|
-
console.log("\
|
|
260
|
-
|
|
260
|
+
console.log(import_chalk.default.green("\u2713 TypeScript build complete!"));
|
|
261
|
+
const manifestPath = import_node_path.default.join(projectPath, "dist", ".mcp-use-manifest.json");
|
|
262
|
+
const manifest = {
|
|
263
|
+
includeInspector: options.withInspector || false,
|
|
264
|
+
buildTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
265
|
+
widgets: builtWidgets
|
|
266
|
+
};
|
|
267
|
+
await fs.mkdir(import_node_path.default.dirname(manifestPath), { recursive: true });
|
|
268
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
269
|
+
console.log(import_chalk.default.green("\u2713 Build manifest created"));
|
|
270
|
+
console.log(import_chalk.default.green.bold(`
|
|
271
|
+
\u2713 Build complete!`));
|
|
272
|
+
if (builtWidgets.length > 0) {
|
|
273
|
+
console.log(import_chalk.default.gray(` ${builtWidgets.length} widget(s) built`));
|
|
274
|
+
}
|
|
275
|
+
if (options.withInspector) {
|
|
276
|
+
console.log(import_chalk.default.gray(" Inspector included"));
|
|
277
|
+
}
|
|
261
278
|
} catch (error) {
|
|
262
|
-
console.error("Build failed:", error);
|
|
279
|
+
console.error(import_chalk.default.red("Build failed:"), error);
|
|
263
280
|
process.exit(1);
|
|
264
281
|
}
|
|
265
282
|
});
|
|
266
|
-
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("--no-open", "Do not auto-open inspector").action(async (options) => {
|
|
283
|
+
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) => {
|
|
267
284
|
try {
|
|
268
|
-
const projectPath =
|
|
285
|
+
const projectPath = import_node_path.default.resolve(options.path);
|
|
269
286
|
let port = parseInt(options.port, 10);
|
|
270
|
-
|
|
271
|
-
`);
|
|
272
|
-
if (!await isPortAvailable(port)) {
|
|
273
|
-
console.log(`\
|
|
274
|
-
const availablePort = await findAvailablePort(port);
|
|
275
|
-
console.log(`\
|
|
287
|
+
const host = options.host;
|
|
288
|
+
console.log(import_chalk.default.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
289
|
+
if (!await isPortAvailable(port, host)) {
|
|
290
|
+
console.log(import_chalk.default.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
|
|
291
|
+
const availablePort = await findAvailablePort(port, host);
|
|
292
|
+
console.log(import_chalk.default.green.bold(`\u2713 Using port ${availablePort} instead`));
|
|
276
293
|
port = availablePort;
|
|
277
294
|
}
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
await (0, import_promises.access)(import_node_path2.default.join(projectPath, serverFile));
|
|
281
|
-
} catch {
|
|
282
|
-
serverFile = "src/server.ts";
|
|
283
|
-
}
|
|
295
|
+
const serverFile = await findServerFile(projectPath);
|
|
284
296
|
const processes = [];
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
tscProc.stdout?.on("data", (data) => {
|
|
291
|
-
const output = data.toString();
|
|
292
|
-
if (output.includes("Watching for file changes")) {
|
|
293
|
-
console.log("\x1B[32m\u2713\x1B[0m TypeScript compiler watching...");
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
processes.push(tscProc);
|
|
297
|
-
buildWidgets(projectPath, true).catch((error) => {
|
|
298
|
-
console.error("Widget builder failed:", error);
|
|
299
|
-
});
|
|
300
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
301
|
-
const serverProc = (0, import_node_child_process.spawn)("npx", ["tsx", "watch", serverFile], {
|
|
302
|
-
cwd: projectPath,
|
|
303
|
-
stdio: "inherit",
|
|
304
|
-
shell: false,
|
|
305
|
-
env: { ...process.env, PORT: String(port) }
|
|
297
|
+
const serverProc = runCommand("npx", ["tsx", "watch", serverFile], projectPath, {
|
|
298
|
+
PORT: String(port),
|
|
299
|
+
HOST: host,
|
|
300
|
+
NODE_ENV: "development"
|
|
306
301
|
});
|
|
307
302
|
processes.push(serverProc);
|
|
308
303
|
if (options.open !== false) {
|
|
309
304
|
const startTime = Date.now();
|
|
310
|
-
const ready = await waitForServer(port);
|
|
305
|
+
const ready = await waitForServer(port, host);
|
|
311
306
|
if (ready) {
|
|
312
|
-
const mcpUrl = `http
|
|
313
|
-
const inspectorUrl = `http
|
|
307
|
+
const mcpUrl = `http://${host}:${port}/mcp`;
|
|
308
|
+
const inspectorUrl = `http://${host}:${port}/inspector?autoConnect=${encodeURIComponent(mcpUrl)}`;
|
|
314
309
|
const readyTime = Date.now() - startTime;
|
|
315
|
-
console.log(`
|
|
316
|
-
|
|
317
|
-
console.log(`
|
|
318
|
-
console.log(`
|
|
319
|
-
console.log(`
|
|
320
|
-
|
|
321
|
-
`);
|
|
310
|
+
console.log(import_chalk.default.green.bold(`\u2713 Ready in ${readyTime}ms`));
|
|
311
|
+
console.log(import_chalk.default.whiteBright(`Local: http://${host}:${port}`));
|
|
312
|
+
console.log(import_chalk.default.whiteBright(`Network: http://${host}:${port}`));
|
|
313
|
+
console.log(import_chalk.default.whiteBright(`MCP: ${mcpUrl}`));
|
|
314
|
+
console.log(import_chalk.default.whiteBright(`Inspector: ${inspectorUrl}
|
|
315
|
+
`));
|
|
322
316
|
await (0, import_open.default)(inspectorUrl);
|
|
323
317
|
}
|
|
324
318
|
}
|
|
325
319
|
const cleanup = () => {
|
|
326
|
-
console.log("\n\nShutting down...");
|
|
320
|
+
console.log(import_chalk.default.gray("\n\nShutting down..."));
|
|
327
321
|
processes.forEach((proc) => proc.kill());
|
|
328
322
|
process.exit(0);
|
|
329
323
|
};
|
|
@@ -332,19 +326,19 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
332
326
|
await new Promise(() => {
|
|
333
327
|
});
|
|
334
328
|
} catch (error) {
|
|
335
|
-
console.error("Dev mode failed:", error);
|
|
329
|
+
console.error(import_chalk.default.red("Dev mode failed:"), error);
|
|
336
330
|
process.exit(1);
|
|
337
331
|
}
|
|
338
332
|
});
|
|
339
333
|
program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").action(async (options) => {
|
|
340
334
|
try {
|
|
341
|
-
const projectPath =
|
|
335
|
+
const projectPath = import_node_path.default.resolve(options.path);
|
|
342
336
|
const port = parseInt(options.port, 10);
|
|
343
337
|
console.log(`\x1B[36m\x1B[1mmcp-use\x1B[0m \x1B[90mVersion: ${packageJson.version}\x1B[0m
|
|
344
338
|
`);
|
|
345
339
|
let serverFile = "dist/index.js";
|
|
346
340
|
try {
|
|
347
|
-
await (0, import_promises.access)(
|
|
341
|
+
await (0, import_promises.access)(import_node_path.default.join(projectPath, serverFile));
|
|
348
342
|
} catch {
|
|
349
343
|
serverFile = "dist/server.js";
|
|
350
344
|
}
|
|
@@ -352,7 +346,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
352
346
|
const serverProc = (0, import_node_child_process.spawn)("node", [serverFile], {
|
|
353
347
|
cwd: projectPath,
|
|
354
348
|
stdio: "inherit",
|
|
355
|
-
env: { ...process.env, PORT: String(port) }
|
|
349
|
+
env: { ...process.env, PORT: String(port), NODE_ENV: "production" }
|
|
356
350
|
});
|
|
357
351
|
const cleanup = () => {
|
|
358
352
|
console.log("\n\nShutting down...");
|
package/dist/index.mjs
CHANGED
|
@@ -1,205 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import "dotenv/config";
|
|
4
5
|
import { Command } from "commander";
|
|
5
|
-
|
|
6
|
-
// src/build.ts
|
|
7
|
-
import { promises as fs } from "fs";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import { build, context } from "esbuild";
|
|
10
|
-
import { globby } from "globby";
|
|
11
|
-
var ROUTE_PREFIX = "/mcp-use/widgets";
|
|
12
|
-
var SRC_DIR = "resources";
|
|
13
|
-
var OUT_DIR = "dist/resources";
|
|
14
|
-
function toRoute(file) {
|
|
15
|
-
const rel = file.replace(new RegExp(`^${SRC_DIR}/`), "").replace(/\.tsx?$/, "");
|
|
16
|
-
return `${ROUTE_PREFIX}/${rel}`;
|
|
17
|
-
}
|
|
18
|
-
function outDirForRoute(route) {
|
|
19
|
-
return path.join(OUT_DIR, route.replace(/^\//, ""));
|
|
20
|
-
}
|
|
21
|
-
function htmlTemplate({ title, scriptPath }) {
|
|
22
|
-
return `<!doctype html>
|
|
23
|
-
<html lang="en">
|
|
24
|
-
<head>
|
|
25
|
-
<meta charset="UTF-8" />
|
|
26
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
27
|
-
<title>${title} Widget</title>
|
|
28
|
-
<style>
|
|
29
|
-
body {
|
|
30
|
-
margin: 0;
|
|
31
|
-
padding: 20px;
|
|
32
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
33
|
-
background: #f5f5f5;
|
|
34
|
-
}
|
|
35
|
-
#widget-root {
|
|
36
|
-
max-width: 1200px;
|
|
37
|
-
margin: 0 auto;
|
|
38
|
-
}
|
|
39
|
-
</style>
|
|
40
|
-
</head>
|
|
41
|
-
<body>
|
|
42
|
-
<div id="widget-root"></div>
|
|
43
|
-
<script type="module" src="${scriptPath}"></script>
|
|
44
|
-
</body>
|
|
45
|
-
</html>`;
|
|
46
|
-
}
|
|
47
|
-
async function buildWidget(entry, projectPath, minify = true) {
|
|
48
|
-
const relativePath = path.relative(projectPath, entry);
|
|
49
|
-
const route = toRoute(relativePath);
|
|
50
|
-
const pageOutDir = path.join(projectPath, outDirForRoute(route));
|
|
51
|
-
const baseName = path.parse(entry).name;
|
|
52
|
-
await build({
|
|
53
|
-
entryPoints: [entry],
|
|
54
|
-
bundle: true,
|
|
55
|
-
splitting: true,
|
|
56
|
-
format: "esm",
|
|
57
|
-
platform: "browser",
|
|
58
|
-
target: "es2018",
|
|
59
|
-
sourcemap: !minify,
|
|
60
|
-
minify,
|
|
61
|
-
outdir: path.join(pageOutDir, "assets"),
|
|
62
|
-
logLevel: "silent",
|
|
63
|
-
loader: {
|
|
64
|
-
".svg": "file",
|
|
65
|
-
".png": "file",
|
|
66
|
-
".jpg": "file",
|
|
67
|
-
".jpeg": "file",
|
|
68
|
-
".gif": "file",
|
|
69
|
-
".css": "css"
|
|
70
|
-
},
|
|
71
|
-
entryNames: `[name]-[hash]`,
|
|
72
|
-
chunkNames: `chunk-[hash]`,
|
|
73
|
-
assetNames: `asset-[hash]`,
|
|
74
|
-
define: {
|
|
75
|
-
"process.env.NODE_ENV": minify ? '"production"' : '"development"'
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
const files = await fs.readdir(path.join(pageOutDir, "assets"));
|
|
79
|
-
const mainJs = files.find((f) => f.startsWith(`${baseName}-`) && f.endsWith(".js"));
|
|
80
|
-
if (!mainJs)
|
|
81
|
-
throw new Error(`Failed to locate entry JS for ${entry}`);
|
|
82
|
-
await fs.mkdir(pageOutDir, { recursive: true });
|
|
83
|
-
await fs.writeFile(
|
|
84
|
-
path.join(pageOutDir, "index.html"),
|
|
85
|
-
htmlTemplate({
|
|
86
|
-
title: baseName,
|
|
87
|
-
scriptPath: `./assets/${mainJs}`
|
|
88
|
-
}),
|
|
89
|
-
"utf8"
|
|
90
|
-
);
|
|
91
|
-
return { baseName, route };
|
|
92
|
-
}
|
|
93
|
-
async function buildWidgets(projectPath, watch = false) {
|
|
94
|
-
const srcDir = path.join(projectPath, SRC_DIR);
|
|
95
|
-
const outDir = path.join(projectPath, OUT_DIR);
|
|
96
|
-
await fs.rm(outDir, { recursive: true, force: true });
|
|
97
|
-
const entries = await globby([`${srcDir}/**/*.tsx`]);
|
|
98
|
-
if (!watch) {
|
|
99
|
-
console.log(`Building ${entries.length} widget files...`);
|
|
100
|
-
}
|
|
101
|
-
if (watch) {
|
|
102
|
-
const contexts = [];
|
|
103
|
-
for (const entry of entries) {
|
|
104
|
-
const relativePath = path.relative(projectPath, entry);
|
|
105
|
-
const route = toRoute(relativePath);
|
|
106
|
-
const pageOutDir = path.join(projectPath, outDirForRoute(route));
|
|
107
|
-
const baseName = path.parse(entry).name;
|
|
108
|
-
const ctx = await context({
|
|
109
|
-
entryPoints: [entry],
|
|
110
|
-
bundle: true,
|
|
111
|
-
splitting: true,
|
|
112
|
-
format: "esm",
|
|
113
|
-
platform: "browser",
|
|
114
|
-
target: "es2018",
|
|
115
|
-
sourcemap: true,
|
|
116
|
-
minify: false,
|
|
117
|
-
outdir: path.join(pageOutDir, "assets"),
|
|
118
|
-
logLevel: "silent",
|
|
119
|
-
loader: {
|
|
120
|
-
".svg": "file",
|
|
121
|
-
".png": "file",
|
|
122
|
-
".jpg": "file",
|
|
123
|
-
".jpeg": "file",
|
|
124
|
-
".gif": "file",
|
|
125
|
-
".css": "css"
|
|
126
|
-
},
|
|
127
|
-
entryNames: `[name]-[hash]`,
|
|
128
|
-
chunkNames: `chunk-[hash]`,
|
|
129
|
-
assetNames: `asset-[hash]`,
|
|
130
|
-
define: {
|
|
131
|
-
"process.env.NODE_ENV": '"development"'
|
|
132
|
-
},
|
|
133
|
-
plugins: [{
|
|
134
|
-
name: "html-writer",
|
|
135
|
-
setup(buildPlugin) {
|
|
136
|
-
buildPlugin.onEnd(async () => {
|
|
137
|
-
try {
|
|
138
|
-
const files = await fs.readdir(path.join(pageOutDir, "assets"));
|
|
139
|
-
const mainJs = files.find((f) => f.startsWith(`${baseName}-`) && f.endsWith(".js"));
|
|
140
|
-
if (mainJs) {
|
|
141
|
-
await fs.mkdir(pageOutDir, { recursive: true });
|
|
142
|
-
await fs.writeFile(
|
|
143
|
-
path.join(pageOutDir, "index.html"),
|
|
144
|
-
htmlTemplate({
|
|
145
|
-
title: baseName,
|
|
146
|
-
scriptPath: `./assets/${mainJs}`
|
|
147
|
-
}),
|
|
148
|
-
"utf8"
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
} catch (err) {
|
|
152
|
-
console.error(`Error writing HTML for ${baseName}:`, err);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}]
|
|
157
|
-
});
|
|
158
|
-
contexts.push(ctx);
|
|
159
|
-
}
|
|
160
|
-
for (const ctx of contexts) {
|
|
161
|
-
await ctx.watch();
|
|
162
|
-
}
|
|
163
|
-
} else {
|
|
164
|
-
for (const entry of entries) {
|
|
165
|
-
const { baseName, route } = await buildWidget(entry, projectPath);
|
|
166
|
-
console.log(`\x1B[32m\u2713\x1B[0m Built ${baseName} -> ${route}`);
|
|
167
|
-
}
|
|
168
|
-
console.log("Build complete!");
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// src/index.ts
|
|
173
6
|
import { spawn } from "child_process";
|
|
174
7
|
import { readFileSync } from "fs";
|
|
175
8
|
import { access } from "fs/promises";
|
|
176
|
-
import
|
|
9
|
+
import path from "path";
|
|
177
10
|
import open from "open";
|
|
11
|
+
import chalk from "chalk";
|
|
178
12
|
var program = new Command();
|
|
179
|
-
var packageContent = readFileSync(
|
|
13
|
+
var packageContent = readFileSync(path.join(__dirname, "../package.json"), "utf-8");
|
|
180
14
|
var packageJson = JSON.parse(packageContent);
|
|
181
15
|
var packageVersion = packageJson.version || "unknown";
|
|
182
|
-
program.name("mcp-use").description("MCP
|
|
183
|
-
async function isPortAvailable(port) {
|
|
16
|
+
program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion);
|
|
17
|
+
async function isPortAvailable(port, host = "localhost") {
|
|
184
18
|
try {
|
|
185
|
-
await fetch(`http
|
|
19
|
+
await fetch(`http://${host}:${port}`);
|
|
186
20
|
return false;
|
|
187
21
|
} catch {
|
|
188
22
|
return true;
|
|
189
23
|
}
|
|
190
24
|
}
|
|
191
|
-
async function findAvailablePort(startPort) {
|
|
25
|
+
async function findAvailablePort(startPort, host = "localhost") {
|
|
192
26
|
for (let port = startPort; port < startPort + 100; port++) {
|
|
193
|
-
if (await isPortAvailable(port)) {
|
|
27
|
+
if (await isPortAvailable(port, host)) {
|
|
194
28
|
return port;
|
|
195
29
|
}
|
|
196
30
|
}
|
|
197
31
|
throw new Error("No available ports found");
|
|
198
32
|
}
|
|
199
|
-
async function waitForServer(port, maxAttempts = 30) {
|
|
33
|
+
async function waitForServer(port, host = "localhost", maxAttempts = 30) {
|
|
200
34
|
for (let i = 0; i < maxAttempts; i++) {
|
|
201
35
|
try {
|
|
202
|
-
const response = await fetch(`http
|
|
36
|
+
const response = await fetch(`http://${host}:${port}/inspector`);
|
|
203
37
|
if (response.ok) {
|
|
204
38
|
return true;
|
|
205
39
|
}
|
|
@@ -209,12 +43,13 @@ async function waitForServer(port, maxAttempts = 30) {
|
|
|
209
43
|
}
|
|
210
44
|
return false;
|
|
211
45
|
}
|
|
212
|
-
function runCommand(command, args, cwd) {
|
|
46
|
+
function runCommand(command, args, cwd, env) {
|
|
213
47
|
return new Promise((resolve, reject) => {
|
|
214
48
|
const proc = spawn(command, args, {
|
|
215
49
|
cwd,
|
|
216
50
|
stdio: "inherit",
|
|
217
|
-
shell: false
|
|
51
|
+
shell: false,
|
|
52
|
+
env: env ? { ...process.env, ...env } : process.env
|
|
218
53
|
});
|
|
219
54
|
proc.on("error", reject);
|
|
220
55
|
proc.on("exit", (code) => {
|
|
@@ -226,81 +61,240 @@ function runCommand(command, args, cwd) {
|
|
|
226
61
|
});
|
|
227
62
|
});
|
|
228
63
|
}
|
|
229
|
-
|
|
64
|
+
async function findServerFile(projectPath) {
|
|
65
|
+
const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
try {
|
|
68
|
+
await access(path.join(projectPath, candidate));
|
|
69
|
+
return candidate;
|
|
70
|
+
} catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error("No server file found");
|
|
75
|
+
}
|
|
76
|
+
async function buildWidgets(projectPath) {
|
|
77
|
+
const { promises: fs } = await import("fs");
|
|
78
|
+
const { build } = await import("vite");
|
|
79
|
+
const resourcesDir = path.join(projectPath, "resources");
|
|
80
|
+
const mcpUrl = process.env.MCP_URL;
|
|
81
|
+
if (!mcpUrl) {
|
|
82
|
+
console.log(chalk.yellow("\u26A0\uFE0F MCP_URL not set - using relative paths (widgets may not work correctly)"));
|
|
83
|
+
console.log(chalk.gray(" Set MCP_URL environment variable for production builds (e.g., https://myserver.com)"));
|
|
84
|
+
}
|
|
230
85
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
86
|
+
await access(resourcesDir);
|
|
87
|
+
} catch {
|
|
88
|
+
console.log(chalk.gray("No resources/ directory found - skipping widget build"));
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
let entries = [];
|
|
92
|
+
try {
|
|
93
|
+
const files = await fs.readdir(resourcesDir);
|
|
94
|
+
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => path.join(resourcesDir, f));
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log(chalk.gray("No widgets found in resources/ directory"));
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
console.log(chalk.gray("No widgets found in resources/ directory"));
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
console.log(chalk.gray(`Building ${entries.length} widget(s)...`));
|
|
104
|
+
const react = (await import("@vitejs/plugin-react")).default;
|
|
105
|
+
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
106
|
+
const builtWidgets = [];
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const baseName = path.basename(entry).replace(/\.tsx?$/, "");
|
|
109
|
+
const widgetName = baseName;
|
|
110
|
+
console.log(chalk.gray(` - Building ${widgetName}...`));
|
|
111
|
+
const tempDir = path.join(projectPath, ".mcp-use", widgetName);
|
|
112
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
113
|
+
const relativeResourcesPath = path.relative(tempDir, resourcesDir).replace(/\\/g, "/");
|
|
114
|
+
const cssContent = `@import "tailwindcss";
|
|
115
|
+
|
|
116
|
+
/* Configure Tailwind to scan the resources directory */
|
|
117
|
+
@source "${relativeResourcesPath}";
|
|
118
|
+
`;
|
|
119
|
+
await fs.writeFile(path.join(tempDir, "styles.css"), cssContent, "utf8");
|
|
120
|
+
const entryContent = `import React from 'react'
|
|
121
|
+
import { createRoot } from 'react-dom/client'
|
|
122
|
+
import './styles.css'
|
|
123
|
+
import Component from '${entry}'
|
|
124
|
+
|
|
125
|
+
const container = document.getElementById('widget-root')
|
|
126
|
+
if (container && Component) {
|
|
127
|
+
const root = createRoot(container)
|
|
128
|
+
root.render(<Component />)
|
|
129
|
+
}
|
|
130
|
+
`;
|
|
131
|
+
const htmlContent = `<!doctype html>
|
|
132
|
+
<html lang="en">
|
|
133
|
+
<head>
|
|
134
|
+
<meta charset="UTF-8" />
|
|
135
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
136
|
+
<title>${widgetName} Widget</title>
|
|
137
|
+
</head>
|
|
138
|
+
<body>
|
|
139
|
+
<div id="widget-root"></div>
|
|
140
|
+
<script type="module" src="/entry.tsx"></script>
|
|
141
|
+
</body>
|
|
142
|
+
</html>`;
|
|
143
|
+
await fs.writeFile(path.join(tempDir, "entry.tsx"), entryContent, "utf8");
|
|
144
|
+
await fs.writeFile(path.join(tempDir, "index.html"), htmlContent, "utf8");
|
|
145
|
+
const outDir = path.join(projectPath, "dist", "resources", "widgets", widgetName);
|
|
146
|
+
const baseUrl = mcpUrl ? `${mcpUrl}/mcp-use/widgets/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
|
|
147
|
+
let widgetMetadata = {};
|
|
148
|
+
try {
|
|
149
|
+
const metadataTempDir = path.join(projectPath, ".mcp-use", `${widgetName}-metadata`);
|
|
150
|
+
await fs.mkdir(metadataTempDir, { recursive: true });
|
|
151
|
+
const { createServer } = await import("vite");
|
|
152
|
+
const metadataServer = await createServer({
|
|
153
|
+
root: metadataTempDir,
|
|
154
|
+
cacheDir: path.join(metadataTempDir, ".vite-cache"),
|
|
155
|
+
plugins: [tailwindcss(), react()],
|
|
156
|
+
resolve: {
|
|
157
|
+
alias: {
|
|
158
|
+
"@": resourcesDir
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
server: {
|
|
162
|
+
middlewareMode: true
|
|
163
|
+
},
|
|
164
|
+
clearScreen: false,
|
|
165
|
+
logLevel: "silent",
|
|
166
|
+
customLogger: {
|
|
167
|
+
info: () => {
|
|
168
|
+
},
|
|
169
|
+
warn: () => {
|
|
170
|
+
},
|
|
171
|
+
error: () => {
|
|
172
|
+
},
|
|
173
|
+
clearScreen: () => {
|
|
174
|
+
},
|
|
175
|
+
hasErrorLogged: () => false,
|
|
176
|
+
hasWarned: false,
|
|
177
|
+
warnOnce: () => {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
try {
|
|
182
|
+
const mod = await metadataServer.ssrLoadModule(entry);
|
|
183
|
+
if (mod.widgetMetadata) {
|
|
184
|
+
widgetMetadata = {
|
|
185
|
+
description: mod.widgetMetadata.description,
|
|
186
|
+
inputs: mod.widgetMetadata.inputs?.shape || {}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.warn(chalk.yellow(` \u26A0 Could not extract metadata for ${widgetName}`));
|
|
192
|
+
} finally {
|
|
193
|
+
await metadataServer.close();
|
|
194
|
+
try {
|
|
195
|
+
await fs.rm(metadataTempDir, { recursive: true, force: true });
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await build({
|
|
203
|
+
root: tempDir,
|
|
204
|
+
base: baseUrl,
|
|
205
|
+
plugins: [tailwindcss(), react()],
|
|
206
|
+
resolve: {
|
|
207
|
+
alias: {
|
|
208
|
+
"@": resourcesDir
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
build: {
|
|
212
|
+
outDir,
|
|
213
|
+
emptyOutDir: true,
|
|
214
|
+
rollupOptions: {
|
|
215
|
+
input: path.join(tempDir, "index.html")
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
const metadataPath = path.join(outDir, "metadata.json");
|
|
220
|
+
await fs.writeFile(metadataPath, JSON.stringify(widgetMetadata, null, 2), "utf8");
|
|
221
|
+
builtWidgets.push(widgetName);
|
|
222
|
+
console.log(chalk.green(` \u2713 Built ${widgetName}`));
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error(chalk.red(` \u2717 Failed to build ${widgetName}:`), error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return builtWidgets;
|
|
228
|
+
}
|
|
229
|
+
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", false).action(async (options) => {
|
|
230
|
+
try {
|
|
231
|
+
const projectPath = path.resolve(options.path);
|
|
232
|
+
const { promises: fs } = await import("fs");
|
|
233
|
+
console.log(chalk.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
234
|
+
const builtWidgets = await buildWidgets(projectPath);
|
|
235
|
+
console.log(chalk.gray("Building TypeScript..."));
|
|
235
236
|
await runCommand("npx", ["tsc"], projectPath);
|
|
236
|
-
console.log("\
|
|
237
|
-
|
|
237
|
+
console.log(chalk.green("\u2713 TypeScript build complete!"));
|
|
238
|
+
const manifestPath = path.join(projectPath, "dist", ".mcp-use-manifest.json");
|
|
239
|
+
const manifest = {
|
|
240
|
+
includeInspector: options.withInspector || false,
|
|
241
|
+
buildTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
242
|
+
widgets: builtWidgets
|
|
243
|
+
};
|
|
244
|
+
await fs.mkdir(path.dirname(manifestPath), { recursive: true });
|
|
245
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
246
|
+
console.log(chalk.green("\u2713 Build manifest created"));
|
|
247
|
+
console.log(chalk.green.bold(`
|
|
248
|
+
\u2713 Build complete!`));
|
|
249
|
+
if (builtWidgets.length > 0) {
|
|
250
|
+
console.log(chalk.gray(` ${builtWidgets.length} widget(s) built`));
|
|
251
|
+
}
|
|
252
|
+
if (options.withInspector) {
|
|
253
|
+
console.log(chalk.gray(" Inspector included"));
|
|
254
|
+
}
|
|
238
255
|
} catch (error) {
|
|
239
|
-
console.error("Build failed:", error);
|
|
256
|
+
console.error(chalk.red("Build failed:"), error);
|
|
240
257
|
process.exit(1);
|
|
241
258
|
}
|
|
242
259
|
});
|
|
243
|
-
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("--no-open", "Do not auto-open inspector").action(async (options) => {
|
|
260
|
+
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) => {
|
|
244
261
|
try {
|
|
245
|
-
const projectPath =
|
|
262
|
+
const projectPath = path.resolve(options.path);
|
|
246
263
|
let port = parseInt(options.port, 10);
|
|
247
|
-
|
|
248
|
-
`);
|
|
249
|
-
if (!await isPortAvailable(port)) {
|
|
250
|
-
console.log(`\
|
|
251
|
-
const availablePort = await findAvailablePort(port);
|
|
252
|
-
console.log(`\
|
|
264
|
+
const host = options.host;
|
|
265
|
+
console.log(chalk.cyan.bold(`mcp-use v${packageJson.version}`));
|
|
266
|
+
if (!await isPortAvailable(port, host)) {
|
|
267
|
+
console.log(chalk.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
|
|
268
|
+
const availablePort = await findAvailablePort(port, host);
|
|
269
|
+
console.log(chalk.green.bold(`\u2713 Using port ${availablePort} instead`));
|
|
253
270
|
port = availablePort;
|
|
254
271
|
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
await access(path2.join(projectPath, serverFile));
|
|
258
|
-
} catch {
|
|
259
|
-
serverFile = "src/server.ts";
|
|
260
|
-
}
|
|
272
|
+
const serverFile = await findServerFile(projectPath);
|
|
261
273
|
const processes = [];
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
267
|
-
tscProc.stdout?.on("data", (data) => {
|
|
268
|
-
const output = data.toString();
|
|
269
|
-
if (output.includes("Watching for file changes")) {
|
|
270
|
-
console.log("\x1B[32m\u2713\x1B[0m TypeScript compiler watching...");
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
processes.push(tscProc);
|
|
274
|
-
buildWidgets(projectPath, true).catch((error) => {
|
|
275
|
-
console.error("Widget builder failed:", error);
|
|
276
|
-
});
|
|
277
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
278
|
-
const serverProc = spawn("npx", ["tsx", "watch", serverFile], {
|
|
279
|
-
cwd: projectPath,
|
|
280
|
-
stdio: "inherit",
|
|
281
|
-
shell: false,
|
|
282
|
-
env: { ...process.env, PORT: String(port) }
|
|
274
|
+
const serverProc = runCommand("npx", ["tsx", "watch", serverFile], projectPath, {
|
|
275
|
+
PORT: String(port),
|
|
276
|
+
HOST: host,
|
|
277
|
+
NODE_ENV: "development"
|
|
283
278
|
});
|
|
284
279
|
processes.push(serverProc);
|
|
285
280
|
if (options.open !== false) {
|
|
286
281
|
const startTime = Date.now();
|
|
287
|
-
const ready = await waitForServer(port);
|
|
282
|
+
const ready = await waitForServer(port, host);
|
|
288
283
|
if (ready) {
|
|
289
|
-
const mcpUrl = `http
|
|
290
|
-
const inspectorUrl = `http
|
|
284
|
+
const mcpUrl = `http://${host}:${port}/mcp`;
|
|
285
|
+
const inspectorUrl = `http://${host}:${port}/inspector?autoConnect=${encodeURIComponent(mcpUrl)}`;
|
|
291
286
|
const readyTime = Date.now() - startTime;
|
|
292
|
-
console.log(`
|
|
293
|
-
|
|
294
|
-
console.log(`
|
|
295
|
-
console.log(`
|
|
296
|
-
console.log(`
|
|
297
|
-
|
|
298
|
-
`);
|
|
287
|
+
console.log(chalk.green.bold(`\u2713 Ready in ${readyTime}ms`));
|
|
288
|
+
console.log(chalk.whiteBright(`Local: http://${host}:${port}`));
|
|
289
|
+
console.log(chalk.whiteBright(`Network: http://${host}:${port}`));
|
|
290
|
+
console.log(chalk.whiteBright(`MCP: ${mcpUrl}`));
|
|
291
|
+
console.log(chalk.whiteBright(`Inspector: ${inspectorUrl}
|
|
292
|
+
`));
|
|
299
293
|
await open(inspectorUrl);
|
|
300
294
|
}
|
|
301
295
|
}
|
|
302
296
|
const cleanup = () => {
|
|
303
|
-
console.log("\n\nShutting down...");
|
|
297
|
+
console.log(chalk.gray("\n\nShutting down..."));
|
|
304
298
|
processes.forEach((proc) => proc.kill());
|
|
305
299
|
process.exit(0);
|
|
306
300
|
};
|
|
@@ -309,19 +303,19 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
309
303
|
await new Promise(() => {
|
|
310
304
|
});
|
|
311
305
|
} catch (error) {
|
|
312
|
-
console.error("Dev mode failed:", error);
|
|
306
|
+
console.error(chalk.red("Dev mode failed:"), error);
|
|
313
307
|
process.exit(1);
|
|
314
308
|
}
|
|
315
309
|
});
|
|
316
310
|
program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").action(async (options) => {
|
|
317
311
|
try {
|
|
318
|
-
const projectPath =
|
|
312
|
+
const projectPath = path.resolve(options.path);
|
|
319
313
|
const port = parseInt(options.port, 10);
|
|
320
314
|
console.log(`\x1B[36m\x1B[1mmcp-use\x1B[0m \x1B[90mVersion: ${packageJson.version}\x1B[0m
|
|
321
315
|
`);
|
|
322
316
|
let serverFile = "dist/index.js";
|
|
323
317
|
try {
|
|
324
|
-
await access(
|
|
318
|
+
await access(path.join(projectPath, serverFile));
|
|
325
319
|
} catch {
|
|
326
320
|
serverFile = "dist/server.js";
|
|
327
321
|
}
|
|
@@ -329,7 +323,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
329
323
|
const serverProc = spawn("node", [serverFile], {
|
|
330
324
|
cwd: projectPath,
|
|
331
325
|
stdio: "inherit",
|
|
332
|
-
env: { ...process.env, PORT: String(port) }
|
|
326
|
+
env: { ...process.env, PORT: String(port), NODE_ENV: "production" }
|
|
333
327
|
});
|
|
334
328
|
const cleanup = () => {
|
|
335
329
|
console.log("\n\nShutting down...");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-use/cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.19-canary.0",
|
|
4
4
|
"description": "Build tool for MCP UI widgets - bundles React components into standalone HTML pages for Model Context Protocol servers",
|
|
5
5
|
"author": "mcp-use, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,16 +32,24 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@tailwindcss/vite": "^4.1.15",
|
|
36
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
37
|
+
"chalk": "^5.3.0",
|
|
38
|
+
"chokidar": "^4.0.0",
|
|
35
39
|
"commander": "^11.0.0",
|
|
40
|
+
"dotenv": "^16.5.0",
|
|
36
41
|
"esbuild": "^0.19.0",
|
|
37
42
|
"globby": "^14.0.0",
|
|
38
43
|
"open": "^10.0.0",
|
|
39
44
|
"tsx": "^4.0.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
45
|
+
"vite": "^6.0.0",
|
|
46
|
+
"ws": "^8.18.0",
|
|
47
|
+
"@mcp-use/inspector": "0.4.7-canary.0",
|
|
48
|
+
"mcp-use": "1.1.7-canary.0"
|
|
42
49
|
},
|
|
43
50
|
"devDependencies": {
|
|
44
51
|
"@types/node": "^20.0.0",
|
|
52
|
+
"@types/ws": "^8.5.10",
|
|
45
53
|
"typescript": "^5.0.0"
|
|
46
54
|
},
|
|
47
55
|
"publishConfig": {
|
package/dist/build.d.ts
DELETED
package/dist/build.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAmGA,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,UAAQ,iBA4FpE"}
|