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

davsclaus pushed a commit to branch 
feature/CAMEL-24565-devconsole-response-schema
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 16c10f64135a5d5b3a7f123cbbd1286a2918e423
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Aug 29 17:42:25 2026 +0200

    CAMEL-24565: camel-console - Authoritative typed response records for dev 
console OpenAPI response schemas
    
    Adds a nested public record Response(...) to 5 pilot dev consoles
    (circuit-breaker, inflight, type-converters, health, context), declared
    statically next to doCallJson() so any future change to the console's output
    is visible right where the record needs updating too. doCallJson() now
    actually constructs and returns the record (converted to JsonObject), rather
    than a parallel declarative doc that could silently drift from the real
    output.
    
    New infrastructure:
    - JsonRecordSupport (tooling/camel-util-json): generic reflective
      record -> JsonObject converter, the runtime half of the record being the
      single source of truth.
    - GenerateDevConsoleMojo.buildResponseSchema 
(tooling/maven/camel-package-maven-plugin):
      generic reflective record-class -> JSON Schema generator, reading
      @Metadata descriptions off record components. Emits a new "response" key
      into each migrated console's generated dev-console/<id>.json, sibling to
      "options".
    - DevConsoleModel (camel-tooling-model) gains a responseSchema field, wired
      through JsonMapper and consumed by 
DevConsoleOpenApiHelper.buildPathItem(),
      which now populates the OpenAPI response content schema instead of the
      empty placeholder - for both the live api console and the static
      dev-consoles-openapi.json catalog artifact, with no extra code needed in
      ApiDevConsole since it already parses the same catalog schema.
    
    The remaining ~50 dev consoles are unaffected and keep the empty response
    placeholder until a follow-up continues the rollout.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/dev-consoles-openapi.json | 454 ++++++++++++++++++++-
 .../catalog/dev-consoles/circuit-breaker.json      |  34 ++
 .../apache/camel/catalog/dev-consoles/context.json | 201 +++++++++
 .../apache/camel/catalog/dev-consoles/health.json  |  81 ++++
 .../camel/catalog/dev-consoles/inflight.json       |  59 +++
 .../catalog/dev-consoles/type-converters.json      |  74 ++++
 .../org/apache/camel/catalog/CamelCatalogTest.java |  14 +
 .../apache/camel/dev-console/circuit-breaker.json  |  34 ++
 .../org/apache/camel/dev-console/context.json      | 201 +++++++++
 .../org/apache/camel/dev-console/health.json       |  81 ++++
 .../org/apache/camel/dev-console/inflight.json     |  59 +++
 .../apache/camel/dev-console/type-converters.json  |  74 ++++
 .../impl/console/CircuitBreakerDevConsole.java     |  31 +-
 .../camel/impl/console/ContextDevConsole.java      | 228 ++++++-----
 .../camel/impl/console/HealthDevConsole.java       |  83 ++--
 .../apache/camel/impl/console/InflightConsole.java |  50 ++-
 .../camel/impl/console/TypeConverterConsole.java   |  91 +++--
 .../impl/console/CircuitBreakerDevConsoleTest.java |  59 +++
 .../camel/impl/console/ContextDevConsoleTest.java  |  12 +
 .../camel/impl/console/HealthDevConsoleTest.java   |  46 +++
 .../camel/impl/console/InflightConsoleTest.java    |  49 +++
 .../impl/console/TypeConverterConsoleTest.java     |  47 +++
 .../camel/tooling/model/DevConsoleModel.java       |  15 +
 .../tooling/model/DevConsoleOpenApiHelper.java     |   6 +-
 .../org/apache/camel/tooling/model/JsonMapper.java |   4 +
 .../tooling/model/DevConsoleOpenApiHelperTest.java |  34 ++
 .../apache/camel/util/json/JsonRecordSupport.java  |  96 +++++
 .../camel/util/json/JsonRecordSupportTest.java     |  94 +++++
 .../maven/packaging/GenerateDevConsoleMojo.java    | 107 +++++
 .../GenerateDevConsoleMojoResponseSchemaTest.java  | 118 ++++++
 30 files changed, 2334 insertions(+), 202 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
index 6846a963fad3..1dd690aaba0e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
@@ -310,7 +310,40 @@
                                                "description": "Circuit Breaker 
output",
                                                "content": {
                                                        "application\/json": {
-                                                               
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"circuitBreakers": {
+                                                                               
        "type": "array",
+                                                                               
        "items": {
+                                                                               
                "type": "object",
+                                                                               
                "properties": {
+                                                                               
                        "routeId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The route ID"
+                                                                               
                        },
+                                                                               
                        "state": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The circuit breaker state"
+                                                                               
                        },
+                                                                               
                        "successfulCalls": {
+                                                                               
                                "type": "integer",
+                                                                               
                                "description": "Number of successful calls"
+                                                                               
                        },
+                                                                               
                        "failedCalls": {
+                                                                               
                                "type": "integer",
+                                                                               
                                "description": "Number of failed calls"
+                                                                               
                        }
+                                                                               
                },
+                                                                               
                "required": [
+                                                                               
                        "successfulCalls",
+                                                                               
                        "failedCalls"
+                                                                               
                ]
+                                                                               
        },
+                                                                               
        "description": "The circuit breakers"
+                                                                               
}
+                                                                       }
+                                                               }
                                                        }
                                                }
                                        }
@@ -344,7 +377,207 @@
                                                "description": "CamelContext 
output",
                                                "content": {
                                                        "application\/json": {
-                                                               
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"name": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "The CamelContext name"
+                                                                               
},
+                                                                               
"description": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "The CamelContext description (only present when 
configured)"
+                                                                               
},
+                                                                               
"profile": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "The active profile (only present when configured)"
+                                                                               
},
+                                                                               
"version": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "The Camel version"
+                                                                               
},
+                                                                               
"state": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "The CamelContext state"
+                                                                               
},
+                                                                               
"phase": {
+                                                                               
        "type": "integer",
+                                                                               
        "description": "The CamelContext lifecycle phase"
+                                                                               
},
+                                                                               
"startTimestamp": {
+                                                                               
        "type": "integer",
+                                                                               
        "description": "Epoch time in milliseconds the CamelContext was started 
(only present once started)"
+                                                                               
},
+                                                                               
"uptime": {
+                                                                               
        "type": "integer",
+                                                                               
        "description": "Uptime in milliseconds"
+                                                                               
},
+                                                                               
"devMode": {
+                                                                               
        "type": "boolean",
+                                                                               
        "description": "Whether dev mode (live reload) is active"
+                                                                               
},
+                                                                               
"statistics": {
+                                                                               
        "type": "object",
+                                                                               
        "properties": {
+                                                                               
                "routesTotal": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Total number of routes"
+                                                                               
                },
+                                                                               
                "routesStarted": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of started routes"
+                                                                               
                },
+                                                                               
                "load01": {
+                                                                               
                        "type": "string",
+                                                                               
                        "description": "1 minute load average (only present 
when available)"
+                                                                               
                },
+                                                                               
                "load05": {
+                                                                               
                        "type": "string",
+                                                                               
                        "description": "5 minute load average (only present 
when available)"
+                                                                               
                },
+                                                                               
                "load15": {
+                                                                               
                        "type": "string",
+                                                                               
                        "description": "15 minute load average (only present 
when available)"
+                                                                               
                },
+                                                                               
                "exchangesThroughput": {
+                                                                               
                        "type": "string",
+                                                                               
                        "description": "Messages per second throughput (only 
present when available)"
+                                                                               
                },
+                                                                               
                "idleSince": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Epoch time in milliseconds since the 
context has been idle"
+                                                                               
                },
+                                                                               
                "exchangesTotal": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Total number of exchanges"
+                                                                               
                },
+                                                                               
                "exchangesFailed": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of failed exchanges"
+                                                                               
                },
+                                                                               
                "exchangesInflight": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of inflight exchanges"
+                                                                               
                },
+                                                                               
                "remoteExchangesTotal": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Total number of remote exchanges"
+                                                                               
                },
+                                                                               
                "remoteExchangesFailed": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of failed remote exchanges"
+                                                                               
                },
+                                                                               
                "remoteExchangesInflight": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of inflight remote exchanges"
+                                                                               
                },
+                                                                               
                "meanProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Mean processing time in milliseconds"
+                                                                               
                },
+                                                                               
                "maxProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Max processing time in milliseconds"
+                                                                               
                },
+                                                                               
                "minProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Min processing time in milliseconds"
+                                                                               
                },
+                                                                               
                "p50ProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "50th percentile processing time in 
milliseconds (only present when available)"
+                                                                               
                },
+                                                                               
                "p95ProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "95th percentile processing time in 
milliseconds (only present when available)"
+                                                                               
                },
+                                                                               
                "p99ProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "99th percentile processing time in 
milliseconds (only present when available)"
+                                                                               
                },
+                                                                               
                "lastProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Processing time in milliseconds of the 
last exchange (only present once an exchange has completed)"
+                                                                               
                },
+                                                                               
                "deltaProcessingTime": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Difference in processing time in 
milliseconds since the previous exchange (only present once an exchange has 
completed)"
+                                                                               
                },
+                                                                               
                "lastCreatedExchangeTimestamp": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Epoch time in milliseconds the last 
exchange was created (only present once an exchange has been created)"
+                                                                               
                },
+                                                                               
                "lastCompletedExchangeTimestamp": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Epoch time in milliseconds the last 
exchange completed (only present once an exchange has completed)"
+                                                                               
                },
+                                                                               
                "lastFailureHandledExchangeTimestamp": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Epoch time in milliseconds the last 
exchange failure was handled (only present once one has occurred)"
+                                                                               
                },
+                                                                               
                "lastFailedExchangeTimestamp": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Epoch time in milliseconds the last 
exchange failed (only present once one has occurred)"
+                                                                               
                },
+                                                                               
                "reload": {
+                                                                               
                        "type": "object",
+                                                                               
                        "properties": {
+                                                                               
                                "reloaded": {
+                                                                               
                                        "type": "integer",
+                                                                               
                                        "description": "Number of successful 
reloads"
+                                                                               
                                },
+                                                                               
                                "failed": {
+                                                                               
                                        "type": "integer",
+                                                                               
                                        "description": "Number of failed 
reloads"
+                                                                               
                                },
+                                                                               
                                "lastError": {
+                                                                               
                                        "type": "object",
+                                                                               
                                        "properties": {
+                                                                               
                                                "message": {
+                                                                               
                                                        "type": "string",
+                                                                               
                                                        "description": "The 
error message"
+                                                                               
                                                },
+                                                                               
                                                "stackTrace": {
+                                                                               
                                                        "type": "array",
+                                                                               
                                                        "items": {
+                                                                               
                                                                "type": "string"
+                                                                               
                                                        },
+                                                                               
                                                        "description": "The 
error stack trace, one entry per line"
+                                                                               
                                                }
+                                                                               
                                        },
+                                                                               
                                        "description": "The last reload error 
(only present when a reload has failed)"
+                                                                               
                                }
+                                                                               
                        },
+                                                                               
                        "required": [
+                                                                               
                                "reloaded",
+                                                                               
                                "failed"
+                                                                               
                        ],
+                                                                               
                        "description": "Reload statistics"
+                                                                               
                }
+                                                                               
        },
+                                                                               
        "required": [
+                                                                               
                "routesTotal",
+                                                                               
                "routesStarted",
+                                                                               
                "idleSince",
+                                                                               
                "exchangesTotal",
+                                                                               
                "exchangesFailed",
+                                                                               
                "exchangesInflight",
+                                                                               
                "remoteExchangesTotal",
+                                                                               
                "remoteExchangesFailed",
+                                                                               
                "remoteExchangesInflight",
+                                                                               
                "meanProcessingTime",
+                                                                               
                "maxProcessingTime",
+                                                                               
                "minProcessingTime"
+                                                                               
        ],
+                                                                               
        "description": "Runtime statistics (only present when management is 
enabled)"
+                                                                               
}
+                                                                       },
+                                                                       
"required": [
+                                                                               
"phase",
+                                                                               
"uptime",
+                                                                               
"devMode"
+                                                                       ]
+                                                               }
                                                        }
                                                }
                                        }
