Skip to main content

Documentation Index

Fetch the complete documentation index at: https://kawax.biz/llms.txt

Use this file to discover all available pages before exploring further.

What is MCP?

Model Context Protocol (MCP) is a standardised protocol for communication between AI clients (Claude, Cursor, GitHub Copilot, etc.) and your application. By implementing an MCP server you let AI agents read your application’s data and execute actions on your behalf.
Laravel MCP is an official package added in Laravel 13, available as laravel/mcp. It provides everything you need to build MCP servers.
An MCP server can expose three types of capabilities:
CapabilityDescription
ToolsFunctions an AI client can call — search, update, or integrate with external APIs
ResourcesData and context an AI client can read
PromptsReusable, parameterised prompt templates

Installation

composer require laravel/mcp
After installation, publish the routes/ai.php file:
php artisan vendor:publish --tag=ai-routes
This creates routes/ai.php where you register your MCP servers.

Creating a server

Generate a server class with Artisan:
php artisan make:mcp-server WeatherServer
The class is placed in app/Mcp/Servers:
<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
use Laravel\Mcp\Server;

#[Name('Weather Server')]
#[Version('1.0.0')]
#[Instructions('This server provides weather information and forecasts.')]
class WeatherServer extends Server
{
    protected array $tools = [
        // GetCurrentWeatherTool::class,
    ];

    protected array $resources = [
        // WeatherGuidelinesResource::class,
    ];

    protected array $prompts = [
        // DescribeWeatherPrompt::class,
    ];
}

Registering a server

Register servers in routes/ai.php. You can register a server as a web server or a local server.

Web server

A web server listens for HTTP POST requests. Use it for remote AI clients or web-based integrations.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class);
Apply middleware just like any other route:
Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);

Local server

A local server runs as an Artisan command. Use it with local AI clients such as Claude Desktop.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('weather', WeatherServer::class);
The MCP client normally starts local servers automatically — you do not need to run mcp:start manually.

Tools

Tools are functions an AI client can call. They can fetch data, update records, or integrate with external services.

Creating a tool

php artisan make:mcp-tool CurrentWeatherTool
Register the tool in the server’s $tools array:
use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    protected array $tools = [
        CurrentWeatherTool::class,
    ];
}
A basic tool implementation:
<?php

namespace App\Mcp\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;

#[Description('Fetches the current weather forecast for a specified location.')]
class CurrentWeatherTool extends Tool
{
    public function handle(Request $request): Response
    {
        $location = $request->get('location');

        // Fetch weather data...

        return Response::text('The weather is sunny, 22°C.');
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'location' => $schema->string()
                ->description('The location to get the weather for.')
                ->required(),
        ];
    }
}

Tool name and description

The tool’s name and title are automatically derived from the class name. CurrentWeatherTool becomes name current-weather and title Current Weather Tool. Override with Name and Title attributes:
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;

#[Name('get-optimistic-weather')]
#[Title('Get Optimistic Weather Forecast')]
class CurrentWeatherTool extends Tool
{
    // ...
}
The Description attribute is not generated automatically. Always provide a meaningful description — it is how the AI model understands what the tool does and when to use it.

Input schema

Define input parameters in the schema method using Laravel’s JSON schema builder:
public function schema(JsonSchema $schema): array
{
    return [
        'location' => $schema->string()
            ->description('The location to get the weather for.')
            ->required(),

        'units' => $schema->string()
            ->enum(['celsius', 'fahrenheit'])
            ->description('The temperature units to use.')
            ->default('celsius'),
    ];
}

Output schema

Define the shape of the response in outputSchema so AI clients can parse it reliably:
public function outputSchema(JsonSchema $schema): array
{
    return [
        'temperature' => $schema->number()
            ->description('Temperature in Celsius')
            ->required(),

        'conditions' => $schema->string()
            ->description('Weather conditions')
            ->required(),

        'humidity' => $schema->integer()
            ->description('Humidity percentage')
            ->required(),
    ];
}

Validation

Use Laravel’s standard validation inside handle:
public function handle(Request $request): Response
{
    $validated = $request->validate([
        'location' => 'required|string|max:100',
        'units'    => 'in:celsius,fahrenheit',
    ], [
        'location.required' => 'You must specify a location, e.g. "New York City" or "Tokyo".',
        'units.in'          => 'Units must be "celsius" or "fahrenheit".',
    ]);

    // Use $validated...
}
When validation fails the AI client receives the error message and can retry. Write clear, actionable error messages to help the agent self-correct.

