This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-kit.git
commit 96385103753c60afe6de591c2a4b66309b95a0f4 Author: Jinal Sarvaiya <[email protected]> AuthorDate: Fri Oct 23 19:33:31 2026 +0100 fix #109: make Citrus MCP server opt-in via --with-citrus-mcp --- CHANGELOG.md | 4 + .../luigidemasi/camelkit/command/InitCommand.java | 5 + .../camelkit/generator/CodexConfigMerger.java | 14 ++- .../camelkit/generator/InitContext.java | 2 + .../camelkit/generator/McpConfigGenerator.java | 10 +- .../camelkit/service/DoctorService.java | 35 +++--- .../luigidemasi/camelkit/service/InitRequest.java | 1 + .../luigidemasi/camelkit/service/InitService.java | 9 +- .../templates/mcp-configs/antigravity-mcp.json | 4 +- .../resources/templates/mcp-configs/bob-mcp.json | 4 +- .../templates/mcp-configs/claude-code-mcp.json | 4 +- .../resources/templates/mcp-configs/codex-mcp.toml | 2 + .../templates/mcp-configs/copilot-mcp.json | 4 +- .../templates/mcp-configs/opencode-mcp.json | 8 +- .../resources/templates/mcp-configs/pi-mcp.json | 4 +- .../resources/templates/mcp-configs/qwen-mcp.json | 4 +- .../generator/AntigravityGeneratorTest.java | 5 +- .../camelkit/generator/CodexCliSmokeTest.java | 3 + .../camelkit/generator/CodexGeneratorTest.java | 12 ++- .../camelkit/generator/CopilotGeneratorTest.java | 4 +- .../camelkit/generator/OpenCodeGeneratorTest.java | 4 +- .../camelkit/generator/PiGeneratorTest.java | 4 +- .../camelkit/generator/QwenGeneratorTest.java | 4 +- .../generator/ResourceConsistencyTest.java | 3 +- .../generator/ShippedAssetStructureTest.java | 16 ++- .../camelkit/service/DoctorServiceTest.java | 119 +++++++++------------ .../camelkit/service/InitServiceTest.java | 67 +++++++++++- .../camelkit/workflow/WorkflowManifestTest.java | 4 +- 28 files changed, 229 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 498f7b09..9084f3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Make Citrus MCP server opt-in (#109)** — `camel-kit init` no longer writes Citrus MCP server + configuration by default. Pass `--with-citrus-mcp` to include it. Workspaces created before this + change that have `citrus.mcp.version` but no `citrus.mcp.enabled` are treated as implicitly opted + in for backward compatibility. `camel-kit doctor` skips Citrus health checks when not opted in. - **Claude Code native skill discovery (#243)** — skills are now installed to `.claude/skills/` with `user-invocable` and `description` frontmatter so Claude Code discovers them natively without command stubs. Legacy `.claude/commands/camel-*.md` stubs are cleaned up on regeneration; user-created commands are preserved. diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/command/InitCommand.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/command/InitCommand.java index 8b2081fd..72953a72 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/command/InitCommand.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/command/InitCommand.java @@ -48,6 +48,10 @@ public class InitCommand extends CamelKitCommand { defaultValue = "default") public String citrusVersion; + @Option(names = {"--with-citrus-mcp"}, + description = "Include the Citrus MCP server in agent configuration (default: off)") + public boolean withCitrusMcp; + @Option(names = {"--no-fetch"}, description = "Skip external catalog fetching") public boolean noFetch; @@ -170,6 +174,7 @@ public class InitCommand extends CamelKitCommand { resolvedTargetDir, citrusVersion, noFetch, + withCitrusMcp, sourcePlatform, detectCommandPrefix(), CamelKitMain.DEFAULT_CITRUS_VERSION, diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/CodexConfigMerger.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/CodexConfigMerger.java index 9f2b044f..8a3d574f 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/CodexConfigMerger.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/CodexConfigMerger.java @@ -14,22 +14,26 @@ final class CodexConfigMerger { static final String BEGIN_MARKER = "# BEGIN CAMEL-KIT MANAGED MCP SERVERS"; static final String END_MARKER = "# END CAMEL-KIT MANAGED MCP SERVERS"; - private static final List<String> MANAGED_SERVERS = List.of("camel", "camel-knowledge", "citrus"); + static List<String> managedServers(boolean citrusEnabled) { + return citrusEnabled + ? List.of("camel", "camel-knowledge", "citrus") + : List.of("camel", "camel-knowledge"); + } - void merge(Path configFile, String renderedConfig) throws IOException { + void merge(Path configFile, String renderedConfig, boolean citrusEnabled) throws IOException { validateToml(renderedConfig, "generated Codex MCP configuration"); String managedBlock = BEGIN_MARKER + System.lineSeparator() + renderedConfig.strip() + System.lineSeparator() + END_MARKER; String existing = Files.isRegularFile(configFile) ? Files.readString(configFile) : ""; - String merged = merge(existing, managedBlock); + String merged = merge(existing, managedBlock, citrusEnabled); validateToml(merged, configFile.toString()); AtomicFileWriter.write(configFile, merged + (merged.endsWith(System.lineSeparator()) ? "" : System.lineSeparator())); } - private String merge(String existing, String managedBlock) throws IOException { + private String merge(String existing, String managedBlock, boolean citrusEnabled) throws IOException { if (existing.isBlank()) { return managedBlock; } @@ -47,7 +51,7 @@ final class CodexConfigMerger { Object mcpServers = Toml.parse(existing).get("mcp_servers"); if (mcpServers instanceof TomlTable servers) { - List<String> conflicts = MANAGED_SERVERS.stream() + List<String> conflicts = managedServers(citrusEnabled).stream() .filter(server -> servers.get(server) instanceof TomlTable) .toList(); if (!conflicts.isEmpty()) { diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/InitContext.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/InitContext.java index 6beb1926..2e588402 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/InitContext.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/InitContext.java @@ -14,6 +14,7 @@ public record InitContext( Path projectDir, String commandPrefix, DistributionConfig distribution, + boolean withCitrusMcp, Printer printer) { public InitContext( @@ -32,6 +33,7 @@ public record InitContext( projectDir, commandPrefix, DistributionConfig.loadFromClasspathOrDefaults(), + false, printer); } } diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/McpConfigGenerator.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/McpConfigGenerator.java index 895ff739..4a83adce 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/McpConfigGenerator.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/McpConfigGenerator.java @@ -31,9 +31,10 @@ class McpConfigGenerator { QuteTemplateEngine qute = new QuteTemplateEngine(); String template = TemplateUtils.readTemplate(ctx.agent().mcpConfigTemplatePath()); - String processed = qute.renderString(template, templateData(ctx.distribution(), workflow)); + String processed + = qute.renderString(template, templateData(ctx.distribution(), workflow, ctx.withCitrusMcp())); if ("toml".equals(ctx.agent().mcpConfigFormat())) { - new CodexConfigMerger().merge(configFile, processed); + new CodexConfigMerger().merge(configFile, processed, ctx.withCitrusMcp()); } else if ("opencode".equals(ctx.agentName())) { new OpenCodeConfigMerger().merge(configFile, processed); } else if ("antigravity".equals(ctx.agentName())) { @@ -90,7 +91,9 @@ class McpConfigGenerator { } } - private Map<String, Object> templateData(DistributionConfig dist, WorkflowManifest workflow) throws IOException { + private Map<String, Object> templateData( + DistributionConfig dist, WorkflowManifest workflow, boolean withCitrusMcp) + throws IOException { Map<String, Object> data = new java.util.HashMap<>( VersionPlaceholderResolver.buildVersionTemplateData(dist)); @@ -101,6 +104,7 @@ class McpConfigGenerator { data.put("KNOWLEDGE_TOOLS_JSON", toJsonArray(knowledgeServer.allowedTools())); data.put("KNOWLEDGE_DESCRIPTION", knowledgeServer.description()); + data.put("CITRUS_ENABLED", withCitrusMcp); WorkflowManifest.WorkflowMcpServer citrusServer = workflow.mcpServer("citrus"); data.put("CITRUS_TOOLS_JSON", toJsonArray(citrusServer.allowedTools())); data.put("CITRUS_DESCRIPTION", citrusServer.description()); diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java index 88ba10c1..d6facb95 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java @@ -48,11 +48,6 @@ public class DoctorService { private static final String NESTED_RULE = "\n"; private static final ObjectMapper JSONC_MAPPER = OpenCodeProjectConfig.newJsonMapper(); private static final Duration PREREQUISITE_TIMEOUT = Duration.ofSeconds(3); - private static final Set<String> PRE_CITRUS_JSON_AGENTS = Set.of( - AgentGeneratorStrategy.BOB2.descriptorValue(), - AgentGeneratorStrategy.CLAUDE.descriptorValue(), - AgentGeneratorStrategy.OPENCODE.descriptorValue(), - AgentGeneratorStrategy.QWEN.descriptorValue()); private static final Set<String> OPENCODE_PERMISSION_ACTIONS = Set.of("allow", "ask", "deny"); private static final List<String> REQUIRED_CONFIG_KEYS = List.of( @@ -534,7 +529,7 @@ public class DoctorService { AgentConfig configuredAgent = AgentRegistry.get(normalizedAgentName); if (configuredAgent != null && "toml".equals(configuredAgent.mcpConfigFormat())) { - checkCodexMcp(root, mcpFile, findings); + checkCodexMcp(root, mcpFile, config, findings); return; } @@ -573,19 +568,16 @@ public class DoctorService { boolean knowledgeOk = checkMcpServer( root, mcpFile, servers, "camel-knowledge", expectations.knowledgeMcpTools(), copilotToolsSchema, piDirectToolsSchema, qwenIncludeToolsSchema, openCodeSchema, antigravitySchema, findings); - boolean citrusOptional = PRE_CITRUS_JSON_AGENTS.contains(normalizedAgentName) - && !config.containsKey("citrus.version") - && !config.containsKey("citrus.mcp.version"); + boolean citrusMcpEnabled = "true".equals(config.getProperty("citrus.mcp.enabled")); + if (!config.containsKey("citrus.mcp.enabled") && config.containsKey("citrus.mcp.version")) { + citrusMcpEnabled = true; + } boolean citrusOk; - if (servers.has("citrus") || !citrusOptional) { + if (citrusMcpEnabled || servers.has("citrus")) { citrusOk = checkMcpServer( root, mcpFile, servers, "citrus", expectations.citrusMcpTools(), copilotToolsSchema, piDirectToolsSchema, qwenIncludeToolsSchema, openCodeSchema, antigravitySchema, findings); } else { - // Only legacy-capable JSON agents without persisted Citrus metadata reach this branch. - findings.add(DoctorFinding.warn("mcp", relativize(root, mcpFile), - "MCP server 'citrus' is not configured; camel-test cannot verify Citrus actions", - "Regenerate the MCP config with camel-kit init --here --force to add the Citrus MCP server.")); citrusOk = true; } boolean openCodePermissionsOk = !openCodeSchema @@ -710,7 +702,7 @@ public class DoctorService { }); } - private void checkCodexMcp(Path root, Path mcpFile, List<DoctorFinding> findings) { + private void checkCodexMcp(Path root, Path mcpFile, Properties config, List<DoctorFinding> findings) { TomlParseResult parsed; try { parsed = Toml.parse(mcpFile); @@ -738,8 +730,17 @@ public class DoctorService { root, mcpFile, servers, "camel", expectations.camelMcpTools(), findings); boolean knowledgeOk = checkCodexMcpServer( root, mcpFile, servers, "camel-knowledge", expectations.knowledgeMcpTools(), findings); - boolean citrusOk = checkCodexMcpServer( - root, mcpFile, servers, "citrus", expectations.citrusMcpTools(), findings); + boolean codexCitrusEnabled = "true".equals(config.getProperty("citrus.mcp.enabled")); + if (!config.containsKey("citrus.mcp.enabled") && config.containsKey("citrus.mcp.version")) { + codexCitrusEnabled = true; + } + boolean citrusOk; + if (codexCitrusEnabled || servers.get("citrus") instanceof TomlTable) { + citrusOk = checkCodexMcpServer( + root, mcpFile, servers, "citrus", expectations.citrusMcpTools(), findings); + } else { + citrusOk = true; + } if (camelOk && knowledgeOk && citrusOk) { findings.add(DoctorFinding.pass("mcp", relativize(root, mcpFile), "Codex MCP config is valid TOML and all tool allowlists match Camel-Kit expectations", diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitRequest.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitRequest.java index 0ae425d3..8729394f 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitRequest.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitRequest.java @@ -15,6 +15,7 @@ public record InitRequest( Path targetDir, String citrusVersion, boolean noFetch, + boolean withCitrusMcp, String sourcePlatform, String commandPrefix, String defaultCitrusVersion, diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitService.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitService.java index 7da03216..69c45f0b 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitService.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/InitService.java @@ -54,7 +54,7 @@ public class InitService { InitContext genCtx = new InitContext( agent, request.agentName(), commandsDir, skillsDir, targetDir, - request.commandPrefix(), request.distribution(), request.printer()); + request.commandPrefix(), request.distribution(), request.withCitrusMcp(), request.printer()); AgentGenerator generator = AgentGeneratorFactory.create(request.agentName()); requireRealAgentDirectories(targetDir, skillsDir, commandsDir); generator.preflight(genCtx); @@ -156,7 +156,12 @@ public class InitService { config.setProperty("agent.name", agentName); config.setProperty("agent.folder", agent.folder()); config.setProperty("citrus.version", request.resolvedCitrusVersion()); - config.setProperty("citrus.mcp.version", request.distribution().citrusMcpVersion()); + if (request.withCitrusMcp()) { + config.setProperty("citrus.mcp.version", request.distribution().citrusMcpVersion()); + config.setProperty("citrus.mcp.enabled", "true"); + } else { + config.setProperty("citrus.mcp.enabled", "false"); + } writeVersionConfig(config, request.distribution(), "main"); if (request.sourcePlatform() != null && !"auto".equals(request.sourcePlatform())) { config.setProperty("project.sourcePlatform", request.sourcePlatform()); diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/antigravity-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/antigravity-mcp.json index 3f0467fc..7f6b1cac 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/antigravity-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/antigravity-mcp.json @@ -17,7 +17,7 @@ "-Dquarkus.log.level=WARN", "io.github.luigidemasi:camel-kit-knowledge-mcp:{KNOWLEDGE_VERSION}:runner" ] - }, + }{#if CITRUS_ENABLED}, "citrus": { "command": "jbang", "args": [ @@ -26,6 +26,6 @@ "-Dquarkus.http.port=-1", "org.citrusframework:citrus-mcp-server:{CITRUS_MCP_VERSION}:runner" ] - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/bob-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/bob-mcp.json index 76f8f547..39fa77d8 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/bob-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/bob-mcp.json @@ -23,7 +23,7 @@ "description": "{KNOWLEDGE_DESCRIPTION}", "autoApprove": {KNOWLEDGE_TOOLS_JSON}, "alwaysAllow": {KNOWLEDGE_TOOLS_JSON} - }, + }{#if CITRUS_ENABLED}, "citrus": { "command": "jbang", "args": [ @@ -35,6 +35,6 @@ "description": "{CITRUS_DESCRIPTION}", "autoApprove": {CITRUS_TOOLS_JSON}, "alwaysAllow": {CITRUS_TOOLS_JSON} - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/claude-code-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/claude-code-mcp.json index 76f8f547..39fa77d8 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/claude-code-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/claude-code-mcp.json @@ -23,7 +23,7 @@ "description": "{KNOWLEDGE_DESCRIPTION}", "autoApprove": {KNOWLEDGE_TOOLS_JSON}, "alwaysAllow": {KNOWLEDGE_TOOLS_JSON} - }, + }{#if CITRUS_ENABLED}, "citrus": { "command": "jbang", "args": [ @@ -35,6 +35,6 @@ "description": "{CITRUS_DESCRIPTION}", "autoApprove": {CITRUS_TOOLS_JSON}, "alwaysAllow": {CITRUS_TOOLS_JSON} - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/codex-mcp.toml b/camel-kit-core/src/main/resources/templates/mcp-configs/codex-mcp.toml index d9c6343a..d19e2f88 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/codex-mcp.toml +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/codex-mcp.toml @@ -19,6 +19,7 @@ args = [ ] enabled_tools = {KNOWLEDGE_TOOLS_JSON} default_tools_approval_mode = "prompt" +{#if CITRUS_ENABLED} [mcp_servers.citrus] command = "jbang" @@ -30,3 +31,4 @@ args = [ ] enabled_tools = {CITRUS_TOOLS_JSON} default_tools_approval_mode = "prompt" +{/if} diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/copilot-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/copilot-mcp.json index 4dfc1ee6..33287595 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/copilot-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/copilot-mcp.json @@ -23,7 +23,7 @@ ], "description": "{KNOWLEDGE_DESCRIPTION}", "tools": {KNOWLEDGE_TOOLS_JSON} - }, + }{#if CITRUS_ENABLED}, "citrus": { "type": "stdio", "command": "jbang", @@ -35,6 +35,6 @@ ], "description": "{CITRUS_DESCRIPTION}", "tools": {CITRUS_TOOLS_JSON} - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/opencode-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/opencode-mcp.json index 6baedd19..ed536d09 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/opencode-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/opencode-mcp.json @@ -2,8 +2,8 @@ "$schema": "https://opencode.ai/config.json", "permission": { "camel_*": "ask", - "camel-knowledge_*": "ask", - "citrus_*": "ask" + "camel-knowledge_*": "ask"{#if CITRUS_ENABLED}, + "citrus_*": "ask"{/if} }, "mcp": { "camel": { @@ -27,7 +27,7 @@ "io.github.luigidemasi:camel-kit-knowledge-mcp:{KNOWLEDGE_VERSION}:runner" ], "enabled": true - }, + }{#if CITRUS_ENABLED}, "citrus": { "type": "local", "command": [ @@ -38,6 +38,6 @@ "org.citrusframework:citrus-mcp-server:{CITRUS_MCP_VERSION}:runner" ], "enabled": true - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/pi-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/pi-mcp.json index e17cc1e3..c7539b1c 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/pi-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/pi-mcp.json @@ -23,7 +23,7 @@ "description": "{KNOWLEDGE_DESCRIPTION}", "directTools": {KNOWLEDGE_TOOLS_JSON}, "excludeTools": [] - }, + }{#if CITRUS_ENABLED}, "citrus": { "command": "jbang", "args": [ @@ -35,6 +35,6 @@ "description": "{CITRUS_DESCRIPTION}", "directTools": {CITRUS_TOOLS_JSON}, "excludeTools": [] - } + }{/if} } } diff --git a/camel-kit-core/src/main/resources/templates/mcp-configs/qwen-mcp.json b/camel-kit-core/src/main/resources/templates/mcp-configs/qwen-mcp.json index 3f8b5f87..94c348d8 100644 --- a/camel-kit-core/src/main/resources/templates/mcp-configs/qwen-mcp.json +++ b/camel-kit-core/src/main/resources/templates/mcp-configs/qwen-mcp.json @@ -21,7 +21,7 @@ ], "description": "{KNOWLEDGE_DESCRIPTION}", "includeTools": {KNOWLEDGE_TOOLS_JSON} - }, + }{#if CITRUS_ENABLED}, "citrus": { "command": "jbang", "args": [ @@ -32,6 +32,6 @@ ], "description": "{CITRUS_DESCRIPTION}", "includeTools": {CITRUS_TOOLS_JSON} - } + }{/if} } } diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/AntigravityGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/AntigravityGeneratorTest.java index 1f1b92c7..bd83092a 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/AntigravityGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/AntigravityGeneratorTest.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import com.fasterxml.jackson.databind.JsonNode; @@ -25,7 +26,9 @@ class AntigravityGeneratorTest { private InitContext context(String prefix) { var agent = AgentRegistry.get("antigravity"); Path skills = tempDir.resolve(agent.skillsDirectory()); - return new InitContext(agent, "antigravity", skills, skills, tempDir, prefix, Printer.noop()); + return new InitContext( + agent, "antigravity", skills, skills, tempDir, prefix, + DistributionConfig.loadFromClasspathOrDefaults(), true, Printer.noop()); } @ParameterizedTest diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexCliSmokeTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexCliSmokeTest.java index c35ccf9e..740c32b5 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexCliSmokeTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexCliSmokeTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import io.github.luigidemasi.camelkit.workflow.WorkflowManifestLoader; @@ -97,6 +98,8 @@ class CodexCliSmokeTest { workspace.resolve(agent.skillsDirectory()), workspace, "camel-kit", + DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); AgentGeneratorFactory.create("codex").generate(context); diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexGeneratorTest.java index 220ecf3f..d989a1ac 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CodexGeneratorTest.java @@ -12,6 +12,7 @@ import java.util.stream.Collectors; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentDescriptor; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import io.github.luigidemasi.camelkit.workflow.WorkflowManifest; import io.github.luigidemasi.camelkit.workflow.WorkflowManifestLoader; @@ -118,8 +119,7 @@ class CodexGeneratorTest { ctx.distribution().camelMcpVersion()); assertServer(servers, "camel-knowledge", workflow.mcpServer("camel-knowledge").allowedTools(), ctx.distribution().knowledgeMcpVersion()); - assertServer(servers, "citrus", workflow.mcpServer("citrus").allowedTools(), - ctx.distribution().citrusMcpVersion()); + assertNull(servers.get("citrus"), "Citrus server must be absent when not opted in"); } @Test @@ -133,7 +133,7 @@ class CodexGeneratorTest { enabled_tools = ["read"] """); - new CodexGenerator().generate(createContext(tempDir)); + new CodexGenerator().generate(createContext(tempDir, true)); TomlParseResult config = Toml.parse(codexDir.resolve("config.toml")); assertFalse(config.hasErrors(), config.errors().toString()); @@ -229,6 +229,10 @@ class CodexGeneratorTest { } private InitContext createContext(Path projectDir) { + return createContext(projectDir, false); + } + + private InitContext createContext(Path projectDir, boolean withCitrusMcp) { AgentConfig agent = AgentRegistry.get("codex"); return new InitContext( agent, @@ -237,6 +241,8 @@ class CodexGeneratorTest { projectDir.resolve(agent.skillsDirectory()), projectDir, "camel-kit", + DistributionConfig.loadFromClasspathOrDefaults(), + withCitrusMcp, Printer.noop()); } diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CopilotGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CopilotGeneratorTest.java index 0c8bf0d3..fca21776 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CopilotGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/CopilotGeneratorTest.java @@ -9,6 +9,7 @@ import java.util.Set; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentDescriptor; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import com.fasterxml.jackson.databind.JsonNode; @@ -35,7 +36,8 @@ class CopilotGeneratorTest { Path skillsDir = tempDir.resolve(agent.skillsDirectory()); return new InitContext( agent, "copilot", skillsDir, skillsDir, tempDir, - "camel-kit", Printer.noop()); + "camel-kit", DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } @Test diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/OpenCodeGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/OpenCodeGeneratorTest.java index f9f7fd96..ec678c5a 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/OpenCodeGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/OpenCodeGeneratorTest.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentDescriptor; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.config.OpenCodeProjectConfig; import io.github.luigidemasi.camelkit.output.Printer; @@ -50,7 +51,8 @@ class OpenCodeGeneratorTest { Path skillsDir = tempDir.resolve(agentBaseFolder + "/skills"); return new InitContext( agent, "opencode", commandsDir, skillsDir, tempDir, - commandPrefix, Printer.noop()); + commandPrefix, DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } @Test diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/PiGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/PiGeneratorTest.java index 8433a230..02756d0e 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/PiGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/PiGeneratorTest.java @@ -8,6 +8,7 @@ import java.util.regex.Pattern; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentDescriptor; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import com.fasterxml.jackson.databind.JsonNode; @@ -36,7 +37,8 @@ class PiGeneratorTest { Path skillsDir = tempDir.resolve(agentBaseFolder + "/skills"); return new InitContext( agent, "pi", commandsDir, skillsDir, tempDir, - "camel-kit", Printer.noop()); + "camel-kit", DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } @Test diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/QwenGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/QwenGeneratorTest.java index c9946154..3618d478 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/QwenGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/QwenGeneratorTest.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentDescriptor; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.output.Printer; import com.fasterxml.jackson.databind.JsonNode; @@ -30,7 +31,8 @@ class QwenGeneratorTest { Path skillsDir = tempDir.resolve(agentBaseFolder + "/skills"); return new InitContext( agent, "qwen", commandsDir, skillsDir, tempDir, - "camel-kit", Printer.noop()); + "camel-kit", DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } @Test diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ResourceConsistencyTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ResourceConsistencyTest.java index 7f38b131..63717aca 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ResourceConsistencyTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ResourceConsistencyTest.java @@ -1658,7 +1658,8 @@ class ResourceConsistencyTest { Path skillsDir = projectDir.resolve(agent.skillsDirectory()); return new InitContext( agent, agentName, commandsDir, skillsDir, projectDir, - "camel-kit", Printer.noop()); + "camel-kit", DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } private static List<String> sortedAgentNames() { diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ShippedAssetStructureTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ShippedAssetStructureTest.java index 1859d4f6..cec219f4 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ShippedAssetStructureTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ShippedAssetStructureTest.java @@ -1200,8 +1200,13 @@ class ShippedAssetStructureTest { assertInstanceOf(TomlTable.class, servers.get("camel"), agentName + " MCP config missing camel server"); assertInstanceOf(TomlTable.class, servers.get("camel-knowledge"), agentName + " MCP config missing camel-knowledge server"); - assertInstanceOf(TomlTable.class, servers.get("citrus"), - agentName + " MCP config missing citrus server"); + if (ctx.withCitrusMcp()) { + assertInstanceOf(TomlTable.class, servers.get("citrus"), + agentName + " MCP config missing citrus server"); + } else { + assertNull(servers.get("citrus"), + agentName + " MCP config must not contain citrus server when not opted in"); + } return; } @@ -1210,7 +1215,12 @@ class ShippedAssetStructureTest { assertNotNull(servers, agentName + " MCP config missing " + ctx.agent().mcpServerContainerKey()); assertTrue(servers.has("camel"), agentName + " MCP config missing camel server"); assertTrue(servers.has("camel-knowledge"), agentName + " MCP config missing camel-knowledge server"); - assertTrue(servers.has("citrus"), agentName + " MCP config missing citrus server"); + if (ctx.withCitrusMcp()) { + assertTrue(servers.has("citrus"), agentName + " MCP config missing citrus server"); + } else { + assertFalse(servers.has("citrus"), + agentName + " MCP config must not contain citrus server when not opted in"); + } } private static InitContext createContext(String agentName, Path projectDir) { diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java index d6964d7a..59a5ee76 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java @@ -10,6 +10,7 @@ import java.util.Map; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentGeneratorStrategy; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.generator.AgentGeneratorFactory; import io.github.luigidemasi.camelkit.generator.InitContext; import io.github.luigidemasi.camelkit.output.Printer; @@ -41,8 +42,8 @@ class DoctorServiceTest { Path tempDir; @Test - void antigravityMcpUsesNativeSchemaAndRequiresCitrus() throws Exception { - createHealthyWorkspace(tempDir, "antigravity"); + void antigravityMcpUsesNativeSchemaAndRequiresCitrusWhenOptedIn() throws Exception { + createHealthyWorkspaceWithCitrus(tempDir, "antigravity"); Path commented = tempDir.resolve(".agents/mcp_config.json"); Files.writeString(commented, Files.readString(commented).replaceFirst("\\{", "{ // custom comment\n")); DoctorResult healthy = new DoctorService().inspect(new DoctorRequest(tempDir)); @@ -998,81 +999,26 @@ class DoctorServiceTest { } @Test - void missingCitrusServerWarnsWithoutVersionPropertiesInLayeredOpenCodeWorkspace() throws Exception { - createHealthyWorkspace(tempDir, OPENCODE); - writeMcpConfig(tempDir, OPENCODE, """ - { - "mcp": { - "camel": {"type": "local"}, - "camel-knowledge": {"type": "local"} - } - } - """); - Files.writeString(tempDir.resolve("opencode.jsonc"), """ - { - "permission": { - "camel_*": "ask", - "camel-knowledge_*": "ask" - } - } - """); - - DoctorResult result = new DoctorService().inspect(new DoctorRequest(tempDir)); - - assertFalse(result.hasFailures(), result.findings().toString()); - assertTrue(hasFinding(result, DoctorFinding.Status.WARN, "mcp", - "MCP server 'citrus' is not configured", "Regenerate")); - assertFalse(hasFinding(result, DoctorFinding.Status.FAIL, "mcp", "citrus", null)); - assertFalse(hasFinding(result, DoctorFinding.Status.PASS, "mcp", - "uses supported server fields", null)); - - markCitrusGenerated(tempDir, "citrus.mcp.version"); - DoctorResult currentResult = new DoctorService().inspect(new DoctorRequest(tempDir)); - - assertTrue(currentResult.hasFailures(), currentResult.findings().toString()); - assertTrue(hasFinding(currentResult, DoctorFinding.Status.FAIL, "mcp", - "MCP server 'citrus' is missing", "Regenerate")); - assertFalse(hasFinding(currentResult, DoctorFinding.Status.WARN, "mcp", - "MCP server 'citrus' is not configured", null)); - } - - @Test - void missingCitrusServerWarnsWithoutVersionPropertiesForLegacyCapableJsonAgents() throws Exception { - for (String agentName : List.of("bob2", CLAUDE, OPENCODE, QWEN)) { + void missingCitrusServerPassesWhenNotOptedIn() throws Exception { + for (String agentName : List.of("bob2", CLAUDE, OPENCODE, QWEN, COPILOT, PI)) { Path root = tempDir.resolve(agentName); createHealthyWorkspace(root, agentName); - removeCitrusServer(root, agentName); DoctorResult result = new DoctorService().inspect(new DoctorRequest(root)); assertFalse(result.hasFailures(), agentName + ": " + result.findings()); - assertTrue(hasFinding(result, DoctorFinding.Status.WARN, "mcp", - "MCP server 'citrus' is not configured", "Regenerate"), + assertFalse(hasFinding(result, DoctorFinding.Status.FAIL, "mcp", "citrus", null), agentName + ": " + result.findings()); - assertFalse(hasFinding(result, DoctorFinding.Status.FAIL, "mcp", - "MCP server 'citrus' is missing", null), - agentName + ": " + result.findings()); - assertFalse(result.findings().stream().anyMatch(finding -> finding.status() == DoctorFinding.Status.PASS - && "mcp".equals(finding.category())), + assertFalse(hasFinding(result, DoctorFinding.Status.WARN, "mcp", "citrus", null), agentName + ": " + result.findings()); } - - Path currentRoot = tempDir.resolve(CLAUDE); - markCitrusGenerated(currentRoot, "citrus.version"); - DoctorResult currentResult = new DoctorService().inspect(new DoctorRequest(currentRoot)); - - assertTrue(currentResult.hasFailures(), currentResult.findings().toString()); - assertTrue(hasFinding(currentResult, DoctorFinding.Status.FAIL, "mcp", - "MCP server 'citrus' is missing", "Regenerate")); - assertFalse(hasFinding(currentResult, DoctorFinding.Status.WARN, "mcp", - "MCP server 'citrus' is not configured", null)); } @Test - void missingCitrusServerFailsForJsonAgentsIntroducedAfterCitrus() throws Exception { - for (String agentName : List.of(COPILOT, PI)) { - Path root = tempDir.resolve(agentName); - createHealthyWorkspace(root, agentName); + void missingCitrusServerFailsWhenOptedIn() throws Exception { + for (String agentName : List.of("bob2", CLAUDE, COPILOT, PI, QWEN)) { + Path root = tempDir.resolve("enabled-" + agentName); + createHealthyWorkspaceWithCitrus(root, agentName); removeCitrusServer(root, agentName); DoctorResult result = new DoctorService().inspect(new DoctorRequest(root)); @@ -1081,12 +1027,22 @@ class DoctorServiceTest { assertTrue(hasFinding(result, DoctorFinding.Status.FAIL, "mcp", "MCP server 'citrus' is missing", "Regenerate"), agentName + ": " + result.findings()); - assertFalse(hasFinding(result, DoctorFinding.Status.WARN, "mcp", - "MCP server 'citrus' is not configured", null), - agentName + ": " + result.findings()); } } + @Test + void missingCitrusServerFailsWithLegacyBackwardCompat() throws Exception { + Path root = tempDir.resolve("legacy-citrus"); + createHealthyWorkspace(root, CLAUDE); + markCitrusGenerated(root, "citrus.mcp.version"); + + DoctorResult result = new DoctorService().inspect(new DoctorRequest(root)); + + assertTrue(result.hasFailures(), result.findings().toString()); + assertTrue(hasFinding(result, DoctorFinding.Status.FAIL, "mcp", + "MCP server 'citrus' is missing", "Regenerate")); + } + @Test void citrusToolAllowlistMismatchFailsForEachJsonAllowlistSchema() throws Exception { Map<String, List<String>> schemas = Map.of( @@ -1098,7 +1054,7 @@ class DoctorServiceTest { for (Map.Entry<String, List<String>> schema : schemas.entrySet()) { String agentName = schema.getKey(); Path root = tempDir.resolve(agentName); - createHealthyWorkspace(root, agentName); + createHealthyWorkspaceWithCitrus(root, agentName); Path mcpFile = root.resolve(AgentRegistry.get(agentName).mcpConfigPath()); ObjectNode config = (ObjectNode) MAPPER.readTree(mcpFile.toFile()); ObjectNode citrus = (ObjectNode) config.path(AgentRegistry.get(agentName).mcpServerContainerKey()) @@ -1697,6 +1653,31 @@ class DoctorServiceTest { AgentGeneratorFactory.create(agentName).generate(context); } + private void createHealthyWorkspaceWithCitrus(Path root, String agentName) throws Exception { + AgentConfig agent = AgentRegistry.get(agentName); + Files.createDirectories(root.resolve(".camel-kit")); + Files.writeString(root.resolve(".camel-kit/config.properties"), String.format(Locale.ROOT, """ + project.name=orders + project.command-prefix=camel-kit + project.runtime=main + project.camelVersion=4.20.0 + project.platformBomVersion=4.20.0 + agent.name=%s + agent.folder=%s + citrus.mcp.enabled=true + citrus.mcp.version=5.0.1 + """, agentName, agent.folder())); + Files.writeString(root.resolve(".camel-kit/project-graph.json"), "{}"); + Files.writeString(root.resolve("mvnw"), "#!/bin/sh\n"); + Path commands = agent.generatesCommandStubs() + ? root.resolve(agent.commandDirectory()) + : root.resolve(".codex/commands"); + InitContext context = new InitContext( + agent, agentName, commands, root.resolve(agent.skillsDirectory()), root, "camel-kit", + DistributionConfig.loadFromClasspathOrDefaults(), true, Printer.noop()); + AgentGeneratorFactory.create(agentName).generate(context); + } + private void writeMcpConfig(Path root, String agentName, String content) throws Exception { AgentConfig agent = AgentRegistry.get(agentName); Path mcpFile = root.resolve(agent.mcpConfigPath()); diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/InitServiceTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/InitServiceTest.java index c9f75073..3022883a 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/InitServiceTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/InitServiceTest.java @@ -56,7 +56,8 @@ class InitServiceTest { assertTrue(config.contains("agent.name=bob2")); assertTrue(config.contains("project.sourcePlatform=mulesoft")); assertTrue(config.contains("citrus.version=5.0.1")); - assertTrue(config.contains("citrus.mcp.version=" + EXPECTED_CITRUS_MCP_VERSION)); + assertTrue(config.contains("citrus.mcp.enabled=false")); + assertFalse(config.contains("citrus.mcp.version=")); assertTrue(progress.events().contains("start:Creating project structure")); assertTrue(progress.events().contains("start:Generating IBM Bob 2 workspace")); @@ -65,14 +66,35 @@ class InitServiceTest { } @Test - void allAgentsGenerateTheDefaultCitrusRunnerAndPassDoctorMcpChecks() throws Exception { + void allAgentsOmitCitrusByDefaultAndPassDoctorMcpChecks() throws Exception { for (String agentName : AgentRegistry.names()) { Path targetDir = tempDir.resolve(agentName); new InitService().initialize(request(targetDir, agentName, InitProgress.noop(), InitReporter.noop())); + String mcp = Files.readString(targetDir.resolve(AgentRegistry.get(agentName).mcpConfigPath())); + assertFalse(mcp.contains("citrus-mcp-server"), agentName + ": citrus should be absent by default"); + DoctorResult result = new DoctorService().inspect(new DoctorRequest(targetDir)); + assertTrue(result.findings().stream().anyMatch(finding -> "mcp".equals(finding.category()) + && finding.status() == DoctorFinding.Status.PASS), agentName + ": " + result.findings()); + assertFalse(result.findings().stream().anyMatch(finding -> "mcp".equals(finding.category()) + && finding.status() == DoctorFinding.Status.FAIL), agentName + ": " + result.findings()); + } + } + + @Test + void allAgentsIncludeCitrusWhenOptedInAndPassDoctorMcpChecks() throws Exception { + for (String agentName : AgentRegistry.names()) { + Path targetDir = tempDir.resolve("citrus-" + agentName); + new InitService().initialize(requestWithCitrusMcp(targetDir, agentName)); + String mcp = Files.readString(targetDir.resolve(AgentRegistry.get(agentName).mcpConfigPath())); assertTrue(mcp.contains( "org.citrusframework:citrus-mcp-server:" + EXPECTED_CITRUS_MCP_VERSION + ":runner"), agentName); + + String config = Files.readString(targetDir.resolve(".camel-kit/config.properties")); + assertTrue(config.contains("citrus.mcp.enabled=true"), agentName); + assertTrue(config.contains("citrus.mcp.version=" + EXPECTED_CITRUS_MCP_VERSION), agentName); + DoctorResult result = new DoctorService().inspect(new DoctorRequest(targetDir)); assertTrue(result.findings().stream().anyMatch(finding -> "mcp".equals(finding.category()) && finding.status() == DoctorFinding.Status.PASS), agentName + ": " + result.findings()); @@ -428,12 +450,14 @@ class InitServiceTest { Path targetDir = tempDir.resolve("orders"); InitResult result = new InitService().initialize( - request(targetDir, "bob2", "4.9.2", InitProgress.noop(), InitReporter.noop())); + request(targetDir, "bob2", "4.9.2", CamelKitMain.distribution(), true, true, + InitProgress.noop(), InitReporter.noop())); assertEquals("4.9.2", result.citrusVersion()); String config = Files.readString(targetDir.resolve(".camel-kit/config.properties")); assertTrue(config.contains("citrus.version=4.9.2")); assertTrue(config.contains("citrus.mcp.version=" + EXPECTED_CITRUS_MCP_VERSION)); + assertTrue(config.contains("citrus.mcp.enabled=true")); String mcp = Files.readString(targetDir.resolve(".bob/mcp.json")); assertTrue(mcp.contains( "org.citrusframework:citrus-mcp-server:" + EXPECTED_CITRUS_MCP_VERSION + ":runner")); @@ -448,12 +472,14 @@ class InitServiceTest { DistributionConfig distribution = DistributionConfig.load(properties); InitResult result = new InitService().initialize( - request(targetDir, "bob2", "4.9.2", distribution, InitProgress.noop(), InitReporter.noop())); + request(targetDir, "bob2", "4.9.2", distribution, true, true, + InitProgress.noop(), InitReporter.noop())); assertEquals("4.9.2", result.citrusVersion()); String config = Files.readString(targetDir.resolve(".camel-kit/config.properties")); assertTrue(config.contains("citrus.version=4.9.2")); assertTrue(config.contains("citrus.mcp.version=4.10.1")); + assertTrue(config.contains("citrus.mcp.enabled=true")); String mcp = Files.readString(targetDir.resolve(".bob/mcp.json")); assertTrue(mcp.contains("org.citrusframework:citrus-mcp-server:4.10.1:runner")); assertFalse(mcp.contains("org.citrusframework:citrus-mcp-server:4.9.2:runner")); @@ -553,6 +579,7 @@ class InitServiceTest { targetDir, "default", true, + false, "mulesoft", "camel-kit", "5.0.1", @@ -578,6 +605,23 @@ class InitServiceTest { return entries; } + private InitRequest requestWithCitrusMcp(Path targetDir, String agentName) { + return new InitRequest( + "orders", + agentName, + targetDir, + "default", + true, + true, + "mulesoft", + "camel-kit", + "5.0.1", + CamelKitMain.distribution(), + Printer.noop(), + InitProgress.noop(), + InitReporter.noop()); + } + private InitRequest request( Path targetDir, String agentName, @@ -594,7 +638,18 @@ class InitServiceTest { DistributionConfig distribution, InitProgress progress, InitReporter reporter) { - return request(targetDir, agentName, citrusVersion, distribution, true, progress, reporter); + return request(targetDir, agentName, citrusVersion, distribution, true, false, progress, reporter); + } + + private InitRequest request( + Path targetDir, + String agentName, + String citrusVersion, + DistributionConfig distribution, + boolean noFetch, + InitProgress progress, + InitReporter reporter) { + return request(targetDir, agentName, citrusVersion, distribution, noFetch, false, progress, reporter); } private InitRequest request( @@ -603,6 +658,7 @@ class InitServiceTest { String citrusVersion, DistributionConfig distribution, boolean noFetch, + boolean withCitrusMcp, InitProgress progress, InitReporter reporter) { return new InitRequest( @@ -611,6 +667,7 @@ class InitServiceTest { targetDir, citrusVersion, noFetch, + withCitrusMcp, "mulesoft", "camel-kit", "5.0.1", diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/workflow/WorkflowManifestTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/workflow/WorkflowManifestTest.java index f9f168c3..3a5bd539 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/workflow/WorkflowManifestTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/workflow/WorkflowManifestTest.java @@ -15,6 +15,7 @@ import java.util.stream.Stream; import io.github.luigidemasi.camelkit.config.AgentConfig; import io.github.luigidemasi.camelkit.config.AgentRegistry; +import io.github.luigidemasi.camelkit.config.DistributionConfig; import io.github.luigidemasi.camelkit.generator.AgentGeneratorFactory; import io.github.luigidemasi.camelkit.generator.DefaultGenerator; import io.github.luigidemasi.camelkit.generator.InitContext; @@ -420,7 +421,8 @@ class WorkflowManifestTest { : skillsDir; return new InitContext( agent, agentName, commandsDir, skillsDir, tempDir, - "camel-kit", Printer.noop()); + "camel-kit", DistributionConfig.loadFromClasspathOrDefaults(), + true, Printer.noop()); } private static Path resourcePath(String resource) throws Exception {
