Content Capabilities
Register A MIC Type
Register a MIC type when an extension needs reusable content that can be inserted into rich-text fields by token. The extension owns the resolver code and any backing storage. MyronCMS owns registration, discovery, and rich-text token resolution.
This tutorial registers a catalog MIC type for my_vendor.
Prerequisites
- An extension package exists under
app/extensions/my_vendor/. manifest.jsondeclares the extension identity and required scopes.- The extension can be registered, enabled, and granted
content.read. - The resolver can read its backing content without crossing another extension's storage boundary.
Human Path
Create a resolver class:
<?php
declare(strict_types=1);
final class MyVendorCatalogMicResolver implements MicElementResolverInterface
{
public function __construct(private readonly MyronExtensionSDK $sdk)
{
}
public function resolve(string $id, array $context): ?string
{
if (!preg_match('/^mic_[a-f0-9]{16}$/', $id)) {
return null;
}
$label = 'Catalog item ' . substr($id, -4);
return '<article class="my-vendor-catalog-card" data-mic-id="' . myronEscape($id) . '">'
. '<h3>' . myronEscape($label) . '</h3>'
. '<p>Reusable extension content.</p>'
. '</article>';
}
public function describe(): array
{
return [
'slug' => 'x.my_vendor.catalog',
'label' => 'Catalog Item',
'description' => 'Reusable catalog block.',
'icon' => 'box',
'scopes' => [MyronApiScopes::CONTENT_READ],
'bankSurface' => 'picker',
'parentSurfaceUrl' => $this->sdk->buildAdminUrl('x.my_vendor.catalog'),
];
}
}
Return it from bootstrap.php:
<?php
declare(strict_types=1);
require_once __DIR__ . '/MyVendorCatalogMicResolver.php';
return [
'micElementTypes' => [
'catalog' => new MyVendorCatalogMicResolver($sdk),
],
];
The bootstrap.php key is the local slug: catalog. The registry constructs the full extension type: x.my_vendor.catalog. The resolver descriptor must report that full slug.
Insert tokens using:
[mic-x.my_vendor.catalog-mic_0123456789abcdef]
AI Path
An AI coding agent can author the resolver file and bootstrap.php locally, then run verification. It should not invent runtime install automation, write directly to extension grant tables, or claim that an installed MCP client can create PHP files.
After a human operator registers and grants the extension, an AI client with content.read can discover loaded MIC descriptors through the content MIC type listing action. If that action is exposed through MCP for the caller, it appears in MCP tools/list.
Descriptor Fields
| Field | Required | Use |
|---|---|---|
slug | Yes | Fully qualified type, such as x.my_vendor.catalog. |
label | Yes | Human-readable type label for UI and discovery. |
description | Yes | Short explanation for pickers and AI clients. |
icon | Yes | Small icon identifier for UI presentation. |
scopes | Yes | Scope metadata required by this content type. |
bankSurface | Yes | One of editor, picker, or none. |
parentSurfaceUrl | Required for picker | Admin URL where entries are managed. |
adminUrl | Optional | Additional admin URL metadata. |
Scopes
Use content.read for read-only insertion and discovery. If the resolver depends on write actions or admin configuration, document those separately in the extension action or admin-route docs. MIC type registration itself does not authorize separate writes.
Artifacts
Files:
app/extensions/my_vendor/MyVendorCatalogMicResolver.phpapp/extensions/my_vendor/bootstrap.php- optional extension storage under
{vendor}_*
Runtime:
MicElementRegistry::registerExtension('my_vendor', 'catalog', $resolver)/api/v1/content/mics/types[mic-x.my_vendor.catalog-mic_0123456789abcdef]
Safety Boundary
Do not pass a fully qualified x.* slug as the bootstrap.php key. The extension loader passes the vendor and local slug to the registry. The descriptor then confirms the fully qualified slug.
Do not return unsafe HTML. Resolver output should be deterministic, escaped where it includes data, and limited to the extension's documented content surface.
Verification
Run:
php app/tests/verify-mic-registry.php
Then verify the extension runtime state:
- The extension is registered, enabled, and granted
content.read. knownTypes()containsx.my_vendor.catalog./api/v1/content/mics/typesreturns the descriptor for acontent.readcaller.- A token shaped like
[mic-x.my_vendor.catalog-mic_0123456789abcdef]resolves in rich text.
Failure Modes
| Failure | What it means | Safe response |
|---|---|---|
Mic extension vendor must be snake_case. | Vendor is empty or not snake_case. | Align directory, manifest, and vendor naming. |
Extension mic slug must be the local slug | bootstrap.php used x.my_vendor.catalog as the key. | Use catalog as the key. |
Mic resolver descriptor slug must match the registered slug. | describe() returned the wrong slug. | Return x.my_vendor.catalog. |
Mic bankSurface must be editor, picker, or none. | Descriptor used an unsupported surface. | Choose one of the implemented values. |
Picker mic types require parentSurfaceUrl. | bankSurface is picker without a parent URL. | Add a parent admin surface URL. |
| Duplicate type rejected | Another resolver already registered the same slug. | Choose a unique local slug. |