@dwizi/create-dzx 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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # create-dzx
2
+
3
+ Scaffold a new dzx MCP server project.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx create-dzx
9
+ ```
10
+
11
+ Or with options:
12
+
13
+ ```bash
14
+ npx create-dzx my-agent --template basic --runtime node
15
+ ```
16
+
17
+ ## Options
18
+
19
+ - `--dir <path>` - Target directory (default: `my-agent`)
20
+ - `--template <basic|tools-only|full>` - Template to scaffold
21
+ - `--runtime <node|deno>` - Runtime to configure
22
+ - `--install` - Install dependencies after scaffolding
23
+ - `--no-install` - Skip dependency installation
24
+ - `--yes` - Accept defaults
25
+ - `--force` - Overwrite existing files
26
+
27
+ ## Templates
28
+
29
+ - **basic** - Includes tools, resources, and prompts
30
+ - **tools-only** - Minimal template with tools only
31
+ - **full** - Full-featured template with all features
32
+
33
+ ## Package Manager Detection
34
+
35
+ `create-dzx` automatically detects your package manager by checking for lockfiles:
36
+ - `pnpm-lock.yaml` → pnpm
37
+ - `package-lock.json` → npm
38
+ - `yarn.lock` → yarn
39
+ - `bun.lockb` → bun
40
+
41
+ If no lockfile is found, it defaults to `pnpm`.
package/bin/index.js ADDED
@@ -0,0 +1,524 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import * as clack from "@clack/prompts";
7
+
8
+ const TEMPLATES = ["basic", "tools-only", "full"];
9
+ const RUNTIMES = ["node", "deno"];
10
+
11
+ /**
12
+ * Normalize a string into a filesystem-safe slug.
13
+ */
14
+ function slugify(value) {
15
+ return value
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9-_]/g, "-")
18
+ .replace(/--+/g, "-")
19
+ .replace(/^-+/, "")
20
+ .replace(/-+$/, "");
21
+ }
22
+
23
+ /**
24
+ * Parse CLI argv into a simple key/value map.
25
+ */
26
+ function parseArgs(argv) {
27
+ const args = { positional: [] };
28
+ for (let i = 0; i < argv.length; i += 1) {
29
+ const arg = argv[i];
30
+ if (!arg.startsWith("--")) {
31
+ args.positional.push(arg);
32
+ continue;
33
+ }
34
+ const key = arg.slice(2);
35
+ const next = argv[i + 1];
36
+ if (next && !next.startsWith("--")) {
37
+ args[key] = next;
38
+ i += 1;
39
+ } else {
40
+ args[key] = true;
41
+ }
42
+ }
43
+ return args;
44
+ }
45
+
46
+ /**
47
+ * Return true when the directory is missing or empty.
48
+ */
49
+ function isEmptyDir(dir) {
50
+ if (!fs.existsSync(dir)) return true;
51
+ return fs.readdirSync(dir).length === 0;
52
+ }
53
+
54
+ /**
55
+ * Recursively list files relative to the base directory.
56
+ */
57
+ function listFiles(dir, baseDir = dir) {
58
+ if (!fs.existsSync(dir)) return [];
59
+ const entries = fs
60
+ .readdirSync(dir, { withFileTypes: true })
61
+ .sort((a, b) => a.name.localeCompare(b.name));
62
+ const files = [];
63
+ for (const entry of entries) {
64
+ const entryPath = path.join(dir, entry.name);
65
+ if (entry.isDirectory()) {
66
+ files.push(...listFiles(entryPath, baseDir));
67
+ } else {
68
+ files.push(path.relative(baseDir, entryPath));
69
+ }
70
+ }
71
+ return files;
72
+ }
73
+
74
+ /**
75
+ * Copy a directory recursively.
76
+ */
77
+ function copyDir(src, dest) {
78
+ fs.mkdirSync(dest, { recursive: true });
79
+ const entries = fs.readdirSync(src, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ const srcPath = path.join(src, entry.name);
82
+ const destPath = path.join(dest, entry.name);
83
+ if (entry.isDirectory()) {
84
+ copyDir(srcPath, destPath);
85
+ } else {
86
+ fs.copyFileSync(srcPath, destPath);
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Detect package manager from lockfiles or environment.
93
+ */
94
+ function detectPackageManager(cwd) {
95
+ const lockfiles = {
96
+ "pnpm-lock.yaml": "pnpm",
97
+ "package-lock.json": "npm",
98
+ "yarn.lock": "yarn",
99
+ "bun.lockb": "bun",
100
+ };
101
+
102
+ for (const [lockfile, pm] of Object.entries(lockfiles)) {
103
+ if (fs.existsSync(path.join(cwd, lockfile))) {
104
+ return pm;
105
+ }
106
+ }
107
+
108
+ // Check parent directories (monorepo context)
109
+ let current = cwd;
110
+ for (let i = 0; i < 5; i++) {
111
+ const parent = path.dirname(current);
112
+ if (parent === current) break;
113
+ for (const [lockfile, pm] of Object.entries(lockfiles)) {
114
+ if (fs.existsSync(path.join(parent, lockfile))) {
115
+ return pm;
116
+ }
117
+ }
118
+ current = parent;
119
+ }
120
+
121
+ // Default to pnpm
122
+ return "pnpm";
123
+ }
124
+
125
+ /**
126
+ * Get install command for a package manager.
127
+ */
128
+ function getInstallCommand(pm) {
129
+ return `${pm} install`;
130
+ }
131
+
132
+ /**
133
+ * Run a shell command in a given working directory.
134
+ */
135
+ async function runCommand(command, cwd) {
136
+ const { spawn } = await import("node:child_process");
137
+ return new Promise((resolve, reject) => {
138
+ const child = spawn(command, { cwd, stdio: "inherit", shell: true });
139
+ child.on("error", (err) => reject(err));
140
+ child.on("exit", (code) => {
141
+ if (code === 0) resolve();
142
+ else reject(new Error(`Command failed (${code}): ${command}`));
143
+ });
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Resolve the templates directory for create-dzx.
149
+ */
150
+ function resolveTemplatesRoot() {
151
+ const here = path.dirname(fileURLToPath(import.meta.url));
152
+ const localTemplatesRoot = path.resolve(here, "..", "templates");
153
+ if (fs.existsSync(localTemplatesRoot)) return localTemplatesRoot;
154
+ // Fallback for when installed as npm package
155
+ return path.resolve(process.cwd(), "node_modules", "create-dzx", "templates");
156
+ }
157
+
158
+ /**
159
+ * Color utilities for terminal output.
160
+ */
161
+ const useColor = Boolean(process.stdout.isTTY);
162
+ function color(code) {
163
+ return (text) => (useColor ? `\x1b[${code}m${text}\x1b[0m` : text);
164
+ }
165
+
166
+ const colorize = {
167
+ green: color("32"),
168
+ cyan: color("36"),
169
+ blue: color("34"),
170
+ gray: color("90"),
171
+ bold: color("1"),
172
+ dim: color("2"),
173
+ };
174
+
175
+ const symbols = {
176
+ check: useColor ? "✔" : "OK",
177
+ step: useColor ? "●" : "*",
178
+ brand: useColor ? "▲" : ">",
179
+ };
180
+
181
+ /**
182
+ * Create a terminal spinner controller.
183
+ */
184
+ function createSpinner(enabled) {
185
+ const frames = ["◐", "◓", "◑", "◒"];
186
+ let timer = null;
187
+ let message = "";
188
+ let frameIndex = 0;
189
+
190
+ const clearLine = () => {
191
+ if (!enabled) return;
192
+ process.stdout.write("\r\x1b[2K");
193
+ };
194
+
195
+ const render = () => {
196
+ if (!enabled) return;
197
+ const frame = frames[frameIndex % frames.length];
198
+ frameIndex += 1;
199
+ process.stdout.write(`\r${colorize.gray(frame)} ${message}`);
200
+ };
201
+
202
+ const start = (nextMessage) => {
203
+ message = nextMessage;
204
+ if (!enabled || timer) return;
205
+ render();
206
+ timer = setInterval(render, 80);
207
+ };
208
+
209
+ const update = (nextMessage) => {
210
+ message = nextMessage;
211
+ if (!enabled || !timer) return;
212
+ render();
213
+ };
214
+
215
+ const pause = () => {
216
+ if (!enabled || !timer) return;
217
+ clearInterval(timer);
218
+ timer = null;
219
+ clearLine();
220
+ };
221
+
222
+ const resume = () => {
223
+ if (!enabled || timer || !message) return;
224
+ render();
225
+ timer = setInterval(render, 80);
226
+ };
227
+
228
+ const stop = () => {
229
+ pause();
230
+ message = "";
231
+ };
232
+
233
+ return { start, update, pause, resume, stop, isEnabled: enabled };
234
+ }
235
+
236
+ /**
237
+ * Print a aligned key/value summary list.
238
+ */
239
+ function printKeyValueList(items) {
240
+ if (items.length === 0) return;
241
+ const maxLabel = items.reduce((max, item) => Math.max(max, item.label.length), 0);
242
+ for (const item of items) {
243
+ const padded = item.label.padEnd(maxLabel);
244
+ // eslint-disable-next-line no-console
245
+ console.log(
246
+ `${colorize.gray(symbols.step)} ${colorize.gray(padded)} : ${colorize.cyan(item.value)}`,
247
+ );
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Get the latest version of @dwizi/dzx from npm registry.
253
+ */
254
+ async function getDzxVersion() {
255
+ try {
256
+ const { promisify } = await import("node:util");
257
+ const { exec } = promisify(await import("node:child_process"));
258
+ const { stdout } = await exec("npm view @dwizi/dzx version", { encoding: "utf8" });
259
+ return stdout.trim();
260
+ } catch {
261
+ // Fallback version if npm query fails
262
+ return "latest";
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Main scaffolding function.
268
+ */
269
+ async function main() {
270
+ const args = parseArgs(process.argv.slice(2));
271
+ const force = Boolean(args.force);
272
+ const isYes = Boolean(args.yes);
273
+ const shouldInstall = args.install
274
+ ? true
275
+ : args["no-install"]
276
+ ? false
277
+ : true; // Default to installing for scaffold mode
278
+
279
+ if (args.help || args.h) {
280
+ // eslint-disable-next-line no-console
281
+ console.log(`
282
+ ${colorize.blue(symbols.brand)} ${colorize.bold("create-dzx")}
283
+
284
+ ${colorize.bold("Usage")} ${colorize.gray("create-dzx [options]")}
285
+
286
+ ${colorize.bold("Options")}
287
+ ${colorize.cyan("--dir <path>".padEnd(20))} ${colorize.gray("target directory (default: my-agent)")}
288
+ ${colorize.cyan("--template <basic|tools-only|full>".padEnd(20))} ${colorize.gray("template to scaffold")}
289
+ ${colorize.cyan("--runtime <node|deno>".padEnd(20))} ${colorize.gray("runtime to configure")}
290
+ ${colorize.cyan("--install".padEnd(20))} ${colorize.gray("install dependencies after scaffolding")}
291
+ ${colorize.cyan("--no-install".padEnd(20))} ${colorize.gray("skip dependency installation")}
292
+ ${colorize.cyan("--yes".padEnd(20))} ${colorize.gray("accept defaults")}
293
+ ${colorize.cyan("--force".padEnd(20))} ${colorize.gray("overwrite existing files")}
294
+ `);
295
+ return;
296
+ }
297
+
298
+ clack.intro("create-dzx");
299
+
300
+ const dirArg = args.dir ?? args.positional[0];
301
+ const defaultDir = "my-agent";
302
+
303
+ let targetDir = path.resolve(process.cwd(), dirArg || defaultDir);
304
+ if (!isYes) {
305
+ const dirResponse = await clack.text({
306
+ message: "Project directory",
307
+ initialValue: dirArg || defaultDir,
308
+ });
309
+ if (clack.isCancel(dirResponse)) {
310
+ clack.cancel("Aborted.");
311
+ process.exit(1);
312
+ }
313
+ targetDir = path.resolve(process.cwd(), dirResponse || defaultDir);
314
+ }
315
+
316
+ let template = args.template || (isYes ? "basic" : undefined);
317
+ if (!template) {
318
+ const templateResponse = await clack.select({
319
+ message: "Template",
320
+ options: [
321
+ { value: "basic", label: "basic" },
322
+ { value: "tools-only", label: "tools-only" },
323
+ { value: "full", label: "full" },
324
+ ],
325
+ initialValue: "basic",
326
+ });
327
+ if (clack.isCancel(templateResponse)) {
328
+ clack.cancel("Aborted.");
329
+ process.exit(1);
330
+ }
331
+ template = templateResponse;
332
+ }
333
+
334
+ let runtime = args.runtime || (isYes ? "node" : undefined);
335
+ if (!runtime) {
336
+ const runtimeResponse = await clack.select({
337
+ message: "Runtime",
338
+ options: [
339
+ { value: "node", label: "node" },
340
+ { value: "deno", label: "deno" },
341
+ ],
342
+ initialValue: "node",
343
+ });
344
+ if (clack.isCancel(runtimeResponse)) {
345
+ clack.cancel("Aborted.");
346
+ process.exit(1);
347
+ }
348
+ runtime = runtimeResponse;
349
+ }
350
+
351
+ template = template ?? "basic";
352
+ runtime = runtime ?? "node";
353
+
354
+ if (!TEMPLATES.includes(template)) {
355
+ throw new Error(`Unknown template: ${template}`);
356
+ }
357
+ if (!RUNTIMES.includes(runtime)) {
358
+ throw new Error(`Unknown runtime: ${runtime}`);
359
+ }
360
+
361
+ if (!force && !isEmptyDir(targetDir)) {
362
+ throw new Error(`Target directory is not empty: ${targetDir}. Use --force to overwrite.`);
363
+ }
364
+
365
+ const templatesRoot = resolveTemplatesRoot();
366
+ const templateDir = path.join(templatesRoot, template);
367
+ if (!fs.existsSync(templateDir)) {
368
+ throw new Error(`Template not found: ${template}`);
369
+ }
370
+
371
+ if (force && !isYes) {
372
+ const confirmation = await clack.confirm({
373
+ message: "This will overwrite existing files. Continue?",
374
+ initialValue: false,
375
+ });
376
+ if (clack.isCancel(confirmation) || confirmation === false) {
377
+ clack.cancel("Aborted.");
378
+ process.exit(1);
379
+ }
380
+ }
381
+
382
+ const spinner = createSpinner(process.stdout.isTTY);
383
+ const stepLabels = [
384
+ "Validating destination",
385
+ "Copying template",
386
+ "Configuring manifest",
387
+ ...(shouldInstall ? ["Installing dependencies"] : []),
388
+ "Finalizing",
389
+ ];
390
+ const stepLabelWidth = stepLabels.reduce((max, label) => Math.max(max, label.length), 0);
391
+ const stepTimes = [];
392
+ let stepStart = Date.now();
393
+ let lastStep = "";
394
+ let spinnerStarted = false;
395
+
396
+ const logStep = (label, ms) => {
397
+ const paddedLabel = label.padEnd(stepLabelWidth);
398
+ const paddedMs = `${ms}ms`.padStart(6);
399
+ const line = `${colorize.cyan(symbols.step)} ${colorize.gray(paddedLabel)} ${colorize.dim(paddedMs)}`;
400
+ if (spinner.isEnabled) {
401
+ spinner.pause();
402
+ // eslint-disable-next-line no-console
403
+ console.log(line);
404
+ spinner.resume();
405
+ } else {
406
+ // eslint-disable-next-line no-console
407
+ console.log(line);
408
+ }
409
+ };
410
+
411
+ const step = (message) => {
412
+ const now = Date.now();
413
+ if (lastStep) {
414
+ const ms = now - stepStart;
415
+ stepTimes.push({ label: lastStep, ms });
416
+ logStep(lastStep, ms);
417
+ }
418
+ lastStep = message;
419
+ stepStart = now;
420
+ if (spinner.isEnabled) {
421
+ if (spinnerStarted) {
422
+ spinner.update(message);
423
+ } else {
424
+ spinner.start(message);
425
+ spinnerStarted = true;
426
+ }
427
+ }
428
+ };
429
+
430
+ const dzxVersion = await getDzxVersion();
431
+ const banner = `${colorize.blue(symbols.brand)} ${colorize.bold("create-dzx")} ${colorize.gray("scaffold")}`;
432
+ // eslint-disable-next-line no-console
433
+ console.log(banner);
434
+
435
+ step("Validating destination");
436
+ if (!force) {
437
+ const templateFiles = listFiles(templateDir);
438
+ const collisions = templateFiles.filter((file) => fs.existsSync(path.join(targetDir, file)));
439
+ if (collisions.length > 0) {
440
+ const preview = collisions
441
+ .slice(0, 8)
442
+ .map((file) => `- ${file}`)
443
+ .join("\n");
444
+ const suffix = collisions.length > 8 ? "\n- ..." : "";
445
+ throw new Error(
446
+ `Refusing to overwrite existing files. Use --force to proceed.\n${preview}${suffix}`,
447
+ );
448
+ }
449
+ }
450
+
451
+ step("Copying template");
452
+ copyDir(templateDir, targetDir);
453
+
454
+ step("Configuring manifest");
455
+ const manifestPath = path.join(targetDir, "mcp.json");
456
+ if (fs.existsSync(manifestPath)) {
457
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
458
+ manifest.name = slugify(path.basename(targetDir));
459
+ manifest.runtime = runtime;
460
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
461
+ }
462
+
463
+ // Update package.json with correct @dwizi/dzx version
464
+ const pkgPath = path.join(targetDir, "package.json");
465
+ if (fs.existsSync(pkgPath)) {
466
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
467
+ if (pkg.dependencies && pkg.dependencies["@dwizi/dzx"]) {
468
+ pkg.dependencies["@dwizi/dzx"] = `^${dzxVersion}`;
469
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
470
+ }
471
+ }
472
+
473
+ if (shouldInstall) {
474
+ step("Installing dependencies");
475
+ if (!fs.existsSync(pkgPath)) {
476
+ if (spinner.isEnabled) spinner.stop();
477
+ throw new Error("Missing package.json in template. Cannot install dependencies.");
478
+ }
479
+ const pm = detectPackageManager(targetDir);
480
+ const installCommand = getInstallCommand(pm);
481
+ try {
482
+ await runCommand(installCommand, targetDir);
483
+ } catch {
484
+ if (spinner.isEnabled) spinner.stop();
485
+ throw new Error(`Dependency installation failed. Run \`${installCommand}\` manually.`);
486
+ }
487
+ }
488
+
489
+ step("Finalizing");
490
+ if (lastStep) {
491
+ const ms = Date.now() - stepStart;
492
+ stepTimes.push({ label: lastStep, ms });
493
+ logStep(lastStep, ms);
494
+ }
495
+ spinner.stop();
496
+
497
+ const totalMs = stepTimes.reduce((sum, item) => sum + item.ms, 0);
498
+ const summaryLines = [
499
+ { label: "dir", value: targetDir },
500
+ { label: "template", value: template },
501
+ { label: "runtime", value: runtime },
502
+ { label: "install", value: shouldInstall ? "yes" : "no" },
503
+ { label: "ready", value: `${totalMs}ms` },
504
+ ];
505
+ // eslint-disable-next-line no-console
506
+ console.log("");
507
+ // eslint-disable-next-line no-console
508
+ console.log(`${colorize.green(symbols.check)} ${colorize.bold("Project ready")}`);
509
+ printKeyValueList(summaryLines);
510
+ const pm = shouldInstall ? detectPackageManager(targetDir) : "pnpm";
511
+ const nextSteps = [
512
+ `cd ${path.basename(targetDir)}`,
513
+ shouldInstall ? "dzx dev" : `${pm} install`,
514
+ shouldInstall ? "" : "dzx dev",
515
+ ].filter(Boolean);
516
+ // eslint-disable-next-line no-console
517
+ console.log(`${colorize.gray("next")} ${colorize.cyan(nextSteps.join(" && "))}`);
518
+ }
519
+
520
+ main().catch((err) => {
521
+ // eslint-disable-next-line no-console
522
+ console.error(err);
523
+ process.exit(1);
524
+ });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@dwizi/create-dzx",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/dwizi/create-dzx.git",
13
+ "directory": "packages/create-dzx"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/dwizi/create-dzx/issues"
17
+ },
18
+ "bin": {
19
+ "create-dzx": "./bin/index.js"
20
+ },
21
+ "files": [
22
+ "bin",
23
+ "templates"
24
+ ],
25
+ "dependencies": {
26
+ "@clack/prompts": "^1.0.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=24"
30
+ },
31
+ "scripts": {
32
+ "build": "node scripts/build.mjs"
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ # Basic Agent
2
+
3
+ This template includes a tool, a resource, and a prompt.
4
+
5
+ ## Commands
6
+ - `pnpm dev`
7
+ - `pnpm inspect`
8
+ - `pnpm build`
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "basic-agent",
3
+ "version": "0.1.0",
4
+ "runtime": "node",
5
+ "entry": "src/server.ts",
6
+ "toolsDir": "tools",
7
+ "resourcesDir": "resources",
8
+ "promptsDir": "prompts",
9
+ "permissions": {
10
+ "network": false,
11
+ "filesystem": { "read": ["./resources", "./prompts"], "write": [] }
12
+ }
13
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "basic-agent",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "dzx dev",
7
+ "inspect": "dzx inspect --json",
8
+ "build": "dzx build --split-tools"
9
+ },
10
+ "dependencies": {
11
+ "@dwizi/dzx": "^0.0.0",
12
+ "zod": "^4.3.6"
13
+ },
14
+ "devDependencies": {
15
+ "tsx": "^4.21.0"
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: summarize
3
+ description: Summarize text in 3 bullets
4
+ inputs:
5
+ - name: text
6
+ type: string
7
+ ---
8
+ Summarize the following:
9
+ {{text}}
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: getting-started
3
+ description: Quick start guide
4
+ ---
5
+ # Getting Started
6
+
7
+ This is a starter resource file for your MCP app.
@@ -0,0 +1,3 @@
1
+ import { createServerFromManifest } from "@dwizi/dzx/runtime";
2
+
3
+ export default createServerFromManifest();
@@ -0,0 +1,17 @@
1
+ import { defineSchema } from "@dwizi/dzx/schema";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Returns a friendly greeting.
6
+ * @param {object} input
7
+ * @param {string} input.name
8
+ * @returns {{ greeting: string }}
9
+ */
10
+ export default async function hello(input: { name: string }) {
11
+ return { greeting: `Hello, ${input.name}!` };
12
+ }
13
+
14
+ export const schema = {
15
+ input: defineSchema(z.object({ name: z.string() })),
16
+ output: defineSchema(z.object({ greeting: z.string() })),
17
+ };
@@ -0,0 +1,8 @@
1
+ # Full Agent
2
+
3
+ This template includes a tool, a resource, a prompt, and a build command example.
4
+
5
+ ## Commands
6
+ - `pnpm dev`
7
+ - `pnpm inspect`
8
+ - `pnpm build`
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "full-agent",
3
+ "version": "0.1.0",
4
+ "runtime": "node",
5
+ "entry": "src/server.ts",
6
+ "toolsDir": "tools",
7
+ "resourcesDir": "resources",
8
+ "promptsDir": "prompts",
9
+ "permissions": {
10
+ "network": false,
11
+ "filesystem": { "read": ["./resources", "./prompts"], "write": [] }
12
+ },
13
+ "build": {
14
+ "command": "pnpm build",
15
+ "output": "dist"
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "full-agent",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "dzx dev",
7
+ "inspect": "dzx inspect --json",
8
+ "build": "dzx build --split-tools"
9
+ },
10
+ "dependencies": {
11
+ "@dwizi/dzx": "^0.0.0",
12
+ "zod": "^4.3.6"
13
+ },
14
+ "devDependencies": {
15
+ "tsx": "^4.21.0"
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: rewrite
3
+ description: Rewrite text for clarity
4
+ inputs:
5
+ - name: text
6
+ type: string
7
+ ---
8
+ Rewrite the following in a clearer tone:
9
+ {{text}}
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: usage
3
+ description: How to use the tools in this MCP
4
+ ---
5
+ # Usage
6
+
7
+ This MCP exposes simple text tools. Start with `convertText`.
@@ -0,0 +1,3 @@
1
+ import { createServerFromManifest } from "@dwizi/dzx/runtime";
2
+
3
+ export default createServerFromManifest();
@@ -0,0 +1,17 @@
1
+ import { defineSchema } from "@dwizi/dzx/schema";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Converts text to uppercase.
6
+ * @param {object} input
7
+ * @param {string} input.text
8
+ * @returns {{ upper: string }}
9
+ */
10
+ export default async function convertText(input: { text: string }) {
11
+ return { upper: input.text.toUpperCase() };
12
+ }
13
+
14
+ export const schema = {
15
+ input: defineSchema(z.object({ text: z.string() })),
16
+ output: defineSchema(z.object({ upper: z.string() })),
17
+ };
@@ -0,0 +1,8 @@
1
+ # Tools-only Agent
2
+
3
+ This template includes tools only. Resources and prompts are optional.
4
+
5
+ ## Commands
6
+ - `pnpm dev`
7
+ - `pnpm inspect`
8
+ - `pnpm build`
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "tools-only-agent",
3
+ "version": "0.1.0",
4
+ "runtime": "node",
5
+ "entry": "src/server.ts",
6
+ "toolsDir": "tools",
7
+ "resourcesDir": "resources",
8
+ "promptsDir": "prompts",
9
+ "permissions": {
10
+ "network": false,
11
+ "filesystem": { "read": [], "write": [] }
12
+ }
13
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "tools-only-agent",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "dzx dev",
7
+ "inspect": "dzx inspect --json",
8
+ "build": "dzx build --split-tools"
9
+ },
10
+ "dependencies": {
11
+ "@dwizi/dzx": "^0.0.0",
12
+ "zod": "^4.3.6"
13
+ },
14
+ "devDependencies": {
15
+ "tsx": "^4.21.0"
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ import { createServerFromManifest } from "@dwizi/dzx/runtime";
2
+
3
+ export default createServerFromManifest();
@@ -0,0 +1,17 @@
1
+ import { defineSchema } from "@dwizi/dzx/schema";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Returns the input as output.
6
+ * @param {object} input
7
+ * @param {string} input.text
8
+ * @returns {{ text: string }}
9
+ */
10
+ export default async function echo(input: { text: string }) {
11
+ return { text: input.text };
12
+ }
13
+
14
+ export const schema = {
15
+ input: defineSchema(z.object({ text: z.string() })),
16
+ output: defineSchema(z.object({ text: z.string() })),
17
+ };