frontmcp 0.1.4 → 0.1.6

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 +49 -24
  2. package/package.json +5 -5
  3. package/src/cli.ts +55 -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,7 +221,6 @@ function sanitizeForNpm(name) {
219
221
  /* ------------------------ Self-version detection -------------------------- */
220
222
  function getSelfVersion() {
221
223
  return __awaiter(this, void 0, void 0, function* () {
222
- // Best-effort: find our own package.json near the executed bin.
223
224
  const binPath = process.argv[1] || __filename;
224
225
  const candidates = [
225
226
  path.resolve(path.dirname(binPath), '../package.json'),
@@ -230,23 +231,51 @@ function getSelfVersion() {
230
231
  if (j === null || j === void 0 ? void 0 : j.version)
231
232
  return j.version;
232
233
  }
233
- // Fallback if not found; still satisfies the "has field" requirement.
234
234
  return '0.0.0';
235
235
  });
236
236
  }
237
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
+ }
238
247
  function runDev(opts) {
239
248
  return __awaiter(this, void 0, void 0, function* () {
240
249
  const cwd = process.cwd();
241
250
  const entry = yield resolveEntry(cwd, opts.entry);
242
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)`);
243
253
  console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);
244
- 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
+ });
245
277
  });
246
278
  }
247
- function isTsLike(p) {
248
- return /\.tsx?$/i.test(p);
249
- }
250
279
  function runBuild(opts) {
251
280
  return __awaiter(this, void 0, void 0, function* () {
252
281
  const cwd = process.cwd();
@@ -477,19 +506,19 @@ function upsertPackageJson(cwd, nameOverride, selfVersion) {
477
506
  npm: '>=10',
478
507
  },
479
508
  dependencies: {
480
- '@frontmcp/sdk': 'latest',
509
+ '@frontmcp/sdk': '^0.1.3',
481
510
  zod: '^3.23.8',
482
511
  'reflect-metadata': '^0.2.2',
483
512
  },
484
513
  devDependencies: {
485
- frontmcp: selfVersion, // exact version used by npx
514
+ frontmcp: selfVersion, // exact CLI version used by npx
486
515
  tsx: '^4.20.6',
487
516
  typescript: '^5.5.3',
488
517
  },
489
518
  };
490
519
  if (!existing) {
491
520
  yield writeJSON(pkgPath, base);
492
- 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)'));
493
522
  return;
494
523
  }
495
524
  const merged = Object.assign(Object.assign({}, base), existing);
@@ -498,12 +527,8 @@ function upsertPackageJson(cwd, nameOverride, selfVersion) {
498
527
  merged.type = existing.type || base.type;
499
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 });
500
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 });
501
- merged.dependencies = Object.assign(Object.assign(Object.assign({}, base.dependencies), (existing.dependencies || {})), {
502
- // ensure pins
503
- zod: '^3.23.8', 'reflect-metadata': '^0.2.2' });
504
- merged.devDependencies = Object.assign(Object.assign(Object.assign({}, base.devDependencies), (existing.devDependencies || {})), {
505
- // ensure pins
506
- 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' });
507
532
  yield writeJSON(pkgPath, merged);
508
533
  console.log(c('green', '✅ Updated package.json (ensured exact frontmcp and pinned versions)'));
509
534
  });
@@ -553,9 +578,9 @@ import { z } from 'zod';
553
578
  const AddTool = tool({
554
579
  name: 'add',
555
580
  description: 'Add two numbers',
556
- inputSchema: z.object({ a: z.number(), b: z.number() }),
557
- outputSchema: z.object({ result: z.number() }),
558
- })((input, _ctx) => {
581
+ inputSchema: { a: z.number(), b: z.number() },
582
+ outputSchema: { result: z.number() },
583
+ })(async (input, _ctx) => {
559
584
  return { result: input.a + input.b };
560
585
  });
561
586
 
@@ -585,7 +610,7 @@ function runCreate(projectArg) {
585
610
  process.chdir(targetDir);
586
611
  // 1) tsconfig
587
612
  yield runInit(targetDir);
588
- // 2) package.json (with pinned deps and exact frontmcp version)
613
+ // 2) package.json (pinned deps + exact CLI version)
589
614
  const selfVersion = yield getSelfVersion();
590
615
  yield upsertPackageJson(targetDir, pkgName, selfVersion);
591
616
  // 3) files
@@ -595,7 +620,7 @@ function runCreate(projectArg) {
595
620
  console.log('\nNext steps:');
596
621
  console.log(` 1) cd ${folder}`);
597
622
  console.log(' 2) npm install');
598
- 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'));
599
624
  console.log(' 4) npm run inspect ', c('gray', '# launch MCP Inspector'));
600
625
  console.log(' 5) npm run build ', c('gray', '# compile with tsc via frontmcp build'));
601
626
  });
@@ -629,7 +654,7 @@ function main() {
629
654
  yield runInspector();
630
655
  break;
631
656
  case 'create': {
632
- const projectName = parsed._[1]; // require a name
657
+ const projectName = parsed._[1];
633
658
  yield runCreate(projectName);
634
659
  break;
635
660
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "FrontMCP command line interface",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -12,10 +12,10 @@
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",
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
19
  "tsx": "^4.20.6",
20
20
  "typescript": "^5.5.3"
21
21
  },
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,7 +183,6 @@ function sanitizeForNpm(name: string): string {
177
183
  /* ------------------------ Self-version detection -------------------------- */
178
184
 
179
185
  async function getSelfVersion(): Promise<string> {
180
- // Best-effort: find our own package.json near the executed bin.
181
186
  const binPath = process.argv[1] || __filename;
182
187
  const candidates = [
183
188
  path.resolve(path.dirname(binPath), '../package.json'),
@@ -187,22 +192,53 @@ async function getSelfVersion(): Promise<string> {
187
192
  const j = await readJSON<any>(p);
188
193
  if (j?.version) return j.version;
189
194
  }
190
- // Fallback if not found; still satisfies the "has field" requirement.
191
195
  return '0.0.0';
192
196
  }
193
197
 
194
198
  /* --------------------------------- Actions -------------------------------- */
195
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
+
196
208
  async function runDev(opts: ParsedArgs): Promise<void> {
197
209
  const cwd = process.cwd();
198
210
  const entry = await resolveEntry(cwd, opts.entry);
211
+
199
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)`);
200
214
  console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);
