diff --git a/src/converters/claude-to-kiro.ts b/src/converters/claude-to-kiro.ts index 2711267..a29980d 100644 --- a/src/converters/claude-to-kiro.ts +++ b/src/converters/claude-to-kiro.ts @@ -53,7 +53,7 @@ export function convertClaudeToKiro( convertCommandToSkill(command, usedSkillNames, agentNames), ) - // Convert MCP servers (stdio only) + // Convert MCP servers (stdio and remote) const mcpServers = convertMcpServers(plugin.mcpServers) // Build steering files from CLAUDE.md @@ -177,19 +177,20 @@ function convertMcpServers( const result: Record = {} for (const [name, server] of Object.entries(servers)) { - if (!server.command) { + if (server.command) { + const entry: KiroMcpServer = { command: server.command } + if (server.args && server.args.length > 0) entry.args = server.args + if (server.env && Object.keys(server.env).length > 0) entry.env = server.env + result[name] = entry + } else if (server.url) { + const entry: KiroMcpServer = { url: server.url } + if (server.headers && Object.keys(server.headers).length > 0) entry.headers = server.headers + result[name] = entry + } else { console.warn( - `Warning: MCP server "${name}" has no command (HTTP/SSE transport). Kiro only supports stdio. Skipping.`, + `Warning: MCP server "${name}" has no command or url. Skipping.`, ) - continue } - - const entry: KiroMcpServer = { command: server.command } - if (server.args && server.args.length > 0) entry.args = server.args - if (server.env && Object.keys(server.env).length > 0) entry.env = server.env - - console.log(`MCP server "${name}" will execute: ${server.command}${server.args ? " " + server.args.join(" ") : ""}`) - result[name] = entry } return result } diff --git a/src/parsers/claude.ts b/src/parsers/claude.ts index 0d3f0b3..17cf86f 100644 --- a/src/parsers/claude.ts +++ b/src/parsers/claude.ts @@ -158,7 +158,8 @@ async function loadMcpServers( const mcpPath = path.join(root, ".mcp.json") if (await pathExists(mcpPath)) { - return readJson>(mcpPath) + const raw = await readJson>(mcpPath) + return unwrapMcpServers(raw) } return undefined @@ -232,12 +233,20 @@ async function loadMcpPaths( for (const entry of toPathList(value)) { const resolved = resolveWithinRoot(root, entry, "mcpServers path") if (await pathExists(resolved)) { - configs.push(await readJson>(resolved)) + const raw = await readJson>(resolved) + configs.push(unwrapMcpServers(raw)) } } return configs } +function unwrapMcpServers(raw: Record): Record { + if (raw.mcpServers && typeof raw.mcpServers === "object") { + return raw.mcpServers as Record + } + return raw as Record +} + function mergeMcpConfigs(configs: Record[]): Record { return configs.reduce((acc, config) => ({ ...acc, ...config }), {}) } diff --git a/tests/kiro-converter.test.ts b/tests/kiro-converter.test.ts index e638f71..e44ac3f 100644 --- a/tests/kiro-converter.test.ts +++ b/tests/kiro-converter.test.ts @@ -174,11 +174,7 @@ describe("convertClaudeToKiro", () => { expect(bundle.mcpServers.local.args).toEqual(["hello"]) }) - test("MCP HTTP servers skipped with warning", () => { - const warnings: string[] = [] - const originalWarn = console.warn - console.warn = (msg: string) => warnings.push(msg) - + test("MCP HTTP servers converted with url", () => { const plugin: ClaudePlugin = { ...fixturePlugin, mcpServers: { @@ -189,11 +185,32 @@ describe("convertClaudeToKiro", () => { skills: [], } + const bundle = convertClaudeToKiro(plugin, defaultOptions) + + expect(Object.keys(bundle.mcpServers)).toHaveLength(1) + expect(bundle.mcpServers.httpServer).toEqual({ url: "https://example.com/mcp" }) + }) + + test("MCP servers with no command or url skipped with warning", () => { + const warnings: string[] = [] + const originalWarn = console.warn + console.warn = (msg: string) => warnings.push(msg) + + const plugin: ClaudePlugin = { + ...fixturePlugin, + mcpServers: { + broken: {} as any, + }, + agents: [], + commands: [], + skills: [], + } + const bundle = convertClaudeToKiro(plugin, defaultOptions) console.warn = originalWarn expect(Object.keys(bundle.mcpServers)).toHaveLength(0) - expect(warnings.some((w) => w.includes("no command") || w.includes("HTTP"))).toBe(true) + expect(warnings.some((w) => w.includes("no command or url"))).toBe(true) }) test("plugin with zero agents produces empty agents array", () => {