preflight-mcp 0.1.3 → 0.1.5

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/dist/server.js CHANGED
@@ -3,7 +3,8 @@ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mc
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import * as z from 'zod';
5
5
  import { getConfig } from './config.js';
6
- import { checkForUpdates, clearBundleMulti, createBundle, findBundleStorageDir, getBundlePathsForId, getEffectiveStorageDir, listBundles, repairBundle, updateBundle, } from './bundle/service.js';
6
+ import { assertBundleComplete, checkForUpdates, checkInProgressLock, clearBundleMulti, computeCreateInputFingerprint, createBundle, findBundleStorageDir, getBundlePathsForId, getEffectiveStorageDir, listBundles, repairBundle, updateBundle, } from './bundle/service.js';
7
+ import { getProgressTracker } from './jobs/progressTracker.js';
7
8
  import { readManifest } from './bundle/manifest.js';
8
9
  import { safeJoin, toBundleFileUri } from './mcp/uris.js';
9
10
  import { wrapPreflightError } from './mcp/errorKinds.js';
@@ -81,9 +82,12 @@ const RepairBundleInputSchema = {
81
82
  rebuildGuides: z.boolean().optional().describe('If true, rebuild START_HERE.md and AGENTS.md when missing/empty.'),
82
83
  rebuildOverview: z.boolean().optional().describe('If true, rebuild OVERVIEW.md when missing/empty.'),
83
84
  };
