@g1cloud/api-gen 1.0.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.
- package/.claude/settings.local.json +22 -0
- package/CLAUDE.md +63 -0
- package/README.md +379 -0
- package/dist/analyzer/controllerAnalyzer.d.ts +20 -0
- package/dist/analyzer/controllerAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/controllerAnalyzer.js +101 -0
- package/dist/analyzer/controllerAnalyzer.js.map +1 -0
- package/dist/analyzer/parameterAnalyzer.d.ts +19 -0
- package/dist/analyzer/parameterAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/parameterAnalyzer.js +207 -0
- package/dist/analyzer/parameterAnalyzer.js.map +1 -0
- package/dist/analyzer/responseAnalyzer.d.ts +12 -0
- package/dist/analyzer/responseAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/responseAnalyzer.js +116 -0
- package/dist/analyzer/responseAnalyzer.js.map +1 -0
- package/dist/analyzer/schemaGenerator.d.ts +6 -0
- package/dist/analyzer/schemaGenerator.d.ts.map +1 -0
- package/dist/analyzer/schemaGenerator.js +347 -0
- package/dist/analyzer/schemaGenerator.js.map +1 -0
- package/dist/analyzer/securityAnalyzer.d.ts +6 -0
- package/dist/analyzer/securityAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/securityAnalyzer.js +177 -0
- package/dist/analyzer/securityAnalyzer.js.map +1 -0
- package/dist/generator/openapiGenerator.d.ts +14 -0
- package/dist/generator/openapiGenerator.d.ts.map +1 -0
- package/dist/generator/openapiGenerator.js +340 -0
- package/dist/generator/openapiGenerator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +218 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +61 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +199 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +257 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-server.mjs +45586 -0
- package/dist/parser/astAnalyzer.d.ts +87 -0
- package/dist/parser/astAnalyzer.d.ts.map +1 -0
- package/dist/parser/astAnalyzer.js +321 -0
- package/dist/parser/astAnalyzer.js.map +1 -0
- package/dist/parser/javaParser.d.ts +10 -0
- package/dist/parser/javaParser.d.ts.map +1 -0
- package/dist/parser/javaParser.js +805 -0
- package/dist/parser/javaParser.js.map +1 -0
- package/dist/types/index.d.ts +217 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/examples/CreateUserRequest.java +80 -0
- package/examples/DepartmentDTO.java +45 -0
- package/examples/Filter.java +39 -0
- package/examples/PaginatedList.java +71 -0
- package/examples/ProductController.java +136 -0
- package/examples/ProductDTO.java +129 -0
- package/examples/RoleDTO.java +47 -0
- package/examples/SearchParam.java +55 -0
- package/examples/Sort.java +70 -0
- package/examples/UpdateUserRequest.java +74 -0
- package/examples/UserController.java +98 -0
- package/examples/UserDTO.java +119 -0
- package/package.json +51 -0
- package/prompt/01_Initial.md +358 -0
- package/prompt/02_/354/266/224/352/260/200.md +31 -0
- package/src/analyzer/controllerAnalyzer.ts +125 -0
- package/src/analyzer/parameterAnalyzer.ts +259 -0
- package/src/analyzer/responseAnalyzer.ts +142 -0
- package/src/analyzer/schemaGenerator.ts +412 -0
- package/src/analyzer/securityAnalyzer.ts +200 -0
- package/src/generator/openapiGenerator.ts +378 -0
- package/src/index.ts +212 -0
- package/src/lib.ts +240 -0
- package/src/mcp-server.ts +310 -0
- package/src/parser/astAnalyzer.ts +373 -0
- package/src/parser/javaParser.ts +901 -0
- package/src/types/index.ts +238 -0
- package/test-boolean.yaml +607 -0
- package/test-filter.yaml +576 -0
- package/test-inner.ts +59 -0
- package/test-output.yaml +650 -0
- package/test-paginated.yaml +585 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +30 -0
package/src/lib.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library entry point for programmatic API generation
|
|
3
|
+
* This module exports the core functionality for use by MCP server and other integrations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as yaml from 'js-yaml';
|
|
9
|
+
import { CLIOptions, ProcessingContext, OpenAPISpec, ControllerInfo } from './types';
|
|
10
|
+
import { parseJavaSource } from './parser/javaParser';
|
|
11
|
+
import { analyzeControllers } from './analyzer/controllerAnalyzer';
|
|
12
|
+
import { generateOpenAPISpec, generateOpenAPISpecForController } from './generator/openapiGenerator';
|
|
13
|
+
|
|
14
|
+
export interface GenerateOptions {
|
|
15
|
+
/** Java source directories to parse */
|
|
16
|
+
source: string[];
|
|
17
|
+
/** Optional: specific directory to search for RestControllers */
|
|
18
|
+
apiSource?: string;
|
|
19
|
+
/** API title */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** API version */
|
|
22
|
+
version?: string;
|
|
23
|
+
/** API base path */
|
|
24
|
+
basePath?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GenerateResult {
|
|
28
|
+
/** Generated OpenAPI spec object */
|
|
29
|
+
spec: OpenAPISpec;
|
|
30
|
+
/** YAML string of the spec */
|
|
31
|
+
yaml: string;
|
|
32
|
+
/** Summary information */
|
|
33
|
+
summary: {
|
|
34
|
+
controllerCount: number;
|
|
35
|
+
endpointCount: number;
|
|
36
|
+
schemaCount: number;
|
|
37
|
+
controllers: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
endpointCount: number;
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface GeneratePerControllerResult {
|
|
45
|
+
/** Map of controller name to generated spec */
|
|
46
|
+
specs: Map<string, { spec: OpenAPISpec; yaml: string }>;
|
|
47
|
+
/** Summary information */
|
|
48
|
+
summary: {
|
|
49
|
+
controllerCount: number;
|
|
50
|
+
totalEndpointCount: number;
|
|
51
|
+
controllers: Array<{
|
|
52
|
+
name: string;
|
|
53
|
+
endpointCount: number;
|
|
54
|
+
schemaCount: number;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate OpenAPI specification from Spring Boot Java source code
|
|
61
|
+
* Returns a single combined spec for all controllers
|
|
62
|
+
*/
|
|
63
|
+
export async function generateOpenAPI(options: GenerateOptions): Promise<GenerateResult> {
|
|
64
|
+
const sources = options.source;
|
|
65
|
+
|
|
66
|
+
if (sources.length === 0) {
|
|
67
|
+
throw new Error('At least one source path is required');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Validate source paths exist
|
|
71
|
+
for (const src of sources) {
|
|
72
|
+
const sourcePath = path.resolve(src);
|
|
73
|
+
if (!fs.existsSync(sourcePath)) {
|
|
74
|
+
throw new Error(`Source path does not exist: ${sourcePath}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Parse Java files from all source directories
|
|
79
|
+
const javaClasses = new Map<string, any>();
|
|
80
|
+
|
|
81
|
+
for (const src of sources) {
|
|
82
|
+
const sourcePath = path.resolve(src);
|
|
83
|
+
const classes = await parseJavaSource(sourcePath);
|
|
84
|
+
for (const [name, cls] of classes) {
|
|
85
|
+
javaClasses.set(name, cls);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (javaClasses.size === 0) {
|
|
90
|
+
throw new Error('No Java files found in the specified directories');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Initialize processing context
|
|
94
|
+
const apiSourcePath = options.apiSource ? path.resolve(options.apiSource) : undefined;
|
|
95
|
+
const context: ProcessingContext = {
|
|
96
|
+
javaClasses,
|
|
97
|
+
controllers: [],
|
|
98
|
+
dtoSchemas: new Map(),
|
|
99
|
+
referencedTypes: new Set(),
|
|
100
|
+
apiSourcePath,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Analyze controllers
|
|
104
|
+
const controllers = analyzeControllers(context);
|
|
105
|
+
context.controllers = controllers;
|
|
106
|
+
|
|
107
|
+
if (controllers.length === 0) {
|
|
108
|
+
throw new Error('No REST controllers found in the source files');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Generate OpenAPI spec
|
|
112
|
+
const cliOptions: CLIOptions = {
|
|
113
|
+
source: sources,
|
|
114
|
+
apiSource: options.apiSource,
|
|
115
|
+
title: options.title || 'API Documentation',
|
|
116
|
+
version: options.version || '1.0.0',
|
|
117
|
+
basePath: options.basePath,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const spec = generateOpenAPISpec(controllers, context, cliOptions);
|
|
121
|
+
|
|
122
|
+
// Convert to YAML
|
|
123
|
+
const yamlContent = yaml.dump(spec, {
|
|
124
|
+
indent: 2,
|
|
125
|
+
lineWidth: -1,
|
|
126
|
+
noRefs: true,
|
|
127
|
+
sortKeys: false,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const totalEndpoints = controllers.reduce((sum, c) => sum + c.endpoints.length, 0);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
spec,
|
|
134
|
+
yaml: yamlContent,
|
|
135
|
+
summary: {
|
|
136
|
+
controllerCount: controllers.length,
|
|
137
|
+
endpointCount: totalEndpoints,
|
|
138
|
+
schemaCount: Object.keys(spec.components.schemas).length,
|
|
139
|
+
controllers: controllers.map(c => ({
|
|
140
|
+
name: c.className,
|
|
141
|
+
endpointCount: c.endpoints.length,
|
|
142
|
+
})),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate separate OpenAPI specifications per controller
|
|
149
|
+
*/
|
|
150
|
+
export async function generateOpenAPIPerController(options: GenerateOptions): Promise<GeneratePerControllerResult> {
|
|
151
|
+
const sources = options.source;
|
|
152
|
+
|
|
153
|
+
if (sources.length === 0) {
|
|
154
|
+
throw new Error('At least one source path is required');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Validate source paths exist
|
|
158
|
+
for (const src of sources) {
|
|
159
|
+
const sourcePath = path.resolve(src);
|
|
160
|
+
if (!fs.existsSync(sourcePath)) {
|
|
161
|
+
throw new Error(`Source path does not exist: ${sourcePath}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Parse Java files from all source directories
|
|
166
|
+
const javaClasses = new Map<string, any>();
|
|
167
|
+
|
|
168
|
+
for (const src of sources) {
|
|
169
|
+
const sourcePath = path.resolve(src);
|
|
170
|
+
const classes = await parseJavaSource(sourcePath);
|
|
171
|
+
for (const [name, cls] of classes) {
|
|
172
|
+
javaClasses.set(name, cls);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (javaClasses.size === 0) {
|
|
177
|
+
throw new Error('No Java files found in the specified directories');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Initialize processing context
|
|
181
|
+
const apiSourcePath = options.apiSource ? path.resolve(options.apiSource) : undefined;
|
|
182
|
+
const context: ProcessingContext = {
|
|
183
|
+
javaClasses,
|
|
184
|
+
controllers: [],
|
|
185
|
+
dtoSchemas: new Map(),
|
|
186
|
+
referencedTypes: new Set(),
|
|
187
|
+
apiSourcePath,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Analyze controllers
|
|
191
|
+
const controllers = analyzeControllers(context);
|
|
192
|
+
context.controllers = controllers;
|
|
193
|
+
|
|
194
|
+
if (controllers.length === 0) {
|
|
195
|
+
throw new Error('No REST controllers found in the source files');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Generate OpenAPI specs per controller
|
|
199
|
+
const cliOptions: CLIOptions = {
|
|
200
|
+
source: sources,
|
|
201
|
+
apiSource: options.apiSource,
|
|
202
|
+
title: options.title || 'API Documentation',
|
|
203
|
+
version: options.version || '1.0.0',
|
|
204
|
+
basePath: options.basePath,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const specs = new Map<string, { spec: OpenAPISpec; yaml: string }>();
|
|
208
|
+
const controllerSummaries: Array<{ name: string; endpointCount: number; schemaCount: number }> = [];
|
|
209
|
+
|
|
210
|
+
for (const controller of controllers) {
|
|
211
|
+
const spec = generateOpenAPISpecForController(controller, context, cliOptions);
|
|
212
|
+
const yamlContent = yaml.dump(spec, {
|
|
213
|
+
indent: 2,
|
|
214
|
+
lineWidth: -1,
|
|
215
|
+
noRefs: true,
|
|
216
|
+
sortKeys: false,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
specs.set(controller.className, { spec, yaml: yamlContent });
|
|
220
|
+
controllerSummaries.push({
|
|
221
|
+
name: controller.className,
|
|
222
|
+
endpointCount: controller.endpoints.length,
|
|
223
|
+
schemaCount: Object.keys(spec.components.schemas).length,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const totalEndpoints = controllers.reduce((sum, c) => sum + c.endpoints.length, 0);
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
specs,
|
|
231
|
+
summary: {
|
|
232
|
+
controllerCount: controllers.length,
|
|
233
|
+
totalEndpointCount: totalEndpoints,
|
|
234
|
+
controllers: controllerSummaries,
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Re-export types for consumers
|
|
240
|
+
export type { CLIOptions, ProcessingContext, OpenAPISpec, ControllerInfo } from './types';
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Server for Spring Boot to OpenAPI generation
|
|
5
|
+
*
|
|
6
|
+
* This server exposes the API generation functionality as MCP tools
|
|
7
|
+
* that can be called by AI assistants.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
11
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
|
+
import {
|
|
13
|
+
CallToolRequestSchema,
|
|
14
|
+
ListToolsRequestSchema,
|
|
15
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
16
|
+
import { generateOpenAPI, generateOpenAPIPerController } from './lib';
|
|
17
|
+
|
|
18
|
+
// Create MCP server
|
|
19
|
+
const server = new Server(
|
|
20
|
+
{
|
|
21
|
+
name: 'spring-to-openapi',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
capabilities: {
|
|
26
|
+
tools: {},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Define tool schemas
|
|
32
|
+
const tools = [
|
|
33
|
+
{
|
|
34
|
+
name: 'generate-openapi',
|
|
35
|
+
description:
|
|
36
|
+
'Generate OpenAPI v3 YAML specification from Spring Boot Java source code. Returns a single combined spec for all REST controllers found.',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object' as const,
|
|
39
|
+
properties: {
|
|
40
|
+
source: {
|
|
41
|
+
type: 'array',
|
|
42
|
+
items: { type: 'string' },
|
|
43
|
+
description: 'Java source code directory path(s) to parse',
|
|
44
|
+
},
|
|
45
|
+
apiSource: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description:
|
|
48
|
+
'Optional: specific directory to search for RestControllers (defaults to first source)',
|
|
49
|
+
},
|
|
50
|
+
title: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'API title (default: "API Documentation")',
|
|
53
|
+
},
|
|
54
|
+
version: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'API version (default: "1.0.0")',
|
|
57
|
+
},
|
|
58
|
+
basePath: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'API base path (e.g., "/api/v1")',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ['source'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'generate-openapi-per-controller',
|
|
68
|
+
description:
|
|
69
|
+
'Generate separate OpenAPI v3 YAML specifications for each REST controller. Useful when you need individual API docs per controller.',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object' as const,
|
|
72
|
+
properties: {
|
|
73
|
+
source: {
|
|
74
|
+
type: 'array',
|
|
75
|
+
items: { type: 'string' },
|
|
76
|
+
description: 'Java source code directory path(s) to parse',
|
|
77
|
+
},
|
|
78
|
+
apiSource: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Optional: specific directory to search for RestControllers',
|
|
81
|
+
},
|
|
82
|
+
title: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'API title prefix',
|
|
85
|
+
},
|
|
86
|
+
version: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'API version (default: "1.0.0")',
|
|
89
|
+
},
|
|
90
|
+
basePath: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'API base path',
|
|
93
|
+
},
|
|
94
|
+
controllerName: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
description: 'Optional: only generate for specific controller name',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
required: ['source'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'list-controllers',
|
|
104
|
+
description:
|
|
105
|
+
'List all REST controllers found in the Spring Boot source code without generating full specs. Useful for exploring the codebase first.',
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: 'object' as const,
|
|
108
|
+
properties: {
|
|
109
|
+
source: {
|
|
110
|
+
type: 'array',
|
|
111
|
+
items: { type: 'string' },
|
|
112
|
+
description: 'Java source code directory path(s) to parse',
|
|
113
|
+
},
|
|
114
|
+
apiSource: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'Optional: specific directory to search for RestControllers',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
required: ['source'],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
// Register tools list handler
|
|
125
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
126
|
+
return { tools };
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Register tool call handler
|
|
130
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
131
|
+
const { name, arguments: args } = request.params;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
switch (name) {
|
|
135
|
+
case 'generate-openapi': {
|
|
136
|
+
const { source, apiSource, title, version, basePath } = args as {
|
|
137
|
+
source: string[];
|
|
138
|
+
apiSource?: string;
|
|
139
|
+
title?: string;
|
|
140
|
+
version?: string;
|
|
141
|
+
basePath?: string;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const result = await generateOpenAPI({
|
|
145
|
+
source,
|
|
146
|
+
apiSource,
|
|
147
|
+
title,
|
|
148
|
+
version,
|
|
149
|
+
basePath,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const summaryText = [
|
|
153
|
+
'## Generation Summary',
|
|
154
|
+
`- Controllers: ${result.summary.controllerCount}`,
|
|
155
|
+
`- Endpoints: ${result.summary.endpointCount}`,
|
|
156
|
+
`- Schemas: ${result.summary.schemaCount}`,
|
|
157
|
+
'',
|
|
158
|
+
'### Controllers:',
|
|
159
|
+
...result.summary.controllers.map(
|
|
160
|
+
(c) => `- ${c.name}: ${c.endpointCount} endpoint(s)`
|
|
161
|
+
),
|
|
162
|
+
'',
|
|
163
|
+
'---',
|
|
164
|
+
'',
|
|
165
|
+
'## OpenAPI Specification (YAML)',
|
|
166
|
+
'',
|
|
167
|
+
'```yaml',
|
|
168
|
+
result.yaml,
|
|
169
|
+
'```',
|
|
170
|
+
].join('\n');
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: 'text', text: summaryText }],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case 'generate-openapi-per-controller': {
|
|
178
|
+
const { source, apiSource, title, version, basePath, controllerName } =
|
|
179
|
+
args as {
|
|
180
|
+
source: string[];
|
|
181
|
+
apiSource?: string;
|
|
182
|
+
title?: string;
|
|
183
|
+
version?: string;
|
|
184
|
+
basePath?: string;
|
|
185
|
+
controllerName?: string;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const result = await generateOpenAPIPerController({
|
|
189
|
+
source,
|
|
190
|
+
apiSource,
|
|
191
|
+
title,
|
|
192
|
+
version,
|
|
193
|
+
basePath,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (controllerName) {
|
|
197
|
+
const spec = result.specs.get(controllerName);
|
|
198
|
+
if (!spec) {
|
|
199
|
+
const available = Array.from(result.specs.keys()).join(', ');
|
|
200
|
+
return {
|
|
201
|
+
content: [
|
|
202
|
+
{
|
|
203
|
+
type: 'text',
|
|
204
|
+
text: `Controller "${controllerName}" not found. Available controllers: ${available}`,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
isError: true,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
text: [
|
|
216
|
+
`## OpenAPI Specification for ${controllerName}`,
|
|
217
|
+
'',
|
|
218
|
+
'```yaml',
|
|
219
|
+
spec.yaml,
|
|
220
|
+
'```',
|
|
221
|
+
].join('\n'),
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const parts: string[] = [
|
|
228
|
+
'## Generation Summary',
|
|
229
|
+
`- Total Controllers: ${result.summary.controllerCount}`,
|
|
230
|
+
`- Total Endpoints: ${result.summary.totalEndpointCount}`,
|
|
231
|
+
'',
|
|
232
|
+
'### Controllers:',
|
|
233
|
+
...result.summary.controllers.map(
|
|
234
|
+
(c) =>
|
|
235
|
+
`- ${c.name}: ${c.endpointCount} endpoint(s), ${c.schemaCount} schema(s)`
|
|
236
|
+
),
|
|
237
|
+
'',
|
|
238
|
+
'---',
|
|
239
|
+
'',
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const [specName, spec] of result.specs) {
|
|
243
|
+
parts.push(`## ${specName}`);
|
|
244
|
+
parts.push('');
|
|
245
|
+
parts.push('```yaml');
|
|
246
|
+
parts.push(spec.yaml);
|
|
247
|
+
parts.push('```');
|
|
248
|
+
parts.push('');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
case 'list-controllers': {
|
|
257
|
+
const { source, apiSource } = args as {
|
|
258
|
+
source: string[];
|
|
259
|
+
apiSource?: string;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const result = await generateOpenAPI({
|
|
263
|
+
source,
|
|
264
|
+
apiSource,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const parts: string[] = [
|
|
268
|
+
'## REST Controllers Found',
|
|
269
|
+
'',
|
|
270
|
+
`Total: ${result.summary.controllerCount} controller(s) with ${result.summary.endpointCount} endpoint(s)`,
|
|
271
|
+
'',
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
for (const controller of result.summary.controllers) {
|
|
275
|
+
parts.push(`### ${controller.name}`);
|
|
276
|
+
parts.push(`- Endpoints: ${controller.endpointCount}`);
|
|
277
|
+
parts.push('');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
default:
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
288
|
+
isError: true,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
295
|
+
isError: true,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Start the server
|
|
301
|
+
async function main() {
|
|
302
|
+
const transport = new StdioServerTransport();
|
|
303
|
+
await server.connect(transport);
|
|
304
|
+
console.error('Spring-to-OpenAPI MCP Server running on stdio');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
main().catch((error) => {
|
|
308
|
+
console.error('Fatal error:', error);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
});
|