Set Up the PagibleAI MCP Server

Set up the remote PagibleAI MCP endpoint for production use. The process covers a login UI, Passport OAuth 2.1, MCP route registration, signing keys and a first authenticated call.

The endpoint used throughout this page is https://yourdomain.com/mcp/cms. Replace the host with the application's public HTTPS domain.

Requirements

The server needs:

  • PagibleAI CMS with the pagible-mcp package
  • HTTPS for the public MCP endpoint
  • Shell and Composer access
  • A login page for OAuth approval
  • A User model with tenant_id and cmsperms
  • A user with only the permissions needed through MCP

The full aimeos/pagible package includes pagible-mcp and pagible-ai. With separate packages, AI tools appear only after pagible-ai is installed and configured.

Installation

Choose the authentication method

Use OAuth 2.1 with Laravel Passport for external clients. It supports browser approval and dynamic client registration. The steps below use this method.

For controlled internal clients, Laravel MCP also supports Sanctum bearer tokens and custom middleware. Not every hosted MCP client accepts those methods. Compare the options in the Laravel MCP authentication guide.

Provide a login page

Use the application's existing authentication UI. If it has none, Laravel Breeze is an optional starting point:

composer require laravel/breeze
php artisan breeze:install blade --dark
npm install
npm run build

Commit the generated source and production assets according to your deployment workflow. If administrators create every CMS account, disable public registration.

Install Laravel Passport

Install Passport's API scaffolding. Passport issues and validates the access tokens used by the MCP endpoint:

php artisan install:api --passport

Update the User model

Implement Passport's OAuthenticatable contract and use its HasApiTokens trait in app/Models/User.php. Keep the traits and interfaces already required by your application:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\Contracts\OAuthenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable implements OAuthenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Add the Passport API guard

Add the api guard to the guards array in config/auth.php. Preserve any other guards your application already defines:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Generate and protect Passport keys

Generate Passport's signing keys once for the deployment environment:

php artisan passport:keys

Keep the private key secret and both keys persistent across deployments. Never commit them. If your runtime cannot retain the files, provide their contents through PASSPORT_PRIVATE_KEY and PASSPORT_PUBLIC_KEY.

For Kubernetes, create consistently named secrets:

kubectl create secret generic pagible-oauth-private \
  --from-file=oauth-private=storage/oauth-private.key \
  -n <namespace>

kubectl create secret generic pagible-oauth-public \
  --from-file=oauth-public=storage/oauth-public.key \
  -n <namespace>

Reference the same secret names in the deployment:

- name: PASSPORT_PRIVATE_KEY
  valueFrom:
    secretKeyRef:
      name: pagible-oauth-private
      key: oauth-private
- name: PASSPORT_PUBLIC_KEY
  valueFrom:
    secretKeyRef:
      name: pagible-oauth-public
      key: oauth-public

Install and register the MCP routes

Use PagibleAI's installer to publish routes/ai.php and apply the dedicated MCP rate-limiter name:

php artisan cms:install:mcp

Then register OAuth discovery and the CMS server in routes/ai.php:

use Aimeos\Cms\Mcp\CmsServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

Mcp::web('/mcp/cms', CmsServer::class)
    ->middleware(['auth:api', 'throttle:cms-mcp']);

The cms-mcp limiter allows 120 requests per minute for each user, or each IP before authentication. Keep the endpoint behind HTTPS.

Add the OAuth consent screen

Publish Laravel MCP's Passport authorization view:

php artisan vendor:publish --tag=mcp-views

Tell Passport to use the view in app/Providers/AppServiceProvider.php:

use Laravel\Passport\Passport;

public function boot(): void
{
    Passport::authorizationView(function ($parameters) {
        return view('mcp.authorize', $parameters);
    });
}

Verify the server

Clear cached configuration after changing guards, keys or routes. Then list the CMS endpoint:

php artisan optimize:clear
php artisan route:list --path=mcp/cms

Laravel versions show different action and middleware columns. Confirm that these methods and paths are present:

GET|HEAD  mcp/cms
POST      mcp/cms
DELETE    mcp/cms

Only POST carries MCP messages. GET and DELETE should return 405 Method Not Allowed with Allow: POST.

Check the protected-resource metadata advertised for this endpoint:

curl -s https://yourdomain.com/.well-known/oauth-protected-resource/mcp/cms

With the default settings, expect your MCP resource URL, your application as the authorization server and the mcp:use scope:

{
  "resource": "https://yourdomain.com/mcp/cms",
  "authorization_servers": ["https://yourdomain.com"],
  "scopes_supported": ["mcp:use"]
}

If you set mcp.authorization_server, the value in authorization_servers will be that configured issuer instead.

Make the first authenticated call

Connect one client and complete OAuth in the browser. Then call the read-only get-locales tool. After initialization, the client sends the equivalent of:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get-locales",
    "arguments": {}
  }
}

For an installation configured with English and German, the JSON-RPC result is:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"locales\":[\"en\",\"de\"]}"
      }
    ],
    "isError": false,
    "structuredContent": {
      "locales": ["en", "de"]
    }
  }
}

Your codes will reflect cms.locales. Some clients display only structuredContent. Success is indicated by isError: false and a locales array.

If the tool is missing, confirm that the user belongs to the active tenant and has a non-empty cmsperms value. Resolve that before testing write tools.

Security checklist

  • Expose the endpoint only through HTTPS.
  • Give MCP users the smallest practical cmsperms role instead of admin.
  • Use separate user accounts so the audit history identifies who authorized each client.
  • Keep Passport keys outside source control and persistent across deployments.
  • Disable public registration when accounts are provisioned by an administrator.
  • Review tool scans when the server changes; newly added write tools should not be enabled without review.
  • Treat content returned by websites and uploaded files as untrusted input when an assistant can call write tools.
  • Keep backups even though PagibleAI stores versions and soft-deleted records.

Frequently asked questions

Why does the MCP endpoint return 401?

Check that the api guard uses Passport and that the User model implements OAuthenticatable. Every application instance must have the same Passport signing keys. Run php artisan optimize:clear after changing the guard, keys or environment.

Why does OAuth approval fail?

First test the application's normal web login. Then check that the MCP authorization view is published and Mcp::oauthRoutes() is registered. After changing keys or routes, clear the client's saved authorization and connect again.

Why does the endpoint return 429?

The cms-mcp limiter allows 120 requests per minute per authenticated user. Reduce parallel calls or retry after the rate-limit window resets.

Next steps