Loading...

This is taking longer than expected.

The MCP server

Let people connect their own AI assistant with a token, under their own permissions, and add your product's tools to it.

Weblabor Base ships two MCP servers, where a person connects their own AI assistant — Claude, ChatGPT or any client that speaks MCP over HTTP — with a token:

  • The user's server, at /mcp, with the tools of the user area: tickets, the person's own files and referrals. Its tokens are created in My profile.
  • The admin server, at /mcp/admin, with the tools of the admin panel: users and roles, blocking, announcements, the activity log and the product's figures. Only superadministrators reach it, and its tokens are created in the admin panel's MCP screen.

A token opens only the server it was created for. Everything the assistant does through it is done as the person who created the token, with their permissions and in their language. This guide covers turning each server on, what the base tools do, the limits, and how to add your product's own tools.

If an assistant was connected to /mcp before the admin server existed, the administration tools are no longer there: they moved to /mcp/admin, with an admin token. The tokens created before keep working on /mcp.

Turn the user's server on for an account

MCP is an add-on called MCP, with no price. The seeders create it on the first deploy (AddOnSeeder), and every account starts with it off, as every add-on does:

  • Without paid add-ons (FEATURE_ADDONS_ENABLED off, how Weblabor Base ships), it shows in Add-ons with its Activate switch.
  • With paid add-ons on, it is a free add-on of the catalogue that the account activates.

Switching it off stops every token of that account at once, without deleting them, and switching it on again brings them back.

The tokens screen also asks for a permission, Manage Mcp Tokens (manage mcp tokens). The admin role has it; give it to any other role from Roles in the admin panel. A person with the add-on on and the permission sees AI assistant (MCP) in My profile, where they:

  1. Copy the MCP server URL into their assistant.
  2. Write a Token name, such as the assistant it is for, and click Create token.
  3. Copy the token from the yellow box. It is shown once; after Done only its name stays.
  4. Click Revoke next to a token to stop it right away. The others keep working.

The assistant sends the token as Authorization: Bearer <token> on every request.

A product can leave the user's server off for everybody with app_server (see Change the configuration): the address stays, and answers that the server is off.

Turn the admin server on

The admin server is for superadministrators — the emails in config('app.sudo') — and has no add-on and no price. A superadministrator opens MCP in the admin panel's menu, which nobody else sees, where they:

  1. Check Admin MCP server on. It starts on: without a token nobody gets in.
  2. Copy the MCP server URL, the /mcp/admin address, into their assistant.
  3. Create, copy and revoke their admin tokens exactly as in the profile.

Switched off, every admin token is refused with This MCP server is switched off.; the menu entry and the tokens stay, and switching it on again brings them back. A person who stops being a superadministrator, or is blocked, takes their admin tokens with them.

What the assistant can do

The user administration tools are on the admin server, under the admin panel's rules. The admin panel only opens for the admin role, so every tool that reaches one of its screens needs that role besides the permission:

Tool Server What it does Permission it needs
who-am-i User Who the creator of the token is: user, roles, permissions, language. None
list-users Admin The users, newest first, with a search. retrieve user and the admin role
list-roles Admin The roles and the name the other tools take. retrieve role and the admin role
create-user Admin Creates a user through the panel's own form, with its validation and its welcome email. Without a password, one is generated and the user is asked to choose their own. create user and the admin role
delete-user Admin Deletes a user as the panel does. A sudo user cannot be deleted. delete user and the admin role
change-user-role Admin Replaces the user's roles with the one given and emails the user, as the panel does. update user and the admin role
block-user Admin Blocks a user with the panel's Block User action: they are signed out on their next request and keep their account. A user with the admin role cannot be blocked. The admin role
unblock-user Admin Lifts the block with the same action. The admin role

Beyond the users, the base brings the tools of its own screens, each one on the server of its screen and with that screen's rules. The tickets stay on the user's server even for the support inbox, because whoever answers support works it without the admin role:

Tool Server What it does Permission it needs
list-tickets User The person's own tickets (scope mine, the default), or every ticket in the support inbox order (scope inbox). None for their own; answer ticket for the inbox
show-ticket User A ticket with its conversation. None for their own; answer ticket for anybody else's
reply-ticket User Replies without attachments: as the author on their own ticket, as support on anybody else's. A closed ticket takes no reply. None for their own; answer ticket for anybody else's
close-ticket User Closes a ticket. Needs confirm: true. None for their own; answer ticket for anybody else's
list-announcements Admin The announcements, drafts included, with a search and a status. retrieve announcement and the admin role
create-announcement Admin Creates an announcement through the panel's own form; published with send_email, it is emailed as in the panel. create announcement and the admin role
update-announcement Admin Changes the fields given, through the same form. update announcement and the admin role
delete-announcement Admin Deletes an announcement as the panel does. Needs confirm: true. delete announcement and the admin role
list-media-folders User The folders of the person's own media library, with their path. None
list-media User The files of one of those folders. None
create-media-folder User Creates a folder in the person's library, with the screen's name rules. None
rename-media User Renames one of the person's files. None
delete-media User Deletes one of the person's files and frees its space. Needs confirm: true. None
list-referrals User The people the person referred, with the rewards each one earned and the referral link. None
list-referral-rewards User Every referral reward the person earned, and the totals per currency. None
list-activity Admin The activity log, with the filters of the panel's activity screen. The admin role

The media tools only ever reach the person's own library, never another account's files, and none of them uploads a file. Every tool that deletes or closes something changes nothing until it is called again with confirm: true.

Some tools depend on a module: the ticket tools exist while tickets are on (features.tickets.enabled), the announcement tools while announcements are on (features.announcements) and the referral tools while referrals are on (features.referrals.enabled). With the module off they are neither listed nor callable, as its screens answer not found. A tool of one server called on the other is not found either.

