[
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)