gnodet opened a new pull request, #27840: URL: https://github.com/apache/camel/pull/27840
## Summary Three confirmed bugs in `camel-jbang-mcp` on main, all introduced together with the MCP server feature. ## Root Causes & Fixes ### 1. `AuthoringTools.call()` — CamelCatalog rebuilt on every tool call `call()` created a `new ToolContext()` on every invocation. `ToolContext` holds the `CamelCatalog` and loads it lazily on first access — so every call triggered a fresh catalog load instead of reusing the shared instance. **Fix:** Inject `CatalogService` into `AuthoringTools` and pre-populate the `ToolContext` with its cached catalog before executing the registry tool. Added `ToolContext.setCatalog()` to allow injecting a pre-built catalog instance. ### 2. `KameletTools.camel_catalog_kamelets()` — no caching, limit before sort - `KameletCatalogHelper.loadKamelets()` was called on every request, rebuilding the downloader and classloader each time. - The `limit` guard was applied inside the `HashMap` iteration loop — before the `sort` — so results depended on hash iteration order and were non-deterministic across calls. **Fix:** - Added a `ConcurrentHashMap<String, Map<String, Object>> kameletCache` keyed by version. A private `loadKamelets(version)` helper checks the cache first, using `putIfAbsent` to handle concurrent first calls correctly. - Moved sorting before the limit: build the full filtered `all` list, sort alphabetically, then `subList(0, maxResults)`. - Added `total` to `KameletListResult` to expose how many kamelets matched before the limit was applied. - `camel_catalog_kamelet_doc` also reuses the cache. ### 3. `RepeatedToolCalls.repeatOf()` — failed calls consume the retry budget `repeatOf()` incremented the call count unconditionally via `counts.merge(...)` before the tool ran. A tool call that threw an exception consumed a slot in the `FULL_ANSWERS` budget, so a model that legitimately retried a failed call could hit the repeat-note wall without ever getting a successful answer. **Fix:** Split counting into two phases: - `repeatOf()` now only **peeks** (`getOrDefault`) without incrementing. Returns `null` if count < `FULL_ANSWERS`. - New `recordSuccessfulAnswer(tool, args)` method — called by the caller **only after the tool returns without throwing** — increments the count. - `RepeatedCallSessions` got a matching `recordSuccessfulAnswer(McpConnection, tool, args)` method. - `AuthoringTools.callDeterministic()` calls `repeatedCalls.recordSuccessfulAnswer()` after a successful `call()`. ## Tests - `RepeatedToolCallsTest` — updated existing tests to use the new two-phase API; added `aFailedCallDoesNotCountAgainstTheRetryBudget()`. - `KameletToolsTest` (new) — covers: repeated calls return identical sorted results, limit applied after sort, `total` reflects untruncated count, type/name/description filters, kamelet doc detail, unknown kamelet error, cache hit on second call. - `CatalogServiceTest` — added Quarkus+camelVersion regression test (network-disabled in CI). All 46 tests pass locally. --- _Hermes Agent (Claude Sonnet 4.6) on behalf of Guillaume Nodet_ -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
