[ 
https://issues.apache.org/jira/browse/CAMEL-24352?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18105127#comment-18105127
 ] 

Karol Krawczyk commented on CAMEL-24352:
----------------------------------------

h3. Stdout hygiene: measured

The issue calls stdout hygiene "the actual work" and I said I would measure 
rather than assume.
Here are the numbers.

Setup: released Camel JBang 4.22.0, macOS, stdout redirected to a file (non-TTY 
— the pipe case an
MCP client creates). A single {{ai-tool}} route plus 
{{camel.server.mcp-enabled=true}}. Every run
below used a custom log4j2 config whose Console appender sets {{target = 
SYSTEM_ERR}}, passed via
{{--logging-config-path}} — i.e. the prospective {{log4j2-stdio.properties}} 
variant, simulated
without patching Camel.

h3. Baseline: how much leaks today

Plain {{camel run tools.yaml}}: *30 lines / 4698 bytes on stdout*, nothing from 
Camel on stderr.
All of it the log4j2 Console appender. Root cause confirmed in the source: none 
of the four
variants with a Console appender ({{log4j2.properties}}, {{-no-color}}, 
{{-json}}, {{-main}}) sets
{{appender.*.target}}, so log4j2 defaults to {{SYSTEM_OUT}}.

h3. With the Console appender on stderr

|| Scenario || stdout || note ||
| plain run | *0 B* | 30 log lines on stderr; MCP {{initialize}} returned 200 
with {{Mcp-Session-Id}} and {{tools.listChanged}} |
| broken YAML | *0 B* | 153 lines of diagnostics on stderr |
| missing file | *0 B* | 25 lines on stderr |
| {{--verbose}} | *0 B* | also with a real Maven download 
({{--dep=org.apache.camel:camel-chunk}}): {{Downloaded: ...}} goes through 
log4j2 |
| {{--console}} | *0 B* | dev console on /q/dev |
| {{--dev}} | *0 B* | over ~7 minutes including a live reload ({{Routes 
reloaded summary (total:1 started:1)}}) and shutdown |
| {{--background}} | *112 B* | leaks — see below |

So the logging half is settled: a {{log4j2-stdio.properties}} variant plus one 
branch in
{{RuntimeUtil.configureLog}} is sufficient, and the server stays fully 
functional with a silent stdout.

h3. What still writes to stdout

{{Printer.SystemOutPrinter}}, in two shapes:

* {{printErr(...)}} is a {{default}} method delegating to {{printf}}, so CLI 
error messages go to
  *stdout*, not stderr. Reproduced: {{camel run tools.yaml --source-dir=.}} 
prints
  {{ERROR: Cannot specify both file(s) and source-dir at the same time.}} (68 
B) on stdout.
* {{--background}} prints two lines on stdout from the parent process:
  {{Running Camel Main: tools in background with PID: nnnnn (waiting to 
startup)}} and
  {{Camel Main: tools (state: Running)}}. The same applies to {{camel stop 
<name>}}
  ({{Shutting down Camel integration (PID: nnnnn)}}).

So the stderr-backed {{Printer}} I mentioned earlier is not cosmetic, and 
{{printErr}} itself has to
be redefined rather than merely implemented.

h3. Correction to my earlier comment

I expected the honest answer might still require redirecting {{System.out}} as 
a backstop. The
measurement does not support that: across five run modes and two error paths, 
stdout was empty to
the byte once the Console appender targeted stderr. Two focused changes look 
sufficient —
the {{log4j2-stdio}} variant and a stderr-backed {{Printer}}/{{printErr}}.

h3. Two things this says about the design

* {{--background}} should be rejected in stdio mode. It leaks, and a background 
process has no pipe
  to the client anyway.
* {{--dev}} is compatible and worth keeping: an agent can hold the stdio 
session while the developer
  edits and reloads {{ai-tool}} routes. That is a nicer story than I expected 
going in.

Side note for anyone reproducing this: {{--dev}} ignores {{--max-seconds}} (the 
run went 6m57s until
killed instead of the requested 35s), and {{--background}} needs a {{camel}} 
binary on PATH because it
spawns the child through {{ProcessBuilder("camel")}} — a jbang alias is not 
enough.

The open question from my previous comment (whether the options live under 
{{camel.server.*}}, a new
{{camel.mcp.*}} namespace, or a JBang-only {{--mcp-stdio}} flag) is unaffected 
by any of this — all
three land the same way now that stdout hygiene is scoped.

_Reported by Claude Code on behalf of Karol Krawczyk_

> camel-jbang - Serve ai-tool routes over the MCP stdio transport
> ---------------------------------------------------------------
>
>                 Key: CAMEL-24352
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24352
>             Project: Camel
>          Issue Type: Improvement
>          Components: camel-ai, camel-jbang
>            Reporter: Federico Mariani
>            Priority: Major
>
> Follow-up to CAMEL-24308. Local coding agents and IDEs launch MCP servers as 
> child processes speaking JSON-RPC over stdin/stdout (the {{"command": ...}} 
> entry in an {{mcp.json}}). A developer should be able to register {{camel run 
> tools.yaml}} that way and have the tagged {{ai-tool}} routes served as local 
> tools — no port, no URL, process lifetime = session.
> The transport is nearly free: the MCP Java SDK ships 
> {{StdioServerTransportProvider}}, so this is a thin engine variant behind the 
> existing {{McpServerEngine}} SPI (e.g. a 
> {{camel.server.mcp-transport=http|stdio}} option or a JBang flag such as 
> {{--mcp-stdio}}), with the bridge unchanged.
> The actual work is stdout hygiene: in stdio mode stdout must carry only 
> protocol frames, so JBang needs to route all logging, the banner and 
> route-startup summaries to stderr (or a file) and suppress anything else 
> printed to stdout.
> Spring Boot and Quarkus already document their stdio setups via their native 
> MCP stacks (camel-mcp-server-starter / camel-quarkus-mcp-server usage docs); 
> this issue covers Camel Main / JBang.
> _Filed by Claude Code on behalf of Federico Mariani_



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to