84
- const ReadFileInputSchema = {
85
- bundleId: z.string().describe('Bundle ID.'),
86
- file: z.string().default('OVERVIEW.md').describe('File path relative to bundle root. Common files: OVERVIEW.md, START_HERE.md, AGENTS.md, manifest.json, or any repo file like repos/owner/repo/norm/README.md'),
85
+ const GetTaskStatusInputSchema = {
86
+ taskId: z.string().optional().describe('Task ID to query (from BUNDLE_IN_PROGRESS error).'),
87
+ fingerprint: z.string().optional().describe('Fingerprint to query (computed from repos/libraries/topics).'),
88
+ repos: z.array(CreateRepoInputSchema).optional().describe('Repos to compute fingerprint from (alternative to fingerprint).'),
89
+ libraries: z.array(z.string()).optional().describe('Libraries for fingerprint computation.'),
90
+ topics: z.array(z.string()).optional().describe('Topics for fingerprint computation.'),
87
91
  };
88
92
  export async function startServer() {
89
93
  const cfg = getConfig();
@@ -95,7 +99,7 @@ export async function startServer() {
95
99
  startHttpServer(cfg);
96
100
  const server = new McpServer({
97
101
  name: 'preflight-mcp',
98
- version: '0.1.3',
102
+ version: '0.1.5',
99
103
  description: 'Create evidence-based preflight bundles for repositories (docs + code) with SQLite FTS search.',
100
104
  }, {
101
105
  capabilities: {
@@ -254,13 +258,20 @@ export async function startServer() {
254
258
  };
255
259
  });
256
260
  server.registerTool('preflight_read_file', {
257
- title: 'Read bundle file',
258
- description: 'Read a file from bundle. Use when: "show overview", "read file", "查看概览", "项目概览", "看README", "查看文档", "bundle详情", "bundle状态", "仓库信息". Common files: OVERVIEW.md, START_HERE.md, AGENTS.md, manifest.json (for bundle metadata/status).',
259
- inputSchema: ReadFileInputSchema,
261
+ title: 'Read bundle file(s)',
262
+ description: 'Read file(s) from bundle. Two modes: ' +
263
+ '(1) Omit "file" param → returns ALL key files (OVERVIEW.md, START_HERE.md, AGENTS.md, manifest.json, repo READMEs) in one call. ' +
264
+ '(2) Provide "file" param → returns that specific file. ' +
265
+ 'Use when: "查看bundle", "show bundle", "read overview", "bundle概览", "项目信息".',
266
+ inputSchema: {
267
+ bundleId: z.string().describe('Bundle ID to read.'),
268
+ file: z.string().optional().describe('Specific file to read. If omitted, returns all key files (OVERVIEW.md, START_HERE.md, AGENTS.md, manifest.json, repo READMEs).'),
269
+ },
260
270
  outputSchema: {
261
271
  bundleId: z.string(),
262
- file: z.string(),
263
- content: z.string(),
272
+ file: z.string().optional(),
273
+ content: z.string().optional(),
274
+ files: z.record(z.string(), z.string().nullable()).optional(),
264
275
  },
265
276
  annotations: {
266
277
  readOnlyHint: true,
@@ -271,16 +282,66 @@ export async function startServer() {
271
282
  if (!storageDir) {
272
283
  throw new Error(`Bundle not found: ${args.bundleId}`);
273
284
  }
274
- const bundleRoot = getBundlePathsForId(storageDir, args.bundleId).rootDir;
275
- const absPath = safeJoin(bundleRoot, args.file);
276
- const content = await fs.readFile(absPath, 'utf8');
277
- const out = {
278
- bundleId: args.bundleId,
279
- file: args.file,
280
- content,
281
- };
285
+ const paths = getBundlePathsForId(storageDir, args.bundleId);
286
+ const bundleRoot = paths.rootDir;
287
+ // Single file mode
288
+ if (args.file) {
289
+ const absPath = safeJoin(bundleRoot, args.file);
290
+ const content = await fs.readFile(absPath, 'utf8');
291
+ const out = { bundleId: args.bundleId, file: args.file, content };
292
+ return {
293
+ content: [{ type: 'text', text: content }],
294
+ structuredContent: out,
295
+ };
296
+ }
297
+ // Batch mode: read all key files
298
+ const keyFiles = ['OVERVIEW.md', 'START_HERE.md', 'AGENTS.md', 'manifest.json'];
299
+ const files = {};
300
+ for (const file of keyFiles) {
301
+ try {
302
+ const absPath = safeJoin(bundleRoot, file);
303
+ files[file] = await fs.readFile(absPath, 'utf8');
304
+ }
305
+ catch {
306
+ files[file] = null;
307
+ }
308
+ }
309
+ // Try to find and read repo README files
310
+ try {
311
+ const manifest = await readManifest(paths.manifestPath);
312
+ for (const repo of manifest.repos ?? []) {
313
+ if (!repo.id)
314
+ continue;
315
+ const [owner, repoName] = repo.id.split('/');
316
+ if (!owner || !repoName)
317
+ continue;
318
+ const readmeNames = ['README.md', 'readme.md', 'Readme.md', 'README.MD'];
319
+ for (const readmeName of readmeNames) {
320
+ const readmePath = `repos/${owner}/${repoName}/norm/${readmeName}`;
321
+ try {
322
+ const absPath = safeJoin(bundleRoot, readmePath);
323
+ files[readmePath] = await fs.readFile(absPath, 'utf8');
324
+ break;
325
+ }
326
+ catch {
327
+ // Try next
328
+ }
329
+ }
330
+ }
331
+ }
332
+ catch {
333
+ // Ignore manifest read errors
334
+ }
335
+ // Build combined text output
336
+ const textParts = [];
337
+ for (const [filePath, content] of Object.entries(files)) {
338
+ if (content) {
339
+ textParts.push(`=== ${filePath} ===\n${content}`);
340
+ }
341
+ }
342
+ const out = { bundleId: args.bundleId, files };
282
343
  return {
283
- content: [{ type: 'text', text: content }],
344
+ content: [{ type: 'text', text: textParts.join('\n\n') || '(no files found)' }],
284
345
  structuredContent: out,
285
346
  };
286
347
  }
@@ -321,22 +382,23 @@ export async function startServer() {
321
382
  description: 'Create a new bundle from GitHub repos or local directories (or update an existing one if ifExists=updateExisting). Use when: "index this repo", "create bundle for", "add repo to preflight", "索引这个仓库", "创建bundle", "添加GitHub项目", "学习这个项目". NOTE: If the bundle contains code files, consider asking user if they want to generate dependency graph (preflight_evidence_dependency_graph) or establish trace links (preflight_trace_upsert).',
322
383
  inputSchema: CreateBundleInputSchema,
323
384
  outputSchema: {
324
- bundleId: z.string(),
325
- createdAt: z.string(),
326
- updatedAt: z.string(),
385
+ // Normal completion fields
386
+ bundleId: z.string().optional(),
387
+ createdAt: z.string().optional(),
388
+ updatedAt: z.string().optional(),
327
389
  resources: z.object({
328
390
  startHere: z.string(),
329
391
  agents: z.string(),
330
392
  overview: z.string(),
331
393
  manifest: z.string(),
332
- }),
394
+ }).optional(),
333
395
  repos: z.array(z.object({
334
396
  kind: z.enum(['github', 'local']),
335
397
  id: z.string(),
336
398
  source: z.enum(['git', 'archive', 'local']).optional(),
337
399
  headSha: z.string().optional(),
338
400
  notes: z.array(z.string()).optional(),
339
- })),
401
+ })).optional(),
340
402
  libraries: z
341
403
  .array(z.object({
342
404
  kind: z.literal('context7'),
@@ -347,6 +409,20 @@ export async function startServer() {
347
409
  files: z.array(z.string()).optional(),
348
410
  }))
349
411
  .optional(),
412
+ // User-facing warnings (e.g., git clone failed, used zip fallback)
413
+ warnings: z.array(z.string()).optional(),
414
+ // In-progress status fields
415
+ status: z.enum(['in-progress', 'complete']).optional(),
416
+ message: z.string().optional(),
417
+ taskId: z.string().optional(),
418
+ fingerprint: z.string().optional(),
419
+ /** Repo IDs requested (for in-progress status only, different from repos array) */
420
+ requestedRepos: z.array(z.string()).optional(),
421
+ startedAt: z.string().optional(),
422
+ elapsedSeconds: z.number().optional(),
423
+ currentPhase: z.string().optional(),
424
+ currentProgress: z.number().optional(),
425
+ currentMessage: z.string().optional(),
350
426
  },
351
427
  annotations: {
352
428
  openWorldHint: true,
@@ -370,12 +446,48 @@ export async function startServer() {
370
446
  ...summary,
371
447
  resources,
372
448
  };
449
+ // Build text response - prominently show warnings if any
450
+ let textResponse = '';
451
+ if (summary.warnings && summary.warnings.length > 0) {
452
+ textResponse += '📢 **Network Issues Encountered:**\n';
453
+ for (const warn of summary.warnings) {
454
+ textResponse += `${warn}\n`;
455
+ }
456
+ textResponse += '\n';
457
+ }
458
+ textResponse += `✅ Bundle created: ${summary.bundleId}\n`;
459
+ textResponse += `Repos: ${summary.repos.map(r => `${r.id} (${r.source})`).join(', ')}`;
373
460
  return {
374
- content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
461
+ content: [{ type: 'text', text: textResponse }],
375
462
  structuredContent: out,
376
463
  };
377
464
  }
378
465
  catch (err) {
466
+ // Handle BUNDLE_IN_PROGRESS error specially - provide useful info instead of just error
467
+ if (err?.code === 'BUNDLE_IN_PROGRESS') {
468
+ const elapsedSec = err.startedAt
469
+ ? Math.round((Date.now() - new Date(err.startedAt).getTime()) / 1000)
470
+ : 0;
471
+ // Check current progress from tracker
472
+ const tracker = getProgressTracker();
473
+ const task = err.taskId ? tracker.getTask(err.taskId) : undefined;
474
+ const out = {
475
+ status: 'in-progress',
476
+ message: `Bundle creation already in progress. Use preflight_get_task_status to check progress.`,
477
+ taskId: err.taskId,
478
+ fingerprint: err.fingerprint,
479
+ requestedRepos: err.repos,
480
+ startedAt: err.startedAt,
481
+ elapsedSeconds: elapsedSec,
482
+ currentPhase: task?.phase,
483
+ currentProgress: task?.progress,
484
+ currentMessage: task?.message,
485
+ };
486
+ return {
487
+ content: [{ type: 'text', text: `⚠️ Bundle creation in progress (${elapsedSec}s elapsed). ${task ? `Current: ${task.phase} (${task.progress}%) - ${task.message}` : 'Use preflight_get_task_status to check progress.'}` }],
488
+ structuredContent: out,
489
+ };
490
+ }
379
491
  throw wrapPreflightError(err);
380
492
  }
381
493
  });
@@ -388,6 +500,8 @@ export async function startServer() {
388
500
  mode: z.enum(['validate', 'repair']),
389
501
  repaired: z.boolean(),
390
502
  actionsTaken: z.array(z.string()),
503
+ /** Issues that cannot be fixed by repair (require re-download) */
504
+ unfixableIssues: z.array(z.string()).optional(),
391
505
  before: z.object({
392
506
  isValid: z.boolean(),
393
507
  missingComponents: z.array(z.string()),
@@ -409,11 +523,21 @@ export async function startServer() {
409
523
  rebuildGuides: args.rebuildGuides,
410
524
  rebuildOverview: args.rebuildOverview,
411
525
  });
412
- const summaryLine = out.mode === 'validate'
413
- ? `VALIDATE ${out.bundleId}: ${out.before.isValid ? 'OK' : 'MISSING'} (${out.before.missingComponents.length} issue(s))`
414
- : out.repaired
415
- ? `REPAIRED ${out.bundleId}: ${out.actionsTaken.length} action(s), now ${out.after.isValid ? 'OK' : 'STILL_MISSING'} (${out.after.missingComponents.length} issue(s))`
416
- : `NOOP ${out.bundleId}: nothing to repair (already OK)`;
526
+ let summaryLine;
527
+ if (out.mode === 'validate') {
528
+ summaryLine = `VALIDATE ${out.bundleId}: ${out.before.isValid ? 'OK' : 'INVALID'} (${out.before.missingComponents.length} issue(s))`;
529
+ }
530
+ else if (out.unfixableIssues && out.unfixableIssues.length > 0) {
531
+ // Has unfixable issues - clearly communicate this
532
+ summaryLine = `⚠️ UNFIXABLE ${out.bundleId}: ${out.unfixableIssues.length} issue(s) cannot be repaired offline.\n` +
533
+ out.unfixableIssues.map(i => ` - ${i}`).join('\n');
534
+ }
535
+ else if (out.repaired) {
536
+ summaryLine = `REPAIRED ${out.bundleId}: ${out.actionsTaken.length} action(s), now ${out.after.isValid ? 'OK' : 'STILL_INVALID'} (${out.after.missingComponents.length} issue(s))`;
537
+ }
538
+ else {
539
+ summaryLine = `NOOP ${out.bundleId}: nothing to repair (already OK)`;
540
+ }
417
541
  return {
418
542
  content: [{ type: 'text', text: summaryLine }],
419
543
  structuredContent: out,
@@ -486,22 +610,38 @@ export async function startServer() {
486
610
  structuredContent: out,
487
611
  };
488
612
  }
489
- const { summary, changed } = await updateBundle(cfg, args.bundleId, { force: args.force });
490
- const resources = {
491
- startHere: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'START_HERE.md' }),
492
- agents: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'AGENTS.md' }),
493
- overview: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'OVERVIEW.md' }),
494
- manifest: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'manifest.json' }),
495
- };
496
- const out = {
497
- changed: args.force ? true : changed,
498
- ...summary,
499
- resources,
500
- };
501
- return {
502
- content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
503
- structuredContent: out,
504
- };
613
+ // Create task for progress tracking
614
+ const tracker = getProgressTracker();
615
+ const fingerprint = `update-${args.bundleId}`;
616
+ const taskId = tracker.startTask(fingerprint, [args.bundleId]);
617
+ try {
618
+ const { summary, changed } = await updateBundle(cfg, args.bundleId, {
619
+ force: args.force,
620
+ onProgress: (phase, progress, message, total) => {
621
+ tracker.updateProgress(taskId, phase, progress, message, total);
622
+ },
623
+ });
624
+ tracker.completeTask(taskId, args.bundleId);
625
+ const resources = {
626
+ startHere: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'START_HERE.md' }),
627
+ agents: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'AGENTS.md' }),
628
+ overview: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'OVERVIEW.md' }),
629
+ manifest: toBundleFileUri({ bundleId: summary.bundleId, relativePath: 'manifest.json' }),
630
+ };
631
+ const out = {
632
+ changed: args.force ? true : changed,
633
+ ...summary,
634
+ resources,
635
+ };
636
+ return {
637
+ content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
638
+ structuredContent: out,
639
+ };
640
+ }
641
+ catch (updateErr) {
642
+ tracker.failTask(taskId, updateErr instanceof Error ? updateErr.message : String(updateErr));
643
+ throw updateErr;
644
+ }
505
645
  }
