Skip to content

Commit d2e32ac

Browse files
committed
fix: mcp ssl verification
This commit squashes two fixes: - fix(config-yaml): Handles the 'api_key' field in config.yaml, allowing for snake_case, which fixes authentication for providers like OpenRouter and Groq. - fix(mcp): Passes the 'verifySsl' option to sse and streamable-http MCP transports, allowing users to disable SSL verification for debugging.
1 parent c8639ac commit d2e32ac

File tree

2 files changed

+83
-46
lines changed

2 files changed

+83
-46
lines changed

core/context/mcp/MCPConnection.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpsProxyAgent } from "https-proxy-agent";
12
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
23
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
34

@@ -346,29 +347,48 @@ class MCPConnection {
346347
return transport;
347348
case "websocket":
348349
return new WebSocketClientTransport(new URL(options.transport.url));
349-
case "sse":
350-
return new SSEClientTransport(new URL(options.transport.url), {
350+
case "sse": {
351+
const { url, requestOptions } = options.transport;
352+
const agent =
353+
typeof requestOptions?.verifySsl === "boolean" &&
354+
!requestOptions.verifySsl
355+
? new HttpsProxyAgent({ rejectUnauthorized: false })
356+
: undefined;
357+
358+
const customFetch = (
359+
input: RequestInfo | URL,
360+
init?: RequestInit,
361+
): Promise<Response> => {
362+
return fetch(input, {
363+
...init,
364+
// @ts-ignore
365+
agent,
366+
});
367+
};
368+
369+
return new SSEClientTransport(new URL(url), {
351370
eventSourceInit: {
352-
fetch: (input, init) =>
353-
fetch(input, {
354-
...init,
355-
headers: {
356-
...init?.headers,
357-
...(options.transport.requestOptions?.headers as
358-
| Record<string, string>
359-
| undefined),
360-
},
361-
}),
371+
// @ts-ignore
372+
fetch: customFetch,
373+
headers: requestOptions?.headers as Record<string, string>,
362374
},
363-
requestInit: { headers: options.transport.requestOptions?.headers },
375+
requestInit: { headers: requestOptions?.headers },
364376
});
365-
case "streamable-http":
366-
return new StreamableHTTPClientTransport(
367-
new URL(options.transport.url),
368-
{
369-
requestInit: { headers: options.transport.requestOptions?.headers },
370-
},
371-
);
377+
}
378+
case "streamable-http": {
379+
const { url, requestOptions } = options.transport;
380+
const agent =
381+
typeof requestOptions?.verifySsl === "boolean" &&
382+
!requestOptions.verifySsl
383+
? new HttpsProxyAgent({ rejectUnauthorized: false })
384+
: undefined;
385+
386+
return new StreamableHTTPClientTransport(new URL(url), {
387+
// @ts-ignore
388+
agent,
389+
requestInit: { headers: requestOptions?.headers },
390+
});
391+
}
372392
default:
373393
throw new Error(
374394
`Unsupported transport type: ${(options.transport as any).type}`,

packages/config-yaml/src/schemas/models.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ const baseModelFields = {
134134
name: z.string(),
135135
model: z.string(),
136136
apiKey: z.string().optional(),
137+
api_key: z.string().optional(),
137138
apiBase: z.string().optional(),
138139
maxStopWords: z.number().optional(),
139140
roles: modelRolesSchema.array().optional(),
@@ -151,37 +152,53 @@ const baseModelFields = {
151152
autocompleteOptions: autocompleteOptionsSchema.optional(),
152153
};
153154

154-
export const modelSchema = z.union([
155-
z.object({
156-
...baseModelFields,
157-
provider: z.literal("continue-proxy"),
158-
apiKeyLocation: z.string().optional(),
159-
envSecretLocations: z.record(z.string(), z.string()).optional(),
160-
orgScopeId: z.string().nullable(),
161-
onPremProxyUrl: z.string().nullable(),
162-
}),
163-
z.object({
164-
...baseModelFields,
165-
provider: z.string().refine((val) => val !== "continue-proxy"),
166-
sourceFile: z.string().optional(),
167-
}),
168-
]);
169-
170-
export const partialModelSchema = z.union([
171-
z
172-
.object({
155+
export const modelSchema = z
156+
.union([
157+
z.object({
173158
...baseModelFields,
174159
provider: z.literal("continue-proxy"),
175160
apiKeyLocation: z.string().optional(),
176161
envSecretLocations: z.record(z.string(), z.string()).optional(),
177-
})
178-
.partial(),
179-
z
180-
.object({
162+
orgScopeId: z.string().nullable(),
163+
onPremProxyUrl: z.string().nullable(),
164+
}),
165+
z.object({
181166
...baseModelFields,
182167
provider: z.string().refine((val) => val !== "continue-proxy"),
183-
})
184-
.partial(),
185-
]);
168+
sourceFile: z.string().optional(),
169+
}),
170+
])
171+
.transform((val) => {
172+
if (val.api_key && !val.apiKey) {
173+
val.apiKey = val.api_key;
174+
}
175+
delete val.api_key;
176+
return val;
177+
});
178+
179+
export const partialModelSchema = z
180+
.union([
181+
z
182+
.object({
183+
...baseModelFields,
184+
provider: z.literal("continue-proxy"),
185+
apiKeyLocation: z.string().optional(),
186+
envSecretLocations: z.record(z.string(), z.string()).optional(),
187+
})
188+
.partial(),
189+
z
190+
.object({
191+
...baseModelFields,
192+
provider: z.string().refine((val) => val !== "continue-proxy"),
193+
})
194+
.partial(),
195+
])
196+
.transform((val) => {
197+
if (val.api_key && !val.apiKey) {
198+
val.apiKey = val.api_key;
199+
}
200+
delete val.api_key;
201+
return val;
202+
});
186203

187204
export type ModelConfig = z.infer<typeof modelSchema>;

0 commit comments

Comments
 (0)