Dependency injection

Tools are resolved through the service container, so you can type-hint dependencies in the constructor or handle method:
<?php

namespace App\Mcp\Tools;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    public function __construct(
        protected WeatherRepository $weather,
    ) {}

    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $location = $request->get('location');
        $forecast = $weather->getForecastFor($location);

        return Response::text("Forecast: {$forecast}");
    }
}

Annotations

Add annotations to give AI clients additional hints about a tool’s behaviour:
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[IsIdempotent]
#[IsReadOnly]
class CurrentWeatherTool extends Tool
{
    // ...
}
AnnotationDescription
#[IsReadOnly]Tool does not modify any state
#[IsDestructive]Tool may perform destructive updates
#[IsIdempotent]Repeating the call with the same arguments has no additional side effects
#[IsOpenWorld]Tool may interact with external entities

Conditional registration

Implement shouldRegister to expose a tool only when certain conditions are met:
public function shouldRegister(Request $request): bool
{
    return $request?->user()?->subscribed() ?? false;
}
When this returns false, the tool is invisible to the AI client.

Responses

Tools must return a Laravel\Mcp\Response instance.
return Response::text('Weather Summary: Sunny, 22°C');
return Response::error('Unable to fetch weather data. Please try again.');
return Response::image(file_get_contents(storage_path('weather/radar.png')), 'image/png');

return Response::audio(file_get_contents(storage_path('weather/alert.mp3')), 'audio/mp3');

// Load directly from storage (MIME type is auto-detected)
return Response::fromStorage('weather/radar.png');
public function handle(Request $request): array
{
    return [
        Response::text('Weather Summary: Sunny, 22°C'),
        Response::text("**Detailed Forecast**\n- Morning: 18°C\n- Afternoon: 25°C"),
    ];
}
return Response::structured([
    'temperature' => 22.5,
    'conditions'  => 'Partly cloudy',
    'humidity'    => 65,
]);
Stream progress updates for long-running operations:
public function handle(Request $request): Generator
{
    $locations = $request->array('locations');

    foreach ($locations as $index => $location) {
        yield Response::notification('processing/progress', [
            'current'  => $index + 1,
            'total'    => count($locations),
            'location' => $location,
        ]);

        yield Response::text($this->forecastFor($location));
    }
}

Prompts

Prompts are reusable, parameterised prompt templates that standardise common queries an AI client sends to a language model.

Creating a prompt

php artisan make:mcp-prompt DescribeWeatherPrompt
Register it in the server’s $prompts array:
use App\Mcp\Prompts\DescribeWeatherPrompt;

class WeatherServer extends Server
{
    protected array $prompts = [
        DescribeWeatherPrompt::class,
    ];
}

Prompt arguments

Define prompt parameters in the arguments method:
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;

class DescribeWeatherPrompt extends Prompt
{
    public function arguments(): array
    {
        return [
            new Argument(
                name: 'tone',
                description: 'The tone for the weather description (e.g., formal, casual, humorous).',
                required: true,
            ),
        ];
    }
}

Prompt responses

Return user and assistant messages from the handle method. Use asAssistant() to mark a message as coming from the assistant:
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;

class DescribeWeatherPrompt extends Prompt
{
    public function handle(Request $request): array
    {
        $tone = $request->string('tone');

        return [
            Response::text("You are a helpful weather assistant. Describe the weather in a {$tone} tone.")->asAssistant(),
            Response::text('What is the current weather like in Tokyo?'),
        ];
    }
}

Resources

Resources are data or information an AI client can load as context — documentation, configuration, or dynamic application data that improves the quality of AI responses.

Creating a resource

php artisan make:mcp-resource WeatherGuidelinesResource
Register it in the server’s $resources array:
use App\Mcp\Resources\WeatherGuidelinesResource;

class WeatherServer extends Server
{
    protected array $resources = [
        WeatherGuidelinesResource::class,
    ];
}
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Resource;

#[Description('Comprehensive guidelines for using the Weather API.')]
class WeatherGuidelinesResource extends Resource
{
    public function handle(Request $request): Response
    {
        $guidelines = "# Weather API Guidelines\n\n- Always specify a location...";

        return Response::text($guidelines);
    }
}

URI and MIME type

