Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,11 @@ eight seconds for the reload of the written file and answers with its outcome (`
with the cause and the validator's report, properties, or unknown), so an agent does not go on with a route that
did not load. A write with no selected integration answers as before.

The `camel_run` tool, when no files are named, starts the project with `camel run --source-dir=.` instead of
listing the directory's files: the directory is watched, so a file added afterwards (a bean file, a Java class
under `src/main/java`) is part of the app and reloaded in dev mode, and a `restart` starts the same way. Naming
files keeps the previous behaviour. The `camel_control` tool gets a `reload` action, what `camel cmd reload` does.

In the TUI MCP server the tools `tui_catalog_doc`, `tui_validate_source`, `tui_write_file`, `tui_get_files`,
`tui_control`, `tui_get_log`, `tui_get_errors` and `tui_eval_expression` were renamed to the `camel_` names
above; the `tui_` prefix is now reserved for the tools that only make sense in front of the screen. Update
Expand Down
7 changes: 5 additions & 2 deletions docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,14 @@ project `directory` as an argument, the runtime tools take the integration `name
`read-only` access level of the security layer hides the tool altogether.

| `camel_run`
| Starts an integration from a project directory with `camel run` in a separate process, in dev mode by
default so route files reload when written. Returns the pid, name and log file once the integration is up.
| Starts an integration from a project directory with `camel run --source-dir` in a separate process, in dev
mode by default: the directory is watched, so a changed or added file (a route, a bean file, a Java class) is
reloaded. Name files to run only those, for a directory that holds several apps. Returns the pid, name and
log file once the integration is up.

| `camel_control`
| Controls a running integration: `stop`, `kill`, `restart` (picks up edited files without dev mode),
`reload` (loads the routes again from their files without a restart, as `camel cmd reload` does),
`stop-routes`, `start-routes`, `reset-stats`.

| `camel_get_log`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,9 @@ static void register(Consumer<ToolDescriptor> registry) {
}));

