api2ai 1.0.27 → 1.0.28

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.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `.env` and `.env.example`. They differ only in placeholder credentials, so
3
+ * one template renders both (they used to be two copies that drifted).
4
+ */
5
+ export function generateEnvFile({ baseUrl, port, requireApprovals = true, example = false }) {
6
+ const apiHost = safeHost(baseUrl);
7
+ return `# Server Configuration
8
+ PORT=${port}
9
+ # 127.0.0.1 locally; set HOST=0.0.0.0 to serve on a network / in a container
10
+ # HOST=0.0.0.0
11
+ NODE_ENV=development
12
+
13
+ # API Configuration
14
+ API_BASE_URL=${baseUrl || 'https://api.example.com'}
15
+
16
+ # Authentication
17
+ ${example ? 'API_KEY=your-api-key-here' : '# API_KEY=your-api-key'}
18
+ # API_AUTH_HEADER=Header-Name:header-value
19
+
20
+ # Security — allowed API hosts (comma-separated). Empty = only API_BASE_URL's host${apiHost ? ` (${apiHost})` : ''}.
21
+ # ALLOWED_API_HOSTS=${apiHost || 'api.example.com'}
22
+
23
+ # Tool policy — set to true to allow medium/high-risk (mutating) tools
24
+ ALLOW_RESTRICTED_TOOLS=false
25
+ # Set to false to bypass per-call approval requirement for restricted tools
26
+ REQUIRE_APPROVALS=${requireApprovals ? 'true' : 'false'}
27
+
28
+ # Request limits
29
+ REQUEST_TIMEOUT_MS=30000
30
+ MAX_RESPONSE_BYTES=10485760
31
+
32
+ # Public URL of this server, when it sits behind a proxy
33
+ # MCP_URL=https://your-production-url.com
34
+
35
+ # Browser origins allowed to call /mcp (comma-separated hostnames or URLs)
36
+ # ALLOWED_ORIGINS=app1.com,https://app2.com
37
+ `;
38
+ }
39
+
40
+ function safeHost(url) {
41
+ try {
42
+ return url ? new URL(url).host : '';
43
+ } catch {
44
+ return '';
45
+ }
46
+ }
@@ -0,0 +1,126 @@
1
+ /** `src/http-client.js` of the generated server: one outbound request per tool call. */
2
+ export function generateHttpClient() {
3
+ return `// HTTP client for API requests
4
+ import { sanitizeHeaderParams, MAX_RESPONSE_BYTES, REQUEST_TIMEOUT_MS } from './policy.js';
5
+
6
+ /**
7
+ * Join base URL and path template. Concatenated rather than \`new URL(path, base)\`:
8
+ * an absolute path would drop the base's own path (\`/api/v3\` + \`/pet\` → \`/pet\`).
9
+ */
10
+ export function buildUrl(baseUrl, pathTemplate, pathParams = {}) {
11
+ let path = pathTemplate;
12
+ for (const [key, value] of Object.entries(pathParams)) {
13
+ path = path.replaceAll(\`{\${key}}\`, encodeURIComponent(String(value)));
14
+ }
15
+ return baseUrl.replace(/\\/+$/, '') + (path.startsWith('/') ? path : \`/\${path}\`);
16
+ }
17
+
18
+ export function buildQueryString(queryParams = {}) {
19
+ const params = new URLSearchParams();
20
+ for (const [key, value] of Object.entries(queryParams)) {
21
+ if (value === undefined || value === null) continue;
22
+ for (const v of Array.isArray(value) ? value : [value]) {
23
+ params.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v));
24
+ }
25
+ }
26
+ return params.toString();
27
+ }
28
+
29
+ function encodeBody(body, contentType) {
30
+ if (typeof body === 'string') return body;
31
+ if (contentType.includes('application/x-www-form-urlencoded')) {
32
+ return buildQueryString(body);
33
+ }
34
+ return JSON.stringify(body);
35
+ }
36
+
37
+ /** Read the body, aborting as soon as it exceeds MAX_RESPONSE_BYTES. */
38
+ async function readCapped(response) {
39
+ if (!response.body) return '';
40
+ const reader = response.body.getReader();
41
+ const chunks = [];
42
+ let total = 0;
43
+ for (;;) {
44
+ const { done, value } = await reader.read();
45
+ if (done) break;
46
+ total += value.byteLength;
47
+ if (total > MAX_RESPONSE_BYTES) {
48
+ await reader.cancel();
49
+ throw new Error(\`Response body exceeds \${MAX_RESPONSE_BYTES} bytes\`);
50
+ }
51
+ chunks.push(value);
52
+ }
53
+ return Buffer.concat(chunks).toString('utf8');
54
+ }
55
+
56
+ export async function executeRequest(toolConfig, args, config = {}) {
57
+ const { baseUrl: configBaseUrl, headers: configHeaders = {} } = config;
58
+ const baseUrl = configBaseUrl || toolConfig.baseUrl;
59
+ if (!baseUrl) {
60
+ throw new Error(\`No base URL configured for tool: \${toolConfig.name}. Set API_BASE_URL.\`);
61
+ }
62
+
63
+ const pathParams = {};
64
+ const queryParams = {};
65
+ const rawHeaderParams = {};
66
+
67
+ for (const param of toolConfig.executionParameters || []) {
68
+ const value = args[param.name];
69
+ if (value === undefined) continue;
70
+ if (param.in === 'path') pathParams[param.name] = value;
71
+ else if (param.in === 'query') queryParams[param.name] = value;
72
+ else if (param.in === 'header') rawHeaderParams[param.name] = value;
73
+ }
74
+
75
+ let url = buildUrl(baseUrl, toolConfig.pathTemplate, pathParams);
76
+ const queryString = buildQueryString(queryParams);
77
+ if (queryString) url += (url.includes('?') ? '&' : '?') + queryString;
78
+
79
+ const method = toolConfig.method.toUpperCase();
80
+ const hasBody = args.requestBody !== undefined && !['GET', 'HEAD', 'OPTIONS'].includes(method);
81
+ const contentType = toolConfig.requestBodyContentType || 'application/json';
82
+
83
+ // configHeaders (auth) always win over spec-defined header params
84
+ const headers = {
85
+ Accept: 'application/json, */*;q=0.8',
86
+ ...sanitizeHeaderParams(rawHeaderParams),
87
+ ...configHeaders,
88
+ };
89
+ if (hasBody) headers['Content-Type'] = contentType;
90
+
91
+ let response;
92
+ try {
93
+ response = await fetch(url, {
94
+ method,
95
+ headers,
96
+ body: hasBody ? encodeBody(args.requestBody, contentType) : undefined,
97
+ // A redirect could carry the Authorization header to another host.
98
+ redirect: 'error',
99
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
100
+ });
101
+ } catch (err) {
102
+ if (err?.name === 'TimeoutError') {
103
+ throw new Error(\`\${method} \${url} timed out after \${REQUEST_TIMEOUT_MS}ms\`);
104
+ }
105
+ throw new Error(\`\${method} \${url} failed: \${err?.cause?.message || err?.message || err}\`);
106
+ }
107
+
108
+ const declaredLength = Number.parseInt(response.headers.get('content-length') || '0', 10);
109
+ if (declaredLength > MAX_RESPONSE_BYTES) {
110
+ throw new Error(\`Response too large: \${declaredLength} bytes (max \${MAX_RESPONSE_BYTES})\`);
111
+ }
112
+
113
+ const text = await readCapped(response);
114
+ let data = text;
115
+ if ((response.headers.get('content-type') || '').includes('json') && text) {
116
+ try {
117
+ data = JSON.parse(text);
118
+ } catch {
119
+ // Mislabelled body — hand the raw text back instead of failing the call.
120
+ }
121
+ }
122
+
123
+ return { status: response.status, statusText: response.statusText, data, ok: response.ok };
124
+ }
125
+ `;
126
+ }
@@ -0,0 +1,21 @@
1
+ import { DEPENDENCY_VERSIONS, NODE_ENGINE } from '../versions.js';
2
+
3
+ export function generatePackageJson(serverName, tools) {
4
+ return JSON.stringify({
5
+ name: serverName,
6
+ version: '1.0.0',
7
+ description: `MCP server generated from OpenAPI spec (${tools.length} tools)`,
8
+ type: 'module',
9
+ main: 'src/index.js',
10
+ scripts: {
11
+ // Node's own .env loader (22.9+) replaces the dotenv dependency.
12
+ start: 'node --env-file-if-exists=.env src/index.js',
13
+ // `mcp-use dev` imports the default-exported server, hot-reloads it and
14
+ // mounts the Inspector next to the MCP endpoint.
15
+ dev: 'mcp-use dev --entry src/index.js',
16
+ 'start:inspector': 'mcp-use start --entry src/index.js --with-inspector',
17
+ },
18
+ dependencies: { ...DEPENDENCY_VERSIONS },
19
+ engines: { node: NODE_ENGINE },
20
+ }, null, 2) + '\n';
21
+ }
@@ -0,0 +1,69 @@
1
+ import { literal } from '../spec/schema-to-zod.js';
2
+
3
+ /** `src/policy.js` of the generated server: tool policy, host allowlist, header filter. */
4
+ export function generatePolicy(baseUrl) {
5
+ let specHost = '';
6
+ try {
7
+ specHost = baseUrl ? new URL(baseUrl).host : '';
8
+ } catch { /* relative or templated server URL — no default allowlist */ }
9
+
10
+ return `// Runtime security policy for the generated MCP server
11
+
12
+ const BLOCKED_HEADER_NAMES = new Set([
13
+ 'authorization', 'cookie', 'set-cookie', 'x-api-key',
14
+ 'x-auth-token', 'proxy-authorization', 'www-authenticate',
15
+ 'host', 'content-length', 'transfer-encoding', 'connection',
16
+ ]);
17
+
18
+ // The API host the spec declares. With ALLOWED_API_HOSTS unset, only this
19
+ // host may be called, so a tampered API_BASE_URL cannot redirect credentials.
20
+ const SPEC_API_HOST = ${literal(specHost)};
21
+
22
+ const intEnv = (name, fallback) => {
23
+ const n = Number.parseInt(process.env[name] ?? '', 10);
24
+ return Number.isFinite(n) && n > 0 ? n : fallback;
25
+ };
26
+
27
+ export const MAX_RESPONSE_BYTES = intEnv('MAX_RESPONSE_BYTES', 10 * 1024 * 1024);
28
+ export const REQUEST_TIMEOUT_MS = intEnv('REQUEST_TIMEOUT_MS', 30_000);
29
+
30
+ /** Drop credential and hop-by-hop headers a tool argument tries to set. */
31
+ export function sanitizeHeaderParams(headerParams = {}) {
32
+ const out = {};
33
+ for (const [k, v] of Object.entries(headerParams)) {
34
+ if (!BLOCKED_HEADER_NAMES.has(k.toLowerCase())) out[k] = String(v);
35
+ }
36
+ return out;
37
+ }
38
+
39
+ export function assertAllowedBaseUrl(baseUrl) {
40
+ const url = new URL(baseUrl);
41
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') {
42
+ throw new Error(\`Unsupported API protocol: \${url.protocol}\`);
43
+ }
44
+ const configured = (process.env.ALLOWED_API_HOSTS || '')
45
+ .split(',')
46
+ .map(s => s.trim())
47
+ .filter(Boolean);
48
+ const allowedHosts = configured.length > 0 ? configured : [SPEC_API_HOST].filter(Boolean);
49
+ if (allowedHosts.length > 0 && !allowedHosts.includes(url.host)) {
50
+ throw new Error(\`Disallowed API host: \${url.host}. Add it to ALLOWED_API_HOSTS to allow it.\`);
51
+ }
52
+ }
53
+
54
+ export function checkToolPolicy(toolConfig) {
55
+ if (!toolConfig.enabledByDefault && process.env.ALLOW_RESTRICTED_TOOLS !== 'true') {
56
+ throw new Error(
57
+ \`Tool '\${toolConfig.name}' is disabled by policy (riskLevel: \${toolConfig.riskLevel}). \` +
58
+ 'Set ALLOW_RESTRICTED_TOOLS=true to enable restricted tools.'
59
+ );
60
+ }
61
+ if (toolConfig.requiresApproval && process.env.REQUIRE_APPROVALS !== 'false') {
62
+ throw new Error(
63
+ \`Tool '\${toolConfig.name}' requires approval (riskLevel: \${toolConfig.riskLevel}). \` +
64
+ 'Set REQUIRE_APPROVALS=false to bypass, or route through an approval handler.'
65
+ );
66
+ }
67
+ }
68
+ `;
69
+ }
@@ -0,0 +1,124 @@
1
+ import { NODE_ENGINE } from '../versions.js';
2
+
3
+ const cell = (s) => String(s).replace(/\|/g, '\\|').replace(/\s+/g, ' ');
4
+
5
+ /** `README.md` of the generated server. */
6
+ export function generateReadme(serverName, tools, specPath, baseUrl, port) {
7
+ const toolList = tools
8
+ .map(t => {
9
+ const desc = t.description.split('\n')[0];
10
+ return `| \`${t.name}\` | ${t.method.toUpperCase()} | ${cell(t.pathTemplate)} | ${t.riskLevel} | ${cell(desc.substring(0, 60))}${desc.length > 60 ? '…' : ''} |`;
11
+ })
12
+ .join('\n');
13
+
14
+ return `# ${serverName}
15
+
16
+ MCP server generated from an OpenAPI specification by [api2ai](https://www.npmjs.com/package/api2ai), built on the [mcp-use](https://mcp-use.com) framework.
17
+
18
+ - 🛠️ **${tools.length} tools** — one per OpenAPI operation, with Zod-validated input
19
+ - 📡 **Streamable HTTP** at \`/mcp\`
20
+ - 🔍 **Inspector** at \`/mcp/inspector\` via \`npm run dev\`
21
+ - 🔐 **Auth** — bearer token or a custom header, never overridable by tool input
22
+ - 🛡️ **Risk policy** — mutating and sensitive tools are off until you opt in
23
+
24
+ ## Quick start
25
+
26
+ Requires Node ${NODE_ENGINE}.
27
+
28
+ \`\`\`bash
29
+ npm install
30
+ cp .env.example .env # then add your API credentials
31
+ npm start # http://localhost:${port}/mcp
32
+ npm run dev # hot reload + Inspector at http://localhost:${port}/mcp/inspector
33
+ \`\`\`
34
+
35
+ ## Security
36
+
37
+ **Generation-time classification** — each tool is \`low\`, \`medium\` or \`high\` risk.
38
+ Read-only (\`GET\`/\`HEAD\`/\`OPTIONS\`) operations are \`low\` and enabled.
39
+ Mutating operations are \`medium\` and blocked unless \`ALLOW_RESTRICTED_TOOLS=true\`.
40
+ Anything matching admin, payment, auth, billing or credential patterns is \`high\`.
41
+
42
+ **Runtime policy** — \`checkToolPolicy()\` runs before every call and enforces the risk level and approval settings below.
43
+
44
+ **HTTP hardening** — request timeouts, a streamed response size cap, no redirects, a host allowlist (by default only the spec's API host), and credential headers that tool arguments cannot set.
45
+
46
+ > The Inspector exposes every registered tool. Don't run \`npm run dev\` or \`start:inspector\` on a public interface.
47
+
48
+ ## Environment variables
49
+
50
+ | Variable | Description | Default |
51
+ |----------|-------------|---------|
52
+ | \`PORT\` | Server port | ${port} |
53
+ | \`HOST\` | Bind address (\`0.0.0.0\` for containers) | 127.0.0.1 |
54
+ | \`API_BASE_URL\` | Base URL for API requests | ${baseUrl || '(none in spec)'} |
55
+ | \`API_KEY\` | Sent as \`Authorization: Bearer …\` | - |
56
+ | \`API_AUTH_HEADER\` | Custom auth header, \`Header-Name:value\` | - |
57
+ | \`ALLOWED_API_HOSTS\` | Comma-separated API hosts that may be called | the spec's host |
58
+ | \`ALLOW_RESTRICTED_TOOLS\` | Enable medium/high-risk tools | false |
59
+ | \`REQUIRE_APPROVALS\` | Refuse restricted tools until approved | true |
60
+ | \`REQUEST_TIMEOUT_MS\` | Upstream request timeout | 30000 |
61
+ | \`MAX_RESPONSE_BYTES\` | Upstream response size cap | 10485760 |
62
+ | \`MCP_URL\` | Public URL when behind a proxy (its host is allowed) | - |
63
+ | \`ALLOWED_ORIGINS\` | Browser origins allowed to call \`/mcp\` | - |
64
+
65
+ ## Connect a client
66
+
67
+ **Claude Code**
68
+
69
+ \`\`\`bash
70
+ claude mcp add --transport http ${serverName} http://localhost:${port}/mcp
71
+ \`\`\`
72
+
73
+ **Claude Desktop, Cursor, and other JSON-configured clients**
74
+
75
+ \`\`\`json
76
+ {
77
+ "mcpServers": {
78
+ "${serverName}": { "url": "http://localhost:${port}/mcp" }
79
+ }
80
+ }
81
+ \`\`\`
82
+
83
+ ## Tools
84
+
85
+ | Tool | Method | Path | Risk | Description |
86
+ |------|--------|------|------|-------------|
87
+ ${toolList}
88
+
89
+ ## Project structure
90
+
91
+ \`\`\`
92
+ ${serverName}/
93
+ ├── .env / .env.example
94
+ ├── package.json
95
+ └── src/
96
+ ├── index.js # MCPServer + one server.tool() per operation
97
+ ├── http-client.js # Outbound requests (timeouts, size cap, no redirects)
98
+ ├── tools-config.js # Method, path, parameters and risk for each tool
99
+ └── policy.js # Tool policy, host allowlist, header filtering
100
+ \`\`\`
101
+
102
+ ## Deploy
103
+
104
+ \`\`\`dockerfile
105
+ FROM node:22-alpine
106
+ WORKDIR /app
107
+ COPY package*.json ./
108
+ RUN npm ci --omit=dev
109
+ COPY . .
110
+ ENV NODE_ENV=production HOST=0.0.0.0
111
+ EXPOSE ${port}
112
+ CMD ["node", "src/index.js"]
113
+ \`\`\`
114
+
115
+ ## Source
116
+
117
+ - **OpenAPI spec**: \`${specPath}\`
118
+ - **Generated**: ${new Date().toISOString()}
119
+
120
+ ## License
121
+
122
+ MIT
123
+ `;
124
+ }
@@ -0,0 +1,128 @@
1
+ import { literal } from '../spec/schema-to-zod.js';
2
+
3
+ function toolRegistration(tool) {
4
+ return `
5
+ server.tool(
6
+ {
7
+ name: ${literal(tool.name)},
8
+ title: ${literal(tool.title)},
9
+ description: ${literal(tool.description)},
10
+ inputSchema: ${tool.zodSchema},
11
+ annotations: ${JSON.stringify(tool.annotations)},
12
+ },
13
+ (params) => runTool(${literal(tool.name)}, params),
14
+ );`;
15
+ }
16
+
17
+ /** `src/index.js` of the generated server, written against the mcp-use 2.x API. */
18
+ export function generateServerIndex(serverName, tools, baseUrl, port) {
19
+ return `#!/usr/bin/env node
20
+
21
+ /**
22
+ * ${serverName.replace(/\*\//g, '* /')} — MCP server (${tools.length} tools), generated by api2ai.
23
+ *
24
+ * npm start serve on http://localhost:${port}/mcp
25
+ * npm run dev hot reload + Inspector at /mcp/inspector (mcp-use dev)
26
+ */
27
+
28
+ import { realpathSync } from 'node:fs';
29
+ import { fileURLToPath } from 'node:url';
30
+ import { MCPServer } from 'mcp-use';
31
+ import { z } from 'zod';
32
+ import { executeRequest } from './http-client.js';
33
+ import { toolConfigMap } from './tools-config.js';
34
+ import { assertAllowedBaseUrl, checkToolPolicy } from './policy.js';
35
+
36
+ // ============================================================================
37
+ // Configuration
38
+ // ============================================================================
39
+
40
+ const PORT = Number.parseInt(process.env.PORT || '${port}', 10);
41
+
42
+ const apiConfig = {
43
+ baseUrl: process.env.API_BASE_URL || ${baseUrl ? literal(baseUrl) : 'null'},
44
+ headers: {},
45
+ };
46
+
47
+ if (process.env.API_KEY) {
48
+ apiConfig.headers['Authorization'] = \`Bearer \${process.env.API_KEY}\`;
49
+ }
50
+
51
+ if (process.env.API_AUTH_HEADER) {
52
+ const [key, ...valueParts] = process.env.API_AUTH_HEADER.split(':');
53
+ const value = valueParts.join(':'); // values may contain colons
54
+ if (key && value) apiConfig.headers[key.trim()] = value.trim();
55
+ }
56
+
57
+ if (apiConfig.baseUrl) assertAllowedBaseUrl(apiConfig.baseUrl);
58
+
59
+ // mcp-use matches allowedOrigins/allowedHosts by hostname; accept full URLs too.
60
+ const hostnames = (list) => (list || '')
61
+ .split(',')
62
+ .map(s => s.trim())
63
+ .filter(Boolean)
64
+ .map(s => { try { return new URL(s).hostname; } catch { return s; } });
65
+
66
+ const allowedOrigins = hostnames(process.env.ALLOWED_ORIGINS);
67
+ const allowedHosts = hostnames(process.env.MCP_URL);
68
+
69
+ // ============================================================================
70
+ // Server
71
+ // ============================================================================
72
+
73
+ const server = new MCPServer({
74
+ name: ${literal(serverName)},
75
+ version: '1.0.0',
76
+ description: 'MCP server generated from an OpenAPI specification',
77
+ port: PORT,
78
+ ...(allowedOrigins.length > 0 && { allowedOrigins }),
79
+ ...(allowedHosts.length > 0 && { allowedHosts }),
80
+ });
81
+
82
+ const errorResult = (message) => ({ isError: true, content: [{ type: 'text', text: message }] });
83
+
84
+ /** Policy check → upstream call → MCP tool result. Shared by every tool. */
85
+ async function runTool(name, params) {
86
+ const toolConfig = toolConfigMap.get(name);
87
+ try {
88
+ checkToolPolicy(toolConfig);
89
+ const result = await executeRequest(toolConfig, params, apiConfig);
90
+ const body = typeof result.data === 'string' ? result.data : JSON.stringify(result.data, null, 2);
91
+
92
+ if (!result.ok) {
93
+ return errorResult(\`Error: \${result.status} \${result.statusText}\\n\${body}\`);
94
+ }
95
+ // Objects and arrays also go out as structuredContent for clients that use it.
96
+ return typeof result.data === 'object' && result.data !== null
97
+ ? { content: [{ type: 'text', text: body }], structuredContent: result.data }
98
+ : { content: [{ type: 'text', text: body || \`\${result.status} \${result.statusText}\` }] };
99
+ } catch (err) {
100
+ return errorResult(err?.message || String(err));
101
+ }
102
+ }
103
+
104
+ // ============================================================================
105
+ // Tools
106
+ // ============================================================================
107
+ ${tools.map(toolRegistration).join('\n')}
108
+
109
+ export default server;
110
+
111
+ // ============================================================================
112
+ // Start — only when run directly; \`mcp-use dev\` imports the default export.
113
+ // ============================================================================
114
+
115
+ const isMain = Boolean(process.argv[1]) && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
116
+
117
+ if (isMain) {
118
+ const { url } = await server.listen(PORT);
119
+ console.log(\`
120
+ 🚀 ${serverName.replace(/[`$\\]/g, '\\$&')} MCP server
121
+ MCP endpoint: \${url}
122
+ Tools: ${tools.length}
123
+ API base: \${apiConfig.baseUrl || 'not configured (set API_BASE_URL)'}
124
+ Policy: ALLOW_RESTRICTED_TOOLS=\${process.env.ALLOW_RESTRICTED_TOOLS || 'false'}, REQUIRE_APPROVALS=\${process.env.REQUIRE_APPROVALS || 'true'}
125
+ \`);
126
+ }
127
+ `;
128
+ }
@@ -0,0 +1,25 @@
1
+ /** `src/tools-config.js` of the generated server: per-tool routing + policy data. */
2
+ export function generateToolsConfig(tools) {
3
+ const toolConfigs = tools.map(tool => ({
4
+ name: tool.name,
5
+ description: tool.description,
6
+ method: tool.method,
7
+ pathTemplate: tool.pathTemplate,
8
+ executionParameters: tool.executionParameters,
9
+ requestBodyContentType: tool.requestBodyContentType,
10
+ baseUrl: tool.baseUrl,
11
+ tags: tool.tags,
12
+ operationId: tool.operationId,
13
+ riskLevel: tool.riskLevel,
14
+ requiresApproval: tool.requiresApproval,
15
+ allowedInInspector: tool.allowedInInspector,
16
+ enabledByDefault: tool.enabledByDefault,
17
+ }));
18
+
19
+ return `// Tool configurations extracted from the OpenAPI spec
20
+
21
+ export const toolConfigs = ${JSON.stringify(toolConfigs, null, 2)};
22
+
23
+ export const toolConfigMap = new Map(toolConfigs.map(t => [t.name, t]));
24
+ `;
25
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Dependency versions written into every generated server's package.json.
3
+ *
4
+ * The generated code targets the mcp-use 2.x API (`import { MCPServer } from
5
+ * 'mcp-use'`, `inputSchema`, async `listen()`); a caret on the major keeps
6
+ * `npm install` from pulling a 3.x that the templates were not written for.
7
+ * Bump these together with the templates in ./templates/.
8
+ */
9
+ export const DEPENDENCY_VERSIONS = {
10
+ 'mcp-use': '^2.7.3',
11
+ // mcp-use validates tool input through Standard Schema; zod 4 implements it.
12
+ zod: '^4.6.5',
13
+ };
14
+
15
+ /** mcp-use 2.x declares `engines.node >= 22.22.2`. */
16
+ export const NODE_ENGINE = '>=22.22.2';