@mcpmake/core 0.3.3 → 0.3.5

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 (130) hide show
  1. package/dist/analyzer/goal-crawler.d.ts.map +1 -1
  2. package/dist/analyzer/goal-crawler.js +6 -12
  3. package/dist/analyzer/goal-crawler.js.map +1 -1
  4. package/dist/analyzer/same-origin.d.ts +21 -0
  5. package/dist/analyzer/same-origin.d.ts.map +1 -0
  6. package/dist/analyzer/same-origin.js +32 -0
  7. package/dist/analyzer/same-origin.js.map +1 -0
  8. package/dist/analyzer/semantic-analyzer.d.ts.map +1 -1
  9. package/dist/analyzer/semantic-analyzer.js +3 -0
  10. package/dist/analyzer/semantic-analyzer.js.map +1 -1
  11. package/dist/analyzer/site-crawler.d.ts.map +1 -1
  12. package/dist/analyzer/site-crawler.js +12 -13
  13. package/dist/analyzer/site-crawler.js.map +1 -1
  14. package/dist/emitter/index.d.ts.map +1 -1
  15. package/dist/emitter/index.js +149 -19
  16. package/dist/emitter/index.js.map +1 -1
  17. package/dist/emitter/mcpb-bundler.d.ts +14 -0
  18. package/dist/emitter/mcpb-bundler.d.ts.map +1 -1
  19. package/dist/emitter/mcpb-bundler.js +52 -4
  20. package/dist/emitter/mcpb-bundler.js.map +1 -1
  21. package/dist/emitter/project-scaffolder.d.ts +5 -0
  22. package/dist/emitter/project-scaffolder.d.ts.map +1 -1
  23. package/dist/emitter/project-scaffolder.js +1 -1
  24. package/dist/emitter/project-scaffolder.js.map +1 -1
  25. package/dist/emitter/python-annotations.d.ts +77 -0
  26. package/dist/emitter/python-annotations.d.ts.map +1 -0
  27. package/dist/emitter/python-annotations.js +330 -0
  28. package/dist/emitter/python-annotations.js.map +1 -0
  29. package/dist/emitter/python-template-loader.js +1 -1
  30. package/dist/emitter/python-template-loader.js.map +1 -1
  31. package/dist/emitter/python-templates/server.py.hbs +69 -33
  32. package/dist/emitter/site-templates/browser-manager.ts.hbs +145 -32
  33. package/dist/emitter/site-templates/config.ts.hbs +21 -4
  34. package/dist/emitter/site-templates/env.example.hbs +8 -0
  35. package/dist/emitter/site-templates/server-main-http.ts.hbs +13 -4
  36. package/dist/emitter/site-templates/server-main.ts.hbs +5 -2
  37. package/dist/emitter/site-templates/telemetry.ts.hbs +13 -2
  38. package/dist/emitter/templates/config.ts.hbs +29 -2
  39. package/dist/emitter/templates/discovery.ts.hbs +66 -13
  40. package/dist/emitter/templates/http-executor.ts.hbs +4 -3
  41. package/dist/emitter/templates/oauth.ts.hbs +1 -2
  42. package/dist/emitter/templates/readme.md.hbs +3 -2
  43. package/dist/emitter/templates/resources.ts.hbs +3 -2
  44. package/dist/emitter/templates/server-main-http.ts.hbs +218 -130
  45. package/dist/emitter/templates/task-handlers.ts.hbs +2 -1
  46. package/dist/emitter/templates/task-manager.ts.hbs +5 -3
  47. package/dist/emitter/templates/task-sse.ts.hbs +9 -1
  48. package/dist/emitter/templates/tool-handler.ts.hbs +17 -4
  49. package/dist/emitter/worker-templates/config.ts.hbs +25 -2
  50. package/dist/emitter/worker-templates/worker.ts.hbs +6 -2
  51. package/dist/emitter/worker-templates/wrangler.toml.hbs +5 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/parser/har-normalizer.d.ts.map +1 -1
  57. package/dist/parser/har-normalizer.js +28 -3
  58. package/dist/parser/har-normalizer.js.map +1 -1
  59. package/dist/parser/openapi-loader.d.ts +19 -0
  60. package/dist/parser/openapi-loader.d.ts.map +1 -1
  61. package/dist/parser/openapi-loader.js +78 -10
  62. package/dist/parser/openapi-loader.js.map +1 -1
  63. package/dist/parser/operation-extractor.js +4 -4
  64. package/dist/parser/operation-extractor.js.map +1 -1
  65. package/dist/parser/overlay-loader.js +50 -11
  66. package/dist/parser/overlay-loader.js.map +1 -1
  67. package/dist/parser/postman-loader.d.ts.map +1 -1
  68. package/dist/parser/postman-loader.js +64 -6
  69. package/dist/parser/postman-loader.js.map +1 -1
  70. package/dist/parser/schema-converter.d.ts +15 -1
  71. package/dist/parser/schema-converter.d.ts.map +1 -1
  72. package/dist/parser/schema-converter.js +30 -2
  73. package/dist/parser/schema-converter.js.map +1 -1
  74. package/dist/recorder/browser-recorder.d.ts.map +1 -1
  75. package/dist/recorder/browser-recorder.js +2 -1
  76. package/dist/recorder/browser-recorder.js.map +1 -1
  77. package/dist/rescan/diff-engine.js +17 -10
  78. package/dist/rescan/diff-engine.js.map +1 -1
  79. package/dist/rescan/rescan-runner.js +4 -4
  80. package/dist/rescan/rescan-runner.js.map +1 -1
  81. package/dist/rescan/rescan-scheduler.d.ts +54 -2
  82. package/dist/rescan/rescan-scheduler.d.ts.map +1 -1
  83. package/dist/rescan/rescan-scheduler.js +193 -19
  84. package/dist/rescan/rescan-scheduler.js.map +1 -1
  85. package/dist/site-transformer/selector-healer.d.ts.map +1 -1
  86. package/dist/site-transformer/selector-healer.js +9 -0
  87. package/dist/site-transformer/selector-healer.js.map +1 -1
  88. package/dist/site-transformer/tool-generator.js +4 -7
  89. package/dist/site-transformer/tool-generator.js.map +1 -1
  90. package/dist/transformer/auth-detector.d.ts.map +1 -1
  91. package/dist/transformer/auth-detector.js +49 -1
  92. package/dist/transformer/auth-detector.js.map +1 -1
  93. package/dist/transformer/catalog-builder.d.ts +25 -3
  94. package/dist/transformer/catalog-builder.d.ts.map +1 -1
  95. package/dist/transformer/catalog-builder.js +32 -12
  96. package/dist/transformer/catalog-builder.js.map +1 -1
  97. package/dist/transformer/client-compat.d.ts.map +1 -1
  98. package/dist/transformer/client-compat.js +17 -6
  99. package/dist/transformer/client-compat.js.map +1 -1
  100. package/dist/transformer/har-dedup.js +2 -2
  101. package/dist/transformer/har-dedup.js.map +1 -1
  102. package/dist/transformer/har-to-operations.d.ts.map +1 -1
  103. package/dist/transformer/har-to-operations.js +10 -3
  104. package/dist/transformer/har-to-operations.js.map +1 -1
  105. package/dist/transformer/llm-namer.d.ts.map +1 -1
  106. package/dist/transformer/llm-namer.js +3 -0
  107. package/dist/transformer/llm-namer.js.map +1 -1
  108. package/dist/transformer/resource-builder.d.ts.map +1 -1
  109. package/dist/transformer/resource-builder.js +112 -18
  110. package/dist/transformer/resource-builder.js.map +1 -1
  111. package/dist/transformer/stainless-translator.d.ts.map +1 -1
  112. package/dist/transformer/stainless-translator.js +5 -2
  113. package/dist/transformer/stainless-translator.js.map +1 -1
  114. package/dist/transformer/tool-builder.d.ts.map +1 -1
  115. package/dist/transformer/tool-builder.js +67 -16
  116. package/dist/transformer/tool-builder.js.map +1 -1
  117. package/dist/types/index.d.ts +39 -0
  118. package/dist/types/index.d.ts.map +1 -1
  119. package/dist/utils/logger.d.ts +38 -1
  120. package/dist/utils/logger.d.ts.map +1 -1
  121. package/dist/utils/logger.js +75 -1
  122. package/dist/utils/logger.js.map +1 -1
  123. package/dist/utils/playwright-loader.d.ts +21 -0
  124. package/dist/utils/playwright-loader.d.ts.map +1 -0
  125. package/dist/utils/playwright-loader.js +21 -0
  126. package/dist/utils/playwright-loader.js.map +1 -0
  127. package/dist/utils/ssrf-guard.d.ts.map +1 -1
  128. package/dist/utils/ssrf-guard.js +8 -0
  129. package/dist/utils/ssrf-guard.js.map +1 -1
  130. package/package.json +4 -3
