rapidoc 12.0.0 → 12.1.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/README.md +127 -25
- package/dist/rapidoc-flow-min.js +580 -0
- package/dist/rapidoc-flow-min.js.LICENSE.txt +171 -0
- package/dist/rapidoc-min.js +541 -1309
- package/dist/rapidoc-min.js.LICENSE.txt +381 -0
- package/package.json +21 -18
- package/src/README.md +150 -0
- package/src/components/README.md +91 -0
- package/src/components/api-request.ts +43 -0
- package/src/components/api-response.ts +9 -4
- package/src/components/schema-base-element.ts +18 -4
- package/src/components/schema-table.ts +58 -13
- package/src/components/schema-tree.ts +7 -0
- package/src/components/tag-input.ts +5 -9
- package/src/flow.js +3 -0
- package/src/rapi-flow.ts +1178 -0
- package/src/rapidoc-mini.ts +27 -6
- package/src/rapidoc.ts +108 -62
- package/src/styles/README.md +96 -0
- package/src/styles/css-bundle.js +2 -2
- package/src/styles/endpoint-styles.js +6 -1
- package/src/styles/flow-styles.js +248 -0
- package/src/styles/nav-styles.js +39 -15
- package/src/styles/schema-styles.js +13 -5
- package/src/styles/theme-tokens.js +2 -2
- package/src/templates/README.md +89 -0
- package/src/templates/api-response-template.js +14 -10
- package/src/templates/callback-template.js +1 -1
- package/src/templates/downloads-template.js +12 -27
- package/src/templates/endpoint-template.js +15 -24
- package/src/templates/expanded-endpoint-template.js +8 -4
- package/src/templates/focused-endpoint-template.js +4 -3
- package/src/templates/navbar-template.js +20 -11
- package/src/templates/request-body-template.js +29 -7
- package/src/templates/request-params-template.js +15 -4
- package/src/templates/security-scheme-template.js +11 -2
- package/src/templates/server-template.js +3 -2
- package/src/utils/README.md +146 -0
- package/src/utils/arazzo/README.md +119 -0
- package/src/utils/arazzo/components.js +15 -0
- package/src/utils/arazzo/graph.js +269 -0
- package/src/utils/arazzo/index.js +52 -0
- package/src/utils/arazzo/operations.js +106 -0
- package/src/utils/arazzo/parse.js +196 -0
- package/src/utils/arazzo/refs.js +40 -0
- package/src/utils/arazzo/svg.js +707 -0
- package/src/utils/curl-utils.js +5 -2
- package/src/utils/highlighter.js +3 -0
- package/src/utils/mcp/README.md +155 -0
- package/src/utils/mcp/common.js +45 -346
- package/src/utils/mcp/manifest.js +382 -0
- package/src/utils/mcp/project/server.ts +53 -0
- package/src/utils/mcp/project.js +88 -0
- package/src/utils/mcp/runtime.js +289 -0
- package/src/utils/mcp/schema.js +98 -0
- package/src/utils/mcp/workflow-runtime.js +374 -0
- package/src/utils/mcp/workflows.js +240 -0
- package/src/utils/mcp/zip.js +102 -0
- package/src/utils/mcp-generator.js +29 -125
- package/src/utils/mock-interceptor.js +134 -85
- package/src/utils/request-utils.js +61 -0
- package/src/utils/schema-utils.js +33 -6
- package/src/utils/slugger.js +5 -7
- package/src/utils/spec-parser.js +190 -164
- package/src/utils/url-utils.js +36 -1
- package/dist/rapidoc-static-min.js +0 -13542
- package/src/static/index.d.ts +0 -149
- package/src/static/index.js +0 -62
- package/src/static/navigation.js +0 -497
- package/src/static/render.js +0 -490
- package/src/static/serializer.js +0 -216
- package/src/utils/mcp/generators/csharp.js +0 -242
- package/src/utils/mcp/generators/go.js +0 -298
- package/src/utils/mcp/generators/java.js +0 -330
- package/src/utils/mcp/generators/python.js +0 -248
- package/src/utils/mcp/generators/rust.js +0 -281
- package/src/utils/mcp/generators/typescript.js +0 -290
package/src/static/serializer.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Isomorphic HTML serializer for Lit TemplateResult in Node.js, Bun, and SSR/SSG runtimes.
|
|
3
|
-
*
|
|
4
|
-
* Flattens declarative Lit templates into pure, static HTML strings without requiring
|
|
5
|
-
* a browser window, headless browser, or full DOM emulator (JSDOM/HappyDOM).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Minimal polyfill for customElements registry in pure Node/Bun environments
|
|
9
|
-
if (typeof globalThis.customElements === "undefined") {
|
|
10
|
-
globalThis.customElements = {
|
|
11
|
-
define: () => {},
|
|
12
|
-
get: () => undefined,
|
|
13
|
-
whenDefined: () => Promise.resolve(),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Escapes characters for HTML text nodes and attribute values to prevent injection.
|
|
19
|
-
* @param {string} str
|
|
20
|
-
* @returns {string}
|
|
21
|
-
*/
|
|
22
|
-
export function escapeHtml(str) {
|
|
23
|
-
if (typeof str !== "string") return String(str ?? "");
|
|
24
|
-
return str
|
|
25
|
-
.replace(/&/g, "&")
|
|
26
|
-
.replace(/</g, "<")
|
|
27
|
-
.replace(/>/g, ">")
|
|
28
|
-
.replace(/"/g, """)
|
|
29
|
-
.replace(/'/g, "'");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Escapes characters for HTML text nodes (quotes are left as is, since they are inert in text).
|
|
34
|
-
* @param {string} str
|
|
35
|
-
* @returns {string}
|
|
36
|
-
*/
|
|
37
|
-
function escapeText(str) {
|
|
38
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Directives whose value is inserted as markup, matching Lit's behavior in the browser.
|
|
43
|
-
* Matched by the static `directiveName` (survives minification, unlike class names).
|
|
44
|
-
*/
|
|
45
|
-
const RAW_MARKUP_DIRECTIVES = new Set(["unsafeHTML", "unsafeSVG"]);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Recursively serializes a Lit TemplateResult, directive, CSSResult, primitive, or array
|
|
49
|
-
* into a pure static HTML string.
|
|
50
|
-
*
|
|
51
|
-
* - Event listeners (`@click="..."`) are stripped.
|
|
52
|
-
* - Non-attribute property bindings (`.prop="..."`) are stripped.
|
|
53
|
-
* - Boolean attributes (`?disabled="..."`) are converted to standard HTML attributes if truthy, or omitted if falsy.
|
|
54
|
-
* - Strings from plain bindings are HTML-escaped, as Lit does in the browser (`&<>` in text,
|
|
55
|
-
* plus quotes in attribute values). Unquoted attribute bindings (`value=${x}`) are quoted.
|
|
56
|
-
* - Lit directives (`unsafeHTML`, `ifDefined`, `guard`) are evaluated transparently; only
|
|
57
|
-
* `unsafeHTML` / `unsafeSVG` values are inserted as raw markup. An attribute whose whole value is
|
|
58
|
-
* `nothing` (e.g. `ifDefined(undefined)`) is omitted.
|
|
59
|
-
* - CSSResult instances (`css\`...\``) are resolved via their `.cssText` property.
|
|
60
|
-
*
|
|
61
|
-
* @param {any} result - The value or TemplateResult to serialize.
|
|
62
|
-
* @param {Object} [options]
|
|
63
|
-
* @param {boolean} [options.stripComments=false] - Whether to strip HTML comments.
|
|
64
|
-
* @returns {string} Serialized HTML string.
|
|
65
|
-
*/
|
|
66
|
-
export function renderTemplateToString(result, options = {}) {
|
|
67
|
-
return serializeValue(result, options, false);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {any} result
|
|
72
|
-
* @param {Object} options
|
|
73
|
-
* @param {boolean} inAttribute - Whether the value is bound inside an attribute value.
|
|
74
|
-
* @returns {string}
|
|
75
|
-
*/
|
|
76
|
-
function serializeValue(result, options, inAttribute) {
|
|
77
|
-
if (result == null || result === false) {
|
|
78
|
-
return "";
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (typeof result === "string") {
|
|
82
|
-
return inAttribute ? escapeHtml(result) : escapeText(result);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (typeof result === "number" || typeof result === "bigint") {
|
|
86
|
-
return String(result);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (typeof result === "symbol") {
|
|
90
|
-
return "";
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (Array.isArray(result)) {
|
|
94
|
-
return result.map((item) => serializeValue(item, options, inAttribute)).join("");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Handle Lit CSSResult
|
|
98
|
-
if (typeof result === "object" && result.cssText) {
|
|
99
|
-
return result.cssText;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Handle Lit directives (unsafeHTML, guard, live)
|
|
103
|
-
if (typeof result === "object" && result._$litDirective$) {
|
|
104
|
-
// unsafeHTML(str) / unsafeSVG(str): inserted as markup, never escaped
|
|
105
|
-
if (RAW_MARKUP_DIRECTIVES.has(result._$litDirective$.directiveName)) {
|
|
106
|
-
const markup = result.values?.[0];
|
|
107
|
-
return typeof markup === "string" ? markup : serializeValue(markup, options, inAttribute);
|
|
108
|
-
}
|
|
109
|
-
// guard([deps], () => fn)
|
|
110
|
-
if (result.values && typeof result.values[1] === "function") {
|
|
111
|
-
return serializeValue(result.values[1](), options, inAttribute);
|
|
112
|
-
}
|
|
113
|
-
// live(value) and other value-passing directives
|
|
114
|
-
if (result.values && result.values.length > 0) {
|
|
115
|
-
return serializeValue(result.values[0], options, inAttribute);
|
|
116
|
-
}
|
|
117
|
-
return "";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Handle Lit TemplateResult
|
|
121
|
-
if (typeof result === "object" && "strings" in result && "values" in result) {
|
|
122
|
-
let out = "";
|
|
123
|
-
let skipNextLeadingQuote = false;
|
|
124
|
-
// Whether the parser position is inside a start tag (between `<` and `>`), so a binding is an attribute value
|
|
125
|
-
let inTag = false;
|
|
126
|
-
|
|
127
|
-
for (let i = 0; i < result.strings.length; i++) {
|
|
128
|
-
let str = result.strings[i];
|
|
129
|
-
const lastOpen = str.lastIndexOf("<");
|
|
130
|
-
const lastClose = str.lastIndexOf(">");
|
|
131
|
-
if (lastOpen !== lastClose) {
|
|
132
|
-
inTag = lastOpen > lastClose;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Strip matching quote from previous expression if skipped
|
|
136
|
-
if (skipNextLeadingQuote) {
|
|
137
|
-
if (str.startsWith('"') || str.startsWith("'")) {
|
|
138
|
-
str = str.slice(1);
|
|
139
|
-
}
|
|
140
|
-
skipNextLeadingQuote = false;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (i < result.values.length) {
|
|
144
|
-
const val = result.values[i];
|
|
145
|
-
|
|
146
|
-
// 1. Event bindings: e.g. @click="..." or @click=...
|
|
147
|
-
const eventMatch = str.match(/@([\w.-]+)=(["']?)$/);
|
|
148
|
-
if (eventMatch) {
|
|
149
|
-
str = str.slice(0, eventMatch.index).trimEnd();
|
|
150
|
-
if (eventMatch[2]) {
|
|
151
|
-
skipNextLeadingQuote = true;
|
|
152
|
-
}
|
|
153
|
-
out += str;
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// 2. Property bindings: e.g. .config="..." or .config=...
|
|
158
|
-
const propMatch = str.match(/\.([\w.-]+)=(["']?)$/);
|
|
159
|
-
if (propMatch) {
|
|
160
|
-
str = str.slice(0, propMatch.index).trimEnd();
|
|
161
|
-
if (propMatch[2]) {
|
|
162
|
-
skipNextLeadingQuote = true;
|
|
163
|
-
}
|
|
164
|
-
out += str;
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 3. Boolean attributes: e.g. ?disabled="..." or ?disabled=...
|
|
169
|
-
const boolMatch = str.match(/\?([\w.-]+)=(["']?)$/);
|
|
170
|
-
if (boolMatch) {
|
|
171
|
-
const attrName = boolMatch[1];
|
|
172
|
-
const hasQuote = Boolean(boolMatch[2]);
|
|
173
|
-
str = str.slice(0, boolMatch.index).trimEnd();
|
|
174
|
-
if (hasQuote) {
|
|
175
|
-
skipNextLeadingQuote = true;
|
|
176
|
-
}
|
|
177
|
-
const isTruthy = Boolean(val && val !== "false");
|
|
178
|
-
out += str + (isTruthy ? ` ${attrName}` : "");
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 4. Regular attribute or text node
|
|
183
|
-
const attrMatch = inTag ? str.match(/\s[^\s"'>/=]+=(["']?)$/) : null;
|
|
184
|
-
if (
|
|
185
|
-
attrMatch &&
|
|
186
|
-
typeof val === "symbol" &&
|
|
187
|
-
(!attrMatch[1] || result.strings[i + 1]?.startsWith(attrMatch[1]))
|
|
188
|
-
) {
|
|
189
|
-
// `nothing` (e.g. ifDefined(undefined)) as the whole attribute value removes the attribute, as Lit does
|
|
190
|
-
out += str.slice(0, attrMatch.index);
|
|
191
|
-
skipNextLeadingQuote = Boolean(attrMatch[1]);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
if (attrMatch && !attrMatch[1]) {
|
|
195
|
-
// Unquoted attribute binding: quote it so an escaped value cannot break out into new attributes
|
|
196
|
-
out += `${str}"${serializeValue(val, options, true)}"`;
|
|
197
|
-
} else {
|
|
198
|
-
out += str;
|
|
199
|
-
out += serializeValue(val, options, inTag);
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
out += str;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (options.stripComments) {
|
|
207
|
-
out = out.replace(/<!--[\s\S]*?-->/g, "");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return out;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return "";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export default renderTemplateToString;
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single-file C# (.NET) MCP Server Generator
|
|
3
|
-
* Uses ModelContextProtocol.NET and Semantic Kernel conventions.
|
|
4
|
-
*/
|
|
5
|
-
import { toPascalCase, toCamelCase, toSnakeCase, estimateTokenCost } from "../common.js";
|
|
6
|
-
|
|
7
|
-
export function generateCSharpMcpServer(metadata, tools, resources, workflows, options = {}) {
|
|
8
|
-
const serverName = toPascalCase(options.serverName || metadata.title || "RapiDocMcpServer");
|
|
9
|
-
const budget = estimateTokenCost(tools, resources, workflows);
|
|
10
|
-
const defaultBaseUrl = metadata.servers?.[0]?.url || "http://localhost:3000";
|
|
11
|
-
|
|
12
|
-
const toolMethods = tools
|
|
13
|
-
.map((t) => {
|
|
14
|
-
const csharpArgs = t.parameters.map((p) => {
|
|
15
|
-
let csharpType = "string";
|
|
16
|
-
if (p.type === "integer") csharpType = "int";
|
|
17
|
-
else if (p.type === "number") csharpType = "double";
|
|
18
|
-
else if (p.type === "boolean") csharpType = "bool";
|
|
19
|
-
else if (p.type === "array") csharpType = "IEnumerable<string>";
|
|
20
|
-
else if (p.type === "object") csharpType = "Dictionary<string, object>";
|
|
21
|
-
const defaultVal = p.required ? "" : " = default";
|
|
22
|
-
return `[Description(${JSON.stringify(p.description || "")})] ${csharpType} ${toCamelCase(p.name)}${defaultVal}`;
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
csharpArgs.push(
|
|
26
|
-
'[Description("Optional Bearer API key override for multi-tenant sessions")] string apiKey = default',
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const pathParams = t.parameters.filter((p) => p.in === "path");
|
|
30
|
-
const safePathCode =
|
|
31
|
-
pathParams.length > 0
|
|
32
|
-
? pathParams
|
|
33
|
-
.map(
|
|
34
|
-
(p) =>
|
|
35
|
-
` var safe${toPascalCase(p.name)} = Uri.EscapeDataString(${toCamelCase(p.name)}?.ToString() ?? "");`,
|
|
36
|
-
)
|
|
37
|
-
.join("\n") + "\n"
|
|
38
|
-
: "";
|
|
39
|
-
|
|
40
|
-
const pathSub = t.path.replace(/\{([^}]+)\}/g, (match, p1) => `{safe${toPascalCase(p1)}}`);
|
|
41
|
-
const queryParams = t.parameters.filter((p) => p.in === "query");
|
|
42
|
-
const bodyParams = t.parameters.filter((p) => p.in === "body");
|
|
43
|
-
const hasBody = ["POST", "PUT", "PATCH"].includes(t.method) && bodyParams.length > 0;
|
|
44
|
-
|
|
45
|
-
const queryCode =
|
|
46
|
-
queryParams.length > 0
|
|
47
|
-
? ` var queryParams = new List<string>();
|
|
48
|
-
${queryParams.map((p) => ` if (${toCamelCase(p.name)} != default) queryParams.Add("${p.name}=" + Uri.EscapeDataString(${toCamelCase(p.name)}.ToString()));`).join("\n")}
|
|
49
|
-
var queryString = queryParams.Count > 0 ? "?" + string.Join("&", queryParams) : "";\n`
|
|
50
|
-
: "";
|
|
51
|
-
|
|
52
|
-
const urlExpr = queryParams.length > 0 ? `$"${pathSub}" + queryString` : `$"${pathSub}"`;
|
|
53
|
-
|
|
54
|
-
const bodyCode = hasBody
|
|
55
|
-
? ` var bodyPayload = new Dictionary<string, object>();
|
|
56
|
-
${bodyParams.map((p) => ` if (${toCamelCase(p.name)} != default) bodyPayload["${p.name}"] = ${toCamelCase(p.name)};`).join("\n")}
|
|
57
|
-
if (bodyPayload.Count > 0)
|
|
58
|
-
{
|
|
59
|
-
request.Content = new StringContent(JsonSerializer.Serialize(bodyPayload), System.Text.Encoding.UTF8, "application/json");
|
|
60
|
-
}\n`
|
|
61
|
-
: "";
|
|
62
|
-
|
|
63
|
-
return `
|
|
64
|
-
[McpTool("${t.name}", ${JSON.stringify(t.description)})]
|
|
65
|
-
public static async Task<string> ${toPascalCase(t.name)}(${csharpArgs.join(", ")})
|
|
66
|
-
{
|
|
67
|
-
using var client = new HttpClient { BaseAddress = new Uri(BaseUrl.TrimEnd('/') + "/") };
|
|
68
|
-
var resolvedKey = !string.IsNullOrEmpty(apiKey) ? apiKey : ApiKey;
|
|
69
|
-
if (!string.IsNullOrEmpty(resolvedKey))
|
|
70
|
-
{
|
|
71
|
-
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolvedKey);
|
|
72
|
-
}
|
|
73
|
-
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
74
|
-
${safePathCode}${queryCode} var request = new HttpRequestMessage(HttpMethod.${toPascalCase(t.method)}, ${urlExpr});
|
|
75
|
-
${t.parameters
|
|
76
|
-
.filter((p) => p.in === "header")
|
|
77
|
-
.map(
|
|
78
|
-
(p) =>
|
|
79
|
-
` if (!string.IsNullOrEmpty(${toCamelCase(p.name)})) request.Headers.TryAddWithoutValidation("${p.name}", ${toCamelCase(p.name)});`,
|
|
80
|
-
)
|
|
81
|
-
.join("\n")}
|
|
82
|
-
${bodyCode} var response = await client.SendAsync(request);
|
|
83
|
-
return await response.Content.ReadAsStringAsync();
|
|
84
|
-
}`;
|
|
85
|
-
})
|
|
86
|
-
.join("\n");
|
|
87
|
-
|
|
88
|
-
const resourceMethods = resources
|
|
89
|
-
.map((r) => {
|
|
90
|
-
return `
|
|
91
|
-
[McpResource("${r.uri}", "${r.name}", ${JSON.stringify(r.description)})]
|
|
92
|
-
public static async Task<string> Get${toPascalCase(r.name)}(string uri = "")
|
|
93
|
-
{
|
|
94
|
-
using var client = new HttpClient { BaseAddress = new Uri(BaseUrl.TrimEnd('/') + "/") };
|
|
95
|
-
var resolvedKey = ApiKey;
|
|
96
|
-
var outboundQuery = HttpUtility.ParseQueryString(string.Empty);
|
|
97
|
-
|
|
98
|
-
if (!string.IsNullOrEmpty(uri) && uri.Contains("?"))
|
|
99
|
-
{
|
|
100
|
-
var parsedUri = new Uri(uri);
|
|
101
|
-
var incomingParams = HttpUtility.ParseQueryString(parsedUri.Query);
|
|
102
|
-
|
|
103
|
-
var dynamicKey = incomingParams["apiKey"] ?? incomingParams["api_key"];
|
|
104
|
-
if (!string.IsNullOrEmpty(dynamicKey)) resolvedKey = dynamicKey;
|
|
105
|
-
|
|
106
|
-
foreach (string key in incomingParams)
|
|
107
|
-
{
|
|
108
|
-
if (key != null && !key.Equals("apikey", StringComparison.OrdinalIgnoreCase) && !key.Equals("api_key", StringComparison.OrdinalIgnoreCase))
|
|
109
|
-
{
|
|
110
|
-
outboundQuery[key] = incomingParams[key];
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!string.IsNullOrEmpty(resolvedKey)) client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolvedKey);
|
|
116
|
-
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("${r.mimeType || "application/json"}"));
|
|
117
|
-
|
|
118
|
-
var targetPath = "${r.path}" + (outboundQuery.Count > 0 ? "?" + outboundQuery.ToString() : "");
|
|
119
|
-
return await client.GetStringAsync(targetPath.TrimStart('/'));
|
|
120
|
-
}`;
|
|
121
|
-
})
|
|
122
|
-
.join("\n");
|
|
123
|
-
|
|
124
|
-
const workflowMethods = workflows
|
|
125
|
-
.map((w) => {
|
|
126
|
-
const paramKeys = w.parameters ? Object.keys(w.parameters) : [];
|
|
127
|
-
const args = paramKeys
|
|
128
|
-
.map(
|
|
129
|
-
(k) =>
|
|
130
|
-
`[Description(${JSON.stringify(w.parameters[k].description || "")})] string ${toCamelCase(k)}`,
|
|
131
|
-
)
|
|
132
|
-
.concat([
|
|
133
|
-
'[Description("Optional Bearer API key override for multi-tenant sessions")] string apiKey = default',
|
|
134
|
-
])
|
|
135
|
-
.join(", ");
|
|
136
|
-
const argKey = paramKeys[0] || "id";
|
|
137
|
-
|
|
138
|
-
return `
|
|
139
|
-
[McpTool("${w.name}", ${JSON.stringify(w.description || "Composite workflow")})]
|
|
140
|
-
public static async Task<string> ${toPascalCase(w.name)}(${args})
|
|
141
|
-
{
|
|
142
|
-
using var client = new HttpClient { BaseAddress = new Uri(BaseUrl.TrimEnd('/') + "/") };
|
|
143
|
-
var resolvedKey = !string.IsNullOrEmpty(apiKey) ? apiKey : ApiKey;
|
|
144
|
-
if (!string.IsNullOrEmpty(resolvedKey)) client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", resolvedKey);
|
|
145
|
-
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
146
|
-
|
|
147
|
-
var results = new Dictionary<string, object>();
|
|
148
|
-
${(w.steps || [])
|
|
149
|
-
.map(
|
|
150
|
-
(s, idx) => ` // Step ${idx + 1}: ${s.call}
|
|
151
|
-
var resp_${idx} = await client.GetStringAsync($"api/{Uri.EscapeDataString(${toCamelCase(argKey)} ?? "")}");
|
|
152
|
-
results["${s.call}"] = JsonSerializer.Deserialize<object>(resp_${idx});`,
|
|
153
|
-
)
|
|
154
|
-
.join("\n")}
|
|
155
|
-
|
|
156
|
-
return JsonSerializer.Serialize(results, new JsonSerializerOptions { WriteIndented = true });
|
|
157
|
-
}`;
|
|
158
|
-
})
|
|
159
|
-
.join("\n");
|
|
160
|
-
|
|
161
|
-
return `/*
|
|
162
|
-
Auto-generated by RapiDoc Curated MCP Generator
|
|
163
|
-
Spec: ${metadata.title} (v${metadata.version})
|
|
164
|
-
|
|
165
|
-
[MULTI-TENANCY & AUTHENTICATION]
|
|
166
|
-
- Local Stdio Mode: Claude Desktop, Cursor, and local agents use API_KEY from environment.
|
|
167
|
-
- Multi-Tenant / Agent Mode: When running in a shared service, the calling AI agent can pass
|
|
168
|
-
per-session credentials directly in the \`apiKey\` parameter of any tool.
|
|
169
|
-
|
|
170
|
-
[TOKEN BUDGET DIAGNOSTIC]
|
|
171
|
-
- Total Tools: ${budget.totalTools}
|
|
172
|
-
- Total Resources: ${budget.totalResources}
|
|
173
|
-
- Total Workflows: ${budget.totalWorkflows}
|
|
174
|
-
- Estimated Prompt Context Overhead: ~${budget.estimatedTokens.toLocaleString()} tokens/turn
|
|
175
|
-
${budget.warnings.map((w) => `- ${w}`).join("\n")}
|
|
176
|
-
|
|
177
|
-
[HOW TO RUN]
|
|
178
|
-
1. dotnet restore
|
|
179
|
-
2. dotnet run
|
|
180
|
-
*/
|
|
181
|
-
|
|
182
|
-
using System;
|
|
183
|
-
using System.Collections.Generic;
|
|
184
|
-
using System.ComponentModel;
|
|
185
|
-
using System.Linq;
|
|
186
|
-
using System.Net.Http;
|
|
187
|
-
using System.Net.Http.Headers;
|
|
188
|
-
using System.Text.Json;
|
|
189
|
-
using System.Threading.Tasks;
|
|
190
|
-
using System.Web;
|
|
191
|
-
using ModelContextProtocol.NET.Server;
|
|
192
|
-
|
|
193
|
-
namespace ${serverName};
|
|
194
|
-
|
|
195
|
-
public class Program
|
|
196
|
-
{
|
|
197
|
-
private static readonly string ApiKey = Environment.GetEnvironmentVariable("API_KEY") ?? "";
|
|
198
|
-
private static readonly string BaseUrl = Environment.GetEnvironmentVariable("API_BASE_URL") ?? "${defaultBaseUrl}";
|
|
199
|
-
|
|
200
|
-
public static async Task Main(string[] args)
|
|
201
|
-
{
|
|
202
|
-
var server = new McpServer(new McpServerOptions
|
|
203
|
-
{
|
|
204
|
-
ServerInfo = new() { Name = "${toSnakeCase(serverName)}", Version = "${metadata.version || "1.0.0"}" }
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
server.RegisterTools<Program>();
|
|
208
|
-
server.RegisterResources<Program>();
|
|
209
|
-
|
|
210
|
-
await server.StartAsync(Console.OpenStandardInput(), Console.OpenStandardOutput());
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
${resourceMethods}
|
|
214
|
-
${toolMethods}
|
|
215
|
-
${workflowMethods}
|
|
216
|
-
}
|
|
217
|
-
`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export function generateCSharpFiles(serverCode, metadata) {
|
|
221
|
-
const csproj = `<Project Sdk="Microsoft.NET.Sdk">\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <TargetFramework>net8.0</TargetFramework>\n <ImplicitUsings>enable</ImplicitUsings>\n <Nullable>enable</Nullable>\n </PropertyGroup>\n <ItemGroup>\n <PackageReference Include="ModelContextProtocol.NET.Server" Version="1.0.0" />\n </ItemGroup>\n</Project>\n`;
|
|
222
|
-
const readme = `# ${metadata.title} - MCP Server (C# / .NET)
|
|
223
|
-
|
|
224
|
-
Auto-generated by RapiDoc Curated MCP Generator.
|
|
225
|
-
|
|
226
|
-
### Prerequisites
|
|
227
|
-
- .NET 8.0 SDK or newer
|
|
228
|
-
|
|
229
|
-
### Building & Running
|
|
230
|
-
\`\`\`bash
|
|
231
|
-
dotnet restore
|
|
232
|
-
export API_KEY="your-api-key"
|
|
233
|
-
dotnet run
|
|
234
|
-
\`\`\`
|
|
235
|
-
`;
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
"Program.cs": serverCode,
|
|
239
|
-
"Server.csproj": csproj,
|
|
240
|
-
"README.md": readme,
|
|
241
|
-
};
|
|
242
|
-
}
|