@@ -692,7 +925,87 @@
                                                "description": "Health Check 
output",
                                                "content": {
                                                        "application\/json": {
-                                                               
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"up": {
+                                                                               
        "type": "boolean",
+                                                                               
        "description": "Whether the overall health status is up"
+                                                                               
},
+                                                                               
"ready": {
+                                                                               
        "type": "boolean",
+                                                                               
        "description": "Whether the readiness checks are up"
+                                                                               
},
+                                                                               
"live": {
+                                                                               
        "type": "boolean",
+                                                                               
        "description": "Whether the liveness checks are up"
+                                                                               
},
+                                                                               
"checks": {
+                                                                               
        "type": "array",
+                                                                               
        "items": {
+                                                                               
                "type": "object",
+                                                                               
                "properties": {
+                                                                               
                        "id": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The health check ID"
+                                                                               
                        },
+                                                                               
                        "group": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The health check group"
+                                                                               
                        },
+                                                                               
                        "up": {
+                                                                               
                                "type": "boolean",
+                                                                               
                                "description": "Whether the health check is up"
+                                                                               
                        },
+                                                                               
                        "state": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The health check state"
+                                                                               
                        },
+                                                                               
                        "enabled": {
+                                                                               
                                "type": "boolean",
+                                                                               
                                "description": "Whether the health check is 
enabled"
+                                                                               
                        },
+                                                                               
                        "readiness": {
+                                                                               
                                "type": "boolean",
+                                                                               
                                "description": "Whether the health check is a 
readiness check"
+                                                                               
                        },
+                                                                               
                        "liveness": {
+                                                                               
                                "type": "boolean",
+                                                                               
                                "description": "Whether the health check is a 
liveness check"
+                                                                               
                        },
+                                                                               
                        "message": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The failure message (only 
present when not up)"
+                                                                               
                        },
+                                                                               
                        "stackTrace": {
+                                                                               
                                "type": "array",
+                                                                               
                                "items": {
+                                                                               
                                        "type": "string"
+                                                                               
                                },
+                                                                               
                                "description": "The failure stack trace, one 
entry per line (only present when not up and an error is available)"
+                                                                               
                        },
+                                                                               
                        "details": {
+                                                                               
                                "type": "object",
+                                                                               
                                "additionalProperties": true,
+                                                                               
                                "description": "Additional health check 
specific details (only present when available)"
+                                                                               
                        }
+                                                                               
                },
+                                                                               
                "required": [
+                                                                               
                        "up",
+                                                                               
                        "enabled",
+                                                                               
                        "readiness",
+                                                                               
                        "liveness"
+                                                                               
                ]
+                                                                               
        },
+                                                                               
        "description": "The individual health checks"
+                                                                               
}
+                                                                       },
+                                                                       
"required": [
+                                                                               
"up",
+                                                                               
"ready",
+                                                                               
"live"
+                                                                       ]
+                                                               }
                                                        }
                                                }
                                        }
@@ -812,7 +1125,65 @@
                                                "description": "Inflight 
Exchanges output",
                                                "content": {
                                                        "application\/json": {
-                                                               
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"inflight": {
+                                                                               
        "type": "integer",
+                                                                               
        "description": "Number of inflight exchanges"
+                                                                               
},
+                                                                               
"inflightBrowseEnabled": {
+                                                                               
        "type": "boolean",
+                                                                               
        "description": "Whether browsing inflight exchanges is enabled"
+                                                                               
},
+                                                                               
"exchanges": {
+                                                                               
        "type": "array",
+                                                                               
        "items": {
+                                                                               
                "type": "object",
+                                                                               
                "properties": {
+                                                                               
                        "exchangeId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The exchange ID"
+                                                                               
                        },
+                                                                               
                        "fromRouteId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The route ID where the exchange 
originated from"
+                                                                               
                        },
+                                                                               
                        "fromRemoteEndpoint": {
+                                                                               
                                "type": "boolean",
+                                                                               
                                "description": "Whether the exchange originated 
from a remote endpoint"
+                                                                               
                        },
+                                                                               
                        "atRouteId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The route ID where the exchange 
currently is"
+                                                                               
                        },
+                                                                               
                        "nodeId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The node ID where the exchange 
currently is"
+                                                                               
                        },
+                                                                               
                        "elapsed": {
+                                                                               
                                "type": "integer",
+                                                                               
                                "description": "Elapsed time in milliseconds 
since the exchange started"
+                                                                               
                        },
+                                                                               
                        "duration": {
+                                                                               
                                "type": "integer",
+                                                                               
                                "description": "Duration in milliseconds the 
exchange has been at the current node"
+                                                                               
                        }
+                                                                               
                },
+                                                                               
                "required": [
+                                                                               
                        "fromRemoteEndpoint",
+                                                                               
                        "elapsed",
+                                                                               
                        "duration"
+                                                                               
                ]
+                                                                               
        },
+                                                                               
        "description": "The inflight exchanges (only present when browsing is 
enabled)"
+                                                                               
}
+                                                                       },
+                                                                       
"required": [
+                                                                               
"inflight",
+                                                                               
"inflightBrowseEnabled"
+                                                                       ]
+                                                               }
                                                        }
                                                }
                                        }
@@ -2431,7 +2802,80 @@
                                                "description": "Type Converters 
output",
                                                "content": {
                                                        "application\/json": {
-                                                               
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"size": {
+                                                                               
        "type": "integer",
+                                                                               
        "description": "Number of registered type converters"
+                                                                               
},
+                                                                               
"exists": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "Whether a type converter must exist or not"
+                                                                               
},
+                                                                               
"existsLoggingLevel": {
+                                                                               
        "type": "string",
+                                                                               
        "description": "Logging level used when a type converter does not exist"
+                                                                               
},
+                                                                               
"statistics": {
+                                                                               
        "type": "object",
+                                                                               
        "properties": {
+                                                                               
                "attemptCounter": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of attempted conversions"
+                                                                               
                },
+                                                                               
                "hitCounter": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of successful conversions (cache 
hit)"
+                                                                               
                },
+                                                                               
                "missCounter": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of cache misses"
+                                                                               
                },
+                                                                               
                "failedCounter": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of failed conversions"
+                                                                               
                },
+                                                                               
                "noopCounter": {
+                                                                               
                        "type": "integer",
+                                                                               
                        "description": "Number of noop conversions"
+                                                                               
                }
+                                                                               
        },
+                                                                               
        "required": [
+                                                                               
                "attemptCounter",
+                                                                               
                "hitCounter",
+                                                                               
                "missCounter",
+                                                                               
                "failedCounter",
+                                                                               
                "noopCounter"
+                                                                               
        ],
+                                                                               
        "description": "Type converter usage statistics (only present when 
statistics are enabled)"
+                                                                               
},
+                                                                               
"converters": {
+                                                                               
        "type": "array",
+                                                                               
        "items": {
+                                                                               
                "type": "object",
+                                                                               
                "properties": {
+                                                                               
                        "from": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The source type"
+                                                                               
                        },
+                                                                               
                        "to": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The target type"
+                                                                               
                        },
+                                                                               
                        "converterClass": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The type converter 
implementation class"
+                                                                               
                        }
+                                                                               
                }
+                                                                               
        },
+                                                                               
        "description": "The registered type converters"
+                                                                               
}
+                                                                       },
+                                                                       
"required": [
+                                                                               
"size"
+                                                                       ]
+                                                               }
                                                        }
                                                }
                                        }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/circuit-breaker.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/circuit-breaker.json
