@stackkedjohn/mcp-factory-cli 0.2.2 → 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.
@@ -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
- for (const resourceGroup of ast.content) {
30
- if (resourceGroup.element === 'category' && hasClass(resourceGroup, 'resourceGroup')) {
31
- for (const resource of resourceGroup.content) {
32
- if (resource.element === 'resource') {
33
- const resourcePath = resource.attributes?.href?.content || '';
34
- for (const transition of resource.content) {
35
- if (transition.element === 'transition') {
36
- const transaction = transition.content.find((c) => c.element === 'httpTransaction');
37
- if (transaction) {
38
- const endpoint = parseAction(transaction, resourcePath, transition);
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
- // Also check for resources at the top level (like in api category)
47
- if (resourceGroup.element === 'category' && hasClass(resourceGroup, 'api')) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackkedjohn/mcp-factory-cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Generate production-ready MCP servers from API documentation",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -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
- for (const resourceGroup of ast.content) {
47
- if (resourceGroup.element === 'category' && hasClass(resourceGroup, 'resourceGroup')) {
48
- for (const resource of resourceGroup.content) {
49
- if (resource.element === 'resource') {
50
- const resourcePath = resource.attributes?.href?.content || '';
51
-
52
- for (const transition of resource.content) {
53
- if (transition.element === 'transition') {
54
- const transaction = transition.content.find((c: any) => c.element === 'httpTransaction');
55
- if (transaction) {
56
- const endpoint = parseAction(transaction, resourcePath, transition);
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
 
@@ -5,14 +5,14 @@ export const tools = [
5
5
  {{#each endpoints}}
6
6
  {
7
7
  name: '{{id}}',
8
- description: '{{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: '{{description}}',{{/if}}
15
+ {{#if description}}description: {{{json description}}},{{/if}}
16
16
  },
17
17
  {{/each}}
18
18
  },