This commit is contained in:
Evan 2025-01-22 09:54:00 +08:00
parent ab8f5d6f36
commit 991175d72e
12 changed files with 482 additions and 48 deletions

View File

@ -0,0 +1,94 @@
# MCP Mode Implementation Changes
## Overview
Implemented a tri-state MCP mode setting to replace the existing boolean toggle, allowing users to:
1. Fully enable MCP (including server use and build instructions)
2. Enable server use only (excluding build instructions to save tokens)
3. Disable MCP completely
## Changes Made
### 1. Type Definition
Added McpMode type in `src/shared/mcp.ts`:
```typescript
export type McpMode = "enabled" | "server-use-only" | "disabled"
```
### 2. VSCode Setting
Updated setting definition in `package.json`:
```json
"cline.mcp.enabled": {
"type": "string",
"enum": ["enabled", "server-use-only", "disabled"],
"enumDescriptions": [
"Full MCP functionality including server use and build instructions",
"Enable MCP server use but exclude build instructions from AI prompts to save tokens",
"Disable all MCP functionality"
],
"default": "enabled",
"description": "Control MCP server functionality and its inclusion in AI prompts"
}
```
### 3. McpHub Changes
Modified `src/services/mcp/McpHub.ts`:
- Removed `isMcpEnabled()` method
- Added `getMode(): McpMode` method that returns the current mode from VSCode settings
### 4. Message Types
Updated message types to support the new mode:
In `src/shared/WebviewMessage.ts` and `src/shared/ExtensionMessage.ts`:
- Added `mode?: McpMode` property with comment indicating its use with specific message types
### 5. MCP View Changes
Updated `webview-ui/src/components/mcp/McpView.tsx`:
- Replaced checkbox with dropdown for mode selection
- Updated state management to use McpMode type
- Added mode-specific descriptions:
- Enabled: "Full MCP functionality including server use and build instructions"
- Server Use Only: "MCP server use is enabled, but build instructions are excluded from AI prompts to save tokens"
- Disabled: Warning about MCP being disabled and token implications
- Updated visibility conditions based on mode
### 6. System Prompt Generation
Added comment in `src/core/prompts/system.ts.checks` for implementing mode-specific content:
```typescript
// Mode checks for MCP content:
// - mcpHub.getMode() === "disabled" -> exclude all MCP content
// - mcpHub.getMode() === "server-use-only" -> include server tools/resources but exclude build instructions
// - mcpHub.getMode() === "enabled" -> include all MCP content (tools, resources, and build instructions)
```
The server building content to be conditionally included (only in "enabled" mode) spans the following sections in system.ts:
- Lines 1012-1015: Main section about creating MCP servers
- Lines 1017-1021: OAuth and authentication handling
- Lines 1025-1392: Example weather server implementation
- Lines 1394-1399: Guidelines for modifying existing servers
- Lines 1401-1405: Usage notes about when to create vs use existing tools
## Next Steps
1. Implement the system prompt changes using the mode checks provided in system.ts.checks
2. Test the implementation with all three modes to ensure proper functionality
## Testing Required
1. Verify mode switching in UI works correctly
2. Confirm proper state persistence
3. Test system prompt generation with each mode
4. Verify server connections behave correctly in each mode
5. Check token usage differences between modes

301
implementing-mcp-mode.md Normal file
View File

