Skip to main content
Glama

Agentic Actions demo

A team task board that shows Agentic Actions for Laravel in a real app. Each operation on the board is one Action class. The same class answers the board's forms, a JSON route, php artisan actions:run, a copilot's tool calls and MCP clients such as Claude. Teams are the tenants: every action runs inside the team in the URL, and each role gets a different answer.

Try it at https://demo.agentic-actions.com. "Try the demo" gives you a private sandbox for 24 hours, with no sign-up. The package's documentation is at https://agentic-actions.com.

It is built on the Laravel React starter kit with teams: Laravel 13, Inertia 3, React 19, PHP 8.4 and SQLite. DEMO.md has the hand-test scripts for every feature, and CLAUDE.md is the guide for contributors and coding agents.

What to try

  1. Open the board. The cards and the counts come from the list-tasks and summarize-board actions, run in-process.

  2. Change a card's status, or add a task. The board's forms call the same actions through their JSON routes, and Precognition checks the fields against the action's schema() as you type.

  3. Click Ask the assistant and try the suggestion chips. "Add three launch tasks for Marcus" runs real actions, and the cards appear on the board. "Delete 'Order new office chairs'" asks you to confirm on a card first. "Add a task for Marcus to review the pricing page" opens a form for the due date and the priority. "Tasks by status" answers with a table and a chart.

  4. Switch to Vera, a viewer. The same buttons are there, and the server refuses her writes. Gus is not in Acme, so the Acme board answers 403 for him.

  5. Open the team's AI clients page. Make a token for Claude Code or Cursor, or copy the connector URL for a Claude.ai custom connector. Your own model then works on the same board over MCP, with your role.

Related MCP server: boardagent-mcp

Run it locally

You need PHP 8.4 with pdo_sqlite, Composer, and Node 22.18 or later.

git clone https://github.com/agentic-actions/demo.git agentic-actions-demo
cd agentic-actions-demo
composer install
npm install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate --seed
php artisan passport:keys
npm run build

Laravel Herd serves the folder at http://agentic-actions-demo.test (run herd link in it if the folder is outside a parked path). Without Herd, set APP_URL=http://127.0.0.1:8000 in .env and run php artisan serve.

passport:keys writes the OAuth key pair to storage/, which git ignores. Without it, the MCP URLs answer 500 instead of 401. The npm client @agentic-actions/client installs from the Composer package (file:vendor/agentic-actions/laravel/js), so run composer install before npm install.

Seeded users

Every password is password. The seeded users are for local use only; the hosted demo makes a sandbox for each visitor instead.

Email

Name

Acme

Globex

owner@example.com

Olivia Park

Owner

member@example.com

Marcus Reed

Member

Member

viewer@example.com

Vera Lind

Viewer

globex@example.com

Gus Novak

Owner

php artisan migrate:fresh --seed puts the boards back to the seeded state.

What each role may do on a board:

Owner

Admin

Member

Viewer

Read, search, summarize

yes

yes

yes

yes

Create and update tasks

yes

yes

yes

no

Delete tasks

yes

yes

no

no

Create projects

yes

yes

no

no

The copilot

With OPENROUTER_API_KEY empty, the copilot runs in scripted mode. app/Ai/ScriptedPlanner.php plays the model on laravel/ai's fake gateway, and the actions, permissions, cards and forms it drives are real. Set the key (and OPENROUTER_MODEL, anthropic/claude-sonnet-5 by default) to use a real model. ASSISTANT_SCRIPTED=true keeps scripted mode with a key set.

Claude.ai and other OAuth clients

A Claude.ai custom connector or ChatGPT signs in with OAuth and needs a public HTTPS address. php artisan app:tunnel opens a short-lived Cloudflare quick tunnel to a local copy. It refuses to start while any account still has the password password. See DEMO.md.

The CLI

--as takes a user id and --tenant a team slug. The seeded ids are 1 owner, 2 member, 3 viewer and 4 globex.

php artisan actions:list
php artisan actions:run summarize-board --as=1 --tenant=acme
php artisan actions:run create-task title="Call the printer" assignee="Marcus Reed" priority=high --as=1 --tenant=acme
php artisan actions:run create-task title="Nope" --as=3 --tenant=acme       # viewer: exit 3, denied
php artisan actions:check

Tests

php artisan test --compact
npm test

How hosted mode works

DEMO_HOSTED=true turns the app into the public demo. It is off by default, and a local copy behaves as described above.

