@ubuligan/create-mcp-app 0.1.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/LICENSE +21 -0
- package/dist/index.js +521 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jsznpm
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { basename } from "path";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
|
|
9
|
+
// src/scaffold.ts
|
|
10
|
+
import { mkdir, readdir, writeFile } from "fs/promises";
|
|
11
|
+
import { dirname, join, resolve } from "path";
|
|
12
|
+
|
|
13
|
+
// src/templates/client.ts
|
|
14
|
+
var CLIENT_VERSION = "^0.1.0";
|
|
15
|
+
function clientFiles(opts, standalone) {
|
|
16
|
+
if (standalone) {
|
|
17
|
+
return [{ path: "src/index.ts", contents: standaloneClient() }];
|
|
18
|
+
}
|
|
19
|
+
return [{ path: "scripts/demo-client.ts", contents: demoClient() }];
|
|
20
|
+
}
|
|
21
|
+
function clientDeps() {
|
|
22
|
+
return { "@ubuligan/client": CLIENT_VERSION };
|
|
23
|
+
}
|
|
24
|
+
function standaloneClient() {
|
|
25
|
+
return `import { createMCPClient } from "@ubuligan/client";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Demo MCP client. Point it at a server with either:
|
|
29
|
+
* MCP_URL=http://localhost:3000/mcp (Streamable HTTP)
|
|
30
|
+
* MCP_STDIO="node ../server/dist/index.js" (stdio command)
|
|
31
|
+
*/
|
|
32
|
+
async function main(): Promise<void> {
|
|
33
|
+
const url = process.env.MCP_URL;
|
|
34
|
+
const stdio = process.env.MCP_STDIO;
|
|
35
|
+
|
|
36
|
+
const client = await createMCPClient({
|
|
37
|
+
logger: "info",
|
|
38
|
+
transport: url
|
|
39
|
+
? { type: "http", url }
|
|
40
|
+
: stdio
|
|
41
|
+
? { type: "stdio", command: stdio.split(" ")[0]!, args: stdio.split(" ").slice(1) }
|
|
42
|
+
: (() => {
|
|
43
|
+
throw new Error("Set MCP_URL or MCP_STDIO to choose a server");
|
|
44
|
+
})(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const { tools } = await client.listTools();
|
|
48
|
+
console.log(\`Connected. \${tools.length} tools available:\`);
|
|
49
|
+
for (const tool of tools) console.log(\` - \${tool.name}: \${tool.description ?? ""}\`);
|
|
50
|
+
|
|
51
|
+
await client.close();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
main().catch((error) => {
|
|
55
|
+
console.error(error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
function demoClient() {
|
|
61
|
+
return `import { createMCPClient } from "@ubuligan/client";
|
|
62
|
+
|
|
63
|
+
/** Spawns the local built server over stdio and exercises it. */
|
|
64
|
+
async function main(): Promise<void> {
|
|
65
|
+
const client = await createMCPClient({
|
|
66
|
+
logger: "info",
|
|
67
|
+
transport: { type: "stdio", command: "node", args: ["dist/index.js"] },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const { tools } = await client.listTools();
|
|
71
|
+
console.log(\`Connected. \${tools.length} tools available:\`);
|
|
72
|
+
for (const tool of tools) console.log(\` - \${tool.name}\`);
|
|
73
|
+
|
|
74
|
+
if (tools.some((t) => t.name === "echo")) {
|
|
75
|
+
const result = await client.callTool("echo", { message: "hello from the demo client" });
|
|
76
|
+
console.log("echo result:", JSON.stringify(result.content));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await client.close();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main().catch((error) => {
|
|
83
|
+
console.error(error);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
});
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/templates/server.ts
|
|
90
|
+
var SDK_VERSION = "^1.29.0";
|
|
91
|
+
var SERVER_VERSION = "^0.1.0";
|
|
92
|
+
function serverFiles(opts, prefix = "") {
|
|
93
|
+
const files = [];
|
|
94
|
+
const p2 = (rel) => `${prefix}${rel}`;
|
|
95
|
+
if (opts.features.includes("tools")) files.push({ path: p2("src/tools.ts"), contents: TOOLS_FILE });
|
|
96
|
+
if (opts.features.includes("resources"))
|
|
97
|
+
files.push({ path: p2("src/resources.ts"), contents: resourcesFile(opts.name) });
|
|
98
|
+
if (opts.features.includes("prompts")) files.push({ path: p2("src/prompts.ts"), contents: PROMPTS_FILE });
|
|
99
|
+
files.push({ path: p2("src/server.ts"), contents: serverEntry(opts) });
|
|
100
|
+
files.push({ path: p2("src/index.ts"), contents: indexDispatcher(opts.transport) });
|
|
101
|
+
return files;
|
|
102
|
+
}
|
|
103
|
+
function serverDeps(_transport) {
|
|
104
|
+
const dependencies = {
|
|
105
|
+
"@ubuligan/server": SERVER_VERSION,
|
|
106
|
+
"@modelcontextprotocol/sdk": SDK_VERSION,
|
|
107
|
+
zod: "^3.23.8"
|
|
108
|
+
};
|
|
109
|
+
return { dependencies, devDependencies: {} };
|
|
110
|
+
}
|
|
111
|
+
var TOOLS_FILE = `import { z } from "zod";
|
|
112
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
113
|
+
|
|
114
|
+
/** Register example tools on the server. Add your own here. */
|
|
115
|
+
export function registerTools(server: McpServer): void {
|
|
116
|
+
server.registerTool(
|
|
117
|
+
"echo",
|
|
118
|
+
{
|
|
119
|
+
title: "Echo",
|
|
120
|
+
description: "Echo back the provided message",
|
|
121
|
+
inputSchema: { message: z.string().describe("Message to echo back") },
|
|
122
|
+
},
|
|
123
|
+
async ({ message }) => ({
|
|
124
|
+
content: [{ type: "text", text: \`Echo: \${message}\` }],
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
server.registerTool(
|
|
129
|
+
"add",
|
|
130
|
+
{
|
|
131
|
+
title: "Add",
|
|
132
|
+
description: "Add two numbers",
|
|
133
|
+
inputSchema: { a: z.number(), b: z.number() },
|
|
134
|
+
},
|
|
135
|
+
async ({ a, b }) => ({
|
|
136
|
+
content: [{ type: "text", text: String(a + b) }],
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
function resourcesFile(name) {
|
|
142
|
+
return `import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
143
|
+
|
|
144
|
+
/** Register example resources on the server. Add your own here. */
|
|
145
|
+
export function registerResources(server: McpServer): void {
|
|
146
|
+
server.registerResource(
|
|
147
|
+
"app-config",
|
|
148
|
+
"config://app",
|
|
149
|
+
{
|
|
150
|
+
title: "App Config",
|
|
151
|
+
description: "Static application configuration",
|
|
152
|
+
mimeType: "application/json",
|
|
153
|
+
},
|
|
154
|
+
async (uri) => ({
|
|
155
|
+
contents: [
|
|
156
|
+
{
|
|
157
|
+
uri: uri.href,
|
|
158
|
+
mimeType: "application/json",
|
|
159
|
+
text: JSON.stringify({ name: ${JSON.stringify(name)}, env: "development" }, null, 2),
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
var PROMPTS_FILE = `import { z } from "zod";
|
|
168
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
169
|
+
|
|
170
|
+
/** Register example prompts on the server. Add your own here. */
|
|
171
|
+
export function registerPrompts(server: McpServer): void {
|
|
172
|
+
server.registerPrompt(
|
|
173
|
+
"greeting",
|
|
174
|
+
{
|
|
175
|
+
title: "Greeting",
|
|
176
|
+
description: "Generate a friendly greeting for someone",
|
|
177
|
+
argsSchema: { name: z.string().describe("Person to greet") },
|
|
178
|
+
},
|
|
179
|
+
({ name }) => ({
|
|
180
|
+
messages: [
|
|
181
|
+
{
|
|
182
|
+
role: "user",
|
|
183
|
+
content: { type: "text", text: \`Write a short, friendly greeting for \${name}.\` },
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
`;
|
|
190
|
+
function serverEntry(opts) {
|
|
191
|
+
const imports = [];
|
|
192
|
+
const calls = [];
|
|
193
|
+
if (opts.features.includes("tools")) {
|
|
194
|
+
imports.push(`import { registerTools } from "./tools.js";`);
|
|
195
|
+
calls.push(" registerTools(server);");
|
|
196
|
+
}
|
|
197
|
+
if (opts.features.includes("resources")) {
|
|
198
|
+
imports.push(`import { registerResources } from "./resources.js";`);
|
|
199
|
+
calls.push(" registerResources(server);");
|
|
200
|
+
}
|
|
201
|
+
if (opts.features.includes("prompts")) {
|
|
202
|
+
imports.push(`import { registerPrompts } from "./prompts.js";`);
|
|
203
|
+
calls.push(" registerPrompts(server);");
|
|
204
|
+
}
|
|
205
|
+
return `import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
206
|
+
${imports.join("\n")}
|
|
207
|
+
|
|
208
|
+
/** Create a fully configured MCP server instance. */
|
|
209
|
+
export function createServer(): McpServer {
|
|
210
|
+
const server = new McpServer({
|
|
211
|
+
name: ${JSON.stringify(opts.name)},
|
|
212
|
+
version: "0.1.0",
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
${calls.join("\n")}
|
|
216
|
+
|
|
217
|
+
return server;
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
function indexDispatcher(transport) {
|
|
222
|
+
if (transport === "stdio") {
|
|
223
|
+
return `import { serve } from "@ubuligan/server";
|
|
224
|
+
import { createServer } from "./server.js";
|
|
225
|
+
|
|
226
|
+
serve(createServer(), { transport: "stdio", logger: "info" }).catch((error) => {
|
|
227
|
+
console.error(error);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
});
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
232
|
+
if (transport === "http") {
|
|
233
|
+
return `import { serve } from "@ubuligan/server";
|
|
234
|
+
import { createServer } from "./server.js";
|
|
235
|
+
|
|
236
|
+
serve(createServer, {
|
|
237
|
+
transport: "http",
|
|
238
|
+
port: Number(process.env.PORT ?? 3000),
|
|
239
|
+
logger: "info",
|
|
240
|
+
}).catch((error) => {
|
|
241
|
+
console.error(error);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
});
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
return `import { serve } from "@ubuligan/server";
|
|
247
|
+
import { createServer } from "./server.js";
|
|
248
|
+
|
|
249
|
+
const mode = process.env.MCP_TRANSPORT ?? process.argv[2] ?? "stdio";
|
|
250
|
+
|
|
251
|
+
serve(mode === "http" ? createServer : createServer(), {
|
|
252
|
+
transport: mode === "http" ? "http" : "stdio",
|
|
253
|
+
port: Number(process.env.PORT ?? 3000),
|
|
254
|
+
logger: "info",
|
|
255
|
+
}).catch((error) => {
|
|
256
|
+
console.error(error);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
});
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/templates/project.ts
|
|
263
|
+
function projectFiles(opts) {
|
|
264
|
+
return [
|
|
265
|
+
{ path: "package.json", contents: packageJson(opts) },
|
|
266
|
+
{ path: "tsconfig.json", contents: TSCONFIG },
|
|
267
|
+
{ path: "README.md", contents: readme(opts) },
|
|
268
|
+
{ path: ".gitignore", contents: GITIGNORE }
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
function packageJson(opts) {
|
|
272
|
+
const hasServer = opts.template === "server" || opts.template === "both";
|
|
273
|
+
const hasClient = opts.template === "client" || opts.template === "both";
|
|
274
|
+
let dependencies = {};
|
|
275
|
+
let devDependencies = {
|
|
276
|
+
"@types/node": "^22.10.0",
|
|
277
|
+
tsx: "^4.19.2",
|
|
278
|
+
typescript: "^5.7.2"
|
|
279
|
+
};
|
|
280
|
+
if (hasServer) {
|
|
281
|
+
const sd = serverDeps(opts.transport);
|
|
282
|
+
dependencies = { ...dependencies, ...sd.dependencies };
|
|
283
|
+
devDependencies = { ...devDependencies, ...sd.devDependencies };
|
|
284
|
+
}
|
|
285
|
+
if (hasClient) {
|
|
286
|
+
dependencies = { ...dependencies, ...clientDeps() };
|
|
287
|
+
}
|
|
288
|
+
const scripts = {
|
|
289
|
+
build: "tsc",
|
|
290
|
+
typecheck: "tsc --noEmit"
|
|
291
|
+
};
|
|
292
|
+
if (hasServer) {
|
|
293
|
+
scripts.dev = "tsx watch src/index.ts";
|
|
294
|
+
scripts.start = "node dist/index.js";
|
|
295
|
+
}
|
|
296
|
+
if (opts.template === "both") {
|
|
297
|
+
scripts["demo:client"] = "tsx scripts/demo-client.ts";
|
|
298
|
+
}
|
|
299
|
+
if (opts.template === "client") {
|
|
300
|
+
scripts.dev = "tsx watch src/index.ts";
|
|
301
|
+
scripts.start = "node dist/index.js";
|
|
302
|
+
}
|
|
303
|
+
return JSON.stringify(
|
|
304
|
+
{
|
|
305
|
+
name: opts.name,
|
|
306
|
+
version: "0.1.0",
|
|
307
|
+
private: true,
|
|
308
|
+
type: "module",
|
|
309
|
+
scripts,
|
|
310
|
+
dependencies: sortKeys(dependencies),
|
|
311
|
+
devDependencies: sortKeys(devDependencies)
|
|
312
|
+
},
|
|
313
|
+
null,
|
|
314
|
+
2
|
|
315
|
+
) + "\n";
|
|
316
|
+
}
|
|
317
|
+
function sortKeys(obj) {
|
|
318
|
+
return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));
|
|
319
|
+
}
|
|
320
|
+
var TSCONFIG = `{
|
|
321
|
+
"compilerOptions": {
|
|
322
|
+
"target": "ES2022",
|
|
323
|
+
"module": "NodeNext",
|
|
324
|
+
"moduleResolution": "NodeNext",
|
|
325
|
+
"lib": ["ES2022"],
|
|
326
|
+
"outDir": "dist",
|
|
327
|
+
"rootDir": "src",
|
|
328
|
+
"strict": true,
|
|
329
|
+
"esModuleInterop": true,
|
|
330
|
+
"skipLibCheck": true,
|
|
331
|
+
"resolveJsonModule": true,
|
|
332
|
+
"declaration": true,
|
|
333
|
+
"sourceMap": true,
|
|
334
|
+
"forceConsistentCasingInFileNames": true
|
|
335
|
+
},
|
|
336
|
+
"include": ["src"]
|
|
337
|
+
}
|
|
338
|
+
`;
|
|
339
|
+
var GITIGNORE = `node_modules/
|
|
340
|
+
dist/
|
|
341
|
+
*.log
|
|
342
|
+
.env
|
|
343
|
+
.DS_Store
|
|
344
|
+
`;
|
|
345
|
+
function readme(opts) {
|
|
346
|
+
const lines = [`# ${opts.name}`, "", "Generated with `create-mcp-app`.", ""];
|
|
347
|
+
lines.push("## Setup", "", "```bash", "npm install", "npm run build", "```", "");
|
|
348
|
+
if (opts.template !== "client") {
|
|
349
|
+
lines.push("## Run the server", "");
|
|
350
|
+
if (opts.transport === "stdio" || opts.transport === "both") {
|
|
351
|
+
lines.push("Over stdio (Claude Desktop / local clients):", "", "```bash", "npm start", "```", "");
|
|
352
|
+
}
|
|
353
|
+
if (opts.transport === "http" || opts.transport === "both") {
|
|
354
|
+
const cmd = opts.transport === "both" ? "MCP_TRANSPORT=http npm start" : "npm start";
|
|
355
|
+
lines.push("Over Streamable HTTP:", "", "```bash", cmd, "# -> http://localhost:3000/mcp", "```", "");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (opts.template === "client") {
|
|
359
|
+
lines.push(
|
|
360
|
+
"## Run the client",
|
|
361
|
+
"",
|
|
362
|
+
"```bash",
|
|
363
|
+
"MCP_URL=http://localhost:3000/mcp npm start",
|
|
364
|
+
'# or: MCP_STDIO="node path/to/server.js" npm start',
|
|
365
|
+
"```",
|
|
366
|
+
""
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
if (opts.template === "both") {
|
|
370
|
+
lines.push("## Demo client", "", "```bash", "npm run build && npm run demo:client", "```", "");
|
|
371
|
+
}
|
|
372
|
+
lines.push(
|
|
373
|
+
"## Generate a type-safe SDK",
|
|
374
|
+
"",
|
|
375
|
+
"Point `mcp-codegen` at this server to get a fully typed client:",
|
|
376
|
+
"",
|
|
377
|
+
"```bash",
|
|
378
|
+
'npx @ubuligan/codegen "node dist/index.js" -o sdk.ts',
|
|
379
|
+
"```",
|
|
380
|
+
""
|
|
381
|
+
);
|
|
382
|
+
return lines.join("\n");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/scaffold.ts
|
|
386
|
+
function buildFiles(opts) {
|
|
387
|
+
const files = [...projectFiles(opts)];
|
|
388
|
+
if (opts.template === "server") {
|
|
389
|
+
files.push(...serverFiles(opts));
|
|
390
|
+
} else if (opts.template === "client") {
|
|
391
|
+
files.push(...clientFiles(opts, true));
|
|
392
|
+
} else {
|
|
393
|
+
files.push(...serverFiles(opts));
|
|
394
|
+
files.push(...clientFiles(opts, false));
|
|
395
|
+
}
|
|
396
|
+
return files;
|
|
397
|
+
}
|
|
398
|
+
async function isDirEmpty(dir) {
|
|
399
|
+
try {
|
|
400
|
+
const entries = await readdir(dir);
|
|
401
|
+
return entries.length === 0;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (error.code === "ENOENT") return true;
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async function scaffold(opts) {
|
|
408
|
+
const root = resolve(process.cwd(), opts.dir);
|
|
409
|
+
const files = buildFiles(opts);
|
|
410
|
+
for (const file of files) {
|
|
411
|
+
const fullPath = join(root, file.path);
|
|
412
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
413
|
+
await writeFile(fullPath, file.contents, "utf8");
|
|
414
|
+
}
|
|
415
|
+
return root;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/index.ts
|
|
419
|
+
var program = new Command();
|
|
420
|
+
program.name("create-mcp-app").description("Scaffold a working MCP server + client project").argument("[dir]", "Target directory").option("-t, --template <kind>", "server | client | both").option("--transport <kind>", "stdio | http | both").option("--features <list>", "comma list: tools,resources,prompts").option("-y, --yes", "Skip prompts and use defaults", false).action(async (dir, flags) => {
|
|
421
|
+
await run(dir, flags);
|
|
422
|
+
});
|
|
423
|
+
async function run(dirArg, flags) {
|
|
424
|
+
p.intro(pc.bgCyan(pc.black(" create-mcp-app ")));
|
|
425
|
+
const dir = dirArg ?? (flags.yes ? "my-mcp-app" : await ask.dir());
|
|
426
|
+
const name = sanitizeName(basename(dir));
|
|
427
|
+
const template = flags.template ?? (flags.yes ? "both" : await ask.template());
|
|
428
|
+
const transport = flags.transport ?? (flags.yes ? "both" : template === "client" ? "stdio" : await ask.transport());
|
|
429
|
+
const features = parseFeatures(flags.features) ?? (flags.yes ? ALL_FEATURES : await ask.features(template));
|
|
430
|
+
const opts = { dir, name, template, transport, features };
|
|
431
|
+
if (!await isDirEmpty(dir)) {
|
|
432
|
+
const proceed = flags.yes ? true : await p.confirm({ message: `Directory ${pc.yellow(dir)} is not empty. Continue?`, initialValue: false });
|
|
433
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
434
|
+
p.cancel("Aborted.");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const spin = p.spinner();
|
|
439
|
+
spin.start("Creating project");
|
|
440
|
+
const root = await scaffold(opts);
|
|
441
|
+
spin.stop("Project created");
|
|
442
|
+
p.note(
|
|
443
|
+
[
|
|
444
|
+
`cd ${dir}`,
|
|
445
|
+
"npm install",
|
|
446
|
+
"npm run build",
|
|
447
|
+
template === "both" ? "npm run demo:client" : template === "client" ? "MCP_URL=... npm start" : "npm start"
|
|
448
|
+
].join("\n"),
|
|
449
|
+
"Next steps"
|
|
450
|
+
);
|
|
451
|
+
p.outro(pc.green(`Done \u2192 ${root}`));
|
|
452
|
+
}
|
|
453
|
+
var ALL_FEATURES = ["tools", "resources", "prompts"];
|
|
454
|
+
var ask = {
|
|
455
|
+
async dir() {
|
|
456
|
+
const value = await p.text({
|
|
457
|
+
message: "Project directory?",
|
|
458
|
+
placeholder: "my-mcp-app",
|
|
459
|
+
defaultValue: "my-mcp-app"
|
|
460
|
+
});
|
|
461
|
+
return guard(value, "my-mcp-app");
|
|
462
|
+
},
|
|
463
|
+
async template() {
|
|
464
|
+
const value = await p.select({
|
|
465
|
+
message: "What do you want to scaffold?",
|
|
466
|
+
options: [
|
|
467
|
+
{ value: "both", label: "Server + client demo" },
|
|
468
|
+
{ value: "server", label: "MCP server only" },
|
|
469
|
+
{ value: "client", label: "MCP client only" }
|
|
470
|
+
],
|
|
471
|
+
initialValue: "both"
|
|
472
|
+
});
|
|
473
|
+
return guard(value, "both");
|
|
474
|
+
},
|
|
475
|
+
async transport() {
|
|
476
|
+
const value = await p.select({
|
|
477
|
+
message: "Which transport(s)?",
|
|
478
|
+
options: [
|
|
479
|
+
{ value: "both", label: "stdio + HTTP" },
|
|
480
|
+
{ value: "stdio", label: "stdio (local)" },
|
|
481
|
+
{ value: "http", label: "Streamable HTTP (remote)" }
|
|
482
|
+
],
|
|
483
|
+
initialValue: "both"
|
|
484
|
+
});
|
|
485
|
+
return guard(value, "both");
|
|
486
|
+
},
|
|
487
|
+
async features(template) {
|
|
488
|
+
if (template === "client") return ALL_FEATURES;
|
|
489
|
+
const value = await p.multiselect({
|
|
490
|
+
message: "Which server features?",
|
|
491
|
+
options: [
|
|
492
|
+
{ value: "tools", label: "Tools" },
|
|
493
|
+
{ value: "resources", label: "Resources" },
|
|
494
|
+
{ value: "prompts", label: "Prompts" }
|
|
495
|
+
],
|
|
496
|
+
initialValues: ALL_FEATURES,
|
|
497
|
+
required: false
|
|
498
|
+
});
|
|
499
|
+
if (p.isCancel(value) || value.length === 0) return ALL_FEATURES;
|
|
500
|
+
return value;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
function guard(value, fallback) {
|
|
504
|
+
if (p.isCancel(value)) {
|
|
505
|
+
p.cancel("Aborted.");
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
return value || fallback;
|
|
509
|
+
}
|
|
510
|
+
function parseFeatures(raw) {
|
|
511
|
+
if (!raw) return void 0;
|
|
512
|
+
const valid = new Set(ALL_FEATURES);
|
|
513
|
+
const parsed = raw.split(",").map((s) => s.trim()).filter((s) => valid.has(s));
|
|
514
|
+
return parsed.length > 0 ? parsed : ALL_FEATURES;
|
|
515
|
+
}
|
|
516
|
+
function sanitizeName(raw) {
|
|
517
|
+
const cleaned = raw.toLowerCase().replace(/[^a-z0-9-~._]/g, "-").replace(/^[-_.]+/, "").replace(/-+/g, "-");
|
|
518
|
+
return cleaned || "mcp-app";
|
|
519
|
+
}
|
|
520
|
+
program.parseAsync();
|
|
521
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/scaffold.ts","../src/templates/client.ts","../src/templates/server.ts","../src/templates/project.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { basename } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport { isDirEmpty, scaffold } from \"./scaffold.js\";\nimport type { Feature, ScaffoldOptions, TemplateKind, TransportKind } from \"./types.js\";\n\ninterface CliFlags {\n template?: TemplateKind;\n transport?: TransportKind;\n features?: string;\n yes?: boolean;\n}\n\nconst program = new Command();\nprogram\n .name(\"create-mcp-app\")\n .description(\"Scaffold a working MCP server + client project\")\n .argument(\"[dir]\", \"Target directory\")\n .option(\"-t, --template <kind>\", \"server | client | both\")\n .option(\"--transport <kind>\", \"stdio | http | both\")\n .option(\"--features <list>\", \"comma list: tools,resources,prompts\")\n .option(\"-y, --yes\", \"Skip prompts and use defaults\", false)\n .action(async (dir: string | undefined, flags: CliFlags) => {\n await run(dir, flags);\n });\n\nasync function run(dirArg: string | undefined, flags: CliFlags): Promise<void> {\n p.intro(pc.bgCyan(pc.black(\" create-mcp-app \")));\n\n const dir = dirArg ?? (flags.yes ? \"my-mcp-app\" : await ask.dir());\n const name = sanitizeName(basename(dir));\n\n const template = flags.template ?? (flags.yes ? \"both\" : await ask.template());\n const transport =\n flags.transport ?? (flags.yes ? \"both\" : template === \"client\" ? \"stdio\" : await ask.transport());\n const features = parseFeatures(flags.features) ?? (flags.yes ? ALL_FEATURES : await ask.features(template));\n\n const opts: ScaffoldOptions = { dir, name, template, transport, features };\n\n if (!(await isDirEmpty(dir))) {\n const proceed = flags.yes\n ? true\n : await p.confirm({ message: `Directory ${pc.yellow(dir)} is not empty. Continue?`, initialValue: false });\n if (p.isCancel(proceed) || !proceed) {\n p.cancel(\"Aborted.\");\n process.exit(1);\n }\n }\n\n const spin = p.spinner();\n spin.start(\"Creating project\");\n const root = await scaffold(opts);\n spin.stop(\"Project created\");\n\n p.note(\n [\n `cd ${dir}`,\n \"npm install\",\n \"npm run build\",\n template === \"both\" ? \"npm run demo:client\" : template === \"client\" ? \"MCP_URL=... npm start\" : \"npm start\",\n ].join(\"\\n\"),\n \"Next steps\",\n );\n p.outro(pc.green(`Done → ${root}`));\n}\n\nconst ALL_FEATURES: Feature[] = [\"tools\", \"resources\", \"prompts\"];\n\nconst ask = {\n async dir(): Promise<string> {\n const value = await p.text({\n message: \"Project directory?\",\n placeholder: \"my-mcp-app\",\n defaultValue: \"my-mcp-app\",\n });\n return guard(value, \"my-mcp-app\");\n },\n async template(): Promise<TemplateKind> {\n const value = await p.select({\n message: \"What do you want to scaffold?\",\n options: [\n { value: \"both\", label: \"Server + client demo\" },\n { value: \"server\", label: \"MCP server only\" },\n { value: \"client\", label: \"MCP client only\" },\n ],\n initialValue: \"both\",\n });\n return guard(value, \"both\") as TemplateKind;\n },\n async transport(): Promise<TransportKind> {\n const value = await p.select({\n message: \"Which transport(s)?\",\n options: [\n { value: \"both\", label: \"stdio + HTTP\" },\n { value: \"stdio\", label: \"stdio (local)\" },\n { value: \"http\", label: \"Streamable HTTP (remote)\" },\n ],\n initialValue: \"both\",\n });\n return guard(value, \"both\") as TransportKind;\n },\n async features(template: TemplateKind): Promise<Feature[]> {\n if (template === \"client\") return ALL_FEATURES;\n const value = await p.multiselect({\n message: \"Which server features?\",\n options: [\n { value: \"tools\", label: \"Tools\" },\n { value: \"resources\", label: \"Resources\" },\n { value: \"prompts\", label: \"Prompts\" },\n ],\n initialValues: ALL_FEATURES,\n required: false,\n });\n if (p.isCancel(value) || (value as Feature[]).length === 0) return ALL_FEATURES;\n return value as Feature[];\n },\n};\n\nfunction guard<T>(value: T | symbol, fallback: T): T {\n if (p.isCancel(value)) {\n p.cancel(\"Aborted.\");\n process.exit(1);\n }\n return (value as T) || fallback;\n}\n\nfunction parseFeatures(raw?: string): Feature[] | undefined {\n if (!raw) return undefined;\n const valid = new Set(ALL_FEATURES);\n const parsed = raw\n .split(\",\")\n .map((s) => s.trim())\n .filter((s): s is Feature => valid.has(s as Feature));\n return parsed.length > 0 ? parsed : ALL_FEATURES;\n}\n\nfunction sanitizeName(raw: string): string {\n const cleaned = raw\n .toLowerCase()\n .replace(/[^a-z0-9-~._]/g, \"-\")\n .replace(/^[-_.]+/, \"\")\n .replace(/-+/g, \"-\");\n return cleaned || \"mcp-app\";\n}\n\nprogram.parseAsync();\n","import { mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { GeneratedFile, ScaffoldOptions } from \"./types.js\";\nimport { clientFiles } from \"./templates/client.js\";\nimport { projectFiles } from \"./templates/project.js\";\nimport { serverFiles } from \"./templates/server.js\";\n\n/** Assemble the full list of files for the chosen options (pure, testable). */\nexport function buildFiles(opts: ScaffoldOptions): GeneratedFile[] {\n const files: GeneratedFile[] = [...projectFiles(opts)];\n\n if (opts.template === \"server\") {\n files.push(...serverFiles(opts));\n } else if (opts.template === \"client\") {\n files.push(...clientFiles(opts, true));\n } else {\n // both: server at src/, plus a standalone demo client script.\n files.push(...serverFiles(opts));\n files.push(...clientFiles(opts, false));\n }\n\n return files;\n}\n\nexport async function isDirEmpty(dir: string): Promise<boolean> {\n try {\n const entries = await readdir(dir);\n return entries.length === 0;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return true;\n throw error;\n }\n}\n\n/** Write all generated files to disk under `opts.dir`. */\nexport async function scaffold(opts: ScaffoldOptions): Promise<string> {\n const root = resolve(process.cwd(), opts.dir);\n const files = buildFiles(opts);\n\n for (const file of files) {\n const fullPath = join(root, file.path);\n await mkdir(dirname(fullPath), { recursive: true });\n await writeFile(fullPath, file.contents, \"utf8\");\n }\n\n return root;\n}\n","import type { GeneratedFile, ScaffoldOptions } from \"../types.js\";\r\n\r\nconst CLIENT_VERSION = \"^0.1.0\";\r\n\r\n/**\r\n * Files for the client portion. When `prefix` is set (the \"both\" template) the\r\n * client is a standalone demo script that spawns the local server over stdio.\r\n */\r\nexport function clientFiles(opts: ScaffoldOptions, standalone: boolean): GeneratedFile[] {\r\n if (standalone) {\r\n return [{ path: \"src/index.ts\", contents: standaloneClient() }];\r\n }\r\n // \"both\": demo script that talks to the freshly built local server.\r\n return [{ path: \"scripts/demo-client.ts\", contents: demoClient() }];\r\n}\r\n\r\nexport function clientDeps(): Record<string, string> {\r\n return { \"@ubuligan/client\": CLIENT_VERSION };\r\n}\r\n\r\nfunction standaloneClient(): string {\r\n return `import { createMCPClient } from \"@ubuligan/client\";\r\n\r\n/**\r\n * Demo MCP client. Point it at a server with either:\r\n * MCP_URL=http://localhost:3000/mcp (Streamable HTTP)\r\n * MCP_STDIO=\"node ../server/dist/index.js\" (stdio command)\r\n */\r\nasync function main(): Promise<void> {\r\n const url = process.env.MCP_URL;\r\n const stdio = process.env.MCP_STDIO;\r\n\r\n const client = await createMCPClient({\r\n logger: \"info\",\r\n transport: url\r\n ? { type: \"http\", url }\r\n : stdio\r\n ? { type: \"stdio\", command: stdio.split(\" \")[0]!, args: stdio.split(\" \").slice(1) }\r\n : (() => {\r\n throw new Error(\"Set MCP_URL or MCP_STDIO to choose a server\");\r\n })(),\r\n });\r\n\r\n const { tools } = await client.listTools();\r\n console.log(\\`Connected. \\${tools.length} tools available:\\`);\r\n for (const tool of tools) console.log(\\` - \\${tool.name}: \\${tool.description ?? \"\"}\\`);\r\n\r\n await client.close();\r\n}\r\n\r\nmain().catch((error) => {\r\n console.error(error);\r\n process.exit(1);\r\n});\r\n`;\r\n}\r\n\r\nfunction demoClient(): string {\r\n return `import { createMCPClient } from \"@ubuligan/client\";\r\n\r\n/** Spawns the local built server over stdio and exercises it. */\r\nasync function main(): Promise<void> {\r\n const client = await createMCPClient({\r\n logger: \"info\",\r\n transport: { type: \"stdio\", command: \"node\", args: [\"dist/index.js\"] },\r\n });\r\n\r\n const { tools } = await client.listTools();\r\n console.log(\\`Connected. \\${tools.length} tools available:\\`);\r\n for (const tool of tools) console.log(\\` - \\${tool.name}\\`);\r\n\r\n if (tools.some((t) => t.name === \"echo\")) {\r\n const result = await client.callTool(\"echo\", { message: \"hello from the demo client\" });\r\n console.log(\"echo result:\", JSON.stringify(result.content));\r\n }\r\n\r\n await client.close();\r\n}\r\n\r\nmain().catch((error) => {\r\n console.error(error);\r\n process.exit(1);\r\n});\r\n`;\r\n}\r\n","import type { Feature, GeneratedFile, ScaffoldOptions, TransportKind } from \"../types.js\";\r\n\r\nconst SDK_VERSION = \"^1.29.0\";\r\nconst SERVER_VERSION = \"^0.1.0\";\r\n\r\n/** Build all files for the server portion of the project, under `src/`. */\r\nexport function serverFiles(opts: ScaffoldOptions, prefix = \"\"): GeneratedFile[] {\r\n const files: GeneratedFile[] = [];\r\n const p = (rel: string) => `${prefix}${rel}`;\r\n\r\n if (opts.features.includes(\"tools\")) files.push({ path: p(\"src/tools.ts\"), contents: TOOLS_FILE });\r\n if (opts.features.includes(\"resources\"))\r\n files.push({ path: p(\"src/resources.ts\"), contents: resourcesFile(opts.name) });\r\n if (opts.features.includes(\"prompts\")) files.push({ path: p(\"src/prompts.ts\"), contents: PROMPTS_FILE });\r\n\r\n files.push({ path: p(\"src/server.ts\"), contents: serverEntry(opts) });\r\n files.push({ path: p(\"src/index.ts\"), contents: indexDispatcher(opts.transport) });\r\n\r\n return files;\r\n}\r\n\r\n/** Dependency entries the server portion contributes to package.json. */\r\nexport function serverDeps(_transport: TransportKind): {\r\n dependencies: Record<string, string>;\r\n devDependencies: Record<string, string>;\r\n} {\r\n // Boot logic (stdio + Streamable HTTP) lives in @ubuligan/server, so the\r\n // generated project just depends on it — no express/transport boilerplate.\r\n const dependencies: Record<string, string> = {\r\n \"@ubuligan/server\": SERVER_VERSION,\r\n \"@modelcontextprotocol/sdk\": SDK_VERSION,\r\n zod: \"^3.23.8\",\r\n };\r\n return { dependencies, devDependencies: {} };\r\n}\r\n\r\nconst TOOLS_FILE = `import { z } from \"zod\";\r\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\n\r\n/** Register example tools on the server. Add your own here. */\r\nexport function registerTools(server: McpServer): void {\r\n server.registerTool(\r\n \"echo\",\r\n {\r\n title: \"Echo\",\r\n description: \"Echo back the provided message\",\r\n inputSchema: { message: z.string().describe(\"Message to echo back\") },\r\n },\r\n async ({ message }) => ({\r\n content: [{ type: \"text\", text: \\`Echo: \\${message}\\` }],\r\n }),\r\n );\r\n\r\n server.registerTool(\r\n \"add\",\r\n {\r\n title: \"Add\",\r\n description: \"Add two numbers\",\r\n inputSchema: { a: z.number(), b: z.number() },\r\n },\r\n async ({ a, b }) => ({\r\n content: [{ type: \"text\", text: String(a + b) }],\r\n }),\r\n );\r\n}\r\n`;\r\n\r\nfunction resourcesFile(name: string): string {\r\n return `import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\n\r\n/** Register example resources on the server. Add your own here. */\r\nexport function registerResources(server: McpServer): void {\r\n server.registerResource(\r\n \"app-config\",\r\n \"config://app\",\r\n {\r\n title: \"App Config\",\r\n description: \"Static application configuration\",\r\n mimeType: \"application/json\",\r\n },\r\n async (uri) => ({\r\n contents: [\r\n {\r\n uri: uri.href,\r\n mimeType: \"application/json\",\r\n text: JSON.stringify({ name: ${JSON.stringify(name)}, env: \"development\" }, null, 2),\r\n },\r\n ],\r\n }),\r\n );\r\n}\r\n`;\r\n}\r\n\r\nconst PROMPTS_FILE = `import { z } from \"zod\";\r\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\n\r\n/** Register example prompts on the server. Add your own here. */\r\nexport function registerPrompts(server: McpServer): void {\r\n server.registerPrompt(\r\n \"greeting\",\r\n {\r\n title: \"Greeting\",\r\n description: \"Generate a friendly greeting for someone\",\r\n argsSchema: { name: z.string().describe(\"Person to greet\") },\r\n },\r\n ({ name }) => ({\r\n messages: [\r\n {\r\n role: \"user\",\r\n content: { type: \"text\", text: \\`Write a short, friendly greeting for \\${name}.\\` },\r\n },\r\n ],\r\n }),\r\n );\r\n}\r\n`;\r\n\r\nfunction serverEntry(opts: ScaffoldOptions): string {\r\n const imports: string[] = [];\r\n const calls: string[] = [];\r\n if (opts.features.includes(\"tools\")) {\r\n imports.push(`import { registerTools } from \"./tools.js\";`);\r\n calls.push(\" registerTools(server);\");\r\n }\r\n if (opts.features.includes(\"resources\")) {\r\n imports.push(`import { registerResources } from \"./resources.js\";`);\r\n calls.push(\" registerResources(server);\");\r\n }\r\n if (opts.features.includes(\"prompts\")) {\r\n imports.push(`import { registerPrompts } from \"./prompts.js\";`);\r\n calls.push(\" registerPrompts(server);\");\r\n }\r\n\r\n return `import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\n${imports.join(\"\\n\")}\r\n\r\n/** Create a fully configured MCP server instance. */\r\nexport function createServer(): McpServer {\r\n const server = new McpServer({\r\n name: ${JSON.stringify(opts.name)},\r\n version: \"0.1.0\",\r\n });\r\n\r\n${calls.join(\"\\n\")}\r\n\r\n return server;\r\n}\r\n`;\r\n}\r\n\r\nfunction indexDispatcher(transport: TransportKind): string {\r\n // Transport boot is handled by @ubuligan/server's `serve`. For http we pass\r\n // the factory so each request gets a fresh server (stateless mode).\r\n if (transport === \"stdio\") {\r\n return `import { serve } from \"@ubuligan/server\";\r\nimport { createServer } from \"./server.js\";\r\n\r\nserve(createServer(), { transport: \"stdio\", logger: \"info\" }).catch((error) => {\r\n console.error(error);\r\n process.exit(1);\r\n});\r\n`;\r\n }\r\n if (transport === \"http\") {\r\n return `import { serve } from \"@ubuligan/server\";\r\nimport { createServer } from \"./server.js\";\r\n\r\nserve(createServer, {\r\n transport: \"http\",\r\n port: Number(process.env.PORT ?? 3000),\r\n logger: \"info\",\r\n}).catch((error) => {\r\n console.error(error);\r\n process.exit(1);\r\n});\r\n`;\r\n }\r\n // both: choose via MCP_TRANSPORT env or first CLI arg, default stdio.\r\n return `import { serve } from \"@ubuligan/server\";\r\nimport { createServer } from \"./server.js\";\r\n\r\nconst mode = process.env.MCP_TRANSPORT ?? process.argv[2] ?? \"stdio\";\r\n\r\nserve(mode === \"http\" ? createServer : createServer(), {\r\n transport: mode === \"http\" ? \"http\" : \"stdio\",\r\n port: Number(process.env.PORT ?? 3000),\r\n logger: \"info\",\r\n}).catch((error) => {\r\n console.error(error);\r\n process.exit(1);\r\n});\r\n`;\r\n}\r\n\r\nexport type { Feature };\r\n","import type { GeneratedFile, ScaffoldOptions } from \"../types.js\";\r\nimport { clientDeps } from \"./client.js\";\r\nimport { serverDeps } from \"./server.js\";\r\n\r\n/** Generate package.json, tsconfig.json, README.md and .gitignore. */\r\nexport function projectFiles(opts: ScaffoldOptions): GeneratedFile[] {\r\n return [\r\n { path: \"package.json\", contents: packageJson(opts) },\r\n { path: \"tsconfig.json\", contents: TSCONFIG },\r\n { path: \"README.md\", contents: readme(opts) },\r\n { path: \".gitignore\", contents: GITIGNORE },\r\n ];\r\n}\r\n\r\nfunction packageJson(opts: ScaffoldOptions): string {\r\n const hasServer = opts.template === \"server\" || opts.template === \"both\";\r\n const hasClient = opts.template === \"client\" || opts.template === \"both\";\r\n\r\n let dependencies: Record<string, string> = {};\r\n let devDependencies: Record<string, string> = {\r\n \"@types/node\": \"^22.10.0\",\r\n tsx: \"^4.19.2\",\r\n typescript: \"^5.7.2\",\r\n };\r\n\r\n if (hasServer) {\r\n const sd = serverDeps(opts.transport);\r\n dependencies = { ...dependencies, ...sd.dependencies };\r\n devDependencies = { ...devDependencies, ...sd.devDependencies };\r\n }\r\n if (hasClient) {\r\n dependencies = { ...dependencies, ...clientDeps() };\r\n }\r\n\r\n const scripts: Record<string, string> = {\r\n build: \"tsc\",\r\n typecheck: \"tsc --noEmit\",\r\n };\r\n if (hasServer) {\r\n scripts.dev = \"tsx watch src/index.ts\";\r\n scripts.start = \"node dist/index.js\";\r\n }\r\n if (opts.template === \"both\") {\r\n scripts[\"demo:client\"] = \"tsx scripts/demo-client.ts\";\r\n }\r\n if (opts.template === \"client\") {\r\n scripts.dev = \"tsx watch src/index.ts\";\r\n scripts.start = \"node dist/index.js\";\r\n }\r\n\r\n return (\r\n JSON.stringify(\r\n {\r\n name: opts.name,\r\n version: \"0.1.0\",\r\n private: true,\r\n type: \"module\",\r\n scripts,\r\n dependencies: sortKeys(dependencies),\r\n devDependencies: sortKeys(devDependencies),\r\n },\r\n null,\r\n 2,\r\n ) + \"\\n\"\r\n );\r\n}\r\n\r\nfunction sortKeys(obj: Record<string, string>): Record<string, string> {\r\n return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));\r\n}\r\n\r\nconst TSCONFIG = `{\r\n \"compilerOptions\": {\r\n \"target\": \"ES2022\",\r\n \"module\": \"NodeNext\",\r\n \"moduleResolution\": \"NodeNext\",\r\n \"lib\": [\"ES2022\"],\r\n \"outDir\": \"dist\",\r\n \"rootDir\": \"src\",\r\n \"strict\": true,\r\n \"esModuleInterop\": true,\r\n \"skipLibCheck\": true,\r\n \"resolveJsonModule\": true,\r\n \"declaration\": true,\r\n \"sourceMap\": true,\r\n \"forceConsistentCasingInFileNames\": true\r\n },\r\n \"include\": [\"src\"]\r\n}\r\n`;\r\n\r\nconst GITIGNORE = `node_modules/\r\ndist/\r\n*.log\r\n.env\r\n.DS_Store\r\n`;\r\n\r\nfunction readme(opts: ScaffoldOptions): string {\r\n const lines: string[] = [`# ${opts.name}`, \"\", \"Generated with `create-mcp-app`.\", \"\"];\r\n\r\n lines.push(\"## Setup\", \"\", \"```bash\", \"npm install\", \"npm run build\", \"```\", \"\");\r\n\r\n if (opts.template !== \"client\") {\r\n lines.push(\"## Run the server\", \"\");\r\n if (opts.transport === \"stdio\" || opts.transport === \"both\") {\r\n lines.push(\"Over stdio (Claude Desktop / local clients):\", \"\", \"```bash\", \"npm start\", \"```\", \"\");\r\n }\r\n if (opts.transport === \"http\" || opts.transport === \"both\") {\r\n const cmd = opts.transport === \"both\" ? \"MCP_TRANSPORT=http npm start\" : \"npm start\";\r\n lines.push(\"Over Streamable HTTP:\", \"\", \"```bash\", cmd, \"# -> http://localhost:3000/mcp\", \"```\", \"\");\r\n }\r\n }\r\n\r\n if (opts.template === \"client\") {\r\n lines.push(\r\n \"## Run the client\",\r\n \"\",\r\n \"```bash\",\r\n 'MCP_URL=http://localhost:3000/mcp npm start',\r\n '# or: MCP_STDIO=\"node path/to/server.js\" npm start',\r\n \"```\",\r\n \"\",\r\n );\r\n }\r\n\r\n if (opts.template === \"both\") {\r\n lines.push(\"## Demo client\", \"\", \"```bash\", \"npm run build && npm run demo:client\", \"```\", \"\");\r\n }\r\n\r\n lines.push(\r\n \"## Generate a type-safe SDK\",\r\n \"\",\r\n \"Point `mcp-codegen` at this server to get a fully typed client:\",\r\n \"\",\r\n \"```bash\",\r\n 'npx @ubuligan/codegen \"node dist/index.js\" -o sdk.ts',\r\n \"```\",\r\n \"\",\r\n );\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n"],"mappings":"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","names":["p"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ubuligan/create-mcp-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a working MCP server + client project (TypeScript, stdio + HTTP)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-mcp-app": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@clack/prompts": "^0.8.2",
|
|
16
|
+
"commander": "^12.1.0",
|
|
17
|
+
"picocolors": "^1.1.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^5.7.2",
|
|
21
|
+
"tsup": "^8.3.5"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"author": "jsznpm",
|
|
27
|
+
"homepage": "https://github.com/jsznpm/create-mcp-toolkit#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/jsznpm/create-mcp-toolkit/issues"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/jsznpm/create-mcp-toolkit.git",
|
|
34
|
+
"directory": "packages/create-mcp-app"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"mcp",
|
|
38
|
+
"model-context-protocol",
|
|
39
|
+
"ai",
|
|
40
|
+
"llm",
|
|
41
|
+
"scaffold",
|
|
42
|
+
"cli",
|
|
43
|
+
"create",
|
|
44
|
+
"generator",
|
|
45
|
+
"template"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"typecheck": "tsc --noEmit"
|
|
50
|
+
}
|
|
51
|
+
}
|