The URI is derived automatically from the class name (e.g. weather://resources/weather-guidelines). Customise with Uri and MimeType attributes:
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Attributes\Uri;
use Laravel\Mcp\Server\Resource;

#[Uri('weather://resources/guidelines')]
#[MimeType('application/pdf')]
class WeatherGuidelinesResource extends Resource
{
    // ...
}

Resource templates

Implement HasUriTemplate to define dynamic resources with URI variables:
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Contracts\HasUriTemplate;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Support\UriTemplate;

#[Description('Access user files by ID')]
#[MimeType('text/plain')]
class UserFileResource extends Resource implements HasUriTemplate
{
    public function uriTemplate(): UriTemplate
    {
        return new UriTemplate('file://users/{userId}/files/{fileId}');
    }

    public function handle(Request $request): Response
    {
        $userId = $request->get('userId');
        $fileId = $request->get('fileId');

        return Response::text("File {$fileId} for user {$userId}");
    }
}
URI variables are automatically injected into the request and accessible via get().

Resource annotations

Annotate resources with audience, priority, and last-modified date:
use Laravel\Mcp\Enums\Role;
use Laravel\Mcp\Server\Annotations\Audience;
use Laravel\Mcp\Server\Annotations\LastModified;
use Laravel\Mcp\Server\Annotations\Priority;
use Laravel\Mcp\Server\Resource;

#[Audience(Role::User)]
#[LastModified('2025-01-12T15:00:58Z')]
#[Priority(0.9)]
class UserDashboardResource extends Resource
{
    // ...
}
AnnotationTypeDescription
#[Audience]Role or arrayIntended audience (Role::User, Role::Assistant, or both)
#[Priority]floatImportance score from 0.0 to 1.0
#[LastModified]stringISO 8601 timestamp of the last modification

Apps

Laravel MCP supports MCP Apps, an extension of the Model Context Protocol that allows tools to render interactive HTML applications within sandboxed iframes in supported hosts. This allows you to build dashboards, forms, visualizations, and other rich experiences that go beyond plain text responses. An MCP app consists of two parts working together:
  • An app resource that returns the self-contained HTML for your application.
  • A tool that is linked to the app resource using the #[RendersApp] attribute. When the tool is called, the host fetches and renders the linked resource.

Creating app resources

Create an app resource using the make:mcp-app-resource Artisan command:
php artisan make:mcp-app-resource WeatherDashboardApp
This command creates two files: a PHP class in app/Mcp/Resources and a Blade view in resources/views/mcp. The view name is automatically inferred from the class name. For example, WeatherDashboardApp maps to mcp.weather-dashboard-app:
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;

#[Description('An interactive weather dashboard.')]
#[AppMeta]
class WeatherDashboardApp extends AppResource
{
    /**
     * Handle the app resource request.
     */
    public function handle(Request $request): Response
    {
        return Response::view('mcp.weather-dashboard-app', [
            'title' => $this->title(),
        ]);
    }
}
AppResource extends the base Resource class and automatically configures the ui:// URI scheme and the text/html;profile=mcp-app MIME type required by the MCP Apps specification. Like any other resource, you must register it in your server’s $resources array. The generated Blade view uses the <x-mcp::app> component, which renders a complete HTML document with the client-side MCP SDK bundled and ready to use:
<x-mcp::app :title="$title">
    <x-slot:head>
        <script type="module">
        createMcpApp(async (app) => {
            document.getElementById('run-btn').addEventListener('click', async () => {
                const result = await app.callServerTool('get-weather-data', {});
                document.getElementById('output').textContent = result.content[0]?.text ?? '';
            });
        });
        </script>
    </x-slot:head>

    <div id="app">
        <button id="run-btn">Refresh</button>
        <p id="output"></p>
    </div>
</x-mcp::app>
The createMcpApp global is provided by the bundled SDK and handles connecting the iframe to the server, applying host theming, and exposing helpers such as callServerTool, sendMessage, openLink, and event callbacks. For the full client-side API, refer to the MCP Apps specification.

Rendering apps from tools

To display an app resource, link a tool to it using the #[RendersApp] attribute. When the tool is called, Laravel MCP includes the resource’s URI in the tool metadata so the host can render the app in a sandboxed iframe:
<?php

namespace App\Mcp\Tools;

use App\Mcp\Resources\WeatherDashboardApp;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Tool;

#[RendersApp(resource: WeatherDashboardApp::class)]
class ShowWeatherDashboard extends Tool
{
    /**
     * Handle the tool request.
     */
    public function handle(Request $request): Response
    {
        return Response::text('Weather dashboard loaded.');
    }
}
Laravel MCP automatically advertises the io.modelcontextprotocol/ui capability whenever any AppResource is registered, so no additional server configuration is required.

App tool visibility

Each #[RendersApp] tool can limit who may invoke it via the visibility argument. This is useful for exposing private, app-only tools that the UI calls to load or refresh data without making those tools visible to the model:
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Ui\Enums\Visibility;

#[RendersApp(resource: WeatherDashboardApp::class, visibility: [Visibility::App])]
class GetWeatherData extends Tool
{
    // ...
}
The Visibility enum has two cases, Model and App, and defaults to both. Use [Visibility::App] for backend actions the UI calls directly, or [Visibility::Model] to make a tool unavailable to the UI.

App configuration

The #[AppMeta] attribute on your app resource configures the iframe’s Content Security Policy, browser permissions, and any library scripts that should be included in the view’s <head>:
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Ui\Enums\Library;
use Laravel\Mcp\Server\Ui\Enums\Permission;

#[AppMeta(
    connectDomains: ['https://api.weather.com'],
    permissions: [Permission::Geolocation],
    libraries: [Library::Tailwind, Library::Alpine],
)]
class WeatherDashboardApp extends AppResource
{
    // ...
}
The Library enum includes pre-configured CDN scripts for common front-end libraries such as Library::Tailwind and Library::Alpine, and their CDN origins are automatically merged into the CSP. The Permission enum covers browser permissions such as Camera, Microphone, Geolocation, and ClipboardWrite.
For computed or dynamic configuration, override the appMeta method on your resource using the fluent AppMeta, Csp, and Permissions builders from the Laravel\Mcp\Server\Ui namespace.

