@stackkedjohn/mcp-factory-cli 0.2.1 → 0.2.3
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/cli.js +7 -1
- package/dist/generator/engine.js +2 -0
- package/dist/parsers/apib-parser.js +15 -42
- package/package.json +1 -1
- package/src/cli.ts +8 -1
- package/src/generator/engine.ts +3 -0
- package/src/parsers/apib-parser.ts +17 -48
- package/templates/tools.ts.hbs +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
3
6
|
import { createCommand } from './commands/create.js';
|
|
4
7
|
import { validateCommand } from './commands/validate.js';
|
|
5
8
|
import { listCommand } from './commands/list.js';
|
|
6
9
|
import { installCommand } from './commands/install.js';
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
7
13
|
const program = new Command();
|
|
8
14
|
program
|
|
9
15
|
.name('mcp-factory')
|
|
10
16
|
.description('Generate production-ready MCP servers from API documentation')
|
|
11
|
-
.version(
|
|
17
|
+
.version(packageJson.version);
|
|
12
18
|
program
|
|
13
19
|
.command('create')
|
|
14
20
|
.description('Generate MCP server from API documentation')
|
package/dist/generator/engine.js
CHANGED
|
@@ -7,6 +7,8 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
// Register Handlebars helper for equality check
|
|
9
9
|
Handlebars.registerHelper('eq', (a, b) => a === b);
|
|
10
|
+
// Register Handlebars helper for JSON stringification
|
|
11
|
+
Handlebars.registerHelper('json', (value) => JSON.stringify(value));
|
|
10
12
|
export async function generateServer(schema, patterns, outputDir) {
|
|
11
13
|
const context = {
|
|
12
14
|
name: schema.name,
|
|
@@ -24,56 +24,29 @@ export async function parseAPIBlueprint(content) {
|
|
|
24
24
|
const apiMetadata = ast.content[0];
|
|
25
25
|
const name = apiMetadata.meta?.title?.content || 'Untitled API';
|
|
26
26
|
const baseUrl = extractBaseUrl(ast);
|
|
27
|
-
// Extract all endpoints
|
|
27
|
+
// Extract all endpoints with recursive category traversal
|
|
28
28
|
const endpoints = [];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
endpoints.push(endpoint);
|
|
40
|
-
}
|
|
29
|
+
function extractEndpoints(items) {
|
|
30
|
+
for (const item of items) {
|
|
31
|
+
if (item.element === 'resource') {
|
|
32
|
+
const resourcePath = item.attributes?.href?.content || '';
|
|
33
|
+
for (const transition of item.content || []) {
|
|
34
|
+
if (transition.element === 'transition') {
|
|
35
|
+
const transaction = transition.content?.find((c) => c.element === 'httpTransaction');
|
|
36
|
+
if (transaction) {
|
|
37
|
+
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
38
|
+
endpoints.push(endpoint);
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
for (const item of resourceGroup.content) {
|
|
49
|
-
if (item.element === 'resource') {
|
|
50
|
-
const resourcePath = item.attributes?.href?.content || '';
|
|
51
|
-
for (const transition of item.content) {
|
|
52
|
-
if (transition.element === 'transition') {
|
|
53
|
-
const transaction = transition.content.find((c) => c.element === 'httpTransaction');
|
|
54
|
-
if (transaction) {
|
|
55
|
-
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
56
|
-
endpoints.push(endpoint);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Also check for resources at the very top level
|
|
64
|
-
if (resourceGroup.element === 'resource') {
|
|
65
|
-
const resourcePath = resourceGroup.attributes?.href?.content || '';
|
|
66
|
-
for (const transition of resourceGroup.content) {
|
|
67
|
-
if (transition.element === 'transition') {
|
|
68
|
-
const transaction = transition.content.find((c) => c.element === 'httpTransaction');
|
|
69
|
-
if (transaction) {
|
|
70
|
-
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
71
|
-
endpoints.push(endpoint);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
43
|
+
else if (item.element === 'category' && item.content) {
|
|
44
|
+
// Recursively process nested categories
|
|
45
|
+
extractEndpoints(item.content);
|
|
74
46
|
}
|
|
75
47
|
}
|
|
76
48
|
}
|
|
49
|
+
extractEndpoints(ast.content);
|
|
77
50
|
// Detect authentication
|
|
78
51
|
const auth = detectAuth(ast);
|
|
79
52
|
return {
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
4
7
|
import { createCommand } from './commands/create.js';
|
|
5
8
|
import { validateCommand } from './commands/validate.js';
|
|
6
9
|
import { listCommand } from './commands/list.js';
|
|
7
10
|
import { installCommand } from './commands/install.js';
|
|
8
11
|
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
15
|
+
|
|
9
16
|
const program = new Command();
|
|
10
17
|
|
|
11
18
|
program
|
|
12
19
|
.name('mcp-factory')
|
|
13
20
|
.description('Generate production-ready MCP servers from API documentation')
|
|
14
|
-
.version(
|
|
21
|
+
.version(packageJson.version);
|
|
15
22
|
|
|
16
23
|
program
|
|
17
24
|
.command('create')
|
package/src/generator/engine.ts
CHANGED
|
@@ -11,6 +11,9 @@ const __dirname = path.dirname(__filename);
|
|
|
11
11
|
// Register Handlebars helper for equality check
|
|
12
12
|
Handlebars.registerHelper('eq', (a, b) => a === b);
|
|
13
13
|
|
|
14
|
+
// Register Handlebars helper for JSON stringification
|
|
15
|
+
Handlebars.registerHelper('json', (value) => JSON.stringify(value));
|
|
16
|
+
|
|
14
17
|
export interface GenerationContext {
|
|
15
18
|
name: string;
|
|
16
19
|
baseUrl: string;
|
|
@@ -40,63 +40,32 @@ export async function parseAPIBlueprint(content: string): Promise<APISchema> {
|
|
|
40
40
|
const name = apiMetadata.meta?.title?.content || 'Untitled API';
|
|
41
41
|
const baseUrl = extractBaseUrl(ast);
|
|
42
42
|
|
|
43
|
-
// Extract all endpoints
|
|
43
|
+
// Extract all endpoints with recursive category traversal
|
|
44
44
|
const endpoints: Endpoint[] = [];
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
endpoints.push(endpoint);
|
|
58
|
-
}
|
|
46
|
+
function extractEndpoints(items: any[]) {
|
|
47
|
+
for (const item of items) {
|
|
48
|
+
if (item.element === 'resource') {
|
|
49
|
+
const resourcePath = item.attributes?.href?.content || '';
|
|
50
|
+
|
|
51
|
+
for (const transition of item.content || []) {
|
|
52
|
+
if (transition.element === 'transition') {
|
|
53
|
+
const transaction = transition.content?.find((c: any) => c.element === 'httpTransaction');
|
|
54
|
+
if (transaction) {
|
|
55
|
+
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
56
|
+
endpoints.push(endpoint);
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
59
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Also check for resources at the top level (like in api category)
|
|
66
|
-
if (resourceGroup.element === 'category' && hasClass(resourceGroup, 'api')) {
|
|
67
|
-
for (const item of resourceGroup.content) {
|
|
68
|
-
if (item.element === 'resource') {
|
|
69
|
-
const resourcePath = item.attributes?.href?.content || '';
|
|
70
|
-
|
|
71
|
-
for (const transition of item.content) {
|
|
72
|
-
if (transition.element === 'transition') {
|
|
73
|
-
const transaction = transition.content.find((c: any) => c.element === 'httpTransaction');
|
|
74
|
-
if (transaction) {
|
|
75
|
-
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
76
|
-
endpoints.push(endpoint);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Also check for resources at the very top level
|
|
85
|
-
if (resourceGroup.element === 'resource') {
|
|
86
|
-
const resourcePath = resourceGroup.attributes?.href?.content || '';
|
|
87
|
-
|
|
88
|
-
for (const transition of resourceGroup.content) {
|
|
89
|
-
if (transition.element === 'transition') {
|
|
90
|
-
const transaction = transition.content.find((c: any) => c.element === 'httpTransaction');
|
|
91
|
-
if (transaction) {
|
|
92
|
-
const endpoint = parseAction(transaction, resourcePath, transition);
|
|
93
|
-
endpoints.push(endpoint);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
60
|
+
} else if (item.element === 'category' && item.content) {
|
|
61
|
+
// Recursively process nested categories
|
|
62
|
+
extractEndpoints(item.content);
|
|
96
63
|
}
|
|
97
64
|
}
|
|
98
65
|
}
|
|
99
66
|
|
|
67
|
+
extractEndpoints(ast.content);
|
|
68
|
+
|
|
100
69
|
// Detect authentication
|
|
101
70
|
const auth = detectAuth(ast);
|
|
102
71
|
|
package/templates/tools.ts.hbs
CHANGED
|
@@ -5,14 +5,14 @@ export const tools = [
|
|
|
5
5
|
{{#each endpoints}}
|
|
6
6
|
{
|
|
7
7
|
name: '{{id}}',
|
|
8
|
-
description:
|
|
8
|
+
description: {{{json description}}},
|
|
9
9
|
inputSchema: {
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
12
12
|
{{#each parameters}}
|
|
13
13
|
{{name}}: {
|
|
14
14
|
type: '{{schema.type}}',
|
|
15
|
-
{{#if description}}description:
|
|
15
|
+
{{#if description}}description: {{{json description}}},{{/if}}
|
|
16
16
|
},
|
|
17
17
|
{{/each}}
|
|
18
18
|
},
|