506
646
  catch (err) {
507
647
  throw wrapPreflightError(err);
@@ -607,6 +747,8 @@ export async function startServer() {
607
747
  },
608
748
  }, async (args) => {
609
749
  try {
750
+ // Check bundle completeness before any operation
751
+ await assertBundleComplete(cfg, args.bundleId);
610
752
  // Resolve bundle location across storageDirs (more robust than a single effectiveDir).
611
753
  const storageDir = await findBundleStorageDir(cfg.storageDirs, args.bundleId);
612
754
  if (!storageDir) {
@@ -643,7 +785,11 @@ export async function startServer() {
643
785
  });
644
786
  server.registerTool('preflight_evidence_dependency_graph', {
645
787
  title: 'Evidence: dependency graph (callers + imports)',
646
- description: 'Generate an evidence-based dependency graph for a target file/symbol inside a bundle. Output is deterministic (FTS + regex) and every edge includes traceable sources (file + range). This tool is read-only.',
788
+ description: 'Generate an evidence-based dependency graph. Two modes: ' +
789
+ '(1) TARGET MODE: provide target.file to analyze a specific file\'s imports and callers. ' +
790
+ '(2) GLOBAL MODE: omit target to generate a project-wide import graph of all code files. ' +
791
+ 'For target mode, file path must be bundle-relative: repos/{owner}/{repo}/norm/{path}. ' +
792
+ 'Use preflight_search_bundle to find file paths, or check OVERVIEW.md.',
647
793
  inputSchema: DependencyGraphInputSchema,
648
794
  outputSchema: {
649
795
  meta: z.any(),
@@ -655,6 +801,8 @@ export async function startServer() {
655
801
  },
656
802
  }, async (args) => {
657
803
  try {
804
+ // Check bundle completeness before generating dependency graph
805
+ await assertBundleComplete(cfg, args.bundleId);
658
806
  const out = await generateDependencyGraph(cfg, args);
659
807
  return {
660
808
  content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
@@ -679,6 +827,8 @@ export async function startServer() {
679
827
  },
680
828
  }, async (args) => {
681
829
  try {
830
+ // Check bundle completeness before trace upsert
831
+ await assertBundleComplete(cfg, args.bundleId);
682
832
  const out = await traceUpsert(cfg, args);
683
833
  return {
684
834
  content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
@@ -715,6 +865,10 @@ export async function startServer() {
715
865
  },
716
866
  }, async (args) => {
717
867
  try {
868
+ // Check bundle completeness if bundleId is provided
869
+ if (args.bundleId) {
870
+ await assertBundleComplete(cfg, args.bundleId);
871
+ }
718
872
  const out = await traceQuery(cfg, args);
719
873
  return {
720
874
  content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
@@ -763,6 +917,115 @@ export async function startServer() {
763
917
  throw wrapPreflightError(err);
764
918
  }
765
919
  });
920
+ // Get task status - for checking progress of in-progress bundle creations
921
+ server.registerTool('preflight_get_task_status', {
922
+ title: 'Get task status',
923
+ description: 'Check status of bundle creation tasks (especially in-progress ones). Use when: "check bundle creation progress", "what is the status", "查看任务状态", "下载进度". Can query by taskId (from error), fingerprint, or repos.',
924
+ inputSchema: GetTaskStatusInputSchema,
925
+ outputSchema: {
926
+ found: z.boolean(),
927
+ task: z.object({
928
+ taskId: z.string(),
929
+ fingerprint: z.string(),
930
+ phase: z.string(),
931
+ progress: z.number(),
932
+ total: z.number().optional(),
933
+ message: z.string(),
934
+ startedAt: z.string(),
935
+ updatedAt: z.string(),
936
+ repos: z.array(z.string()),
937
+ bundleId: z.string().optional(),
938
+ error: z.string().optional(),
939
+ }).optional(),
940
+ inProgressLock: z.object({
941
+ bundleId: z.string(),
942
+ status: z.string(),
943
+ startedAt: z.string().optional(),
944
+ taskId: z.string().optional(),
945
+ repos: z.array(z.string()).optional(),
946
+ elapsedSeconds: z.number().optional(),
947
+ }).optional(),
948
+ activeTasks: z.array(z.object({
949
+ taskId: z.string(),
950
+ fingerprint: z.string(),
951
+ phase: z.string(),
952
+ progress: z.number(),
953
+ message: z.string(),
954
+ repos: z.array(z.string()),
955
+ startedAt: z.string(),
956
+ })).optional(),
957
+ },
958
+ annotations: {
959
+ readOnlyHint: true,
960
+ },
961
+ }, async (args) => {
962
+ try {
963
+ const tracker = getProgressTracker();
964
+ let result = { found: false };
965
+ // Compute fingerprint if repos provided
966
+ let fingerprint = args.fingerprint;
967
+ if (!fingerprint && args.repos?.length) {
968
+ fingerprint = computeCreateInputFingerprint({
969
+ repos: args.repos,
970
+ libraries: args.libraries,
971
+ topics: args.topics,
972
+ });
973
+ }
974
+ // Query by taskId
975
+ if (args.taskId) {
976
+ const task = tracker.getTask(args.taskId);
977
+ if (task) {
978
+ result = { found: true, task };
979
+ }
980
+ }
981
+ // Query by fingerprint
982
+ else if (fingerprint) {
983
+ const task = tracker.getTaskByFingerprint(fingerprint);
984
+ if (task) {
985
+ result = { found: true, task };
986
+ }
987
+ // Also check persistent in-progress lock
988
+ const lock = await checkInProgressLock(cfg, fingerprint);
989
+ if (lock) {
990
+ const elapsedSeconds = lock.startedAt
991
+ ? Math.round((Date.now() - new Date(lock.startedAt).getTime()) / 1000)
992
+ : undefined;
993
+ result.inProgressLock = {
994
+ bundleId: lock.bundleId,
995
+ status: lock.status ?? 'unknown',
996
+ startedAt: lock.startedAt,
997
+ taskId: lock.taskId,
998
+ repos: lock.repos,
999
+ elapsedSeconds,
1000
+ };
1001
+ result.found = true;
1002
+ }
1003
+ }
1004
+ // If no specific query, return all active tasks
1005
+ else {
1006
+ const activeTasks = tracker.listActiveTasks();
1007
+ if (activeTasks.length > 0) {
1008
+ result = { found: true, activeTasks };
1009
+ }
1010
+ }
1011
+ const summary = result.found
1012
+ ? result.task
1013
+ ? `Task ${result.task.taskId}: ${result.task.phase} (${result.task.progress}%) - ${result.task.message}`
1014
+ : result.activeTasks
1015
+ ? `${result.activeTasks.length} active task(s)`
1016
+ : result.inProgressLock
1017
+ ? `In-progress lock found (started ${result.inProgressLock.elapsedSeconds}s ago)`
1018
+ : 'Status found'
1019
+ : 'No matching task found';
1020
+ return {
1021
+ content: [{ type: 'text', text: summary }],
1022
+ structuredContent: result,
1023
+ };
1024
+ }
1025
+ catch (err) {
1026
+ throw wrapPreflightError(err);
1027
+ }
1028
+ });
766
1029
  // Provide backward-compatible parsing of the same URI via resources/read for clients that bypass templates.
767
1030
  // This is a safety net: if a client gives us a fully-specified URI, we can still serve it.
768
1031
  server.registerResource('bundle-file-compat', 'preflight://bundle-file', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preflight-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "MCP server that creates evidence-based preflight bundles for GitHub repositories and library docs.",
5
5
  "type": "module",
6
6
  "license": "MIT",