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.
This commit is contained in:
Daniel Riccio 2025-08-06 11:06:41 -05:00
parent 7e92618e43
commit b223d71a7f
No known key found for this signature in database
GPG key ID: FFD5FD825F8E8209
5 changed files with 77 additions and 49 deletions

View file

@ -40,7 +40,17 @@ export type GroupOptions = z.infer<typeof groupOptionsSchema>
* 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<typeof groupEntrySchema>

View file

@ -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:

View file

@ -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

View file

@ -69,45 +69,28 @@ const McpSelector: React.FC<McpSelectorProps> = ({
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

View file

@ -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) => {