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

davsclaus pushed a commit to branch CAMEL-25928-body-type-resolver
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 06ce281ebbf3a7f460e69965ce289284e7d1b0d7
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 25 08:58:24 2026 +0100

    CAMEL-25928: docs - Message Body Types, a page about the body types: what 
they are, the tools that use them, how to declare them, and their JSON with the 
rule to read it
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../modules/ROOT/partials/body-types.adoc          |   2 +-
 docs/user-manual/modules/ROOT/nav.adoc             |   1 +
 .../user-manual/modules/ROOT/pages/body-types.adoc | 329 +++++++++++++++++++++
 .../modules/ROOT/pages/camel-catalog.adoc          | 106 +------
 .../modules/ROOT/pages/camel-jbang-mcp.adoc        |   2 +-
 .../modules/ROOT/pages/camel-jbang-validation.adoc |   2 +-
 .../modules/ROOT/pages/endpoint-annotations.adoc   |   4 +-
 7 files changed, 344 insertions(+), 102 deletions(-)

diff --git a/docs/components/modules/ROOT/partials/body-types.adoc 
b/docs/components/modules/ROOT/partials/body-types.adoc
index 8df01700d940..a79f7abeeb2b 100644
--- a/docs/components/modules/ROOT/partials/body-types.adoc
+++ b/docs/components/modules/ROOT/partials/body-types.adoc
@@ -16,6 +16,6 @@ ifeval::[{bodytypecount} != 0]
 
jsonpathTable::{body-types-module}:example$json/{shortname}.json['nodes$.bodyTypes.*','camel.bodySide(path[2])|camel.bodyCarriers(value)|camel.bodyDescription(value)','camel=xref:ROOT:js/camel.js']
 
 The bodies are the ones with the default options. A side that is not listed is 
not known, and tooling does not guess
-it. The carriers are described in the xref:manual::camel-catalog.adoc[Camel 
Catalog].
+it. The carriers, the sides and the tools that use them are described in 
xref:manual::body-types.adoc[Message Body Types].
 endif::[]
 // body types: END
diff --git a/docs/user-manual/modules/ROOT/nav.adoc 
b/docs/user-manual/modules/ROOT/nav.adoc
index 702ca4ebea49..8440caa8c701 100644
--- a/docs/user-manual/modules/ROOT/nav.adoc
+++ b/docs/user-manual/modules/ROOT/nav.adoc
@@ -113,6 +113,7 @@
 ** xref:jmx.adoc[JMX]
 ** xref:key-value-repository.adoc[KeyValueRepository]
 ** xref:lifecycle.adoc[Camel Lifecycle]
+** xref:body-types.adoc[Message Body Types]
 ** xref:message-size.adoc[Message Size]
 ** xref:oncompletion.adoc[OnCompletion]
 ** xref:pluggable-class-resolvers.adoc[Pluggable Class Resolvers]
