PHP code example of gtapps / laravel-agentic

1. Go to this page and download the library: Download gtapps/laravel-agentic library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

gtapps / laravel-agentic example snippets


use Spatie\LaravelData\Data;

class RefundInvoiceInput extends Data
{
    public function __construct(
        public int $invoiceId,
        public float $amount,
        public string $reason = 'requested_by_customer',
    ) {}
}

use Gtapps\LaravelAgentic\Attributes\AgentAction;
use Gtapps\LaravelAgentic\Contracts\ActionContext;
use Gtapps\LaravelAgentic\Enums\Surface;

#[AgentAction(
    name: 'refund-invoice',
    description: 'Refund an invoice to the original payment method.', // written for the MODEL
    needsApproval: true,
    surfaces: [Surface::Mcp, Surface::AiTool, Surface::Http, Surface::Cli, Surface::Job], // define the ones you want, omit for all
)]
class RefundInvoice
{
    // Shared by default; define authorizeMcp(), authorizeHttp(), etc. to replace it for one surface.
    public function authorize(ActionContext $ctx, RefundInvoiceInput $input): bool
    {
        return $ctx->user()->can('refund', Invoice::find($input->invoiceId));
    }

    public function handle(RefundInvoiceInput $input, ActionContext $ctx): RefundResult
    {
        // business logic; $ctx->caller() ∈ {mcp, ai-tool, http, cli, job}
    }
}

use Gtapps\LaravelAgentic\Surfaces\Mcp\AgenticServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp', AgenticServer::class)->middleware(['auth:sanctum']);

#[AgentAction(name: 'refund-invoice', surfaces: [Surface::Mcp, Surface::Cli])]

#[AgentAction(..., needsApproval: BigRefundsNeedApproval::class)]
// class BigRefundsNeedApproval {
//     public function __invoke(RefundInvoiceInput $input): bool {
//         return $input->amount > 100;
//     }
// }

$response = $agent->forUser($user)->prompt('Refund invoice 42');

if ($response->hasPendingApprovals()) {
    // Returns null until every paused call has an answer, so poll (or
    // re-enter on your approval event).
    $decisions = Agentic::approvalDecisions($response->pendingApprovals, $user);

    if ($decisions !== null) {
        $response = $agent->continue($response->conversationId, $user)->prompt($decisions);
    }
}

// routes/web.php, POST only. Never grant over GET: chat-app link
// preview prefetchers will auto-approve your links.
Route::post('/approvals/{id}', function (string $id, Request $request) {
    $granted = app(ApprovalBroker::class)->decide(
        $id,                        // $approval->id from ApprovalRequested
        $request->input('token'),   // timing-safe verified
        approve: true,
        decidedBy: $request->user()->email,
    );

    return $granted ? response()->noContent() : abort(410);
})->middleware(['auth', 'can:approve-agentic']);

#[AgentAction(name: 'refund-invoice', surfaces: [Surface::Mcp, Surface::Cli])]
class RefundInvoice
{
    public function authorize(ActionContext $ctx, RefundInvoiceInput $input): bool
    {
        return Gate::forUser($ctx->user())->allows('refund', $input->invoiceId);
    }

    public function authorizeCli(): bool
    {
        return true;   // local-trust surface: the process boundary is the auth line
    }
}

public function authorizeMcp(RefundInvoiceInput $input): Response
{
    return $input->amount <= 10_000
        ? Response::allow()
        : Response::deny('Refunds over $10,000 must be run by a human via CLI.');
}

class ListInvoicesInput extends Data
{
    public function __construct(
        #[Present]
        /** @var int[] */
        public array $ids = [],
    ) {}
}

class ListInvoicesInput extends PaginatedInput
{
    // add your own filters via a constructor; page/perPage still apply
}

public function handle(ListInvoicesInput $input): mixed
{
    return InvoiceSummary::collect(
        Invoice::query()->paginate($input->perPage, page: $input->page)
    );
}

class StoreInvoiceInput extends Data
{
    public function __construct(public int $invoiceId, public float $amount) {}

    /** Reuse the REST rules as the single source of truth during migration. */
    public static function rules(): array
    {
        return (new StoreInvoiceRequest)->rules();
    }
}

public function perPage(int $default = 100, int $max = 250): int
{
    return max(1, min($max, $this->integer('per_page', $default)));
}

public function handle(ListInvoicesInput $input): mixed
{
    $perPage = max(1, min(250, $input->perPage));
    // ...
}

public static function rules(): array
{
    return [
        'invoiceId' => ['

use Gtapps\LaravelAgentic\Facades\Agentic;

it('refunds after checkout', function () {
    $fake = Agentic::fake();

    app(CheckoutFlow::class)->cancelAndRefund($order);

    $fake->assertRan('refund-invoice', fn (array $args) => $args['invoiceId'] === $order->invoice_id);
    $fake->assertAudited('refund-invoice');
});

it('never runs actions on validation failure', function () {
    $fake = Agentic::fake();

    // ...

    $fake->assertNothingRan();
});

it('knocks for large refunds', function () {
    $fake = Agentic::fake()->

return [
    'discovery' => ['paths' => [app_path('Actions')]], // scanned for #[AgentAction]
    'http' => [
        'enabled' => env('AGENTIC_HTTP_ENABLED', false), // opt-in; set true and add your guard first
        'prefix' => 'agentic',            // POST /agentic/actions/{name}
        'middleware' => ['api'],          // add your guard, e.g. 'auth:sanctum'
    ],
    'mcp' => [
        'tiers' => ['unauthenticated' => []], // allowlist for anonymous callers
        'exclude' => [],                      // hard denylist, beats everything
    ],
    'approvals' => [
        'ttl' => env('AGENTIC_APPROVALS_TTL', 600),      // seconds until knock/grant expires to deny
        'connection' => env('AGENTIC_APPROVALS_CONNECTION'), // null = default connection
    ],
    'redact' => [],                       // dot-path globs, e.g. '*.password'
    'audit' => [
        'enabled' => env('AGENTIC_AUDIT_ENABLED', true),
        'connection' => env('AGENTIC_AUDIT_CONNECTION'), // null = default connection
    ],
];
bash
php artisan vendor:publish --tag=agentic-config
php artisan vendor:publish --tag=agentic-agents-md   # AGENTS.md for your repo
bash
php artisan agentic:make-action RefundInvoice
php artisan agentic:make-action Invoices/ListInvoices --paginated