@christopher_dondici/mcp-gen 2.1.2
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/CHANGELOG.md +217 -0
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/README.pt-BR.md +320 -0
- package/RELEASE_NOTES.md +98 -0
- package/SECURITY.md +75 -0
- package/SECURITY.pt-BR.md +77 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +685 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/generator.d.ts +4 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +275 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/incremental.d.ts +25 -0
- package/dist/core/incremental.d.ts.map +1 -0
- package/dist/core/incremental.js +91 -0
- package/dist/core/incremental.js.map +1 -0
- package/dist/core/parser.d.ts +3 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +372 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +107 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/security-lint.d.ts +53 -0
- package/dist/core/security-lint.d.ts.map +1 -0
- package/dist/core/security-lint.js +470 -0
- package/dist/core/security-lint.js.map +1 -0
- package/dist/core/security.d.ts +41 -0
- package/dist/core/security.d.ts.map +1 -0
- package/dist/core/security.js +150 -0
- package/dist/core/security.js.map +1 -0
- package/dist/core/templating.d.ts +5 -0
- package/dist/core/templating.d.ts.map +1 -0
- package/dist/core/templating.js +211 -0
- package/dist/core/templating.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/go/Dockerfile.hbs +16 -0
- package/dist/templates/go/README.md.hbs +77 -0
- package/dist/templates/go/ci.yml.hbs +28 -0
- package/dist/templates/go/client.go.hbs +86 -0
- package/dist/templates/go/go.mod.hbs +5 -0
- package/dist/templates/go/models.go.hbs +28 -0
- package/dist/templates/go/server.go.hbs +220 -0
- package/dist/templates/python/Dockerfile.hbs +12 -0
- package/dist/templates/python/README.md.hbs +115 -0
- package/dist/templates/python/ci.yml.hbs +24 -0
- package/dist/templates/python/models.py.hbs +20 -0
- package/dist/templates/python/requirements.txt.hbs +3 -0
- package/dist/templates/python/server.py.hbs +195 -0
- package/dist/templates/typescript/Dockerfile.hbs +18 -0
- package/dist/templates/typescript/README.md.hbs +91 -0
- package/dist/templates/typescript/ci.yml.hbs +28 -0
- package/dist/templates/typescript/client.hbs +49 -0
- package/dist/templates/typescript/models.hbs +23 -0
- package/dist/templates/typescript/package.json.hbs +26 -0
- package/dist/templates/typescript/server.hbs +337 -0
- package/dist/templates/typescript/tsconfig.json.hbs +17 -0
- package/examples/petstore.json +130 -0
- package/examples/petstore.yaml +131 -0
- package/examples/week3.json +47 -0
- package/package.json +64 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ErrorCode,
|
|
6
|
+
ListResourcesRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
McpError,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { ApiClient } from "./client.js";
|
|
11
|
+
|
|
12
|
+
// Auto-generated by mcp-gen v{{generatorVersion}}
|
|
13
|
+
// Spec: {{info.title}} v{{info.version}}
|
|
14
|
+
// Generated: {{generatedAt}}
|
|
15
|
+
{{#if incremental}}
|
|
16
|
+
// Incremental mode: edit code between @@mcp-gen markers — it will be preserved on re-generation.
|
|
17
|
+
{{/if}}
|
|
18
|
+
|
|
19
|
+
const BASE_URL = "{{baseUrl}}";
|
|
20
|
+
const API_TOKEN = typeof process !== "undefined" ? process.env.TOKEN : undefined;
|
|
21
|
+
const client = new ApiClient(BASE_URL, API_TOKEN);
|
|
22
|
+
|
|
23
|
+
// ─── Logging ─────────────────────────────────────────────────────────────────
|
|
24
|
+
// IMPORTANT: This server uses stdio transport. stdout is reserved for JSON-RPC.
|
|
25
|
+
// Never use console.log() — it will corrupt the MCP stream.
|
|
26
|
+
// Always use log() (writes to stderr) for any debug output.
|
|
27
|
+
const log = (...args: unknown[]) => console.error("[{{serverName}}]", ...args);
|
|
28
|
+
|
|
29
|
+
// ─── Serialization ───────────────────────────────────────────────────────────
|
|
30
|
+
// Safely converts any handler return value to a string for the MCP text content.
|
|
31
|
+
// Handles: objects, arrays, primitives, Buffers, Uint8Arrays (as base64), and
|
|
32
|
+
// non-serializable values (circular refs, undefined, functions).
|
|
33
|
+
function safeSerialize(value: unknown): string {
|
|
34
|
+
if (value === undefined || value === null) return String(value);
|
|
35
|
+
if (typeof value === "string") return value;
|
|
36
|
+
if (typeof value !== "object") return String(value);
|
|
37
|
+
|
|
38
|
+
// Binary data — encode as base64 instead of corrupting the stream
|
|
39
|
+
if (Buffer.isBuffer(value)) return value.toString("base64");
|
|
40
|
+
if (value instanceof Uint8Array) return Buffer.from(value).toString("base64");
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
return JSON.stringify(value, null, 2);
|
|
44
|
+
} catch {
|
|
45
|
+
// Circular references or non-serializable values
|
|
46
|
+
return String(value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const server = new Server(
|
|
51
|
+
{ name: "{{serverName}}", version: "{{serverVersion}}" },
|
|
52
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// ─── Security helpers (scoped resource policy by default) ───────────────────
|
|
56
|
+
{{#if securitySchemes}}
|
|
57
|
+
const securitySchemes = {{{json securitySchemes}}};
|
|
58
|
+
{{else}}
|
|
59
|
+
const securitySchemes: Record<string, unknown> = {};
|
|
60
|
+
{{/if}}
|
|
61
|
+
|
|
62
|
+
type ToolAuthContext = {
|
|
63
|
+
tokenId?: string;
|
|
64
|
+
principal?: string;
|
|
65
|
+
expiresAt?: string;
|
|
66
|
+
allowedTools?: string[];
|
|
67
|
+
endpointAllowlist?: string[];
|
|
68
|
+
spendLimitUsd?: number;
|
|
69
|
+
spendUsedUsd?: number;
|
|
70
|
+
revoked?: boolean;
|
|
71
|
+
requestId?: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type ToolPolicy = {
|
|
75
|
+
endpoint: string;
|
|
76
|
+
requiresTtl: boolean;
|
|
77
|
+
requiresSpendLimit: boolean;
|
|
78
|
+
requiresRequestLog: boolean;
|
|
79
|
+
requiresRevocationCheck: boolean;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const rawCredentialKeys = [
|
|
83
|
+
"authorization",
|
|
84
|
+
"token",
|
|
85
|
+
"access_token",
|
|
86
|
+
"api_key",
|
|
87
|
+
"apikey",
|
|
88
|
+
"x-api-key",
|
|
89
|
+
"client_secret",
|
|
90
|
+
"refresh_token",
|
|
91
|
+
"password",
|
|
92
|
+
"secret",
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const toolPolicies: Record<string, ToolPolicy> = {
|
|
96
|
+
{{#each tools}}
|
|
97
|
+
"{{name}}": {
|
|
98
|
+
endpoint: "{{method}} {{path}}",
|
|
99
|
+
requiresTtl: true,
|
|
100
|
+
requiresSpendLimit: true,
|
|
101
|
+
requiresRequestLog: true,
|
|
102
|
+
requiresRevocationCheck: true,
|
|
103
|
+
},
|
|
104
|
+
{{/each}}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
function hasRawCredentialKey(value: unknown): boolean {
|
|
108
|
+
if (!value || typeof value !== "object") return false;
|
|
109
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
110
|
+
for (const [key, nested] of entries) {
|
|
111
|
+
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
112
|
+
if (rawCredentialKeys.some((candidate) => normalized === candidate.replace(/[^a-z0-9]/g, ""))) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (nested && typeof nested === "object" && hasRawCredentialKey(nested)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function extractCustomArgs(args: Record<string, unknown> | undefined): Record<string, unknown> {
|
|
123
|
+
const out: Record<string, unknown> = {};
|
|
124
|
+
if (!args) return out;
|
|
125
|
+
for (const [k, v] of Object.entries(args)) {
|
|
126
|
+
if (k === "authContext") continue;
|
|
127
|
+
out[k] = v;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getAuthContext(args: Record<string, unknown> | undefined): ToolAuthContext | undefined {
|
|
133
|
+
const ctx = args?.authContext;
|
|
134
|
+
if (!ctx || typeof ctx !== "object") return undefined;
|
|
135
|
+
return ctx as ToolAuthContext;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function ensureNoRawCredentials(args: Record<string, unknown> | undefined): void {
|
|
139
|
+
if (!args) return;
|
|
140
|
+
if (hasRawCredentialKey(args)) {
|
|
141
|
+
throw new McpError(
|
|
142
|
+
ErrorCode.InvalidParams,
|
|
143
|
+
"Raw provider credentials are not allowed in tool arguments. Use scoped authContext metadata only."
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isExpired(expiresAt: string): boolean {
|
|
149
|
+
const ts = Date.parse(expiresAt);
|
|
150
|
+
if (Number.isNaN(ts)) return true;
|
|
151
|
+
return ts <= Date.now();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function requireSecurity(toolName: string, args: Record<string, unknown> | undefined, security: unknown): void {
|
|
155
|
+
ensureNoRawCredentials(args);
|
|
156
|
+
const policy = toolPolicies[toolName];
|
|
157
|
+
const authContext = getAuthContext(args);
|
|
158
|
+
|
|
159
|
+
if (!policy) {
|
|
160
|
+
throw new McpError(ErrorCode.InternalError, `Missing security policy for tool: ${toolName}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!authContext) {
|
|
164
|
+
throw new McpError(
|
|
165
|
+
ErrorCode.InvalidParams,
|
|
166
|
+
"Missing authContext. Provide scoped metadata (tokenId, expiresAt, limits, requestId) from your gateway."
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (policy.requiresRevocationCheck && authContext.revoked) {
|
|
171
|
+
throw new McpError(ErrorCode.InvalidRequest, "Access revoked");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (policy.requiresTtl && (!authContext.expiresAt || isExpired(authContext.expiresAt))) {
|
|
175
|
+
throw new McpError(ErrorCode.InvalidRequest, "Token expired or missing TTL");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (policy.requiresRequestLog && !authContext.requestId) {
|
|
179
|
+
throw new McpError(ErrorCode.InvalidParams, "requestId is required for audit logging");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (policy.requiresSpendLimit) {
|
|
183
|
+
const limit = authContext.spendLimitUsd;
|
|
184
|
+
const used = authContext.spendUsedUsd;
|
|
185
|
+
if (typeof limit !== "number" || typeof used !== "number") {
|
|
186
|
+
throw new McpError(ErrorCode.InvalidParams, "Spend limit metadata is required");
|
|
187
|
+
}
|
|
188
|
+
if (used > limit) {
|
|
189
|
+
throw new McpError(ErrorCode.InvalidRequest, "Spend limit exceeded");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (authContext.allowedTools && !authContext.allowedTools.includes(toolName)) {
|
|
194
|
+
throw new McpError(ErrorCode.InvalidRequest, `Tool not allowed by scope: ${toolName}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (authContext.endpointAllowlist && !authContext.endpointAllowlist.includes(policy.endpoint)) {
|
|
198
|
+
throw new McpError(ErrorCode.InvalidRequest, `Endpoint not allowed: ${policy.endpoint}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!security) return;
|
|
202
|
+
try {
|
|
203
|
+
const secArray = security as Array<Record<string, string[]>>;
|
|
204
|
+
if (!Array.isArray(secArray) || secArray.length === 0) return;
|
|
205
|
+
const req = secArray[0] ?? {};
|
|
206
|
+
const requiresScheme = Object.keys(req).length > 0;
|
|
207
|
+
if (!requiresScheme) return;
|
|
208
|
+
if (!authContext.tokenId) {
|
|
209
|
+
throw new McpError(ErrorCode.InvalidRequest, "Missing tokenId for secured operation");
|
|
210
|
+
}
|
|
211
|
+
const declaredScheme = Object.keys(req).some((schemeName) => {
|
|
212
|
+
const scheme = (securitySchemes as Record<string, unknown> | null)?.[schemeName];
|
|
213
|
+
return Boolean(scheme);
|
|
214
|
+
});
|
|
215
|
+
if (!declaredScheme) {
|
|
216
|
+
throw new McpError(ErrorCode.InvalidRequest, "Declared security scheme not found in OpenAPI components");
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
throw new McpError(ErrorCode.InvalidRequest, "Unauthorized");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
{{#if requiresAuth}}
|
|
224
|
+
function requireApiAuth(): void {
|
|
225
|
+
if (!API_TOKEN) {
|
|
226
|
+
throw new McpError(
|
|
227
|
+
ErrorCode.InvalidRequest,
|
|
228
|
+
"This server uses an authenticated API. Set the TOKEN environment variable before starting."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
{{/if}}
|
|
233
|
+
|
|
234
|
+
// ─── Tool Definitions ────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
237
|
+
tools: [
|
|
238
|
+
{{#each tools}}
|
|
239
|
+
{
|
|
240
|
+
name: "{{name}}",
|
|
241
|
+
description: "{{escapeText description}}",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object" as const,
|
|
244
|
+
properties: {
|
|
245
|
+
{{#each params}}
|
|
246
|
+
{{name}}: { type: "{{type}}"{{#if (ne type "object")}}{{#if format}}, format: "{{format}}"{{/if}}{{#if enum}}, enum: {{{json enum}}}{{/if}}{{/if}}, description: "{{escapeText description}}" },
|
|
247
|
+
{{/each}}
|
|
248
|
+
authContext: {
|
|
249
|
+
type: "object" as const,
|
|
250
|
+
description: "Scoped auth metadata from a gateway (no raw provider credentials).",
|
|
251
|
+
properties: {
|
|
252
|
+
tokenId: { type: "string" as const },
|
|
253
|
+
principal: { type: "string" as const },
|
|
254
|
+
expiresAt: { type: "string" as const },
|
|
255
|
+
allowedTools: { type: "array" as const, items: { type: "string" as const } },
|
|
256
|
+
endpointAllowlist: { type: "array" as const, items: { type: "string" as const } },
|
|
257
|
+
spendLimitUsd: { type: "number" as const },
|
|
258
|
+
spendUsedUsd: { type: "number" as const },
|
|
259
|
+
revoked: { type: "boolean" as const },
|
|
260
|
+
requestId: { type: "string" as const },
|
|
261
|
+
},
|
|
262
|
+
additionalProperties: false,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
required: [{{#each params}}{{#if required}}"{{name}}",{{/if}}{{/each}}],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
{{/each}}
|
|
269
|
+
],
|
|
270
|
+
}));
|
|
271
|
+
|
|
272
|
+
{{#if http}}
|
|
273
|
+
// Exported helper so the client can assemble HTTP requests from a tool + params.
|
|
274
|
+
function buildHttpArgs(args: Record<string, unknown> | undefined): Record<string, unknown> {
|
|
275
|
+
return extractCustomArgs(args);
|
|
276
|
+
}
|
|
277
|
+
{{/if}}
|
|
278
|
+
|
|
279
|
+
// ─── Tool Handlers ───────────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
282
|
+
const { name, arguments: args } = request.params;
|
|
283
|
+
|
|
284
|
+
switch (name) {
|
|
285
|
+
{{#each tools}}
|
|
286
|
+
case "{{name}}": {
|
|
287
|
+
// {{method}} {{path}}
|
|
288
|
+
// @@mcp-gen:start:{{name}}
|
|
289
|
+
requireSecurity("{{name}}", args as Record<string, unknown> | undefined, {{#if security}}{{json security}}{{else}}null{{/if}});
|
|
290
|
+
{{#if ../http}}
|
|
291
|
+
{{#if ../requiresAuth}}requireApiAuth();{{/if}}
|
|
292
|
+
const out = await client.{{name}}(extractCustomArgs(args as Record<string, unknown> | undefined));
|
|
293
|
+
return { content: [{ type: "text" as const, text: safeSerialize(out) }] };
|
|
294
|
+
{{else}}
|
|
295
|
+
{{#if exampleResponse}}
|
|
296
|
+
return {
|
|
297
|
+
content: [{ type: "text" as const, text: safeSerialize({{json exampleResponse}}) }],
|
|
298
|
+
};
|
|
299
|
+
{{else}}
|
|
300
|
+
throw new McpError(ErrorCode.InternalError, "Handler not implemented: {{name}}");
|
|
301
|
+
{{/if}}
|
|
302
|
+
{{/if}}
|
|
303
|
+
// @@mcp-gen:end:{{name}}
|
|
304
|
+
}
|
|
305
|
+
{{/each}}
|
|
306
|
+
default:
|
|
307
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// ─── Resources (from OpenAPI parameter schemas) ────────────────────────────
|
|
312
|
+
|
|
313
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
314
|
+
resources: [
|
|
315
|
+
{{#each tools}}
|
|
316
|
+
{
|
|
317
|
+
uri: "{{path}}",
|
|
318
|
+
name: "{{escapeText description}}",
|
|
319
|
+
description: "{{method}} {{path}} — {{escapeText description}}",
|
|
320
|
+
mimeType: "application/json",
|
|
321
|
+
},
|
|
322
|
+
{{/each}}
|
|
323
|
+
],
|
|
324
|
+
}));
|
|
325
|
+
|
|
326
|
+
// ─── Start ───────────────────────────────────────────────────────────────────
|
|
327
|
+
|
|
328
|
+
async function main(): Promise<void> {
|
|
329
|
+
const transport = new StdioServerTransport();
|
|
330
|
+
await server.connect(transport);
|
|
331
|
+
log("running on stdio");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
main().catch((err) => {
|
|
335
|
+
log("Fatal:", err);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.3",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Pet Store",
|
|
5
|
+
"description": "A sample Pet Store API to test mcp-gen",
|
|
6
|
+
"version": "1.0.0"
|
|
7
|
+
},
|
|
8
|
+
"servers": [{ "url": "https://petstore.example.com/v1" }],
|
|
9
|
+
"paths": {
|
|
10
|
+
"/pets": {
|
|
11
|
+
"get": {
|
|
12
|
+
"summary": "List all pets",
|
|
13
|
+
"operationId": "listPets",
|
|
14
|
+
"tags": ["pets"],
|
|
15
|
+
"parameters": [
|
|
16
|
+
{
|
|
17
|
+
"name": "limit",
|
|
18
|
+
"in": "query",
|
|
19
|
+
"description": "Maximum number of pets to return",
|
|
20
|
+
"required": false,
|
|
21
|
+
"schema": { "type": "integer", "maximum": 100 }
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"responses": {
|
|
25
|
+
"200": {
|
|
26
|
+
"description": "A list of pets",
|
|
27
|
+
"content": {
|
|
28
|
+
"application/json": {
|
|
29
|
+
"schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } },
|
|
30
|
+
"example": [
|
|
31
|
+
{ "id": 1, "name": "Whiskers", "tag": "cat" },
|
|
32
|
+
{ "id": 2, "name": "Rex", "tag": "dog" }
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"post": {
|
|
40
|
+
"summary": "Create a pet",
|
|
41
|
+
"operationId": "createPet",
|
|
42
|
+
"tags": ["pets"],
|
|
43
|
+
"requestBody": {
|
|
44
|
+
"required": true,
|
|
45
|
+
"content": {
|
|
46
|
+
"application/json": {
|
|
47
|
+
"schema": { "$ref": "#/components/schemas/NewPet" }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"responses": {
|
|
52
|
+
"201": {
|
|
53
|
+
"description": "Pet created",
|
|
54
|
+
"content": {
|
|
55
|
+
"application/json": {
|
|
56
|
+
"schema": { "$ref": "#/components/schemas/Pet" },
|
|
57
|
+
"example": { "id": 3, "name": "Fluffy", "tag": "rabbit" }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"/pets/{petId}": {
|
|
65
|
+
"get": {
|
|
66
|
+
"summary": "Get a pet by ID",
|
|
67
|
+
"operationId": "getPetById",
|
|
68
|
+
"tags": ["pets"],
|
|
69
|
+
"parameters": [
|
|
70
|
+
{
|
|
71
|
+
"name": "petId",
|
|
72
|
+
"in": "path",
|
|
73
|
+
"required": true,
|
|
74
|
+
"description": "The ID of the pet to retrieve",
|
|
75
|
+
"schema": { "type": "integer" }
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"responses": {
|
|
79
|
+
"200": {
|
|
80
|
+
"description": "The pet",
|
|
81
|
+
"content": {
|
|
82
|
+
"application/json": {
|
|
83
|
+
"schema": { "$ref": "#/components/schemas/Pet" },
|
|
84
|
+
"example": { "id": 1, "name": "Whiskers", "tag": "cat" }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"delete": {
|
|
91
|
+
"summary": "Delete a pet",
|
|
92
|
+
"operationId": "deletePet",
|
|
93
|
+
"tags": ["pets"],
|
|
94
|
+
"parameters": [
|
|
95
|
+
{
|
|
96
|
+
"name": "petId",
|
|
97
|
+
"in": "path",
|
|
98
|
+
"required": true,
|
|
99
|
+
"description": "The ID of the pet to delete",
|
|
100
|
+
"schema": { "type": "integer" }
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"responses": { "204": { "description": "Pet deleted" } }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"components": {
|
|
108
|
+
"schemas": {
|
|
109
|
+
"Pet": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"description": "A pet in the store",
|
|
112
|
+
"required": ["id", "name"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"id": { "type": "integer", "description": "Unique identifier" },
|
|
115
|
+
"name": { "type": "string", "description": "Name of the pet" },
|
|
116
|
+
"tag": { "type": "string", "description": "Type/species of the pet" }
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"NewPet": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"description": "Payload for creating a new pet",
|
|
122
|
+
"required": ["name"],
|
|
123
|
+
"properties": {
|
|
124
|
+
"name": { "type": "string", "description": "Name of the pet" },
|
|
125
|
+
"tag": { "type": "string", "description": "Type/species of the pet" }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
openapi: "3.0.3"
|
|
2
|
+
info:
|
|
3
|
+
title: Pet Store
|
|
4
|
+
description: A sample Pet Store API to test mcp-gen YAML support
|
|
5
|
+
version: "1.0.0"
|
|
6
|
+
servers:
|
|
7
|
+
- url: https://petstore.example.com/v1
|
|
8
|
+
paths:
|
|
9
|
+
/pets:
|
|
10
|
+
get:
|
|
11
|
+
summary: List all pets
|
|
12
|
+
operationId: listPets
|
|
13
|
+
tags:
|
|
14
|
+
- pets
|
|
15
|
+
parameters:
|
|
16
|
+
- name: limit
|
|
17
|
+
in: query
|
|
18
|
+
description: Maximum number of pets to return
|
|
19
|
+
required: false
|
|
20
|
+
schema:
|
|
21
|
+
type: integer
|
|
22
|
+
maximum: 100
|
|
23
|
+
responses:
|
|
24
|
+
"200":
|
|
25
|
+
description: A list of pets
|
|
26
|
+
content:
|
|
27
|
+
application/json:
|
|
28
|
+
schema:
|
|
29
|
+
type: array
|
|
30
|
+
items:
|
|
31
|
+
$ref: "#/components/schemas/Pet"
|
|
32
|
+
example:
|
|
33
|
+
- id: 1
|
|
34
|
+
name: Whiskers
|
|
35
|
+
tag: cat
|
|
36
|
+
- id: 2
|
|
37
|
+
name: Rex
|
|
38
|
+
tag: dog
|
|
39
|
+
post:
|
|
40
|
+
summary: Create a pet
|
|
41
|
+
operationId: createPet
|
|
42
|
+
tags:
|
|
43
|
+
- pets
|
|
44
|
+
requestBody:
|
|
45
|
+
required: true
|
|
46
|
+
content:
|
|
47
|
+
application/json:
|
|
48
|
+
schema:
|
|
49
|
+
$ref: "#/components/schemas/NewPet"
|
|
50
|
+
responses:
|
|
51
|
+
"201":
|
|
52
|
+
description: Pet created
|
|
53
|
+
content:
|
|
54
|
+
application/json:
|
|
55
|
+
schema:
|
|
56
|
+
$ref: "#/components/schemas/Pet"
|
|
57
|
+
example:
|
|
58
|
+
id: 3
|
|
59
|
+
name: Fluffy
|
|
60
|
+
tag: rabbit
|
|
61
|
+
/pets/{petId}:
|
|
62
|
+
get:
|
|
63
|
+
summary: Get a pet by ID
|
|
64
|
+
operationId: getPetById
|
|
65
|
+
tags:
|
|
66
|
+
- pets
|
|
67
|
+
parameters:
|
|
68
|
+
- name: petId
|
|
69
|
+
in: path
|
|
70
|
+
required: true
|
|
71
|
+
description: The ID of the pet to retrieve
|
|
72
|
+
schema:
|
|
73
|
+
type: integer
|
|
74
|
+
responses:
|
|
75
|
+
"200":
|
|
76
|
+
description: The pet
|
|
77
|
+
content:
|
|
78
|
+
application/json:
|
|
79
|
+
schema:
|
|
80
|
+
$ref: "#/components/schemas/Pet"
|
|
81
|
+
example:
|
|
82
|
+
id: 1
|
|
83
|
+
name: Whiskers
|
|
84
|
+
tag: cat
|
|
85
|
+
"404":
|
|
86
|
+
description: Pet not found
|
|
87
|
+
delete:
|
|
88
|
+
summary: Delete a pet
|
|
89
|
+
operationId: deletePet
|
|
90
|
+
tags:
|
|
91
|
+
- pets
|
|
92
|
+
parameters:
|
|
93
|
+
- name: petId
|
|
94
|
+
in: path
|
|
95
|
+
required: true
|
|
96
|
+
description: The ID of the pet to delete
|
|
97
|
+
schema:
|
|
98
|
+
type: integer
|
|
99
|
+
responses:
|
|
100
|
+
"204":
|
|
101
|
+
description: Pet deleted
|
|
102
|
+
components:
|
|
103
|
+
schemas:
|
|
104
|
+
Pet:
|
|
105
|
+
type: object
|
|
106
|
+
description: A pet in the store
|
|
107
|
+
required:
|
|
108
|
+
- id
|
|
109
|
+
- name
|
|
110
|
+
properties:
|
|
111
|
+
id:
|
|
112
|
+
type: integer
|
|
113
|
+
description: Unique identifier
|
|
114
|
+
name:
|
|
115
|
+
type: string
|
|
116
|
+
description: Name of the pet
|
|
117
|
+
tag:
|
|
118
|
+
type: string
|
|
119
|
+
description: Type/species of the pet
|
|
120
|
+
NewPet:
|
|
121
|
+
type: object
|
|
122
|
+
description: Payload for creating a new pet
|
|
123
|
+
required:
|
|
124
|
+
- name
|
|
125
|
+
properties:
|
|
126
|
+
name:
|
|
127
|
+
type: string
|
|
128
|
+
description: Name of the pet
|
|
129
|
+
tag:
|
|
130
|
+
type: string
|
|
131
|
+
description: Type/species of the pet
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.1",
|
|
3
|
+
"info": { "title": "Week3 API", "version": "1.0.0" },
|
|
4
|
+
"servers": [{ "url": "https://api.week3.test" }],
|
|
5
|
+
"paths": {
|
|
6
|
+
"/items": {
|
|
7
|
+
"get": {
|
|
8
|
+
"summary": "List items",
|
|
9
|
+
"responses": {
|
|
10
|
+
"200": {
|
|
11
|
+
"description": "OK",
|
|
12
|
+
"content": {
|
|
13
|
+
"application/json": {
|
|
14
|
+
"schema": { "$ref": "#/components/schemas/ItemList" }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"security": [{ "ApiKeyAuth": [] }]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"components": {
|
|
24
|
+
"securitySchemes": {
|
|
25
|
+
"ApiKeyAuth": { "type": "apiKey", "in": "header", "name": "x-api-key" },
|
|
26
|
+
"BearerAuth": { "type": "http", "scheme": "bearer" }
|
|
27
|
+
},
|
|
28
|
+
"schemas": {
|
|
29
|
+
"A": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": { "a": { "type": "string" } }
|
|
32
|
+
},
|
|
33
|
+
"B": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": { "b": { "type": "number" } }
|
|
36
|
+
},
|
|
37
|
+
"Item": {
|
|
38
|
+
"oneOf": [ { "$ref": "#/components/schemas/A" }, { "$ref": "#/components/schemas/B" } ],
|
|
39
|
+
"discriminator": { "propertyName": "type" }
|
|
40
|
+
},
|
|
41
|
+
"ItemList": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"items": { "$ref": "#/components/schemas/Item" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|