index 5426d9653234..af45681fbece 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/circuit-breaker.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/circuit-breaker.json
@@ -11,6 +11,40 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "circuitBreakers": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "routeId": {
+              "type": "string",
+              "description": "The route ID"
+            },
+            "state": {
+              "type": "string",
+              "description": "The circuit breaker state"
+            },
+            "successfulCalls": {
+              "type": "integer",
+              "description": "Number of successful calls"
+            },
+            "failedCalls": {
+              "type": "integer",
+              "description": "Number of failed calls"
+            }
+          },
+          "required": [
+            "successfulCalls",
+            "failedCalls"
+          ]
+        },
+        "description": "The circuit breakers"
+      }
+    }
   }
 }
 
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/context.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/context.json
index aa9c21003630..abf65937f19a 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/context.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/context.json
@@ -11,6 +11,207 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "name": {
+        "type": "string",
+        "description": "The CamelContext name"
+      },
+      "description": {
+        "type": "string",
+        "description": "The CamelContext description (only present when 
configured)"
+      },
+      "profile": {
+        "type": "string",
+        "description": "The active profile (only present when configured)"
+      },
+      "version": {
+        "type": "string",
+        "description": "The Camel version"
+      },
+      "state": {
+        "type": "string",
+        "description": "The CamelContext state"
+      },
+      "phase": {
+        "type": "integer",
+        "description": "The CamelContext lifecycle phase"
+      },
+      "startTimestamp": {
+        "type": "integer",
+        "description": "Epoch time in milliseconds the CamelContext was 
started (only present once started)"
+      },
+      "uptime": {
+        "type": "integer",
+        "description": "Uptime in milliseconds"
+      },
+      "devMode": {
+        "type": "boolean",
+        "description": "Whether dev mode (live reload) is active"
+      },
+      "statistics": {
+        "type": "object",
+        "properties": {
+          "routesTotal": {
+            "type": "integer",
+            "description": "Total number of routes"
+          },
+          "routesStarted": {
+            "type": "integer",
+            "description": "Number of started routes"
+          },
+          "load01": {
+            "type": "string",
+            "description": "1 minute load average (only present when 
available)"
+          },
+          "load05": {
+            "type": "string",
+            "description": "5 minute load average (only present when 
available)"
+          },
+          "load15": {
+            "type": "string",
+            "description": "15 minute load average (only present when 
available)"
+          },
+          "exchangesThroughput": {
+            "type": "string",
+            "description": "Messages per second throughput (only present when 
available)"
+          },
+          "idleSince": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds since the context has 
been idle"
+          },
+          "exchangesTotal": {
+            "type": "integer",
+            "description": "Total number of exchanges"
+          },
+          "exchangesFailed": {
+            "type": "integer",
+            "description": "Number of failed exchanges"
+          },
+          "exchangesInflight": {
+            "type": "integer",
+            "description": "Number of inflight exchanges"
+          },
+          "remoteExchangesTotal": {
+            "type": "integer",
+            "description": "Total number of remote exchanges"
+          },
+          "remoteExchangesFailed": {
+            "type": "integer",
+            "description": "Number of failed remote exchanges"
+          },
+          "remoteExchangesInflight": {
+            "type": "integer",
+            "description": "Number of inflight remote exchanges"
+          },
+          "meanProcessingTime": {
+            "type": "integer",
+            "description": "Mean processing time in milliseconds"
+          },
+          "maxProcessingTime": {
+            "type": "integer",
+            "description": "Max processing time in milliseconds"
+          },
+          "minProcessingTime": {
+            "type": "integer",
+            "description": "Min processing time in milliseconds"
+          },
+          "p50ProcessingTime": {
+            "type": "integer",
+            "description": "50th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "p95ProcessingTime": {
+            "type": "integer",
+            "description": "95th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "p99ProcessingTime": {
+            "type": "integer",
+            "description": "99th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "lastProcessingTime": {
+            "type": "integer",
+            "description": "Processing time in milliseconds of the last 
exchange (only present once an exchange has completed)"
+          },
+          "deltaProcessingTime": {
+            "type": "integer",
+            "description": "Difference in processing time in milliseconds 
since the previous exchange (only present once an exchange has completed)"
+          },
+          "lastCreatedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange was 
created (only present once an exchange has been created)"
+          },
+          "lastCompletedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
completed (only present once an exchange has completed)"
+          },
+          "lastFailureHandledExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
failure was handled (only present once one has occurred)"
+          },
+          "lastFailedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
failed (only present once one has occurred)"
+          },
+          "reload": {
+            "type": "object",
+            "properties": {
+              "reloaded": {
+                "type": "integer",
+                "description": "Number of successful reloads"
+              },
+              "failed": {
+                "type": "integer",
+                "description": "Number of failed reloads"
+              },
+              "lastError": {
+                "type": "object",
+                "properties": {
+                  "message": {
+                    "type": "string",
+                    "description": "The error message"
+                  },
+                  "stackTrace": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The error stack trace, one entry per line"
+                  }
+                },
+                "description": "The last reload error (only present when a 
reload has failed)"
+              }
+            },
+            "required": [
+              "reloaded",
+              "failed"
+            ],
+            "description": "Reload statistics"
+          }
+        },
+        "required": [
+          "routesTotal",
+          "routesStarted",
+          "idleSince",
+          "exchangesTotal",
+          "exchangesFailed",
+          "exchangesInflight",
+          "remoteExchangesTotal",
+          "remoteExchangesFailed",
+          "remoteExchangesInflight",
+          "meanProcessingTime",
+          "maxProcessingTime",
+          "minProcessingTime"
+        ],
+        "description": "Runtime statistics (only present when management is 
enabled)"
+      }
+    },
+    "required": [
+      "phase",
+      "uptime",
+      "devMode"
+    ]
   }
 }
 
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/health.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/health.json
index e5083a15e3e5..7ea7a35af2d5 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/health.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/health.json
@@ -33,6 +33,87 @@
       "defaultValue": "default",
       "description": "Exposure level for health check details"
     }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "up": {
+        "type": "boolean",
+        "description": "Whether the overall health status is up"
+      },
+      "ready": {
+        "type": "boolean",
+        "description": "Whether the readiness checks are up"
+      },
+      "live": {
+        "type": "boolean",
+        "description": "Whether the liveness checks are up"
+      },
+      "checks": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "id": {
+              "type": "string",
+              "description": "The health check ID"
+            },
+            "group": {
+              "type": "string",
+              "description": "The health check group"
+            },
+            "up": {
+              "type": "boolean",
+              "description": "Whether the health check is up"
+            },
+            "state": {
+              "type": "string",
+              "description": "The health check state"
+            },
+            "enabled": {
+              "type": "boolean",
+              "description": "Whether the health check is enabled"
+            },
+            "readiness": {
+              "type": "boolean",
+              "description": "Whether the health check is a readiness check"
+            },
+            "liveness": {
+              "type": "boolean",
+              "description": "Whether the health check is a liveness check"
+            },
+            "message": {
+              "type": "string",
+              "description": "The failure message (only present when not up)"
+            },
+            "stackTrace": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              },
+              "description": "The failure stack trace, one entry per line 
(only present when not up and an error is available)"
+            },
+            "details": {
+              "type": "object",
+              "additionalProperties": true,
+              "description": "Additional health check specific details (only 
present when available)"
+            }
+          },
+          "required": [
+            "up",
+            "enabled",
+            "readiness",
+            "liveness"
+          ]
+        },
+        "description": "The individual health checks"
+      }
+    },
+    "required": [
+      "up",
+      "ready",
+      "live"
+    ]
   }
 }
 
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/inflight.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/inflight.json
index b758445cf916..b3b3126529fa 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/inflight.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/inflight.json
@@ -41,6 +41,65 @@
       "secret": false,
       "description": "Limits the number of entries displayed"
     }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "inflight": {
+        "type": "integer",
+        "description": "Number of inflight exchanges"
+      },
+      "inflightBrowseEnabled": {
+        "type": "boolean",
+        "description": "Whether browsing inflight exchanges is enabled"
+      },
+      "exchanges": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "exchangeId": {
+              "type": "string",
+              "description": "The exchange ID"
+            },
+            "fromRouteId": {
+              "type": "string",
+              "description": "The route ID where the exchange originated from"
+            },
+            "fromRemoteEndpoint": {
+              "type": "boolean",
+              "description": "Whether the exchange originated from a remote 
endpoint"
+            },
+            "atRouteId": {
+              "type": "string",
+              "description": "The route ID where the exchange currently is"
+            },
+            "nodeId": {
+              "type": "string",
+              "description": "The node ID where the exchange currently is"
+            },
+            "elapsed": {
+              "type": "integer",
+              "description": "Elapsed time in milliseconds since the exchange 
started"
+            },
+            "duration": {
+              "type": "integer",
+              "description": "Duration in milliseconds the exchange has been 
at the current node"
+            }
+          },
+          "required": [
+            "fromRemoteEndpoint",
+            "elapsed",
+            "duration"
+          ]
+        },
+        "description": "The inflight exchanges (only present when browsing is 
enabled)"
+      }
+    },
+    "required": [
+      "inflight",
+      "inflightBrowseEnabled"
+    ]
   }
 }
 
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/type-converters.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/type-converters.json
index 45f5ea1da158..046ab339dae9 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/type-converters.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/type-converters.json
@@ -11,6 +11,80 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "size": {
+        "type": "integer",
+        "description": "Number of registered type converters"
+      },
+      "exists": {
+        "type": "string",
+        "description": "Whether a type converter must exist or not"
+      },
+      "existsLoggingLevel": {
+        "type": "string",
+        "description": "Logging level used when a type converter does not 
exist"
+      },
+      "statistics": {
+        "type": "object",
+        "properties": {
+          "attemptCounter": {
+            "type": "integer",
+            "description": "Number of attempted conversions"
+          },
+          "hitCounter": {
+            "type": "integer",
+            "description": "Number of successful conversions (cache hit)"
+          },
+          "missCounter": {
+            "type": "integer",
+            "description": "Number of cache misses"
+          },
+          "failedCounter": {
+            "type": "integer",
+            "description": "Number of failed conversions"
+          },
+          "noopCounter": {
+            "type": "integer",
+            "description": "Number of noop conversions"
+          }
+        },
+        "required": [
+          "attemptCounter",
+          "hitCounter",
+          "missCounter",
+          "failedCounter",
+          "noopCounter"
+        ],
+        "description": "Type converter usage statistics (only present when 
statistics are enabled)"
+      },
+      "converters": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "from": {
+              "type": "string",
+              "description": "The source type"
+            },
+            "to": {
+              "type": "string",
+              "description": "The target type"
+            },
+            "converterClass": {
+              "type": "string",
+              "description": "The type converter implementation class"
+            }
+          }
+        },
+        "description": "The registered type converters"
+      }
+    },
+    "required": [
+      "size"
+    ]
   }
 }
 
diff --git 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index dda65723921c..13cb5ac34d14 100644
--- 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -1795,6 +1795,20 @@ public class CamelCatalogTest {
         JsonObject post = route.getJsonObject("post");
         Assertions.assertNotNull(post);
         Assertions.assertNotNull(post.getJsonObject("requestBody"));
+
+        // a console migrated to an authoritative typed Response record has a 
real response schema
+        JsonObject circuitBreaker = 
paths.getJsonObject("/q/dev/circuit-breaker");
+        JsonObject cbSchema = 
circuitBreaker.getJsonObject("get").getJsonObject("responses").getJsonObject("200")
+                
.getJsonObject("content").getJsonObject("application/json").getJsonObject("schema");
+        Assertions.assertNotNull(cbSchema);
+        Assertions.assertEquals("object", cbSchema.getString("type"));
+        
Assertions.assertNotNull(cbSchema.getJsonObject("properties").getJsonObject("circuitBreakers"));
+
+        // a console not yet migrated still has the empty placeholder
+        JsonObject jvm = paths.getJsonObject("/q/dev/jvm");
+        JsonObject jvmContent = 
jvm.getJsonObject("get").getJsonObject("responses").getJsonObject("200")
+                .getJsonObject("content").getJsonObject("application/json");
+        Assertions.assertTrue(jvmContent.isEmpty());
     }
 
     @Test
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/circuit-breaker.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/circuit-breaker.json
index 5426d9653234..af45681fbece 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/circuit-breaker.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/circuit-breaker.json
@@ -11,6 +11,40 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "circuitBreakers": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "routeId": {
+              "type": "string",
+              "description": "The route ID"
+            },
+            "state": {
+              "type": "string",
+              "description": "The circuit breaker state"
+            },
+            "successfulCalls": {
+              "type": "integer",
+              "description": "Number of successful calls"
+            },
+            "failedCalls": {
+              "type": "integer",
+              "description": "Number of failed calls"
+            }
+          },
+          "required": [
+            "successfulCalls",
+            "failedCalls"
+          ]
+        },
+        "description": "The circuit breakers"
+      }
+    }
   }
 }
 
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/context.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/context.json
index aa9c21003630..abf65937f19a 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/context.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/context.json
@@ -11,6 +11,207 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "name": {
+        "type": "string",
+        "description": "The CamelContext name"
+      },
+      "description": {
+        "type": "string",
+        "description": "The CamelContext description (only present when 
configured)"
+      },
+      "profile": {
+        "type": "string",
+        "description": "The active profile (only present when configured)"
+      },
+      "version": {
+        "type": "string",
+        "description": "The Camel version"
+      },
+      "state": {
+        "type": "string",
+        "description": "The CamelContext state"
+      },
+      "phase": {
+        "type": "integer",
+        "description": "The CamelContext lifecycle phase"
+      },
+      "startTimestamp": {
+        "type": "integer",
+        "description": "Epoch time in milliseconds the CamelContext was 
started (only present once started)"
+      },
+      "uptime": {
+        "type": "integer",
+        "description": "Uptime in milliseconds"
+      },
+      "devMode": {
+        "type": "boolean",
+        "description": "Whether dev mode (live reload) is active"
+      },
+      "statistics": {
+        "type": "object",
+        "properties": {
+          "routesTotal": {
+            "type": "integer",
+            "description": "Total number of routes"
+          },
+          "routesStarted": {
+            "type": "integer",
+            "description": "Number of started routes"
+          },
+          "load01": {
+            "type": "string",
+            "description": "1 minute load average (only present when 
available)"
+          },
+          "load05": {
+            "type": "string",
+            "description": "5 minute load average (only present when 
available)"
+          },
+          "load15": {
+            "type": "string",
+            "description": "15 minute load average (only present when 
available)"
+          },
+          "exchangesThroughput": {
+            "type": "string",
+            "description": "Messages per second throughput (only present when 
available)"
+          },
+          "idleSince": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds since the context has 
been idle"
+          },
+          "exchangesTotal": {
+            "type": "integer",
+            "description": "Total number of exchanges"
+          },
+          "exchangesFailed": {
+            "type": "integer",
+            "description": "Number of failed exchanges"
+          },
+          "exchangesInflight": {
+            "type": "integer",
+            "description": "Number of inflight exchanges"
+          },
+          "remoteExchangesTotal": {
+            "type": "integer",
+            "description": "Total number of remote exchanges"
+          },
+          "remoteExchangesFailed": {
+            "type": "integer",
+            "description": "Number of failed remote exchanges"
+          },
+          "remoteExchangesInflight": {
+            "type": "integer",
+            "description": "Number of inflight remote exchanges"
+          },
+          "meanProcessingTime": {
+            "type": "integer",
+            "description": "Mean processing time in milliseconds"
+          },
+          "maxProcessingTime": {
+            "type": "integer",
+            "description": "Max processing time in milliseconds"
+          },
+          "minProcessingTime": {
+            "type": "integer",
+            "description": "Min processing time in milliseconds"
+          },
+          "p50ProcessingTime": {
+            "type": "integer",
+            "description": "50th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "p95ProcessingTime": {
+            "type": "integer",
+            "description": "95th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "p99ProcessingTime": {
+            "type": "integer",
+            "description": "99th percentile processing time in milliseconds 
(only present when available)"
+          },
+          "lastProcessingTime": {
+            "type": "integer",
+            "description": "Processing time in milliseconds of the last 
exchange (only present once an exchange has completed)"
+          },
+          "deltaProcessingTime": {
+            "type": "integer",
+            "description": "Difference in processing time in milliseconds 
since the previous exchange (only present once an exchange has completed)"
+          },
+          "lastCreatedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange was 
created (only present once an exchange has been created)"
+          },
+          "lastCompletedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
completed (only present once an exchange has completed)"
+          },
+          "lastFailureHandledExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
failure was handled (only present once one has occurred)"
+          },
+          "lastFailedExchangeTimestamp": {
+            "type": "integer",
+            "description": "Epoch time in milliseconds the last exchange 
failed (only present once one has occurred)"
+          },
+          "reload": {
+            "type": "object",
+            "properties": {
+              "reloaded": {
+                "type": "integer",
+                "description": "Number of successful reloads"
+              },
+              "failed": {
+                "type": "integer",
+                "description": "Number of failed reloads"
+              },
+              "lastError": {
+                "type": "object",
+                "properties": {
+                  "message": {
+                    "type": "string",
+                    "description": "The error message"
+                  },
+                  "stackTrace": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The error stack trace, one entry per line"
+                  }
+                },
+                "description": "The last reload error (only present when a 
reload has failed)"
+              }
+            },
+            "required": [
+              "reloaded",
+              "failed"
+            ],
+            "description": "Reload statistics"
+          }
+        },
+        "required": [
+          "routesTotal",
+          "routesStarted",
+          "idleSince",
+          "exchangesTotal",
+          "exchangesFailed",
+          "exchangesInflight",
+          "remoteExchangesTotal",
+          "remoteExchangesFailed",
+          "remoteExchangesInflight",
+          "meanProcessingTime",
+          "maxProcessingTime",
+          "minProcessingTime"
+        ],
+        "description": "Runtime statistics (only present when management is 
enabled)"
+      }
+    },
+    "required": [
+      "phase",
+      "uptime",
+      "devMode"
+    ]
   }
 }
 
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/health.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/health.json
index e5083a15e3e5..7ea7a35af2d5 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/health.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/health.json
@@ -33,6 +33,87 @@
       "defaultValue": "default",
       "description": "Exposure level for health check details"
     }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "up": {
+        "type": "boolean",
+        "description": "Whether the overall health status is up"
+      },
+      "ready": {
+        "type": "boolean",
+        "description": "Whether the readiness checks are up"
+      },
+      "live": {
+        "type": "boolean",
+        "description": "Whether the liveness checks are up"
+      },
+      "checks": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "id": {
+              "type": "string",
+              "description": "The health check ID"
+            },
+            "group": {
+              "type": "string",
+              "description": "The health check group"
+            },
+            "up": {
+              "type": "boolean",
+              "description": "Whether the health check is up"
+            },
+            "state": {
+              "type": "string",
+              "description": "The health check state"
+            },
+            "enabled": {
+              "type": "boolean",
+              "description": "Whether the health check is enabled"
+            },
+            "readiness": {
+              "type": "boolean",
+              "description": "Whether the health check is a readiness check"
+            },
+            "liveness": {
+              "type": "boolean",
+              "description": "Whether the health check is a liveness check"
+            },
+            "message": {
+              "type": "string",
+              "description": "The failure message (only present when not up)"
+            },
+            "stackTrace": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              },
+              "description": "The failure stack trace, one entry per line 
(only present when not up and an error is available)"
+            },
+            "details": {
+              "type": "object",
+              "additionalProperties": true,
+              "description": "Additional health check specific details (only 
present when available)"
+            }
+          },
+          "required": [
+            "up",
+            "enabled",
+            "readiness",
+            "liveness"
+          ]
+        },
+        "description": "The individual health checks"
+      }
+    },
+    "required": [
+      "up",
+      "ready",
+      "live"
+    ]
   }
 }
 
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/inflight.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/inflight.json
index b758445cf916..b3b3126529fa 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/inflight.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/inflight.json
@@ -41,6 +41,65 @@
       "secret": false,
       "description": "Limits the number of entries displayed"
     }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "inflight": {
+        "type": "integer",
+        "description": "Number of inflight exchanges"
+      },
+      "inflightBrowseEnabled": {
+        "type": "boolean",
+        "description": "Whether browsing inflight exchanges is enabled"
+      },
+      "exchanges": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "exchangeId": {
+              "type": "string",
+              "description": "The exchange ID"
+            },
+            "fromRouteId": {
+              "type": "string",
+              "description": "The route ID where the exchange originated from"
+            },
+            "fromRemoteEndpoint": {
+              "type": "boolean",
+              "description": "Whether the exchange originated from a remote 
endpoint"
+            },
+            "atRouteId": {
+              "type": "string",
+              "description": "The route ID where the exchange currently is"
+            },
+            "nodeId": {
+              "type": "string",
+              "description": "The node ID where the exchange currently is"
+            },
+            "elapsed": {
+              "type": "integer",
+              "description": "Elapsed time in milliseconds since the exchange 
started"
+            },
+            "duration": {
+              "type": "integer",
+              "description": "Duration in milliseconds the exchange has been 
at the current node"
+            }
+          },
+          "required": [
+            "fromRemoteEndpoint",
+            "elapsed",
+            "duration"
+          ]
+        },
+        "description": "The inflight exchanges (only present when browsing is 
enabled)"
+      }
+    },
+    "required": [
+      "inflight",
+      "inflightBrowseEnabled"
+    ]
   }
 }
 
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/type-converters.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/type-converters.json
index 45f5ea1da158..046ab339dae9 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/type-converters.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/type-converters.json
@@ -11,6 +11,80 @@
     "groupId": "org.apache.camel",
     "artifactId": "camel-console",
     "version": "4.23.0-SNAPSHOT"
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "size": {
+        "type": "integer",
+        "description": "Number of registered type converters"
+      },
+      "exists": {
+        "type": "string",
+        "description": "Whether a type converter must exist or not"
+      },
+      "existsLoggingLevel": {
+        "type": "string",
+        "description": "Logging level used when a type converter does not 
exist"
+      },
+      "statistics": {
+        "type": "object",
+        "properties": {
+          "attemptCounter": {
+            "type": "integer",
+            "description": "Number of attempted conversions"
+          },
+          "hitCounter": {
+            "type": "integer",
+            "description": "Number of successful conversions (cache hit)"
+          },
+          "missCounter": {
+            "type": "integer",
+            "description": "Number of cache misses"
+          },
+          "failedCounter": {
+            "type": "integer",
+            "description": "Number of failed conversions"
+          },
+          "noopCounter": {
+            "type": "integer",
+            "description": "Number of noop conversions"
+          }
+        },
+        "required": [
+          "attemptCounter",
+          "hitCounter",
+          "missCounter",
+          "failedCounter",
+          "noopCounter"
+        ],
+        "description": "Type converter usage statistics (only present when 
statistics are enabled)"
+      },
+      "converters": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "from": {
+              "type": "string",
+              "description": "The source type"
+            },
+            "to": {
+              "type": "string",
+              "description": "The target type"
+            },
+            "converterClass": {
+              "type": "string",
+              "description": "The type converter implementation class"
+            }
+          }
+        },
+        "description": "The registered type converters"
+      }
+    },
+    "required": [
+      "size"
+    ]
   }
 }
 
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/CircuitBreakerDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/CircuitBreakerDevConsole.java
index dce9f5ca438f..338c77af3843 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/CircuitBreakerDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/CircuitBreakerDevConsole.java
@@ -16,20 +16,32 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.Route;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.RoutePolicy;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.throttling.ThrottlingExceptionRoutePolicy;
 import org.apache.camel.util.TimeUtils;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.JsonRecordSupport;
 
 @DevConsole(name = "circuit-breaker", description = "Display circuit breaker 
information")
 public class CircuitBreakerDevConsole extends AbstractDevConsole {
 
+    public record Entry(
+            @Metadata(description = "The route ID") String routeId,
+            @Metadata(description = "The circuit breaker state") String state,
+            @Metadata(description = "Number of successful calls") int 
successfulCalls,
+            @Metadata(description = "Number of failed calls") int failedCalls) 
{
+    }
+
+    public record Response(@Metadata(description = "The circuit breakers") 
List<Entry> circuitBreakers) {
+    }
+
     public CircuitBreakerDevConsole() {
         super("camel", "circuit-breaker", "Circuit Breaker", "Display circuit 
breaker information");
     }
@@ -57,23 +69,16 @@ public class CircuitBreakerDevConsole extends 
AbstractDevConsole {
 
     @Override
     protected Map<String, Object> doCallJson(Map<String, Object> options) {
-        JsonObject root = new JsonObject();
-
-        final JsonArray list = new JsonArray();
+        List<Entry> entries = new ArrayList<>();
         for (Route route : getCamelContext().getRoutes()) {
             for (RoutePolicy rp : route.getRoutePolicyList()) {
                 if (rp instanceof ThrottlingExceptionRoutePolicy cb) {
-                    JsonObject jo = new JsonObject();
-                    jo.put("routeId", route.getRouteId());
-                    jo.put("state", cb.getStateAsString());
-                    jo.put("successfulCalls", cb.getSuccess());
-                    jo.put("failedCalls", cb.getFailures());
-                    list.add(jo);
+                    entries.add(new Entry(route.getRouteId(), 
cb.getStateAsString(), cb.getSuccess(), cb.getFailures()));
                 }
             }
         }
-        root.put("circuitBreakers", list);
 
-        return root;
+        Response response = new Response(entries);
+        return JsonRecordSupport.toJsonObject(response);
     }
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
index 7124fd3302cb..64f08a622c65 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -16,8 +16,9 @@
  */
 package org.apache.camel.impl.console;
 
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -26,6 +27,7 @@ import org.apache.camel.ContextEvents;
 import org.apache.camel.api.management.ManagedCamelContext;
 import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
 import org.apache.camel.clock.Clock;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.ReloadStrategy;
 import org.apache.camel.spi.ResourceReloadStrategy;
 import org.apache.camel.spi.annotations.DevConsole;
@@ -33,12 +35,64 @@ import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.ExceptionHelper;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.util.TimeUtils;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.JsonRecordSupport;
 
 @DevConsole(name = "context", displayName = "CamelContext", description = 
"Overall information about the CamelContext")
 public class ContextDevConsole extends AbstractDevConsole {
 
+    public record LastError(
+            @Metadata(description = "The error message") String message,
+            @Metadata(description = "The error stack trace, one entry per 
line") List<String> stackTrace) {
+    }
+
+    public record Reload(
+            @Metadata(description = "Number of successful reloads") int 
reloaded,
+            @Metadata(description = "Number of failed reloads") int failed,
+            @Metadata(description = "The last reload error (only present when 
a reload has failed)") LastError lastError) {
+    }
+
+    public record Statistics(
+            @Metadata(description = "Total number of routes") int routesTotal,
+            @Metadata(description = "Number of started routes") int 
routesStarted,
+            @Metadata(description = "1 minute load average (only present when 
available)") String load01,
+            @Metadata(description = "5 minute load average (only present when 
available)") String load05,
+            @Metadata(description = "15 minute load average (only present when 
available)") String load15,
+            @Metadata(description = "Messages per second throughput (only 
present when available)") String exchangesThroughput,
+            @Metadata(description = "Epoch time in milliseconds since the 
context has been idle") long idleSince,
+            @Metadata(description = "Total number of exchanges") long 
exchangesTotal,
+            @Metadata(description = "Number of failed exchanges") long 
exchangesFailed,
+            @Metadata(description = "Number of inflight exchanges") long 
exchangesInflight,
+            @Metadata(description = "Total number of remote exchanges") long 
remoteExchangesTotal,
+            @Metadata(description = "Number of failed remote exchanges") long 
remoteExchangesFailed,
+            @Metadata(description = "Number of inflight remote exchanges") 
long remoteExchangesInflight,
+            @Metadata(description = "Mean processing time in milliseconds") 
long meanProcessingTime,
+            @Metadata(description = "Max processing time in milliseconds") 
long maxProcessingTime,
+            @Metadata(description = "Min processing time in milliseconds") 
long minProcessingTime,
+            @Metadata(description = "50th percentile processing time in 
milliseconds (only present when available)") Long p50ProcessingTime,
+            @Metadata(description = "95th percentile processing time in 
milliseconds (only present when available)") Long p95ProcessingTime,
+            @Metadata(description = "99th percentile processing time in 
milliseconds (only present when available)") Long p99ProcessingTime,
+            @Metadata(description = "Processing time in milliseconds of the 
last exchange (only present once an exchange has completed)") Long 
lastProcessingTime,
+            @Metadata(description = "Difference in processing time in 
milliseconds since the previous exchange (only present once an exchange has 
completed)") Long deltaProcessingTime,
+            @Metadata(description = "Epoch time in milliseconds the last 
exchange was created (only present once an exchange has been created)") Long 
lastCreatedExchangeTimestamp,
+            @Metadata(description = "Epoch time in milliseconds the last 
exchange completed (only present once an exchange has completed)") Long 
lastCompletedExchangeTimestamp,
+            @Metadata(description = "Epoch time in milliseconds the last 
exchange failure was handled (only present once one has occurred)") Long 
lastFailureHandledExchangeTimestamp,
+            @Metadata(description = "Epoch time in milliseconds the last 
exchange failed (only present once one has occurred)") Long 
lastFailedExchangeTimestamp,
+            @Metadata(description = "Reload statistics") Reload reload) {
+    }
+
+    public record Response(
+            @Metadata(description = "The CamelContext name") String name,
+            @Metadata(description = "The CamelContext description (only 
present when configured)") String description,
+            @Metadata(description = "The active profile (only present when 
configured)") String profile,
+            @Metadata(description = "The Camel version") String version,
+            @Metadata(description = "The CamelContext state") String state,
+            @Metadata(description = "The CamelContext lifecycle phase") int 
phase,
+            @Metadata(description = "Epoch time in milliseconds the 
CamelContext was started (only present once started)") Long startTimestamp,
+            @Metadata(description = "Uptime in milliseconds") long uptime,
+            @Metadata(description = "Whether dev mode (live reload) is 
active") boolean devMode,
+            @Metadata(description = "Runtime statistics (only present when 
management is enabled)") Statistics statistics) {
+    }
+
     public ContextDevConsole() {
         super("camel", "context", "CamelContext", "Overall information about 
the CamelContext");
     }
@@ -134,115 +188,91 @@ public class ContextDevConsole extends 
AbstractDevConsole {
         return sb.toString();
     }
 
-    protected JsonObject doCallJson(Map<String, Object> options) {
-        JsonObject root = new JsonObject();
-        root.put("name", getCamelContext().getName());
-        if (getCamelContext().getDescription() != null) {
-            root.put("description", getCamelContext().getDescription());
-        }
-        if (getCamelContext().getCamelContextExtension().getProfile() != null) 
{
-            root.put("profile", 
getCamelContext().getCamelContextExtension().getProfile());
-        }
-        root.put("version", getCamelContext().getVersion());
-        root.put("state", getCamelContext().getStatus().name());
-        root.put("phase", 
getCamelContext().getCamelContextExtension().getStatusPhase());
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
+        String description = getCamelContext().getDescription();
+        String profile = 
getCamelContext().getCamelContextExtension().getProfile();
         long uptimeMillis = getCamelContext().getUptime().toMillis();
         Clock startClock = 
getCamelContext().getClock().get(ContextEvents.START);
-        if (startClock != null) {
-            root.put("startTimestamp", startClock.getCreated());
-        }
-        root.put("uptime", uptimeMillis);
-        root.put("devMode", 
getCamelContext().hasService(ResourceReloadStrategy.class) != null);
+        Long startTimestamp = startClock != null ? startClock.getCreated() : 
null;
+        int phase = 
getCamelContext().getCamelContextExtension().getStatusPhase();
+        boolean devMode = 
getCamelContext().hasService(ResourceReloadStrategy.class) != null;
 
+        Statistics stats = null;
         ManagedCamelContext mcc = 
getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
         if (mcc != null) {
             ManagedCamelContextMBean mb = mcc.getManagedCamelContext();
             if (mb != null) {
-                JsonObject stats = new JsonObject();
+                stats = buildStatistics(mb);
+            }
+        }
 
-                int total = mb.getTotalRoutes();
-                int started = mb.getStartedRoutes();
-                stats.put("routesTotal", total);
-                stats.put("routesStarted", started);
+        Response response = new Response(
+                getCamelContext().getName(), description, profile, 
getCamelContext().getVersion(),
+                getCamelContext().getStatus().name(), phase, startTimestamp, 
uptimeMillis, devMode, stats);
+        return JsonRecordSupport.toJsonObject(response);
+    }
 
-                String load1 = getLoad1(mb);
-                String load5 = getLoad5(mb);
-                String load15 = getLoad15(mb);
-                if (!load1.isEmpty() || !load5.isEmpty() || !load15.isEmpty()) 
{
-                    stats.put("load01", load1);
-                    stats.put("load05", load5);
-                    stats.put("load15", load15);
-                }
-                String thp = getThroughput(mb);
-                if (!thp.isEmpty()) {
-                    stats.put("exchangesThroughput", thp);
-                }
-                stats.put("idleSince", mb.getIdleSince());
-                stats.put("exchangesTotal", mb.getExchangesTotal());
-                stats.put("exchangesFailed", mb.getExchangesFailed());
-                stats.put("exchangesInflight", mb.getExchangesInflight());
-                stats.put("remoteExchangesTotal", 
mb.getRemoteExchangesTotal());
-                stats.put("remoteExchangesFailed", 
mb.getRemoteExchangesFailed());
-                stats.put("remoteExchangesInflight", 
mb.getRemoteExchangesInflight());
-                stats.put("meanProcessingTime", mb.getMeanProcessingTime());
-                stats.put("maxProcessingTime", mb.getMaxProcessingTime());
-                stats.put("minProcessingTime", mb.getMinProcessingTime());
-                if (mb.getProcessingTimeP50() >= 0) {
-                    stats.put("p50ProcessingTime", mb.getProcessingTimeP50());
-                    stats.put("p95ProcessingTime", mb.getProcessingTimeP95());
-                    stats.put("p99ProcessingTime", mb.getProcessingTimeP99());
-                }
-                if (mb.getExchangesTotal() > 0) {
-                    stats.put("lastProcessingTime", 
mb.getLastProcessingTime());
-                    stats.put("deltaProcessingTime", 
mb.getDeltaProcessingTime());
-                }
-                Date last = mb.getLastExchangeCreatedTimestamp();
-                if (last != null) {
-                    stats.put("lastCreatedExchangeTimestamp", last.getTime());
-                }
-                last = mb.getLastExchangeCompletedTimestamp();
-                if (last != null) {
-                    stats.put("lastCompletedExchangeTimestamp", 
last.getTime());
-                }
-                last = mb.getLastExchangeFailureHandledTimestamp();
-                if (last != null) {
-                    stats.put("lastFailureHandledExchangeTimestamp", 
last.getTime());
-                }
-                last = mb.getLastExchangeFailureTimestamp();
-                if (last != null) {
-                    stats.put("lastFailedExchangeTimestamp", last.getTime());
-                }
-                // reload stats
-                int reloaded = 0;
-                int reloadedFailed = 0;
-                Exception reloadCause = null;
-                Set<ReloadStrategy> rs = 
getCamelContext().hasServices(ReloadStrategy.class);
-                for (ReloadStrategy r : rs) {
-                    reloaded += r.getReloadCounter();
-                    reloadedFailed += r.getFailedCounter();
-                    if (reloadCause == null) {
-                        reloadCause = r.getLastError();
-                    }
-                }
-                JsonObject ro = new JsonObject();
-                ro.put("reloaded", reloaded);
-                ro.put("failed", reloadedFailed);
-                if (reloadCause != null) {
-                    JsonObject eo = new JsonObject();
-                    eo.put("message", reloadCause.getMessage());
-                    JsonArray arr2 = new JsonArray();
-                    final String trace = 
ExceptionHelper.stackTraceToString(reloadCause);
-                    eo.put("stackTrace", arr2);
-                    Collections.addAll(arr2, trace.split("\n"));
-                    ro.put("lastError", eo);
-                }
-                stats.put("reload", ro);
+    private Statistics buildStatistics(ManagedCamelContextMBean mb) {
+        String load1 = getLoad1(mb);
+        String load5 = getLoad5(mb);
+        String load15 = getLoad15(mb);
+        boolean hasLoad = !load1.isEmpty() || !load5.isEmpty() || 
!load15.isEmpty();
+
+        String thp = getThroughput(mb);
+
+        Long p50 = null;
+        Long p95 = null;
+        Long p99 = null;
+        if (mb.getProcessingTimeP50() >= 0) {
+            p50 = mb.getProcessingTimeP50();
+            p95 = mb.getProcessingTimeP95();
+            p99 = mb.getProcessingTimeP99();
+        }
+
+        Long lastProcessingTime = null;
+        Long deltaProcessingTime = null;
+        if (mb.getExchangesTotal() > 0) {
+            lastProcessingTime = mb.getLastProcessingTime();
+            deltaProcessingTime = mb.getDeltaProcessingTime();
+        }
+
+        Long lastCreated = timestampOf(mb.getLastExchangeCreatedTimestamp());
+        Long lastCompleted = 
timestampOf(mb.getLastExchangeCompletedTimestamp());
+        Long lastFailureHandled = 
timestampOf(mb.getLastExchangeFailureHandledTimestamp());
+        Long lastFailed = timestampOf(mb.getLastExchangeFailureTimestamp());
 
-                root.put("statistics", stats);
+        // reload stats
+        int reloaded = 0;
+        int reloadedFailed = 0;
+        Exception reloadCause = null;
+        Set<ReloadStrategy> rs = 
getCamelContext().hasServices(ReloadStrategy.class);
+        for (ReloadStrategy r : rs) {
+            reloaded += r.getReloadCounter();
+            reloadedFailed += r.getFailedCounter();
+            if (reloadCause == null) {
+                reloadCause = r.getLastError();
             }
         }
+        LastError lastError = null;
+        if (reloadCause != null) {
+            final String trace = 
ExceptionHelper.stackTraceToString(reloadCause);
+            lastError = new LastError(reloadCause.getMessage(), 
Arrays.asList(trace.split("\n")));
+        }
+        Reload reload = new Reload(reloaded, reloadedFailed, lastError);
+
+        return new Statistics(
+                mb.getTotalRoutes(), mb.getStartedRoutes(),
+                hasLoad ? load1 : null, hasLoad ? load5 : null, hasLoad ? 
load15 : null,
+                thp.isEmpty() ? null : thp,
+                mb.getIdleSince(), mb.getExchangesTotal(), 
mb.getExchangesFailed(), mb.getExchangesInflight(),
+                mb.getRemoteExchangesTotal(), mb.getRemoteExchangesFailed(), 
mb.getRemoteExchangesInflight(),
+                mb.getMeanProcessingTime(), mb.getMaxProcessingTime(), 
mb.getMinProcessingTime(),
+                p50, p95, p99, lastProcessingTime, deltaProcessingTime,
+                lastCreated, lastCompleted, lastFailureHandled, lastFailed, 
reload);
+    }
 
-        return root;
+    private static Long timestampOf(Date date) {
+        return date != null ? date.getTime() : null;
     }
 
     private String getLoad1(ManagedCamelContextMBean mb) {
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
index ed28e197c446..4e101683eb6d 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
@@ -16,8 +16,11 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.stream.Stream;
 
@@ -27,8 +30,7 @@ import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.ExceptionHelper;
 import org.apache.camel.support.console.AbstractDevConsole;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.JsonRecordSupport;
 
 @DevConsole(name = "health", displayName = "Health Check", description = 
"Health Check Status")
 public class HealthDevConsole extends AbstractDevConsole {
@@ -37,6 +39,26 @@ public class HealthDevConsole extends AbstractDevConsole {
               defaultValue = "default", javaType = "java.lang.String", enums = 
"default,oneline,full")
     public static final String EXPOSURE_LEVEL = "exposureLevel";
 
+    public record CheckEntry(
+            @Metadata(description = "The health check ID") String id,
+            @Metadata(description = "The health check group") String group,
+            @Metadata(description = "Whether the health check is up") boolean 
up,
+            @Metadata(description = "The health check state") String state,
+            @Metadata(description = "Whether the health check is enabled") 
boolean enabled,
+            @Metadata(description = "Whether the health check is a readiness 
check") boolean readiness,
+            @Metadata(description = "Whether the health check is a liveness 
check") boolean liveness,
+            @Metadata(description = "The failure message (only present when 
not up)") String message,
+            @Metadata(description = "The failure stack trace, one entry per 
line (only present when not up and an error is available)") List<String> 
stackTrace,
+            @Metadata(description = "Additional health check specific details 
(only present when available)") Map<String, String> details) {
+    }
+
+    public record Response(
+            @Metadata(description = "Whether the overall health status is up") 
boolean up,
+            @Metadata(description = "Whether the readiness checks are up") 
boolean ready,
+            @Metadata(description = "Whether the liveness checks are up") 
boolean live,
+            @Metadata(description = "The individual health checks") 
List<CheckEntry> checks) {
+    }
+
     public HealthDevConsole() {
         super("camel", "health", "Health Check", "Health Check Status");
     }
@@ -79,61 +101,44 @@ public class HealthDevConsole extends AbstractDevConsole {
     }
 
     @Override
-    protected JsonObject doCallJson(Map<String, Object> options) {
-        JsonObject root = new JsonObject();
-
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
         String exposureLevel = optionString(options, EXPOSURE_LEVEL);
         Collection<HealthCheck.Result> readies = 
HealthCheckHelper.invokeReadiness(getCamelContext(), exposureLevel);
         Collection<HealthCheck.Result> lives = 
HealthCheckHelper.invokeLiveness(getCamelContext(), exposureLevel);
         boolean ready = HealthCheckHelper.isResultsUp(readies, true);
         boolean live = HealthCheckHelper.isResultsUp(lives, false);
-        root.put("up", ready && live);
-        root.put("ready", ready);
-        root.put("live", live);
-
-        JsonArray arr = new JsonArray();
-        root.put("checks", arr);
-
-        Stream<HealthCheck.Result> checks = Stream.concat(readies.stream(), 
lives.stream());
-        checks.forEach(res -> {
-            JsonObject jo = new JsonObject();
-            arr.add(jo);
 
+        List<CheckEntry> checks = new ArrayList<>();
+        Stream.concat(readies.stream(), lives.stream()).forEach(res -> {
             boolean ok = res.getState().equals(HealthCheck.State.UP);
-            jo.put("id", res.getCheck().getId());
-            jo.put("group", res.getCheck().getGroup());
-            if (ok) {
-                jo.put("up", true);
-            } else {
-                jo.put("up", false);
-            }
-            jo.put("state", res.getState().toString());
-            jo.put("enabled", res.getCheck().isEnabled());
-            jo.put("readiness", res.getCheck().isReadiness());
-            jo.put("liveness", res.getCheck().isLiveness());
 
+            String message = null;
+            List<String> stackTrace = null;
             if (!ok) {
-                String msg = res.getMessage().orElse("");
-                jo.put("message", msg);
+                message = res.getMessage().orElse("");
 
                 Throwable cause = res.getError().orElse(null);
                 if (cause != null) {
-                    JsonArray arr2 = new JsonArray();
                     final String trace = 
ExceptionHelper.stackTraceToString(cause);
-                    jo.put("stackTrace", arr2);
-                    Collections.addAll(arr2, trace.split("\n"));
+                    stackTrace = Arrays.asList(trace.split("\n"));
                 }
             }
 
+            Map<String, String> details = null;
             if (!res.getDetails().isEmpty()) {
-                JsonObject details = new JsonObject();
-                res.getDetails().forEach((k, v) -> {
-                    details.put(k, v.toString());
-                });
-                jo.put("details", details);
+                details = new LinkedHashMap<>();
+                for (Map.Entry<String, Object> entry : 
res.getDetails().entrySet()) {
+                    details.put(entry.getKey(), entry.getValue().toString());
+                }
             }
+
+            checks.add(new CheckEntry(
+                    res.getCheck().getId(), res.getCheck().getGroup(), ok, 
res.getState().toString(),
+                    res.getCheck().isEnabled(), res.getCheck().isReadiness(), 
res.getCheck().isLiveness(), message,
+                    stackTrace, details));
         });
 
-        return root;
+        Response response = new Response(ready && live, ready, live, checks);
+        return JsonRecordSupport.toJsonObject(response);
     }
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/InflightConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/InflightConsole.java
index 90253559903e..3e730680466b 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/InflightConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/InflightConsole.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.spi.InflightRepository;
@@ -23,8 +25,7 @@ import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.util.TimeUtils;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.JsonRecordSupport;
 
 @DevConsole(name = "inflight", displayName = "Inflight Exchanges", description 
= "Display inflight exchanges")
 public class InflightConsole extends AbstractDevConsole {
@@ -36,6 +37,22 @@ public class InflightConsole extends AbstractDevConsole {
     @Metadata(label = "query", description = "Limits the number of entries 
displayed", javaType = "java.lang.Integer")
     public static final String LIMIT = "limit";
 
+    public record Exchange(
+            @Metadata(description = "The exchange ID") String exchangeId,
+            @Metadata(description = "The route ID where the exchange 
originated from") String fromRouteId,
+            @Metadata(description = "Whether the exchange originated from a 
remote endpoint") boolean fromRemoteEndpoint,
+            @Metadata(description = "The route ID where the exchange currently 
is") String atRouteId,
+            @Metadata(description = "The node ID where the exchange currently 
is") String nodeId,
+            @Metadata(description = "Elapsed time in milliseconds since the 
exchange started") long elapsed,
+            @Metadata(description = "Duration in milliseconds the exchange has 
been at the current node") long duration) {
+    }
+
+    public record Response(
+            @Metadata(description = "Number of inflight exchanges") int 
inflight,
+            @Metadata(description = "Whether browsing inflight exchanges is 
enabled") boolean inflightBrowseEnabled,
+            @Metadata(description = "The inflight exchanges (only present when 
browsing is enabled)") List<Exchange> exchanges) {
+    }
+
     public InflightConsole() {
         super("camel", "inflight", "Inflight Exchanges", "Display inflight 
exchanges");
     }
@@ -63,31 +80,24 @@ public class InflightConsole extends AbstractDevConsole {
     }
 
     @Override
-    protected JsonObject doCallJson(Map<String, Object> options) {
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
         String filter = optionString(options, FILTER);
         int max = optionInt(options, LIMIT, Integer.MAX_VALUE);
 
-        JsonObject root = new JsonObject();
-
         InflightRepository repo = getCamelContext().getInflightRepository();
-        root.put("inflight", repo.size());
-        root.put("inflightBrowseEnabled", repo.isInflightBrowseEnabled());
-        if (repo.isInflightBrowseEnabled()) {
-            final JsonArray list = new JsonArray();
+        boolean browseEnabled = repo.isInflightBrowseEnabled();
+
+        List<Exchange> exchanges = null;
+        if (browseEnabled) {
+            exchanges = new ArrayList<>();
             for (InflightRepository.InflightExchange ie : repo.browse(filter, 
max, false)) {
-                JsonObject props = new JsonObject();
-                props.put("exchangeId", ie.getExchange().getExchangeId());
-                props.put("fromRouteId", ie.getFromRouteId());
-                props.put("fromRemoteEndpoint", ie.isFromRemoteEndpoint());
-                props.put("atRouteId", ie.getAtRouteId());
-                props.put("nodeId", ie.getNodeId());
-                props.put("elapsed", ie.getElapsed());
-                props.put("duration", ie.getDuration());
-                list.add(props);
+                exchanges.add(new Exchange(
+                        ie.getExchange().getExchangeId(), ie.getFromRouteId(), 
ie.isFromRemoteEndpoint(),
+                        ie.getAtRouteId(), ie.getNodeId(), ie.getElapsed(), 
ie.getDuration()));
             }
-            root.put("exchanges", list);
         }
 
-        return root;
+        Response response = new Response(repo.size(), browseEnabled, 
exchanges);
+        return JsonRecordSupport.toJsonObject(response);
     }
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TypeConverterConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TypeConverterConsole.java
index 1d3504fc4e0c..4a3e302d5339 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TypeConverterConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TypeConverterConsole.java
@@ -16,19 +16,45 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.camel.TypeConverter;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.TypeConverterRegistry;
 import org.apache.camel.spi.TypeConvertible;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
-import org.apache.camel.util.json.JsonArray;
-import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.JsonRecordSupport;
 
 @DevConsole(name = "type-converters", description = "Camel Type Converter 
information")
 public class TypeConverterConsole extends AbstractDevConsole {
 
+    public record Statistics(
+            @Metadata(description = "Number of attempted conversions") long 
attemptCounter,
+            @Metadata(description = "Number of successful conversions (cache 
hit)") long hitCounter,
+            @Metadata(description = "Number of cache misses") long missCounter,
+            @Metadata(description = "Number of failed conversions") long 
failedCounter,
+            @Metadata(description = "Number of noop conversions") long 
noopCounter) {
+    }
+
+    public record Converter(
+            @Metadata(description = "The source type") String from,
+            @Metadata(description = "The target type") String to,
+            @Metadata(description = "The type converter implementation class") 
String converterClass) {
+    }
+
+    public record Response(
+            @Metadata(description = "Number of registered type converters") 
int size,
+            @Metadata(description = "Whether a type converter must exist or 
not") String exists,
+            @Metadata(description = "Logging level used when a type converter 
does not exist") String existsLoggingLevel,
+            @Metadata(description = "Type converter usage statistics (only 
present when statistics are enabled)") Statistics statistics,
+            @Metadata(description = "The registered type converters") 
List<Converter> converters) {
+    }
+
     public TypeConverterConsole() {
         super("camel", "type-converters", "Type Converters", "Camel Type 
Converter information");
     }
@@ -53,38 +79,53 @@ public class TypeConverterConsole extends 
AbstractDevConsole {
     }
 
     @Override
-    protected JsonObject doCallJson(Map<String, Object> options) {
-        JsonObject root = new JsonObject();
-
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
         TypeConverterRegistry reg = 
getCamelContext().getTypeConverterRegistry();
-        root.put("size", reg.size());
-        root.put("exists", reg.getTypeConverterExists().name());
-        root.put("existsLoggingLevel", 
reg.getTypeConverterExistsLoggingLevel().name());
 
         final TypeConverterRegistry.Statistics statistics = 
reg.getStatistics();
-        JsonObject props = new JsonObject();
+        AtomicLong attemptCounter = new AtomicLong();
+        AtomicLong hitCounter = new AtomicLong();
+        AtomicLong missCounter = new AtomicLong();
+        AtomicLong failedCounter = new AtomicLong();
+        AtomicLong noopCounter = new AtomicLong();
+        AtomicBoolean statisticsEnabled = new AtomicBoolean();
 
-        statistics.computeIfEnabled(statistics::getAttemptCounter, v -> 
props.put("attemptCounter", v));
-        statistics.computeIfEnabled(statistics::getHitCounter, v -> 
props.put("hitCounter", v));
-        statistics.computeIfEnabled(statistics::getMissCounter, v -> 
props.put("missCounter", v));
-        statistics.computeIfEnabled(statistics::getFailedCounter, v -> 
props.put("failedCounter", v));
-        statistics.computeIfEnabled(statistics::getNoopCounter, v -> 
props.put("noopCounter", v));
+        statistics.computeIfEnabled(statistics::getAttemptCounter, v -> {
+            attemptCounter.set(v);
+            statisticsEnabled.set(true);
+        });
+        statistics.computeIfEnabled(statistics::getHitCounter, v -> {
+            hitCounter.set(v);
+            statisticsEnabled.set(true);
+        });
+        statistics.computeIfEnabled(statistics::getMissCounter, v -> {
+            missCounter.set(v);
+            statisticsEnabled.set(true);
+        });
+        statistics.computeIfEnabled(statistics::getFailedCounter, v -> {
+            failedCounter.set(v);
+            statisticsEnabled.set(true);
+        });
+        statistics.computeIfEnabled(statistics::getNoopCounter, v -> {
+            noopCounter.set(v);
+            statisticsEnabled.set(true);
+        });
 
-        if (!props.isEmpty()) {
-            root.put("statistics", props);
-        }
+        Statistics stats = statisticsEnabled.get()
+                ? new Statistics(
+                        attemptCounter.get(), hitCounter.get(), 
missCounter.get(), failedCounter.get(), noopCounter.get())
+                : null;
 
-        JsonArray arr = new JsonArray();
+        List<Converter> converters = new ArrayList<>();
         for (Map.Entry<TypeConvertible<?, ?>, TypeConverter> e : 
reg.listTypeConverters().entrySet()) {
             TypeConvertible<?, ?> tc = e.getKey();
-            JsonObject jo = new JsonObject();
-            jo.put("from", tc.getFrom().getCanonicalName());
-            jo.put("to", tc.getTo().getCanonicalName());
-            jo.put("converterClass", e.getValue().getClass().getName());
-            arr.add(jo);
+            converters.add(new Converter(
+                    tc.getFrom().getCanonicalName(), 
tc.getTo().getCanonicalName(), e.getValue().getClass().getName()));
         }
-        root.put("converters", arr);
 
-        return root;
+        Response response = new Response(
+                reg.size(), reg.getTypeConverterExists().name(), 
reg.getTypeConverterExistsLoggingLevel().name(), stats,
+                converters);
+        return JsonRecordSupport.toJsonObject(response);
     }
 }
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/CircuitBreakerDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/CircuitBreakerDevConsoleTest.java
new file mode 100644
index 000000000000..6873c5045d58
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/CircuitBreakerDevConsoleTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.throttling.ThrottlingExceptionRoutePolicy;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CircuitBreakerDevConsoleTest extends AbstractDevConsoleTest {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").routeId("myRoute")
+                        .routePolicy(new ThrottlingExceptionRoutePolicy())
+                        .to("mock:result");
+                from("direct:other").routeId("otherRoute")
+                        .to("mock:other");
+            }
+        };
+    }
+
+    @Test
+    public void testCircuitBreakerConsole() {
+        DevConsole con = assertConsoleExists("circuit-breaker", "camel");
+
+        JsonObject out = callJson(con);
+        JsonArray entries = out.getJsonArray("circuitBreakers");
+        assertEquals(1, entries.size());
+
+        JsonObject entry = (JsonObject) entries.get(0);
+        assertEquals("myRoute", entry.getString("routeId"));
+        assertTrue(entry.getString("state").length() > 0);
+        assertEquals(0, entry.getInteger("successfulCalls"));
+        assertEquals(0, entry.getInteger("failedCalls"));
+    }
+}
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
index 0043ea6454f7..ae4303d36afa 100644
--- 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
@@ -48,6 +48,18 @@ public class ContextDevConsoleTest extends 
ContextTestSupport {
         JsonObject out = (JsonObject) con.call(DevConsole.MediaType.JSON);
         Assertions.assertNotNull(out);
         Assertions.assertEquals(context.getName(), out.getString("name"));
+        Assertions.assertEquals(context.getVersion(), 
out.getString("version"));
+        Assertions.assertEquals(context.getStatus().name(), 
out.getString("state"));
+        Assertions.assertNotNull(out.getLong("uptime"));
+        Assertions.assertNotNull(out.getBoolean("devMode"));
+
+        JsonObject statistics = out.getJsonObject("statistics");
+        if (statistics != null) {
+            JsonObject reload = statistics.getJsonObject("reload");
+            Assertions.assertNotNull(reload);
+            Assertions.assertNotNull(reload.getInteger("reloaded"));
+            Assertions.assertNotNull(reload.getInteger("failed"));
+        }
     }
 
 }
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/HealthDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/HealthDevConsoleTest.java
new file mode 100644
index 000000000000..b527e44d089b
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/HealthDevConsoleTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class HealthDevConsoleTest extends AbstractDevConsoleTest {
+
+    @Test
+    public void testHealthConsole() {
+        DevConsole con = assertConsoleExists("health", "camel");
+
+        JsonObject out = callJson(con);
+        assertNotNull(out.getBoolean("up"));
+        assertNotNull(out.getBoolean("ready"));
+        assertNotNull(out.getBoolean("live"));
+
+        JsonArray checks = out.getJsonArray("checks");
+        assertNotNull(checks);
+        for (Object check : checks) {
+            JsonObject jo = (JsonObject) check;
+            assertNotNull(jo.getString("id"));
+            assertNotNull(jo.getBoolean("up"));
+            assertNotNull(jo.getString("state"));
+        }
+    }
+}
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/InflightConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/InflightConsoleTest.java
new file mode 100644
index 000000000000..389c93bc2f2c
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/InflightConsoleTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class InflightConsoleTest extends AbstractDevConsoleTest {
+
+    @Test
+    public void testBrowseDisabledOmitsExchanges() {
+        DevConsole con = assertConsoleExists("inflight", "camel");
+
+        JsonObject out = callJson(con);
+        assertEquals(0, out.getInteger("inflight"));
+        assertEquals(false, out.getBoolean("inflightBrowseEnabled"));
+        assertFalse(out.containsKey("exchanges"));
+    }
+
+    @Test
+    public void testBrowseEnabledIncludesExchanges() {
+        context.getInflightRepository().setInflightBrowseEnabled(true);
+
+        DevConsole con = assertConsoleExists("inflight", "camel");
+
+        JsonObject out = callJson(con);
+        assertEquals(true, out.getBoolean("inflightBrowseEnabled"));
+        assertTrue(out.containsKey("exchanges"));
+    }
+}
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/TypeConverterConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/TypeConverterConsoleTest.java
new file mode 100644
index 000000000000..3353b849a063
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/TypeConverterConsoleTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TypeConverterConsoleTest extends AbstractDevConsoleTest {
+
+    @Test
+    public void testTypeConverterConsole() {
+        DevConsole con = assertConsoleExists("type-converters", "camel");
+
+        JsonObject out = callJson(con);
+        assertTrue(out.getInteger("size") > 0);
+        assertNotNull(out.getString("exists"));
+        assertNotNull(out.getString("existsLoggingLevel"));
+
+        JsonArray converters = out.getJsonArray("converters");
+        assertNotNull(converters);
+        assertTrue(converters.size() > 0);
+
+        JsonObject converter = (JsonObject) converters.get(0);
+        assertNotNull(converter.getString("from"));
+        assertNotNull(converter.getString("to"));
+        assertNotNull(converter.getString("converterClass"));
+    }
+}
diff --git 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleModel.java
 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleModel.java
index 74a4afa5ee0f..e8c37e1b04de 100644
--- 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleModel.java
+++ 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleModel.java
@@ -16,10 +16,13 @@
  */
 package org.apache.camel.tooling.model;
 
+import org.apache.camel.util.json.JsonObject;
+
 public class DevConsoleModel extends 
ArtifactModel<DevConsoleModel.DevConsoleOptionModel> {
 
     protected String group;
     protected boolean readOnly = true;
+    protected JsonObject responseSchema;
 
     public DevConsoleModel() {
     }
@@ -40,6 +43,18 @@ public class DevConsoleModel extends 
ArtifactModel<DevConsoleModel.DevConsoleOpt
         this.readOnly = readOnly;
     }
 
+    /**
+     * The JSON Schema describing this console's response payload, or {@code 
null} if the console has not declared one
+     * (via a nested {@code Response} record).
+     */
+    public JsonObject getResponseSchema() {
+        return responseSchema;
+    }
+
+    public void setResponseSchema(JsonObject responseSchema) {
+        this.responseSchema = responseSchema;
+    }
+
     @Override
     public Kind getKind() {
         return Kind.console;
diff --git 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelper.java
 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelper.java
index 4bbdbd40e855..a3721de99a67 100644
--- 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelper.java
+++ 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelper.java
@@ -79,8 +79,12 @@ public final class DevConsoleOpenApiHelper {
         JsonObject responses = new JsonObject();
         JsonObject ok = new JsonObject();
         ok.put("description", model.getTitle() + " output");
+        JsonObject mediaType = new JsonObject();
+        if (model.getResponseSchema() != null) {
+            mediaType.put("schema", model.getResponseSchema());
+        }
         JsonObject responseContent = new JsonObject();
-        responseContent.put("application/json", new JsonObject());
+        responseContent.put("application/json", mediaType);
         ok.put("content", responseContent);
         responses.put("200", ok);
         operation.put("responses", responses);
diff --git 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
index 46903a93bd7b..721329bc2afc 100644
--- 
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
+++ 
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
@@ -505,6 +505,7 @@ public final class JsonMapper {
                 model.addOption(option);
             }
         }
+        model.setResponseSchema((JsonObject) obj.get("response"));
         return model;
     }
 
@@ -525,6 +526,9 @@ public final class JsonMapper {
         if (!model.getOptions().isEmpty()) {
             wrapper.put("options", asJsonObject(model.getOptions()));
         }
+        if (model.getResponseSchema() != null) {
+            wrapper.put("response", model.getResponseSchema());
+        }
         return wrapper;
     }
 
diff --git 
a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelperTest.java
 
b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelperTest.java
index f92b673b1227..38d1a802e34e 100644
--- 
a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelperTest.java
+++ 
b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/DevConsoleOpenApiHelperTest.java
@@ -100,6 +100,40 @@ class DevConsoleOpenApiHelperTest {
         assertNull(get.get("requestBody"));
     }
 
+    @Test
+    void consoleWithResponseSchemaPopulatesResponseContent() {
+        DevConsoleModel model = new DevConsoleModel();
+        model.setName("circuit-breaker");
+        model.setTitle("Circuit Breaker");
+        model.setDescription("Circuit breaker information");
+        model.setReadOnly(true);
+
+        JsonObject schema = new JsonObject();
+        schema.put("type", "object");
+        model.setResponseSchema(schema);
+
+        JsonObject pathItem = DevConsoleOpenApiHelper.buildPathItem(model);
+        JsonObject content = 
pathItem.getJsonObject("get").getJsonObject("responses").getJsonObject("200")
+                .getJsonObject("content").getJsonObject("application/json");
+
+        assertEquals(schema, content.getJsonObject("schema"));
+    }
+
+    @Test
+    void consoleWithoutResponseSchemaHasEmptyResponseContent() {
+        DevConsoleModel model = new DevConsoleModel();
+        model.setName("context");
+        model.setTitle("CamelContext");
+        model.setDescription("Overall information about the CamelContext");
+        model.setReadOnly(true);
+
+        JsonObject pathItem = DevConsoleOpenApiHelper.buildPathItem(model);
+        JsonObject content = 
pathItem.getJsonObject("get").getJsonObject("responses").getJsonObject("200")
+                .getJsonObject("content").getJsonObject("application/json");
+
+        assertTrue(content.isEmpty());
+    }
+
     @Test
     void openApiDocumentContainsOnePathPerConsoleSortedByName() {
         DevConsoleModel context = new DevConsoleModel();
diff --git 
a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonRecordSupport.java
 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonRecordSupport.java
new file mode 100644
index 000000000000..2ecfa97f4c4b
--- /dev/null
+++ 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonRecordSupport.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.json;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.RecordComponent;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Reflectively converts a Java {@link Record} into a {@link JsonObject}, so a 
record can be used as the single source
+ * of truth for both a runtime JSON payload and (via reflection over the 
record's structure) a JSON Schema describing
+ * it.
+ * <p/>
+ * A record component whose value is {@code null} is omitted from the 
resulting {@link JsonObject}, mirroring the common
+ * "only include a field if present" convention. Supported component value 
types: primitives, {@link String}, boxed
+ * numbers/{@link Boolean}, {@link Enum} (converted via {@link Enum#name()}), 
nested {@link Record}s,
+ * {@link Collection}s thereof, and {@link Map}s (converted to a {@link 
JsonObject}).
+ */
+public final class JsonRecordSupport {
+
+    private JsonRecordSupport() {
+    }
+
+    /**
+     * Converts a record instance into a {@link JsonObject}, one entry per 
record component.
+     */
+    public static JsonObject toJsonObject(Record record) {
+        JsonObject json = new JsonObject();
+        for (RecordComponent component : 
record.getClass().getRecordComponents()) {
+            Object value = accessorValue(record, component);
+            Object jsonValue = toJsonValue(value);
+            if (jsonValue != null) {
+                json.put(component.getName(), jsonValue);
+            }
+        }
+        return json;
+    }
+
+    private static Object accessorValue(Record record, RecordComponent 
component) {
+        try {
+            Method accessor = component.getAccessor();
+            accessor.setAccessible(true);
+            return accessor.invoke(record);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(
+                    "Cannot read record component: " + component.getName() + " 
on " + record.getClass(), e);
+        }
+    }
+
+    private static Object toJsonValue(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Record r) {
+            return toJsonObject(r);
+        }
+        if (value instanceof Map<?, ?> map) {
+            JsonObject json = new JsonObject();
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                Object v = toJsonValue(entry.getValue());
+                if (v != null) {
+                    json.put(String.valueOf(entry.getKey()), v);
+                }
+            }
+            return json;
+        }
+        if (value instanceof Collection<?> col) {
+            JsonArray array = new JsonArray();
+            for (Object item : col) {
+                array.add(toJsonValue(item));
+            }
+            return array;
+        }
+        if (value instanceof Enum<?> e) {
+            return e.name();
+        }
+        // String, boxed numbers, Boolean, and anything already JSON-safe pass 
through as-is
+        return value;
+    }
+}
diff --git 
a/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonRecordSupportTest.java
 
b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonRecordSupportTest.java
new file mode 100644
index 000000000000..c97369fdcb0d
--- /dev/null
+++ 
b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonRecordSupportTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.json;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class JsonRecordSupportTest {
+
+    record FlatRecord(String name, int count, boolean active) {
+    }
+
+    record RowRecord(String id, long value) {
+    }
+
+    record NestedRecord(String label, RowRecord row, List<RowRecord> rows) {
+    }
+
+    record NullableRecord(String required, String optional) {
+    }
+
+    record MapRecord(String id, Map<String, String> details) {
+    }
+
+    @Test
+    void flatRecordConvertsAllFields() {
+        JsonObject json = JsonRecordSupport.toJsonObject(new FlatRecord("foo", 
3, true));
+
+        assertEquals("foo", json.getString("name"));
+        assertEquals(3, json.getInteger("count"));
+        assertEquals(true, json.getBoolean("active"));
+    }
+
+    @Test
+    void nestedRecordAndListOfRecordsConvertRecursively() {
+        RowRecord row = new RowRecord("r1", 42L);
+        List<RowRecord> rows = List.of(new RowRecord("r1", 1L), new 
RowRecord("r2", 2L));
+        NestedRecord nested = new NestedRecord("top", row, rows);
+
+        JsonObject json = JsonRecordSupport.toJsonObject(nested);
+
+        assertEquals("top", json.getString("label"));
+        JsonObject rowJson = json.getJsonObject("row");
+        assertEquals("r1", rowJson.getString("id"));
+        assertEquals(42L, rowJson.getLong("value"));
+
+        JsonArray rowsJson = json.getJsonArray("rows");
+        assertEquals(2, rowsJson.size());
+        assertEquals("r2", ((JsonObject) rowsJson.get(1)).getString("id"));
+    }
+
+    @Test
+    void nullComponentIsOmittedFromOutput() {
+        JsonObject json = JsonRecordSupport.toJsonObject(new 
NullableRecord("present", null));
+
+        assertEquals("present", json.getString("required"));
+        assertFalse(json.containsKey("optional"));
+        assertNull(json.get("optional"));
+    }
+
+    @Test
+    void mapFieldConvertsToJsonObject() {
+        Map<String, String> details = new LinkedHashMap<>();
+        details.put("cause", "timeout");
+        details.put("retryable", "true");
+
+        JsonObject json = JsonRecordSupport.toJsonObject(new 
MapRecord("check1", details));
+
+        JsonObject detailsJson = json.getJsonObject("details");
+        assertEquals("timeout", detailsJson.getString("cause"));
+        assertEquals("true", detailsJson.getString("retryable"));
+    }
+}
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojo.java
index 2f2c0976b70b..cd1fa69c826e 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojo.java
@@ -19,9 +19,14 @@ package org.apache.camel.maven.packaging;
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.RecordComponent;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.StringJoiner;
 
 import javax.inject.Inject;
@@ -33,6 +38,7 @@ import 
org.apache.camel.tooling.model.DevConsoleModel.DevConsoleOptionModel;
 import org.apache.camel.tooling.model.JsonMapper;
 import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
+import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -84,6 +90,7 @@ public class GenerateDevConsoleMojo extends 
AbstractGeneratorMojo {
         private String description;
         private boolean deprecated;
         private boolean readOnly = true;
+        private JsonObject responseSchema;
         private final List<DevConsoleOptionModel> options = new ArrayList<>();
 
         public String getClassName() {
@@ -142,6 +149,14 @@ public class GenerateDevConsoleMojo extends 
AbstractGeneratorMojo {
             this.readOnly = readOnly;
         }
 
+        public JsonObject getResponseSchema() {
+            return responseSchema;
+        }
+
+        public void setResponseSchema(JsonObject responseSchema) {
+            this.responseSchema = responseSchema;
+        }
+
         public List<DevConsoleOptionModel> getOptions() {
             return options;
         }
@@ -183,6 +198,7 @@ public class GenerateDevConsoleMojo extends 
AbstractGeneratorMojo {
             boolean skip = "default-registry".equals(model.getName());
             if (!skip) {
                 extractQueryOptions(model);
+                extractResponseSchema(model);
                 models.add(model);
             }
         });
@@ -248,6 +264,9 @@ public class GenerateDevConsoleMojo extends 
AbstractGeneratorMojo {
         if (!model.getOptions().isEmpty()) {
             root.put("options", JsonMapper.asJsonObject(model.getOptions()));
         }
+        if (model.getResponseSchema() != null) {
+            root.put("response", model.getResponseSchema());
+        }
         return root;
     }
 
@@ -315,6 +334,94 @@ public class GenerateDevConsoleMojo extends 
AbstractGeneratorMojo {
         
model.getOptions().sort(Comparator.comparing(BaseOptionModel::getName));
     }
 
+    private void extractResponseSchema(DevConsoleModel model) {
+        Class<?> clazz;
+        try {
+            clazz = loadClass(model.getClassName());
+        } catch (NoClassDefFoundError e) {
+            getLog().debug("Cannot load class: " + model.getClassName());
+            return;
+        }
+        model.setResponseSchema(buildResponseSchema(clazz));
+    }
+
+    /**
+     * Builds a JSON Schema for the console's response, from its nested {@code 
Response} record (if any). Returns
+     * {@code null} when the console has no such record, so consoles that have 
not declared a typed response keep
+     * generating no {@code "response"} key, same as before this schema 
generation existed.
+     */
+    static JsonObject buildResponseSchema(Class<?> consoleClass) {
+        for (Class<?> nested : consoleClass.getDeclaredClasses()) {
+            if ("Response".equals(nested.getSimpleName()) && 
nested.isRecord()) {
+                return buildRecordSchema(nested);
+            }
+        }
+        return null;
+    }
+
+    private static JsonObject buildRecordSchema(Class<?> recordClass) {
+        JsonObject schema = new JsonObject();
+        schema.put("type", "object");
+
+        JsonObject properties = new JsonObject();
+        JsonArray required = new JsonArray();
+        for (RecordComponent component : recordClass.getRecordComponents()) {
+            JsonObject propertySchema = 
buildTypeSchema(component.getGenericType());
+            String description = componentDescription(recordClass, component);
+            if (description != null) {
+                propertySchema.put("description", description);
+            }
+            properties.put(component.getName(), propertySchema);
+            if (component.getType().isPrimitive()) {
+                required.add(component.getName());
+            }
+        }
+        schema.put("properties", properties);
+        if (!required.isEmpty()) {
+            schema.put("required", required);
+        }
+        return schema;
+    }
+
+    private static String componentDescription(Class<?> recordClass, 
RecordComponent component) {
+        try {
+            Field field = recordClass.getDeclaredField(component.getName());
+            Metadata metadata = field.getAnnotation(Metadata.class);
+            if (metadata != null && !metadata.description().isEmpty()) {
+                return metadata.description();
+            }
+        } catch (NoSuchFieldException e) {
+            // ignore - no annotation available
+        }
+        return null;
+    }
+
+    private static JsonObject buildTypeSchema(Type type) {
+        if (type instanceof ParameterizedType pt) {
+            Class<?> rawType = (Class<?>) pt.getRawType();
+            if (Collection.class.isAssignableFrom(rawType)) {
+                JsonObject schema = new JsonObject();
+                schema.put("type", "array");
+                schema.put("items", 
buildTypeSchema(pt.getActualTypeArguments()[0]));
+                return schema;
+            }
+            if (Map.class.isAssignableFrom(rawType)) {
+                JsonObject schema = new JsonObject();
+                schema.put("type", "object");
+                schema.put("additionalProperties", true);
+                return schema;
+            }
+        }
+
+        Class<?> clazz = (Class<?>) type;
+        if (clazz.isRecord()) {
+            return buildRecordSchema(clazz);
+        }
+        JsonObject schema = new JsonObject();
+        schema.put("type", javaTypeToType(clazz.getName()));
+        return schema;
+    }
+
     private static String javaTypeToType(String javaType) {
         return switch (javaType) {
             case "java.lang.String" -> "string";
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojoResponseSchemaTest.java
 
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojoResponseSchemaTest.java
new file mode 100644
index 000000000000..2705e690e308
--- /dev/null
+++ 
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/GenerateDevConsoleMojoResponseSchemaTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.maven.packaging;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class GenerateDevConsoleMojoResponseSchemaTest {
+
+    static class NoResponseConsole {
+    }
+
+    static class SampleConsole {
+
+        record Row(String id, long value) {
+        }
+
+        public record Response(
+                @Metadata(description = "The size") int size,
+                @Metadata(description = "An optional label") String label,
+                Row row,
+                List<Row> rows,
+                Map<String, String> details) {
+        }
+    }
+
+    @Test
+    void consoleWithoutResponseRecordReturnsNull() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(NoResponseConsole.class);
+        assertEquals(null, schema);
+    }
+
+    @Test
+    void primitiveFieldIsRequiredAndDescribed() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(SampleConsole.class);
+
+        assertEquals("object", schema.getString("type"));
+        JsonObject properties = schema.getJsonObject("properties");
+
+        JsonObject size = properties.getJsonObject("size");
+        assertEquals("integer", size.getString("type"));
+        assertEquals("The size", size.getString("description"));
+
+        var required = schema.getCollection("required");
+        assertTrue(required.contains("size"));
+    }
+
+    @Test
+    void referenceFieldIsNotRequired() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(SampleConsole.class);
+        JsonObject properties = schema.getJsonObject("properties");
+
+        JsonObject label = properties.getJsonObject("label");
+        assertEquals("string", label.getString("type"));
+        assertEquals("An optional label", label.getString("description"));
+
+        var required = schema.getCollection("required");
+        assertFalse(required.contains("label"));
+    }
+
+    @Test
+    void nestedRecordBuildsNestedObjectSchema() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(SampleConsole.class);
+        JsonObject properties = schema.getJsonObject("properties");
+
+        JsonObject row = properties.getJsonObject("row");
+        assertEquals("object", row.getString("type"));
+        JsonObject rowProperties = row.getJsonObject("properties");
+        assertEquals("string", 
rowProperties.getJsonObject("id").getString("type"));
+        assertEquals("integer", 
rowProperties.getJsonObject("value").getString("type"));
+    }
+
+    @Test
+    void listOfRecordBuildsArrayOfObjectSchema() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(SampleConsole.class);
+        JsonObject properties = schema.getJsonObject("properties");
+
+        JsonObject rows = properties.getJsonObject("rows");
+        assertEquals("array", rows.getString("type"));
+        JsonObject items = rows.getJsonObject("items");
+        assertEquals("object", items.getString("type"));
+        assertEquals("string", 
items.getJsonObject("properties").getJsonObject("id").getString("type"));
+    }
+
+    @Test
+    void mapFieldBuildsOpenObjectSchema() {
+        JsonObject schema = 
GenerateDevConsoleMojo.buildResponseSchema(SampleConsole.class);
+        JsonObject properties = schema.getJsonObject("properties");
+
+        JsonObject details = properties.getJsonObject("details");
+        assertEquals("object", details.getString("type"));
+        assertEquals(true, details.get("additionalProperties"));
+        assertNull(details.get("properties"));
+    }
+}

Reply via email to