frontmcp 0.1.5 → 0.1.7

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.
Files changed (3) hide show
  1. package/dist/cli.js +61 -24
  2. package/package.json +7 -7
  3. package/src/cli.ts +66 -21
package/dist/cli.js CHANGED
@@ -71,7 +71,7 @@ ${c('bold', 'Usage')}
71
71
  frontmcp <command> [options]
72
72
 
73
73
  ${c('bold', 'Commands')}
74
- dev Start in development mode (tsx --watch <entry>)
74
+ dev Start in development mode (tsx --watch <entry> + async type-check)
75
75
  build Compile entry with TypeScript (tsc)
76
76
  init Create or fix a tsconfig.json suitable for FrontMCP
77
77
  doctor Check Node/npm versions and tsconfig requirements
@@ -151,14 +151,16 @@ function resolveEntry(cwd, explicit) {
151
151
  const pkg = yield readJSON(pkgPath);
152
152
  if (pkg && typeof pkg.main === 'string' && pkg.main.trim()) {
153
153
  const mainCandidates = tryCandidates(path.resolve(cwd, pkg.main));
154
- for (const p of mainCandidates)
154
+ for (const p of mainCandidates) {
155
155
  if (yield fileExists(p))
156
156
  return p;
157
+ }
157
158
  const asDir = path.resolve(cwd, pkg.main);
158
159
  const idxCandidates = tryCandidates(path.join(asDir, 'index'));
159
- for (const p of idxCandidates)
160
+ for (const p of idxCandidates) {
160
161
  if (yield fileExists(p))
161
162
  return p;
163
+ }
162
164
  }
163
165
  }
164
166
  const fallback = path.join(cwd, 'src', 'main.ts');
@@ -219,22 +221,61 @@ function sanitizeForNpm(name) {
219
221
  /* ------------------------ Self-version detection -------------------------- */
220
222
  function getSelfVersion() {
221
223
  return __awaiter(this, void 0, void 0, function* () {
222
- return '0.1.5';
224
+ const binPath = process.argv[1] || __filename;
225
+ const candidates = [
226
+ path.resolve(path.dirname(binPath), '../package.json'),
227
+ path.resolve(path.dirname(binPath), '../../package.json'),
228
+ ];
229
+ for (const p of candidates) {
230
+ const j = yield readJSON(p);
231
+ if (j === null || j === void 0 ? void 0 : j.version)
232
+ return j.version;
233
+ }
234
+ return '0.0.0';
223
235
  });
224
236
  }
225
237
  /* --------------------------------- Actions -------------------------------- */
238
+ function isTsLike(p) {
239
+ return /\.tsx?$/i.test(p);
240
+ }
241
+ function killQuiet(proc) {
242
+ try {
243
+ proc && proc.kill('SIGINT');
244
+ }
245
+ catch (_a) { }
246
+ }
226
247
  function runDev(opts) {
227
248
  return __awaiter(this, void 0, void 0, function* () {
228
249
  const cwd = process.cwd();
229
250
  const entry = yield resolveEntry(cwd, opts.entry);
230
251
  console.log(`${c('cyan', '[dev]')} using entry: ${path.relative(cwd, entry)}`);
252
+ console.log(`${c('gray', '[dev]')} starting ${c('bold', 'tsx --watch')} and ${c('bold', 'tsc --noEmit --watch')} (async type-checker)`);
231
253
  console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);
232
- yield runCmd('npx', ['-y', 'tsx', '--watch', entry]);
254
+ // Start tsx watcher (app run)
255
+ const app = (0, child_process_1.spawn)('npx', ['-y', 'tsx', '--watch', entry], { stdio: 'inherit', shell: true });
256
+ // Start tsc in watch mode for async type-checking (non-blocking)
257
+ const checker = (0, child_process_1.spawn)('npx', ['-y', 'tsc', '--noEmit', '--pretty', '--watch'], { stdio: 'inherit', shell: true });
258
+ const cleanup = () => {
259
+ killQuiet(checker);
260
+ killQuiet(app);
261
+ };
262
+ process.on('SIGINT', () => {
263
+ cleanup();
264
+ process.exit(0);
265
+ });
266
+ yield new Promise((resolve, reject) => {
267
+ app.on('close', (_code) => {
268
+ // When app exits, stop checker too.
269
+ cleanup();
270
+ resolve();
271
+ });
272
+ app.on('error', (err) => {
273
+ cleanup();
274
+ reject(err);
275
+ });
276
+ });
233
277
  });
234
278
  }
