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]

Reply via email to