A sandbox per visitor. "Try the demo" (POST /demo) creates a sandbox: you as the Acme owner, plus Marcus, Vera and Gus, the Acme and Globex boards, and a personal team for each person. Every person in it has a random password nobody knows. You are signed in straight away. The bar at the top shows the time left and has a "View as" menu that signs you in as one of the other three. The same menu copies a signed resume link, also shown on the AI clients page, that signs you back in from another browser, which is how you approve a Claude.ai connector on another device. Your session lasts as long as the sandbox. Team slugs get a random suffix, such as acme-k3x9qp, and so do the teams you add or rename.

A scripted copilot. On the host the copilot always follows the script, whatever ASSISTANT_SCRIPTED or any key says, and the app refuses every outbound HTTP request. To use a real model on the board, connect your own MCP client.

Kit features off. Sign-up, password reset, email verification, two-factor and passkeys answer 404. Email changes, password changes, account deletion and invitations answer 403, and their controls are hidden. Nothing sends email. The app answers only to the host in APP_URL.

Limits. Each surface has its own named rate limit (AppServiceProvider::configureRateLimits):

Surface

Limit

Try the demo

5 an hour per address, 120 an hour in all, at most 500 live sandboxes

Resume link and View as

30 a minute per address

Assistant turns

10 a minute and 300 a day per person, 30 a minute per address, one reply at a time

Board actions

60 calls and 30 change-feed polls a minute per person

Team, member, token and connection changes

20 a minute per person; 5 new tokens a minute

MCP

30 calls a minute per person (60 on a local copy)

OAuth

registration 10 a minute and 30 a day per address, 500 a day in all; Anthropic's range 120 a minute and 500 a day of its own; consent 20 a minute

A sandbox team holds at most 200 tasks and 20 projects, a visitor can add 5 teams, and each person can hold 5 live tokens per team. A token expires with its sandbox.

Resets. When a sandbox expires, its people are signed out and its MCP tokens stop working. The scheduler runs demo:prune-sandboxes every 15 minutes. It deletes each sandbox that expired more than five minutes ago, with everything in it: people, teams, boards, conversations, sessions, Sanctum tokens, OAuth tokens and MCP connections. demo:prune-clients deletes, once a day, OAuth clients that registered and never connected. passport:purge and sanctum:prune-expired run daily too, and with the database cache store, a daily task deletes its expired rows.

Hosting it

These are the steps for a Laravel Forge server with PHP 8.4, nginx, Node 22.18 or later, and optionally Redis. The server's default command-line PHP must be 8.4 too (in Forge, the server's PHP tab): npm run build runs php artisan through whatever php comes first on the path, not $FORGE_PHP.

Site and DNS

  • Create the site for demo.agentic-actions.com with zero-downtime deployments, and add a Let's Encrypt certificate.

  • Point an A record at the server, DNS only. With Cloudflare's proxy off, the app sees the real client address for its rate limits, and Anthropic's addresses (160.79.104.0/21) reach the OAuth routes without Cloudflare's firewall in the way. If you ever turn the proxy on, add $middleware->trustProxies(at: [...]) with Cloudflare's ranges in bootstrap/app.php.

Environment

Set these in the site's .env. Never set OPENROUTER_API_KEY or any other model key on the host: not in .env, the PHP-FPM pool or the system environment.

Variable

Value

APP_NAME

"Agentic Actions Demo"

APP_ENV

production

APP_KEY

Made once, before the first deploy, and never changed (below)

APP_DEBUG

false

APP_URL

https://demo.agentic-actions.com

APP_PUBLIC_URL

https://demo.agentic-actions.com (shows the connector URL)

VITE_APP_NAME

"${APP_NAME}". npm run build reads it for the page titles

DEMO_HOSTED

true

ASSISTANT_SCRIPTED

true

ASSISTANT_DEMO_PACE_MS

150

LOG_CHANNEL

daily

LOG_LEVEL

warning

DB_CONNECTION

sqlite

DB_DATABASE

The absolute path of the SQLite file, outside the release directory

DB_BUSY_TIMEOUT

5000

DB_JOURNAL_MODE

wal

DB_SYNCHRONOUS

normal

DB_TRANSACTION_MODE

IMMEDIATE

CACHE_STORE

redis, or database without Redis

SESSION_DRIVER

redis, or database without Redis

SESSION_LIFETIME

1440, the sandbox's 24 hours. Hosted mode never allows less

SESSION_SECURE_COOKIE

true

REDIS_HOST

127.0.0.1, when Redis is used

QUEUE_CONNECTION

sync. Nothing is queued, so no worker is needed

MAIL_MAILER

array. Nothing sends mail