diff --git a/docs/user-manual/modules/ROOT/pages/body-types.adoc 
b/docs/user-manual/modules/ROOT/pages/body-types.adoc
new file mode 100644
index 000000000000..608279dffa0d
--- /dev/null
+++ b/docs/user-manual/modules/ROOT/pages/body-types.adoc
@@ -0,0 +1,329 @@
+= Message Body Types
+
+*Available as of Camel 4.24*
+
+Every step of a route needs some kinds of message body and leaves one. The 
`file` consumer gives the file it read,
+`unmarshal: json` turns text into a `Map` or a `List`, and the `file` producer 
writes text, bytes, a stream or a file,
+but not a `Map`. When the body one step leaves is not a body the next step can 
take, the route fails, but only when a
+message gets there.
+
+Camel describes these bodies in its catalog, as the _body types_ of each 
component, data format, language and EIP.
+They say what each step needs and what it leaves, with its default options and 
with the options that change it. Tools
+read them to tell before the route runs what the body is at each step, and 
where it does not fit.
+
+The body types help you:
+
+* *Find a route that cannot work before it runs.* The YAML validator follows 
the body through the steps of each route,
+  and reports a step that cannot take the body it gets, with a hint of what to 
add.
+* *Know what a step gives without reading its code.* The documentation page of 
each component, data format, language
+  and EIP has a _Message Body_ section, and the Camel CLI and its MCP server 
show the same.
+* *Write routes with AI.* An AI assistant that reads the body types writes 
steps that fit together, such as a
+  `marshal` before a step that sends text.
+* *See what the body was when the routes ran.* With the message history on, 
Camel records the body each step has been
+  reached with. A step reached with bodies of more than one kind is a part of 
the route that needs attention.
+
+== Carriers: the kinds of body
+
+A body type names the _carriers_ of the body: the kinds of Java object it can 
be in. They are coarse on purpose: a
+carrier that is right is better than a precise Java type that is wrong.
+
+[width="100%",cols="1,3,1",options="header"]
+|===
+| Carrier | The body is | Kind at runtime
+| `null` | No body. | `null`
+| `text` | A `String`. | `payload`
+| `bytes` | A `byte[]`. | `payload`
+| `stream` | An `InputStream`, a `Reader`, or the stream cache of Camel. | 
`payload`
+| `file` | A file: the `GenericFile` of the file consumers, a `java.io.File` 
or a `Path`. | `payload`
+| `value` | A single value: a number, a boolean or an enum. | `payload`
+| `map` | A `java.util.Map`, such as a JSON object parsed, or a row of a 
query. | `data`
+| `pojo` | Another Java object, such as a DOM `Document` or a class a payload 
is unmarshalled into. | `data`
+| `list` | A `List`, another `Collection` or an array, such as a JSON array 
parsed, or the rows of a query. | `collection`
+| `iterator` | An `Iterator`, read once, element by element, with no size and 
no index. | `collection`
+|===
+
+Three more words have a meaning of their own:
+
+* `unchanged` -- the body after the step is the body before it, such as after 
a `log` or a `to: kafka`.
+* `any` -- the step takes any body, such as a `setHeader`. Only for the bodies 
a step needs.
+* `unknown` -- the body depends on what the other system sends, or on a class 
the user configures. Tools must not guess
+  it. Only for the body after a step.
+
+The _kinds_ are what Camel records at runtime (see 
xref:components:eips:message-history.adoc[Message History]): a
+step reached with bodies of one kind, such as `String` and `byte[]`, is 
dynamic but fine; a step reached with a payload
+for some messages and data for others needs attention.
+
+== Sides: what a step needs and what it leaves
+
+The body types of a model are a set of _sides_. A side that is not listed is 
not known.
+
+[width="100%",cols="1,1,3",options="header"]
+|===
+| Model | Side | The body
+.4+| Component | `consumerOut` | The body of the message the consumer creates: 
what a `from` gives the route.
+| `consumerReply` | The bodies a consumer that answers the caller can reply 
with: what the route can end with.
+| `producerIn` | The bodies the producer can send or write: what a `to` needs.
+| `producerOut` | The body after the producer.
+.4+| Data format | `marshalIn` | The bodies the data format can marshal.
+| `marshalOut` | The body after `marshal`.
+| `unmarshalIn` | The bodies the data format can unmarshal.
+| `unmarshalOut` | The body after `unmarshal`.
+.2+| Language | `expressionIn` | The bodies the expressions of the language 
can read.
+| `expressionOut` | The result of an expression.
+.3+| EIP | `eipIn` | The bodies the EIP needs, such as text for `tokenizer`.
+| `eipOut` | The body after the EIP.
+| `eipSteps` | The body the steps of the EIP start with, such as each part for 
`split`.
+|===
+
+== Examples
+
+=== A step that cannot take the body
+
+This route reads JSON files, parses them, and writes them to another directory:
+
+// yaml-validator: skip
+[source,yaml]
+----
+- route:
+    from:
+      uri: file:inbox
+      steps:
+        - unmarshal:
+            json: {}
+        - to:
+            uri: file:outbox
+----
+
+The `file` consumer gives the file (`consumerOut` is `file`), `unmarshal: 
json` leaves a `Map` or a `List`
+(`unmarshalOut`), and the `file` producer needs text, bytes, a stream or a 
file (`producerIn`). So the validator
+reports the `to`:
+
+[source,text]
+----
+to: file needs the body as text, bytes, a stream or a file, but the body here 
is parsed data (a Map or a List), not
+text - unmarshal: json turns the text into data: add marshal: json before this 
step
+----
+
+With a `marshal` before it, the body is JSON text again, which the `file` 
producer writes:
+
+[source,yaml]
+----
+- route:
+    from:
+      uri: file:inbox
+      steps:
+        - unmarshal:
+            json: {}
+        - marshal:
+            json: {}
+        - to:
+            uri: file:outbox
+----
+
+=== Options change the body
+
+The body after a step can depend on its options. The `xslt` producer leaves 
the result as text, unless its `output`
+option says otherwise:
+
+[source,json]
+----
+"producerOut": {
+  "carriers": [ "text" ], "javaType": "java.lang.String", "dependsOn": [ 
"resultHandlerFactory" ],
+  "byOption": [
+    { "option": "output", "value": "bytes", "carriers": [ "bytes" ], 
"javaType": "byte[]" },
+    { "option": "output", "value": "DOM", "carriers": [ "pojo" ], "javaType": 
"org.w3c.dom.Document" },
+    { "option": "output", "value": "file", "carriers": [ "file" ], "javaType": 
"java.io.File" } ],
+  "description": "The result of the transformation" }
+----
+
+With `output=bytes` the body is a `byte[]`. With a `resultHandlerFactory` the 
factory decides the body, which the
+catalog cannot know, so it is not known.
+
+=== An option that names the class of the body
+
+Some options name the class the result is converted to: the `resultType` of a 
language, the `unmarshalType` of a JSON
+data format, the `type` of `convertBodyTo`, or the `outputClass` of `sql` for 
one row. When such an option is set, the
+body is an object of that class, or null:
+
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:order
+      steps:
+        - setBody:
+            simple:
+              expression: "${header.quantity}"
+              resultType: java.lang.Integer
+        - to:
+            uri: sql:select * from orders where id = :#id
+            parameters:
+              outputType: SelectOne
+              outputClass: com.acme.Order
+----
+
+`setBody` leaves the result of its expression, and with `resultType` that is 
an `Integer`, a `value`. The `sql`
+producer leaves a `List` of rows by default, and with `outputType=SelectOne` 
and `outputClass` one `com.acme.Order`,
+or null when there is no row.
+
+=== EIPs
+
+The body after an EIP is often the body of something else: `marshal` leaves 
what its data format writes, `to` what
+the producer of its endpoint leaves, and `setBody` the result of its 
expression. The catalog says so with `from`,
+instead of copying the body types of every data format, component and language:
+
+[source,json]
+----
+"eipOut": { "from": "marshalOut", "dependsOn": [ "variableReceive" ],
+  "description": "What the data format writes. With variableReceive the result 
goes into the variable, and the body stays as it was" }
+----
+
+An EIP with steps of its own also says what they start with. The steps of a 
`split` start with each part, and after
+it the body is as it was before the split:
+
+[source,json]
+----
+"eipOut": { "carriers": [ "unchanged" ], "dependsOn": [ "aggregationStrategy" 
],
+  "description": "The body as it was before the split, unless an 
aggregationStrategy is set" },
+"eipSteps": { "carriers": [ "unknown" ],
+  "description": "Each part of what the expression gives, such as an element 
of a List or a line of text" }
+----
+
+== Tooling
+
+=== Documentation
+
+The page of each component, data format, language and EIP has a _Message Body_ 
section with a row per side, which
+is generated from the catalog.
+
+=== Camel CLI and its MCP server
+
+`camel catalog doc` prints the same table for a component, data format, 
language or EIP, such as
+`camel catalog doc xslt` or `camel catalog doc eip:split`. The 
`camel_catalog_doc` tool of the
+xref:camel-jbang-mcp.adoc[MCP server] gives each side as a line of text, so an 
AI assistant reads them with the
+options:
+
+[source,text]
+----
+text (java.lang.String). The result of the transformation. With output=bytes: 
bytes (byte[]); output=DOM: pojo
+(org.w3c.dom.Document); output=file: file (java.io.File). Not known when 
resultHandlerFactory is set
+----
+
+=== YAML validator
+
+`camel validate source` follows the body through the steps of each route with 
the body types, and reports a step
+that cannot take the body it gets (see 
xref:camel-jbang-validation.adoc[Validation]). Where the catalog does not
+know the body, such as after an HTTP consumer, it says nothing, unless a 
running integration has shown what the body
+is.
+
+=== At runtime
+
+With the message history on, such as with `camel run --dev`, Camel records the 
types and kinds of the bodies each
+step has been reached with:
+
+* xref:components:eips:message-history.adoc[Message History] explains the 
kinds, the `BodyTypeRegistry`, the
+  `body-type` dev console and the JMX attributes.
+* `camel cmd route-structure` shows them per step (see 
xref:camel-jbang-diagnostics.adoc[Diagnostics]), as do the
+  xref:camel-jbang-tui-diagram.adoc#_body_types[diagram] and the
+  xref:camel-jbang-tui-source-editor.adoc#_body_types[source editor] of the 
Camel CLI.
+* The route coverage report of the xref:camel-report-maven-plugin.adoc[Camel 
Report Maven Plugin] shows the body types
+  the tests reached each step with, and can fail the build on a step reached 
with bodies of more than one kind.
+
+== Declaring body types
+
+A component declares its body types with the `@BodyTypes` annotation on its 
endpoint class, a data format and a
+language on their class, and an EIP on its model class in `camel-core-model`. 
That is also where the EIPs that other
+modules implement are declared, such as `circuitBreaker`. A subclass does not 
have the body types of its superclass,
+as it may change the body:
+
+[source,java]
+----
+@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, description = "The 
file as read ..."),
+           producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, 
Carrier.STREAM, Carrier.FILE }),
+           producerOut = @BodyType(Carrier.UNCHANGED))
+@UriEndpoint(firstVersion = "1.0.0", scheme = "file", ...)
+public class FileEndpoint extends GenericFileEndpoint<File> {
+----
+
+The values come from the code that sets the body. The build checks that:
+
+* each side belongs to the kind of model; a consumer-only component has no 
producer sides;
+* the options a side names exist, and a value of an option is one the option 
can have;
+* `unchanged` and `unknown` are only on the body after a step, and `any` only 
on what a step needs;
+* `from` names a side the body can come from, and a side with `from` has no 
carriers of its own;
+* a `javaTypeOption` is an option of the body after a step that the side does 
not also depend on.
+
+The runtime body types the message history records are checked against them in 
tests, such as for the EIPs of core.
+
+== Reference: the JSON of the body types
+
+This section is for the tools, and the AI assistants, that read the catalog.
+
+The JSON of a component, data format, language or EIP in the catalog has a 
`bodyTypes` object next to its options, with
+an object per side. Each side has these fields; a field that does not apply is 
left out:
+
+[width="100%",cols="1,1,4",options="header"]
+|===
+| Field | Type | Meaning
+| `carriers` | array of strings | The carriers of the body: one, or several 
when the body can be any of them. Left out
+for a side with `from`.
+| `javaType` | string | The Java type of the body, when it is always of this 
type.
+| `format` | string | The format of the payload, when it is always the same: 
`json`, `xml`, `csv`, `yaml`, `text` or
+`binary`.
+| `dependsOn` | array of strings | The options the body depends on. The side 
holds while they keep their default values.
+When the step sets one, only a `byOption` entry for that value says the body; 
without one it is not known.
+| `byOption` | array of objects | The body for a value of an option: `option`, 
`value`, `carriers`, and optionally
+`javaType` and `javaTypeOption`.
+| `javaTypeOption` | string | The option that names the class of the body, 
such as `resultType`. When the step sets
+it, the body is an object of that class, or null.
+| `from` | string | For an EIP, the side of another model the body is: 
`expressionIn`, `expressionOut` (the language
+of its expression), `marshalIn`, `marshalOut`, `unmarshalIn`, `unmarshalOut` 
(its data format), `producerIn`,
+`producerOut` (its endpoint as a producer), `consumerOut` (its endpoint as a 
consumer), or `steps` (what its own
+steps end with).
+| `description` | string | What to know about the body, in a sentence.
+|===
+
+=== Reading a side for a step
+
+To know the body a step leaves, or the bodies it needs, read its side with the 
options the step sets, in this order:
+
+. *No side:* not known.
+. *A side with `from`:* when the step sets an option of `dependsOn`, not 
known. Otherwise, read the side `from` names on
+the model the step uses (the language of its expression, its data format or 
its endpoint), with the options of that
+model. `steps` is the body the steps of the EIP end with.
+. *An option the side names whose value is not known before the route runs* (a 
`+{{placeholder}}+` or a `+${expression}+`),
+or an option the step does not set but that can be set elsewhere, such as on 
the component: not known.
+. *An option of `dependsOn` the step sets:* the `byOption` entry for that 
value says the body. When it has a
+`javaTypeOption` the step sets, the body is an object of that class. Without 
such an entry: not known.
+. *The `javaTypeOption` of the side, when the step sets it:* an object of the 
class it names, or null. It wins over the
+`byOption` entries of other options.
+. *The `byOption` entries for the values the step sets:* their carriers (or 
the class of their `javaTypeOption`). When
+two of them say different bodies: not known.
+. *Otherwise:* the carriers of the side. `unknown` is not known, and 
`unchanged` is the body before the step.
+
+The values of an option compare in any case, and in any style of an enum 
constant, as Camel takes them:
+`convert-to-json`, `convertToJson` and `CONVERT_TO_JSON` are one value.
+
+The carrier of a class that a `javaTypeOption` names follows the class: 
`String` is `text`, `byte[]` is `bytes`, an
+`InputStream` or a `Reader` is `stream`, a `File` or a `Path` is `file`, a 
number, a boolean or a character is
+`value`, a `Map` is `map`, a `Collection` or an array is `list`, an `Iterator` 
is `iterator`, `Object` is `unknown`, and
+any other class is `pojo`. The short names Camel takes, such as `String` and 
`int`, count too.
+
+=== Java API
+
+The models of `camel-catalog` have the body types, and `BodyTypeResolver` of 
`camel-tooling-model` reads a side for the
+options of a step, by the rule above. The YAML validator uses it:
+
+[source,java]
+----
+CamelCatalog catalog = new DefaultCamelCatalog();
+
+BodyTypeModel out = 
catalog.componentModel("xslt").getBodyType(BodyTypeModel.PRODUCER_OUT);
+out.getCarriers();      // [text]
+out.getJavaType();      // java.lang.String
+
+BodyTypeResolver.Resolved bytes = BodyTypeResolver.resolve(out, 
Map.of("output", "bytes"), Set.of());
+bytes.carriers();       // [bytes]
+
+BodyTypeModel.carrierOf("java.util.Map");   // map
+----
diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
index 2e7cc5513d6b..864526f0844e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
@@ -129,86 +129,20 @@ question people, IDEs and AI agents ask most when they 
write the next step of a
 file it read, the `http` producer leaves the response as a byte array or a 
stream, and after `unmarshal: json` the body
 is a `Map` or a `List`.
 
-A component, data format, language or EIP can declare its body types, and the 
catalog then has a `bodyTypes` section
-with one entry per side:
-
-[width="100%",cols="25%,75%",options="header",]
-|===
-|Side |The body
-|`consumerOut` |The body of the message the consumer creates.
-|`consumerReply` |The bodies a consumer that answers the caller can reply 
with: the body the route ends with.
-|`producerIn` |The bodies the producer can send or write.
-|`producerOut` |The body after the producer.
-|`marshalIn`, `marshalOut` |The bodies a data format can marshal, and the body 
after marshal.
-|`unmarshalIn`, `unmarshalOut` |The bodies a data format can unmarshal, and 
the body after unmarshal.
-|`expressionIn`, `expressionOut` |The bodies the expressions of a language can 
read, and their result.
-|`eipIn` |The bodies an EIP needs, such as text for `tokenizer`.
-|`eipOut` |The body after an EIP.
-|`eipSteps` |The body the steps of an EIP start with, such as each part for 
`split`.
-|===
-
-Each side has the _carriers_ of the body, the kinds of Java object it is in: 
`null`, `text`, `bytes`, `stream`,
-`file`, `value`, `map`, `list`, `iterator` (read once, with no size and no 
index) and `pojo`; `unchanged` for a step
-that leaves the body as it was, `any` for a step that takes any body, and 
`unknown` for a body that depends on what
-the other system sends or on a class the user configures. A side can also have 
the Java type when the body is always of that type, the format of the payload 
(json,
-xml, csv, yaml, text or binary), the options the body depends on, the body for 
other values of an option, and a
-description:
+A component, data format, language or EIP that declares its body types has a 
`bodyTypes` section in the catalog, with
+an entry per side: what a consumer gives, what a producer needs and leaves, 
what a data format marshals and unmarshals
+to, what an expression reads and gives, and what an EIP needs, leaves and 
starts its own steps with:
 
 [source,json]
 ----
 "bodyTypes": {
-  "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], 
"format": "xml", "dependsOn": [ "source" ],
-    "description": "The XML to transform: text, bytes, a stream, a file, a DOM 
or another javax.xml.transform.Source. ..." },
-  "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"dependsOn": [ "resultHandlerFactory" ],
-    "byOption": [ { "option": "output", "value": "bytes", "carriers": [ 
"bytes" ], "javaType": "byte[]" }, ... ],
-    "description": "The result of the transformation" }
+  "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
+    "description": "What converts to an InputStream: text, bytes, a stream or 
a file. A Map or a List fails: marshal it first" },
+  "producerOut": { "carriers": [ "unchanged" ] }
 }
 ----
 