201
- await runCmd('npx', ['-y', 'tsx', '--watch', entry]);
202
- }
203
215
 
204
- function isTsLike(p: string): boolean {
205
- 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
+ });
206
242
  }
207
243
 
208
244
  async function runBuild(opts: ParsedArgs): Promise<void> {
@@ -439,12 +475,12 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
439
475
  npm: '>=10',
440
476
  },
441
477
  dependencies: {
442
- '@frontmcp/sdk': 'latest',
478
+ '@frontmcp/sdk': '^0.1.3',
443
479
  zod: '^3.23.8',
444
480
  'reflect-metadata': '^0.2.2',
445
481
  },
446
482
  devDependencies: {
447
- frontmcp: selfVersion, // exact version used by npx
483
+ frontmcp: selfVersion, // exact CLI version used by npx
448
484
  tsx: '^4.20.6',
449
485
  typescript: '^5.5.3',
450
486
  },
@@ -452,7 +488,7 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
452
488
 
453
489
  if (!existing) {
454
490
  await writeJSON(pkgPath, base);
455
- 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)'));
456
492
  return;
457
493
  }
458
494
 
@@ -480,7 +516,6 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
480
516
  merged.dependencies = {
481
517
  ...base.dependencies,
482
518
  ...(existing.dependencies || {}),
483
- // ensure pins
484
519
  zod: '^3.23.8',
485
520
  'reflect-metadata': '^0.2.2',
486
521
  };
@@ -488,7 +523,6 @@ async function upsertPackageJson(cwd: string, nameOverride: string | undefined,
488
523
  merged.devDependencies = {
489
524
  ...base.devDependencies,
490
525
  ...(existing.devDependencies || {}),
491
- // ensure pins
492
526
  frontmcp: selfVersion,
493
527
  tsx: '^4.20.6',
494
528
  typescript: '^5.5.3',
@@ -544,9 +578,9 @@ import { z } from 'zod';
544
578
  const AddTool = tool({
545
579
  name: 'add',
546
580
  description: 'Add two numbers',
547
- inputSchema: z.object({ a: z.number(), b: z.number() }),
548
- outputSchema: z.object({ result: z.number() }),
549
- })((input, _ctx) => {
581
+ inputSchema: { a: z.number(), b: z.number() },
582
+ outputSchema: { result: z.number() },
583
+ })(async (input, _ctx) => {
550
584
  return { result: input.a + input.b };
551
585
  });
552
586
 
@@ -580,7 +614,7 @@ async function runCreate(projectArg?: string): Promise<void> {
580
614
  // 1) tsconfig
581
615
  await runInit(targetDir);
582
616
 
583
- // 2) package.json (with pinned deps and exact frontmcp version)
617
+ // 2) package.json (pinned deps + exact CLI version)
584
618
  const selfVersion = await getSelfVersion();
585
619
  await upsertPackageJson(targetDir, pkgName, selfVersion);
586
620
 
@@ -592,7 +626,7 @@ async function runCreate(projectArg?: string): Promise<void> {
592
626
  console.log('\nNext steps:');
593
627
  console.log(` 1) cd ${folder}`);
594
628
  console.log(' 2) npm install');
595
- 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'));
596
630
  console.log(' 4) npm run inspect ', c('gray', '# launch MCP Inspector'));
597
631
  console.log(' 5) npm run build ', c('gray', '# compile with tsc via frontmcp build'));
598
632
  }
@@ -628,7 +662,7 @@ async function main(): Promise<void> {
628
662
  await runInspector();
629
663
  break;
630
664
  case 'create': {
631
- const projectName = parsed._[1]; // require a name
665
+ const projectName = parsed._[1];
632
666
  await runCreate(projectName);
633
667
  break;
634
668
  }