Content Capabilities
MIC Resolvers
A MIC resolver is a small runtime bridge from a rich-text token to reusable HTML. Authors store a token in a rich-text field. MyronCMS later resolves that token through a registered resolver when rendering the field.
Use MIC resolvers for structured content that belongs outside a single paragraph but should be inserted inside rich text, such as tables, forms, catalog cards, course references, quiz embeds, or extension-owned reusable blocks.
Mental Model
The lifecycle has four parts:
- A developer authors a resolver class under
app/extensions/{vendor}/. - The extension returns that resolver from
bootstrap.phpundermicElementTypes. - The loader registers the local slug as
x.{vendor}.{slug}throughMicElementRegistry::registerExtension(). - Rich-text rendering replaces matching
[mic-*]tokens with resolver output.
The resolver interface is intentionally small:
interface MicElementResolverInterface
{
public function resolve(string $id, array $context): ?string;
public function describe(): array;
}
describe() makes the type discoverable. resolve() turns one item id into HTML.
Token Shape
Core tokens use:
[mic-{slug}-mic_0123456789abcdef]
Extension tokens use:
[mic-x.{vendor}.{slug}-mic_0123456789abcdef]
The runtime token matcher accepts ids shaped like mic_[a-f0-9]{16}. Near-miss strings are left alone. A matching token with no resolver output renders a missing marker instead of silently disappearing.
Human Path
A developer creates an extension resolver, returns it from bootstrap.php, and has an operator register, enable, and grant the extension. A content author can then insert the resolver's token into a rich-text field through an allowed UI path.
The resolver decides how to read extension-owned content. Extension storage must still follow the extension storage rules, including {vendor}_* table boundaries when migrations are used.
AI Path
An AI coding agent can author the local resolver files, update bootstrap.php, and run verification in the repository.
An installed MCP client cannot create the PHP resolver, enable the extension, or grant scopes. After the extension is registered, enabled, loaded, and scope-covered, an AI client may discover registered MIC type descriptors through the content MIC type listing action exposed to MCP.
Runtime Context
During rich-text resolution, MyronCMS passes a context array with these current keys:
| Key | Meaning |
|---|---|
pageId | Current page id when the renderer knows it. |
fieldKey | Rich-text field key being rendered. |
channel | Rendering channel, such as an admin preview or public render. |
userScopes | Scope list available to the render context. |
Treat context keys as additive. The public interface is tagged @apiVersion 1, and future context growth should not require a method signature change.
Scopes
The resolver descriptor includes scopes. Use the least scope needed by the content lookup. Common read-only resolvers use:
'scopes' => [MyronApiScopes::CONTENT_READ],
The descriptor scopes are discovery and safety metadata for content capability use. They do not give a resolver permission to bypass extension grants, loader checks, or caller scope checks on separate actions.
Artifacts
Authoring artifacts:
app/extensions/{vendor}/bootstrap.phpapp/extensions/{vendor}/{ResolverName}.php- optional extension tables under
{vendor}_*
Runtime artifacts:
MicElementResolverInterfaceMicElementRegistry- rich-text field values containing
[mic-*]tokens /api/v1/content/mics/types- MCP
tools/listfor the loaded type-listing action when the caller hascontent.read
Safety Boundary
Rich-text input is sanitized before MIC token resolution. Resolver output is not the same thing as arbitrary rich-text input; it is pre-blessed HTML returned by trusted local extension code.
Do not use a resolver to inject scripts, forms with unsafe handling, core admin chrome, public render hooks, or cross-extension data access. A resolver should return deterministic, accessible HTML for the requested id or null when it cannot resolve safely.
Verification
Run the MIC registry and Content Banks checks:
php app/tests/verify-mic-registry.php
php app/tests/verify-content-banks-mic.php
For a loaded extension, verify that:
knownTypes()includesx.{vendor}.{slug},/api/v1/content/mics/typesreturns the descriptor for acontent.readcaller,- a valid token renders the expected HTML,
- an unknown id renders a missing marker,
- a near-miss token remains unchanged.
Failure Modes
| Failure | What it means | Safe response |
|---|---|---|
| Resolver never appears in known types | Extension did not load, was not enabled, lacked grants, or did not return the resolver from micElementTypes. | Fix extension load state and bootstrap shape. |
| Descriptor slug mismatch | describe() does not return the registered fully qualified slug. | Return x.{vendor}.{slug} from describe(). |
| Token remains unchanged | The token does not match the runtime token shape. | Use [mic-x.{vendor}.{slug}-mic_0123456789abcdef]. |
| Missing marker renders | The token matched, but the resolver returned null or an empty string. | Check the id and resolver lookup path. |
| AI client cannot create a resolver | Expected behavior for installed MCP clients. | Use local-authoring in the repository, then register and grant the extension. |