MyronCMS Developers Hub Docs sandbox review

Integrations

Mail Transports

Mail transports let MyronCMS send outbound email through a narrow provider contract. Core registers the built-in smtp transport at boot. Extensions can add provider-specific transports by returning transport instances from bootstrap.php.

Use this surface when an integration provider needs to make a delivery backend available to MyronCMS without changing form notification code or other mail callers.

Mental Model

The lifecycle has five parts:

  1. A developer authors a MailTransportInterface implementation under app/extensions/{vendor}/.
  2. The extension returns that instance from bootstrap.php under mailTransports.
  3. The loader registers each local slug through MailTransportRegistry::registerExtension().
  4. The registry stores extension transports under x.{vendor}.{slug}.
  5. Operators and scoped clients inspect known descriptors through the mail transport listing action.

The interface is intentionally small:

interface MailTransportInterface
{
    public function send(MailEnvelope $envelope): MailDispatchResult;
    public function describe(): array;
}

send() receives the normalized MailEnvelope. describe() returns the discovery metadata that the admin/API/MCP listing path exposes.

Human Path

A developer creates a provider transport class, updates the extension bootstrap.php, and has an operator register, enable, and grant the extension. The transport then appears in the registry when the extension loads.

Example bootstrap.php shape:

<?php

declare(strict_types=1);

require_once __DIR__ . '/mail/MyVendorApiTransport.php';

return [
    'mailTransports' => [
        'api' => new MyVendorApiTransport($sdk),
    ],
];

The local key api is not the final runtime id. The loader combines it with the extension vendor and registers:

x.my_vendor.api

AI Path

An AI coding agent can author the local PHP class, update bootstrap.php, and run repository verification. That is local authoring.

An installed MCP client cannot create the PHP files, enable the extension, grant scopes, or choose secrets for the provider. After the extension is loaded, an AI client with administration.read can inspect registered transport descriptors through:

GET /api/v1/administration/mail/transports

The same action is exposed to MCP, so a scoped MCP caller can discover it through tools/list and call the listed tool. The action returns a transports object keyed by registered transport id.

Scopes

Transport authoring is local and has no runtime caller scope by itself. Runtime inspection is gated:

SurfaceScope
/api/v1/administration/mail/transportsadministration.read
MCP tool listing for the same actionadministration.read
Extension setup actions, if the provider adds themWhatever those actions declare

Descriptor scopes should describe the setup or inspection scopes a provider needs. They do not grant the extension or the caller any permission.

Artifacts

Local artifacts:

  • app/extensions/{vendor}/mail/{TransportName}.php
  • app/extensions/{vendor}/bootstrap.php
  • app/extensions/{vendor}/manifest.json

Runtime artifacts:

  • MailTransportInterface
  • MailTransportRegistry
  • registered id x.{vendor}.{slug}
  • GET /api/v1/administration/mail/transports
  • MCP tools/list and tools/call for the listing action when scoped

Safety Boundary

Do not hard-code provider API keys, SMTP passwords, signing secrets, bearer tokens, or webhook secrets in extension files. Extension files are source artifacts. Secrets belong in governed settings or another approved secret storage path introduced by a separate contract.

Do not register transports outside the extension namespace. Core transports use snake_case ids such as smtp; extension transports must use x.{vendor}.{slug} through the registry.

Do not treat descriptor visibility as send permission. A caller that can list descriptors has not gained the ability to configure a provider or bypass the mail settings workflow.

Verification

Run the foundation checks:

php app/tests/verify-extension-system-foundation.php
php app/tests/verify-forms-mail-substrate.php

For a loaded extension, verify:

  • the extension is registered, enabled, and granted its manifest scopes,
  • the bootstrap payload includes mailTransports,
  • knownTransports() includes x.{vendor}.{slug},
  • GET /api/v1/administration/mail/transports returns the descriptor for an administration.read caller,
  • MCP tools/list exposes the listing action only to a caller with administration.read.

Failure Modes

FailureLikely causeSafe response
Transport is absent from descriptorsExtension is not registered, disabled, under-granted, or did not return the transport from mailTransports.Fix extension load state and bootstrap shape.
Load error mentions snake_caseVendor or local transport slug failed registry validation.Use lowercase snake_case for vendor and slug.
Descriptor appears but setup does not workDescriptor metadata exists, but provider setup action or settings path is incomplete.Verify the provider setup workflow and required scopes.
MCP caller cannot see the listing actionCaller lacks administration.read, or the action is not exposed in the running install.Use a correctly scoped caller; do not broaden scopes automatically.
Provider secret appears in sourceSecret was hard-coded into extension files.Move the secret to an approved settings or secret storage path before use.