feat: add setting to enable/disable MCP server creation

- Add enableMcpServerCreation setting to control whether MCP server creation is allowed
- Add UI toggle in settings view for this feature
- Update system prompt to conditionally include MCP server creation documentation
- Add tests for new functionality
This commit is contained in:
sam hoang
2025-02-01 09:34:53 +07:00
parent 8ce5f9a890
commit f906755d90
12 changed files with 1263 additions and 12 deletions

View File

@@ -842,8 +842,14 @@ export class Cline {
}) })
} }
const { browserViewportSize, mode, customModePrompts, preferredLanguage, experiments } = const {
(await this.providerRef.deref()?.getState()) ?? {} browserViewportSize,
mode,
customModePrompts,
preferredLanguage,
experiments,
enableMcpServerCreation,
} = (await this.providerRef.deref()?.getState()) ?? {}
const { customModes } = (await this.providerRef.deref()?.getState()) ?? {} const { customModes } = (await this.providerRef.deref()?.getState()) ?? {}
const systemPrompt = await (async () => { const systemPrompt = await (async () => {
const provider = this.providerRef.deref() const provider = this.providerRef.deref()
@@ -864,6 +870,7 @@ export class Cline {
preferredLanguage, preferredLanguage,
this.diffEnabled, this.diffEnabled,
experiments, experiments,
enableMcpServerCreation,
) )
})() })()

File diff suppressed because it is too large Load Diff

View File

@@ -174,6 +174,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -194,6 +195,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -216,6 +218,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -236,6 +239,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -256,6 +260,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -276,6 +281,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
true, // diffEnabled true, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toContain("apply_diff") expect(prompt).toContain("apply_diff")
@@ -297,6 +303,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
false, // diffEnabled false, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).not.toContain("apply_diff") expect(prompt).not.toContain("apply_diff")
@@ -318,6 +325,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).not.toContain("apply_diff") expect(prompt).not.toContain("apply_diff")
@@ -339,6 +347,7 @@ describe("SYSTEM_PROMPT", () => {
"Spanish", // preferredLanguage "Spanish", // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toContain("Language Preference:") expect(prompt).toContain("Language Preference:")
@@ -371,6 +380,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
// Role definition should be at the top // Role definition should be at the top
@@ -406,6 +416,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
undefined, undefined,
experiments, experiments,
true, // enableMcpServerCreation
) )
// Role definition from promptComponent should be at the top // Role definition from promptComponent should be at the top
@@ -436,6 +447,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
undefined, undefined,
experiments, experiments,
true, // enableMcpServerCreation
) )
// Should use the default mode's role definition // Should use the default mode's role definition
@@ -458,6 +470,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, // experiments - undefined should disable all experimental tools experiments, // experiments - undefined should disable all experimental tools
true, // enableMcpServerCreation
) )
// Verify experimental tools are not included in the prompt // Verify experimental tools are not included in the prompt
@@ -485,6 +498,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
// Verify experimental tools are included in the prompt when enabled // Verify experimental tools are included in the prompt when enabled
@@ -512,6 +526,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
undefined, // diffEnabled undefined, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
// Verify only enabled experimental tools are included // Verify only enabled experimental tools are included
@@ -539,6 +554,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
true, // diffEnabled true, // diffEnabled
experiments, experiments,
true, // enableMcpServerCreation
) )
// Verify base instruction lists all available tools // Verify base instruction lists all available tools
@@ -568,6 +584,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
true, true,
experiments, experiments,
true, // enableMcpServerCreation
) )
// Verify detailed instructions for each tool // Verify detailed instructions for each tool
@@ -623,6 +640,7 @@ describe("addCustomInstructions", () => {
undefined, undefined,
undefined, undefined,
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
@@ -643,11 +661,60 @@ describe("addCustomInstructions", () => {
undefined, undefined,
undefined, undefined,
experiments, experiments,
true, // enableMcpServerCreation
) )
expect(prompt).toMatchSnapshot() expect(prompt).toMatchSnapshot()
}) })
it("should include MCP server creation info when enabled", async () => {
const mockMcpHub = createMockMcpHub()
const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false, // supportsComputerUse
mockMcpHub, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
true, // enableMcpServerCreation
)
expect(prompt).toContain("Creating an MCP Server")
expect(prompt).toMatchSnapshot()
})
it("should exclude MCP server creation info when disabled", async () => {
const mockMcpHub = createMockMcpHub()
const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false, // supportsComputerUse
mockMcpHub, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
false, // enableMcpServerCreation
)
expect(prompt).not.toContain("Creating an MCP Server")
expect(prompt).toMatchSnapshot()
})
it("should prioritize mode-specific rules for code mode", async () => { it("should prioritize mode-specific rules for code mode", async () => {
const instructions = await addCustomInstructions("", "", "/test/path", defaultModeSlug) const instructions = await addCustomInstructions("", "", "/test/path", defaultModeSlug)
expect(instructions).toMatchSnapshot() expect(instructions).toMatchSnapshot()

View File

@@ -1,7 +1,11 @@
import { DiffStrategy } from "../../diff/DiffStrategy" import { DiffStrategy } from "../../diff/DiffStrategy"
import { McpHub } from "../../../services/mcp/McpHub" import { McpHub } from "../../../services/mcp/McpHub"
export async function getMcpServersSection(mcpHub?: McpHub, diffStrategy?: DiffStrategy): Promise<string> { export async function getMcpServersSection(
mcpHub?: McpHub,
diffStrategy?: DiffStrategy,
enableMcpServerCreation?: boolean,
): Promise<string> {
if (!mcpHub) { if (!mcpHub) {
return "" return ""
} }
@@ -43,7 +47,7 @@ export async function getMcpServersSection(mcpHub?: McpHub, diffStrategy?: DiffS
.join("\n\n")}` .join("\n\n")}`
: "(No MCP servers currently connected)" : "(No MCP servers currently connected)"
return `MCP SERVERS const baseSection = `MCP SERVERS
The Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities. The Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities.
@@ -51,7 +55,15 @@ The Model Context Protocol (MCP) enables communication between the system and lo
When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool. When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.
${connectedServers} ${connectedServers}`
if (!enableMcpServerCreation) {
return baseSection
}
return (
baseSection +
`
## Creating an MCP Server ## Creating an MCP Server
@@ -86,7 +98,7 @@ weather-server/
... ...
"type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script) "type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)
"scripts": { "scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"", "build": "tsc && node -e "require('fs').chmodSync('build/index.js', '755')"",
... ...
} }
... ...
@@ -398,11 +410,11 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de
## Editing MCP Servers ## Editing MCP Servers
The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${ The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${
mcpHub mcpHub
.getServers() .getServers()
.map((server) => server.name) .map((server) => server.name)
.join(", ") || "(None running currently)" .join(", ") || "(None running currently)"
}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files. }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files.
However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.
@@ -411,4 +423,5 @@ However some MCP servers may be running from installed packages rather than a lo
The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that..."). The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that...").
Remember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.` Remember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.`
)
} }

