[xwiki-contrib/ai-llm] a425ff: LLMAI-154: Add the delete_document MCP tool
Branch: refs/heads/main Home: https://github.com/xwiki-contrib/ai-llm Commit: a425ff04a10079d61a08bc7bbb4ae0dea303d56a https://github.com/xwiki-contrib/ai-llm/commit/a425ff04a10079d61a08bc7bbb4ae... Author: Paul Pantiru <[email protected]> Date: 2026-07-11 (Sat, 11 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPDocumentAccess.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/resources/META-INF/components.txt M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPDocumentAccessTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentToolTest.java Log Message: ----------- LLMAI-154: Add the delete_document MCP tool * New write tool (off by default) deleting a document to the recycle bin: base_version is REQUIRED as proof-of-recent-read, a bin-less wiki is refused outright (the tool never deletes permanently), and rights are checked twice - Right.DELETE through the access door, then api.Document.delete()'s own re-check * No recursive delete: a space home page with children is refused with the platform delete UI's own child count (its query shape, escaped-literal binds, hidden/translations included); the lookup deliberately bypasses the row-query door so unviewable children still block the delete, and fails closed on query errors. The WebPreferences-only-child case proceeds and reports the remainder (we never cascade, diverging from the UI's delete-both default) * Sensitive-reference denylist (security audit): WebPreferences, XWikiPreferences, main-wiki XWikiServer* descriptors and the MCP config document are refused with a delete-it-manually message and URL - a per-tool capability limit where the UI's human confirmation has no MCP equivalent; deleting the MCP config doc would reset the endpoint to enabled defaults * MCPWriteSupport gains a right-parameterized resolveFor (edit tools unchanged) and the access door's denial message now says "delete" for DELETE denials; TOCTOU between the guards and the delete is documented as accepted risk (platform's own delete action carries the same race with no version check at all) * 20 behavioral tests for the tool + the two access-door denial tests now verify the space filter is never consulted after a rights deny Commit: f95c704f65bb0412a7b42451b19b6a76b25db9f0 https://github.com/xwiki-contrib/ai-llm/commit/f95c704f65bb0412a7b42451b19b6... Author: Paul Pantiru <[email protected]> Date: 2026-07-11 (Sat, 11 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPRowQuery.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/MCPRowQuery.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetSchemaTool.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaText.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/resources/META-INF/components.txt M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPRowQueryTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetSchemaToolTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaTextTest.java Log Message: ----------- LLMAI-155: Add the get_schema MCP tool * First tool of the structured-data family (new man category): no-arg lists the wiki's instantiated classes with instance counts, with a class reference it renders the field definitions in a precise field-per-line grammar (name: Type(detail) "Pretty Name" modifiers [validation: regexp]) designed to double as write_schema's future input; the man page documents the grammar for agent parsing * Catalog counts ride an HQL group-by through the row-query door (translation guard, no hidden clause - class docs are conventionally hidden) and each row's CLASS DOCUMENT is authorized (space filter + view) before listing; counts stay deliberate upper bounds with an honesty note, plus a fetch-ceiling incompleteness note that survives the output budget cut * The PropertyClass-family fan-out lives in a package-private MCPSchemaText renderer (tool 17/20, renderer 10/20, no suppressions): type dispatch on getClassType() strings with instanceof-guarded casts; DBList/ComputedField script bodies are never read, Password fields carry a masked-in-results note, and every wiki-authored fragment is line-break-stripped and clamped (per-fragment cap, StaticList values joined at value boundaries, whole response under the module's output budget with a truncation notice) * MCPRowQuery gains a Map-bind overload (single-bind delegates via singletonMap to preserve null-value behavior); both overloads now document the scalar row shape of single-column selects * 24 tool + 10 renderer tests (exact-text grammar pins over real oldcore property classes, forgery line-count pin, budget/clamp pins, load-failure error hygiene, view-denied catalog drop); module at 609 Commit: 08b4aff45fcc5151ad17a4ff95137641f67b83ae https://github.com/xwiki-contrib/ai-llm/commit/08b4aff45fcc5151ad17a4ff95137... Author: Paul Pantiru <[email protected]> Date: 2026-07-17 (Fri, 17 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPRowQuery.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/MCPRowQuery.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupport.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaText.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPTextGuards.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/resources/META-INF/components.txt M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPRowQueryTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupportTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsToolTest.java Log Message: ----------- LLMAI-156: Add the query_objects MCP tool * Find XObjects by class + declarative field filters ("field op value", ops = != > < contains, AND-ed, capped at 10) compiled into bound HQL: every agent value is a bind (contains rides the platform's escaped QueryParameter chain via a new MCPRowQuery.Contains record); the only composed text is Hibernate entity names and op tokens from closed maps plus generated aliases - field names, class name and document scope are all bind values * Filter values are validated AND coerced by the class's own PropertyClass.fromString (17.4 returns null on garbage - treated as a teaching refusal naming the expected format; booleans get explicit vocabulary checks since fromString never fails them; bare ISO dates accepted as UTC midnight); refused as filter/sort targets: unknown fields (listing valid ones), Password (value-probing oracle), computed fields (no stored rows), list-typed fields, large-text sort (SELECT DISTINCT over CLOB breaks on some DBMS) * Totals are AUTHORIZED-ONLY, deliberately diverging from the search tools' upper bounds (security audit High): a DB-side count under attacker-chosen value predicates is a value-extraction oracle over rights-denied documents (differential counting binary-searches exact field values), so the tool scans up to the 2000-row door ceiling, authorizes every row, pages client-side and reports the exact viewable total ("N+" at the ceiling); a dedicated test proves denied matches leave no countable trace * Results render (object NUMBER, vVERSION) headers - the exact addressing write_object will need - with values read only via safeget/getValue (never the display pipeline), Password masked unread, hidden documents included by design (documented, get_schema rationale), every wiki-authored fragment stripped and clamped via the new shared MCPTextGuards, whole response under the output budget * 28 tool + 29 support tests over the real door and real oldcore property classes (statement/bind pins, escaping chain end-to-end, coercion matrix, refusal matrix, paging/ceiling/budget); module 665 Commit: ebf80fa6ba279883f3dc68654934990a43243106 https://github.com/xwiki-contrib/ai-llm/commit/ebf80fa6ba279883f3dc686549349... Author: Paul Pantiru <[email protected]> Date: 2026-07-17 (Fri, 17 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-api/src/main/java/org/xwiki/contrib/llm/mcp/MCPToolSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-api/src/test/java/org/xwiki/contrib/llm/mcp/MCPToolSupportTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentTool.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupport.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupport.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/resources/META-INF/components.txt A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectToolTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupportTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java Log Message: ----------- LLMAI-157, LLMAI-158: Add the write_object and delete_object MCP tools * write_object creates or updates one XObject's fields, delete_object removes one - both off by default, addressed by class + object NUMBER (the query_objects header addressing), sharing an MCPObjectWriteSupport seam that owns the whole BaseObject/BaseClass/ PropertyClass family so the tools stay under fan-out * Field values are validated and coerced by the class's own PropertyClass.fromString wired exactly as BaseClass.fromMap does (fromString -> setObject -> safeput, storing the very instance validation inspected); the read side's vocabulary is reused, not reimplemented - unknown field lists the valid ones, Password and computed fields refused, fromString-null is a teaching refusal naming the expected format, booleans get an explicit vocabulary check; both tools mutate xdoc.clone() and save through api.Document with [AI] comments so the acting user is recorded content author * Sensitive-class denylist (security audit High+Medium): objects of XWikiRights/XWikiGlobalRights/XWikiServerClass/MCPServerConfigClass/ XWikiGroups/XWikiUsers are refused for write AND delete - the document denylist cannot protect these because a page's rights live as XWikiRights objects ON the page and group membership is an XWikiGroups object ON the group document (adding yourself to the admin group = indirect admin grant); the sensitive-DOCUMENT denylist is now shared from MCPWriteSupport (delete_document delegates, behavior byte-identical). Password fields refused as write targets * write_object mirrors write_document's base_version discipline (required on existing, tolerant on create); delete_object requires base_version always (destructive); object-only saves version normally and the compare URL renders the object diff, so every change is one click from human review * mcp-api MCPToolSupport gains a string-map param type (fields) plus requiredStringMap/requiredInteger builders and requireStringMap/ requireInteger accessors so required params advertise honestly * 28 write + 14 delete + 3 support + 8 api tests (real-store create/ update/delete incl. object-number holes, the full refusal matrix, sensitive-class/document refusals saving nothing, date parse both ways, clone-before-mutate on save failure); mcp-server 714, mcp-api 49 Commit: 6fb0aaed7aa05dbec27deb2c7a71065883ade2b7 https://github.com/xwiki-contrib/ai-llm/commit/6fb0aaed7aa05dbec27deb2c7a710... Author: Paul Pantiru <[email protected]> Date: 2026-07-17 (Fri, 17 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupportTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupportTest.java Log Message: ----------- LLMAI-156, LLMAI-157, LLMAI-158: Fix two live-only object-tool bugs on platform 17.10 Both surfaced in the first live smoke on a 17.10.8 instance (the extension builds against 17.4.0-8); both were invisible to the unit suite - one a fake-store gap, one build-vs-run version skew. * delete_object removal now persists. It cloned the loaded document, removeXObject'd the clone, then saved via a fresh api.Document - whose own lazy getDoc() clone drops the xObjectsToRemove list that cloneInternal never copies, so the store issued no row DELETE and the object survived while the version still bumped (creates/updates persist because they live in the object vector, which IS deep-copied). Now removes THROUGH the api wrapper (getObject(EntityReference,int) + removeObject on the wrapper's own internal clone, then save), the platform's own REST idiom - the mutated instance is the saved instance. MCPObjectWriteSupport.removeObject becomes read-only requireObjectExists; the tool guards presence, then removes via the already-authorized classRef (not a re-resolved name, which would hit the absent 'explicit' resolver and NPE). MockitoOldcore cannot model the clone-drops-toRemove behavior (verified: the old code passes the unit test), so this fix's sole gate was the live re-smoke - confirmed: delete then re-read shows the row gone. * Bad Number/Date values no longer leak/mislead. PropertyClass. fromString returns null on garbage on 17.4 (our validation signal) but THROWS XWikiException on 17.10 (XWIKI-20910); query_objects leaked the raw exception to the wire (-32603) and write_object reported a misleading save-failure. New shared parseOrNull wraps fromString in catch(Exception) - XWikiException can't be caught narrowly (not declared thrown on the 17.4 build target, so it won't compile), and Exception covers both the 17.4 null and the 17.10 throw - routed from coerce/dateBind/validateAndCoerce so both tools give the same teaching refusal on either platform. Throwing-stub tests pin the catch (the 17.4 test JVM can't reproduce the real throw). mcp-server 716 tests, 0 checkstyle; live-validated on 17.10.8 incl. the new ComputedField/DBList field types (schema render, filter refusals, computed output masked, no script/SQL echo). Commit: faeaae81d56328eaed2ad9c1e60d20d81ec940bb https://github.com/xwiki-contrib/ai-llm/commit/faeaae81d56328eaed2ad9c1e60d2... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaText.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaWriteSupport.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/resources/META-INF/components.txt A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaWriteSupportTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-ui/src/main/resources/AI/MCP/Code/MCPServerConfigSheet.xml Log Message: ----------- LLMAI-159: Add the write_schema MCP tool * write_schema creates or modifies ONE field of an XClass - operation add_field / modify_field / remove_field, off by default. The target document IS the class; the first add_field on a fieldless/new document creates the class (a zero-field class serialises to empty class-XML and is invisible, so there is no separate "create class" verb). Types are added through BaseClass.add*Field (ComputedField constructed by hand); per-type attributes (size, numberType, values, sql, script, multiSelect, contentType...) are set generically as PropertyClass meta-properties via setIntValue/setStringValue/setLargeStringValue behind a per-type allowed table, so the whole BaseClass/PropertyClass fan-out lives in a new package-private MCPSchemaWriteSupport seam and the tool stays under cap * modify_field changes pretty-name + same-type attributes only; a TYPE change is remove_field then add_field (adders no-op on an existing name, and routing a swap through remove fires the orphan disclosure). base_version mirrors the siblings: required-on-existing / absent-on-create for add and modify, required ALWAYS for remove (destructive) * Removing a field orphans data REVERSIBLY - class defs live in XML, values in separate name-keyed tables, so values become orphaned rows that still load, re-save and reappear if the field is re-added with a compatible type. So no confirm handshake: base_version is the gate and the remove result discloses an AUTHORIZED-ONLY orphan count (scan to the door ceiling, authorise each doc, N+ at 2000; class + field names are HQL binds), for cardinality-over-denied-docs consistency with query_objects * Sensitive-class-DEFINITION guard (security audit): the object-write SENSITIVE_CLASSES set is reused via a new MCPObjectWriteSupport.isSensitiveClass to refuse EDITING THE SCHEMA of XWikiRights/GlobalRights/Groups/Users/ ServerClass/MCPServerConfigClass - a hole the object denylist (guards object writes) and the document denylist did not cover; redefining XWiki.XWikiUsers would corrupt the farm's user model. Sensitive-document denylist still applies * Script/SQL-bearing field types (ComputedField, DBList/DBTreeList/Page, Velocity TextArea) are ALLOWED - rights are king. Execution is capped at the class doc author's rights, and api.Document.save routes a non-programming author through saveAsAuthor, overwriting the author with the acting agent, so an authored field can never run above the agent's own rights (auditor traced end to end: ComputedField runs via AuthorExecutor at getAuthorReference = the agent). The real footgun - the author downgrade can disable an app's own scripts on an edited class doc - is named in the man page's WARNING * Reviewer clean on blockers/must-fix; security audit found no Critical/High/ Medium (HQL binding, door routing, denylist evasion, the author downgrade and the orphan-count oracle all verified). Fixes folded in: the tool's fragments use MCPTextGuards.fragment (clamp consistency), the stored version comment is neutralised, +6 refusal-branch tests. mcp-server 756 tests, mcp-ui 0 checkstyle Commit: 4822533873d601bb1af14d3d8c32da8f06cfff75 https://github.com/xwiki-contrib/ai-llm/commit/4822533873d601bb1af14d3d8c32d... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/xwikistore/XWikiDocumentStoreEventListener.java A application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/xwikistore/XWikiDocumentStoreEventListenerTest.java Log Message: ----------- LLMAI-161: Deleting a document orphaned its RAG index chunks * The delete cleanup in XWikiDocumentStoreEventListener serialized the blank delete-event source with the default serializer, which drops the locale reference parameter - the produced ID (wiki:Space.Page) matched neither the trailing-; form of default documents nor the ;fr form of translations written by XWikiDocumentDocument#getID, so every deletion (any locale) left its chunks orphaned in Solr * Fix: serialize getOriginalDocument().getDocumentReferenceWithLocale() with the withparameters serializer - on deletion the event source is a blank locale-free document and the actually deleted row, including its locale, is the original document; deleteAllDocuments fires one event per translation, so each row cleans exactly its own chunks * New test class pins both ID forms (;fr and trailing ;) with the real serializers, the event source constructed exactly as the platform builds it, plus update-branch task queueing and the collection space-list branch (set-compare: reordering is not a change) * Existing indexes likely contain orphaned chunks from past deletions; reindexing the affected collections clears them Commit: 5abfa2ffd8365076e85a0df3e8443f28453c21da https://github.com/xwiki-contrib/ai-llm/commit/5abfa2ffd8365076e85a0df3e8443... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/pom.xml M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentToolTest.java Log Message: ----------- LLMAI-160: Make delete_document and rendered reads translation-aware * delete_document now deletes a page's translations with it via XWiki#deleteAllDocuments (platform-UI behavior: every row to the recycle bin under one shared batch ID); the tool's rights-checked- twice property is preserved with an explicit Document#hasAccessLevel re-check, since deleteAllDocuments itself checks nothing - the delete right attaches to the locale-free reference, so one check covers all translations (no per-translation right exists) * Success result names the deleted translation count and locales; the failure message admits possible partial deletion (the per-row delete is not transactional) and that retrying is safe - the default document, the base_version holder, is deleted last; man page states that base_version covers the default-locale document only * get_document rendered mode now renders the addressed document deterministically via XWikiDocument#displayDocument (identical render path with retrieveTranslation=false) - getRenderedContent silently swapped in a translation selected by the request language preference while title, version and base_version described the default document * Tests: fr+de translation rows on the real mock store all gone after one delete, api-level re-check denial after the door passed, honest partial-failure message, displayDocument method-choice pin (never getRenderedContent); needs a registered BatchOperationExecutor and an explicit xwiki-platform-refactoring-api test dependency Commit: 487a8fbdf2042dfc96893387b9d1104d2867e3c6 https://github.com/xwiki-contrib/ai-llm/commit/487a8fbdf2042dfc96893387b9d11... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/SolrConnector.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/mcp/MCPSearchCollectionsTool.java A application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/SolrConnectorTest.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/DefaultCollectionManagerTest.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/mcp/MCPSearchCollectionsToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/pom.xml M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryDocumentsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryDocumentsToolTest.java M application-ai-llm-models/application-ai-llm-models-api/src/main/java/org/xwiki/contrib/llm/openai/Context.java Log Message: ----------- LLMAI-160: Add read-side translation support to the MCP tools * get_document gains an optional locale parameter: exact-match, no language fallback - a missing translation is a teaching refusal listing the translations that exist and the default language; a locale equal to the default language is served as a default read (the default row stores no language). Both the source-mode and rendered-mode loads carry the locale, and rendered reads set and restore the context locale so translation-macro/sheet output matches the emitted language= view URL (URL-encoded value) * Headers gain Language: (omitted for an undeclared ROOT default locale) and Translations: fr, de (default: en) discovery lines; authorization stays locale-free by design - rights attach to the document and locale-carrying references never reach authorization APIs; man page states writes/edits touch the default language version only while delete removes all translations, and that a translation's Version is not a valid base_version * query_documents emits Language: per hit ((translation) suffix when the row is a translation - N translations of a page are no longer indistinguishable) and gains a locale filter: exact match on the indexed locale field (doclocale is indexed but empty on default rows, so only locale expresses content-in-language-X) * search_collections emits a locale-free <reference> plus <language> per hit so search results map directly onto get_document calls; <reference> is double-guarded - withparameters round-trip (shape) AND the chunk's collection must use the XWiki document store (provenance), since internal-store IDs are client-chosen and a reference-shaped upload ID must not attribute uploaded content to a real page; language flows through a new Context.language record component populated in SolrConnector (was fetched and dropped) * A validated Locale is NOT a safe token: commons-lang3 toLocale accepts arbitrary variant segments including newlines, so every locale echo is line-break-stripped (header lines, refusals, the query tool's Solr-derived language values) and the Solr filter value is escaped via toCompleteFilterQueryString * Live-validated on 17.10 multilingual (en/fr/ro): exact locale reads, refusals, rendered coherence, locale filter, wiki-store reference/language emission incl. the trailing-; default-row ID form, internal-store reference suppression, and LLMAI-161 chunk cleanup on delete observed working end to end * Tests: mcp-server 776 (variant-smuggled locale refusal single-line, ROOT-locale header omission, context-locale set/restore order, URL-encoding pin, filter fq pin, active-filters echo), index-api 107 (spoofed reference-shaped internal ID emits no reference, sanitized language, lookup-failure caching), models-api green; explicit localization-api dependency (module convention) Commit: a29d49d1c70e62f05ea91e656110e5b3180ff730 https://github.com/xwiki-contrib/ai-llm/commit/a29d49d1c70e62f05ea91e656110e... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-api/pom.xml M application-ai-llm-mcp/application-ai-llm-mcp-api/src/main/java/org/xwiki/contrib/llm/mcp/MCPToolSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-api/src/test/java/org/xwiki/contrib/llm/mcp/MCPToolSupportTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/pom.xml M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryDocumentsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaToolTest.java Log Message: ----------- LLMAI-160: Add write-side translation support to the MCP tools * write_document and edit_document gain an optional locale parameter: exact-match like the read side, base_version is ROW-scoped (a translation has its own history - omitted when creating the translation row, required by write_document / optionally checked by edit_document against that row's version when updating); success, conflict and no-change messages name the translation, and view/ compare URLs carry a URL-encoded language= parameter * Creating a NEW translation is refused when the default document is missing (the store would happily persist an orphan row) and on non-multilingual wikis (the platform UI silently mis-routes that save into the default document); editing an existing translation row is allowed regardless, matching the platform. Translation rows are loaded exactly (never getTranslatedDocument - parent-locale fallback would edit fr when fr_FR was asked), never built by cloning the default document (objects are keyed by fullName across rows), and get the page's syntax copied on creation (deliberate divergence: the UI leaves the wiki default syntax) * Every MCP document creation now stamps the wiki default locale (XWiki#getDefaultLocale - deterministic, unlike the UI's persisted request-dependent locale preference) through one seam in MCPWriteSupport#inTargetWiki, covering write_document, edit_document, write_object and write_schema; translation rows are never stamped * The default-language predicate is shared between read and write and falls back to the WIKI default when a document's defaultLocale is ROOT - programmatically created pages (extension initializers, REST, old imports) never get the UI's stamp, and without the fallback a locale=wiki-default write would mint a bogus translation row (the read side refused); gate order puts the short-circuit before the missing-default refusal so locale=wiki-default on a missing page is a plain create. Same fallback fixes a live-found header bug: the (default: X) suffix claimed the loaded ROW's own language on translation reads of such pages; the Language: line still names only DECLARED content languages * parseLocale is deduplicated into MCPToolSupport using commons-lang3 directly (already a mcp-api dependency for StringUtils since LLMAI-153; the platform localization wrapper would add a dep for unused conveniences) and refuses locales longer than 5 characters (the legacy XWD_LANGUAGE column length - longer values fail the save or silently truncate; security-audit Low); xwiki-platform-localization-api stays in mcp-server as an explicit test-scope dependency (test-only imports) * Live-validated on 17.10 multilingual (en/fr/ro): ro translation create/update/edit with independent row versions, full refusal matrix incl. the storage cap, ROOT-fallback default-row write, default-locale stamp on a fresh page, honest (default: en) header * Tests: mcp-api 55 (parseLocale forms, cap, stripped echo), mcp-server 802 (bogus-en-row pin, wiki-default-on-missing create, realistic ROOT-defaultLocale translation rows, stamp survival through object/schema clones, byte-identical locale-less messages); MCPEditDocumentTool fan-out suppression (measured 21/20, Locale is the 21st type) Commit: ece4b93163cae4f1f8cee2c9e0a13942a2f63736 https://github.com/xwiki-contrib/ai-llm/commit/ece4b93163cae4f1f8cee2c9e0a13... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/CollectionManager.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/SolrConnector.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/DefaultCollectionManager.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/mcp/MCPSearchCollectionsTool.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/rest/DefaultSearchResource.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/xwikistore/XWikiDocumentDocument.java M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/rest/SearchResource.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/SolrConnectorTest.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/DefaultCollectionManagerTest.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/mcp/MCPSearchCollectionsToolTest.java A application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/rest/DefaultSearchResourceTest.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/xwikistore/XWikiDocumentDocumentTest.java Log Message: ----------- LLMAI-160: Pin translation language in RAG citations and add a search locale filter * Translation chunks' source URLs now carry ?language=xx (URL-encoded; the URL factory appends query strings verbatim) so a French citation opens the French page; default-row chunks keep bare URLs (a stamped default would go stale if the wiki default changes). URLs are baked into chunks at index time: existing collections need a reindex for pinned citations, which is embedding-free (unchanged content means the embeddings are copied) * Collection search gains a locale filter: fq on the AI core's language string field (indexed by default like the collection/wiki filter fields - no schema change, works on already-indexed chunks; on Solr 9.4.1 the fq is a KNN PRE-filter, so topK is computed among matching chunks). Our chunks store the concrete default locale even on default rows, so locale=fr means content-in-French including French-default pages. Exposed as a locale parameter on search_collections (shared parseLocale) and on the search REST resource (lang3 validation + the same 5-char storage cap, static 400, no input echo); threaded through a new locale overload of CollectionManager#hybridSearch (old signature delegates; the interface default documents that it ignores the filter) * search_collections now sanitizes the <url> element like <language> (uploader-typed internal-store URLs could forge </url><reference> elements, undercutting the store-provenance guard) * New DefaultSearchResourceTest - the REST resource had no tests at all; covers the locale outcomes without pinning the limit argument order (LLMAI-163 fixes the pre-existing semantic/keyword swap found during this review) * Review round also surfaced LLMAI-164 (keywordSearch missing the wiki filter - pre-existing, separate fix); chat-side locale filtering deferred (no per-request channel; would need a RAGChatRequestFilterClass field + UI) * Tests: index-api 123 green incl. KNN-prefilter fq placement pins (filter queries only, never the knn query string), URL-encoding pin on a variant-carrying locale, forged-url single-line pin, and a parameterized hybridSearch locale-forwarding matrix Commit: 0e12284aeffce0bdbef9db51833e40555c0974e4 https://github.com/xwiki-contrib/ai-llm/commit/0e12284aeffce0bdbef9db51833e4... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/internal/rest/DefaultSearchResource.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/internal/rest/DefaultSearchResourceTest.java Log Message: ----------- LLMAI-163: REST search swapped the semantic and keyword result limits * DefaultSearchResource passed limitKeywordResults into hybridSearch's limitSemanticSimilarity position and vice versa, swapped since the hybrid search was introduced (LLMAI-130); with asymmetric limits the caller's keyword budget silently drove the KNN topK. The MCP tool and the chat filter pass theirs correctly * New test pins the interface argument order (the test class itself is new as of LLMAI-160 Stage 3; its other tests deliberately avoid pinning the limit order so this fix lands cleanly) Commit: 6a4466a2dea444f09abcf5a98cb20b5170dddc3b https://github.com/xwiki-contrib/ai-llm/commit/6a4466a2dea444f09abcf5a98cb20... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/SolrConnector.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/SolrConnectorTest.java Log Message: ----------- LLMAI-164: keywordSearch was missing the wiki filter * The keyword path built its query with the collections filter but never applied buildWikiQuery, unlike the semantic path. Collection names in the filter are not wiki-qualified, so a same-named collection on another wiki leaked its chunks into keyword results - past the collection-level group gate, which was evaluated against the current wiki's collection only (the per-document canView post-filter still applied; security-audit Medium) * Fix: the same buildWikiQuery filter query as prepareQuery, with a test pinning the wiki fq on the keyword path Commit: 5043bcdfda5e11f22f2f37d0e79a660e82845325 https://github.com/xwiki-contrib/ai-llm/commit/5043bcdfda5e11f22f2f37d0e79a6... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/script/MCPFarmScriptService.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/script/MCPFarmScriptServiceTest.java M application-ai-llm-mcp/application-ai-llm-mcp-ui/src/main/resources/AI/MCP/Code/MCPServerConfigSheet.xml M application-ai-llm-mcp/application-ai-llm-mcp-ui/src/main/resources/AI/MCP/Code/Translations.xml Log Message: ----------- LLMAI-162: Add the MCP Server URL with copy-to-clipboard to the config pages * New MCPFarmScriptService#getServerUrl(wikiId): composes the external endpoint URL (<base>/<webapp>rest/wikis/<id>/aiLLM/mcp, the literal path mirroring MCPResource's @Path) from the server URL the platform advertises for the wiki - descriptor alias, or xwiki.home for the main wiki - falling back to the current request's server URL when a wiki has no advertised base; same composition as the platform's REST getBaseURI incl. the root-webapp special case. Read-only and ungated like isEnabled (a script-right caller already gets the full wiki descriptor ungated from the platform); the wiki id path segment is URL-encoded defensively (ids outside the alphanumeric convention) * Config sheet: read-only URL row (input-group + copy button) at the top of the settings fieldset - the input carries no name so the save form never posts it, and Enter in it is swallowed (submitting from a subwiki would persist the display-seeded enable state); the farm dashboard gains a per-row copy button using each ROW wiki's URL * Clipboard JS in the sheet's existing vanilla-JS JSX: navigator.clipboard first (secure contexts), select+execCommand fallback (config row selects the adjacent input, dashboard rows a transient textarea), Notification feedback on success AND on fallback failure; notification texts are HTML-escaped in JS because the 17.4-baseline Notification widget renders innerHTML (the plain text default only exists since 17.10.9; security-audit Low) * All URL/label emissions escaped; the endpoint works from any farm host (the wiki is selected by the path segment, stated in the hint) * Tests: mcp-server 30 script-service tests incl. exact URL-shape pin, trailing-slash and root-webapp cases, descriptor-preferred pin (request factory never consulted when the wiki advertises a base), null-base fallback, MalformedURLException-to-null, encoded odd id Commit: e34c9efbced9570009540f8d8b79433fbf03135f https://github.com/xwiki-contrib/ai-llm/commit/e34c9efbced9570009540f8d8b794... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPManTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentToolTest.java Log Message: ----------- [Misc] Agent-UX polish from the first real-usage friction log * man xwiki-syntax ESCAPING now warns about the two traps that shipped real bugs when an agent wrote docs ABOUT syntax: link/formatting markup is still parsed inside ##monospace## (escape ~[~[...~]~]), and word:// in prose is auto-detected as a URI scheme * man edit_document blesses the append idiom for growing large pages (anchor old_string on the exact current tail; empty old_string is refused on existing pages precisely so appends stay read-anchored) * The plain rendered banner steers verification to format="html" (plain text flattens links/fences/tables; a missing link target is marked wikicreatelink there - the resolved-link class is stripped by default, so only the negative oracle is advertised) * write_document/edit_document success results note when the saved body contains {{velocity}}/{{groovy}} macros - what users see is generated at view time, so the agent is pointed at rendered=true (the read-side provenance note never covered the write path) * mcp-server 809 tests green Commit: a7e29edbf87de95adf3adb4e3f6750e8d4997997 https://github.com/xwiki-contrib/ai-llm/commit/a7e29edbf87de95adf3adb4e3f675... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaText.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaTextTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaToolTest.java Log Message: ----------- [Misc] get_schema display honesty + write-path refusal polish (usage-test friction) * get_schema flags a StaticList default that is not among the values (a live app shipped default=How-To against tutorial|howto|...) and resolves TextArea content types honestly: the class editor stores a literal --- placeholder that the old code echoed verbatim; now known types render as plain/wiki/velocityCode/velocityWiki, the --- placeholder derives from the editor via the platform's own compatibility rule, and only editor=PureText with unset contenttype stays a bare TextArea * The display renames required write-side normalization to keep the read-to-write round-trip whole (review catch): write_schema's contentType/editor attributes were stored RAW, and 'plain' is not platform vocabulary - getByValue misses it and the field silently wiki-renders while get_schema echoes 'plain' back, hiding the mismatch. Both attributes now accept the display AND platform vocabularies case-insensitively, store the canonical enum values, and refuse unknown tokens with a teaching list * ComputedField and Password write refusals gain the house tail: state that nothing was saved and name the corrective action * mcp-server 828 tests green Commit: 7e45a27c474f698a7483a6805ebb0368eda8ac8a https://github.com/xwiki-contrib/ai-llm/commit/7e45a27c474f698a7483a6805ebb0... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsToolTest.java Log Message: ----------- LLMAI-156: query_objects lists a document's objects without a class * class becomes optional when document is given: a document-only call inventories every object on that page grouped by class - class reference, object number, document version in the header, and deliberately NO field values (strictly less than any per-class call on the same viewable document already shows, so the authorized-only totals and value-oracle decisions are untouched) * Same door as the per-class document restriction (VIEW via resolveAndAuthorize before any query), same ceiling, same per-row authorization and paging; the inventory statement is the per-class HQL minus the class bind with className as a third select column, built from constants with the document name as a bind * select/filters/sort are class-scoped and refused with a teaching message when class is omitted; a nonexistent document reports No such document (not a false empty inventory), echoes neutralized * Born from the DocApp real-usage test: 'what objects does this page carry?' needed the class known in advance * Tests incl. exact grouped-output and statement pins, mid-class paging header re-emission, hostile-echo neutralization, door denial with the store never queried Commit: 4a95506f1ed5a091538b9d6fb820354c7e3e51bc https://github.com/xwiki-contrib/ai-llm/commit/4a95506f1ed5a091538b9d6fb8203... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java Log Message: ----------- LLMAI-157: write_object accepts an optional title * Applied on create and update with write_document's exact semantics and echoes (Title set. / Title updated. / silence when omitted or unchanged), compared against the pre-save clone, one save * Kills the extra version the object-first creation flow needed just to name its page (DocApp real-usage friction: a landing page IS a pure object + title) * Tests: create-with-title single 1.1 version, update retitle, unchanged title not echoed, omitted title untouched Commit: a50d16c1da8bee7a5af8e31579e01bca8f4622e2 https://github.com/xwiki-contrib/ai-llm/commit/a50d16c1da8bee7a5af8e31579e01... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-api/src/main/java/org/xwiki/contrib/llm/mcp/MCPSourceText.java M application-ai-llm-mcp/application-ai-llm-mcp-api/src/test/java/org/xwiki/contrib/llm/mcp/MCPSourceTextTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPDocumentAccess.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPWikiReach.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPObjectQuerySupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryDocumentsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPSchemaWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java Log Message: ----------- LLMAI-153: Budget-cutter seam, echo neutralization everywhere, small hardenings * MCPSourceText gains the shared output-budget cutter: public OUTPUT_TRUNCATION_NOTE + budgeted() (cut at the last complete line within MAX_OUTPUT_CHARS, hard cut when no newline, note appended; callers append honesty footers AFTER the cut). Kills the byte-identical private copies get_schema and query_objects carried - the third and fourth output-budget dialect collapse into one home, next to the constants they read. 3 new MCPSourceTextTest pins * Every echo of an agent-supplied argument (reference, class, wiki id, query text, filter echo) in an agent-facing message is neutralized: MCPTextGuards.fragment inside internal.tool, stripLineBreaks in internal.access (package visibility). Agent-supplied values launder wiki-authored text - agents copy references they just read from page content - so a raw echo is a line-forgery vector into the tool-result grammar the agent trusts. ~30 sites across 13 files converged; supersedes get_document's old "echoed verbatim" @param convention. Sanctioned exception: the aggregated describeActiveFilters echo keeps bare stripLineBreaks (joined parts legitimately exceed the 200 cap) * delete_document's child-count read now shape-dispatches on Number / Object[] like its siblings instead of a raw (Long) cast that would escape as an uncaught CCE on an unexpected store row shape * Dead @SuppressWarnings("checkstyle:IllegalCatch") dropped from MCPObjectQuerySupport.parseOrNull - the ruleset ships that rule commented out, so the suppression suppressed nothing (the XWIKI-20910 version-skew rationale lives on in the Javadoc) * MCPSchemaWriteSupport.setAttr's silent default -> {} now throws IllegalStateException: a future AttrKind without a setter fails loud in tests instead of silently dropping the attribute Commit: 62803e9fc97d06a3cfc1aaca649004cd1aad6d16 https://github.com/xwiki-contrib/ai-llm/commit/62803e9fc97d06a3cfc1aaca64900... Author: Paul Pantiru <[email protected]> Date: 2026-07-19 (Sun, 19 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/access/DefaultMCPDocumentAccessTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/access/MCPWikiReachTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/AbstractMCPToolTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/AbstractMCPWriteToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPDeleteObjectToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetSchemaToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPGetTreeToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPListWikisToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPManToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryDocumentsToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPQueryObjectsToolTest.java A application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPToolTestUtils.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaToolTest.java Log Message: ----------- LLMAI-153: Extract the shared MCP tool test harness * The scan's own math came due: 10 new test classes since the harness was spec'd, textOf copied 9x, the oldcore write prologue duplicated near line-for-line including its comments across six fixtures * MCPToolTestUtils (textOf/request), AbstractMCPToolTest (getTool seam over the MCPTool interface: call/callText/referenceDescription + the 3x-duplicated parseRef/localName fixture helpers), and AbstractMCPWriteToolTest (the four universal @MockComponent doors, USER_REFERENCE, a base @BeforeEach with only the invariant oldcore core, opt-in helpers for the divergent parts: allowSimpleSavePath / registerCurrentResolver / registerLocalizationContext + the shared load/store/translation/version/nothing-saved plumbing) * Platform-grounded on AbstractJobTest/AbstractAttachmentsResourceTest: annotations and @InjectMockComponents stay on the concrete classes; both Mockito extensions walk superclasses via ReflectionUtils.getAllFields, which dedupes by field NAME subclass-first - migration deletes per-class copies, never shadows * 13 classes migrated, 358 lines of duplicated plumbing removed; deliberately NOT unified: buildQueryMock/blogClass (three genuinely different fixtures each), per-class LogCaptureExtension (two write tests deliberately have none), pinned message literals (the house value-oracle stance), and the index module's two tool tests (would need an mcp-api test-jar - flagged, not done) * Hostile-echo pin tests for the neutralization sweep (13 sites, one per swept production file) + a shape pin for delete_document's Object[]/Number child-count row * Pure relocation proved by the gate: mcp-server 842 tests green (841 baseline + the shape pin), checkstyle 0, jacoco met Commit: ff26a75a57316a36e50b556f361a219369118bed https://github.com/xwiki-contrib/ai-llm/commit/ff26a75a57316a36e50b556f361a2... Author: Paul Pantiru <[email protected]> Date: 2026-07-27 (Mon, 27 Jul 2026) Changed paths: M application-ai-llm-index/application-ai-llm-index-api/src/main/java/org/xwiki/contrib/llm/SolrConnector.java M application-ai-llm-index/application-ai-llm-index-api/src/test/java/org/xwiki/contrib/llm/SolrConnectorTest.java Log Message: ----------- [Misc] SolrConnector must not close the platform's shared Solr client * All nine Solr operations wrapped the client obtained from solr.getCore("aillm").getClient() in try-with-resources - but the platform caches ONE client per core for the instance lifetime and only closes it at dispose; no platform caller ever closes it * Works today only by accident of topology: the embedded EmbeddedSolrServer is constructed container-not-owned, so close() is a no-op. With solr.type=remote (the supported cluster topology, also valid single-node) the client is an HttpSolrClient whose close() destroys the shared connection pool - the first chunk operation would kill the cached client and every later store/delete/search fails until restart * Found by the cluster-readiness analysis of the index module (the rest came out clean: local listeners + instance-scoped TaskManager + shared core = exactly-once indexing, no duplicate embedding spend) * Fix: fetch the client into a local variable, never close (checked SolrException from getCore stays inside the existing catch at the catch-sites, declared at the no-catch sites); the test that pinned client.close() now pins never-close with the lifecycle rationale Commit: f4368817748663c20b36e97ff8e33823c90852b2 https://github.com/xwiki-contrib/ai-llm/commit/f4368817748663c20b36e97ff8e33... Author: Paul Pantiru <[email protected]> Date: 2026-07-27 (Mon, 27 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-api/src/main/java/org/xwiki/contrib/llm/mcp/MCPToolSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-api/src/test/java/org/xwiki/contrib/llm/mcp/MCPToolSupportTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaToolTest.java Log Message: ----------- LLMAI-165: Optional hidden flag on the four write tools * write_document, edit_document, write_object and write_schema accept an optional tri-state hidden boolean with the title convention: omitted leaves the flag untouched (an ordinary edit can never silently unhide a page), true/false applied in the same save, echoed only on an actual change (Marked hidden. / Marked visible.) * Born from live AWM testing: agents could build an app but not hide its Code space - class/sheet/template pages were all left visible. write_schema's man page now teaches the convention (class pages are technical pages, pass hidden=true) * Two platform gotchas baked into shared MCPWriteSupport helpers (hiddenChanged/applyHidden/hiddenLine/editableWithHidden), rationale in applyHidden's Javadoc: setHidden(false) clears the metadata-dirty flag (an unhide-only save would persist without a version bump), so the flag is forced back on - AWM's own defensive pattern; and api.Document.setHidden bypasses the wrapper's lazy clone (would mutate the store cache in place), so the flag is applied on an explicit XWikiDocument clone, which provably carries both the value and the dirty markers through later lazy re-clones. write/edit document clone conditionally - the hidden-omitted path is untouched * edit_document accepts a hidden-only call (guard message now offers edit, title or hidden) and its version comment gains ", marked hidden" / ", marked visible" * With locale the flag applies to the translation row being saved, matching the platform edit form and the Solr per-row hidden field * mcp-api: MCPToolSupport gains boolOrNull, the tri-state boolean accessor (bool coerces absent to false, which would have broken omitted-means-untouched) * Tests: four-quadrant tri-state suite per tool incl. the dirty-flag regression pin (hidden-only unhide must bump the version) and combined title+hidden echo-join pins; mcp-api 61, mcp-server 856, checkstyle 0, jacoco met. Live-validated post-redeploy: Solr exclusion/includeHidden, real-Hibernate version bump on unhide, omitted-stays-hidden, per-row fr/en translation split, single-save creation on all four tools Commit: de0ed4fe1806cd4f8a0205df887cad41967bab4a https://github.com/xwiki-contrib/ai-llm/commit/de0ed4fe1806cd4f8a0205df887ca... Author: Paul Pantiru <[email protected]> Date: 2026-07-27 (Mon, 27 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPManTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSchemaTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPManToolTest.java Log Message: ----------- LLMAI-166: man reference page for application structure (AWM) * New man awm reference page teaching agents the AWM app shape and build recipe: the hidden Code space, sheet binding, a four-step core recipe, Create-dialog integration, the #livetable home page, the AWM-citizenship objects, and rights/limits. Grounded in the platform AWM generators * REFERENCE_PAGES switched Map.of -> sorted unmodifiable TreeMap: renderUnknown joins the key set into an agent-facing string, and Map.of iteration order is JVM-random * write_schema's man page cross-references man awm Commit: cd75fa880044d45445b5f33a99caab91900d569c https://github.com/xwiki-contrib/ai-llm/commit/cd75fa880044d45445b5f33a99caa... Author: Paul Pantiru <[email protected]> Date: 2026-07-27 (Mon, 27 Jul 2026) Changed paths: M application-ai-llm-mcp/application-ai-llm-mcp-api/src/main/java/org/xwiki/contrib/llm/mcp/MCPToolSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-api/src/test/java/org/xwiki/contrib/llm/mcp/MCPToolSupportTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectTool.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/main/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteSupport.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPEditDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteDocumentToolTest.java M application-ai-llm-mcp/application-ai-llm-mcp-server/src/test/java/org/xwiki/contrib/llm/mcp/internal/tool/MCPWriteObjectToolTest.java Log Message: ----------- [Misc] Close two write-tool gaps found in live app building * An explicit empty title now CLEARS the title on write_document, edit_document and write_object (echo: Title cleared.); omitted still means untouched. mcp-api gains stringOrEmpty, the accessor distinguishing an absent parameter from a present-but-empty one * write_object accepts an empty fields map: creates marker objects carrying only the class defaults; an update with no fields, no title and no hidden is refused as nothing-to-change; the class-defaults note is only echoed when the object was created * Shared titleLine helper in MCPWriteSupport replaces the three per-tool title echo blocks * mcp-api 65 tests, mcp-server 871, checkstyle 0, jacoco met Compare: https://github.com/xwiki-contrib/ai-llm/compare/039d5d94fc41...cd75fa880044 To unsubscribe from these emails, change your notification settings at https://github.com/xwiki-contrib/ai-llm/settings/notifications
participants (1)
-
XWiki Notifications