@ -0,0 +1,301 @@
# Implementing MCP Mode Setting
## Overview
Currently, the MCP (Model Context Protocol) setting is a binary option (enabled/disabled) that controls whether MCP server functionality is included in AI prompts. We need to extend this to a trinary setting with the following modes:
1. **Enabled**: Full MCP functionality (current enabled state)
2. **Server Use Only**: Enable MCP server use but exclude build instructions from prompts
3. **Disabled**: No MCP functionality (current disabled state)
This change will help users better control token usage while maintaining access to MCP server capabilities when needed.
## Current Implementation
### VSCode Setting
Currently defined in `package.json`:
```json
"cline.mcp.enabled": {
"type": "boolean",
"default": true,
"description": "Include MCP server functionality in AI prompts. When disabled, the AI will not be aware of MCP capabilities. This saves context window tokens."
}
```
### Core Logic
- `system.ts` uses the setting to conditionally include MCP content in prompts
- `ClineProvider.ts` handles setting changes and webview communication
### UI
- `McpView.tsx` displays a checkbox for toggling MCP functionality
- Shows warning message when disabled
## Implementation Steps
### Implementation Order
The changes should be implemented in this order to minimize disruption:
1. Add new type definitions first
2. Update McpHub to handle both old and new setting values
3. Update message types and ClineProvider
4. Update VSCode setting definition
5. Update UI components
6. Update system prompt generation
### Step 1: Update VSCode Setting
In `package.json`, update the setting definition:
```json
"cline.mcp.enabled": {
"type": "string",
"enum": ["enabled", "server-use-only", "disabled"],
"enumDescriptions": [
"Full MCP functionality including server use and build instructions",
"Enable MCP server use but exclude build instructions from AI prompts to save tokens",
"Disable all MCP functionality"
],
"default": "enabled",
"description": "Control MCP server functionality and its inclusion in AI prompts"
}
```
### Step 2: Update Type Definitions
In `src/shared/mcp.ts`, add the MCP mode type:
```typescript
export type McpMode = "enabled" | "server-use-only" | "disabled"
```
### Step 3: Update McpHub
In `src/services/mcp/McpHub.ts`, update the configuration reading:
```typescript
export class McpHub {
public getMode(): McpMode {
const mode = vscode.workspace.getConfiguration("cline.mcp").get<McpMode>("enabled", "enabled")
// Handle legacy boolean values
if (typeof mode === "boolean") {
return mode ? "enabled" : "disabled"
}
return mode
}
}
```
### Step 4: Update Message Types
In `src/shared/ExtensionMessage.ts` and `src/shared/WebviewMessage.ts`, update the message types:
```typescript
// ExtensionMessage.ts
export type ExtensionMessage =
| {
type: "mcpEnabled"
mode: McpMode
}
| {
// ... other message types
}
// WebviewMessage.ts
export type WebviewMessage =
| {
type: "toggleMcp"
mode: McpMode
}
| {
// ... other message types
}
```
### Step 5: Update ClineProvider
In `src/core/webview/ClineProvider.ts`, update the message handling:
```typescript
export class ClineProvider {
// ... existing code ...
private async handleWebviewMessage(message: WebviewMessage) {
switch (message.type) {
case "toggleMcp": {
await vscode.workspace.getConfiguration("cline.mcp").update("enabled", message.mode, true)
break
}
// ... other cases ...
}
}
private async handleConfigurationChange(e: vscode.ConfigurationChangeEvent) {
if (e && e.affectsConfiguration("cline.mcp.enabled")) {
const mode = this.mcpHub?.getMode() ?? "enabled"
await this.postMessageToWebview({
type: "mcpEnabled",
mode,
})
}
}
}
```
### Step 6: Update System Prompt Generation
In `src/core/prompts/system.ts`, modify how MCP content is included:
```typescript
export const SYSTEM_PROMPT = async (
cwd: string,
supportsComputerUse: boolean,
mcpMode: McpMode,
browserSettings: BrowserSettings,
) => {
// Base prompt content...
// Include MCP content for both 'enabled' and 'server-use-only' modes
if (mcpMode !== "disabled") {
let mcpContent = `
====
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.
# Connected MCP Servers
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.
`
// Add server listings...
mcpContent += getServerListings()
// Only include build instructions in full mode
if (mcpMode === "enabled") {
mcpContent += `
## Creating an MCP Server
[... build instructions content ...]`
}
return basePrompt + mcpContent
}
return basePrompt
}
```
### Step 5: Update UI
In `webview-ui/src/components/mcp/McpView.tsx`, replace the checkbox with a select:
```typescript
const McpModeSelect: React.FC<{
value: McpMode;
onChange: (value: McpMode) => void;
}> = ({ value, onChange }) => {
return (
<VSCodeDropdown
value={value}
onChange={(e) => onChange((e.target as HTMLSelectElement).value as McpMode)}
>
<option value="enabled">Fully Enabled</option>
<option value="server-use-only">Server Use Only</option>
<option value="disabled">Disabled</option>
</select>
);
};
// Update the main component
const McpView = ({ onDone }: McpViewProps) => {
const [mcpMode, setMcpMode] = useState<McpMode>("enabled");
useEffect(() => {
vscode.postMessage({ type: "getMcpEnabled" });
}, []);
useEffect(() => {
const handler = (event: MessageEvent) => {
const message = event.data;
if (message.type === "mcpEnabled") {
setMcpMode(message.mode);
}
};
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
const handleModeChange = (newMode: McpMode) => {
vscode.postMessage({
type: "toggleMcp",
mode: newMode,
});
setMcpMode(newMode);
};
return (
// ... existing wrapper divs ...
<div>
<McpModeSelect value={mcpMode} onChange={handleModeChange} />
{mcpMode === "server-use-only" && (
<div style={{
marginTop: "4px",
marginLeft: "24px",
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
}}>
MCP server use is enabled, but build instructions are excluded from AI prompts to save tokens.
</div>
)}
{mcpMode === "disabled" && (
<div style={{
padding: "8px 12px",
marginTop: "8px",
background: "var(--vscode-textBlockQuote-background)",
border: "1px solid var(--vscode-textBlockQuote-border)",
borderRadius: "4px",
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
lineHeight: "1.4",
}}>
MCP is currently disabled. Enable MCP to use MCP servers and tools. Enabling MCP will use additional tokens.
</div>
)}
</div>
);
};
```
## Testing Plan
1. Functionality Testing
- Test each mode:
- Enabled: Full MCP functionality
- Server Use Only: Verify servers work but build instructions are excluded
- Disabled: No MCP functionality
2. UI Testing
- Verify select component displays correctly
- Check mode-specific messages
- Test mode switching
3. System Prompt Testing
- Verify correct sections are included/excluded based on mode
- Check server listings in each mode
- Validate build instructions presence/absence
## Implementation Notes
- The system prompt directly checks the mode value to determine what content to include
- The UI provides clear feedback about the implications of each mode
- Error handling remains consistent with the existing implementation