View File

@@ -40,6 +40,7 @@ async function generatePrompt(
preferredLanguage?: string, preferredLanguage?: string,
diffEnabled?: boolean, diffEnabled?: boolean,
experiments?: Record<string, boolean>, experiments?: Record<string, boolean>,
enableMcpServerCreation?: boolean,
): Promise<string> { ): Promise<string> {
if (!context) { if (!context) {
throw new Error("Extension context is required for generating system prompt") throw new Error("Extension context is required for generating system prompt")
@@ -49,7 +50,7 @@ async function generatePrompt(
const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined
const [mcpServersSection, modesSection] = await Promise.all([ const [mcpServersSection, modesSection] = await Promise.all([
getMcpServersSection(mcpHub, effectiveDiffStrategy), getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation),
getModesSection(context), getModesSection(context),
]) ])
@@ -105,6 +106,7 @@ export const SYSTEM_PROMPT = async (
preferredLanguage?: string, preferredLanguage?: string,
diffEnabled?: boolean, diffEnabled?: boolean,
experiments?: Record<string, boolean>, experiments?: Record<string, boolean>,
enableMcpServerCreation?: boolean,
): Promise<string> => { ): Promise<string> => {
if (!context) { if (!context) {
throw new Error("Extension context is required for generating system prompt") throw new Error("Extension context is required for generating system prompt")
@@ -139,5 +141,6 @@ export const SYSTEM_PROMPT = async (
preferredLanguage, preferredLanguage,
diffEnabled, diffEnabled,
experiments, experiments,
enableMcpServerCreation,
) )
} }

View File

@@ -102,6 +102,7 @@ type GlobalStateKey =
| "writeDelayMs" | "writeDelayMs"
| "terminalOutputLineLimit" | "terminalOutputLineLimit"
| "mcpEnabled" | "mcpEnabled"
| "enableMcpServerCreation"
| "alwaysApproveResubmit" | "alwaysApproveResubmit"
| "requestDelaySeconds" | "requestDelaySeconds"
| "rateLimitSeconds" | "rateLimitSeconds"
@@ -841,6 +842,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("mcpEnabled", mcpEnabled) await this.updateGlobalState("mcpEnabled", mcpEnabled)
await this.postStateToWebview() await this.postStateToWebview()
break break
case "enableMcpServerCreation":
await this.updateGlobalState("enableMcpServerCreation", message.bool ?? true)
await this.postStateToWebview()
break
case "playSound": case "playSound":
if (message.audioType) { if (message.audioType) {
const soundPath = path.join(this.context.extensionPath, "audio", `${message.audioType}.wav`) const soundPath = path.join(this.context.extensionPath, "audio", `${message.audioType}.wav`)
@@ -1129,6 +1134,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
mcpEnabled, mcpEnabled,
fuzzyMatchThreshold, fuzzyMatchThreshold,
experiments, experiments,
enableMcpServerCreation,
} = await this.getState() } = await this.getState()
// Create diffStrategy based on current model and settings // Create diffStrategy based on current model and settings
@@ -1157,6 +1163,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
preferredLanguage, preferredLanguage,
diffEnabled, diffEnabled,
experiments, experiments,
enableMcpServerCreation,
) )
await this.postMessageToWebview({ await this.postMessageToWebview({
@@ -1994,6 +2001,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
terminalOutputLineLimit, terminalOutputLineLimit,
fuzzyMatchThreshold, fuzzyMatchThreshold,
mcpEnabled, mcpEnabled,
enableMcpServerCreation,
alwaysApproveResubmit, alwaysApproveResubmit,
requestDelaySeconds, requestDelaySeconds,
rateLimitSeconds, rateLimitSeconds,
@@ -2036,6 +2044,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
terminalOutputLineLimit: terminalOutputLineLimit ?? 500, terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0, fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
mcpEnabled: mcpEnabled ?? true, mcpEnabled: mcpEnabled ?? true,
enableMcpServerCreation: enableMcpServerCreation ?? true,
alwaysApproveResubmit: alwaysApproveResubmit ?? false, alwaysApproveResubmit: alwaysApproveResubmit ?? false,
requestDelaySeconds: requestDelaySeconds ?? 10, requestDelaySeconds: requestDelaySeconds ?? 10,
rateLimitSeconds: rateLimitSeconds ?? 0, rateLimitSeconds: rateLimitSeconds ?? 0,
@@ -2160,6 +2169,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
screenshotQuality, screenshotQuality,
terminalOutputLineLimit, terminalOutputLineLimit,
mcpEnabled, mcpEnabled,
enableMcpServerCreation,
alwaysApproveResubmit, alwaysApproveResubmit,
requestDelaySeconds, requestDelaySeconds,
rateLimitSeconds, rateLimitSeconds,
@@ -2233,6 +2243,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("screenshotQuality") as Promise<number | undefined>, this.getGlobalState("screenshotQuality") as Promise<number | undefined>,
this.getGlobalState("terminalOutputLineLimit") as Promise<number | undefined>, this.getGlobalState("terminalOutputLineLimit") as Promise<number | undefined>,
this.getGlobalState("mcpEnabled") as Promise<boolean | undefined>, this.getGlobalState("mcpEnabled") as Promise<boolean | undefined>,
this.getGlobalState("enableMcpServerCreation") as Promise<boolean | undefined>,
this.getGlobalState("alwaysApproveResubmit") as Promise<boolean | undefined>, this.getGlobalState("alwaysApproveResubmit") as Promise<boolean | undefined>,
this.getGlobalState("requestDelaySeconds") as Promise<number | undefined>, this.getGlobalState("requestDelaySeconds") as Promise<number | undefined>,
this.getGlobalState("rateLimitSeconds") as Promise<number | undefined>, this.getGlobalState("rateLimitSeconds") as Promise<number | undefined>,
@@ -2356,6 +2367,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
return langMap[vscodeLang.split("-")[0]] ?? "English" return langMap[vscodeLang.split("-")[0]] ?? "English"
})(), })(),
mcpEnabled: mcpEnabled ?? true, mcpEnabled: mcpEnabled ?? true,
enableMcpServerCreation: enableMcpServerCreation ?? true,
alwaysApproveResubmit: alwaysApproveResubmit ?? false, alwaysApproveResubmit: alwaysApproveResubmit ?? false,
requestDelaySeconds: Math.max(5, requestDelaySeconds ?? 10), requestDelaySeconds: Math.max(5, requestDelaySeconds ?? 10),
rateLimitSeconds: rateLimitSeconds ?? 0, rateLimitSeconds: rateLimitSeconds ?? 0,

View File

@@ -323,6 +323,7 @@ describe("ClineProvider", () => {
browserViewportSize: "900x600", browserViewportSize: "900x600",
fuzzyMatchThreshold: 1.0, fuzzyMatchThreshold: 1.0,
mcpEnabled: true, mcpEnabled: true,
enableMcpServerCreation: false,
requestDelaySeconds: 5, requestDelaySeconds: 5,
rateLimitSeconds: 0, rateLimitSeconds: 0,
mode: defaultModeSlug, mode: defaultModeSlug,
@@ -895,6 +896,7 @@ describe("ClineProvider", () => {
}, },
}, },
mcpEnabled: true, mcpEnabled: true,
enableMcpServerCreation: false,
mode: "code" as const, mode: "code" as const,
experiments: experimentDefault, experiments: experimentDefault,
} as any) } as any)
@@ -927,6 +929,7 @@ describe("ClineProvider", () => {
}, },
}, },
mcpEnabled: false, mcpEnabled: false,
enableMcpServerCreation: false,
mode: "code" as const, mode: "code" as const,
experiments: experimentDefault, experiments: experimentDefault,
} as any) } as any)
@@ -991,6 +994,7 @@ describe("ClineProvider", () => {
}, },
customModePrompts: {}, customModePrompts: {},
mode: "code", mode: "code",
enableMcpServerCreation: true,
mcpEnabled: false, mcpEnabled: false,
browserViewportSize: "900x600", browserViewportSize: "900x600",
experimentalDiffStrategy: true, experimentalDiffStrategy: true,
@@ -1025,6 +1029,7 @@ describe("ClineProvider", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
true, // diffEnabled true, // diffEnabled
experimentDefault, experimentDefault,
true,
) )
// Run the test again to verify it's consistent // Run the test again to verify it's consistent
@@ -1048,6 +1053,7 @@ describe("ClineProvider", () => {
diffEnabled: false, diffEnabled: false,
fuzzyMatchThreshold: 0.8, fuzzyMatchThreshold: 0.8,
experiments: experimentDefault, experiments: experimentDefault,
enableMcpServerCreation: true,
} as any) } as any)
// Mock SYSTEM_PROMPT to verify diffEnabled is passed as false // Mock SYSTEM_PROMPT to verify diffEnabled is passed as false
@@ -1076,6 +1082,7 @@ describe("ClineProvider", () => {
undefined, // preferredLanguage undefined, // preferredLanguage
false, // diffEnabled false, // diffEnabled
experimentDefault, experimentDefault,
true,
) )
}) })
@@ -1090,6 +1097,7 @@ describe("ClineProvider", () => {
architect: { customInstructions: "Architect mode instructions" }, architect: { customInstructions: "Architect mode instructions" },
}, },
mode: "architect", mode: "architect",
enableMcpServerCreation: false,
mcpEnabled: false, mcpEnabled: false,
browserViewportSize: "900x600", browserViewportSize: "900x600",
experiments: experimentDefault, experiments: experimentDefault,

