This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-25672-mcp-instructions
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 96bfa937421dfd0e996c1035a3839eb2fd7fc51a
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 12 18:03:22 2026 +0100

    CAMEL-25672: camel-jbang-mcp - server instructions for everyday work; 
migration guidance in a prompt and a tool
    
    The server instructions came from an evaluation of agents migrating legacy
    integrations to Camel, but most users work on an existing Camel project
    every day. The default text now serves that work: what the server is for,
    the rules that cost a model most when it writes routes (EIP steps, one
    when list in a choice, top-level onException, variables, Simple
    operators, reading the answer of a write), the daily tools (files, run,
    log, errors, eval), and one line that points to the migration guidance.
    
    The migration rules (smtp not mail, mail headers, Camel* headers before
    an http or mail call, xslt parameters, port and dependencies of camel run)
    and a workflow are now in the camel_migrate_to_camel prompt, and in the
    camel_migration_instructions tool for clients without prompts.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../modules/ROOT/pages/camel-jbang-mcp.adoc        |  60 +++++++---
 .../core/commands/mcp/McpServerInstructions.java   | 129 ++++++++++++++++-----
 .../jbang/core/commands/mcp/MigrationTools.java    |  13 +++
 .../jbang/core/commands/mcp/PromptDefinitions.java |  22 ++++
 .../commands/mcp/McpServerInstructionsTest.java    |  93 +++++++++++++--
 .../core/commands/mcp/MigrationToolsTest.java      |   7 ++
 .../core/commands/mcp/PromptDefinitionsTest.java   |  19 +++
 7 files changed, 289 insertions(+), 54 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
index 2fb95d1b16f0..7b32e9f4439e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -94,26 +94,38 @@ IMPORTANT: The MCP server does **not** expose REST 
endpoints — all communicati
 == Server instructions
 
 The `initialize` response carries `instructions`: a short plain-text guide 
(under 4096 characters) that the agent
-host gives the model at session start, before any tool call and before any 
tool description is loaded. It answers the
-questions that no tool answers and that agents otherwise go hunting for:
-
-* The first line is a one-sentence summary with the Camel version of the 
server: look components up by URI scheme
-  and validate with `camel_validate_source` before finishing.
-* The rules: components by URI scheme (`smtp`, not `mail`; `platform-http` for 
an HTTP server); `onException`,
-  `errorHandler`, `routeConfiguration`, `intercept` and `onCompletion` are 
top-level entries, not steps, and
-  `handled`/`continued` are expressions; under `camel run`, 
`camel.server.port` (or `--port`) sets the HTTP port, and
-  extra jars go in a `//DEPS` line, `camel.jbang.dependencies` in 
`application.properties`, or `--dep` (the
-  `camel_run` tool has no dependencies argument); the mail Subject, From and 
To headers are ignored unless
-  `useHeaderSubject`, `useHeaderFrom` and `useHeaderRecipients` are `true`; 
remove the `Camel*` headers you did not
-  set yourself before an http or mail producer; `xslt` gets the headers and 
variables as stylesheet parameters; a
-  source that passes the schema can still fail at runtime, so run it and read 
the log.
+host gives the model at session start, before any tool call and before any 
tool description is loaded. It serves
+everyday work on a Camel project (building, running and fixing its routes) and 
answers the questions that no tool
+answers and that agents otherwise go hunting for:
+
+* The first line is a one-sentence summary with the Camel version of the 
server: build, run and fix the routes of a
+  Camel project, change its files with `camel_edit_file`, run it with 
`camel_run`, and read what happened with
+  `camel_get_log`.
+* The rules for writing routes in the YAML DSL, the mistakes that cost a model 
most: the steps of an EIP go under its
+  `steps`; a `choice` has one `when` list; `onException`, `errorHandler`, 
`routeConfiguration`, `intercept` and
+  `onCompletion` are top-level entries, not steps, and `handled`/`continued` 
are expressions; a value the route keeps
+  for itself goes in a variable, not an exchange property or a header; Simple 
operators go between functions, not
+  inside them; components are named by URI scheme (`platform-http` for an HTTP 
server); and after a write, its
+  answer has the reload and what the routes logged after it.
+* The daily tools: `camel_get_files`, `camel_edit_file` and `camel_write_file` 
to read and change the files,
+  `camel_run` and `camel_control` to run and stop, `camel_get_log` and 
`camel_get_errors` for what happened,
+  `camel_eval_expression`, `camel_validate_source` and 
`camel_project_overview`.
 * Which tool answers which question, by tool name: `camel_catalog_doc`, 
