Core Concepts
Scopes And Grants
Scopes are the permission vocabulary shared by human/admin workflows, AI bearer callers, API dispatch, MCP discovery, and extension loading.
An extension must pass two layers of scope control:
- The extension must be registered, enabled, and granted the scopes declared in its manifest.
- The API or MCP caller must hold the action's required scope at call time.
Base Extension Scopes
Extension manifests can request only these six base scopes:
| Scope | Use |
|---|---|
design.read | Read design surfaces. |
design.write | Mutate design surfaces. |
content.read | Read content surfaces. |
content.write | Mutate content surfaces. |
administration.read | Read administration surfaces. |
administration.write | Mutate administration surfaces. |
Scopes.php also contains scopes used by other platform areas, but ExtensionManifest.php and ExtensionScopeGrantRepository.php restrict extension manifests and extension grants to the six base scopes above.
Human Path
An operator registers a locally installed extension from the Extensions admin surface:
/extensions/register
The operator provides:
- vendor,
- name,
- version,
- granted scopes.
Installed extensions are reviewed from:
/extensions
That surface shows enabled/disabled state, granted scopes, load health, and latest load errors. It also lets an administrator enable, disable, or revoke individual scopes.
AI Path
The grant workflow is human-admin-only in extension v1. An installed MCP client cannot grant itself scopes, enable an extension, or revoke the trust gate that controls it.
An AI coding agent working locally can author the manifest and action metadata. After the operator grants scopes and enables the extension, an installed API or MCP client can call exposed actions only when its caller context also holds the action's required scope.
The Scope Gates
| Gate | Source | Effect |
|---|---|---|
Manifest requiredScopes | manifest.json | Declares what the extension needs to load safely. |
| Operator grant row | extension_scope_grants | Records allowed scopes and enabled state for a vendor. |
| Loader grant check | ExtensionLoader::isGranted() | Skips unregistered or disabled extensions and rejects under-granted extensions. |
Action requiredScope | MyronActionMetadata | Declares the caller scope needed for one action. |
| API dispatcher caller scope | ActionDispatcher through caller context | Rejects calls when the caller lacks the action scope. |
MCP tools/list filter | Server.php | Lists only exposed tools the caller scope can invoke. |
The gates are additive. Passing one does not bypass the others.
MCP Discovery
MCP tool listing is filtered by:
exposeToMcp: true,- the caller's held scopes.
If an action is valid and API-callable but exposeToMcp is false, it should not appear in MCP tools/list. If the caller lacks the required scope, the tool should also be omitted for that caller.
Failure Modes
| Symptom | Likely cause |
|---|---|
| Extension is absent from runtime actions | Vendor was not registered, extension is disabled, or loader skipped it. |
| Load error mentions ungranted scope | Manifest requested a scope that the operator did not grant. |
API returns auth_required | Request has no valid session or bearer caller. |
API returns auth_invalid | Bearer token or session caller is invalid. |
API returns scope_insufficient | Caller lacks the action's required scope. |
| MCP tool missing | Action is not loaded, exposeToMcp is false, or caller lacks scope. |
| Scope revoke makes a previously working extension fail | The manifest still requires a scope no longer granted. |
Safety Boundary
Do not document trust-granting as MCP self-service. Grant creation, enable/disable, and revocation are operator/admin workflows. If a future contract introduces an API or MCP route for privileged administration, it must be evaluated as a separate safety decision.
Verification
Run the foundation test:
php app/tests/verify-extension-system-foundation.php
For runtime verification, call an extension action with a caller that holds the action scope, then repeat with a caller that lacks it. The scoped caller should succeed; the under-scoped caller should fail or see no MCP tool.