diff --git a/packages/types/src/mode.ts b/packages/types/src/mode.ts index 28c5b45927..4a0900d624 100644 --- a/packages/types/src/mode.ts +++ b/packages/types/src/mode.ts @@ -40,7 +40,17 @@ export type GroupOptions = z.infer * GroupEntry */ -export const groupEntrySchema = z.union([toolGroupsSchema, z.tuple([toolGroupsSchema, groupOptionsSchema])]) +export const groupEntrySchema = z.union([ + toolGroupsSchema, + z.tuple([toolGroupsSchema, groupOptionsSchema]), + // Allow direct mcp configuration object + z.object({ + mcp: z.object({ + included: z.array(z.string()), + description: z.string().optional(), + }), + }), +]) export type GroupEntry = z.infer diff --git a/src/core/prompts/sections/mcp-servers.ts b/src/core/prompts/sections/mcp-servers.ts index 62b3a5f6d6..3b2f15e018 100644 --- a/src/core/prompts/sections/mcp-servers.ts +++ b/src/core/prompts/sections/mcp-servers.ts @@ -54,18 +54,34 @@ export async function getMcpServersSection( if (currentMode) { // Find MCP group configuration const mcpGroup = currentMode.groups.find((group: GroupEntry) => { + // Handle tuple format: ["mcp", { mcp: { included: [...] } }] if (Array.isArray(group) && group.length === 2 && group[0] === "mcp") { return true } + // Handle direct object format: { mcp: { included: [...] } } + if (typeof group === "object" && !Array.isArray(group) && "mcp" in group) { + return true + } return getGroupName(group) === "mcp" }) - // If MCP group configuration is found, get mcpIncludedList from mcp.included - if (mcpGroup && Array.isArray(mcpGroup) && mcpGroup.length === 2) { - const options = mcpGroup[1] as { mcp?: { included?: unknown[] } } - mcpIncludedList = Array.isArray(options.mcp?.included) - ? options.mcp.included.filter((item: unknown): item is string => typeof item === "string") - : undefined + // Extract mcpIncludedList based on the format + if (mcpGroup) { + let mcpOptions: { mcp?: { included?: unknown[] } } | undefined + + if (Array.isArray(mcpGroup) && mcpGroup.length === 2) { + // Tuple format + mcpOptions = mcpGroup[1] as { mcp?: { included?: unknown[] } } + } else if (typeof mcpGroup === "object" && !Array.isArray(mcpGroup) && "mcp" in mcpGroup) { + // Direct object format + mcpOptions = mcpGroup as { mcp?: { included?: unknown[] } } + } + + if (mcpOptions) { + mcpIncludedList = Array.isArray(mcpOptions.mcp?.included) + ? mcpOptions.mcp.included.filter((item: unknown): item is string => typeof item === "string") + : undefined + } } } @@ -77,7 +93,7 @@ export async function getMcpServersSection( connectedServers = `${filteredServers .map((server) => { const tools = server.tools - ?.filter((tool) => tool.enabledForPrompt !== false) + ?.filter((tool) => tool.enabledForPrompt !== false) ?.map((tool) => { const schemaStr = tool.inputSchema ? ` Input Schema: diff --git a/src/shared/modes.ts b/src/shared/modes.ts index f68d25c682..f7fb6a5eca 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -23,13 +23,26 @@ export function getGroupName(group: GroupEntry): ToolGroup { if (typeof group === "string") { return group } - - return group[0] + if (Array.isArray(group)) { + return group[0] + } + // Handle direct MCP object format + if (typeof group === "object" && "mcp" in group) { + return "mcp" as ToolGroup + } + return group as ToolGroup } // Helper to get group options if they exist function getGroupOptions(group: GroupEntry): GroupOptions | undefined { - return Array.isArray(group) ? group[1] : undefined + if (Array.isArray(group)) { + return group[1] + } + // Handle direct MCP object format - return the object itself as options + if (typeof group === "object" && "mcp" in group) { + return group as GroupOptions + } + return undefined } // Helper to check if a file path matches a regex pattern diff --git a/webview-ui/src/components/modes/McpSelector.tsx b/webview-ui/src/components/modes/McpSelector.tsx index 50ce0a1f74..a2a1b57e78 100644 --- a/webview-ui/src/components/modes/McpSelector.tsx +++ b/webview-ui/src/components/modes/McpSelector.tsx @@ -69,45 +69,28 @@ const McpSelector: React.FC = ({ setMcpIncludedList(included) }, [currentMode]) // Handle save - function updateMcpGroupOptions(groups: GroupEntry[] = [], group: string, mcpIncludedList: string[]): GroupEntry[] { - let mcpGroupFound = false - const newGroups = groups - .map((g) => { - if (Array.isArray(g) && g[0] === group) { - mcpGroupFound = true - return [ - group, - { - ...(g[1] || {}), - mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined, - }, - ] as GroupEntry - } - if (typeof g === "string" && g === group) { - mcpGroupFound = true - return [ - group, - { - mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined, - }, - ] as GroupEntry - } - return g - }) - .filter((g) => g !== undefined) + function updateMcpGroupOptions(groups: GroupEntry[] = [], _group: string, mcpIncludedList: string[]): GroupEntry[] { + // Filter out any existing "mcp" entries (both string and object forms) + const filteredGroups = groups.filter((g) => { + if (typeof g === "string") { + return g !== "mcp" + } + if (Array.isArray(g) && g[0] === "mcp") { + return false + } + if (typeof g === "object" && g !== null && !Array.isArray(g) && "mcp" in g) { + return false + } + return true + }) - if (!mcpGroupFound && group === "mcp") { - const groupsWithoutSimpleMcp = newGroups.filter((g) => g !== "mcp") - groupsWithoutSimpleMcp.push([ - "mcp", - { - mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined, - }, - ]) - return groupsWithoutSimpleMcp as GroupEntry[] - } else { - return newGroups as GroupEntry[] + // Add the new MCP configuration if there are selected servers + if (mcpIncludedList.length > 0) { + // Directly add the mcp object without wrapping in an array + return [...filteredGroups, { mcp: { included: mcpIncludedList } }] as GroupEntry[] } + + return filteredGroups as GroupEntry[] } // Handle save diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index f7e4739fe1..1eb2b8bd97 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -62,7 +62,13 @@ type ModesViewProps = { // Helper to get group name regardless of format function getGroupName(group: GroupEntry): ToolGroup { - return Array.isArray(group) ? group[0] : group + if (Array.isArray(group)) { + return group[0] + } + if (typeof group === "object" && "mcp" in group) { + return "mcp" as ToolGroup + } + return group as ToolGroup } const ModesView = ({ onDone }: ModesViewProps) => {