Ask how the product is doing

The admin server also gives the figures of the admin dashboard, to ask the assistant how the product is doing without opening the panel. Only a superadministrator reaches them, and one without the admin role the dashboard asks is answered that the admin role is missing. They only read, and they never give an email: the lists bring each user's ID, name, sign-up date and last connection, and the rest are counts.

Tool What it answers Exists while
product-overview How many users there are, how many connected today, this week and this month, and how many signed up in the period. Always
daily-activity Period by period: sign-ups, accumulated users, connected users and activity log events. Always
list-inactive-users Who has not connected since the start of the day a number of days ago, 30 by default, with their last connection. Always
list-active-users Who connected between two days, ordered by last connection or by sign-up date. Always
users-by-country Users by country, with Unknown for those without one. Always
revenue-summary By day or by month: what was collected from plans, add-ons and meters, as the dashboard's Daily Revenue chart draws it. While paid plans are on, also the paid subscriptions created, cancelled and accumulated, and the monthly recurring revenue of the active subscriptions at their catalogue price. The dashboard shows Daily Revenue
plans-ranking Which plans sell most in the period, and the active subscribers of each one today. Paid plans are on
add-ons-ranking Which add-ons are switched on in most accounts, by kind of account. Add-ons are on
tracking-events Tracking events period by period, in all and by type. Tracking is on
referral-subscriptions Referral sign-ups and paid referral subscriptions, period by period. Referrals are on

The numbers are the dashboard's for the same range: by default the last 30 days, by day up to 50 days and then by week or by month, and a range covers up to 366 days. Days are whole days in the application timezone for everybody, so a sign-up of the first hours of a day counts on that day, whatever the timezone of the person who asks. Connected users per day come from the statistic stored each night for the day before, as on the dashboard, and the answer says how many days of the range have none yet. The overview and the lists of active and inactive users read each person's last connection instead, and each answer says which source it read. Active and inactive users share one limit, the start of the day that many days ago, so nobody is in both lists. Asking leaves nothing in the activity log, so it never changes the dashboard's activity log events nor the ones daily-activity answers.

When the person is not allowed, the tool answers with the permission that is missing, for example the person who created this token needs the "retrieve user" permission, or with the role, the person who created this token needs the admin role, and changes nothing. A token never reaches more than its creator can.

Whatever a tool changes is written in the activity log with the creator as the author, and the record's history shows a Via MCP tag with the token's name.

When the server says no

The server refuses before running anything, in the creator's language:

Answer When
A valid MCP token is required. No token, an unknown one or a revoked one.
This MCP server is switched off. The user's server with app_server off, or the admin server switched off in the admin panel.
This MCP token belongs to another MCP server. Connect it to … A profile token sent to /mcp/admin, or an admin token sent to /mcp. The answer names the right address.
The MCP add-on is switched off for this account. User's server: the creator's account has MCP off.
The person who created this MCP token no longer has access to MCP. User's server: the creator is blocked or lost the Manage Mcp Tokens permission. Admin server: the creator is blocked or is no longer a superadministrator.
This MCP token is no longer valid. The creator was deleted.
This MCP token made more than 600 requests in a minute. Try again in a few seconds. The token went over its limit. Each token has its own.

Change the configuration

It lives under mcp in config/features.php, with no environment variable:

'mcp' => [
    'app_server' => true,
    'token_prefix' => 'mcp_',
    'requests_per_minute' => 600,
],

app_server set to false leaves the user's server off for the whole product, without removing its address: every request answers that the server is off. The admin server does not read it; it has its switch in the admin panel.

token_prefix only names the product at the start of each new token. A token is found by the SHA-256 hash of its whole value, so changing the prefix never stops the tokens made before. Only that hash is stored, in mcp_tokens.

Add your product's tools

Write a class in app/Mcp/Tools that extends App\Mcp\Tools\ApplicationTool, ask the creator's permissions in it, and register it from a service provider on the server of its screen — AppServer for a screen of the user area, AdminServer for one of the admin panel:

use App\Mcp\Servers\AppServer;

public function boot(): void
{
    AppServer::registerTools([
        \App\Mcp\Tools\ListInvoices::class,
    ]);
}
namespace App\Mcp\Tools;

use App\Models\Invoice;
use Laravel\Mcp\{Request, Response, ResponseFactory};
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;

#[Description('Lists the invoices the person can see.')]

#[IsReadOnly]
class ListInvoices extends ApplicationTool
{
    public function handle(Request $request): Response|ResponseFactory
    {
        if (! $this->token()->allows('viewAny', Invoice::class)) {
            return $this->missingPermission('retrieve invoice');
        }
        return Response::structured([
            'invoices' => Invoice::latest()->limit(25)->get(['id', 'number', 'total'])->toArray(),
        ]);
    }
}

The server publishes it next to the base tools, named after the class in kebab case (list-invoices), and the other server never does. The base never needs editing for it, and the base registers its own screen tools the same way, so a product only ever appends to the list of each server. A product's own server that extends AppServer keeps the tools registered on AppServer besides its own.

Two switches of ApplicationTool cover the cases the base tools use. Set protected bool $superadminOnly = true; and start handle() with if (! $this->isListedFor($this->creator())) { return $this->onlyForSuperadmins(); } for a tool only superadministrators see. A tool that reaches a screen of the admin panel also checks $this->creator()->isAdmin() and answers $this->missingAdminRole() without it. Override moduleIsOn() to return your module's switch for a tool that must disappear while that module is off. A tool that deletes answers $this->confirmationRequired() until it receives confirm: true. The full reference, with the middleware and the migrations, is in docs/weblabor-base/mcp.md.