Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

feat(mcp-tools): OpenBuiltToolProvider — AI companion MCP tools#14

Merged
rubenvdlinde merged 1 commit into
developmentfrom
feature/openbuilt-mcp-tools
May 12, 2026
Merged

feat(mcp-tools): OpenBuiltToolProvider — AI companion MCP tools#14
rubenvdlinde merged 1 commit into
developmentfrom
feature/openbuilt-mcp-tools

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Summary

Adds the per-app MCP tool provider for OpenBuilt so the AI Chat Companion (hydra ADR-034) can surface OpenBuilt's virtual-app catalogue to an LLM. Implements OCA\OpenRegister\Mcp\IMcpToolProvider per hydra ADR-035 (MCP tool surface). The interface ships in openregister PR #1466 (not yet merged); until then OpenBuilt implements the test stub at tests/Stubs/Mcp/IMcpToolProvider.php (loaded via composer autoload-dev PSR-4).

This is an MVP skeleton — two read-only tools backed by code that exists today (ApplicationsController already does the slug → manifest lookup; OpenBuilt stores application + built-app-route objects in OpenRegister's openbuilt register).

Tools (2, read-only)

id description
openbuilt.listApps List the virtual apps built with OpenBuilt in your organisation (slug, name, status, version). Args: limit (int 1–50, default 20), statusFilter (any/draft/published/archived).
openbuilt.getAppManifest Fetch the runtime manifest blob for one published virtual app, by kebab-case slug (required). Resolves slug → BuiltAppRoute → Application → manifest.

Auth design (OWASP A01 / ADR-005): requireAuthenticatedUser() runs after argument validation and before business logic; it returns string|null (no unconditional pass, no catch(\Throwable) swallow). Object-level scoping to the caller's organisation is enforced by OpenRegister's ObjectService multitenancy filter. invokeTool() never throws — every failure path returns ['isError'=>true,'error'=>…,'message'=>…]. List results are capped at 20 items.

Files

  • lib/Mcp/OpenBuiltToolProvider.php — the provider (SPDX in docblock, @category Mcp, @package OCA\OpenBuilt\Mcp)
  • tests/Stubs/Mcp/IMcpToolProvider.php — interface stub (used in CI before openregister PR #1466)
  • tests/Unit/Mcp/OpenBuiltToolProviderTest.php — 9 tests (getAppId, catalogue shape, unknown-tool error array, arg validation, unauthenticated-forbidden gate, auth-passed-then-internal-error)
  • lib/AppInfo/Application.phpregisterServiceAlias('OCA\OpenRegister\Mcp\IMcpToolProvider::openbuilt', OpenBuiltToolProvider::class)
  • composer.jsonautoload-dev PSR-4 OCA\OpenRegister\tests/Stubs/
  • package.json / package-lock.json — bump @conduction/nextcloud-vue ^1.0.0-beta.30^1.0.0-beta.31

Quality

composer check (lint, phpcs, psalm, test:unit) passes. phpmd clean. psalm / phpstan clean. npm run lint clean. New unit tests (9) pass.

Pre-existing (unrelated): tests/unit/Controller/ApplicationsControllerTest.php has 5 errors when run outside the NC container (createMock(Register::class)->method('getId') against the tests/stubs/openregister-stubs.php stubs, which don't declare the Register/Schema entities) — passes in CI where openregister is installed. Not touched here.

Done vs. deferred

Done: the two tools above, DI registration, stub interface, unit tests, nc-vue bump.

Widget mount: the nc-vue bump is the mount — CnAppRoot in beta.31 auto-mounts <CnAiCompanion />. OpenBuilt's BuilderHost.vue already uses CnAppRoot, so the companion renders inside virtual apps. Wiring CnAppRoot into the OpenBuilt shell itself (App.vue currently uses NcContent) is a separate frontend task — deferred.

Deferred (remaining #13 work): write tools (create app, publish/archive, edit manifest), template-marketplace tools, schema-editor tools, RBAC-aware scoping once the RBAC spec lands. Tracked under #13.

Refs #13 · hydra ADR-034 · hydra ADR-035

MCP coverage

  • openbuilt.listApps — list the organisation's OpenBuilt virtual apps (slug/name/status/version), capped at 20, auth-gated, org-scoped via OpenRegister multitenancy
  • openbuilt.getAppManifest — fetch a published virtual app's runtime manifest by slug (slug → BuiltAppRoute → Application → manifest), auth-gated

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openbuilt @ a046677

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 428/428
PHPUnit
Newman
Playwright ⏭️

Coverage: 0% (0/71 statements)


Quality workflow — 2026-05-12 05:04 UTC

Download the full PDF report from the workflow artifacts.

…skeleton)

Implements OCA\OpenRegister\Mcp\IMcpToolProvider (hydra ADR-034 AI Chat
Companion + ADR-035 MCP tool surface) with two read-only MVP tools:

- openbuilt.listApps        — list the virtual apps in the caller's org
- openbuilt.getAppManifest  — fetch one published virtual app's manifest by slug

Per-object auth (requireAuthenticatedUser) runs after argument validation
and before business logic; org scoping is enforced by OpenRegister's
ObjectService multitenancy filter. invokeTool never throws — all failure
paths return a structured error envelope.

Adds:
- lib/Mcp/OpenBuiltToolProvider.php
- tests/Stubs/Mcp/IMcpToolProvider.php (stub until openregister PR #1466)
- tests/Unit/Mcp/OpenBuiltToolProviderTest.php
- DI registration in lib/AppInfo/Application.php
- composer autoload-dev PSR-4 for the stub
- bump @conduction/nextcloud-vue to ^1.0.0-beta.31 (CnAppRoot auto-mounts CnAiCompanion)

Refs #13
@rubenvdlinde rubenvdlinde force-pushed the feature/openbuilt-mcp-tools branch from 49311fe to 0e39294 Compare May 12, 2026 12:25
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Rebased onto development (6b3c2ef): dropped the obsolete nc-vue beta.31 bump (dev is at beta.35), merged Application.php (kept the chain's event listeners — DeepLinkRegistrationListener + ApplicationVersionSnapshotListener — plus the MCP service alias), kept the tests/Stubs/Mcp/IMcpToolProvider.php stub (OR #1466 still unmerged). phpunit-unit.xml 84/84 pass (incl. 9 OpenBuiltToolProviderTest); phpcs/phpmd/psalm/phpstan/eslint/stylelint clean; build OK. New HEAD 0e39294. Ready to merge.

@rubenvdlinde rubenvdlinde merged commit 6591806 into development May 12, 2026
47 of 49 checks passed
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

CI follow-up: all quality gates green (PHP quality x6, PHPUnit matrix PHP 8.3/8.4 x NC 31/32/33, Vue eslint/stylelint, Security composer/npm, License). The only red check — Integration Tests (Newman) ("OpenRegister-backed Application CRUD" round-trip: manifest endpoint returns 404, DELETE returns 404) — is pre-existing on development: the same job fails on dev's HEAD (6b3c2ef, PR #28 merge). It is unrelated to this read-only MCP provider change and is not regressed by the rebase. Recommend admin-merge; the Newman breakage should be tracked separately.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openbuilt @ 06faf55

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 432/432
PHPUnit
Newman
Playwright ⏭️

Coverage: 0% (0/19 statements)


Quality workflow — 2026-05-12 12:35 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant