Skip to content

One action, every caller ​

Write an operation once, as an Action class, and the same class answers a route, Artisan, the queue, TypeScript, an agent and MCP clients.

  • Web routePOST /actions/create-post
  • TypeScriptcreatePost()
  • Artisanactions:run create-post
  • The queueCreatePost::dispatch()
action
CreatePostEffect::Write
one pipeline, whoever calls
  1. exposure
  2. token abilities
  3. tenant membership
  4. authorize()
  5. validation
  6. handle()
  7. output allowlist
  • A laravel/ai agenta tool it can call
  • MCP clientsthe create-post tool
Every caller goes through the same pipeline before handle() runs.

The action ​

An action is one class that extends AgenticActions\Action. Its properties declare facts: what it does, its effect, what a success makes stale. Its methods declare behaviour: the input, the output, who may run it, and the work.

app/Actions/CreatePost.php
  • #[Expose]opens the route, agents and MCP
  • final class CreatePost extends Action
  • facts
  • $descriptionwhat a model reads before it calls
  • $effect = Effect::WriteRead, Write, Destructive or External
  • $touches = ['posts']what a success makes stale
  • behaviour
  • schema()the input: rules, tool schema, TypeScript types
  • outputSchema()the only keys that leave the server
  • authorize()who may run it; without it, nobody
  • handle()does the work
CreatePost, the example from Getting started, one member at a time.

Without authorize() the action is denied everywhere, and a key outputSchema() does not declare never leaves the server. See The action in Concepts for every member.

What each caller gets ​

Each caller comes in its own way, then takes the same steps, so a rule written once in authorize() or schema() holds for a form, a worker and a model alike. What each step answers when it says no is on the pipeline, and the full list in What that one class gets.

  • Web route
    POST /actions/create-post
    JSON gets the output; a form gets a redirect.Actions::routes()
  • TypeScript
    createPost()
    The route, typed from schema() and outputSchema().actions:typescript
  • Artisan
    actions:run create-post
    Runs as the user --as names.no #[Expose] needed
  • The queue
    CreatePost::dispatch()
    The worker runs the whole pipeline, as the caller.no #[Expose] needed
  • laravel/ai agent
    create-post
    The model reads $description and the schema.composer require laravel/ai
  • MCP clients
    create-post
    Listed for a token that names actions:write.a token guard
The same class, as each caller sees it, and what the app adds for each one.

What #[Expose] opens ​

#[Expose] is the only way onto the route, agents and MCP. Artisan, the queue and your own run() reach an action without it. See Effects and surfaces.

Released under the MIT License.