Skip to content

Commit b223d71

Browse files
committed
fix: update MCP configuration to use direct object format in YAML
- Updated schema to accept { mcp: { included: [...] } } directly as a GroupEntry - Modified McpSelector to create the direct object format instead of tuple - Updated getGroupName and getGroupOptions helpers to handle the new format - Updated MCP server section to properly extract included list from both formats This resolves the nested array issue in YAML generation when toggling MCP checkbox and selecting specific servers. The YAML now correctly generates: - mcp: included: [...] Instead of the previous nested structure.
1 parent 7e92618 commit b223d71

File tree

5 files changed

+77
-49
lines changed

5 files changed

+77
-49
lines changed

packages/types/src/mode.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,17 @@ export type GroupOptions = z.infer<typeof groupOptionsSchema>
4040
* GroupEntry
4141
*/
4242

43-
export const groupEntrySchema = z.union([toolGroupsSchema, z.tuple([toolGroupsSchema, groupOptionsSchema])])
43+
export const groupEntrySchema = z.union([
44+
toolGroupsSchema,
45+
z.tuple([toolGroupsSchema, groupOptionsSchema]),
46+
// Allow direct mcp configuration object
47+
z.object({
48+
mcp: z.object({
49+
included: z.array(z.string()),
50+
description: z.string().optional(),
51+
}),
52+
}),
53+
])
4454

4555
export type GroupEntry = z.infer<typeof groupEntrySchema>
4656

