Wiki source code of Access Doors

Last modified by Vincent Massol on 2026/09/04 20:04

Show last authors
1 The access doors centralize the MCP server's authorization and scoping decisions so that no tool re-implements them.
2
3 Three are public component roles of the {{code}}application-ai-llm-mcp-api{{/code}} module (package {{code}}org.xwiki.contrib.llm.mcp{{/code}}), so a tool contributed by any module can inject them: {{code}}MCPDocumentAccess{{/code}}, {{code}}MCPWikiReach{{/code}} and {{code}}MCPDocumentSearch{{/code}}. Two are internal to the server module (package {{code}}org.xwiki.contrib.llm.mcp.internal.access{{/code}}): {{code}}MCPSpaceFilter{{/code}}, which the public doors apply on the caller's behalf, and {{code}}MCPRowQuery{{/code}}.
4
5 A tool must never resolve a {{code}}DocumentReference{{/code}} itself: doing so escapes the space policy and can reach another wiki unchecked.
6
7 == MCPDocumentAccess ==
8
9 Single sanctioned way to resolve a reference and enforce authorization.
10
11 {{code language="java"}}
12 @Role
13 public interface MCPDocumentAccess {
14 DocumentReference resolveAndAuthorize(String reference, Right right)
15 throws MCPAccessDeniedException;
16 }
17 {{/code}}
18
19 Applies in order: reach gate → {{code}}ContextualAuthorizationManager.hasAccess{{/code}} → space filter. Throws {{code}}MCPAccessDeniedException{{/code}} with an agent-facing refusal message.
20
21 {{plantuml}}
22 @startuml
23 !theme bluegray
24 start
25 :a tool calls resolveAndAuthorize\nwith the reference it was given;
26 :resolve it against the current context;
27 if (the target is in another wiki?) then (yes)
28 if (this endpoint may reach that wiki?) then (no)
29 :refused: cross-wiki reach\nis not enabled for this endpoint;
30 stop
31 else (yes)
32 endif
33 else (no)
34 endif
35 if (the user has the required right\non the document?) then (no)
36 :refused: you do not have\npermission to view or edit it;
37 stop
38 else (yes)
39 endif
40 if (the space filter allows it?) then (no)
41 :refused: outside the content\nthis endpoint exposes;
42 stop
43 else (yes)
44 endif
45 :the tool receives the DocumentReference;
46 stop
47 @enduml
48 {{/plantuml}}
49
50 == MCPSpaceFilter ==
51
52 Applies the MCP space whitelist or blacklist. Internal to the server module: the public doors above already apply it, so a tool in another module does not call it directly.
53
54 {{code language="java"}}
55 @Role
56 public interface MCPSpaceFilter {
57 boolean isAllowed(DocumentReference reference);
58 List<String> filterQueries();
59 }
60 {{/code}}
61
62 |=Method |=Returns
63 |{{code}}isAllowed(reference){{/code}} |Whether that one document passes the configured filter mode and entries
64 |{{code}}filterQueries(){{/code}} |The Solr filter query clauses that scope search results to the same set
65
66 A space entry covers the space and its whole subtree; a document entry matches exactly one document. Both fail //closed// on read errors: {{code}}isAllowed{{/code}} denies, and {{code}}filterQueries{{/code}} returns a match-nothing {{code}}-*:*{{/code}} clause.
67
68 == MCPWikiReach ==
69
70 Decides how far an endpoint reaches beyond its own wiki.
71
72 {{code language="java"}}
73 @Role
74 public interface MCPWikiReach {
75 boolean isReachEnabled();
76 boolean canReachWiki(String wikiId);
77 List<String> resolveSearchWikis(String wikiParam);
78 }
79 {{/code}}
80
81 |=Method |=Returns
82 |{{code}}isReachEnabled(){{/code}} |Whether this endpoint may cross wikis at all
83 |{{code}}canReachWiki(wikiId){{/code}} |Whether that one target wiki is reachable
84 |{{code}}resolveSearchWikis(wikiParam){{/code}} |The wikis a search covers, from the tool's {{code}}wiki{{/code}} parameter: blank → the current wiki, {{code}}"all"{{/code}} → the whole farm, an id → that single wiki, validated
85
86 Backed by the main-wiki-only {{code}}reachEnabledWikis{{/code}} list. A reach-enabled endpoint reaches every wiki in the farm regardless of the target's own enable gate, tool toggles, or space filter.
87
88 == MCPDocumentSearch ==
89
90 Builds a Solr search query scoped to wikis, space filter, and rights.
91
92 {{code language="java"}}
93 @Role
94 public interface MCPDocumentSearch {
95 Query createQuery(String statement, List<String> additionalFilterQueries,
96 List<String> targetWikiIds);
97 }
98 {{/code}}
99
100 Uses {{code}}SecureQuery{{/code}} with {{code}}checkCurrentUser(true){{/code}}, wiki-scope clauses, and the space filter — the single query path for {{code}}query_documents{{/code}}.
101
102 == MCPAccessDeniedException ==
103
104 {{code language="java"}}
105 public class MCPAccessDeniedException extends Exception {
106 public MCPAccessDeniedException(String message) { ... }
107 }
108 {{/code}}
109
110 Carries the agent-facing refusal message. Thrown by {{code}}MCPDocumentAccess.resolveAndAuthorize(){{/code}} and caught by the tool to produce an {{code}}isError{{/code}} result.
111
112 == MCPRowQuery ==
113
114 Internal door to the authorized HQL row pipeline of the navigation tools: bounded row fetching against one target wiki, plus per-row resolution and authorization of the fetched rows.
115
116 * Every fetch limit is clamped to at most {{code}}MAX_FETCH_PER_QUERY{{/code}} (2000 rows), so a broad statement cannot materialize an unbounded row set.
117 * {{code}}resolveInto{{/code}} resolves a row explicitly into the target wiki, and {{code}}isAuthorized{{/code}} applies the space filter and then the view right, in that order.
118 * Hidden-page exclusion is explicit HQL rather than a query filter, so it holds regardless of the calling user's own profile preference.

Get Connected