View File

@ -0,0 +1,16 @@
# MCP Server Building Sections in system.ts
1. Main section about creating MCP servers: Lines 1012-1015
- Introduces the concept of creating MCP servers for new tools
2. OAuth and authentication handling: Lines 1017-1021
- Details about non-interactive environment and handling credentials
3. Example weather server implementation: Lines 1025-1392
- Complete example showing server creation, implementation, and configuration
4. Editing existing servers: Lines 1394-1399
- Guidelines for modifying existing MCP servers
5. Usage note: Lines 1401-1405
- Context about when to create vs use existing tools

View File

@ -50,9 +50,15 @@
"title": "Cline",
"properties": {
"cline.mcp.enabled": {
"type": "boolean",
"default": true,
"description": "Include MCP server functionality in AI prompts. When disabled, the AI will not be aware of MCP capabilities. This saves context window tokens."
"type": "string",
"enum": ["enabled", "server-use-only", "disabled"],
"enumDescriptions": [
"Full MCP functionality including server use and build instructions",
"Enable MCP server use but exclude build instructions from AI prompts to save tokens",
"Disable all MCP functionality"
],
"default": "enabled",
"description": "Control MCP server functionality and its inclusion in AI prompts"
}
}
},

View File

@ -178,7 +178,7 @@ Usage:
}
${
mcpHub.isMcpEnabled()
mcpHub.getMode() !== "disabled"
? `
## use_mcp_tool
Description: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.
@ -301,7 +301,7 @@ return (
</diff>
</replace_in_file>
${
mcpHub.isMcpEnabled()
mcpHub.getMode() !== "disabled"
? `
## Example 4: Requesting to use an MCP tool
@ -348,7 +348,7 @@ It is crucial to proceed step-by-step, waiting for the user's message after each
By waiting for and carefully considering the user's response after each tool use, you can react accordingly and make informed decisions about how to proceed with the task. This iterative process helps ensure the overall success and accuracy of your work.
${
mcpHub.isMcpEnabled()
mcpHub.getMode() !== "disabled"
? `
====
@ -396,8 +396,13 @@ ${
})
.join("\n\n")}`
: "(No MCP servers currently connected)"
}`
: ""
}
${
mcpHub.getMode() === "enabled"
? `
## Creating an MCP Server
The user may ask you something along the lines of "add a tool" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with \`use_mcp_tool\` and \`access_mcp_resource\`.
@ -738,6 +743,7 @@ npm run build
7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
## 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: ${
@ -757,6 +763,7 @@ Remember: The MCP documentation and example provided above are to help you under
`
: ""
}
====
EDITING FILES
@ -849,7 +856,7 @@ CAPABILITIES
: ""
}
${
mcpHub.isMcpEnabled()
mcpHub.getMode() !== "disabled"
? `
- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.
`
@ -891,7 +898,7 @@ RULES
: ""
}
${
mcpHub.isMcpEnabled()
mcpHub.getMode() !== "disabled"
? `
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
`

View File

@ -0,0 +1,4 @@
// Mode checks for MCP content:
// - mcpHub.getMode() === "disabled" -> exclude all MCP content
// - mcpHub.getMode() === "server-use-only" -> include server tools/resources but exclude build instructions
// - mcpHub.getMode() === "enabled" -> include all MCP content (tools, resources, and build instructions)

View File

@ -205,11 +205,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
})
}
if (e && e.affectsConfiguration("cline.mcp.enabled")) {
// Send updated MCP enabled state
const enabled = this.mcpHub?.isMcpEnabled() ?? true
// Send updated MCP mode
const mode = this.mcpHub?.getMode() ?? "enabled"
await this.postMessageToWebview({
type: "mcpEnabled",
enabled,
mode,
})
}
},
@ -581,15 +581,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
break
}
case "getMcpEnabled": {
const enabled = this.mcpHub?.isMcpEnabled() ?? true
const mode = this.mcpHub?.getMode() ?? "enabled"
await this.postMessageToWebview({
type: "mcpEnabled",
enabled,
mode,
})
break
}
case "toggleMcp": {
await vscode.workspace.getConfiguration("cline.mcp").update("enabled", message.enabled, true)
await vscode.workspace.getConfiguration("cline.mcp").update("enabled", message.mode, true)
break
}
// Add more switch case statements here as more webview message commands

