@stackkedjohn/mcp-factory-cli 0.1.2 → 0.2.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.
@@ -0,0 +1,781 @@
1
+ # API Blueprint (.apib) Support Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Add support for parsing API Blueprint (.apib) format files to generate MCP servers, enabling users to use .apib documentation alongside OpenAPI/Swagger specs.
6
+
7
+ **Architecture:** Extend the existing parser pipeline with a new API Blueprint parser that transforms .apib AST into our unified APISchema format. Use the `protagonist` library for .apib parsing, following the same pattern as existing OpenAPI/Postman parsers.
8
+
9
+ **Tech Stack:** TypeScript, protagonist (API Blueprint parser), existing MCP Factory architecture
10
+
11
+ ---
12
+
13
+ ## Task 1: Add protagonist dependency and type definitions
14
+
15
+ **Files:**
16
+ - Modify: `package.json`
17
+ - Modify: `package-lock.json` (auto-updated)
18
+
19
+ **Step 1: Add protagonist npm package**
20
+
21
+ Run:
22
+ ```bash
23
+ npm install protagonist
24
+ ```
25
+
26
+ Expected: Package added to dependencies, package-lock.json updated
27
+
28
+ **Step 2: Add TypeScript types for protagonist**
29
+
30
+ Run:
31
+ ```bash
32
+ npm install --save-dev @types/protagonist
33
+ ```
34
+
35
+ Expected: Types added to devDependencies
36
+
37
+ Note: If @types/protagonist doesn't exist, we'll declare types inline in the parser file.
38
+
39
+ **Step 3: Commit dependency changes**
40
+
41
+ ```bash
42
+ git add package.json package-lock.json
43
+ git commit -m "chore: add protagonist for API Blueprint parsing"
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Task 2: Update format detector to recognize .apib files
49
+
50
+ **Files:**
51
+ - Modify: `src/parsers/detector.ts`
52
+
53
+ **Step 1: Write failing test for .apib detection**
54
+
55
+ Create: `src/parsers/detector.test.ts`
56
+
57
+ ```typescript
58
+ import { describe, it } from 'node:test';
59
+ import assert from 'node:assert';
60
+ import { detectFormat } from './detector.js';
61
+ import * as fs from 'fs/promises';
62
+ import * as path from 'path';
63
+
64
+ describe('Format Detector', () => {
65
+ it('should detect API Blueprint format from .apib file', async () => {
66
+ // Create temp .apib file
67
+ const tempFile = path.join(process.cwd(), 'test.apib');
68
+ await fs.writeFile(tempFile, 'FORMAT: 1A\n# My API\n## GET /users', 'utf-8');
69
+
70
+ try {
71
+ const result = await detectFormat(tempFile);
72
+ assert.strictEqual(result.format, 'apib');
73
+ assert.ok(result.content.includes('FORMAT: 1A'));
74
+ } finally {
75
+ await fs.unlink(tempFile);
76
+ }
77
+ });
78
+ });
79
+ ```
80
+
81
+ **Step 2: Run test to verify it fails**
82
+
83
+ Run:
84
+ ```bash
85
+ npm run build && node --test dist/parsers/detector.test.js
86
+ ```
87
+
88
+ Expected: FAIL - format should be 'apib' but got 'unknown'
89
+
90
+ **Step 3: Update detector.ts to add apib format type**
91
+
92
+ Modify: `src/parsers/detector.ts`
93
+
94
+ ```typescript
95
+ export type InputFormat = 'openapi' | 'swagger' | 'postman' | 'apib' | 'unknown';
96
+ ```
97
+
98
+ **Step 4: Update detector.ts to check for .apib extension**
99
+
100
+ Modify: `src/parsers/detector.ts` in the `detectFormat` function, add before JSON/YAML parsing:
101
+
102
+ ```typescript
103
+ export async function detectFormat(input: string): Promise<DetectionResult> {
104
+ let content: string;
105
+
106
+ // Check if input is a file path
107
+ try {
108
+ content = await fs.readFile(input, 'utf-8');
109
+ } catch {
110
+ throw new ParseError(`Could not read file: ${input}`);
111
+ }
112
+
113
+ // Check file extension for API Blueprint
114
+ if (input.endsWith('.apib') || input.endsWith('.apiblueprint')) {
115
+ return { format: 'apib', content };
116
+ }
117
+
118
+ // Alternatively, check content for API Blueprint markers
119
+ if (content.trim().startsWith('FORMAT: 1A')) {
120
+ return { format: 'apib', content };
121
+ }
122
+
123
+ // Rest of existing JSON/YAML parsing...
124
+ ```
125
+
126
+ **Step 5: Run test to verify it passes**
127
+
128
+ Run:
129
+ ```bash
130
+ npm run build && node --test dist/parsers/detector.test.js
131
+ ```
132
+
133
+ Expected: PASS - .apib files detected correctly
134
+
135
+ **Step 6: Commit detector changes**
136
+
137
+ ```bash
138
+ git add src/parsers/detector.ts src/parsers/detector.test.ts
139
+ git commit -m "feat: detect API Blueprint (.apib) format"
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Task 3: Create API Blueprint parser
145
+
146
+ **Files:**
147
+ - Create: `src/parsers/apib-parser.ts`
148
+ - Create: `src/parsers/apib-parser.test.ts`
149
+
150
+ **Step 1: Write failing test for API Blueprint parsing**
151
+
152
+ Create: `src/parsers/apib-parser.test.ts`
153
+
154
+ ```typescript
155
+ import { describe, it } from 'node:test';
156
+ import assert from 'node:assert';
157
+ import { parseAPIBlueprint } from './apib-parser.js';
158
+
159
+ describe('API Blueprint Parser', () => {
160
+ it('should parse a basic API Blueprint document', async () => {
161
+ const apibContent = `
162
+ FORMAT: 1A
163
+ HOST: https://api.example.com
164
+
165
+ # My API
166
+ This is my API description.
167
+
168
+ ## GET /users
169
+ Get a list of users
170
+
171
+ + Response 200 (application/json)
172
+ + Body
173
+
174
+ [
175
+ {
176
+ "id": 1,
177
+ "name": "John Doe"
178
+ }
179
+ ]
180
+
181
+ ## POST /users
182
+ Create a new user
183
+
184
+ + Request (application/json)
185
+ + Body
186
+
187
+ {
188
+ "name": "John Doe"
189
+ }
190
+
191
+ + Response 201 (application/json)
192
+ + Body
193
+
194
+ {
195
+ "id": 1,
196
+ "name": "John Doe"
197
+ }
198
+ `;
199
+
200
+ const schema = await parseAPIBlueprint(apibContent);
201
+
202
+ assert.strictEqual(schema.name, 'My API');
203
+ assert.strictEqual(schema.baseUrl, 'https://api.example.com');
204
+ assert.strictEqual(schema.endpoints.length, 2);
205
+
206
+ // Check GET endpoint
207
+ const getEndpoint = schema.endpoints.find(e => e.method === 'GET');
208
+ assert.ok(getEndpoint);
209
+ assert.strictEqual(getEndpoint.path, '/users');
210
+ assert.strictEqual(getEndpoint.description, 'Get a list of users');
211
+
212
+ // Check POST endpoint
213
+ const postEndpoint = schema.endpoints.find(e => e.method === 'POST');
214
+ assert.ok(postEndpoint);
215
+ assert.strictEqual(postEndpoint.path, '/users');
216
+ assert.strictEqual(postEndpoint.description, 'Create a new user');
217
+ });
218
+ });
219
+ ```
220
+
221
+ **Step 2: Run test to verify it fails**
222
+
223
+ Run:
224
+ ```bash
225
+ npm run build && node --test dist/parsers/apib-parser.test.js
226
+ ```
227
+
228
+ Expected: FAIL - parseAPIBlueprint is not defined
229
+
230
+ **Step 3: Create minimal API Blueprint parser implementation**
231
+
232
+ Create: `src/parsers/apib-parser.ts`
233
+
234
+ ```typescript
235
+ import protagonist from 'protagonist';
236
+ import { APISchema, Endpoint, AuthConfig, Parameter, ResponseSchema, ErrorSchema, SchemaType, RequestBody } from '../schema/api-schema.js';
237
+
238
+ export async function parseAPIBlueprint(content: string): Promise<APISchema> {
239
+ // Parse the API Blueprint content
240
+ const result = await protagonist.parse(content);
241
+
242
+ if (result.error) {
243
+ throw new Error(`API Blueprint parse error: ${result.error.message}`);
244
+ }
245
+
246
+ const ast = result.ast;
247
+
248
+ // Extract API name and description
249
+ const name = ast.name || 'Untitled API';
250
+ const baseUrl = extractBaseUrl(ast);
251
+
252
+ // Extract endpoints from resource groups
253
+ const endpoints: Endpoint[] = [];
254
+
255
+ for (const resourceGroup of ast.resourceGroups || []) {
256
+ for (const resource of resourceGroup.resources || []) {
257
+ for (const action of resource.actions || []) {
258
+ const endpoint = parseAction(resource, action);
259
+ endpoints.push(endpoint);
260
+ }
261
+ }
262
+ }
263
+
264
+ // Detect authentication (basic heuristic)
265
+ const auth: AuthConfig = detectAuth(ast);
266
+
267
+ return {
268
+ name,
269
+ baseUrl,
270
+ auth,
271
+ endpoints,
272
+ commonHeaders: {},
273
+ };
274
+ }
275
+
276
+ function extractBaseUrl(ast: any): string {
277
+ // Check metadata for HOST
278
+ if (ast.metadata) {
279
+ for (const meta of ast.metadata) {
280
+ if (meta.name === 'HOST') {
281
+ return meta.value;
282
+ }
283
+ }
284
+ }
285
+ return '';
286
+ }
287
+
288
+ function parseAction(resource: any, action: any): Endpoint {
289
+ const method = action.method.toUpperCase() as Endpoint['method'];
290
+ const path = resource.uriTemplate;
291
+ const description = action.description || action.name || '';
292
+
293
+ // Extract parameters from URI template and action parameters
294
+ const parameters: Parameter[] = parseParameters(resource, action);
295
+
296
+ // Extract request body if present
297
+ const requestBody = parseRequestBody(action);
298
+
299
+ // Extract response schema
300
+ const response = parseResponse(action);
301
+
302
+ // Extract error responses
303
+ const errors = parseErrors(action);
304
+
305
+ return {
306
+ id: `${method.toLowerCase()}${path.replace(/[^a-zA-Z0-9]/g, '_')}`,
307
+ method,
308
+ path,
309
+ description,
310
+ parameters,
311
+ requestBody,
312
+ response,
313
+ errors,
314
+ };
315
+ }
316
+
317
+ function parseParameters(resource: any, action: any): Parameter[] {
318
+ const parameters: Parameter[] = [];
319
+
320
+ // Parse URI parameters
321
+ if (resource.parameters) {
322
+ for (const param of resource.parameters) {
323
+ parameters.push({
324
+ name: param.name,
325
+ in: 'path',
326
+ description: param.description || '',
327
+ required: param.required || false,
328
+ schema: {
329
+ type: mapTypeToSchemaType(param.type),
330
+ },
331
+ });
332
+ }
333
+ }
334
+
335
+ // Parse action parameters (usually query/header)
336
+ if (action.parameters) {
337
+ for (const param of action.parameters) {
338
+ parameters.push({
339
+ name: param.name,
340
+ in: 'query', // Default to query, could be enhanced
341
+ description: param.description || '',
342
+ required: param.required || false,
343
+ schema: {
344
+ type: mapTypeToSchemaType(param.type),
345
+ },
346
+ });
347
+ }
348
+ }
349
+
350
+ return parameters;
351
+ }
352
+
353
+ function parseRequestBody(action: any): RequestBody | undefined {
354
+ // Find request with a body
355
+ const request = action.examples?.[0]?.requests?.[0];
356
+ if (!request || !request.body) {
357
+ return undefined;
358
+ }
359
+
360
+ return {
361
+ description: request.description || '',
362
+ required: true,
363
+ contentType: request.headers?.['Content-Type']?.[0]?.value || 'application/json',
364
+ schema: {
365
+ type: 'object', // Simplified - could parse JSON schema from body
366
+ },
367
+ };
368
+ }
369
+
370
+ function parseResponse(action: any): ResponseSchema {
371
+ // Get first successful response (200-299)
372
+ const response = action.examples?.[0]?.responses?.[0];
373
+
374
+ if (!response) {
375
+ return {
376
+ statusCode: 200,
377
+ description: '',
378
+ contentType: 'application/json',
379
+ schema: { type: 'object' },
380
+ };
381
+ }
382
+
383
+ return {
384
+ statusCode: parseInt(response.name) || 200,
385
+ description: response.description || '',
386
+ contentType: response.headers?.['Content-Type']?.[0]?.value || 'application/json',
387
+ schema: {
388
+ type: 'object', // Simplified - could parse JSON schema from body
389
+ },
390
+ };
391
+ }
392
+
393
+ function parseErrors(action: any): ErrorSchema[] {
394
+ const errors: ErrorSchema[] = [];
395
+
396
+ const responses = action.examples?.[0]?.responses || [];
397
+ for (const response of responses) {
398
+ const statusCode = parseInt(response.name);
399
+ if (statusCode >= 400) {
400
+ errors.push({
401
+ statusCode,
402
+ description: response.description || `Error ${statusCode}`,
403
+ });
404
+ }
405
+ }
406
+
407
+ return errors;
408
+ }
409
+
410
+ function detectAuth(ast: any): AuthConfig {
411
+ // Simple heuristic: check if any examples mention authentication
412
+ const astString = JSON.stringify(ast).toLowerCase();
413
+
414
+ if (astString.includes('bearer') || astString.includes('jwt')) {
415
+ return {
416
+ type: 'bearer',
417
+ location: 'header',
418
+ name: 'Authorization',
419
+ description: 'Bearer token authentication',
420
+ };
421
+ }
422
+
423
+ if (astString.includes('api-key') || astString.includes('apikey')) {
424
+ return {
425
+ type: 'api-key',
426
+ location: 'header',
427
+ name: 'X-API-Key',
428
+ description: 'API key authentication',
429
+ };
430
+ }
431
+
432
+ return {
433
+ type: 'none',
434
+ };
435
+ }
436
+
437
+ function mapTypeToSchemaType(type?: string): SchemaType['type'] {
438
+ if (!type) return 'string';
439
+
440
+ const lowerType = type.toLowerCase();
441
+ if (lowerType.includes('number') || lowerType.includes('integer')) return 'number';
442
+ if (lowerType.includes('bool')) return 'boolean';
443
+ if (lowerType.includes('array')) return 'array';
444
+ if (lowerType.includes('object')) return 'object';
445
+
446
+ return 'string';
447
+ }
448
+ ```
449
+
450
+ **Step 4: Run test to verify it passes**
451
+
452
+ Run:
453
+ ```bash
454
+ npm run build && node --test dist/parsers/apib-parser.test.js
455
+ ```
456
+
457
+ Expected: PASS - API Blueprint parsed correctly into APISchema
458
+
459
+ **Step 5: Commit API Blueprint parser**
460
+
461
+ ```bash
462
+ git add src/parsers/apib-parser.ts src/parsers/apib-parser.test.ts
463
+ git commit -m "feat: implement API Blueprint parser"
464
+ ```
465
+
466
+ ---
467
+
468
+ ## Task 4: Wire up API Blueprint parser in create command
469
+
470
+ **Files:**
471
+ - Modify: `src/commands/create.ts`
472
+
473
+ **Step 1: Write integration test**
474
+
475
+ Create: `test-fixtures/sample.apib`
476
+
477
+ ```apib
478
+ FORMAT: 1A
479
+ HOST: https://api.example.com
480
+
481
+ # Sample API
482
+ A simple API for testing
483
+
484
+ ## Users Collection [/users]
485
+
486
+ ### List Users [GET]
487
+ Get a list of all users
488
+
489
+ + Response 200 (application/json)
490
+ + Body
491
+
492
+ [
493
+ {"id": 1, "name": "Alice"},
494
+ {"id": 2, "name": "Bob"}
495
+ ]
496
+
497
+ ### Create User [POST]
498
+ Create a new user
499
+
500
+ + Request (application/json)
501
+ + Body
502
+
503
+ {"name": "Charlie"}
504
+
505
+ + Response 201 (application/json)
506
+ + Body
507
+
508
+ {"id": 3, "name": "Charlie"}
509
+ ```
510
+
511
+ **Step 2: Test manually to verify it fails**
512
+
513
+ Run:
514
+ ```bash
515
+ npm run build
516
+ node dist/cli.js create test-fixtures/sample.apib
517
+ ```
518
+
519
+ Expected: Error - "Could not detect format" or similar
520
+
521
+ **Step 3: Import API Blueprint parser in create command**
522
+
523
+ Modify: `src/commands/create.ts`
524
+
525
+ Add import at top:
526
+ ```typescript
527
+ import { parseAPIBlueprint } from '../parsers/apib-parser.js';
528
+ ```
529
+
530
+ **Step 4: Add API Blueprint case to parser switch**
531
+
532
+ Modify: `src/commands/create.ts` in the `createCommand` function:
533
+
534
+ ```typescript
535
+ // Parse to APISchema
536
+ let schema;
537
+ if (detection.format === 'openapi' || detection.format === 'swagger') {
538
+ schema = parseOpenAPI(detection.content);
539
+ } else if (detection.format === 'postman') {
540
+ schema = parsePostman(detection.content);
541
+ } else if (detection.format === 'apib') {
542
+ schema = await parseAPIBlueprint(detection.content);
543
+ } else if (options.aiParse) {
544
+ logger.info('Using AI parser for unstructured docs...');
545
+ schema = await parseWithAI(JSON.stringify(detection.content));
546
+ } else {
547
+ throw new ParseError('Could not detect format. Use --ai-parse for unstructured docs.');
548
+ }
549
+ ```
550
+
551
+ **Step 5: Test integration manually**
552
+
553
+ Run:
554
+ ```bash
555
+ npm run build
556
+ node dist/cli.js create test-fixtures/sample.apib
557
+ ```
558
+
559
+ Expected: Success - MCP server generated from .apib file
560
+
561
+ **Step 6: Verify generated server builds and runs**
562
+
563
+ Run:
564
+ ```bash
565
+ cd "Sample API-mcp"
566
+ npm install
567
+ npm run build
568
+ npm test
569
+ ```
570
+
571
+ Expected: All commands succeed
572
+
573
+ **Step 7: Commit integration**
574
+
575
+ ```bash
576
+ git add src/commands/create.ts test-fixtures/sample.apib
577
+ git commit -m "feat: integrate API Blueprint parser into create command"
578
+ ```
579
+
580
+ ---
581
+
582
+ ## Task 5: Update documentation
583
+
584
+ **Files:**
585
+ - Modify: `README.md`
586
+
587
+ **Step 1: Update supported formats section**
588
+
589
+ Modify: `README.md`
590
+
591
+ Find the "Supported Formats" section and update:
592
+
593
+ ```markdown
594
+ **Supported Formats:**
595
+ - OpenAPI 3.x (JSON/YAML)
596
+ - Swagger 2.0 (JSON/YAML)
597
+ - API Blueprint (.apib)
598
+ - Postman Collections (coming soon)
599
+ - Unstructured docs with `--ai-parse` (coming soon)
600
+ ```
601
+
602
+ **Step 2: Add API Blueprint example**
603
+
604
+ Add to examples section:
605
+
606
+ ```markdown
607
+ ### Example: API Blueprint
608
+
609
+ ```bash
610
+ # Generate from API Blueprint file
611
+ mcp-factory create ./api-documentation.apib
612
+
613
+ cd "My API-mcp"
614
+ npm install && npm run build
615
+ mcp-factory install "My API"
616
+ ```
617
+ ```
618
+
619
+ **Step 3: Update quick start if needed**
620
+
621
+ Ensure quick start mentions .apib support:
622
+
623
+ ```markdown
624
+ ## Quick Start
625
+
626
+ ```bash
627
+ # Generate MCP server from API documentation
628
+ # Supports: OpenAPI, Swagger, API Blueprint (.apib)
629
+ mcp-factory create ./api-docs.yaml
630
+
631
+ # Build the generated server
632
+ cd "My API-mcp"
633
+ npm install && npm run build
634
+
635
+ # Install to Claude Desktop
636
+ mcp-factory install "My API"
637
+ ```
638
+ ```
639
+
640
+ **Step 4: Commit documentation updates**
641
+
642
+ ```bash
643
+ git add README.md
644
+ git commit -m "docs: add API Blueprint format support to README"
645
+ ```
646
+
647
+ ---
648
+
649
+ ## Task 6: Version bump and publish preparation
650
+
651
+ **Files:**
652
+ - Modify: `package.json`
653
+
654
+ **Step 1: Run all tests to verify everything works**
655
+
656
+ Run:
657
+ ```bash
658
+ npm run build
659
+ npm test
660
+ ```
661
+
662
+ Expected: All tests pass
663
+
664
+ **Step 2: Test with real .apib file if available**
665
+
666
+ If user has a real .apib file:
667
+ ```bash
668
+ node dist/cli.js create /path/to/real-api.apib
669
+ ```
670
+
671
+ Verify it generates correctly.
672
+
673
+ **Step 3: Bump version**
674
+
675
+ Run:
676
+ ```bash
677
+ npm version minor -m "feat: add API Blueprint (.apib) format support"
678
+ ```
679
+
680
+ Expected: Version bumped from 0.1.2 to 0.2.0 (minor version for new feature)
681
+
682
+ **Step 4: Push changes and tag**
683
+
684
+ ```bash
685
+ git push
686
+ git push --tags
687
+ ```
688
+
689
+ **Step 5: Publish to npm**
690
+
691
+ ```bash
692
+ npm publish --access public
693
+ ```
694
+
695
+ Expected: Package published successfully
696
+
697
+ **Step 6: Verify installation**
698
+
699
+ ```bash
700
+ npm install -g @stackkedjohn/mcp-factory-cli
701
+ mcp-factory --version
702
+ ```
703
+
704
+ Expected: Shows new version 0.2.0
705
+
706
+ ---
707
+
708
+ ## Testing Checklist
709
+
710
+ After implementation, verify:
711
+
712
+ - [ ] .apib files are detected correctly by format detector
713
+ - [ ] API Blueprint parser extracts API name, base URL, and endpoints
714
+ - [ ] Generated MCP server from .apib builds without errors
715
+ - [ ] Generated server includes all endpoints from .apib
716
+ - [ ] Authentication is detected (at least basic heuristics)
717
+ - [ ] Parameters (path/query) are extracted correctly
718
+ - [ ] Request/response bodies are handled
719
+ - [ ] Error responses (4xx/5xx) are captured
720
+ - [ ] Generated server can be installed to Claude Desktop
721
+ - [ ] README accurately documents .apib support
722
+ - [ ] npm package published with new version
723
+
724
+ ---
725
+
726
+ ## Notes for Implementation
727
+
728
+ **Key Libraries:**
729
+ - `protagonist` - Official API Blueprint parser (Node.js wrapper around Drafter C++ library)
730
+ - AST structure follows API Blueprint spec
731
+
732
+ **Known Limitations:**
733
+ - Schema inference from example bodies is simplified (uses generic 'object' type)
734
+ - Authentication detection uses heuristics (not explicit .apib auth declarations)
735
+ - Could enhance with JSON Schema parsing from MSON attributes
736
+
737
+ **Future Enhancements:**
738
+ - Parse MSON (Markdown Syntax for Object Notation) for detailed schemas
739
+ - Better authentication parsing from Headers section
740
+ - Support for Data Structures section
741
+ - Validation against API Blueprint spec
742
+
743
+ **Protagonist AST Structure:**
744
+ ```typescript
745
+ {
746
+ ast: {
747
+ name: string;
748
+ description: string;
749
+ metadata: Array<{name: string, value: string}>;
750
+ resourceGroups: Array<{
751
+ name: string;
752
+ resources: Array<{
753
+ name: string;
754
+ uriTemplate: string;
755
+ parameters: Array<Parameter>;
756
+ actions: Array<{
757
+ name: string;
758
+ method: string;
759
+ description: string;
760
+ examples: Array<{
761
+ requests: Array<{body: string, headers: any}>;
762
+ responses: Array<{name: string, body: string, headers: any}>;
763
+ }>;
764
+ }>;
765
+ }>;
766
+ }>;
767
+ };
768
+ }
769
+ ```
770
+
771
+ ---
772
+
773
+ ## Execution Complete
774
+
775
+ After all tasks completed:
776
+
777
+ 1. Merge feature branch to main
778
+ 2. Verify npm package published
779
+ 3. Test global installation: `npm i -g @stackkedjohn/mcp-factory-cli`
780
+ 4. Test with real .apib file from user
781
+ 5. Update project documentation with new feature announcement