luigidemasi commented on code in PR #149:
URL: https://github.com/apache/camel-kit/pull/149#discussion_r4493066641
##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java:
##########
@@ -30,22 +45,164 @@ 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.
+
+ List<String> mcpServers = readMcpServerNames(ctx);
+ Set<String> seen = new LinkedHashSet<>();
+ boolean collisionDetected = false;
+ StringBuilder denials = new StringBuilder();
+ for (String server : mcpServers) {
+ 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);
+ }
+
+ 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 == '-';
+ }
+
+ private List<String> readMcpServerNames(InitContext ctx) throws
IOException {
+ Set<String> names = new LinkedHashSet<>();
+ 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);
+ }
+ String inlineContent = inlineConfigContent();
+ if (inlineContent != null && !inlineContent.isBlank()) {
+ collectMcpServersFromContent(inlineContent, names);
+ }
+ return List.copyOf(names);
+ }
+
+ static void collectMcpServersFromFile(Path configFile, Set<String> names)
throws IOException {
+ collectMcpServersFromNode(
+
OpenCodeProjectConfig.newJsonMapper().readTree(configFile.toFile()), names);
+ }
+
+ static void collectMcpServersFromContent(String jsonContent, Set<String>
names) throws IOException {
+ collectMcpServersFromNode(
+ OpenCodeProjectConfig.newJsonMapper().readTree(jsonContent),
names);
Review Comment:
[P1] Resolve config substitutions before deriving server denial names
Reading the raw JSON does not yield the names OpenCode actually registers.
For example, with `CK_REVIEW_MCP_NAME=probe` and an MCP entry named
`{env:CK_REVIEW_MCP_NAME}` in `OPENCODE_CONFIG_CONTENT`, this emits a denial
for the normalized placeholder rather than `probe_*`. OpenCode [substitutes
environment/file references before
parsing](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/config/variable.ts),
so its worker receives `probe.mark` instead. I reproduced actual child canary
writes with this inline env reference, an inline `{file:server-name.txt}`
reference containing `probe`, and an env reference in project config. All
inputs existed before generation; adding `probe_*: deny` blocked each matching
control.
Use the resolved host names for both glob generation and collision checks,
or refuse native generation for unresolved dynamic names. Cover substitutions
through both the file and inline paths; simply parsing them with the shared
raw-JSON extractor leaves the read-only MCP boundary bypassable.
_AI-generated by Codex on behalf of @luigidemasi._
##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/generator/OpenCodeGenerator.java:
##########
@@ -30,22 +45,164 @@ 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.
+
+ List<String> mcpServers = readMcpServerNames(ctx);
+ Set<String> seen = new LinkedHashSet<>();
+ boolean collisionDetected = false;
+ StringBuilder denials = new StringBuilder();
+ for (String server : mcpServers) {
+ 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);
+ }
+
+ 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 == '-';
+ }
+
+ private List<String> readMcpServerNames(InitContext ctx) throws
IOException {
+ Set<String> names = new LinkedHashSet<>();
+ 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);
+ }
+ String inlineContent = inlineConfigContent();
+ if (inlineContent != null && !inlineContent.isBlank()) {
+ collectMcpServersFromContent(inlineContent, names);
+ }
+ return List.copyOf(names);
Review Comment:
[P1] Include custom-directory and managed configuration in the MCP inventory
The source list still omits `OPENCODE_CONFIG_DIR/opencode.json[c]` and the
platform's managed config directory (`/etc/opencode` on Linux). Both are
supported [OpenCode config
sources](https://opencode.ai/docs/config/#locations). On OpenCode 2.2.5 I put
the same harmless `probe` MCP server exclusively in each source before **both
init generation and host startup**. In both cases the generated worker lacked
`probe_*: deny`, and the actual `camel-ship-worker` child invoked `probe.mark`
through `execute` and wrote its canary. Adding only that denial blocked each
call. The managed-path reproduction used a private mount namespace, leaving the
host's `/etc` untouched.
Please obtain the effective host MCP inventory, or decline native worker
generation when an active source cannot be bounded. Add generation/runtime
regression cases for the custom directory and platform managed directory; the
plain inline-config fix does not close these inherited-tool paths.
_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]