Building apps with Boost

Laravel MCP includes a dedicated Boost skill reference for building MCP Apps. If you have Laravel Boost installed, your AI coding agent can invoke the mcp-development skill and ask it to scaffold an app resource, Blade view, and linked tool for you. For the complete protocol reference, including the full client-side API and schema details, see the official MCP Apps documentation.

Metadata

Attach MCP spec _meta fields to tool, resource, and prompt responses:
return Response::text('The weather is sunny.')
    ->withMeta(['source' => 'weather-api', 'cached' => true]);
To attach metadata to the response envelope, use Response::make:
return Response::make(
    Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
To attach metadata to the tool, resource, or prompt class itself, define a $meta property:
class CurrentWeatherTool extends Tool
{
    protected ?array $meta = [
        'version' => '2.0',
        'author'  => 'Weather Team',
    ];
}

Authentication

Web servers support Laravel’s standard middleware for authentication.

Sanctum

Use token-based authentication with Laravel Sanctum. The MCP client sends an Authorization: Bearer <token> header.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware('auth:sanctum');

OAuth 2.1

Use Laravel Passport for robust OAuth-based authentication:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware('auth:api');
When using OAuth, publish the MCP authorization views and register them with Passport:
php artisan vendor:publish --tag=mcp-views
// AppServiceProvider::boot()
use Laravel\Passport\Passport;

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

Authorization

Access the authenticated user inside a tool or resource via $request->user():
public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('Permission denied.');
    }

    // Continue...
}

Testing

MCP Inspector

Use the interactive mcp:inspector command to debug your server:
# Web server
php artisan mcp:inspector mcp/weather

# Local server (named 'weather')
php artisan mcp:inspector weather
The command launches the MCP Inspector and displays a client configuration you can copy. If your server uses authentication middleware, include the Authorization header when connecting.

Unit tests

Write unit tests directly against your tools, resources, and prompts:
test('tool', function () {
    $response = WeatherServer::tool(CurrentWeatherTool::class, [
        'location' => 'Tokyo',
        'units'    => 'celsius',
    ]);

    $response
        ->assertOk()
        ->assertSee('The current weather in Tokyo is 22°C and sunny.');
});
Prompts and resources follow the same pattern:
$response = WeatherServer::prompt(DescribeWeatherPrompt::class, ['tone' => 'casual']);
$response = WeatherServer::resource(WeatherGuidelinesResource::class);
Authenticate as a specific user with actingAs:
$response = WeatherServer::actingAs($user)->tool(CurrentWeatherTool::class, [...]);
Key assertion methods:
$response->assertOk();        // No error in the response
$response->assertSee('...');  // Response contains the given text
Last modified on April 28, 2026