@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.
Files changed (72) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/LICENSE +21 -0
  3. package/README.md +439 -0
  4. package/README.pt-BR.md +320 -0
  5. package/RELEASE_NOTES.md +98 -0
  6. package/SECURITY.md +75 -0
  7. package/SECURITY.pt-BR.md +77 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +685 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/core/generator.d.ts +4 -0
  13. package/dist/core/generator.d.ts.map +1 -0
  14. package/dist/core/generator.js +275 -0
  15. package/dist/core/generator.js.map +1 -0
  16. package/dist/core/incremental.d.ts +25 -0
  17. package/dist/core/incremental.d.ts.map +1 -0
  18. package/dist/core/incremental.js +91 -0
  19. package/dist/core/incremental.js.map +1 -0
  20. package/dist/core/parser.d.ts +3 -0
  21. package/dist/core/parser.d.ts.map +1 -0
  22. package/dist/core/parser.js +372 -0
  23. package/dist/core/parser.js.map +1 -0
  24. package/dist/core/registry.d.ts +13 -0
  25. package/dist/core/registry.d.ts.map +1 -0
  26. package/dist/core/registry.js +107 -0
  27. package/dist/core/registry.js.map +1 -0
  28. package/dist/core/security-lint.d.ts +53 -0
  29. package/dist/core/security-lint.d.ts.map +1 -0
  30. package/dist/core/security-lint.js +470 -0
  31. package/dist/core/security-lint.js.map +1 -0
  32. package/dist/core/security.d.ts +41 -0
  33. package/dist/core/security.d.ts.map +1 -0
  34. package/dist/core/security.js +150 -0
  35. package/dist/core/security.js.map +1 -0
  36. package/dist/core/templating.d.ts +5 -0
  37. package/dist/core/templating.d.ts.map +1 -0
  38. package/dist/core/templating.js +211 -0
  39. package/dist/core/templating.js.map +1 -0
  40. package/dist/core/types.d.ts +104 -0
  41. package/dist/core/types.d.ts.map +1 -0
  42. package/dist/core/types.js +3 -0
  43. package/dist/core/types.js.map +1 -0
  44. package/dist/index.d.ts +7 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +20 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/templates/go/Dockerfile.hbs +16 -0
  49. package/dist/templates/go/README.md.hbs +77 -0
  50. package/dist/templates/go/ci.yml.hbs +28 -0
  51. package/dist/templates/go/client.go.hbs +86 -0
  52. package/dist/templates/go/go.mod.hbs +5 -0
  53. package/dist/templates/go/models.go.hbs +28 -0
  54. package/dist/templates/go/server.go.hbs +220 -0
  55. package/dist/templates/python/Dockerfile.hbs +12 -0
  56. package/dist/templates/python/README.md.hbs +115 -0
  57. package/dist/templates/python/ci.yml.hbs +24 -0
  58. package/dist/templates/python/models.py.hbs +20 -0
  59. package/dist/templates/python/requirements.txt.hbs +3 -0
  60. package/dist/templates/python/server.py.hbs +195 -0
  61. package/dist/templates/typescript/Dockerfile.hbs +18 -0
  62. package/dist/templates/typescript/README.md.hbs +91 -0
  63. package/dist/templates/typescript/ci.yml.hbs +28 -0
  64. package/dist/templates/typescript/client.hbs +49 -0
  65. package/dist/templates/typescript/models.hbs +23 -0
  66. package/dist/templates/typescript/package.json.hbs +26 -0
  67. package/dist/templates/typescript/server.hbs +337 -0
  68. package/dist/templates/typescript/tsconfig.json.hbs +17 -0
  69. package/examples/petstore.json +130 -0
  70. package/examples/petstore.yaml +131 -0
  71. package/examples/week3.json +47 -0
  72. 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
+ }