Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/config/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
serializePromptTemplates,
} from "./util";
import { validateConfig } from "./validation.js";
import { mergeMcpRequestOptions } from "./yaml/yamlToContinueConfig";

export function resolveSerializedConfig(
filepath: string,
Expand Down Expand Up @@ -442,7 +443,7 @@
const { name, params } = config.reranker as RerankerDescription;
if (name === "llm") {
const llm = models.find((model) => model.title === params?.modelTitle);
if (!llm) {

Check warning on line 446 in core/config/load.ts

View workflow job for this annotation

GitHub Actions / core-checks

Unexpected negated condition
errors.push({
fatal: false,
message: `Unknown reranking model ${params?.modelTitle}`,
Expand Down Expand Up @@ -526,8 +527,8 @@
).map((server, index) => ({
id: `continue-mcp-server-${index + 1}`,
name: `MCP Server`,
requestOptions: mergeConfigYamlRequestOptions(
requestOptions: mergeMcpRequestOptions(
server.transport.type !== "stdio"

Check warning on line 531 in core/config/load.ts

View workflow job for this annotation

GitHub Actions / core-checks

Unexpected negated condition
? server.transport.requestOptions
: undefined,
config.requestOptions,
Expand Down
104 changes: 104 additions & 0 deletions core/config/yaml/loadYaml.vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
validateConfigYaml,
} from "@continuedev/config-yaml";
import { describe, expect, it } from "vitest";
import { convertYamlMcpConfigToInternalMcpOptions } from "./yamlToContinueConfig";

describe("MCP Server cwd configuration", () => {
describe("YAML schema validation", () => {
Expand Down Expand Up @@ -126,3 +127,106 @@ describe("MCP Server cwd configuration", () => {
});
});
});

describe("convertYamlMcpConfigToInternalMcpOptions", () => {
it("does not inherit global verifySsl false into remote MCP request options", () => {
const result = convertYamlMcpConfigToInternalMcpOptions(
{
name: "remote",
type: "sse",
url: "https://mcp.example.com",
},
{
verifySsl: false,
headers: {
"X-Test": "1",
},
proxy: "https://proxy.example.com",
},
);

expect("requestOptions" in result).toBe(true);
expect(result.requestOptions).toEqual({
headers: {
"X-Test": "1",
},
proxy: "https://proxy.example.com",
});
expect(result.requestOptions?.verifySsl).toBeUndefined();
expect(result.requestOptions).not.toHaveProperty("verifySsl");
});

it("does not inherit global verifySsl false into streamable HTTP MCP options", () => {
const result = convertYamlMcpConfigToInternalMcpOptions(
{
name: "remote",
type: "streamable-http",
url: "https://mcp.example.com",
},
{
verifySsl: false,
timeout: 30,
},
);

expect("requestOptions" in result).toBe(true);
expect(result.requestOptions).toEqual({
timeout: 30,
});
expect(result.requestOptions).not.toHaveProperty("verifySsl");
});

it("preserves explicit per-server verifySsl false", () => {
const result = convertYamlMcpConfigToInternalMcpOptions(
{
name: "remote",
type: "sse",
url: "https://mcp.example.com",
requestOptions: {
verifySsl: false,
},
},
{
verifySsl: false,
},
);

expect("requestOptions" in result).toBe(true);
expect(result.requestOptions?.verifySsl).toBe(false);
});

it("preserves explicit per-server verifySsl true", () => {
const result = convertYamlMcpConfigToInternalMcpOptions(
{
name: "remote",
type: "streamable-http",
url: "https://mcp.example.com",
requestOptions: {
verifySsl: true,
},
},
{
verifySsl: false,
},
);

expect("requestOptions" in result).toBe(true);
expect(result.requestOptions?.verifySsl).toBe(true);
});

it("returns no request options when only global verifySsl is set", () => {
const result = convertYamlMcpConfigToInternalMcpOptions(
{
name: "remote",
type: "sse",
url: "https://mcp.example.com",
},
{
verifySsl: false,
},
);

expect("requestOptions" in result).toBe(true);
expect(result.requestOptions).toBeUndefined();
});
});
27 changes: 26 additions & 1 deletion core/config/yaml/yamlToContinueConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ export function convertYamlRuleToContinueRule(rule: Rule): RuleWithSource {
}
}

export function mergeMcpRequestOptions(
requestOptions?: RequestOptions,
globalRequestOptions?: RequestOptions,
): RequestOptions | undefined {
if (!globalRequestOptions) {
return requestOptions;
}

const {
verifySsl: _globalVerifySsl,
...globalRequestOptionsWithoutVerifySsl
} = globalRequestOptions;

// Global verifySsl can disable MCP certificate validation without a server-specific opt-out.
const sanitizedGlobalRequestOptions =
Object.keys(globalRequestOptionsWithoutVerifySsl).length > 0
? globalRequestOptionsWithoutVerifySsl
: undefined;

return mergeConfigYamlRequestOptions(
requestOptions,
sanitizedGlobalRequestOptions,
);
}

export function convertYamlMcpConfigToInternalMcpOptions(
config: MCPServer,
globalRequestOptions?: RequestOptions,
Expand Down Expand Up @@ -66,7 +91,7 @@ export function convertYamlMcpConfigToInternalMcpOptions(
type,
url,
apiKey,
requestOptions: mergeConfigYamlRequestOptions(
requestOptions: mergeMcpRequestOptions(
requestOptions,
globalRequestOptions,
),
Expand Down
Loading