Skip to content
Merged
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
17 changes: 0 additions & 17 deletions core/config/yaml/loadYaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
ModelRole,
PackageIdentifier,
RegistryClient,
TEMPLATE_VAR_REGEX,
unrollAssistant,
validateConfigYaml,
} from "@continuedev/config-yaml";
Expand Down Expand Up @@ -237,22 +236,6 @@ export async function configYamlToContinueConfig(options: {
sourceFile: doc.sourceFile,
}));

config.mcpServers?.forEach((mcpServer) => {
if ("args" in mcpServer) {
const mcpArgVariables =
mcpServer.args?.filter((arg) => TEMPLATE_VAR_REGEX.test(arg)) ?? [];

if (mcpArgVariables.length === 0) {
return;
}

localErrors.push({
fatal: false,
message: `MCP server "${mcpServer.name}" has unsubstituted variables in args: ${mcpArgVariables.join(", ")}. Please refer to https://docs.continue.dev/hub/secrets/secret-types for managing hub secrets.`,
});
}
});

// Prompt files -
try {
const promptFiles = await getAllPromptFiles(ide, undefined, true);
Expand Down
22 changes: 22 additions & 0 deletions core/context/mcp/MCPConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import { fileURLToPath } from "url";

import {
decodeSecretLocation,
getTemplateVariables,
} from "@continuedev/config-yaml";
import {
SSEClientTransport,
SseError,
Expand Down Expand Up @@ -163,6 +167,24 @@ class MCPConnection {
}
}

const vars = getTemplateVariables(JSON.stringify(this.options));
const unrendered = vars.map((v) => {
const stripped = v.replace("secrets.", "");
try {
return decodeSecretLocation(stripped).secretName;
} catch {
return stripped;
}
});

if (unrendered.length > 0) {
this.errors.push(
`${this.options.name} MCP Server has unresolved secrets: ${unrendered.join(", ")}.
For personal use you can set the secret in the hub at https://hub.continue.dev/settings/secrets.
Org-level secrets can only be used for MCP by Background Agents (https://docs.continue.dev/hub/agents/overview) when \"Include in Env\" is enabled.`,
);
}

this.connectionPromise = Promise.race([
// If aborted by a refresh or other, cancel and don't do anything
new Promise((resolve) => {
Expand Down
21 changes: 21 additions & 0 deletions extensions/cli/src/services/MCPService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
decodeSecretLocation,
getTemplateVariables,
} from "@continuedev/config-yaml";
import { type AssistantConfig } from "@continuedev/sdk";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
Expand Down Expand Up @@ -311,7 +315,24 @@ export class MCPService
this.connections.set(serverName, connection);
this.updateState();

const vars = getTemplateVariables(JSON.stringify(serverConfig));
const secretVars = vars.filter((v) => v.startsWith("secrets."));
const unrendered = secretVars.map((v) => {
return decodeSecretLocation(v.replace("secrets.", "")).secretName;
});

try {
if (unrendered.length > 0) {
const message = `${serverConfig.name} MCP Server has unresolved secrets: ${unrendered.join(", ")}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems duplicate but the message is different enough for CLI since process.env is supported that I decided to leave them separate.

For personal use you can set the secret in the hub at https://hub.continue.dev/settings/secrets or pass it to the CLI environment.
Org-level secrets can only be used for MCP by Background Agents (https://docs.continue.dev/hub/agents/overview) when \"Include in Env\" is enabled for the secret.`;
if (this.isHeadless) {
throw new Error(message);
} else {
connection.warnings.push(message);
}
}

const client = await this.getConnectedClient(serverConfig, connection);

connection.client = client;
Expand Down
Loading