INERTIA_SSR_ENABLED

false

INERTIA_DEVTOOLS_ENABLED

false

Make the app key before the first deploy, since php artisan key:generate needs vendor/, which a new site does not have yet:

php -r "echo 'base64:'.base64_encode(random_bytes(32)), PHP_EOL;"

The deploy caches the config, so a later change to .env takes effect only after php artisan optimize in the current release, or the next deploy.

Redis takes the rate-limit counters, the board's polling and the sessions off SQLite, but either store works. With the database store, the cache and sessions tables come from the migrations.

Shared files

A zero-downtime release is a new directory each time, so these live outside it:

  • .env, which Forge shares between releases.

  • storage/, which Forge also shares. Passport's key pair lives there. Make it once, from the current release, after the first deploy, and never again: new keys end every connector's tokens.

    php artisan passport:keys
    chmod 600 storage/oauth-private.key storage/oauth-public.key
  • The SQLite file. Create it once, for example touch /home/forge/demo.agentic-actions.com/database.sqlite, and set DB_DATABASE to that path. Never copy a local database to the host.

Deploy script

$CREATE_RELEASE()

cd $FORGE_RELEASE_DIRECTORY

$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader
npm ci
npm run build
$FORGE_PHP artisan migrate --force
$FORGE_PHP artisan optimize

$ACTIVATE_RELEASE()

Composer comes first: the npm client installs from vendor/, and the build runs php artisan to write the route helpers, so vendor/ and .env must exist before npm run build. The build also fetches fonts over HTTPS. .npmrc turns off npm's install scripts.

Scheduler and workers

  • Turn on Forge's scheduler (php artisan schedule:run every minute). It runs the sandbox clean-up and the daily prunes. withoutOverlapping needs a shared cache, which both the redis and the database store give.

  • No queue worker and no SSR process are needed.

PHP-FPM

pm = dynamic, pm.max_children around 20 on a 2 GB server, and request_terminate_timeout = 60. PHP 8.4 needs pdo_sqlite, and the redis extension when Redis is used.

nginx

In the site's server block:

client_max_body_size 64k;

location ~ \.php$ {
    # Forge's existing fastcgi lines stay.
    fastcgi_read_timeout 60s;
}
  • The copilot streams its replies as text/event-stream. Keep that type out of gzip_types, and never add fastcgi_ignore_headers X-Accel-Buffering: the stream sends X-Accel-Buffering: no so nginx passes each part on at once.

  • Keep server_name to demo.agentic-actions.com. The app also answers 400 to any host other than APP_URL's.

An optional per-address limit at the edge also covers requests that reach PHP before the app's own limits, such as MCP calls without a token. Put this in the http context (on Forge, a file in /etc/nginx/conf.d/); it leaves Anthropic's range and the health check alone:

geo $from_claude {
    default 0;
    160.79.104.0/21 1;
}

map "$from_claude:$uri" $demo_limit_key {
    "~^1:" "";
    "0:/up" "";
    default $binary_remote_addr;
}

limit_req_zone $demo_limit_key zone=demo_per_ip:10m rate=10r/s;

and this in the server block:

limit_req zone=demo_per_ip burst=40 nodelay;
limit_req_status 429;

After each deploy

curl -si -X POST https://demo.agentic-actions.com/mcp/t/x -H 'Accept: application/json, text/event-stream' | grep -i -E '^HTTP|resource_metadata'   # 401, with resource_metadata
curl -so /dev/null -w '%{http_code}\n' https://demo.agentic-actions.com/_inertia/devtools/entries     # 404
curl -so /dev/null -w '%{http_code}\n' https://demo.agentic-actions.com/register                      # 404
php artisan tinker --execute 'echo App\Ai\ScriptedPlanner::active() ? "scripted" : "LIVE";'             # scripted

Then click "Try the demo" and run one copilot chip. Do not print the config on the host (config:show), since it includes the app key.

License

MIT. See LICENSE.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to manage Kanban boards, cards, sessions, and automations through a unified MCP interface with real-time updates, supporting delegation, agent sessions, and human-in-the-loop interactions.
    AGPL 3.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to manage Kanban projects and tasks over MCP, including creating and listing projects/tasks, setting prerequisites, moving/archiving/restoring/deleting/reordering tasks, tracking history, and accessing attachments. Actions affecting multiple tasks return confirmation requests before applying changes.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI coding agents to read TeamStorm tasks, comments, attachments, and links, and to perform limited write operations such as adding comments and updating task names, descriptions, or statuses through MCP.
    13
    2
    MIT