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 d94ec8f4f95a5be0cb46be84b135ea270b8f2ea8
Author: Jinal Sarvaiya <[email protected]>
AuthorDate: Fri Nov 6 07:43:53 2026 +0000

    fix: ancestor/explicit config enumeration, native type-named server
---
 .../camel/kit/generator/OpenCodeGenerator.java     | 47 +++++++++++++++++++-
 .../camel/kit/generator/OpenCodeGeneratorTest.java | 51 ++++++++++++++++++++++
 2 files changed, 96 insertions(+), 2 deletions(-)

diff --git 
a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java
 
b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java
index 3f3f2743..32943b3c 100644
--- 
a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java
+++ 
b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java
@@ -26,6 +26,10 @@ public class OpenCodeGenerator extends DefaultGenerator {
 
     private static final List<String> GLOBAL_CONFIG_FILES = 
List.of("opencode.json", "opencode.jsonc");
 
+    private static final List<String> ANCESTOR_CONFIG_FILES = List.of(
+            "opencode.json", "opencode.jsonc",
+            ".opencode/opencode.json", ".opencode/opencode.jsonc");
+
     private final QuteTemplateEngine templateEngine = new QuteTemplateEngine();
 
     @Override
@@ -78,13 +82,20 @@ public class OpenCodeGenerator extends DefaultGenerator {
         for (Path configFile : 
OpenCodeProjectConfig.existingFiles(ctx.projectDir())) {
             collectMcpServersFromFile(configFile, names);
         }
+        for (Path ancestorFile : ancestorConfigFiles(ctx.projectDir())) {
+            collectMcpServersFromFile(ancestorFile, names);
+        }
         for (Path globalFile : globalConfigFiles()) {
             collectMcpServersFromFile(globalFile, names);
         }
+        Path explicitConfig = explicitConfigFile();
+        if (explicitConfig != null) {
+            collectMcpServersFromFile(explicitConfig, names);
+        }
         return List.copyOf(names);
     }
 
-    private static void collectMcpServersFromFile(Path configFile, Set<String> 
names) throws IOException {
+    static void collectMcpServersFromFile(Path configFile, Set<String> names) 
throws IOException {
         JsonNode config = 
OpenCodeProjectConfig.newJsonMapper().readTree(configFile.toFile());
         if (config == null) {
             return;
@@ -94,7 +105,7 @@ public class OpenCodeGenerator extends DefaultGenerator {
             return;
         }
         JsonNode servers = mcp.path("servers");
-        boolean hasNativeContainer = servers.isObject() && 
!servers.has("type");
+        boolean hasNativeContainer = servers.isObject() && 
isNativeServersContainer(servers);
         if (hasNativeContainer) {
             servers.fields().forEachRemaining(entry -> {
                 if (entry.getValue().isObject()) {
@@ -110,6 +121,27 @@ public class OpenCodeGenerator extends DefaultGenerator {
         });
     }
 
+    private static boolean isNativeServersContainer(JsonNode servers) {
+        JsonNode type = servers.path("type");
+        return !type.isTextual();
+    }
+
+    static List<Path> ancestorConfigFiles(Path projectDir) throws IOException {
+        Path realProject = projectDir.toRealPath();
+        Path parent = realProject.getParent();
+        List<Path> result = new java.util.ArrayList<>();
+        while (parent != null) {
+            for (String name : ANCESTOR_CONFIG_FILES) {
+                Path candidate = parent.resolve(name);
+                if (Files.exists(candidate)) {
+                    result.add(candidate);
+                }
+            }
+            parent = parent.getParent();
+        }
+        return result;
+    }
+
     static List<Path> globalConfigFiles() {
         Path globalDir = globalConfigDir();
         return GLOBAL_CONFIG_FILES.stream()
@@ -126,6 +158,17 @@ public class OpenCodeGenerator extends DefaultGenerator {
         return Path.of(System.getProperty("user.home"), ".config", "opencode");
     }
 
+    static Path explicitConfigFile() {
+        String explicitPath = System.getenv("OPENCODE_CONFIG");
+        if (explicitPath != null && !explicitPath.isBlank()) {
+            Path path = Path.of(explicitPath);
+            if (Files.exists(path)) {
+                return path;
+            }
+        }
+        return null;
+    }
+
     private void generateAgents(InitContext ctx, Map<String, Object> data, 
boolean includeShipWorker)
             throws Exception {
         Path agentsDir = ctx.projectDir().resolve(".opencode/agents");
diff --git 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java
 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java
index f5514d1c..b15fd00c 100644
--- 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java
+++ 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/OpenCodeGeneratorTest.java
@@ -1156,4 +1156,55 @@ class OpenCodeGeneratorTest {
         assertEquals("__", OpenCodeGenerator.normalizeServerName("üñ"),
                 "All non-ASCII characters must be replaced");
     }
+
+    @Test
+    void shipWorkerDeniesServersFromAncestorConfig() throws Exception {
+        InitContext ctx = createContext();
+
+        Path ancestorDir = tempDir.getParent();
+        Path ancestorConfig = ancestorDir.resolve("opencode.json");
+        Files.writeString(ancestorConfig, """
+                {
+                  "mcp": {
+                    "ancestor-probe": {"type": "local", "command": ["echo"], 
"enabled": true}
+                  }
+                }
+                """);
+
+        try {
+            new OpenCodeGenerator().generate(ctx);
+
+            String worker = Files.readString(
+                    tempDir.resolve(".opencode/agents/camel-ship-worker.md"));
+            assertTrue(worker.contains("ancestor-probe_*: deny"),
+                    "Ship worker must deny MCP servers inherited from ancestor 
config");
+        } finally {
+            Files.deleteIfExists(ancestorConfig);
+        }
+    }
+
+    @Test
+    void shipWorkerDeniesNativeFormatServerNamedType() throws Exception {
+        InitContext ctx = createContext();
+
+        Files.writeString(tempDir.resolve("opencode.json"), """
+                {
+                  "mcp": {
+                    "servers": {
+                      "type": {"type": "local", "command": ["echo"], 
"enabled": true},
+                      "probe": {"type": "local", "command": ["echo"], 
"enabled": true}
+                    }
+                  }
+                }
+                """);
+
+        new OpenCodeGenerator().generate(ctx);
+
+        String worker = Files.readString(
+                tempDir.resolve(".opencode/agents/camel-ship-worker.md"));
+        assertTrue(worker.contains("type_*: deny"),
+                "Ship worker must deny a native server named 'type'");
+        assertTrue(worker.contains("probe_*: deny"),
+                "Ship worker must deny other servers in the same native 
container");
+    }
 }

Reply via email to