-An EIP whose body is the body of another model says which side of it with 
`from`, instead of carriers of its own:
-`marshal` leaves what its data format leaves (`marshalOut`), `to` what the 
producer of its endpoint leaves
-(`producerOut`), `pollEnrich` what the consumer of its endpoint receives 
(`consumerOut`), `setBody` the result of its
-expression (`expressionOut`), and a `filter` what its own steps end with 
(`steps`):
-
-[source,json]
-----
-"bodyTypes": {
-  "eipOut": { "from": "steps", "description": "What its steps end with; the 
body as it was when the predicate does not match" },
-  "eipSteps": { "carriers": [ "unchanged" ] }
-}
-----
-
-An option that names the class of the body is the `javaTypeOption` of a side, 
such as the `resultType` of a language,
-whose result is converted to that class, the `unmarshalType` of a JSON data 
format, and the `type` of `convertBodyTo`.
-When the option is set, the body is an object of the class it names, or null 
when it does not convert. That holds while
-the options the side depends on keep their defaults (an option that makes the 
body a collection of that class, such
-as `useList` of `jackson`, is one of them), and it wins over the bodies 
`byOption` gives for other options. The
-carrier follows the class: `String` is `text`, `byte[]` is `bytes`, an 
`InputStream` or a `Reader` is a `stream`, a
-`File` or a `Path` is a `file`, a number, a boolean or a character is a 
`value`, a `Map` is a `map`, a `Collection` or
-an array is a `list`, an `Iterator` is an `iterator`, `Object` is `unknown`, 
and any other class is a `pojo`.
-`BodyTypeModel.carrierOf` gives it for a class name, also for the short names 
Camel takes, such as `String` and `int`:
-
-[source,json]
-----
-"bodyTypes": {
-  "expressionOut": { "carriers": [ "unknown" ], "javaTypeOption": "resultType",
-    "description": "A text with functions gives text. A single function, such 
as ${body} or ${header.foo}, gives its value as it is" }
-}
-----
-
-A body for an option value can have a `javaTypeOption` too, when the option 
names the class of the body only for that
-value: the `outputClass` of `sql` is the class of the one row with 
`outputType=SelectOne`, and of each row in the List
-otherwise:
-
-[source,json]
-----
-"producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ 
"outputHeader", "noop" ],
-  "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ 
"map", "value", "null" ], "javaTypeOption": "outputClass" }, ... ] }
-----
-
-The sides say what the body is with the default options. A side that is not in 
the catalog is not known, and tooling
-should not guess it. The documentation page of a component, data format, 
language or EIP shows them in its _Message
-Body_ section, and `camel catalog doc` prints them. The Java API gives them on 
the model:
+The Java API gives them on the model:
 
 [source,java]
 ----