registry.accept(tool("camel_run",
"Starts an integration with camel run in a separate process, in dev mode by default (files reload when written). Returns the pid and log file; camel_get_log, camel_get_errors and camel_control follow it.")
"Starts an integration with camel run in a separate process, in dev mode by default (a changed or added file is reloaded). Returns the pid and log file; camel_get_log and camel_control follow it.")
.param("directory", "string", "Project directory to run in", true)
.param("files", "string", "Source files to run, comma-separated (default: every route file in the"
+ " directory)",
.param("files", "string", "Source files to run, comma-separated (default: the whole directory)",
false)
.param("name", "string", "Integration name (default: from the first file)", false)
.param("dev", "boolean", "Dev mode with reload on file change (default true)", false)
Expand All @@ -247,9 +246,11 @@ static void register(Consumer<ToolDescriptor> registry) {

registry.accept(tool("camel_control",
"Controls a running integration: stop (graceful), kill, restart (picks up edited files without dev "
+ "mode), stop-routes, start-routes, reset-stats (clears statistics, routes "
+ "untouched). Never stop, kill or restart unless the user asked for it.")
.param("action", "string", "stop, kill, restart, stop-routes, start-routes or reset-stats", true)
+ "mode), reload (loads the routes again without a restart, e.g. after a changed "
+ "stylesheet), stop-routes, start-routes, reset-stats (clears statistics). "
+ "Never stop, kill or restart unless the "
+ "user asked for it.")
.param("action", "string", "stop, kill, restart, reload, stop-routes, start-routes or reset-stats", true)
.param("name", "string", NAME_DESC, false)
.readOnly(false)
.destructive(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,15 @@ private IntegrationLauncher() {
*/
public static JsonObject run(Path directory, List<String> files, String name, boolean dev, List<String> extraArgs) {
List<String> cmd = new ArrayList<>(LauncherHelper.getCamelCommand());
List<String> sources = files == null || files.isEmpty() ? sourceFiles(directory) : files;
cmd.addAll(runArguments(sources, name, dev, extraArgs));
// no files given: the whole directory is the app (camel run --source-dir), so a file the agent adds later,
// a bean file, a Java class under src/main/java, is part of it and reloaded in dev mode (CAMEL-24861);
// with files given only those run, for a directory that holds several apps
boolean sourceDir = files == null || files.isEmpty();
List<String> sources = sourceDir ? sourceFiles(directory) : files;
cmd.addAll(sourceDir ? sourceDirArguments(name, dev, extraArgs) : runArguments(sources, name, dev, extraArgs));
JsonObject result = new JsonObject();
if (sources.isEmpty()) {
// with --source-dir the guard looks at the top level only; a class under src/main/java is an app too
if (!sourceDir && sources.isEmpty()) {
result.put("directory", directory.toString());
Comment thread
davsclaus marked this conversation as resolved.
result.put("status", "failed");
result.put("error", "No source files to run in " + directory
Expand Down Expand Up @@ -105,9 +110,13 @@ public static JsonObject run(Path directory, List<String> files, String name, bo
result.put("name", started);
result.put("log", LogFileReader.logFile(pid, info.name()).toString());
result.put("devMode", dev);
result.put("sourceDir", sourceDir);
result.put("message", "Started " + started + " (pid " + pid + ")"
+ (dev
? "; dev mode reloads the routes when a source file changes"
? sourceDir
? "; dev mode watches the directory: a changed or added file is"
+ " reloaded"
: "; dev mode reloads the routes when a source file changes"
: "; restart it after changing a source file")
+ ". camel_get_log reads its log, camel_get_errors its failed exchanges.");
return result;
Expand Down Expand Up @@ -156,6 +165,24 @@ static List<String> sourceFiles(Path directory) {
return names;
}

/** The {@code camel run --source-dir=.} arguments: the directory the process starts in is the app. */
static List<String> sourceDirArguments(String name, boolean dev, List<String> extraArgs) {
Comment thread
davsclaus marked this conversation as resolved.
List<String> cmd = new ArrayList<>();
cmd.add("run");
cmd.add("--source-dir=.");
if (dev) {
cmd.add("--dev");
}
if (name != null && !name.isBlank()) {
cmd.add("--name=" + name);
}
cmd.add("--logging-color=false");
if (extraArgs != null) {
cmd.addAll(extraArgs);
}
return cmd;
}

/**
* The {@code camel run} arguments for the given files, name and mode.
*
Expand Down Expand Up @@ -204,7 +231,7 @@ private static String readTail(Path output) {
* Controls a running integration.
*
* @param ctx the context with the selected process
* @param action stop (graceful), kill, restart, stop-routes, start-routes or reset-stats
* @param action stop (graceful), kill, restart, reload, stop-routes, start-routes or reset-stats
* @return what was done
*/
public static String control(ToolContext ctx, String action) {
Expand Down Expand Up @@ -233,8 +260,15 @@ public static String control(ToolContext ctx, String action) {
ctx.executeAction("reset-stats", null);
yield "Statistics reset for pid " + pid;
}
case "reload" -> {
// what camel cmd reload does: the routes are loaded again from their files without a restart, so
// a changed stylesheet or a dropped data file takes effect, and a file consumed once is read again
ctx.executeAction("reload", null);
yield "Reload triggered for pid " + pid + "; camel_get_log shows the routes reloaded summary";
}
default -> throw new ToolExecutionException(
"Unknown action: " + action + ". Use stop, kill, restart, stop-routes, start-routes or reset-stats");
"Unknown action: " + action + ". Use stop, kill, restart, reload, stop-routes, start-routes or"
+ " reset-stats");
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ void noFilesRunsEverySourceFileInTheDirectory(@TempDir Path dir) throws Exceptio
assertThat(IntegrationLauncher.sourceFiles(dir.resolve("nope"))).isEmpty();
}

/** CAMEL-24861: with no files given the directory is the app, so files added later are part of it. */
@Test
void noFilesRunsTheDirectoryAsTheApp() {
assertThat(IntegrationLauncher.sourceDirArguments(null, true, null))
.containsExactly("run", "--source-dir=.", "--dev", "--logging-color=false");
assertThat(IntegrationLauncher.sourceDirArguments("demo", false, List.of("--port=9000")))
.containsExactly("run", "--source-dir=.", "--name=demo", "--logging-color=false", "--port=9000");
}

@Test
void filesNameAndExtraArgumentsArePassedThrough() {
assertThat(IntegrationLauncher.runArguments(List.of("a.camel.yaml", "application.properties"), "demo", true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ class AiPanelPromptBudgetTest {
// and from 8500 when camel_catalog_doc gained the api kind (CAMEL-24708): its kind argument names the core
// classes and script languages the API reference covers, which is what makes a model ask for them
// raised with the core budget (CAMEL-24760)
static final int FULL_BUDGET_TOKENS = 9_200;
// raised from 9200 when camel_control gained the reload action (CAMEL-24861): main was ~9180 already; the
// camel_run and camel_control descriptions were shortened in the same change
static final int FULL_BUDGET_TOKENS = 9_300;

record Prefix(String mode, int tools, long promptChars, long toolChars) {

Expand Down
Loading