View File

@ -15,7 +15,7 @@ import * as path from "path"
import * as vscode from "vscode"
import { z } from "zod"
import { ClineProvider, GlobalFileNames } from "../../core/webview/ClineProvider"
import { McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse } from "../../shared/mcp"
import { McpMode, McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse } from "../../shared/mcp"
import { fileExistsAtPath } from "../../utils/fs"
import { arePathsEqual } from "../../utils/path"
@ -54,8 +54,8 @@ export class McpHub {
return this.connections.map((conn) => conn.server)
}
isMcpEnabled(): boolean {
return vscode.workspace.getConfiguration("cline.mcp").get("enabled") ?? true
getMode(): McpMode {
return vscode.workspace.getConfiguration("cline.mcp").get<McpMode>("enabled", "enabled")
}
async getMcpServersPath(): Promise<string> {

View File

@ -4,7 +4,7 @@ import { ApiConfiguration, ModelInfo } from "./api"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
import { BrowserSettings } from "./BrowserSettings"
import { HistoryItem } from "./HistoryItem"
import { McpServer } from "./mcp"
import { McpMode, McpServer } from "./mcp"
// webview will hold state
export interface ExtensionMessage {
@ -35,7 +35,7 @@ export interface ExtensionMessage {
partialMessage?: ClineMessage
openRouterModels?: Record<string, ModelInfo>
mcpServers?: McpServer[]
enabled?: boolean // For mcpEnabled message
mode?: McpMode // For mcpEnabled message
}
export interface ExtensionState {

View File

@ -1,6 +1,7 @@
import { ApiConfiguration } from "./api"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
import { BrowserSettings } from "./BrowserSettings"
import { McpMode } from "./mcp"
export interface WebviewMessage {
type:
@ -34,7 +35,6 @@ export interface WebviewMessage {
| "openExtensionSettings"
| "getMcpEnabled"
| "toggleMcp"
// | "relaunchChromeDebugMode"
text?: string
askResponse?: ClineAskResponse
apiConfiguration?: ApiConfiguration
@ -43,7 +43,7 @@ export interface WebviewMessage {
number?: number
autoApprovalSettings?: AutoApprovalSettings
browserSettings?: BrowserSettings
enabled?: boolean // For toggleMcp message
mode?: McpMode // Only used with toggleMcp type
}
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"

View File

@ -1,3 +1,5 @@
export type McpMode = "enabled" | "server-use-only" | "disabled"
export type McpServer = {
name: string
config: string

View File

@ -1,15 +1,16 @@
import {
VSCodeButton,
VSCodeDropdown,
VSCodeLink,
VSCodeOption,
VSCodePanels,
VSCodePanelTab,
VSCodePanelView,
VSCodeCheckbox,
} from "@vscode/webview-ui-toolkit/react"
import { useEffect, useState } from "react"
import { vscode } from "../../utils/vscode"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { McpServer } from "../../../../src/shared/mcp"
import { McpMode, McpServer } from "../../../../src/shared/mcp"
import McpToolRow from "./McpToolRow"
import McpResourceRow from "./McpResourceRow"
@ -19,7 +20,7 @@ type McpViewProps = {
const McpView = ({ onDone }: McpViewProps) => {
const { mcpServers: servers } = useExtensionState()
const [isMcpEnabled, setIsMcpEnabled] = useState(true)
const [mcpMode, setMcpMode] = useState<McpMode>("enabled")
useEffect(() => {
// Get initial MCP enabled state
@ -30,19 +31,21 @@ const McpView = ({ onDone }: McpViewProps) => {
const handler = (event: MessageEvent) => {
const message = event.data
if (message.type === "mcpEnabled") {
setIsMcpEnabled(message.enabled)
setMcpMode(message.mode)
}
}
window.addEventListener("message", handler)
return () => window.removeEventListener("message", handler)
}, [])
const toggleMcp = () => {
const handleModeChange = (event: Event | React.FormEvent<HTMLElement>) => {
const select = event.target as HTMLSelectElement
const newMode = select.value as McpMode
vscode.postMessage({
type: "toggleMcp",
enabled: !isMcpEnabled,
mode: newMode,
})
setIsMcpEnabled(!isMcpEnabled)
setMcpMode(newMode)
}
// const [servers, setServers] = useState<McpServer[]>([
// // Add some mock servers for testing
@ -150,7 +153,7 @@ const McpView = ({ onDone }: McpViewProps) => {
</VSCodeLink>
</div>
{/* MCP Toggle Section */}
{/* MCP Mode Section */}
<div
style={{
marginBottom: "16px",
@ -158,31 +161,32 @@ const McpView = ({ onDone }: McpViewProps) => {
borderBottom: "1px solid var(--vscode-textSeparator-foreground)",
}}>
<div>
<VSCodeCheckbox
checked={isMcpEnabled}
onChange={toggleMcp}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
padding: "4px 0",
cursor: "pointer",
fontSize: "13px",
}}>
Enable MCP
</VSCodeCheckbox>
{isMcpEnabled && (
<VSCodeDropdown value={mcpMode} onChange={handleModeChange}>
<VSCodeOption value="enabled">Enabled</VSCodeOption>
<VSCodeOption value="server-use-only">Server use only</VSCodeOption>
<VSCodeOption value="disabled">Disabled</VSCodeOption>
</VSCodeDropdown>
{mcpMode === "enabled" && (
<div
style={{
marginTop: "4px",
marginLeft: "24px",
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
}}>
Disabling MCP will save on tokens passed in the context.
Full MCP functionality including server use and build instructions.
</div>
)}
{!isMcpEnabled && (
{mcpMode === "server-use-only" && (
<div
style={{
marginTop: "4px",
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
}}>
MCP server use is enabled, but build instructions are excluded from AI prompts to save tokens.
</div>
)}
{mcpMode === "disabled" && (
<div
style={{
padding: "8px 12px",
@ -201,7 +205,7 @@ const McpView = ({ onDone }: McpViewProps) => {
</div>
</div>
{servers.length > 0 && isMcpEnabled && (
{servers.length > 0 && mcpMode !== "disabled" && (
<div
style={{
display: "flex",
@ -215,7 +219,7 @@ const McpView = ({ onDone }: McpViewProps) => {
)}
{/* Server Configuration Button */}
{isMcpEnabled && (
{mcpMode !== "disabled" && (
<div style={{ marginTop: "10px", width: "100%" }}>
<VSCodeButton
appearance="secondary"