MyronCMS Developers Hub Docs sandbox review

AI-Native Development

MCP Tool Exposure

MyronCMS exposes many MyronAction instances as MCP tools. The MCP server is a protocol adapter over the same action registry and dispatcher used by the direct /api/v1/... API.

The core rule is simple: scopes are the gate. exposeToMcp:false is a narrow policy carve-out, not the default for writes.

Human Path

When authoring an action, decide whether the action should be visible to MCP:

return new MyronActionMetadata(
    uri: '/api/v1/x/my_vendor/status',
    method: 'GET',
    domain: 'x.my_vendor.status',
    resource: 'status',
    verb: 'get',
    inputSchema: ['type' => 'object', 'properties' => []],
    outputSchema: ['type' => 'object', 'properties' => ['ready' => ['type' => 'boolean']]],
    requiredScope: MyronApiScopes::CONTENT_READ,
    summary: 'Return this extension status.',
    exposeToMcp: true,
);

Use exposeToMcp:true when an AI integration that holds the required scope should be able to call the action. Use exposeToMcp:false only when the action fits a documented hidden category.

AI Path

An installed MCP client discovers available tools only after the MCP discovery boundary is complete:

  1. send initialize with Authorization: Bearer <token>,
  2. read the Mcp-Session-Id response header,
  3. send initialized / notifications/initialized with the same bearer token and session header,
  4. call tools/list.

The server then:

  1. resolves the caller from the request headers,
  2. validates the MCP session state,
  3. iterates over registered actions,
  4. skips actions with exposeToMcp:false,
  5. skips policy-hidden identity/trust-gate actions,
  6. skips scoped actions the caller cannot invoke,
  7. emits each remaining tool with name, description, and inputSchema.

An MCP client should treat absence from tools/list as a discovery result, not as proof the underlying API action does not exist. Check scope and exposure before deciding the action is unavailable.

Tool Names

The default tool name is built from action metadata:

{domain}_{resource}_{verb}

The MCP server normalizes non-alphanumeric separators to underscores, collapses repeated underscores, lowercases, and trims edges. For example:

MetadataTool name
domain x.my_vendor.status, resource status, verb getx_my_vendor_status_status_get
domain design, resource definition, verb applydesign_definition_apply

Legacy dotted names may still be accepted by tools/call as transitional aliases, but tools/list should expose portable snake_case names.

tools/call

tools/call looks up the action by tool name, substitutes URI placeholders from arguments, then delegates to ActionDispatcher::dispatch() with the action method, concrete URI, headers, and arguments.

This means direct API and MCP calls share:

  • authentication,
  • scope checks,
  • input schema validation,
  • action execution,
  • audit binding,
  • rate limiting for AI bearer callers,
  • canonical action errors.

Hidden Tool Classes

Use exposeToMcp:false with a reason. Current documentation should classify hidden status with one of these categories:

CategoryTreatment
Recursive control vectorNever document as MCP-callable. AI cannot mint, rotate, revoke, or re-scope its own trust gate.
Tier-3 identity/admin surfaceDocument as policy-hidden unless a later privileged-admin primitive changes the rule. Current server policy also hides registered administration user actions even when their metadata is otherwise exposed.
Visitor-facing actionDocument runtime behavior without presenting it as operator MCP control.
Internal dispatchDocument only as internals, not public SDK surface.
Unresolved over-hideMark as parity gap candidate and route through Evaluate before changing.

Do not use exposeToMcp:false because an action mutates state. Mutations can be safe MCP tools when the operator has granted the needed scope and the action declares side effects and preconditions.

API Manifest Versus MCP Tools

The direct manifest surface can show the complete registered action catalog, including actions hidden from MCP. MCP tools/list is filtered to tools an MCP caller can discover and normally invoke.

Use the direct manifest for catalog and audit visibility. Use MCP tools/list and the MCP mcp://myroncms/system/manifest resource for the AI client's current callable tool set. The MCP manifest resource is scope-filtered and policy-filtered.

Scopes

If an action has requiredScope: "content.write", a caller without content.write should not see that tool in tools/list. If the caller attempts tools/call anyway, dispatcher scope enforcement still protects the action.

Artifacts

Discovery and call surfaces:

  • MCP initialize
  • MCP tools/list
  • MCP tools/call
  • direct /api/v1/manifest

Source contracts:

  • app/system/mcp/Server.php
  • app/system/api/Action.php
  • app/system/api/ActionDispatcher.php

Safety Boundary

An MCP client must not grant itself access. Trust gates are operator-controlled through scopes, integration tokens, and extension grants. Docs must not tell an AI to solve authorization failures by creating, rotating, revoking, or expanding its own integration.

Verification

Use two callers:

  1. A caller that holds the action scope.
  2. A caller that does not hold the action scope.

For an exposed action:

  • scoped caller sees the tool in tools/list,
  • under-scoped caller does not see it,
  • scoped caller can tools/call,
  • under-scoped direct API call returns scope_insufficient.

For a hidden action:

  • action may appear in the full manifest,
  • action does not appear in MCP tools/list,
  • docs state the hidden reason.

Failure Modes

SymptomLikely causeSafe response
Tool missing from tools/listNot loaded, exposeToMcp:false, or caller lacks scope.Check extension load state, metadata, and caller scope.
JSON-RPC session errorClient skipped initialize, did not preserve Mcp-Session-Id, or skipped initialized.Restart the MCP lifecycle with the same bearer token and session header.
tools/call method not foundTool name does not match a registered exposed or legacy alias.Re-read tools/list and use the listed name.
JSON-RPC auth errorMissing or invalid bearer token.Use an operator-provisioned token.
JSON-RPC authz errorCaller lacks the action scope.Use an appropriately scoped caller.
JSON-RPC rate-limit errorAI integration exceeded its request budget.Back off and retry after the reported window.
isError:true tool resultDispatcher reached the action and it returned a normal action failure.Read the text and resolve input or precondition issues.