mirror of
https://github.com/cline/cline.git
synced 2026-09-04 20:02:30 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85a7b7dbaf | |||
| 852f307268 | |||
| 4e3fe004f4 | |||
| 2b63eed85e | |||
| 2ffdc50ea1 | |||
| 74808431e5 | |||
| 7a523fbaf6 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Add DeepSeek 3.2 to native tool calling allow list
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Prevent simultaneuos refreshes when restoring auth info
|
||||
@@ -1,9 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [3.40.1]
|
||||
|
||||
- Fix cost calculation display for Anthropic API requests
|
||||
|
||||
## [3.40.0]
|
||||
|
||||
- Fix highlighted text flashing when task header is collapsed
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.40.1",
|
||||
"version": "3.40.0",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.84.0"
|
||||
|
||||
@@ -197,4 +197,371 @@ describe("ContextManager", () => {
|
||||
expect((content[0] as Anthropic.Messages.TextBlockParam).text).to.equal("Additional user text")
|
||||
})
|
||||
})
|
||||
|
||||
describe("applyFileReadContextHistoryUpdates", () => {
|
||||
let contextManager: ContextManager
|
||||
|
||||
beforeEach(() => {
|
||||
contextManager = new ContextManager()
|
||||
})
|
||||
|
||||
it("should return early when fileReadIndices is empty", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = []
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.false
|
||||
expect(updatedIndices.size).to.equal(0)
|
||||
})
|
||||
|
||||
it("should not update when file has only one occurrence", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
fileReadIndices.set("test.ts", [[3, 2, "", "replacement text", 0]])
|
||||
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = []
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.false
|
||||
expect(updatedIndices.size).to.equal(0)
|
||||
})
|
||||
|
||||
it("should update all but the last occurrence of duplicate file reads", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
// messageIndex, messageType (READ_FILE_TOOL=2), searchText, replaceText, innerIndex
|
||||
fileReadIndices.set("test.ts", [
|
||||
[3, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate file read...", 0],
|
||||
[5, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate file read...", 0],
|
||||
[7, 2, "", "[read_file for 'test.ts'] Result:\nKeep this one", 0],
|
||||
])
|
||||
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = []
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.true
|
||||
expect(updatedIndices.size).to.equal(2)
|
||||
expect(updatedIndices.has(3)).to.be.true
|
||||
expect(updatedIndices.has(5)).to.be.true
|
||||
expect(updatedIndices.has(7)).to.be.false // Last occurrence should not be updated
|
||||
})
|
||||
|
||||
it("should handle FILE_MENTION type correctly with multiple files in same text", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
// FILE_MENTION = 4
|
||||
fileReadIndices.set("file1.ts", [
|
||||
[
|
||||
3,
|
||||
4,
|
||||
'<file_content path="file1.ts">content1</file_content>',
|
||||
'<file_content path="file1.ts">Duplicate file read...</file_content>',
|
||||
0,
|
||||
],
|
||||
[
|
||||
5,
|
||||
4,
|
||||
'<file_content path="file1.ts">content2</file_content>',
|
||||
'<file_content path="file1.ts">Keep this</file_content>',
|
||||
0,
|
||||
],
|
||||
])
|
||||
fileReadIndices.set("file2.ts", [
|
||||
[
|
||||
3,
|
||||
4,
|
||||
'<file_content path="file2.ts">content3</file_content>',
|
||||
'<file_content path="file2.ts">Duplicate file read...</file_content>',
|
||||
0,
|
||||
],
|
||||
[
|
||||
6,
|
||||
4,
|
||||
'<file_content path="file2.ts">content4</file_content>',
|
||||
'<file_content path="file2.ts">Keep this</file_content>',
|
||||
0,
|
||||
],
|
||||
])
|
||||
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
messageFilePaths.set(3, ["file1.ts", "file2.ts"])
|
||||
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = [
|
||||
{ role: "user", content: "Initial" },
|
||||
{ role: "assistant", content: "Response" },
|
||||
{ role: "user", content: "Message" },
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: '<file_content path="file1.ts">content1</file_content>\n<file_content path="file2.ts">content3</file_content>',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.true
|
||||
expect(updatedIndices.size).to.equal(1)
|
||||
expect(updatedIndices.has(3)).to.be.true
|
||||
})
|
||||
|
||||
it("should handle ALTER_FILE_TOOL type correctly", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
// ALTER_FILE_TOOL = 3
|
||||
fileReadIndices.set("test.ts", [
|
||||
[3, 3, "", "replacement text 1", 0],
|
||||
[5, 3, "", "replacement text 2", 0],
|
||||
])
|
||||
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = []
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.true
|
||||
expect(updatedIndices.size).to.equal(1)
|
||||
expect(updatedIndices.has(3)).to.be.true
|
||||
expect(updatedIndices.has(5)).to.be.false
|
||||
})
|
||||
|
||||
it("should handle native tool calling format (tool_result blocks)", () => {
|
||||
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
|
||||
fileReadIndices.set("test.ts", [
|
||||
[3, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate...", 0],
|
||||
[5, 2, "", "[read_file for 'test.ts'] Result:\nKeep this", 0],
|
||||
])
|
||||
|
||||
const messageFilePaths = new Map<number, string[]>()
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = [
|
||||
{ role: "user", content: "Initial" },
|
||||
{ role: "assistant", content: "Response" },
|
||||
{ role: "user", content: "Message" },
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool_123",
|
||||
content: [{ type: "text", text: "[read_file for 'test.ts'] Result:\noriginal content" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
const timestamp = Date.now()
|
||||
|
||||
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
|
||||
fileReadIndices,
|
||||
messageFilePaths,
|
||||
apiMessages,
|
||||
timestamp,
|
||||
)
|
||||
|
||||
expect(didUpdate).to.be.true
|
||||
expect(updatedIndices.size).to.equal(1)
|
||||
expect(updatedIndices.has(3)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
describe("helper methods for applyFileReadContextHistoryUpdates", () => {
|
||||
let contextManager: ContextManager
|
||||
|
||||
beforeEach(() => {
|
||||
contextManager = new ContextManager()
|
||||
})
|
||||
|
||||
it("getBaseTextForFileMention should get text from existing updates", () => {
|
||||
const messageIndex = 3
|
||||
const innerIndex = 0
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = [
|
||||
{ role: "user", content: "Initial" },
|
||||
{ role: "assistant", content: "Response" },
|
||||
{ role: "user", content: "Message" },
|
||||
{ role: "user", content: [{ type: "text", text: "original text" }] },
|
||||
]
|
||||
|
||||
// Manually set up context history updates
|
||||
const timestamp = Date.now()
|
||||
const innerMap = new Map<number, any[]>()
|
||||
innerMap.set(innerIndex, [[timestamp, "text", ["updated text"], []]])
|
||||
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [4, innerMap])
|
||||
|
||||
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
|
||||
|
||||
expect(result).to.equal("updated text")
|
||||
})
|
||||
|
||||
it("getBaseTextForFileMention should fallback to original message content", () => {
|
||||
const messageIndex = 3
|
||||
const innerIndex = 0
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = [
|
||||
{ role: "user", content: "Initial" },
|
||||
{ role: "assistant", content: "Response" },
|
||||
{ role: "user", content: "Message" },
|
||||
{ role: "user", content: [{ type: "text", text: "original text" }] },
|
||||
]
|
||||
|
||||
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
|
||||
|
||||
expect(result).to.equal("original text")
|
||||
})
|
||||
|
||||
it("getBaseTextForFileMention should handle tool_result blocks", () => {
|
||||
const messageIndex = 3
|
||||
const innerIndex = 0
|
||||
const apiMessages: Anthropic.Messages.MessageParam[] = [
|
||||
{ role: "user", content: "Initial" },
|
||||
{ role: "assistant", content: "Response" },
|
||||
{ role: "user", content: "Message" },
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool_123",
|
||||
content: [{ type: "text", text: "tool result text" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
|
||||
|
||||
expect(result).to.equal("tool result text")
|
||||
})
|
||||
|
||||
it("getPreviouslyReplacedFiles should return empty array when no updates exist", () => {
|
||||
const messageIndex = 3
|
||||
const innerIndex = 0
|
||||
|
||||
const result = (contextManager as any).getPreviouslyReplacedFiles(messageIndex, innerIndex)
|
||||
|
||||
expect(result).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("getPreviouslyReplacedFiles should return previously replaced files", () => {
|
||||
const messageIndex = 3
|
||||
const innerIndex = 0
|
||||
const timestamp = Date.now()
|
||||
|
||||
// Manually set up context history updates with metadata
|
||||
const innerMap = new Map<number, any[]>()
|
||||
innerMap.set(innerIndex, [
|
||||
[
|
||||
timestamp,
|
||||
"text",
|
||||
["updated text"],
|
||||
[
|
||||
["file1.ts", "file2.ts"],
|
||||
["file1.ts", "file2.ts", "file3.ts"],
|
||||
],
|
||||
],
|
||||
])
|
||||
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [4, innerMap])
|
||||
|
||||
const result = (contextManager as any).getPreviouslyReplacedFiles(messageIndex, innerIndex)
|
||||
|
||||
expect(result).to.deep.equal(["file1.ts", "file2.ts"])
|
||||
})
|
||||
|
||||
it("addContextUpdate should create new entry when none exists", () => {
|
||||
const messageIndex = 3
|
||||
const messageType = 2 // READ_FILE_TOOL
|
||||
const innerIndex = 0
|
||||
const timestamp = Date.now()
|
||||
const messageString = "replacement text"
|
||||
|
||||
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp, messageString)
|
||||
|
||||
const contextHistory = (contextManager as any).contextHistoryUpdates
|
||||
expect(contextHistory.has(messageIndex)).to.be.true
|
||||
|
||||
const [storedType, innerMap] = contextHistory.get(messageIndex)
|
||||
expect(storedType).to.equal(messageType)
|
||||
expect(innerMap.has(innerIndex)).to.be.true
|
||||
|
||||
const updates = innerMap.get(innerIndex)
|
||||
expect(updates).to.have.lengthOf(1)
|
||||
expect(updates[0]).to.deep.equal([timestamp, "text", [messageString], []])
|
||||
})
|
||||
|
||||
it("addContextUpdate should append to existing updates", () => {
|
||||
const messageIndex = 3
|
||||
const messageType = 2
|
||||
const innerIndex = 0
|
||||
const timestamp1 = Date.now()
|
||||
const timestamp2 = timestamp1 + 1000
|
||||
|
||||
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp1, "first update")
|
||||
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp2, "second update")
|
||||
|
||||
const contextHistory = (contextManager as any).contextHistoryUpdates
|
||||
const [, innerMap] = contextHistory.get(messageIndex)
|
||||
const updates = innerMap.get(innerIndex)
|
||||
|
||||
expect(updates).to.have.lengthOf(2)
|
||||
expect(updates[1]).to.deep.equal([timestamp2, "text", ["second update"], []])
|
||||
})
|
||||
|
||||
it("getOrCreateInnerMap should return existing map", () => {
|
||||
const messageIndex = 3
|
||||
const messageType = 2
|
||||
const innerMap = new Map<number, any[]>()
|
||||
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [messageType, innerMap])
|
||||
|
||||
const result = (contextManager as any).getOrCreateInnerMap(messageIndex, messageType)
|
||||
|
||||
expect(result).to.equal(innerMap)
|
||||
})
|
||||
|
||||
it("getOrCreateInnerMap should create new map when none exists", () => {
|
||||
const messageIndex = 3
|
||||
const messageType = 2
|
||||
|
||||
const result = (contextManager as any).getOrCreateInnerMap(messageIndex, messageType)
|
||||
|
||||
expect(result).to.be.instanceOf(Map)
|
||||
const contextHistory = (contextManager as any).contextHistoryUpdates
|
||||
expect(contextHistory.has(messageIndex)).to.be.true
|
||||
|
||||
const [storedType, storedMap] = contextHistory.get(messageIndex)
|
||||
expect(storedType).to.equal(messageType)
|
||||
expect(storedMap).to.equal(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -74,11 +74,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
|
||||
controller.stateManager.setGlobalState("enableCheckpointsSetting", request.enableCheckpointsSetting)
|
||||
}
|
||||
|
||||
// Update MCP marketplace setting
|
||||
if (request.mcpMarketplaceEnabled !== undefined) {
|
||||
controller.stateManager.setGlobalState("mcpMarketplaceEnabled", request.mcpMarketplaceEnabled)
|
||||
}
|
||||
|
||||
// Update MCP responses collapsed setting
|
||||
if (request.mcpResponsesCollapsed !== undefined) {
|
||||
controller.stateManager.setGlobalState("mcpResponsesCollapsed", request.mcpResponsesCollapsed)
|
||||
|
||||
@@ -125,6 +125,18 @@ export function transformRemoteConfigToStateShape(remoteConfig: RemoteConfig): P
|
||||
providers.push("cline")
|
||||
}
|
||||
|
||||
// Map LiteLLM provider settings
|
||||
const liteLlmSettings = remoteConfig.providerSettings?.LiteLLM
|
||||
if (liteLlmSettings) {
|
||||
transformed.planModeApiProvider = "litellm"
|
||||
transformed.actModeApiProvider = "litellm"
|
||||
providers.push("litellm")
|
||||
|
||||
if (liteLlmSettings.baseUrl !== undefined) {
|
||||
transformed.liteLlmBaseUrl = liteLlmSettings.baseUrl
|
||||
}
|
||||
}
|
||||
|
||||
// This line needs to stay here, it is order dependent on the above code checking the configured providers
|
||||
if (providers.length > 0) {
|
||||
transformed.remoteConfiguredProviders = providers
|
||||
|
||||
@@ -2716,13 +2716,7 @@ export class Task {
|
||||
await this.postStateToWebview()
|
||||
|
||||
try {
|
||||
const taskMetrics: {
|
||||
cacheWriteTokens: number
|
||||
cacheReadTokens: number
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
totalCost: number | undefined
|
||||
} = { cacheWriteTokens: 0, cacheReadTokens: 0, inputTokens: 0, outputTokens: 0, totalCost: undefined }
|
||||
const taskMetrics = { cacheWriteTokens: 0, cacheReadTokens: 0, inputTokens: 0, outputTokens: 0, totalCost: 0 }
|
||||
|
||||
const abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => {
|
||||
if (this.diffViewProvider.isEditing) {
|
||||
|
||||
@@ -351,6 +351,12 @@ export class AuthService {
|
||||
throw new Error("Auth provider is not set")
|
||||
}
|
||||
|
||||
// If a refresh is already in progress, wait for it to complete
|
||||
if (this._refreshPromise) {
|
||||
Logger.info("Token refresh already in progress, waiting for completion")
|
||||
await this._refreshPromise
|
||||
}
|
||||
|
||||
return this._provider.retrieveClineAuthInfo(this._controller)
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,11 @@ export function isGemini3ModelFamily(id: string): boolean {
|
||||
return modelId.includes("gemini3") || modelId.includes("gemini-3")
|
||||
}
|
||||
|
||||
function isDeepSeek32ModelFamily(id: string): boolean {
|
||||
const modelId = normalize(id)
|
||||
return modelId.includes("deepseek") && modelId.includes("3.2")
|
||||
}
|
||||
|
||||
export function isNextGenModelFamily(id: string): boolean {
|
||||
const modelId = normalize(id)
|
||||
return (
|
||||
@@ -130,7 +135,8 @@ export function isNextGenModelFamily(id: string): boolean {
|
||||
isGPT5ModelFamily(modelId) ||
|
||||
isMinimaxModelFamily(modelId) ||
|
||||
isGemini3ModelFamily(modelId) ||
|
||||
isNextGenOpenSourceModelFamily(modelId)
|
||||
isNextGenOpenSourceModelFamily(modelId) ||
|
||||
isDeepSeek32ModelFamily(modelId)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,25 +18,27 @@ type McpViewProps = {
|
||||
}
|
||||
|
||||
const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
|
||||
const { mcpMarketplaceEnabled, setMcpServers, environment } = useExtensionState()
|
||||
const [activeTab, setActiveTab] = useState<McpViewTab>(initialTab || (mcpMarketplaceEnabled ? "marketplace" : "configure"))
|
||||
const { remoteConfigSettings, setMcpServers, environment } = useExtensionState()
|
||||
// Show marketplace by default unless remote config explicitly disables it
|
||||
const showMarketplace = remoteConfigSettings?.mcpMarketplaceEnabled !== false
|
||||
const [activeTab, setActiveTab] = useState<McpViewTab>(initialTab || (showMarketplace ? "marketplace" : "configure"))
|
||||
|
||||
const handleTabChange = (tab: McpViewTab) => {
|
||||
setActiveTab(tab)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!mcpMarketplaceEnabled && activeTab === "marketplace") {
|
||||
// If marketplace is disabled and we're on marketplace tab, switch to configure
|
||||
if (!showMarketplace && activeTab === "marketplace") {
|
||||
// If marketplace is disabled by remote config and we're on marketplace tab, switch to configure
|
||||
setActiveTab("configure")
|
||||
}
|
||||
}, [mcpMarketplaceEnabled, activeTab])
|
||||
}, [showMarketplace, activeTab])
|
||||
|
||||
// Get setter for MCP marketplace catalog from context
|
||||
const { setMcpMarketplaceCatalog } = useExtensionState()
|
||||
|
||||
useEffect(() => {
|
||||
if (mcpMarketplaceEnabled) {
|
||||
if (showMarketplace) {
|
||||
McpServiceClient.refreshMcpMarketplace(EmptyRequest.create({}))
|
||||
.then((response) => {
|
||||
setMcpMarketplaceCatalog(response)
|
||||
@@ -56,7 +58,7 @@ const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
|
||||
console.error("Failed to fetch MCP servers:", error)
|
||||
})
|
||||
}
|
||||
}, [mcpMarketplaceEnabled])
|
||||
}, [showMarketplace])
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -95,7 +97,7 @@ const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
|
||||
padding: "0 20px 0 20px",
|
||||
borderBottom: "1px solid var(--vscode-panel-border)",
|
||||
}}>
|
||||
{mcpMarketplaceEnabled && (
|
||||
{showMarketplace && (
|
||||
<TabButton isActive={activeTab === "marketplace"} onClick={() => handleTabChange("marketplace")}>
|
||||
Marketplace
|
||||
</TabButton>
|
||||
@@ -110,7 +112,7 @@ const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
|
||||
|
||||
{/* Content container */}
|
||||
<div style={{ width: "100%" }}>
|
||||
{mcpMarketplaceEnabled && activeTab === "marketplace" && <McpMarketplaceView />}
|
||||
{showMarketplace && activeTab === "marketplace" && <McpMarketplaceView />}
|
||||
{activeTab === "addRemote" && <AddRemoteServerForm onServerAdded={() => handleTabChange("configure")} />}
|
||||
{activeTab === "configure" && <ConfigureServersView />}
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,9 @@ import McpMarketplaceCard from "./McpMarketplaceCard"
|
||||
import McpSubmitCard from "./McpSubmitCard"
|
||||
|
||||
const McpMarketplaceView = () => {
|
||||
const { mcpServers, mcpMarketplaceCatalog, setMcpMarketplaceCatalog, mcpMarketplaceEnabled, remoteConfigSettings } =
|
||||
useExtensionState()
|
||||
const { mcpServers, mcpMarketplaceCatalog, setMcpMarketplaceCatalog, remoteConfigSettings } = useExtensionState()
|
||||
|
||||
const showMarketplace = remoteConfigSettings?.mcpMarketplaceEnabled !== false
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||
@@ -80,7 +81,7 @@ const McpMarketplaceView = () => {
|
||||
}
|
||||
setError(null)
|
||||
|
||||
if (mcpMarketplaceEnabled) {
|
||||
if (showMarketplace) {
|
||||
McpServiceClient.refreshMcpMarketplace(EmptyRequest.create({}))
|
||||
.then((response) => {
|
||||
setMcpMarketplaceCatalog(response)
|
||||
|
||||
@@ -180,33 +180,6 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
|
||||
may not work well with large workspaces.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="flex items-center gap-2">
|
||||
<VSCodeCheckbox
|
||||
checked={mcpMarketplaceEnabled}
|
||||
disabled={remoteConfigSettings?.mcpMarketplaceEnabled !== undefined}
|
||||
onChange={(e: any) => {
|
||||
const checked = e.target.checked === true
|
||||
updateSetting("mcpMarketplaceEnabled", checked)
|
||||
}}>
|
||||
Enable MCP Marketplace
|
||||
</VSCodeCheckbox>
|
||||
{remoteConfigSettings?.mcpMarketplaceEnabled !== undefined && (
|
||||
<i className="codicon codicon-lock text-description text-sm" />
|
||||
)}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent hidden={remoteConfigSettings?.mcpMarketplaceEnabled === undefined}>
|
||||
This setting is managed by your organization's remote configuration
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<p className="text-xs text-description">
|
||||
Enables the MCP Marketplace tab for discovering and installing MCP servers.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<label
|
||||
className="block text-sm font-medium text-(--vscode-foreground) mb-1"
|
||||
|
||||
Reference in New Issue
Block a user