src/core/prompts/sections/mcp-servers.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,34 @@ export async function getMcpServersSection(
5454
if (currentMode) {
5555
// Find MCP group configuration
5656
const mcpGroup = currentMode.groups.find((group: GroupEntry) => {
57+
// Handle tuple format: ["mcp", { mcp: { included: [...] } }]
5758
if (Array.isArray(group) && group.length === 2 && group[0] === "mcp") {
5859
return true
5960
}
61+
// Handle direct object format: { mcp: { included: [...] } }
62+
if (typeof group === "object" && !Array.isArray(group) && "mcp" in group) {
63+
return true
64+
}
6065
return getGroupName(group) === "mcp"
6166
})
6267

63-
// If MCP group configuration is found, get mcpIncludedList from mcp.included
64-
if (mcpGroup && Array.isArray(mcpGroup) && mcpGroup.length === 2) {
65-
const options = mcpGroup[1] as { mcp?: { included?: unknown[] } }
66-
mcpIncludedList = Array.isArray(options.mcp?.included)
67-
? options.mcp.included.filter((item: unknown): item is string => typeof item === "string")
68-
: undefined
68+
// Extract mcpIncludedList based on the format
69+
if (mcpGroup) {
70+
let mcpOptions: { mcp?: { included?: unknown[] } } | undefined
71+
72+
if (Array.isArray(mcpGroup) && mcpGroup.length === 2) {
73+
// Tuple format
74+
mcpOptions = mcpGroup[1] as { mcp?: { included?: unknown[] } }
75+
} else if (typeof mcpGroup === "object" && !Array.isArray(mcpGroup) && "mcp" in mcpGroup) {
76+
// Direct object format
77+
mcpOptions = mcpGroup as { mcp?: { included?: unknown[] } }
78+
}
79+
80+
if (mcpOptions) {
81+
mcpIncludedList = Array.isArray(mcpOptions.mcp?.included)
82+
? mcpOptions.mcp.included.filter((item: unknown): item is string => typeof item === "string")
83+
: undefined
84+
}
6985
}
7086
}
7187

@@ -77,7 +93,7 @@ export async function getMcpServersSection(
7793
connectedServers = `${filteredServers
7894
.map((server) => {
7995
const tools = server.tools
80-
?.filter((tool) => tool.enabledForPrompt !== false)
96+
?.filter((tool) => tool.enabledForPrompt !== false)
8197
?.map((tool) => {
8298
const schemaStr = tool.inputSchema
8399
? ` Input Schema:

src/shared/modes.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,26 @@ export function getGroupName(group: GroupEntry): ToolGroup {
2323
if (typeof group === "string") {
2424
return group
2525
}
26-
27-
return group[0]
26+
if (Array.isArray(group)) {
27+
return group[0]
28+
}
29+
// Handle direct MCP object format
30+
if (typeof group === "object" && "mcp" in group) {
31+
return "mcp" as ToolGroup
32+
}
33+
return group as ToolGroup
2834
}
2935

3036
// Helper to get group options if they exist
3137
function getGroupOptions(group: GroupEntry): GroupOptions | undefined {
32-
return Array.isArray(group) ? group[1] : undefined
38+
if (Array.isArray(group)) {
39+
return group[1]
40+
}
41+
// Handle direct MCP object format - return the object itself as options
42+
if (typeof group === "object" && "mcp" in group) {
43+
return group as GroupOptions
44+
}
45+
return undefined
3346
}
3447

3548
// Helper to check if a file path matches a regex pattern

webview-ui/src/components/modes/McpSelector.tsx

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -69,45 +69,28 @@ const McpSelector: React.FC<McpSelectorProps> = ({
6969
setMcpIncludedList(included)
7070
}, [currentMode])
7171
// Handle save
72-
function updateMcpGroupOptions(groups: GroupEntry[] = [], group: string, mcpIncludedList: string[]): GroupEntry[] {
73-
let mcpGroupFound = false
74-
const newGroups = groups
75-
.map((g) => {
76-
if (Array.isArray(g) && g[0] === group) {
77-
mcpGroupFound = true
78-
return [
79-
group,
80-
{
81-
...(g[1] || {}),
82-
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
83-
},
84-
] as GroupEntry
85-
}
86-
if (typeof g === "string" && g === group) {
87-
mcpGroupFound = true
88-
return [
89-
group,
90-
{
91-
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
92-
},
93-
] as GroupEntry
94-
}
95-
return g
96-
})
97-
.filter((g) => g !== undefined)
72+
function updateMcpGroupOptions(groups: GroupEntry[] = [], _group: string, mcpIncludedList: string[]): GroupEntry[] {
73+
// Filter out any existing "mcp" entries (both string and object forms)
74+
const filteredGroups = groups.filter((g) => {
75+
if (typeof g === "string") {
76+
return g !== "mcp"
77+
}
78+
if (Array.isArray(g) && g[0] === "mcp") {
79+
return false
80+
}
81+
if (typeof g === "object" && g !== null && !Array.isArray(g) && "mcp" in g) {
82+
return false
83+
}
84+
return true
85+
})
9886

99-
if (!mcpGroupFound && group === "mcp") {
100-
const groupsWithoutSimpleMcp = newGroups.filter((g) => g !== "mcp")
101-
groupsWithoutSimpleMcp.push([
102-
"mcp",
103-
{
104-
mcp: mcpIncludedList.length > 0 ? { included: mcpIncludedList } : undefined,
105-
},
106-
])
107-
return groupsWithoutSimpleMcp as GroupEntry[]
108-
} else {
109-
return newGroups as GroupEntry[]
87+
// Add the new MCP configuration if there are selected servers
88+
if (mcpIncludedList.length > 0) {
89+
// Directly add the mcp object without wrapping in an array
90+
return [...filteredGroups, { mcp: { included: mcpIncludedList } }] as GroupEntry[]
11091
}
92+
93+
return filteredGroups as GroupEntry[]
11194
}
11295

11396
// Handle save

webview-ui/src/components/modes/ModesView.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@ type ModesViewProps = {
6262

6363
// Helper to get group name regardless of format
6464
function getGroupName(group: GroupEntry): ToolGroup {
65-
return Array.isArray(group) ? group[0] : group
65+
if (Array.isArray(group)) {
66+
return group[0]
67+
}
68+
if (typeof group === "object" && "mcp" in group) {
69+
return "mcp" as ToolGroup
70+
}
71+
return group as ToolGroup
6672
}
6773

6874
const ModesView = ({ onDone }: ModesViewProps) => {

0 commit comments

Comments
 (0)