235
- function isTsLike(p) {
236
- return /\.tsx?$/i.test(p);
237
- }
238
279
  function runBuild(opts) {
239
280
  return __awaiter(this, void 0, void 0, function* () {
240
281
  const cwd = process.cwd();
@@ -273,7 +314,7 @@ const RECOMMENDED_TSCONFIG = {
273
314
  module: REQUIRED_DECORATOR_FIELDS.module,
274
315
  emitDecoratorMetadata: REQUIRED_DECORATOR_FIELDS.emitDecoratorMetadata,
275
316
  experimentalDecorators: REQUIRED_DECORATOR_FIELDS.experimentalDecorators,
276
- moduleResolution: 'node',
317
+ moduleResolution: 'NodeNext',
277
318
  strict: true,
278
319
  esModuleInterop: true,
279
320
  resolveJsonModule: true,
@@ -465,19 +506,19 @@ function upsertPackageJson(cwd, nameOverride, selfVersion) {
465
506
  npm: '>=10',
466
507
  },
467
508
  dependencies: {
468
- '@frontmcp/sdk': 'latest',
509
+ '@frontmcp/sdk': '^0.1.3',
469
510
  zod: '^3.23.8',
470
511
  'reflect-metadata': '^0.2.2',
471
512
  },
472
513
  devDependencies: {
473
- frontmcp: selfVersion, // exact version used by npx
514
+ frontmcp: selfVersion, // exact CLI version used by npx
474
515
  tsx: '^4.20.6',
475
516
  typescript: '^5.5.3',
476
517
  },
477
518
  };
478
519
  if (!existing) {
479
520
  yield writeJSON(pkgPath, base);
480
- console.log(c('green', '✅ Created package.json (pinned tsx/typescript/zod/reflect-metadata, exact frontmcp)'));
521
+ console.log(c('green', '✅ Created package.json (pinned versions + exact frontmcp)'));
481
522
  return;
482
523
  }
483
524
  const merged = Object.assign(Object.assign({}, base), existing);
@@ -486,12 +527,8 @@ function upsertPackageJson(cwd, nameOverride, selfVersion) {
486
527
  merged.type = existing.type || base.type;
487
528
  merged.scripts = Object.assign(Object.assign(Object.assign({}, base.scripts), (existing.scripts || {})), { dev: (_b = (_a = existing.scripts) === null || _a === void 0 ? void 0 : _a.dev) !== null && _b !== void 0 ? _b : base.scripts.dev, build: (_d = (_c = existing.scripts) === null || _c === void 0 ? void 0 : _c.build) !== null && _d !== void 0 ? _d : base.scripts.build, inspect: (_f = (_e = existing.scripts) === null || _e === void 0 ? void 0 : _e.inspect) !== null && _f !== void 0 ? _f : base.scripts.inspect, doctor: (_h = (_g = existing.scripts) === null || _g === void 0 ? void 0 : _g.doctor) !== null && _h !== void 0 ? _h : base.scripts.doctor });
488
529
  merged.engines = Object.assign(Object.assign({}, (existing.engines || {})), { node: ((_j = existing.engines) === null || _j === void 0 ? void 0 : _j.node) || base.engines.node, npm: ((_k = existing.engines) === null || _k === void 0 ? void 0 : _k.npm) || base.engines.npm });
489
- merged.dependencies = Object.assign(Object.assign(Object.assign({}, base.dependencies), (existing.dependencies || {})), {
490
- // ensure pins
491
- zod: '^3.23.8', 'reflect-metadata': '^0.2.2' });
492
- merged.devDependencies = Object.assign(Object.assign(Object.assign({}, base.devDependencies), (existing.devDependencies || {})), {
493
- // ensure pins
494
- frontmcp: selfVersion, tsx: '^4.20.6', typescript: '^5.5.3' });
530
+ merged.dependencies = Object.assign(Object.assign(Object.assign({}, base.dependencies), (existing.dependencies || {})), { zod: '^3.23.8', 'reflect-metadata': '^0.2.2' });
531
+ merged.devDependencies = Object.assign(Object.assign(Object.assign({}, base.devDependencies), (existing.devDependencies || {})), { frontmcp: selfVersion, tsx: '^4.20.6', typescript: '^5.5.3' });
495
532
  yield writeJSON(pkgPath, merged);
496
533
  console.log(c('green', '✅ Updated package.json (ensured exact frontmcp and pinned versions)'));
497
534
  });
@@ -541,9 +578,9 @@ import { z } from 'zod';
541
578
  const AddTool = tool({
542
579
  name: 'add',
543
580
  description: 'Add two numbers',
544
- inputSchema: z.object({ a: z.number(), b: z.number() }),
545
- outputSchema: z.object({ result: z.number() }),
546
- })((input, _ctx) => {
581
+ inputSchema: { a: z.number(), b: z.number() },
582
+ outputSchema: { result: z.number() },
583
+ })(async (input, _ctx) => {
547
584
  return { result: input.a + input.b };
548
585
  });
549
586
 
@@ -573,7 +610,7 @@ function runCreate(projectArg) {
573
610
  process.chdir(targetDir);
574
611
  // 1) tsconfig
575
612
  yield runInit(targetDir);
576
- // 2) package.json (with pinned deps and exact frontmcp version)
613
+ // 2) package.json (pinned deps + exact CLI version)
577
614
  const selfVersion = yield getSelfVersion();
578
615
  yield upsertPackageJson(targetDir, pkgName, selfVersion);
579
616
  // 3) files
@@ -583,7 +620,7 @@ function runCreate(projectArg) {
583
620
  console.log('\nNext steps:');
584
621
  console.log(` 1) cd ${folder}`);
585
622
  console.log(' 2) npm install');
586
- console.log(' 3) npm run dev ', c('gray', '# starts tsx watcher via frontmcp dev'));
623
+ console.log(' 3) npm run dev ', c('gray', '# tsx watcher + async tsc type-check'));
587
624
  console.log(' 4) npm run inspect ', c('gray', '# launch MCP Inspector'));
588
625
  console.log(' 5) npm run build ', c('gray', '# compile with tsc via frontmcp build'));
589
626
  });
@@ -617,7 +654,7 @@ function main() {
617
654
  yield runInspector();
618
655
  break;
619
656
  case 'create': {
620
- const projectName = parsed._[1]; // require a name
657
+ const projectName = parsed._[1];
621
658
  yield runCreate(projectName);
622
659
  break;
623
660
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "FrontMCP command line interface",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -12,15 +12,15 @@
12
12
  "prepare": "npm run build"
13
13
  },
14
14
  "dependencies": {
15
- "@frontmcp/sdk": "0.1.3",
16
- "@frontmcp/core": "0.1.3",
17
- "@frontmcp/plugins": "0.1.3",
18
- "@frontmcp/adapters": "0.1.3",
19
- "tsx": "^4.20.6",
15
+ "@frontmcp/sdk": "^0.1.3",
16
+ "@frontmcp/core": "^0.1.3",
17
+ "@frontmcp/plugins": "^0.1.3",
18
+ "@frontmcp/adapters": "^0.1.3",
19
+ "tsx": "^4.16.0",
20
20
  "typescript": "^5.5.3"
21
21
  },
22
22
  "devDependencies": {
23
- "@types/node": "20.19.9",
23
+ "@types/node": "^20.11.30",
24
24
  "@modelcontextprotocol/inspector": "^0.17.2"
25
25
  }
26
26
  }
package/src/cli.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  import * as fs from 'fs';
8
8
  import {promises as fsp} from 'fs';
9
9
  import * as path from 'path';
10
- import {spawn} from 'child_process';
10
+ import {spawn, ChildProcess} from 'child_process';
11
11
 
12
12
  /* ----------------------------- Types & Helpers ---------------------------- */
13
13
 
@@ -42,7 +42,7 @@ ${c('bold', 'Usage')}
42
42
  frontmcp <command> [options]
43
43
 
44
44
  ${c('bold', 'Commands')}
45
- dev Start in development mode (tsx --watch <entry>)
45
+ dev Start in development mode (tsx --watch <entry> + async type-check)
46
46
  build Compile entry with TypeScript (tsc)
47
47
  init Create or fix a tsconfig.json suitable for FrontMCP
48
48
  doctor Check Node/npm versions and tsconfig requirements
@@ -109,17 +109,23 @@ async function resolveEntry(cwd: string, explicit?: string): Promise<string> {
109
109
  if (await fileExists(full)) return full;
110
110
  throw new Error(`Entry override not found: ${explicit}`);
111
111
  }
112
+
112
113
  const pkgPath = path.join(cwd, 'package.json');
113
114
  if (await fileExists(pkgPath)) {
114
115
  const pkg = await readJSON<any>(pkgPath);
115
116
  if (pkg && typeof pkg.main === 'string' && pkg.main.trim()) {
116
117
  const mainCandidates = tryCandidates(path.resolve(cwd, pkg.main));
117
- for (const p of mainCandidates) if (await fileExists(p)) return p;
118
+ for (const p of mainCandidates) {
119
+ if (await fileExists(p)) return p;
120
+ }
118
121
  const asDir = path.resolve(cwd, pkg.main);
119
122
  const idxCandidates = tryCandidates(path.join(asDir, 'index'));
120
- for (const p of idxCandidates) if (await fileExists(p)) return p;
123
+ for (const p of idxCandidates) {
124
+ if (await fileExists(p)) return p;
125
+ }
121
126
  }
122
127
  }
128
+
123
129
  const fallback = path.join(cwd, 'src', 'main.ts');
124
130
  if (await fileExists(fallback)) return fallback;
125
131
 
@@ -177,21 +183,62 @@ function sanitizeForNpm(name: string): string {
177
183
  /* ------------------------ Self-version detection -------------------------- */
178
184
 
179
185
  async function getSelfVersion(): Promise<string> {
180
- return '0.1.5';
186
+ const binPath = process.argv[1] || __filename;
187
+ const candidates = [
188
+ path.resolve(path.dirname(binPath), '../package.json'),
189
+ path.resolve(path.dirname(binPath), '../../package.json'),
190
+ ];
191
+ for (const p of candidates) {
192
+ const j = await readJSON<any>(p);
193
+ if (j?.version) return j.version;
194
+ }
195
+ return '0.0.0';
181
196
  }
182
197
 
183
198
  /* --------------------------------- Actions -------------------------------- */
184
199
 
200
+ function isTsLike(p: string): boolean {
201
+ return /\.tsx?$/i.test(p);
202
+ }
203
+
204
+ function killQuiet(proc?: ChildProcess) {
205
+ try { proc && proc.kill('SIGINT'); } catch {}
206
+ }
207
+
185
208
  async function runDev(opts: ParsedArgs): Promise<void> {
186
209
  const cwd = process.cwd();
187
210
  const entry = await resolveEntry(cwd, opts.entry);
211
+
188
212
  console.log(`${c('cyan', '[dev]')} using entry: ${path.relative(cwd, entry)}`);
213
+ console.log(`${c('gray', '[dev]')} starting ${c('bold', 'tsx --watch')} and ${c('bold', 'tsc --noEmit --watch')} (async type-checker)`);
189
214
  console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);
190
- await runCmd('npx', ['-y', 'tsx', '--watch', entry]);
191
- }
192
215
 
193
- function isTsLike(p: string): boolean {
194
- return /\.tsx?$/i.test(p);
216
+ // Start tsx watcher (app run)
217
+ const app = spawn('npx', ['-y', 'tsx', '--watch', entry], {stdio: 'inherit', shell: true});
218
+ // Start tsc in watch mode for async type-checking (non-blocking)
219
+ const checker = spawn('npx', ['-y', 'tsc', '--noEmit', '--pretty', '--watch'], {stdio: 'inherit', shell: true});
220
+
221
+ const cleanup = () => {
222
+ killQuiet(checker);
223
+ killQuiet(app);
224
+ };
225
+
226
+ process.on('SIGINT', () => {
227
+ cleanup();
228
+ process.exit(0);
229
+ });
230
+
231
+ await new Promise<void>((resolve, reject) => {
232
+ app.on('close', (_code) => {
233
+ // When app exits, stop checker too.
234
+ cleanup();
235
+ resolve();
236
+ });
237
+ app.on('error', (err) => {
238
+ cleanup();
239
+ reject(err);
240
+ });
241
+ });
195
242
  }
196
243
 
197
244
  async function runBuild(opts: ParsedArgs): Promise<void> {
@@ -239,7 +286,7 @@ const RECOMMENDED_TSCONFIG = {
239
286
  emitDecoratorMetadata: REQUIRED_DECORATOR_FIELDS.emitDecoratorMetadata,
240
287
  experimentalDecorators: REQUIRED_DECORATOR_FIELDS.experimentalDecorators,
241
288
 
242
- moduleResolution: 'node',
289
+ moduleResolution: 'NodeNext',
243
290
  strict: true,
244
291
  esModuleInterop: true,
245
292
  resolveJsonModule: true,
@@ -428,12 +475,12 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
428
475
  npm: '>=10',
429
476
  },
430
477
  dependencies: {
431
- '@frontmcp/sdk': 'latest',
478
+ '@frontmcp/sdk': '^0.1.3',
432
479
  zod: '^3.23.8',
433
480
  'reflect-metadata': '^0.2.2',
434
481
  },
435
482
  devDependencies: {
436
- frontmcp: selfVersion, // exact version used by npx
483
+ frontmcp: selfVersion, // exact CLI version used by npx
437
484
  tsx: '^4.20.6',
438
485
  typescript: '^5.5.3',
439
486
  },
@@ -441,7 +488,7 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
441
488
 
442
489
  if (!existing) {
443
490
  await writeJSON(pkgPath, base);
444
- console.log(c('green', '✅ Created package.json (pinned tsx/typescript/zod/reflect-metadata, exact frontmcp)'));
491
+ console.log(c('green', '✅ Created package.json (pinned versions + exact frontmcp)'));
445
492
  return;
446
493
  }
447
494
 
@@ -469,7 +516,6 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
469
516
  merged.dependencies = {
470
517
  ...base.dependencies,
471
518
  ...(existing.dependencies || {}),
472
- // ensure pins
473
519
  zod: '^3.23.8',
474
520
  'reflect-metadata': '^0.2.2',
475
521
  };
@@ -477,7 +523,6 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
477
523
  merged.devDependencies = {
478
524
  ...base.devDependencies,
479
525
  ...(existing.devDependencies || {}),
480
- // ensure pins
481
526
  frontmcp: selfVersion,
482
527
  tsx: '^4.20.6',
483
528
  typescript: '^5.5.3',
@@ -533,9 +578,9 @@ import { z } from 'zod';
533
578
  const AddTool = tool({
534
579
  name: 'add',
535
580
  description: 'Add two numbers',
536
- inputSchema: z.object({ a: z.number(), b: z.number() }),
537
- outputSchema: z.object({ result: z.number() }),
538
- })((input, _ctx) => {
581
+ inputSchema: { a: z.number(), b: z.number() },
582
+ outputSchema: { result: z.number() },
583
+ })(async (input, _ctx) => {
539
584
  return { result: input.a + input.b };
540
585
  });
541
586
 
@@ -569,7 +614,7 @@ async function runCreate(projectArg?: string): Promise<void> {
569
614
  // 1) tsconfig
570
615
  await runInit(targetDir);
571
616
 
572
- // 2) package.json (with pinned deps and exact frontmcp version)
617
+ // 2) package.json (pinned deps + exact CLI version)
573
618
  const selfVersion = await getSelfVersion();
574
619
  await upsertPackageJson(targetDir, pkgName, selfVersion);
575
620
 
@@ -581,7 +626,7 @@ async function runCreate(projectArg?: string): Promise<void> {
581
626
  console.log('\nNext steps:');
582
627
  console.log(` 1) cd ${folder}`);
583
628
  console.log(' 2) npm install');
584
- console.log(' 3) npm run dev ', c('gray', '# starts tsx watcher via frontmcp dev'));
629
+ console.log(' 3) npm run dev ', c('gray', '# tsx watcher + async tsc type-check'));
585
630
  console.log(' 4) npm run inspect ', c('gray', '# launch MCP Inspector'));
586
631
  console.log(' 5) npm run build ', c('gray', '# compile with tsc via frontmcp build'));
587
632
  }
@@ -617,7 +662,7 @@ async function main(): Promise<void> {
617
662
  await runInspector();
618
663
  break;
619
664
  case 'create': {
620
- const projectName = parsed._[1]; // require a name
665
+ const projectName = parsed._[1];
621
666
  await runCreate(projectName);
622
667
  break;
623
668
  }