Commit de134bb
authored
feat(mcp-tools): ScholiqToolProvider — AI companion MCP tools (MVP skeleton) (#39)
First per-app implementation of OCA\OpenRegister\Mcp\IMcpToolProvider for
Scholiq (LVS + LMS). Scholiq handles student data, which is privacy
sensitive — so the MVP deliberately ships ONLY the two least sensitive,
read-only tools (the course catalogue and a course's module structure):
- scholiq.listCourses — list visible courses (catalogue only, no
learner data); optional limit (1-50, capped at 20) + status filter.
- scholiq.getCourseDetails — one course by id/uuid/slug with its ordered
module (Lesson) structure; course + module metadata only — never
Enrolment, Attestation, Credential or learner objects.
Tools that touch learner records are deferred to a follow-up that wires
proper per-student authorisation (a teacher of that learner's group, the
learner themself, or an admin).
Architecture (hydra ADR-034 + ADR-035):
- lib/Mcp/ScholiqToolProvider.php delegates to OR's ObjectService (same
pattern existing scholiq controllers/handlers already use); injects
IUserSession + IGroupManager for the auth gate, LoggerInterface for
error logging.
- Tool ids namespaced scholiq.{tool}; getTools() always returns the full
catalogue; per-request authorisation runs in invokeTool() AFTER argument
validation but BEFORE business logic. requireCourseReadAccess() rejects
anonymous callers; OpenRegister RBAC inside ObjectService is the second,
per-object gate (so _rbac/_multitenancy stay at their default true).
- invokeTool() never throws — all failure paths return a structured error
array (invalid_arguments / forbidden / not_found / internal_error /
unknown_tool).
- Application.php registers the alias
'OCA\OpenRegister\Mcp\IMcpToolProvider::scholiq' → ScholiqToolProvider.
Until openregister PR #1466 (ai-chat-companion-orchestrator) ships the
real interface, Scholiq implements the stub at
tests/Stubs/Mcp/IMcpToolProvider.php (loaded by tests/bootstrap.php and
tests/bootstrap-unit.php when the interface isn't autoloadable).
Tests: tests/Unit/Mcp/ScholiqToolProviderTest.php — 8 tests / 58
assertions: getAppId, catalogue shape, unknown-tool error envelope (no
throw), argument validation, the anonymous-caller forbidden path, and a
happy-path getCourseDetails that asserts no learner-PII keys leak.
Static analysis: psalm.xml suppresses Undefined-class for the
IMcpToolProvider interface; phpstan.neon scans tests/Stubs so the
cross-app interface resolves, plus broadens the existing OpenRegister
ignore patterns ('unknown interface', 'has invalid type') — which also
clears some pre-existing CredentialIssuanceHandler/XapiCompletionHandler
noise.
Widget mount: bumps @conduction/nextcloud-vue to ^1.0.0-beta.30 (scholiq
already mounts CnAppRoot, which surfaces the AI chat companion from the
manifest's openregister dependency — no extra scaffolding needed).
Refs #361 parent f4583b4 commit de134bb
8 files changed
Lines changed: 1056 additions & 0 deletions
File tree
- lib
- AppInfo
- Mcp
- tests
- Stubs/Mcp
- Unit/Mcp
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
107 | 118 | | |
108 | 119 | | |
109 | 120 | | |
| |||
0 commit comments