rapidoc 11.0.0 → 12.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.
Files changed (80) hide show
  1. package/README.md +46 -31
  2. package/dist/rapidoc-min.js +1424 -213
  3. package/dist/rapidoc-static-min.js +13542 -0
  4. package/package.json +48 -34
  5. package/src/components/api-request.ts +869 -0
  6. package/src/components/api-response.ts +509 -0
  7. package/src/components/json-tree.ts +171 -0
  8. package/src/components/schema-base-element.ts +315 -0
  9. package/src/components/schema-table.ts +430 -0
  10. package/src/components/schema-tree.ts +386 -0
  11. package/src/components/tag-input.ts +184 -0
  12. package/src/index.js +6 -0
  13. package/src/oauth-receiver.ts +29 -0
  14. package/src/rapidoc-mini.ts +582 -0
  15. package/src/rapidoc.ts +1808 -0
  16. package/src/static/index.d.ts +149 -0
  17. package/src/static/index.js +62 -0
  18. package/src/static/navigation.js +497 -0
  19. package/src/static/render.js +490 -0
  20. package/src/static/serializer.js +216 -0
  21. package/src/styles/auth-styles.js +318 -0
  22. package/src/styles/border-styles.js +24 -0
  23. package/src/styles/css-bundle.js +129 -0
  24. package/src/styles/custom-styles.js +8 -0
  25. package/src/styles/dialog-box-styles.js +102 -0
  26. package/src/styles/endpoint-styles.js +559 -0
  27. package/src/styles/flex-styles.js +16 -0
  28. package/src/styles/font-styles.js +432 -0
  29. package/src/styles/info-styles.js +36 -0
  30. package/src/styles/input-styles.js +555 -0
  31. package/src/styles/layout-styles.js +396 -0
  32. package/src/styles/microlighter-styles.js +99 -0
  33. package/src/styles/nav-styles.js +609 -0
  34. package/src/styles/param-list-styles.js +261 -0
  35. package/src/styles/schema-styles.js +288 -0
  36. package/src/styles/tab-styles.js +106 -0
  37. package/src/styles/theme-tokens.js +582 -0
  38. package/src/templates/advanced-search-dialog-template.js +94 -0
  39. package/src/templates/api-response-template.js +219 -0
  40. package/src/templates/callback-template.js +86 -0
  41. package/src/templates/code-samples-template.js +52 -0
  42. package/src/templates/components-template.js +86 -0
  43. package/src/templates/downloads-template.js +85 -0
  44. package/src/templates/endpoint-template.js +386 -0
  45. package/src/templates/expanded-endpoint-template.js +266 -0
  46. package/src/templates/focused-endpoint-template.js +145 -0
  47. package/src/templates/header-template.js +92 -0
  48. package/src/templates/logo-template.js +21 -0
  49. package/src/templates/main-body-template.js +102 -0
  50. package/src/templates/navbar-template.js +489 -0
  51. package/src/templates/overview-template.js +104 -0
  52. package/src/templates/request-body-template.js +651 -0
  53. package/src/templates/request-params-template.js +474 -0
  54. package/src/templates/security-scheme-template.js +1263 -0
  55. package/src/templates/server-template.js +152 -0
  56. package/src/utils/auto-color-scheme.js +99 -0
  57. package/src/utils/bind-navbar.js +175 -0
  58. package/src/utils/color-utils.js +59 -0
  59. package/src/utils/common-utils.js +191 -0
  60. package/src/utils/curl-utils.js +123 -0
  61. package/src/utils/highlighter.js +416 -0
  62. package/src/utils/markdown-utils.js +110 -0
  63. package/src/utils/mcp/common.js +467 -0
  64. package/src/utils/mcp/generators/csharp.js +242 -0
  65. package/src/utils/mcp/generators/go.js +298 -0
  66. package/src/utils/mcp/generators/java.js +330 -0
  67. package/src/utils/mcp/generators/python.js +248 -0
  68. package/src/utils/mcp/generators/rust.js +281 -0
  69. package/src/utils/mcp/generators/typescript.js +290 -0
  70. package/src/utils/mcp-generator.js +143 -0
  71. package/src/utils/mock-interceptor.js +508 -0
  72. package/src/utils/oauth-utils.js +87 -0
  73. package/src/utils/request-utils.js +203 -0
  74. package/src/utils/response-utils.js +203 -0
  75. package/src/utils/sanitize.js +57 -0
  76. package/src/utils/schema-utils.js +1927 -0
  77. package/src/utils/slugger.js +189 -0
  78. package/src/utils/spec-parser.js +915 -0
  79. package/src/utils/url-utils.js +211 -0
  80. package/src/utils/xml-utils.js +61 -0
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Single-file TypeScript MCP Server Generator
3
+ * Uses official @modelcontextprotocol/sdk and zod.
4
+ */
5
+ import { toSnakeCase, estimateTokenCost } from "../common.js";
6
+
7
+ export function generateTypeScriptMcpServer(metadata, tools, resources, workflows, options = {}) {
8
+ const serverName = toSnakeCase(options.serverName || metadata.title || "rapidoc_mcp_server");
9
+ const budget = estimateTokenCost(tools, resources, workflows);
10
+ const defaultBaseUrl = metadata.servers?.[0]?.url || "http://localhost:3000";
11
+
12
+ const toolDefinitions = tools
13
+ .map((t) => {
14
+ // Build Zod schema properties
15
+ const zodProps = t.parameters
16
+ .map((p) => {
17
+ let zodType = "z.string()";
18
+ if (p.type === "integer" || p.type === "number") zodType = "z.number()";
19
+ else if (p.type === "boolean") zodType = "z.boolean()";
20
+ else if (p.type === "array") zodType = "z.array(z.string())";
21
+ else if (p.type === "object") zodType = "z.record(z.any())";
22
+
23
+ if (p.description) {
24
+ zodType += `.describe(${JSON.stringify(p.description)})`;
25
+ }
26
+ if (!p.required) {
27
+ zodType += ".optional()";
28
+ }
29
+ return ` ${JSON.stringify(p.name)}: ${zodType},`;
30
+ })
31
+ .join("\n");
32
+
33
+ const allZodProps = zodProps
34
+ ? `${zodProps}\n apiKey: z.string().optional().describe('Optional Bearer API key override for multi-tenant sessions'),`
35
+ : ` apiKey: z.string().optional().describe('Optional Bearer API key override for multi-tenant sessions'),`;
36
+
37
+ const pathSubstitution = t.path.replace(/\{([^}]+)\}/g, '${encodeURIComponent(args["$1"])}');
38
+
39
+ const headerParams = t.parameters.filter((p) => p.in === "header");
40
+ const headerAppends = headerParams
41
+ .map(
42
+ (p) =>
43
+ ` if (args[${JSON.stringify(p.name)}]) {\n headers[${JSON.stringify(p.name)}] = String(args[${JSON.stringify(p.name)}]);\n }`,
44
+ )
45
+ .join("\n");
46
+
47
+ return `
48
+ // Tool: ${t.name} (${t.method} ${t.path})
49
+ // ${t.isConsequential ? "⚠️ Consequential/Destructive action" : "Safe/Read-only action"}
50
+ server.tool(
51
+ ${JSON.stringify(t.name)},
52
+ ${JSON.stringify(t.description + (t.isConsequential ? " (Requires confirmation)" : ""))},
53
+ {
54
+ ${allZodProps}
55
+ },
56
+ async (args: any) => {
57
+ const targetUrl = new URL(\`\${BASE_URL.replace(/\\/$/, '')}${pathSubstitution}\`);
58
+ let url = targetUrl.toString();
59
+ const queryParams = new URLSearchParams();
60
+ ${t.parameters
61
+ .filter((p) => p.in === "query")
62
+ .map(
63
+ (p) =>
64
+ ` if (args[${JSON.stringify(p.name)}] !== undefined) queryParams.set(${JSON.stringify(p.name)}, String(args[${JSON.stringify(p.name)}]));`,
65
+ )
66
+ .join("\n")}
67
+ const qs = queryParams.toString();
68
+ if (qs) url += (url.includes('?') ? '&' : '?') + qs;
69
+
70
+ const headers: Record<string, string> = {
71
+ 'Accept': 'application/json',
72
+ 'Content-Type': 'application/json',
73
+ };
74
+ ${headerAppends ? "\n" + headerAppends + "\n" : ""} const resolvedKey = args?.apiKey || API_KEY;
75
+ if (resolvedKey) headers['Authorization'] = \`Bearer \${resolvedKey}\`;
76
+
77
+ const fetchOptions: RequestInit = {
78
+ method: ${JSON.stringify(t.method)},
79
+ headers,
80
+ };
81
+
82
+ ${
83
+ ["POST", "PUT", "PATCH"].includes(t.method)
84
+ ? ` const bodyPayload: Record<string, any> = {};
85
+ ${t.parameters
86
+ .filter((p) => p.in === "body")
87
+ .map(
88
+ (p) =>
89
+ ` if (args[${JSON.stringify(p.name)}] !== undefined) bodyPayload[${JSON.stringify(p.name)}] = args[${JSON.stringify(p.name)}];`,
90
+ )
91
+ .join("\n")}
92
+ if (Object.keys(bodyPayload).length > 0) {
93
+ fetchOptions.body = JSON.stringify(bodyPayload);
94
+ }`
95
+ : ""
96
+ }
97
+
98
+ try {
99
+ const response = await fetch(url, fetchOptions);
100
+ const text = await response.text();
101
+ let data = text;
102
+ try { data = JSON.parse(text); } catch {}
103
+ return {
104
+ content: [{ type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }],
105
+ };
106
+ } catch (err: any) {
107
+ return {
108
+ isError: true,
109
+ content: [{ type: 'text', text: \`Error invoking ${t.name}: \${err.message}\` }],
110
+ };
111
+ }
112
+ }
113
+ );`;
114
+ })
115
+ .join("\n");
116
+
117
+ const resourceDefinitions = resources
118
+ .map((r) => {
119
+ return `
120
+ // Resource: ${r.name} (${r.uri})
121
+ server.resource(
122
+ ${JSON.stringify(r.name)},
123
+ ${JSON.stringify(r.uri)},
124
+ async (uri) => {
125
+ const targetUrl = new URL(\`\${BASE_URL.replace(/\\/$/, '')}${r.path}\`);
126
+ const resolvedKey = uri.searchParams?.get('apiKey') || uri.searchParams?.get('api_key') || API_KEY;
127
+ if (uri.searchParams) {
128
+ uri.searchParams.forEach((val, key) => {
129
+ if (!['apikey', 'api_key'].includes(key.toLowerCase())) {
130
+ targetUrl.searchParams.set(key, val);
131
+ }
132
+ });
133
+ }
134
+ const headers: Record<string, string> = { 'Accept': ${JSON.stringify(r.mimeType)} };
135
+ if (resolvedKey) headers['Authorization'] = \`Bearer \${resolvedKey}\`;
136
+
137
+ const res = await fetch(targetUrl.toString(), { headers });
138
+ const text = await res.text();
139
+ return {
140
+ contents: [{
141
+ uri: uri.href,
142
+ text,
143
+ mimeType: ${JSON.stringify(r.mimeType)},
144
+ }],
145
+ };
146
+ }
147
+ );`;
148
+ })
149
+ .join("\n");
150
+
151
+ const workflowDefinitions = workflows
152
+ .map((w) => {
153
+ const paramKeys = w.parameters ? Object.keys(w.parameters) : [];
154
+ const zodProps = paramKeys
155
+ .map(
156
+ (k) =>
157
+ ` ${JSON.stringify(k)}: z.string().describe(${JSON.stringify(w.parameters[k].description || "")}),`,
158
+ )
159
+ .concat([
160
+ ` apiKey: z.string().optional().describe('Optional Bearer API key override for multi-tenant sessions'),`,
161
+ ])
162
+ .join("\n");
163
+ const firstParam = paramKeys[0] || "id";
164
+
165
+ return `
166
+ // Composite Workflow: ${w.name}
167
+ // Collapses multi-step atomic REST calls into 1 tool call
168
+ server.tool(
169
+ ${JSON.stringify(w.name)},
170
+ ${JSON.stringify(w.description || "Composite workflow")},
171
+ {
172
+ ${zodProps}
173
+ },
174
+ async (args: any) => {
175
+ const results: Record<string, any> = {};
176
+ const headers: Record<string, string> = { 'Accept': 'application/json' };
177
+ const resolvedKey = args?.apiKey || API_KEY;
178
+ if (resolvedKey) headers['Authorization'] = \`Bearer \${resolvedKey}\`;
179
+
180
+ try {
181
+ ${(w.steps || [])
182
+ .map((s, idx) => {
183
+ return ` // Step ${idx + 1}: ${s.call}
184
+ const stepUrl_${idx} = \`\${BASE_URL.replace(/\\/$/, '')}/api/\${encodeURIComponent(args[${JSON.stringify(firstParam)}] || '')}\`;
185
+ const resp_${idx} = await fetch(stepUrl_${idx}, { headers });
186
+ results[${JSON.stringify(s.call)}] = await resp_${idx}.json();`;
187
+ })
188
+ .join("\n")}
189
+ return {
190
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
191
+ };
192
+ } catch (err: any) {
193
+ return {
194
+ isError: true,
195
+ content: [{ type: 'text', text: \`Workflow ${w.name} failed: \${err.message}\` }],
196
+ };
197
+ }
198
+ }
199
+ );`;
200
+ })
201
+ .join("\n");
202
+
203
+ return `/**
204
+ * Auto-generated by RapiDoc Curated MCP Generator
205
+ * Spec: ${metadata.title} (v${metadata.version})
206
+ *
207
+ * [MULTI-TENANCY & AUTHENTICATION]
208
+ * - Local Stdio Mode: Claude Desktop, Cursor, and local agents use API_KEY from environment.
209
+ * - Multi-Tenant / Agent Mode: When running in a shared service, the calling AI agent can pass
210
+ * per-session credentials directly in the \`apiKey\` parameter of any tool.
211
+ *
212
+ * [TOKEN BUDGET DIAGNOSTIC]
213
+ * - Total Tools: ${budget.totalTools}
214
+ * - Total Resources: ${budget.totalResources}
215
+ * - Total Workflows: ${budget.totalWorkflows}
216
+ * - Estimated Prompt Context Overhead: ~${budget.estimatedTokens.toLocaleString()} tokens/turn
217
+ ${budget.warnings.map((w) => ` * - ⚠️ ${w}`).join("\n")}
218
+ *
219
+ * [HOW TO RUN]
220
+ * 1. npm install @modelcontextprotocol/sdk zod
221
+ * 2. API_KEY="your-key" npx tsx server.ts
222
+ */
223
+
224
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
225
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
226
+ import { z } from 'zod';
227
+
228
+ const API_KEY = process.env.API_KEY || '';
229
+ const BASE_URL = process.env.API_BASE_URL || ${JSON.stringify(defaultBaseUrl)};
230
+
231
+ const server = new McpServer({
232
+ name: ${JSON.stringify(serverName)},
233
+ version: ${JSON.stringify(metadata.version)},
234
+ });
235
+
236
+ ${resourceDefinitions}
237
+ ${toolDefinitions}
238
+ ${workflowDefinitions}
239
+
240
+ async function main() {
241
+ const transport = new StdioServerTransport();
242
+ await server.connect(transport);
243
+ console.error(\`[MCP] ${serverName} running on stdio\`);
244
+ }
245
+
246
+ main().catch((err) => {
247
+ console.error('[MCP] Fatal error running server:', err);
248
+ process.exit(1);
249
+ });
250
+ `;
251
+ }
252
+
253
+ export function generateTypeScriptFiles(serverCode, metadata, options = {}) {
254
+ const packageJson = {
255
+ name: toSnakeCase(options.serverName || metadata.title || "rapidoc-mcp-server"),
256
+ version: metadata.version || "1.0.0",
257
+ type: "module",
258
+ scripts: {
259
+ start: "npx tsx server.ts",
260
+ },
261
+ dependencies: {
262
+ "@modelcontextprotocol/sdk": "^1.6.0",
263
+ zod: "^3.24.0",
264
+ },
265
+ devDependencies: {
266
+ tsx: "^4.19.0",
267
+ typescript: "^5.7.0",
268
+ },
269
+ };
270
+
271
+ const readme = `# ${metadata.title} - MCP Server (TypeScript)
272
+
273
+ Auto-generated by RapiDoc Curated MCP Generator.
274
+
275
+ ### Prerequisites
276
+ - Node.js 18+
277
+ - \`npm install\`
278
+
279
+ ### Running the Server
280
+ \`\`\`bash
281
+ API_KEY="your-key" npm start
282
+ \`\`\`
283
+ `;
284
+
285
+ return {
286
+ "server.ts": serverCode,
287
+ "package.json": JSON.stringify(packageJson, null, 2),
288
+ "README.md": readme,
289
+ };
290
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * MCP (Model Context Protocol) Server Generator for RapiDoc
3
+ *
4
+ * Implements client-side, zero-runtime-dependency translation from OpenAPI specs
5
+ * into curated, token-efficient MCP servers across multiple language ecosystems:
6
+ * - TypeScript (@modelcontextprotocol/sdk + zod)
7
+ * - Python (fastmcp + standard urllib)
8
+ * - Go (mark3labs/mcp-go)
9
+ * - C# (.NET / ModelContextProtocol.NET)
10
+ * - Rust (mcp-sdk / rmcp)
11
+ * - Java (Spring AI MCP)
12
+ *
13
+ * Honors primary `x-mcp` extensions and compatibility aliases from Redocly,
14
+ * Scalar, OpenAI (x-openai-isConsequential), and Speakeasy.
15
+ */
16
+
17
+ export {
18
+ AUTH_TRANSPORT_HEADERS,
19
+ VALID_MCP_TARGETS,
20
+ MCP_TARGET_LABELS,
21
+ parseMcpTargets,
22
+ toSnakeCase,
23
+ toPascalCase,
24
+ toCamelCase,
25
+ sanitizeDescription,
26
+ deriveToolName,
27
+ extractParameters,
28
+ extractMcpOperations,
29
+ estimateTokenCost,
30
+ generateHostConfigs,
31
+ } from "./mcp/common.js";
32
+
33
+ import { extractMcpOperations, estimateTokenCost, generateHostConfigs } from "./mcp/common.js";
34
+ import {
35
+ generateTypeScriptMcpServer,
36
+ generateTypeScriptFiles,
37
+ } from "./mcp/generators/typescript.js";
38
+ import { generatePythonFastMcpServer, generatePythonFiles } from "./mcp/generators/python.js";
39
+ import { generateGoMcpServer, generateGoFiles } from "./mcp/generators/go.js";
40
+ import { generateCSharpMcpServer, generateCSharpFiles } from "./mcp/generators/csharp.js";
41
+ import { generateRustMcpServer, generateRustFiles } from "./mcp/generators/rust.js";
42
+ import { generateJavaMcpServer, generateJavaFiles } from "./mcp/generators/java.js";
43
+
44
+ export {
45
+ generateTypeScriptMcpServer,
46
+ generatePythonFastMcpServer,
47
+ generateGoMcpServer,
48
+ generateCSharpMcpServer,
49
+ generateRustMcpServer,
50
+ generateJavaMcpServer,
51
+ };
52
+
53
+ /**
54
+ * Main orchestrator: extracts MCP operations, computes budget, and generates
55
+ * language-specific server implementation and packaging manifests.
56
+ *
57
+ * @param {object} parsedSpec Raw or parsed OpenAPI specification object
58
+ * @param {object} options Target language, server name, and filtering options
59
+ * @returns {object} { target, serverCode, hostConfig, budget, files, extracted }
60
+ */
61
+ export function generateMcpServer(parsedSpec, options = {}) {
62
+ const rawTarget = (options.target || "typescript").toLowerCase();
63
+ const target = rawTarget === "c#" ? "csharp" : rawTarget;
64
+ const extracted = extractMcpOperations(parsedSpec, options);
65
+ const budget = estimateTokenCost(extracted.tools, extracted.resources, extracted.workflows);
66
+ const hostConfigs = generateHostConfigs(options.serverName || extracted.metadata.title, {
67
+ target,
68
+ });
69
+
70
+ let serverCode;
71
+ let targetFiles;
72
+
73
+ if (target === "python") {
74
+ serverCode = generatePythonFastMcpServer(
75
+ extracted.metadata,
76
+ extracted.tools,
77
+ extracted.resources,
78
+ extracted.workflows,
79
+ options,
80
+ );
81
+ targetFiles = generatePythonFiles(serverCode, extracted.metadata, options);
82
+ } else if (target === "go") {
83
+ serverCode = generateGoMcpServer(
84
+ extracted.metadata,
85
+ extracted.tools,
86
+ extracted.resources,
87
+ extracted.workflows,
88
+ options,
89
+ );
90
+ targetFiles = generateGoFiles(serverCode, extracted.metadata, options);
91
+ } else if (target === "csharp") {
92
+ serverCode = generateCSharpMcpServer(
93
+ extracted.metadata,
94
+ extracted.tools,
95
+ extracted.resources,
96
+ extracted.workflows,
97
+ options,
98
+ );
99
+ targetFiles = generateCSharpFiles(serverCode, extracted.metadata, options);
100
+ } else if (target === "rust") {
101
+ serverCode = generateRustMcpServer(
102
+ extracted.metadata,
103
+ extracted.tools,
104
+ extracted.resources,
105
+ extracted.workflows,
106
+ options,
107
+ );
108
+ targetFiles = generateRustFiles(serverCode, extracted.metadata, options);
109
+ } else if (target === "java") {
110
+ serverCode = generateJavaMcpServer(
111
+ extracted.metadata,
112
+ extracted.tools,
113
+ extracted.resources,
114
+ extracted.workflows,
115
+ options,
116
+ );
117
+ targetFiles = generateJavaFiles(serverCode, extracted.metadata, options);
118
+ } else {
119
+ // Default: TypeScript
120
+ serverCode = generateTypeScriptMcpServer(
121
+ extracted.metadata,
122
+ extracted.tools,
123
+ extracted.resources,
124
+ extracted.workflows,
125
+ options,
126
+ );
127
+ targetFiles = generateTypeScriptFiles(serverCode, extracted.metadata, options);
128
+ }
129
+
130
+ const files = {
131
+ "claude_desktop_config.json": hostConfigs.claudeDesktopConfig,
132
+ ...targetFiles,
133
+ };
134
+
135
+ return {
136
+ target,
137
+ serverCode,
138
+ hostConfig: hostConfigs.claudeDesktopConfig,
139
+ budget,
140
+ files,
141
+ extracted,
142
+ };
143
+ }