mcp-app-studio 0.5.1 → 0.6.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/README.md +66 -18
- package/dist/{bridge-BXW_-p2R.d.ts → bridge-BKLezf-H.d.ts} +13 -0
- package/dist/{chunk-2OPSDEPI.js → chunk-IEZAKOIG.js} +34 -2
- package/dist/{chunk-4LAH4JH6.js → chunk-QNH5NSRH.js} +0 -19
- package/dist/cli/index.js +162 -133
- package/dist/core/index.d.ts +11 -29
- package/dist/core/index.js +1 -3
- package/dist/index.d.ts +114 -33
- package/dist/index.js +73 -49
- package/dist/platforms/mcp/index.d.ts +2 -2
- package/dist/platforms/mcp/index.js +13 -3
- package/package.json +4 -8
- package/dist/chunk-EPZCYA26.js +0 -162
- package/dist/platforms/chatgpt/index.d.ts +0 -159
- package/dist/platforms/chatgpt/index.js +0 -167
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/cli/index.ts
|
|
2
|
-
import
|
|
2
|
+
import fs5 from "fs";
|
|
3
3
|
import { createWriteStream } from "fs";
|
|
4
4
|
import { Readable } from "stream";
|
|
5
5
|
import { pipeline } from "stream/promises";
|
|
6
|
-
import
|
|
6
|
+
import path6 from "path";
|
|
7
7
|
import os from "os";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import * as p from "@clack/prompts";
|
|
@@ -36,15 +36,43 @@ function filterTemplateTarEntry(rootDir, entryPath, entry) {
|
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// src/cli/
|
|
39
|
+
// src/cli/template-validation.ts
|
|
40
40
|
import fs from "fs";
|
|
41
41
|
import path2 from "path";
|
|
42
|
+
var REQUIRED_TEMPLATE_PATHS = [
|
|
43
|
+
"package.json",
|
|
44
|
+
"scripts/dev.ts",
|
|
45
|
+
"scripts/export.ts",
|
|
46
|
+
"lib/workbench/component-registry.tsx",
|
|
47
|
+
"lib/workbench/wrappers/index.ts",
|
|
48
|
+
"components/examples/index.ts",
|
|
49
|
+
"lib/workbench/store.ts"
|
|
50
|
+
];
|
|
51
|
+
function validateTemplateDir(templateDir) {
|
|
52
|
+
const missing = REQUIRED_TEMPLATE_PATHS.filter(
|
|
53
|
+
(p2) => !fs.existsSync(path2.join(templateDir, p2))
|
|
54
|
+
);
|
|
55
|
+
if (missing.length > 0) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Template validation failed. Missing expected files:
|
|
58
|
+
${missing.map((p2) => `- ${p2}`).join(
|
|
59
|
+
"\n"
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
This may indicate the starter template has changed. Try updating mcp-app-studio or set MCP_APP_STUDIO_TEMPLATE_REPO / MCP_APP_STUDIO_TEMPLATE_REF to a compatible template.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/cli/utils.ts
|
|
68
|
+
import fs2 from "fs";
|
|
69
|
+
import path3 from "path";
|
|
42
70
|
function isValidProjectPath(name) {
|
|
43
71
|
if (!name || name.trim() === "") {
|
|
44
72
|
return { valid: false, error: "Project name is required" };
|
|
45
73
|
}
|
|
46
74
|
const trimmed = name.trim();
|
|
47
|
-
if (
|
|
75
|
+
if (path3.isAbsolute(trimmed)) {
|
|
48
76
|
return {
|
|
49
77
|
valid: false,
|
|
50
78
|
error: "Absolute paths are not allowed. Use a relative path or project name."
|
|
@@ -57,9 +85,9 @@ function isValidProjectPath(name) {
|
|
|
57
85
|
};
|
|
58
86
|
}
|
|
59
87
|
const cwd = process.cwd();
|
|
60
|
-
const resolved =
|
|
61
|
-
const relative =
|
|
62
|
-
if (relative.startsWith("..") ||
|
|
88
|
+
const resolved = path3.resolve(cwd, trimmed);
|
|
89
|
+
const relative = path3.relative(cwd, resolved);
|
|
90
|
+
if (relative.startsWith("..") || path3.isAbsolute(relative)) {
|
|
63
91
|
return {
|
|
64
92
|
valid: false,
|
|
65
93
|
error: "Project must be created within current directory."
|
|
@@ -76,15 +104,15 @@ function toValidPackageName(name) {
|
|
|
76
104
|
return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
|
|
77
105
|
}
|
|
78
106
|
function isEmpty(dir) {
|
|
79
|
-
if (!
|
|
80
|
-
const files =
|
|
107
|
+
if (!fs2.existsSync(dir)) return true;
|
|
108
|
+
const files = fs2.readdirSync(dir);
|
|
81
109
|
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
82
110
|
}
|
|
83
111
|
function emptyDir(dir) {
|
|
84
|
-
if (!
|
|
85
|
-
for (const file of
|
|
112
|
+
if (!fs2.existsSync(dir)) return;
|
|
113
|
+
for (const file of fs2.readdirSync(dir)) {
|
|
86
114
|
if (file === ".git") continue;
|
|
87
|
-
|
|
115
|
+
fs2.rmSync(path3.join(dir, file), { recursive: true, force: true });
|
|
88
116
|
}
|
|
89
117
|
}
|
|
90
118
|
var DEPENDENCY_OVERRIDES = {
|
|
@@ -93,10 +121,10 @@ var DEPENDENCY_OVERRIDES = {
|
|
|
93
121
|
"@assistant-ui/react-markdown": "^0.12.1"
|
|
94
122
|
};
|
|
95
123
|
function updatePackageJson(dir, name, description, opts) {
|
|
96
|
-
const pkgPath =
|
|
97
|
-
if (!
|
|
124
|
+
const pkgPath = path3.join(dir, "package.json");
|
|
125
|
+
if (!fs2.existsSync(pkgPath)) return;
|
|
98
126
|
try {
|
|
99
|
-
const content =
|
|
127
|
+
const content = fs2.readFileSync(pkgPath, "utf-8");
|
|
100
128
|
const pkg = JSON.parse(content);
|
|
101
129
|
pkg["name"] = name;
|
|
102
130
|
pkg["version"] = "0.1.0";
|
|
@@ -114,7 +142,7 @@ function updatePackageJson(dir, name, description, opts) {
|
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
pkg["dependencies"] = deps;
|
|
117
|
-
|
|
145
|
+
fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
118
146
|
`);
|
|
119
147
|
} catch (error) {
|
|
120
148
|
throw new Error(
|
|
@@ -130,8 +158,59 @@ function detectPackageManager() {
|
|
|
130
158
|
return "npm";
|
|
131
159
|
}
|
|
132
160
|
|
|
161
|
+
// src/cli/version.ts
|
|
162
|
+
import fs3 from "fs";
|
|
163
|
+
import path4 from "path";
|
|
164
|
+
function getVersionFromCliDir(cliDir) {
|
|
165
|
+
try {
|
|
166
|
+
const candidatePaths = [
|
|
167
|
+
path4.resolve(cliDir, "../../package.json"),
|
|
168
|
+
path4.resolve(cliDir, "../package.json"),
|
|
169
|
+
path4.resolve(cliDir, "../../../package.json")
|
|
170
|
+
];
|
|
171
|
+
for (const pkgPath of candidatePaths) {
|
|
172
|
+
if (!fs3.existsSync(pkgPath)) continue;
|
|
173
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
174
|
+
if (pkg.version) return pkg.version;
|
|
175
|
+
}
|
|
176
|
+
return "0.0.0";
|
|
177
|
+
} catch {
|
|
178
|
+
return "0.0.0";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/cli/workbench-index.ts
|
|
183
|
+
import fs4 from "fs";
|
|
184
|
+
import path5 from "path";
|
|
185
|
+
function generateWorkbenchIndexExport(components) {
|
|
186
|
+
const exports = [];
|
|
187
|
+
if (components.includes("welcome")) {
|
|
188
|
+
exports.push("WelcomeCardSDK");
|
|
189
|
+
}
|
|
190
|
+
if (components.includes("poi-map")) {
|
|
191
|
+
exports.push("POIMapSDK");
|
|
192
|
+
}
|
|
193
|
+
return exports.length > 0 ? `export { ${exports.join(", ")} } from "./wrappers";` : "// No SDK exports";
|
|
194
|
+
}
|
|
195
|
+
function updateWorkbenchIndex(targetDir, components) {
|
|
196
|
+
const indexPath = path5.join(targetDir, "lib/workbench/index.ts");
|
|
197
|
+
if (!fs4.existsSync(indexPath)) return;
|
|
198
|
+
let content = fs4.readFileSync(indexPath, "utf-8");
|
|
199
|
+
const wrappersExportRegex = /export \{[^}]*\} from "\.\/wrappers";/;
|
|
200
|
+
const newExport = generateWorkbenchIndexExport(components);
|
|
201
|
+
if (wrappersExportRegex.test(content)) {
|
|
202
|
+
content = content.replace(wrappersExportRegex, newExport);
|
|
203
|
+
} else {
|
|
204
|
+
content = `${content.trimEnd()}
|
|
205
|
+
|
|
206
|
+
${newExport}
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
fs4.writeFileSync(indexPath, content);
|
|
210
|
+
}
|
|
211
|
+
|
|
133
212
|
// src/cli/index.ts
|
|
134
|
-
var __dirname =
|
|
213
|
+
var __dirname = path6.dirname(fileURLToPath(import.meta.url));
|
|
135
214
|
var TEMPLATE_REPO = process.env["MCP_APP_STUDIO_TEMPLATE_REPO"] ?? "assistant-ui/mcp-app-studio-starter";
|
|
136
215
|
var TEMPLATE_REF = process.env["MCP_APP_STUDIO_TEMPLATE_REF"] ?? process.env["MCP_APP_STUDIO_TEMPLATE_BRANCH"] ?? "main";
|
|
137
216
|
var DOCS_URL = "https://github.com/assistant-ui/assistant-ui/tree/main/packages/mcp-app-studio";
|
|
@@ -164,19 +243,13 @@ function ensureSupportedNodeVersion() {
|
|
|
164
243
|
}
|
|
165
244
|
}
|
|
166
245
|
function getVersion() {
|
|
167
|
-
|
|
168
|
-
const pkgPath = path3.resolve(__dirname, "../package.json");
|
|
169
|
-
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
170
|
-
return pkg.version || "0.0.0";
|
|
171
|
-
} catch {
|
|
172
|
-
return "0.0.0";
|
|
173
|
-
}
|
|
246
|
+
return getVersionFromCliDir(__dirname);
|
|
174
247
|
}
|
|
175
248
|
function showHelp() {
|
|
176
249
|
console.log(`
|
|
177
250
|
mcp-app-studio v${getVersion()}
|
|
178
251
|
|
|
179
|
-
Create interactive apps for
|
|
252
|
+
Create interactive apps for MCP hosts (including ChatGPT and Claude Desktop).
|
|
180
253
|
|
|
181
254
|
${pc.bold("Requirements:")}
|
|
182
255
|
Node.js >=${REQUIRED_NODE_VERSION.major}.${REQUIRED_NODE_VERSION.minor}.${REQUIRED_NODE_VERSION.patch}
|
|
@@ -236,38 +309,38 @@ function writeStudioConfig(targetDir, template, appName) {
|
|
|
236
309
|
name: appName
|
|
237
310
|
}
|
|
238
311
|
};
|
|
239
|
-
|
|
240
|
-
|
|
312
|
+
fs5.writeFileSync(
|
|
313
|
+
path6.join(targetDir, "mcp-app-studio.config.json"),
|
|
241
314
|
`${JSON.stringify(config, null, 2)}
|
|
242
315
|
`
|
|
243
316
|
);
|
|
244
317
|
}
|
|
245
318
|
function updateServerPackageName(targetDir, projectPackageName) {
|
|
246
|
-
const serverPkgPath =
|
|
247
|
-
if (!
|
|
248
|
-
const raw =
|
|
319
|
+
const serverPkgPath = path6.join(targetDir, "server", "package.json");
|
|
320
|
+
if (!fs5.existsSync(serverPkgPath)) return;
|
|
321
|
+
const raw = fs5.readFileSync(serverPkgPath, "utf-8");
|
|
249
322
|
const pkg = JSON.parse(raw);
|
|
250
323
|
const baseName = projectPackageName.includes("/") ? projectPackageName.split("/").pop() : projectPackageName;
|
|
251
324
|
if (!baseName) return;
|
|
252
325
|
pkg["name"] = `${baseName}-mcp-server`;
|
|
253
|
-
|
|
326
|
+
fs5.writeFileSync(serverPkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
254
327
|
`, "utf-8");
|
|
255
328
|
}
|
|
256
329
|
function ensureServerPostinstall(targetDir) {
|
|
257
|
-
const pkgPath =
|
|
258
|
-
const serverPkgPath =
|
|
259
|
-
if (!
|
|
260
|
-
const raw =
|
|
330
|
+
const pkgPath = path6.join(targetDir, "package.json");
|
|
331
|
+
const serverPkgPath = path6.join(targetDir, "server", "package.json");
|
|
332
|
+
if (!fs5.existsSync(pkgPath) || !fs5.existsSync(serverPkgPath)) return;
|
|
333
|
+
const raw = fs5.readFileSync(pkgPath, "utf-8");
|
|
261
334
|
const pkg = JSON.parse(raw);
|
|
262
335
|
const scripts = pkg["scripts"] ?? {};
|
|
263
336
|
if (scripts["postinstall"]) return;
|
|
264
|
-
const scriptPath =
|
|
337
|
+
const scriptPath = path6.join(
|
|
265
338
|
targetDir,
|
|
266
339
|
"scripts",
|
|
267
340
|
"mcp-app-studio-postinstall.cjs"
|
|
268
341
|
);
|
|
269
|
-
|
|
270
|
-
|
|
342
|
+
fs5.mkdirSync(path6.dirname(scriptPath), { recursive: true });
|
|
343
|
+
fs5.writeFileSync(
|
|
271
344
|
scriptPath,
|
|
272
345
|
`/* eslint-disable */
|
|
273
346
|
const { spawnSync } = require("node:child_process");
|
|
@@ -294,7 +367,7 @@ process.exit(result.status == null ? 1 : result.status);
|
|
|
294
367
|
);
|
|
295
368
|
scripts["postinstall"] = "node scripts/mcp-app-studio-postinstall.cjs";
|
|
296
369
|
pkg["scripts"] = scripts;
|
|
297
|
-
|
|
370
|
+
fs5.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
298
371
|
`, "utf-8");
|
|
299
372
|
}
|
|
300
373
|
function generateComponentRegistry(components) {
|
|
@@ -311,7 +384,7 @@ function generateComponentRegistry(components) {
|
|
|
311
384
|
defaultProps: {
|
|
312
385
|
title: "Welcome!",
|
|
313
386
|
message:
|
|
314
|
-
"This is your
|
|
387
|
+
"This is your MCP App. Edit this component to build something amazing.",
|
|
315
388
|
},
|
|
316
389
|
exportConfig: {
|
|
317
390
|
entryPoint: "lib/workbench/wrappers/welcome-card-sdk.tsx",
|
|
@@ -422,9 +495,9 @@ function generateExamplesIndex(components) {
|
|
|
422
495
|
` : "// No examples\n";
|
|
423
496
|
}
|
|
424
497
|
function updateExportScriptDefaults(targetDir, entryPoint, exportName) {
|
|
425
|
-
const exportScriptPath =
|
|
426
|
-
if (!
|
|
427
|
-
let content =
|
|
498
|
+
const exportScriptPath = path6.join(targetDir, "scripts/export.ts");
|
|
499
|
+
if (!fs5.existsSync(exportScriptPath)) return;
|
|
500
|
+
let content = fs5.readFileSync(exportScriptPath, "utf-8");
|
|
428
501
|
if (content.includes("mcp-app-studio.config.json")) return;
|
|
429
502
|
content = content.replace(
|
|
430
503
|
/entryPoint: "lib\/workbench\/wrappers\/[^"]+"/,
|
|
@@ -443,79 +516,54 @@ function updateExportScriptDefaults(targetDir, entryPoint, exportName) {
|
|
|
443
516
|
/Export name from entry file \(default: [^)]+\)/,
|
|
444
517
|
`Export name from entry file (default: ${exportName})`
|
|
445
518
|
);
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
function generateWorkbenchIndexExport(components) {
|
|
449
|
-
const exports = [];
|
|
450
|
-
if (components.includes("welcome")) {
|
|
451
|
-
exports.push("WelcomeCardSDK");
|
|
452
|
-
}
|
|
453
|
-
if (components.includes("poi-map")) {
|
|
454
|
-
exports.push("POIMapSDK");
|
|
455
|
-
}
|
|
456
|
-
return exports.length > 0 ? `export { ${exports.join(", ")} } from "./wrappers";` : "// No SDK exports";
|
|
457
|
-
}
|
|
458
|
-
function updateWorkbenchIndex(targetDir, components) {
|
|
459
|
-
const indexPath = path3.join(targetDir, "lib/workbench/index.ts");
|
|
460
|
-
let content = fs2.readFileSync(indexPath, "utf-8");
|
|
461
|
-
const wrappersExportRegex = /export \{[^}]*\} from "\.\/wrappers";/;
|
|
462
|
-
const newExport = generateWorkbenchIndexExport(components);
|
|
463
|
-
if (wrappersExportRegex.test(content)) {
|
|
464
|
-
content = content.replace(wrappersExportRegex, newExport);
|
|
465
|
-
} else {
|
|
466
|
-
content = `${content.trimEnd()}
|
|
467
|
-
|
|
468
|
-
${newExport}
|
|
469
|
-
`;
|
|
470
|
-
}
|
|
471
|
-
fs2.writeFileSync(indexPath, content);
|
|
519
|
+
fs5.writeFileSync(exportScriptPath, content);
|
|
472
520
|
}
|
|
473
521
|
function updateWorkbenchStoreDefault(targetDir, defaultComponent) {
|
|
474
|
-
const storePath =
|
|
475
|
-
let content =
|
|
522
|
+
const storePath = path6.join(targetDir, "lib/workbench/store.ts");
|
|
523
|
+
let content = fs5.readFileSync(storePath, "utf-8");
|
|
476
524
|
content = content.replace(
|
|
477
525
|
/selectedComponent: "[^"]+"/,
|
|
478
526
|
`selectedComponent: "${defaultComponent}"`
|
|
479
527
|
);
|
|
480
|
-
|
|
528
|
+
fs5.writeFileSync(storePath, content);
|
|
481
529
|
}
|
|
482
530
|
function applyTemplate(targetDir, template) {
|
|
483
531
|
const components = TEMPLATE_COMPONENTS[template];
|
|
484
|
-
const registryPath =
|
|
532
|
+
const registryPath = path6.join(
|
|
485
533
|
targetDir,
|
|
486
534
|
"lib/workbench/component-registry.tsx"
|
|
487
535
|
);
|
|
488
|
-
|
|
489
|
-
const wrappersIndexPath =
|
|
536
|
+
fs5.writeFileSync(registryPath, generateComponentRegistry(components));
|
|
537
|
+
const wrappersIndexPath = path6.join(
|
|
490
538
|
targetDir,
|
|
491
539
|
"lib/workbench/wrappers/index.ts"
|
|
492
540
|
);
|
|
493
|
-
|
|
494
|
-
const examplesIndexPath =
|
|
541
|
+
fs5.writeFileSync(wrappersIndexPath, generateWrappersIndex(components));
|
|
542
|
+
const examplesIndexPath = path6.join(
|
|
495
543
|
targetDir,
|
|
496
544
|
"components/examples/index.ts"
|
|
497
545
|
);
|
|
498
|
-
|
|
546
|
+
fs5.writeFileSync(examplesIndexPath, generateExamplesIndex(components));
|
|
499
547
|
updateWorkbenchIndex(targetDir, components);
|
|
500
548
|
const defaultComponent = TEMPLATE_DEFAULT_COMPONENT[template];
|
|
501
549
|
updateWorkbenchStoreDefault(targetDir, defaultComponent);
|
|
502
|
-
const examplesDir =
|
|
550
|
+
const examplesDir = path6.join(targetDir, "components/examples");
|
|
503
551
|
if (!components.includes("welcome")) {
|
|
504
|
-
|
|
552
|
+
fs5.rmSync(path6.join(examplesDir, "welcome-card"), {
|
|
505
553
|
recursive: true,
|
|
506
554
|
force: true
|
|
507
555
|
});
|
|
508
|
-
|
|
509
|
-
|
|
556
|
+
fs5.rmSync(
|
|
557
|
+
path6.join(targetDir, "lib/workbench/wrappers/welcome-card-sdk.tsx"),
|
|
510
558
|
{ force: true }
|
|
511
559
|
);
|
|
512
560
|
}
|
|
513
561
|
if (!components.includes("poi-map")) {
|
|
514
|
-
|
|
562
|
+
fs5.rmSync(path6.join(examplesDir, "poi-map"), {
|
|
515
563
|
recursive: true,
|
|
516
564
|
force: true
|
|
517
565
|
});
|
|
518
|
-
|
|
566
|
+
fs5.rmSync(path6.join(targetDir, "lib/workbench/wrappers/poi-map-sdk.tsx"), {
|
|
519
567
|
force: true
|
|
520
568
|
});
|
|
521
569
|
}
|
|
@@ -526,57 +574,32 @@ function applyTemplate(targetDir, template) {
|
|
|
526
574
|
exportConfig.exportName
|
|
527
575
|
);
|
|
528
576
|
}
|
|
529
|
-
var REQUIRED_TEMPLATE_PATHS = [
|
|
530
|
-
"package.json",
|
|
531
|
-
"scripts/dev.ts",
|
|
532
|
-
"scripts/export.ts",
|
|
533
|
-
"lib/workbench/component-registry.tsx",
|
|
534
|
-
"lib/workbench/wrappers/index.ts",
|
|
535
|
-
"components/examples/index.ts",
|
|
536
|
-
"lib/workbench/index.ts",
|
|
537
|
-
"lib/workbench/store.ts"
|
|
538
|
-
];
|
|
539
|
-
function validateTemplateDir(templateDir) {
|
|
540
|
-
const missing = REQUIRED_TEMPLATE_PATHS.filter(
|
|
541
|
-
(p2) => !fs2.existsSync(path3.join(templateDir, p2))
|
|
542
|
-
);
|
|
543
|
-
if (missing.length > 0) {
|
|
544
|
-
throw new Error(
|
|
545
|
-
`Template validation failed. Missing expected files:
|
|
546
|
-
${missing.map((p2) => `- ${p2}`).join(
|
|
547
|
-
"\n"
|
|
548
|
-
)}
|
|
549
|
-
|
|
550
|
-
This may indicate the starter template has changed. Try updating mcp-app-studio or set MCP_APP_STUDIO_TEMPLATE_REPO / MCP_APP_STUDIO_TEMPLATE_REF to a compatible template.`
|
|
551
|
-
);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
577
|
function copyDirContents(srcDir, destDir) {
|
|
555
|
-
|
|
556
|
-
const entries =
|
|
578
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
579
|
+
const entries = fs5.readdirSync(srcDir, { withFileTypes: true });
|
|
557
580
|
for (const entry of entries) {
|
|
558
581
|
if (entry.name === ".git") continue;
|
|
559
|
-
const srcPath =
|
|
560
|
-
const destPath =
|
|
582
|
+
const srcPath = path6.join(srcDir, entry.name);
|
|
583
|
+
const destPath = path6.join(destDir, entry.name);
|
|
561
584
|
if (entry.isDirectory()) {
|
|
562
585
|
copyDirContents(srcPath, destPath);
|
|
563
586
|
continue;
|
|
564
587
|
}
|
|
565
588
|
if (entry.isFile()) {
|
|
566
|
-
|
|
567
|
-
|
|
589
|
+
fs5.mkdirSync(path6.dirname(destPath), { recursive: true });
|
|
590
|
+
fs5.copyFileSync(srcPath, destPath);
|
|
568
591
|
}
|
|
569
592
|
}
|
|
570
593
|
}
|
|
571
594
|
async function downloadTemplateToTemp() {
|
|
572
595
|
const tarballUrl = getGithubArchiveTarballUrl(TEMPLATE_REPO, TEMPLATE_REF);
|
|
573
|
-
const tempDir =
|
|
574
|
-
|
|
596
|
+
const tempDir = fs5.mkdtempSync(
|
|
597
|
+
path6.join(os.tmpdir(), "mcp-app-studio-template-")
|
|
575
598
|
);
|
|
576
|
-
const tarballPath =
|
|
577
|
-
const extractDir =
|
|
599
|
+
const tarballPath = path6.join(tempDir, "template.tar.gz");
|
|
600
|
+
const extractDir = path6.join(tempDir, "extract");
|
|
578
601
|
try {
|
|
579
|
-
|
|
602
|
+
fs5.mkdirSync(extractDir, { recursive: true });
|
|
580
603
|
const response = await fetch(tarballUrl);
|
|
581
604
|
if (!response.ok) {
|
|
582
605
|
throw new Error(
|
|
@@ -598,7 +621,7 @@ async function downloadTemplateToTemp() {
|
|
|
598
621
|
strip: 0,
|
|
599
622
|
filter: (entryPath, entry) => filterTemplateTarEntry(extractDir, entryPath, entry)
|
|
600
623
|
});
|
|
601
|
-
const topLevelDirs =
|
|
624
|
+
const topLevelDirs = fs5.readdirSync(extractDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
602
625
|
if (topLevelDirs.length !== 1) {
|
|
603
626
|
throw new Error(
|
|
604
627
|
`Unexpected template archive layout. Expected a single top-level directory, found ${topLevelDirs.length}.`
|
|
@@ -610,12 +633,12 @@ async function downloadTemplateToTemp() {
|
|
|
610
633
|
"Unexpected template archive layout. No top-level directory found."
|
|
611
634
|
);
|
|
612
635
|
}
|
|
613
|
-
const templateDir =
|
|
636
|
+
const templateDir = path6.join(extractDir, topLevelDir.name);
|
|
614
637
|
validateTemplateDir(templateDir);
|
|
615
638
|
return { tempDir, templateDir };
|
|
616
639
|
} catch (err) {
|
|
617
640
|
try {
|
|
618
|
-
|
|
641
|
+
fs5.rmSync(tempDir, { recursive: true, force: true });
|
|
619
642
|
} catch {
|
|
620
643
|
}
|
|
621
644
|
throw err;
|
|
@@ -713,7 +736,7 @@ async function main() {
|
|
|
713
736
|
} else {
|
|
714
737
|
description = await p.text({
|
|
715
738
|
message: "App description:",
|
|
716
|
-
placeholder: "
|
|
739
|
+
placeholder: "An MCP app that helps users...",
|
|
717
740
|
initialValue: ""
|
|
718
741
|
});
|
|
719
742
|
}
|
|
@@ -763,8 +786,8 @@ async function main() {
|
|
|
763
786
|
p.cancel("Operation cancelled.");
|
|
764
787
|
process.exit(0);
|
|
765
788
|
}
|
|
766
|
-
const targetDir =
|
|
767
|
-
const packageName = projectName === "." ? toValidPackageName(
|
|
789
|
+
const targetDir = path6.resolve(process.cwd(), projectName);
|
|
790
|
+
const packageName = projectName === "." ? toValidPackageName(path6.basename(targetDir)) : toValidPackageName(projectName);
|
|
768
791
|
const config = {
|
|
769
792
|
name: projectName,
|
|
770
793
|
packageName,
|
|
@@ -811,11 +834,11 @@ async function main() {
|
|
|
811
834
|
if (shouldOverwrite) {
|
|
812
835
|
emptyDir(targetDir);
|
|
813
836
|
}
|
|
814
|
-
|
|
837
|
+
fs5.mkdirSync(targetDir, { recursive: true });
|
|
815
838
|
copyDirContents(downloaded.templateDir, targetDir);
|
|
816
839
|
} finally {
|
|
817
840
|
if (downloaded?.tempDir) {
|
|
818
|
-
|
|
841
|
+
fs5.rmSync(downloaded.tempDir, { recursive: true, force: true });
|
|
819
842
|
}
|
|
820
843
|
}
|
|
821
844
|
updatePackageJson(targetDir, config.packageName, config.description, {
|
|
@@ -826,11 +849,11 @@ async function main() {
|
|
|
826
849
|
}
|
|
827
850
|
s.message("Applying template...");
|
|
828
851
|
applyTemplate(targetDir, config.template);
|
|
829
|
-
writeStudioConfig(targetDir, config.template,
|
|
852
|
+
writeStudioConfig(targetDir, config.template, path6.basename(targetDir));
|
|
830
853
|
if (config.includeServer) {
|
|
831
854
|
updateServerPackageName(targetDir, config.packageName);
|
|
832
855
|
} else {
|
|
833
|
-
|
|
856
|
+
fs5.rmSync(path6.join(targetDir, "server"), { recursive: true, force: true });
|
|
834
857
|
}
|
|
835
858
|
s.stop("Project created!");
|
|
836
859
|
const pm = detectPackageManager();
|
|
@@ -850,7 +873,7 @@ async function main() {
|
|
|
850
873
|
];
|
|
851
874
|
if (config.includeServer) {
|
|
852
875
|
structureGuide.push(
|
|
853
|
-
`${pc.cyan("server/")} ${pc.dim("\u2190 MCP server for Claude Desktop")}`
|
|
876
|
+
`${pc.cyan("server/")} ${pc.dim("\u2190 MCP server for Claude Desktop and other MCP hosts")}`
|
|
854
877
|
);
|
|
855
878
|
}
|
|
856
879
|
p.note(structureGuide.join("\n"), "Project structure");
|
|
@@ -859,7 +882,7 @@ async function main() {
|
|
|
859
882
|
`${pc.cyan(`${runCmd} dev`)} ${pc.dim("Start the development workbench")}`
|
|
860
883
|
);
|
|
861
884
|
keyCommands.push(
|
|
862
|
-
`${pc.cyan(`${runCmd} export`)} ${pc.dim("Build & export
|
|
885
|
+
`${pc.cyan(`${runCmd} export`)} ${pc.dim("Build & export app bundle + manifest")}`
|
|
863
886
|
);
|
|
864
887
|
if (config.includeServer) {
|
|
865
888
|
keyCommands.push(
|
|
@@ -870,7 +893,7 @@ async function main() {
|
|
|
870
893
|
p.log.message("");
|
|
871
894
|
p.log.step(pc.bold("Building for multiple platforms:"));
|
|
872
895
|
p.log.message(
|
|
873
|
-
` ${pc.dim("\u2022")} Use ${pc.cyan("useFeature('widgetState')")} to check for ChatGPT
|
|
896
|
+
` ${pc.dim("\u2022")} Use ${pc.cyan("useFeature('widgetState')")} to check for optional ChatGPT extensions`
|
|
874
897
|
);
|
|
875
898
|
p.log.message(
|
|
876
899
|
` ${pc.dim("\u2022")} Use ${pc.cyan("useFeature('modelContext')")} to check for MCP features`
|
|
@@ -886,6 +909,12 @@ async function main() {
|
|
|
886
909
|
p.log.message(
|
|
887
910
|
` ${pc.dim("\u2022")} MCP Guide: ${pc.cyan("https://modelcontextprotocol.io/quickstart")}`
|
|
888
911
|
);
|
|
912
|
+
p.log.message(
|
|
913
|
+
` ${pc.dim("\u2022")} ChatGPT Submission: ${pc.cyan("https://platform.openai.com/apps")}`
|
|
914
|
+
);
|
|
915
|
+
p.log.message(
|
|
916
|
+
` ${pc.dim("\u2022")} Claude Submission: ${pc.cyan("https://support.claude.com/en/articles/12922490-remote-mcp-server-submission-guide")}`
|
|
917
|
+
);
|
|
889
918
|
}
|
|
890
919
|
p.log.message("");
|
|
891
920
|
p.outro(pc.green("Happy building! \u{1F680}"));
|
package/dist/core/index.d.ts
CHANGED
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
type Theme = "light" | "dark";
|
|
12
12
|
/**
|
|
13
13
|
* Platform your app is running on.
|
|
14
|
-
* - `"
|
|
15
|
-
* - `"mcp"` - Running inside an MCP host like Claude Desktop
|
|
14
|
+
* - `"mcp"` - Running inside an MCP Apps host (including ChatGPT)
|
|
16
15
|
* - `"unknown"` - Platform not detected (development mode or unsupported host)
|
|
17
16
|
*
|
|
18
17
|
* Use `usePlatform()` hook or `detectPlatform()` to check at runtime.
|
|
@@ -20,12 +19,10 @@ type Theme = "light" | "dark";
|
|
|
20
19
|
* @example
|
|
21
20
|
* ```tsx
|
|
22
21
|
* const platform = usePlatform();
|
|
23
|
-
* if (platform === '
|
|
24
|
-
* // Use ChatGPT-specific features like widgetState
|
|
25
|
-
* }
|
|
22
|
+
* if (platform === 'unknown') return null;
|
|
26
23
|
* ```
|
|
27
24
|
*/
|
|
28
|
-
type Platform = "
|
|
25
|
+
type Platform = "mcp" | "unknown";
|
|
29
26
|
/**
|
|
30
27
|
* Display mode for the widget.
|
|
31
28
|
* - `"inline"` - Widget embedded in conversation flow (default)
|
|
@@ -314,7 +311,7 @@ type FeatureKey = "callTool" | "openLink" | "sizeReporting" | "closeWidget" | "s
|
|
|
314
311
|
* ```tsx
|
|
315
312
|
* const capabilities = useCapabilities();
|
|
316
313
|
* if (capabilities?.widgetState) {
|
|
317
|
-
* // ChatGPT
|
|
314
|
+
* // ChatGPT extensions (window.openai)
|
|
318
315
|
* }
|
|
319
316
|
* ```
|
|
320
317
|
*/
|
|
@@ -335,11 +332,11 @@ interface HostCapabilities {
|
|
|
335
332
|
sendMessage: boolean;
|
|
336
333
|
/** Can request modal dialogs */
|
|
337
334
|
modal: boolean;
|
|
338
|
-
/** Can upload files (ChatGPT only) */
|
|
335
|
+
/** Can upload files (ChatGPT extensions only) */
|
|
339
336
|
fileUpload: boolean;
|
|
340
|
-
/** Can download files (ChatGPT only) */
|
|
337
|
+
/** Can download files (ChatGPT extensions only) */
|
|
341
338
|
fileDownload: boolean;
|
|
342
|
-
/** Can persist widget state (ChatGPT only) */
|
|
339
|
+
/** Can persist widget state (ChatGPT extensions only) */
|
|
343
340
|
widgetState: boolean;
|
|
344
341
|
/** Can update model context (MCP only) */
|
|
345
342
|
modelContext: boolean;
|
|
@@ -352,16 +349,6 @@ interface HostCapabilities {
|
|
|
352
349
|
/** Teardown notification support (MCP only) */
|
|
353
350
|
teardown: boolean;
|
|
354
351
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Capabilities available on ChatGPT.
|
|
357
|
-
*
|
|
358
|
-
* Notable features:
|
|
359
|
-
* - `widgetState`: Persistent state across refreshes
|
|
360
|
-
* - `fileUpload`/`fileDownload`: File handling
|
|
361
|
-
* - `modal`: Modal dialog support
|
|
362
|
-
* - `pip`: Picture-in-picture display mode
|
|
363
|
-
*/
|
|
364
|
-
declare const CHATGPT_CAPABILITIES: HostCapabilities;
|
|
365
352
|
/**
|
|
366
353
|
* Capabilities available on MCP hosts (e.g., Claude Desktop).
|
|
367
354
|
*
|
|
@@ -396,7 +383,7 @@ type ToolCancelledCallback = (reason: string) => void;
|
|
|
396
383
|
type HostContextChangedCallback = (ctx: Partial<HostContext>) => void;
|
|
397
384
|
type TeardownCallback = () => Promise<void> | void;
|
|
398
385
|
interface HostBridge {
|
|
399
|
-
readonly platform: "
|
|
386
|
+
readonly platform: "mcp";
|
|
400
387
|
readonly capabilities: HostCapabilities;
|
|
401
388
|
connect(): Promise<void>;
|
|
402
389
|
getHostContext(): HostContext | null;
|
|
@@ -431,16 +418,11 @@ interface ExtendedBridge extends HostBridge {
|
|
|
431
418
|
}>;
|
|
432
419
|
requestClose?(): void;
|
|
433
420
|
requestModal?(options: {
|
|
434
|
-
title?: string;
|
|
435
421
|
params?: Record<string, unknown>;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
y: number;
|
|
439
|
-
width: number;
|
|
440
|
-
height: number;
|
|
441
|
-
};
|
|
422
|
+
template?: string;
|
|
423
|
+
[key: string]: unknown;
|
|
442
424
|
}): Promise<void>;
|
|
443
425
|
sendLog?(level: "debug" | "info" | "warning" | "error", data: string): void;
|
|
444
426
|
}
|
|
445
427
|
|
|
446
|
-
export { type AudioContentBlock,
|
|
428
|
+
export { type AudioContentBlock, type ChatMessage, type ContainerDimensions, type ContentBlock, type ContentBlockAnnotations, type ContentBlockIcon, type DisplayMode, type ExtendedBridge, type FeatureKey, type HostBridge, type HostCapabilities, type HostContext, type HostContextChangedCallback, type HostStyles, type ImageContentBlock, MCP_CAPABILITIES, type Platform, type ResourceContentBlock, type ResourceLinkContentBlock, type TeardownCallback, type TextContentBlock, type Theme, type ToolCancelledCallback, type ToolInputCallback, type ToolInputPartialCallback, type ToolResult, type ToolResultCallback, hasFeature, imageBlock, textBlock };
|
package/dist/core/index.js
CHANGED
|
@@ -3,12 +3,10 @@ import {
|
|
|
3
3
|
textBlock
|
|
4
4
|
} from "../chunk-KRCGOYZ5.js";
|
|
5
5
|
import {
|
|
6
|
-
CHATGPT_CAPABILITIES,
|
|
7
6
|
MCP_CAPABILITIES,
|
|
8
7
|
hasFeature
|
|
9
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-QNH5NSRH.js";
|
|
10
9
|
export {
|
|
11
|
-
CHATGPT_CAPABILITIES,
|
|
12
10
|
MCP_CAPABILITIES,
|
|
13
11
|
hasFeature,
|
|
14
12
|
imageBlock,
|