luigidemasi commented on code in PR #149:
URL: https://github.com/apache/camel-kit/pull/149#discussion_r4511466924


##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java:
##########
@@ -30,22 +49,220 @@ public void generate(InitContext ctx) throws Exception {
         Map<String, Object> data = new HashMap<>(
                 Map.of(
                         "COMMAND_PREFIX", ctx.commandPrefix()));
-        // AGENTS.md already generated by the shared generator flow.
+
+        McpInventory inventory = readMcpInventory(ctx);
+        Set<String> seen = new LinkedHashSet<>();
+        boolean collisionDetected = false;
+        StringBuilder denials = new StringBuilder();
+        for (String server : inventory.servers) {
+            if (SUBSTITUTION_PATTERN.matcher(server).find()) {
+                inventory = new McpInventory(inventory.servers, true);
+                continue;
+            }
+            String normalized = normalizeServerName(server);
+            if (BUILTIN_PERMISSION_PREFIXES.contains(normalized)) {
+                collisionDetected = true;
+            } else if (seen.add(normalized)) {
+                denials.append("  ").append(normalized).append("_*: deny\n");
+            }
+        }
+        data.put("MCP_TOOL_DENIALS", denials.toString());
 
         // OpenCode-specific: generate agent definitions
-        generateAgents(ctx, data);
+        generateAgents(ctx, data, !collisionDetected && 
!inventory.hasUnresolvedSubstitution);
+    }
+
+    static String normalizeServerName(String name) {
+        StringBuilder sb = new StringBuilder(name.length());
+        for (int i = 0; i < name.length(); i++) {
+            char c = name.charAt(i);
+            sb.append(isAsciiAlphanumericOrHyphen(c) ? c : '_');
+        }
+        return sb.toString();
+    }
+
+    private static boolean isAsciiAlphanumericOrHyphen(char c) {
+        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' 
&& c <= '9') || c == '-';
+    }
+
+    record McpInventory(List<String> servers, boolean 
hasUnresolvedSubstitution) {
+    }
+
+    private McpInventory readMcpInventory(InitContext ctx) throws IOException {
+        Set<String> names = new LinkedHashSet<>();
+        boolean hasSubstitution = false;
+        for (Path configFile : 
OpenCodeProjectConfig.existingFiles(ctx.projectDir())) {
+            hasSubstitution |= 
collectFromFileWithSubstitutionCheck(configFile, names);
+        }
+        for (Path ancestorFile : ancestorConfigFiles(ctx.projectDir())) {
+            hasSubstitution |= 
collectFromFileWithSubstitutionCheck(ancestorFile, names);
+        }
+        for (Path globalFile : globalConfigFiles()) {
+            hasSubstitution |= 
collectFromFileWithSubstitutionCheck(globalFile, names);
+        }
+        hasSubstitution |= 
collectFromDirWithSubstitutionCheck(customConfigDir(), names);
+        hasSubstitution |= 
collectFromDirWithSubstitutionCheck(managedConfigDir(), names);
+        Path explicitConfig = explicitConfigFile();
+        if (explicitConfig != null) {
+            hasSubstitution |= 
collectFromFileWithSubstitutionCheck(explicitConfig, names);
+        }
+        String inlineContent = inlineConfigContent();
+        if (inlineContent != null && !inlineContent.isBlank()) {
+            if (SUBSTITUTION_PATTERN.matcher(inlineContent).find()) {
+                hasSubstitution = true;
+            }
+            collectMcpServersFromContent(inlineContent, names);
+        }
+        return new McpInventory(List.copyOf(names), hasSubstitution);
+    }
+
+    private static boolean collectFromFileWithSubstitutionCheck(Path 
configFile, Set<String> names)
+            throws IOException {
+        String rawContent = Files.readString(configFile);
+        if (rawContent.startsWith("\uFEFF")) {
+            rawContent = rawContent.substring(1);
+        }
+        boolean hasSubstitution = 
SUBSTITUTION_PATTERN.matcher(rawContent).find();
+        collectMcpServersFromContent(rawContent, names);

Review Comment:
   [P1] Invalidate the old worker before raw-config parsing can fail
   
   The raw scan detects a substitution, but this parser call can throw before 
`generateAgents(..., false)` removes an existing worker. OpenCode [substitutes 
before 
parsing](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/config/variable.ts),
 so `{"mcp":{env:MCP_SERVERS}}` is valid for the host when `MCP_SERVERS` 
contains an MCP-server object, even though the raw text is not JSON.
   
   I initialized a workspace normally, then supplied this config before 
reinitialization, separately through `OPENCODE_CONFIG_CONTENT` and 
`OPENCODE_CONFIG`. The actual CLI returns 1 and leaves the old worker 
byte-for-byte unchanged without `probe_*: deny`. In both corresponding OpenCode 
2.2.5 runs, the real worker child calls `probe.mark` through `execute` and 
writes the harmless canary; the matching namespace-denial controls block it.
   
   Make an unsafe or failed inventory invalidate the installed worker before 
parsing can abort, and skip raw parsing when the chosen policy already requires 
refusing native generation. Cover reinitialization with substitutions that 
produce JSON structure, including the error path.
   
   _AI-generated by Codex on behalf of @luigidemasi._



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to