Build Extensions
Add An Extension Admin Route
An extension admin route adds a human-facing screen under the authenticated MyronCMS admin app. Use it for setup panels, diagnostics, or extension-owned management UI.
Admin routes are not actions. They do not appear in MCP tools/list, and an installed MCP client cannot call them as tools. The AI-supported path is local file authoring in the repository.
Goal
Create a local route at:
/x/my_vendor/settings
from an extension package at:
app/extensions/my_vendor/
Prerequisites
- You are working in the MyronCMS repository.
- The extension has a valid
manifest.json. - The operator can register and enable the extension.
- The route is intended for an authenticated administrator in the browser.
Human Path
Add an adminRoutes entry to manifest.json:
{
"vendor": "my_vendor",
"name": "My Vendor",
"version": "0.1.0",
"requiredScopes": ["administration.read"],
"hasMigrations": false,
"adminRoutes": [
{
"key": "settings",
"path": "admin/routes/settings.php"
}
]
}
Create the route file:
app/extensions/my_vendor/admin/routes/settings.php
Use the admin shell helpers:
<?php
declare(strict_types=1);
require_once dirname(__DIR__, 4) . '/system/bootstrap.php';
$currentUser = myronRequireAdministrationAccess();
$sdk = new MyronExtensionSDK('my_vendor');
myronRenderAdminShellStart($currentUser, [
'title' => 'My Vendor Settings',
'titleContext' => 'Extensions',
'activeItem' => 'settings',
]);
?>
<div class="mc-workspace__header">
<?php myronRenderAdminBreadcrumbs([
['label' => 'Extensions', 'href' => myronBuildAdminUrl('extensions.installed')],
['label' => 'My Vendor Settings'],
]); ?>
<div class="mc-workspace__title-row">
<h1 class="mc-workspace__title">My Vendor Settings</h1>
</div>
<p class="mc-workspace__subtitle">Manage settings owned by this extension.</p>
</div>
<div class="mc-page-canvas mc-page-canvas--site">
<div class="sg-main">
<section class="sg-section">
<h2 class="sg-section__title">Runtime</h2>
<dl class="sg-define-list">
<dt>Vendor</dt><dd><code><?= myronEscape($sdk->getVendor()) ?></code></dd>
<dt>Site type</dt><dd><code><?= myronEscape($sdk->getSiteType()) ?></code></dd>
</dl>
</section>
</div>
</div>
<?php myronRenderAdminShellEnd(); ?>
After the operator registers and enables the extension, MyronCMS mounts the route during loader boot.
AI Path
An AI coding agent can create or edit:
app/extensions/my_vendor/manifest.jsonapp/extensions/my_vendor/admin/routes/settings.php
The agent should then run verification and report the local route that will be mounted after the extension is registered and enabled.
An installed MCP client cannot create route files, register the route as a tool, or force the loader to mount it. If an agent is operating through MCP only, the safe output is an implementation plan or a parity note, not a runtime route change.
Route Mapping
ExtensionManifest.php requires each route entry to have:
| Field | Constraint |
|---|---|
key | Kebab-case style: starts with a lowercase letter and contains lowercase letters, numbers, underscores, or hyphens. |
path | Relative path under the extension directory. It must not be empty, contain .., or start with /. |
ExtensionLoader mounts existing handler files under:
/x/{vendor}/{tail}
The route tail comes from key. If the key repeats the vendor slug or the vendor short name, the loader strips that prefix. For example:
| Vendor | Key | Mounted path |
|---|---|---|
my_vendor | settings | /x/my_vendor/settings |
my_vendor | my-vendor-settings | /x/my_vendor/settings |
my_vendor | vendor-settings | /x/my_vendor/settings |
Scopes
Admin route files do not have action-level requiredScope metadata. Access is controlled by the admin route itself, usually with:
$currentUser = myronRequireAdministrationAccess();
The extension still must pass the loader grant gates. If the manifest declares administration.read, the operator grant must include administration.read before the extension loads.
Artifacts
Local files:
app/extensions/my_vendor/manifest.jsonapp/extensions/my_vendor/admin/routes/settings.php
Runtime surface:
/x/my_vendor/settings
Source references:
app/extensions/myron-sample/admin/routes/hello.phpapp/system/extensions/ExtensionManifest.phpapp/system/extensions/ExtensionLoader.phpapp/system/routing/AppFrontController.php
Safety Boundary
Do not document extension admin routes as MCP-callable tools. If an extension needs AI-callable behavior, expose a scoped MyronAction separately.
Do not skip myronRequireAdministrationAccess() for privileged admin UI. Do not use admin route files to mutate core tables, alter core shell navigation, or bypass extension registration and scope grants.
Do not use absolute route paths or paths that escape the extension directory. The manifest validator rejects those shapes.
Verification
Run:
php app/tests/verify-extension-system-foundation.php
For the reference extension, the test verifies that:
- the sample extension route is registered at
/x/myron_sample/hello, extensions.registerresolves,extensions.installedresolves,- the sample extension URL key resolves to
x/myron_sample/hello.
For a new route, verify that:
- the manifest parses,
- the extension is registered and enabled,
- required manifest scopes are granted,
- the handler file exists at the manifest path,
- the browser route loads for an administrator.
Failure Modes
| Failure | Likely surface | Safe fix |
|---|---|---|
Extension adminRoutes entries must be objects. | Manifest parse. | Use objects with key and path. |
Extension admin route keys must be kebab-case. | Manifest parse. | Use a lowercase route key such as settings. |
Extension admin route paths must be relative paths. | Manifest parse. | Use admin/routes/settings.php, not /admin/... or ../.... |
| Route is not mounted | Loader route registration. | Confirm the extension is registered, enabled, granted, and the handler file exists. |
| Browser route redirects or forbids access | Admin access gate. | Sign in as an administrator; do not weaken the route guard. |
Route absent from MCP tools/list | Expected behavior. | Admin routes are browser UI; create a scoped action for AI-callable behavior. |