`camel_catalog_find`,
-  `camel_catalog_sample`, `camel_catalog_examples`, 
`camel_dependency_for_class`, `camel_validate_source`,
-  `camel_run`, `camel_get_log`, `camel_get_errors` and 
`camel_project_overview`; and that a Citrus test runs with
+  `camel_catalog_sample`, `camel_catalog_examples` and 
`camel_dependency_for_class`; and that a Citrus test runs with
   `camel test run` in the project directory, not with `camel_run`.
+* The last line points to the migration guidance: the `camel_migrate_to_camel` 
and `camel_migrate_project` prompts,
+  and the `camel_migration_instructions` tool.
 
 The first line is self-contained, as some hosts show only that line.
 
+The rules of a migration to Camel are not in this text, as a migration is a 
one-time task: the `camel_migrate_to_camel`
+prompt and the `camel_migration_instructions` tool carry them when a migration 
starts. They cover components by URI
+scheme (`smtp`, not `mail`); the mail Subject, From and To headers, which are 
ignored unless `useHeaderSubject`,
+`useHeaderFrom` and `useHeaderRecipients` are `true`; removing the `Camel*` 
headers you did not set yourself before
+an http or mail producer; `xslt`, which gets the headers and variables as 
stylesheet parameters; the HTTP port
+(`camel.server.port` or `--port`) and the extra jars (`//DEPS`, 
`camel.jbang.dependencies` or `--dep`) of
+`camel run`; and running the app before calling it done. A workflow follows 
the rules.
+
 To replace the text, set `quarkus.mcp.server.server-info.instructions`, for 
example
 `-Dquarkus.mcp.server.server-info.instructions="..."` with `java -jar`. When 
that property is set, the server sends
 its value instead of the built-in text.
@@ -256,6 +268,8 @@ The assistant uses `camel_error_diagnose` to identify the 
exception chain, extra
 * _"What changed with direct-vm in Camel 4?"_ — uses 
`camel_migration_guide_search`
 * _"Migrate from WildFly to Quarkus"_ — uses `camel_migration_wildfly_karaf` 
for archetype commands and steps
 * Use the `camel_migrate_project` prompt for an orchestrated multi-step 
workflow
+* _"Migrate this Spring Integration application to Camel"_ — use the 
`camel_migrate_to_camel` prompt, or the agent
+  calls `camel_migration_instructions` for the rules and workflow of a 
migration to Camel
 
 === OpenAPI contract-first
 
@@ -282,6 +296,8 @@ MCP clients that support prompts expose these as selectable 
workflows:
 
 * `camel_build_integration` — 7-step guided workflow: discover components → 
select EIPs → build → validate → security review
 * `camel_migrate_project` — 6-step migration: analyze → compatibility → 
recipes → guide search → summary
+* `camel_migrate_to_camel` — migration of an integration to Camel: the rules a 
migration gets wrong most, then
+  list → find components → write → run and compare
 * `camel_security_review` — 3-step audit: analyze vulnerabilities → understand 
data flow → produce checklist
 
 == Available Tools
@@ -847,6 +863,11 @@ help validate and scaffold that workflow, following the 
rules of the `rest-opena
 |===
 | Tool | Description
 
+| `camel_migration_instructions`
+| The rules and workflow of a migration of an integration to Camel, from 
another product or from hand-written code:
+  the same text as the `camel_migrate_to_camel` prompt, for clients that use 
tools but no prompts. An agent calls it
+  once, at the start of a migration.
+
 | `camel_migration_analyze`
 | Analyzes a Camel project's `pom.xml` to detect the runtime type (`main`, 
`spring-boot`, `quarkus`, `wildfly`,
   `karaf`), Camel version, Java version, and Camel component dependencies. 
This is the first step in a migration
@@ -1286,6 +1307,15 @@ instructions and executes each step by calling the 
referenced tools.
   OpenRewrite migration recipes, search migration guides for breaking changes 
per component, and produce a
   structured migration summary with blockers, breaking changes, commands, and 
manual steps.
 
+| `camel_migrate_to_camel`
+| `source` (optional)
+| Migration of an integration to Camel, from another product or from 
hand-written code. Carries the rules a
+  migration gets wrong most (components by URI scheme, the mail headers, the 
`Camel*` headers before an http or mail
+  producer, `xslt` parameters, the port and dependencies of `camel run`) and a 
workflow: list what the integration
+  does, find the components and their options, write the routes, then run them 
and compare the results with the old
+  integration. The `camel_migration_instructions` tool returns the same text. 
The server instructions leave these
+  rules out, as they serve everyday work.
+
 | `camel_security_review`
 | `route` (required), `format` (optional)
 | Guided workflow to perform a security audit of a Camel route. Walks through 
three steps: analyze the route
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructions.java
index 763d114a306f..2e9fe2c7f60c 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructions.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructions.java
@@ -26,14 +26,20 @@ import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 /**
- * The {@code instructions} of the MCP {@code initialize} response: the rules 
an agent needs before its first tool call,
- * and which tool answers which question.
+ * The {@code instructions} of the MCP {@code initialize} response: what the 
server is for, the rules an agent needs
+ * before its first tool call, and which tool answers which question.
  * <p>
  * Hosts that defer tool schemas (Claude Code, Codex, OpenCode, Pi) give the 
model this text at session start, before
  * any tool description. Pi shows only the first line and Codex weighs the 
first 512 characters most, so the first line
  * is a one-sentence summary with the Camel version. The whole text stays 
under 4096 characters (what Claude Code
  * loads).
  * <p>
+ * The text serves everyday work on a Camel project: building, running and 
fixing its routes (CAMEL-25672). The rules
+ * are the mistakes that cost a model most when it writes routes, from a 
local-model benchmark. The rules of a migration
+ * to Camel are a separate text ({@link #migration(String)}), sent by the 
{@code camel_migrate_to_camel} prompt and the
+ * {@code camel_migration_instructions} tool when a migration starts: a 
migration is a one-time task, and its rules
+ * (mail headers, xslt parameters) do not help with the daily work.
+ * <p>
  * A value set in {@code quarkus.mcp.server.server-info.instructions} replaces 
this text: Quarkus asks the
  * {@link InitialResponseInfo} beans before it reads the property, so this 
bean steps aside when the property is set.
  */
@@ -45,46 +51,102 @@ public class McpServerInstructions implements 
InitialResponseInfo {
 
     /** The tool names the text maps questions to; a test checks that each one 
is an {@code @Tool} method. */
     static final List<String> MAPPED_TOOLS = List.of(
+            "camel_get_files", "camel_edit_file", "camel_write_file", 
"camel_run", "camel_control", "camel_get_log",
+            "camel_get_errors", "camel_eval_expression", 
"camel_validate_source", "camel_project_overview",
             "camel_catalog_doc", "camel_catalog_find", "camel_catalog_sample", 
"camel_catalog_examples",
-            "camel_dependency_for_class", "camel_validate_source", 
"camel_run", "camel_get_log", "camel_get_errors",
-            "camel_project_overview");
+            "camel_dependency_for_class", "camel_migration_instructions");
+
+    /** The prompt names the text points to; a test checks that each one is a 
{@code @Prompt} method. */
+    static final List<String> MAPPED_PROMPTS = 
List.of("camel_migrate_to_camel", "camel_migrate_project");
 
     /** The text, with the Camel version as its only parameter. Kept here so 
the whole text reviews in one place. */
     private static final String TEMPLATE = """
-            Apache Camel %s catalog, validation and run tools: look components 
up by URI scheme (smtp, \
-            platform-http) and validate with camel_validate_source before 
finishing.
+            Apache Camel %s tools to build, run and fix the routes of a Camel 
project: change its files with \
+            camel_edit_file, run it with camel_run, and read what happened 
with camel_get_log.
+
+            Rules for writing routes (YAML DSL):
+            - The steps of an EIP (split, filter, loop, doTry, ...) go under 
its steps, not after it: \
+            split: {expression: {simple: {expression: "${body}"}}, steps: 
[...]}.
+            - A choice has one when list, each entry with its expression and 
steps, and an optional \
+            otherwise: {steps: [...]}.
+            - onException, errorHandler, routeConfiguration, intercept and 
onCompletion are top-level entries beside \
+            route (or inside routeConfiguration), never steps. handled and 
continued are expressions: \
+            handled: {constant: {expression: "true"}}.
+            - A value the route keeps for itself goes in a variable 
(setVariable, ${variable.name}), not an exchange \
+            property, and not a header: headers go out with the message.
+            - Simple: functions inside ${...}, operators between them: 
${header.a} == 'b', not ${header.a == 'b'}.
+            - Components are named by URI scheme: platform-http for an HTTP 
server, http/https for an HTTP client. \
+            camel_catalog_find maps a protocol or product to the scheme.
+            - After a write, read its answer: it has the reload of the running 
integration and what the routes \
+            logged after it. Passing the schema is not passing at runtime.
+
+            Daily work:
+            - the files of a project: camel_get_files; change one: 
camel_edit_file (several places at once with \
+            edits); a new file: camel_write_file. YAML and properties are 
validated before they are written.
+            - run an integration: camel_run (dev mode reloads each write); 
stop or restart: camel_control, only when \
+            the user asks
+            - what happened: camel_get_log (retries are logged at WARN); the 
exchanges that failed: camel_get_errors
+            - check an expression on a sample message: camel_eval_expression
+            - validate a source without writing it: camel_validate_source
+            - routes, endpoints and mistakes of an existing project: 
camel_project_overview
+
+            Which tool:
+            - options, URI syntax, headers of a component, EIP, data format or 
language: camel_catalog_doc
+            - which component for a protocol or product: camel_catalog_find
+            - a validated YAML sample and where it goes: camel_catalog_sample
+            - complete example projects: camel_catalog_examples
+            - a sample Citrus test (test/<name>.citrus.it.yaml, run with camel 
test run): the test/ file of an \
+            example, camel_catalog_examples with filter citrus
+            - Maven coordinates of a Java class: camel_dependency_for_class
+            - run a Citrus test (test/<name>.citrus.it.yaml): camel test run 
<file> in the project directory, not \
+            camel_run
+
+            Migrating to Camel, or between Camel versions? Use the 
camel_migrate_to_camel or camel_migrate_project \
+            prompt, or call camel_migration_instructions.
+            """;
+
+    /** The tool names the migration text points to; a test checks that each 
one is an {@code @Tool} method. */
+    static final List<String> MIGRATION_TOOLS = List.of(
+            "camel_catalog_find", "camel_catalog_doc", "camel_catalog_sample", 
"camel_dependency_for_class",
+            "camel_validate_source", "camel_write_file", "camel_run", 
"camel_get_log", "camel_get_errors",
+            "camel_runtime_send", "camel_migration_analyze");
+
+    /**
+     * The rules of a migration of an integration to Camel, with the Camel 
version as its only parameter: the rules an
+     * evaluation of agents migrating legacy integrations to Camel found 
(CAMEL-25646).
+     */
+    private static final String MIGRATION_TEMPLATE = """
+            Migrating an integration to Apache Camel %s, from another product 
or from hand-written code. The rules \
+            that migrations get wrong most, then the workflow.
 
             Rules:
             - Components are named by URI scheme: smtp/smtps/imap/pop3 (not 
mail), platform-http for an HTTP server, \
             http/https for an HTTP client. camel_catalog_find maps a protocol 
or product to the scheme.
-            - YAML DSL: onException, errorHandler, routeConfiguration, 
intercept and onCompletion are top-level \
-            entries beside route (or inside routeConfiguration), never steps. 
handled and continued are \
-            expressions: handled: {constant: {expression: "true"}}.
-            - camel run: camel.server.port (or camel run --port) sets the HTTP 
port. Extra jars: a //DEPS line in a \
-            source file, camel.jbang.dependencies=group:artifact:version in 
application.properties, or --dep on the \
-            command line (camel_run has no dependencies argument, so use one 
of the first two).
             - mail producer: the Subject, From and To headers are ignored 
unless useHeaderSubject=true, \
             useHeaderFrom=true and useHeaderRecipients=true; otherwise use the 
subject, from and to options.
             - Before an http or mail producer, remove the Camel* headers you 
did not set yourself \
             (removeHeaders: {pattern: "Camel*"} when none are yours), so 
inbound CamelHttpPath, CamelHttpQuery or \
             CamelHttpUri do not redirect the call and no header leaks into the 
mail.
             - xslt receives the message headers and variables as stylesheet 
parameters (xsl:param).
+            - camel run: camel.server.port (or camel run --port) sets the HTTP 
port. Extra jars: a //DEPS line in a \
+            source file, camel.jbang.dependencies=group:artifact:version in 
application.properties, or --dep on the \
+            command line (camel_run has no dependencies argument, so use one 
of the first two). \
+            camel_dependency_for_class gives the coordinates of a Java class.
             - Passing the YAML schema is not passing at runtime: run the app 
(camel_run) and read its log before \
             calling it done.
 
-            Which tool:
-            - options, URI syntax, headers of a component, EIP, data format or 
language: camel_catalog_doc
-            - which component for a protocol or product: camel_catalog_find
-            - a validated YAML sample and where it goes: camel_catalog_sample
-            - complete example projects: camel_catalog_examples
-            - a sample Citrus test (test/<name>.citrus.it.yaml, run with camel 
test run): the test/ file of an \
-            example, camel_catalog_examples with filter citrus
-            - Maven coordinates (the --dep value) of a Java class: 
camel_dependency_for_class
-            - validate YAML/Java/XML/properties before writing: 
camel_validate_source
-            - run an integration: camel_run; its log: camel_get_log; its 
errors: camel_get_errors
-            - routes, endpoints and mistakes of an existing project: 
camel_project_overview
-            - run a Citrus test (test/<name>.citrus.it.yaml): camel test run 
<file> in the project directory, not \
-            camel_run
+            Workflow:
+            1. List what the integration does: its inbound and outbound 
endpoints, transformations, error handling \
+            and configuration.
+            2. For each protocol or product, find the component 
(camel_catalog_find) and its options \
+            (camel_catalog_doc); camel_catalog_sample gives a validated YAML 
sample of an EIP or component.
+            3. Write the routes and their configuration: camel_validate_source 
checks a source, camel_write_file \
+            validates and writes it.
+            4. Run it (camel_run), read the log (camel_get_log) and the failed 
exchanges (camel_get_errors), and \
+            send it the messages the old integration handled 
(camel_runtime_send) to compare the results.
+
+            Upgrading a Camel project to a newer Camel version instead: use 
the camel_migrate_project prompt, which \
+            starts with camel_migration_analyze.
             """;
 
     @ConfigProperty(name = "quarkus.mcp.server.server-info.instructions")
@@ -102,8 +164,21 @@ public class McpServerInstructions implements 
InitialResponseInfo {
 
     /** The instructions text for the given Camel version. */
     static String build(String camelVersion) {
-        String version = camelVersion == null || camelVersion.isBlank() ? 
"(unknown version)" : camelVersion.trim();
-        return TEMPLATE.formatted(version).strip();
+        return TEMPLATE.formatted(version(camelVersion)).strip();
+    }
+
+    /** The rules and workflow of a migration of an integration to Camel, for 
the given Camel version. */
+    static String migration(String camelVersion) {
+        return MIGRATION_TEMPLATE.formatted(version(camelVersion)).strip();
+    }
+
+    /** The rules and workflow of a migration of an integration to Camel, for 
the Camel version of the catalog. */
+    static String migration() {
+        return migration(new DefaultCamelCatalog().getCatalogVersion());
+    }
+
+    private static String version(String camelVersion) {
+        return camelVersion == null || camelVersion.isBlank() ? "(unknown 
version)" : camelVersion.trim();
     }
 
     String text() {
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
index bf384e21c591..469dcf99eb18 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
@@ -54,6 +54,19 @@ public class MigrationTools {
     @Inject
     CatalogService catalogService;
 
+    /**
+     * The rules and workflow of a migration of an integration to Camel 
(CAMEL-25672), for clients that use tools but no
+     * prompts: the same text as the camel_migrate_to_camel prompt.
+     */
+    @ToolGuardrails(input = McpArgumentGuardrail.class, output = 
McpArgumentGuardrail.class)
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
+          description = "The rules and workflow of a migration of an 
integration to Camel, from another product or "
+                        + "from hand-written code. Call it once, at the start 
of a migration. For an upgrade between "
+                        + "Camel versions, start with camel_migration_analyze 
instead.")
+    public String camel_migration_instructions() {
+        return McpServerInstructions.migration();
+    }
+
     /**
      * Step 1: Analyze a project's pom.xml to detect runtime, Camel version, 
Java version, and components.
      */
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
index 1cafc11a8f34..d4b7168ba45e 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
@@ -150,6 +150,28 @@ public class PromptDefinitions {
         return List.of(PromptMessage.withUserRole(instructions));
     }
 
+    /**
+     * A migration of an integration to Camel, from another product or from 
hand-written code (CAMEL-25672): the rules a
+     * migration gets wrong most and its workflow, which the server 
instructions leave out as they serve everyday work.
+     * The {@code camel_migration_instructions} tool returns the same text, 
for clients without prompts.
+     */
+    @Prompt(name = "camel_migrate_to_camel",
+            description = "Migrate an integration to Camel, from another 
product or from hand-written code: the rules "
+                          + "a migration gets wrong most (component names, 
mail headers, Camel* headers before an "
+                          + "http or mail call, xslt parameters, ports and 
dependencies of camel run) and the "
+                          + "workflow. For an upgrade between Camel versions, 
use camel_migrate_project.")
+    public List<PromptMessage> migrateToCamel(
+            @PromptArg(name = "source",
+                       description = "Optional: what to migrate, such as the 
product and where its files are",
+                       required = false) String source) {
+
+        String text = McpServerInstructions.migration();
+        if (source != null && !source.isBlank()) {
+            text = "What to migrate: " + source.strip() + "\n\n" + text;
+        }
+        return List.of(PromptMessage.withUserRole(text));
+    }
+
     /**
      * Guided workflow for a security review of a Camel route.
      */
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructionsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructionsTest.java
index c4bb6d78031e..810e3f56721e 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructionsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/McpServerInstructionsTest.java
@@ -29,6 +29,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
+import io.quarkiverse.mcp.server.Prompt;
 import io.quarkiverse.mcp.server.Tool;
 import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.junit.jupiter.api.Test;
@@ -37,7 +38,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 /**
  * The server instructions sent in the MCP initialize response: their size, 
their first line and the tool names they
- * point the agent to.
+ * point the agent to. They serve everyday work; the rules of a migration to 
Camel are a separate text, sent when a
+ * migration starts.
  */
 class McpServerInstructionsTest {
 
@@ -52,7 +54,7 @@ class McpServerInstructionsTest {
         assertThat(text).hasSizeLessThan(McpServerInstructions.MAX_LENGTH);
         // Pi shows only the first line, Codex weighs the first 512 characters 
most
         assertThat(firstLine).hasSizeLessThan(512).startsWith("Apache Camel " 
+ version + " ")
-                .contains("URI scheme", "camel_validate_source");
+                .contains("build, run and fix", "camel_edit_file", 
"camel_run", "camel_get_log");
         assertThat(McpServerInstructions.build("9.9.9")).startsWith("Apache 
Camel 9.9.9 ");
         assertThat(McpServerInstructions.build(null)).startsWith("Apache Camel 
(unknown version) ");
     }
@@ -61,15 +63,59 @@ class McpServerInstructionsTest {
     void theRulesComeInTheOrderOfTheProposal() {
         String text = new McpServerInstructions().text();
 
+        // the mistakes that cost a model most when it writes routes, the 
daily tool loop, then the way to a migration
         assertThat(text).containsSubsequence(
-                "URI scheme: smtp", "platform-http",
+                "steps of an EIP", "go under its steps",
+                "A choice has one when list", "otherwise: {steps: [...]}",
                 "top-level", "handled: {constant: {expression: \"true\"}}",
-                "camel.server.port", "//DEPS", "camel.jbang.dependencies", 
"--dep",
+                "variable (setVariable, ${variable.name}), not an exchange 
property",
+                "Simple: functions inside ${...}, operators between them",
+                "URI scheme", "platform-http",
+                "After a write, read its answer",
+                "Daily work:", "camel_get_files", "camel_edit_file", 
"camel_write_file", "camel_run",
+                "camel_control", "camel_get_log", "camel_get_errors", 
"camel_eval_expression",
+                "Which tool:",
+                "Migrating to Camel, or between Camel versions?");
+        assertThat(text.strip()).endsWith("or call 
camel_migration_instructions.");
+    }
+
+    @Test
+    void theRulesOfAMigrationAreLeftToTheMigrationText() {
+        String text = new McpServerInstructions().text();
+
+        // rules that only matter when migrating to Camel are not in the text 
of everyday work
+        assertThat(text).doesNotContain("useHeaderSubject", "removeHeaders", 
"xslt", "--dep", "camel.server.port",
+                "smtp");
+        assertThat(McpServerInstructions.migration("4.0.0")).contains(
+                "useHeaderSubject=true", "removeHeaders", "xslt", "--dep", 
"camel.server.port", "smtp");
+    }
+
+    @Test
+    void theMigrationTextHasTheRulesOfAMigrationThenTheWorkflow() {
+        String version = new DefaultCamelCatalog().getCatalogVersion();
+        String text = McpServerInstructions.migration();
+
+        assertThat(text).startsWith("Migrating an integration to Apache Camel 
" + version + ",");
+        
assertThat(McpServerInstructions.migration(null)).startsWith("Migrating an 
integration to Apache Camel "
+                                                                     + 
"(unknown version),");
+        assertThat(text).containsSubsequence(
+                "URI scheme: smtp", "platform-http",
                 "useHeaderSubject=true", "useHeaderFrom=true", 
"useHeaderRecipients=true",
                 "removeHeaders", "Camel*",
                 "xslt", "headers and variables",
+                "camel.server.port", "//DEPS", "camel.jbang.dependencies", 
"--dep",
                 "run the app",
-                "Which tool:");
+                "Workflow:",
+                "camel_migrate_project");
+    }
+
+    @Test
+    void everyToolOfTheMigrationTextIsAToolOfThisServer() throws IOException {
+        Set<String> named = namesIn(McpServerInstructions.migration());
+        named.removeAll(promptNames());
+
+        assertThat(toolNames()).containsAll(named);
+        
assertThat(McpServerInstructions.MIGRATION_TOOLS).containsExactlyInAnyOrderElementsOf(named);
     }
 
     @Test
@@ -83,18 +129,19 @@ class McpServerInstructionsTest {
 
     @Test
     void everyMappedToolIsMentionedAndIsAToolOfThisServer() throws IOException 
{
-        String text = new McpServerInstructions().text();
+        Set<String> named = namesIn(new McpServerInstructions().text());
         Set<String> toolNames = toolNames();
+        Set<String> promptNames = promptNames();
 
         assertThat(toolNames).as("@Tool methods found").hasSizeGreaterThan(10);
-        // the text names exactly the mapped tools, and each is a tool of this 
server (not renamed or removed)
-        Matcher m = TOOL_NAME.matcher(text);
-        Set<String> named = new HashSet<>();
-        while (m.find()) {
-            named.add(m.group());
-        }
+        assertThat(promptNames).as("@Prompt methods 
found").contains("camel_migrate_project");
+        // the text names exactly the mapped tools and prompts, and each is 
one of this server (not renamed or removed)
+        Set<String> prompts = new HashSet<>(named);
+        prompts.retainAll(promptNames);
+        named.removeAll(promptNames);
         assertThat(toolNames).containsAll(named);
         
assertThat(McpServerInstructions.MAPPED_TOOLS).containsExactlyInAnyOrderElementsOf(named);
+        
assertThat(McpServerInstructions.MAPPED_PROMPTS).containsExactlyInAnyOrderElementsOf(prompts);
     }
 
     @Test
@@ -117,6 +164,28 @@ class McpServerInstructionsTest {
         
assertThat(bean.instructions("camel-mcp-server")).contains(bean.text());
     }
 
+    /** The camel_* names in a text. */
+    private static Set<String> namesIn(String text) {
+        Matcher m = TOOL_NAME.matcher(text);
+        Set<String> named = new HashSet<>();
+        while (m.find()) {
+            named.add(m.group());
+        }
+        return named;
+    }
+
+    /** The names of the {@code @Prompt} methods of the MCP server. */
+    private static Set<String> promptNames() {
+        Set<String> names = new HashSet<>();
+        for (Method method : PromptDefinitions.class.getDeclaredMethods()) {
+            Prompt prompt = method.getAnnotation(Prompt.class);
+            if (prompt != null) {
+                names.add(Prompt.ELEMENT_NAME.equals(prompt.name()) ? 
method.getName() : prompt.name());
+            }
+        }
+        return names;
+    }
+
     /** The names of the {@code @Tool} methods of every class in the package 
of the MCP server. */
     private static Set<String> toolNames() throws IOException {
         Path classes;
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
index 39108501378a..b9003a6ca4ee 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
@@ -32,6 +32,13 @@ class MigrationToolsTest {
         tools.catalogService = new CatalogService();
     }
 
+    @Test
+    void migrationInstructionsAreTheTextOfTheMigratePrompt() {
+        // for clients that use tools but no prompts: the same text as the 
camel_migrate_to_camel prompt
+        
assertThat(tools.camel_migration_instructions()).isEqualTo(McpServerInstructions.migration())
+                .startsWith("Migrating an integration to Apache Camel ");
+    }
+
     // a Spring Boot application packaged as a WAR for Tomcat: the BOM and a 
managed-only component declare nothing,
     // the plugin dependency is not a project dependency
     private static final String SPRING_BOOT_WAR_POM = """
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
index 79a25fd392e9..16f513b160c8 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
@@ -177,6 +177,25 @@ class PromptDefinitionsTest {
         assertThat(text).contains("camel_version_list");
     }
 
+    // ---- camel_migrate_to_camel ----
+
+    @Test
+    void migrateToCamelCarriesTheMigrationText() {
+        String text = extractText(prompts.migrateToCamel(null));
+
+        // the rules the server instructions leave out, as they serve everyday 
work
+        assertThat(text).isEqualTo(McpServerInstructions.migration())
+                .contains("useHeaderSubject=true", "removeHeaders", "xslt", 
"--dep", "Workflow:");
+    }
+
+    @Test
+    void migrateToCamelStartsWithWhatToMigrate() {
+        String text = extractText(prompts.migrateToCamel(" a Mule 3 
application in legacy/ "));
+
+        assertThat(text).startsWith("What to migrate: a Mule 3 application in 
legacy/\n\nMigrating an integration to");
+        assertThat(extractText(prompts.migrateToCamel(" 
"))).isEqualTo(McpServerInstructions.migration());
+    }
+
     // ---- camel_security_review ----
 
     @Test

Reply via email to