@@ -2,20 +2,29 @@ export interface AppConfig {
2
2
  baseUrl: string;
3
3
  {{#each authSchemes}}
4
4
  {{#if (eq type "apiKey")}}
5
+ {{#unless (eq emitApiKeyValue false)}}
5
6
  apiKey?: string;
7
+ {{/unless}}
6
8
  {{#if (eq in "query")}}
9
+ {{#unless (eq emitApiKeyQueryName false)}}
7
10
  /** Query-parameter name the API key is appended under (apiKey-in-query auth). */
8
11
  apiKeyQueryName?: string;
12
+ {{/unless}}
9
13
  {{/if}}
10
14
  {{/if}}
11
15
  {{#if (eq type "http-bearer")}}
16
+ {{#unless (eq emitBearer false)}}
12
17
  bearerToken?: string;
18
+ {{/unless}}
13
19
  {{/if}}
14
20
  {{#if (eq type "http-basic")}}
21
+ {{#unless (eq emitBasic false)}}
15
22
  basicUsername?: string;
16
23
  basicPassword?: string;
24
+ {{/unless}}
17
25
  {{/if}}
18
26
  {{#if (eq type "oauth2")}}
27
+ {{#unless (eq emitOAuth2 false)}}
19
28
  oauth2Token?: string;
20
29
  oauth2ClientId?: string;
21
30
  oauth2ClientSecret?: string;
@@ -25,6 +34,7 @@ export interface AppConfig {
25
34
  /** OAuth scopes requested on token grants. Defaults to the spec-declared
26
35
  * scopes; override at runtime with OAUTH2_SCOPES (space-separated). */
27
36
  oauth2Scopes: string[];
37
+ {{/unless}}
28
38
  {{/if}}
29
39
  {{/each}}
30
40
  maxRetries: number;
@@ -81,6 +91,7 @@ function loadDefaultHeaders(): Record<string, string> {
81
91
 
82
92
  {{#each authSchemes}}
83
93
  {{#if (eq type "oauth2")}}
94
+ {{#unless (eq emitOAuth2 false)}}
84
95
  /**
85
96
  * Resolve the OAuth scopes requested on token grants. Defaults to the
86
97
  * spec-declared scopes baked in at generation time; `OAUTH2_SCOPES`
@@ -95,16 +106,32 @@ function resolveOAuthScopes(): string[] {
95
106
  return {{#if scopes}}{{{json scopes}}}{{else}}[]{{/if}};
96
107
  }
97
108
 
109
+ {{/unless}}
98
110
  {{/if}}
99
111
  {{/each}}
112
+ const posInt = (v: string | undefined, d: number): number => {
113
+ const n = parseInt(v ?? '', 10);
114
+ return Number.isInteger(n) && n > 0 ? n : d;
115
+ };
116
+
117
+ /** Like posInt but accepts 0 — useful where 0 is a meaningful "disable" value. */
118
+ const nonNegInt = (v: string | undefined, d: number): number => {
119
+ const n = parseInt(v ?? '', 10);
120
+ return Number.isInteger(n) && n >= 0 ? n : d;
121
+ };
122
+
100
123
  export function loadConfig(): AppConfig {
101
124
  return {
102
125
  baseUrl: resolveBaseUrl(),
103
126
  {{#each authSchemes}}
104
127
  {{#if (eq type "apiKey")}}
128
+ {{#unless (eq emitApiKeyValue false)}}
105
129
  apiKey: process.env.{{envVarName}},
130
+ {{/unless}}
106
131
  {{#if (eq in "query")}}
132
+ {{#unless (eq emitApiKeyQueryName false)}}
107
133
  apiKeyQueryName: {{{json headerName}}},
134
+ {{/unless}}
108
135
  {{/if}}
109
136
  {{/if}}
110
137
  {{#if (eq type "http-bearer")}}
@@ -124,8 +151,8 @@ export function loadConfig(): AppConfig {
124
151
  oauth2Scopes: resolveOAuthScopes(),
125
152
  {{/if}}
126
153
  {{/each}}
127
- maxRetries: parseInt(process.env.MAX_RETRIES ?? '3', 10),
128
- requestIntervalMs: parseInt(process.env.REQUEST_INTERVAL_MS ?? '100', 10),
154
+ maxRetries: nonNegInt(process.env.MAX_RETRIES, 3),
155
+ requestIntervalMs: nonNegInt(process.env.REQUEST_INTERVAL_MS, 100),
129
156
  defaultHeaders: loadDefaultHeaders(),
130
157
  };
131
158
  }
@@ -10,8 +10,16 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
10
  import { executeRequest } from './http.js';
11
11
  import { applyJqFilter } from './response-filter.js';
12
12
  import type { AppConfig } from './config.js';
13
+ import type { AuthRequirement } from './auth.js';
13
14
  import catalogData from './tool-catalog.json' with { type: 'json' };
14
15
 
16
+ interface CatalogParamMapping {
17
+ /** The key the agent supplies in the tool input. */
18
+ inputKey: string;
19
+ /** The original API parameter name sent upstream. */
20
+ wireName: string;
21
+ }
22
+
15
23
  interface CatalogEntry {
16
24
  name: string;
17
25
  title: string;
@@ -19,10 +27,19 @@ interface CatalogEntry {
19
27
  method: string;
20
28
  path: string;
21
29
  inputSchema: Record<string, unknown>;
22
- pathParams: string[];
23
- queryParams: string[];
30
+ /** Path parameters with inputKey (agent-facing) and wireName (upstream API). */
31
+ pathParams: CatalogParamMapping[];
32
+ /** Query parameters with inputKey (agent-facing) and wireName (upstream API). */
33
+ queryParams: CatalogParamMapping[];
24
34
  hasRequestBody: boolean;
25
35
  requestBodyContentType: string;
36
+ /** The MCP input key for the request body (`body`, `requestBody`, etc.).
37
+ * Only present when `hasRequestBody` is true. */
38
+ bodyInputKey?: string;
39
+ /** Per-operation outbound-auth requirement (D-H2). Forwarded to executeRequest
40
+ * so a public operation (`security: []`) sends no auth and a scheme-scoped
41
+ * operation sends only its scheme(s). Absent → every configured scheme. */
42
+ authRequirement?: AuthRequirement;
26
43
  }
27
44
 
28
45
  const catalog: CatalogEntry[] = catalogData as CatalogEntry[];
@@ -157,33 +174,69 @@ export function registerDiscoveryTools(server: McpServer, config: AppConfig): vo
157
174
  }
158
175
 
159
176
  try {
160
- // Build URL from path template
161
- let url = config.baseUrl + tool.path;
162
- for (const param of tool.pathParams) {
163
- if (args[param] !== undefined) {
164
- url = url.replace(`{${param}}`, encodeURIComponent(String(args[param])));
177
+ // Build the path suffix from the template — read args by inputKey,
178
+ // substitute wireName into the path. Handles parameter name collisions
179
+ // (R14-A). The leftover-placeholder check (R5-F) runs on the PATH ONLY,
180
+ // not on config.baseUrl: a base URL may legitimately contain an
181
+ // unresolved OpenAPI server variable (e.g. `https://api.{region}.x.com`)
182
+ // that is not a tool path parameter, and scanning it would falsely fail
183
+ // every call.
184
+ let pathSuffix = tool.path;
185
+ for (const p of tool.pathParams) {
186
+ if (args[p.inputKey] !== undefined) {
187
+ pathSuffix = pathSuffix.replace(
188
+ `{${p.wireName}}`,
189
+ encodeURIComponent(String(args[p.inputKey])),
190
+ );
165
191
  }
166
192
  }
167
193
 
168
- // Add query params
194
+ // R5-F: refuse to fire a request with unsubstituted path placeholders.
195
+ // A missing required path parameter leaves a literal `{name}` in the
196
+ // path, which would otherwise be sent verbatim upstream. Return an MCP
197
+ // error listing the unfilled parameter(s) instead.
198
+ const missing = pathSuffix.match(/\{[^}]+\}/g);
199
+ if (missing && missing.length > 0) {
200
+ return {
201
+ content: [
202
+ {
203
+ type: 'text' as const,
204
+ text: `Missing required path parameter(s): ${missing
205
+ .map((s) => s.slice(1, -1))
206
+ .join(', ')}. Use get_tool_schema to see required inputs.`,
207
+ },
208
+ ],
209
+ isError: true,
210
+ };
211
+ }
212
+
213
+ // Path is fully substituted — prepend the (possibly server-variable-
214
+ // bearing) base URL to form the request URL.
215
+ let url = config.baseUrl + pathSuffix;
216
+
217
+ // Add query params — read args by inputKey, send upstream under wireName (R14-A).
169
218
  const queryParams = new URLSearchParams();
170
- for (const param of tool.queryParams) {
171
- if (args[param] !== undefined) {
172
- queryParams.set(param, String(args[param]));
219
+ for (const p of tool.queryParams) {
220
+ if (args[p.inputKey] !== undefined) {
221
+ queryParams.set(p.wireName, String(args[p.inputKey]));
173
222
  }
174
223
  }
175
224
  const qs = queryParams.toString();
176
225
  if (qs) url += `?${qs}`;
177
226
 
227
+ const bodyKey = tool.bodyInputKey ?? 'body';
178
228
  const response = await executeRequest({
179
229
  method: tool.method,
180
230
  url,
181
- ...(tool.hasRequestBody && args.body
182
- ? { body: args.body, contentType: tool.requestBodyContentType }
231
+ ...(tool.hasRequestBody && args[bodyKey] !== undefined
232
+ ? { body: args[bodyKey], contentType: tool.requestBodyContentType }
183
233
  : {}),
184
234
  headers: {},
185
235
  config,
186
236
  idempotencyKey,
237
+ // R5-E: scope outbound auth per operation exactly like the static
238
+ // per-tool handlers. Absent → executeRequest applies every scheme.
239
+ authRequirement: tool.authRequirement,
187
240
  });
188
241
 
189
242
  const result =
@@ -194,9 +194,10 @@ export async function executeRequest(options: RequestOptions): Promise<ApiRespon
194
194
  });
195
195
 
196
196
  if (RETRYABLE_STATUS_CODES.includes(response.status) && attempt < config.maxRetries) {
197
- const retryAfter = response.headers.get('Retry-After');
198
- const delay = retryAfter
199
- ? parseInt(retryAfter, 10) * 1000
197
+ const retryAfterRaw = response.headers.get('Retry-After');
198
+ const retryAfterMs = retryAfterRaw ? parseInt(retryAfterRaw, 10) * 1000 : NaN;
199
+ const delay = Number.isFinite(retryAfterMs)
200
+ ? Math.min(retryAfterMs, 60_000)
200
201
  : 1000 * Math.pow(2, attempt);
201
202
  await sleep(delay);
202
203
  continue;
@@ -86,8 +86,7 @@ async function clientCredentialsGrant(config: OAuthConfig): Promise<TokenRespons
86
86
  });
87
87
 
88
88
  if (!response.ok) {
89
- const body = await response.text();
90
- throw new Error(`OAuth client credentials failed (${response.status}): ${body}`);
89
+ throw new Error(`OAuth client credentials failed (${response.status})`);
91
90
  }
92
91
 
93
92
  return response.json() as Promise<TokenResponse>;
@@ -75,14 +75,15 @@ Copy `.env.example` to `.env` and fill in:
75
75
  {{#each authEnvVars}}
76
76
  | `{{name}}` | {{description}} | {{#if required}}Yes{{else}}No{{/if}} |
77
77
  {{/each}}
78
- | `MAX_RETRIES` | Max retry attempts (default: 3) | No |
79
- | `REQUEST_INTERVAL_MS` | Min ms between requests (default: 100) | No |
78
+ | `MAX_RETRIES` | Max retry attempts (default: 3; `0` disables retries) | No |
79
+ | `REQUEST_INTERVAL_MS` | Min ms between requests (default: 100; `0` disables throttling) | No |
80
80
  | `MCP_ENVIRONMENTS` | JSON map of environment name → base URL (e.g. `{"production":"…","sandbox":"…"}`) | No |
81
81
  | `API_ENVIRONMENT` | Selects a named environment from `MCP_ENVIRONMENTS` (falls back to `BASE_URL`) | No |
82
82
  | `MCP_TOOLS` | Comma-separated allowlist of tool names to register | No |
83
83
  | `MCP_EXCLUDE_TOOLS` | Comma-separated denylist of tool names to skip | No |
84
84
  | `MCP_DEFAULT_HEADERS` | JSON object of headers added to every upstream request | No |
85
85
  | `MCP_IDEMPOTENCY_AUTO` | `true` to auto-generate an `Idempotency-Key` for mutating requests | No |
86
+ | `MCP_MAX_SSE_CONNECTIONS` | Max concurrent task-streaming SSE connections (default: 500; HTTP transport only) | No |
86
87
 
87
88
  ### Named environments
88
89
 
@@ -1,4 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
3
  import { executeRequest } from './http.js';
3
4
  import type { AppConfig } from './config.js';
4
5
 
@@ -7,12 +8,12 @@ export function registerAllResources(server: McpServer, config: AppConfig): void
7
8
  {{#if isTemplate}}
8
9
  server.resource(
9
10
  '{{name}}',
10
- '{{uri}}',
11
+ new ResourceTemplate('{{uri}}', { list: undefined }),
11
12
  {
12
13
  description: `{{{description}}}`,
13
14
  mimeType: 'application/json',
14
15
  },
15
- async (uri) => {
16
+ async (uri, variables) => {
16
17
  {{{urlBody}}}
17
18
  const response = await executeRequest({
18
19
  method: 'get',