View File

@@ -107,6 +107,7 @@ export interface ExtensionState {
writeDelayMs: number writeDelayMs: number
terminalOutputLineLimit?: number terminalOutputLineLimit?: number
mcpEnabled: boolean mcpEnabled: boolean
enableMcpServerCreation: boolean
mode: Mode mode: Mode
modeApiConfigs?: Record<Mode, string> modeApiConfigs?: Record<Mode, string>
enhancementApiConfigId?: string enhancementApiConfigId?: string

View File

@@ -62,6 +62,7 @@ export interface WebviewMessage {
| "deleteMessage" | "deleteMessage"
| "terminalOutputLineLimit" | "terminalOutputLineLimit"
| "mcpEnabled" | "mcpEnabled"
| "enableMcpServerCreation"
| "searchCommits" | "searchCommits"
| "refreshGlamaModels" | "refreshGlamaModels"
| "alwaysApproveResubmit" | "alwaysApproveResubmit"

View File

@@ -45,6 +45,7 @@ describe("AutoApproveMenu", () => {
filePaths: [], filePaths: [],
experiments: experimentDefault, experiments: experimentDefault,
customModes: [], customModes: [],
enableMcpServerCreation: false,
// Auto-approve specific properties // Auto-approve specific properties
alwaysAllowReadOnly: false, alwaysAllowReadOnly: false,
@@ -91,6 +92,7 @@ describe("AutoApproveMenu", () => {
setExperimentEnabled: jest.fn(), setExperimentEnabled: jest.fn(),
handleInputChange: jest.fn(), handleInputChange: jest.fn(),
setCustomModes: jest.fn(), setCustomModes: jest.fn(),
setEnableMcpServerCreation: jest.fn(),
} }
beforeEach(() => { beforeEach(() => {

View File

@@ -61,6 +61,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
setExperimentEnabled, setExperimentEnabled,
alwaysAllowModeSwitch, alwaysAllowModeSwitch,
setAlwaysAllowModeSwitch, setAlwaysAllowModeSwitch,
enableMcpServerCreation,
setEnableMcpServerCreation,
} = useExtensionState() } = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined) const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined) const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
@@ -108,6 +110,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
}) })
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch }) vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
vscode.postMessage({ type: "enableMcpServerCreation", bool: enableMcpServerCreation })
onDone() onDone()
} }
} }
@@ -357,6 +360,17 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</p> </p>
</div> </div>
<div style={{ marginBottom: 5 }}>
<VSCodeCheckbox
checked={enableMcpServerCreation}
onChange={(e: any) => setEnableMcpServerCreation(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Enable MCP Server Creation</span>
</VSCodeCheckbox>
<p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
This option allow user to enable and disable MCP server creation for saved tokens usage
</p>
</div>
<div style={{ marginBottom: 15 }}> <div style={{ marginBottom: 15 }}>
<VSCodeCheckbox <VSCodeCheckbox
checked={alwaysAllowModeSwitch} checked={alwaysAllowModeSwitch}

View File

@@ -51,6 +51,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setTerminalOutputLineLimit: (value: number) => void setTerminalOutputLineLimit: (value: number) => void
mcpEnabled: boolean mcpEnabled: boolean
setMcpEnabled: (value: boolean) => void setMcpEnabled: (value: boolean) => void
enableMcpServerCreation: boolean
setEnableMcpServerCreation: (value: boolean) => void
alwaysApproveResubmit?: boolean alwaysApproveResubmit?: boolean
setAlwaysApproveResubmit: (value: boolean) => void setAlwaysApproveResubmit: (value: boolean) => void
requestDelaySeconds: number requestDelaySeconds: number
@@ -92,6 +94,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
screenshotQuality: 75, screenshotQuality: 75,
terminalOutputLineLimit: 500, terminalOutputLineLimit: 500,
mcpEnabled: true, mcpEnabled: true,
enableMcpServerCreation: true,
alwaysApproveResubmit: false, alwaysApproveResubmit: false,
requestDelaySeconds: 5, requestDelaySeconds: 5,
rateLimitSeconds: 0, // Minimum time between successive requests (0 = disabled) rateLimitSeconds: 0, // Minimum time between successive requests (0 = disabled)
@@ -272,6 +275,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setTerminalOutputLineLimit: (value) => setTerminalOutputLineLimit: (value) =>
setState((prevState) => ({ ...prevState, terminalOutputLineLimit: value })), setState((prevState) => ({ ...prevState, terminalOutputLineLimit: value })),
setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })), setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })),
setEnableMcpServerCreation: (value) =>
setState((prevState) => ({ ...prevState, enableMcpServerCreation: value })),
setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })), setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })),
setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })), setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })),
setRateLimitSeconds: (value) => setState((prevState) => ({ ...prevState, rateLimitSeconds: value })), setRateLimitSeconds: (value) => setState((prevState) => ({ ...prevState, rateLimitSeconds: value })),