@@ -219,30 +153,8 @@ out.getCarriers();     // [text]
 out.getJavaType();     // java.lang.String
 ----
 
-Component developers declare them with the `@BodyTypes` annotation on the 
endpoint, data format or language class, and
-the EIPs on their model class in `camel-core-model`, also when the EIP is 
implemented in another module, such as
-`circuitBreaker`. A subclass does not have the body types of its superclass, 
as it may change the body:
-
-[source,java]
-----
-@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, description = "The 
file as read ..."),
-           producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, 
Carrier.STREAM, Carrier.FILE }),
-           producerOut = @BodyType(Carrier.UNCHANGED))
-@UriEndpoint(firstVersion = "1.0.0", scheme = "file", ...)
-public class FileEndpoint extends GenericFileEndpoint<File> {
-----
-
-The build checks that each side belongs to the kind (a consumer-only component 
has no producer sides), that the
-options a side names exist, that a value of an option is one the option can 
have (such as `chat-completion` for the
-`operation` of openai), that `unchanged` is only on the body after a step and 
`any` only on what a step needs,
-that `from` names a side the body can come from, and that a `javaTypeOption` 
is an option of the body after a step
-that the side does not also depend on.
-The values are read from the code that sets the body, and checked against the 
body types the message history records
-when the routes run.
-
-The YAML validator of the Camel CLI follows the body through the steps of a 
route with the body types, and reports a
-step that cannot work on it, such as a Map that a `to: file` cannot write (see
-xref:camel-jbang-validation.adoc#_what_a_running_integration_saw[Camel CLI 
validation]).
+See xref:body-types.adoc[Message Body Types] for what the body types are, the 
tools that use them, how components
+declare them, and the JSON of each side with the rule to read it for the 
options of a step.
 
 === Documentation in the Catalog
 
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 305f64e8ccd0..15dae476ac2a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -318,7 +318,7 @@ project `directory` as an argument, the runtime tools take 
the integration `name
   its group of `application.properties` keys, the key first. A component, data 
format or language that
   declares its body types has them in `bodyTypes`, a line per side: what a 
`from:` gives the route
   (`consumerOut`), what a `to:` can send (`producerIn`) and the body after it 
(`producerOut`), and so on (see
-  xref:camel-catalog.adoc#_body_types_in_the_catalog[Body types in the 
Catalog]). An answer is cut at 18 000
+  xref:body-types.adoc[Message Body Types]). An answer is cut at 18 000
   characters, with a marker that says what was left out.
   `includeOptions` picks the options listed: `common` (the default) leaves out 
the deprecated and advanced
   ones and says how many with `omittedOptions`, `required` lists the required 
ones, `all` everything, `false`
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
index 54c39089b805..709da62cf506 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
@@ -76,7 +76,7 @@ A part of a Java route the parser cannot work out (a value 
computed at runtime)
 The YAML validator follows the message body through the steps of each route, 
and reports a step that cannot work on
 it: a field read such as `+${body.qty}+` on text that was never unmarshalled, 
or a Map that a `to: file` cannot write.
 What a component or a data format does with the body comes from the
-xref:camel-catalog.adoc#_body_types_in_the_catalog[body types in the catalog]: 
the file consumer gives the file it
+xref:body-types.adoc[body types in the catalog]: the file consumer gives the 
file it
 read, the `http` producer leaves the response as bytes or a stream, and 
`unmarshal: json` makes a Map or a List. The
 answer of `openai:chat-completion` is text, also when it is asked for JSON 
with `jsonSchema`, so a field read on it
 needs `unmarshal: json` first. Where they do not tell what the body is, the 
validator says nothing: after a `platform-http` consumer the body
diff --git a/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc 
b/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc
index aa9b755a18b8..6bee27e2e912 100644
--- a/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc
+++ b/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc
@@ -33,8 +33,8 @@ annotated with one or more `@UriParam` or `@UriParams` 
annotations
 |`@Metadata` | Used for special situations to provide additional information.
 
 |`@BodyTypes` | The message body the endpoint works with: the body the 
consumer creates, the bodies the
-producer can send, and the body after the producer. See 
xref:camel-catalog.adoc#_body_types_in_the_catalog[Body types
-in the Catalog].
+producer can send, and the body after the producer. See 
xref:body-types.adoc[Message
+Body Types].
 |=======================================================================
 
 For example see the `TimerEndpoint` from the `camel-timer` component and

Reply via email to