ホーム

Blog

Laravel Model Context Protocol (MCP)

The Laravel Model Context Protocol (MCP) package provides a simple and elegant way for AI clients to interact with your Laravel application through the Model Context Protocol

更新日:2025/10/15

Laravel Model Context Protocol (MCP)

Laravel MCP (Model Context Protocol) is a specialized Laravel package that provides a simple and elegant method for AI clients to interact with your Laravel application. It implements the Model Context Protocol (MCP) and offers an expressive, fluent interface for defining the elements—servers, tools, resources, and prompts—that enable these AI-powered interactions.

1. Installation and Setup

To begin using Laravel MCP, you must install it using Composer

composer require laravel/mcp

After installation, the routes must be published using the Artisan command

php artisan vendor:publish --tag=ai-routes

This creates the routes/ai.php file, which is used to register MCP servers

2. Servers: The Core Communication Point

Servers act as the central communication point, exposing all MCP capabilities (tools, resources, and prompts) to AI clients

2.1 Creating and Registering Servers

Servers are created using the make:mcp-server Artisan command

php artisan make:mcp-server WeatherServer

The resulting class extends Laravel\Mcp\Server and includes properties for defining the server's metadata (e.g., $name, $version, $instructions) and arrays for registering its components ( $tools, $resources, $prompts).

<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    protected string $name = 'Weather Server';
    protected string $version = '1.0.0';
    protected string $instructions = 'This server provides weather information and forecasts.';


    protected array $tools = [
        // GetCurrentWeatherTool::class,
    ];


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

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

Servers must be registered in the routes/ai.php file using two available methods:

2.1.1 Web Servers

These are the most common type, accessible via HTTP POST requests, and are ideal for remote AI clients. They are registered using the Mcp::web facade method and can be protected by applying middleware:

use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);

2.1.2 Local Servers

These run as Artisan commands, designed for local AI assistant integrations (like Laravel Boost). They are registered using the Mcp::local facade method.

use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('weather', WeatherServer::class);

3. Core MCP Components

Laravel MCP applications are primarily defined by three types of components: Tools, Prompts, and Resources

3.1 Tools

Tools enable the MCP server to expose functionality that AI clients can call. They allow language models to perform actions, run code, or interact with external systems

3.1.1 Creation & Registration

Tools are created via make:mcp-tool and registered in the server's $tools property. 

php artisan make:mcp-tool CurrentWeatherTool

The tool's $description is critical metadata that helps AI models understand when and how to use it

class CurrentWeatherTool extends Tool
{

    protected string $name = 'get-optimistic-weather';
    protected string $title = 'Get Optimistic Weather Forecast';
    protected string $description = 'Fetches the current weather forecast for a specified location.';

}

3.1.2 Input Schemas

Tools define arguments using Laravel's Illuminate\JsonSchema\JsonSchema builder within the schema() method. This specifies the required input arguments (e.g., location, units)

<?php

namespace App\Mcp\Tools;

use Illuminate\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    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'),
        ];
    }
}

3.1.3 Validation

Complex validation can be enforced within the tool's handle() method using Laravel's validation features ($request->validate(...)). Clear, actionable error messages must be provided for AI clients

<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * Handle the tool request.
     */
    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 to get the weather for. For example, "New York City" or "Tokyo".',
    		'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.',
		]);
    }
}

3.1.4 Responses

Tools must return a Laravel\Mcp\Response instance. Responses can be simple text (Response::text()), an error message (Response::error()), or multiple content responses (array of Response instances)

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

public function handle(Request $request): Response
{
    // ...

    return Response::text('Weather Summary: Sunny, 72°F');
}

3.1.5 Streaming

Tools can return a PHP Generator from the handle method for long-running operations or real-time data streaming. When used with web servers, this automatically opens an SSE (Server-Sent Events) stream

<?php

namespace App\Mcp\Tools;

use Generator;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    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));
        }
    }
}

3.1.6 Annotations

Tools can include attributes (annotations) like #[IsIdempotent] or #[IsReadOnly] to provide additional metadata about their capabilities and behavior to AI clients

<?php

namespace App\Mcp\Tools;

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
{
    //
}

3.1.7 Conditional Registration

Tools can be registered conditionally at runtime by implementing the shouldRegister() method, based on application state or request parameters (e.g., checking if a user is subscribed)

<?php

namespace App\Mcp\Tools;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * Determine if the tool should be registered.
     */
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

3.1.8 Dependency Injection

The Laravel service container resolves all tools, allowing dependencies to be type-hinted in the constructor or the handle()

<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * Handle the prompt request.
     */
    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $isAvailable = $weather->isServiceAvailable();

        // ...
    }
}

3.2 Prompts

Prompts enable the server to share reusable prompt templates that AI clients can utilize to interact with language models, providing a standardized way to structure common queries

3.2.1 Creation and Registration

Prompts are created via make:mcp-prompt and registered in the server's $prompts property. 

php artisan make:mcp-prompt DescribeWeatherPrompt

A meaningful $description helps AI models understand prompt utility

class DescribeWeatherPrompt extends Prompt
{
    protected string $name = 'weather-assistant';
    protected string $title = 'Weather Assistant Prompt';
    protected string $description = 'Generates a natural-language explanation of the weather for a given location.';

    // ...
}

3.2.2 Arguments and Validation

Prompts define arguments using the arguments() method, returning \Laravel\Mcp\Server\Prompts\Argument instances. Validation is done within the handle method, similar to tools

<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'tone' => 'required|string|max:50',
        ]);
 
        $tone = $validated['tone'];
 
        // Generate the prompt response using the given tone...
    }
    
    public function arguments(): array
    {
        return [
            new Argument(
                name: 'tone',
                description: 'The tone to use in the weather description (e.g., formal, casual, humorous).',
                required: true,
            ),
        ];
    }
}

3.2.3 Responses

Prompts may return a single or an iterable set of Laravel\Mcp\Response instances. The asAssistant() method can indicate that a message should be treated as coming from the AI 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');

        $systemMessage = "You are a helpful weather assistant. Please provide a weather description in a {$tone} tone.";

        $userMessage = "What is the current weather like in New York City?";

        return [
            Response::text($systemMessage)->asAssistant(),
            Response::text($userMessage),
        ];
    }
}

3.2.4 Injection and Conditional Registration

Like tools, prompts support dependency injection in constructors and the handle method, and conditional registration using shouldRegister().

<?php

namespace App\Mcp\Prompts;

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

class CurrentWeatherPrompt extends Prompt
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

3.3 Resources

Resources expose data and content that AI clients can read and use as context when interacting with language models, such as documentation or configuration. Unlike tools and prompts, resources cannot define input schemas or arguments

3.3.1 Creation and Registration

Resources are created via make:mcp-resource and registered in the server's $resources property. 

php artisan make:mcp-resource WeatherGuidelinesResource

A $description is essential metadata

class WeatherGuidelinesResource extends Resource
{
    protected string $name = 'weather-api-docs';
    protected string $title = 'Weather API Documentation';

	protected string $description = 'Comprehensive guidelines for using the Weather API.';
    // ...
}

3.3.2 URI and MIME Type

Resources are identified by a unique URI (e.g., weather://resources/weather-guidelines) and an associated MIME type (defaulting to text/plain). These can be customized using the $uri and $mimeType properties

<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    protected string $uri = 'weather://resources/guidelines';
    protected string $mimeType = 'application/pdf';
}

3.3.3 Responses

Resources must return a Laravel\Mcp\Response. For simple content, use Response::text(). To return binary content, use Response::blob(), where the MIME type is determined by the resource's $mimeType property. Error responses use Response::error()

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

/**
 * Handle the resource request.
 */
public function handle(Request $request): Response
{
    // ...

    return Response::text($weatherData);
}

3.3.4 Injection and Conditional Registration

Resources also support dependency injection and conditional registration via shouldRegister()

<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

4. Authentication and Authorization

Web MCP servers can be protected using standard Laravel middleware

4.1 Authentication

Two primary authentication methods are supported:

4.1.1 OAuth 2.1 (Recommended)

This is the documented authentication mechanism in the Model Context Protocol specification and is widely supported by MCP clients. It is implemented using Laravel Passport. This requires invoking Mcp::oauthRoutes() to register discovery and client registration routes, and then applying Passport’s auth:api middleware to the Mcp::web route. Laravel MCP advertises and uses a single mcp:use scope.

use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

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

4.1.2 Sanctum

If integrating Passport is cumbersome, Sanctum can be used by applying the auth:sanctum middleware, requiring clients to provide an Authorization: Bearer <token> header

use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;

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

4.2 Authorization

Within tools and resources, the currently authenticated user can be accessed via $request->user(). This allows for authorization checks (e.g., using Laravel's permission system) and returning a Response::error() if permission is denied

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;


public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('Permission denied.');
    }

    // ...
}

5. Testing and Debugging

Laravel MCP provides both an interactive debugging tool and methods for comprehensive unit testing

5.1. MCP Inspector

The MCP Inspector is an interactive tool used for testing and debugging registered servers. It can be launched via Artisan (php artisan mcp:inspector [server]) for both web and local servers.

# Web server...
php artisan mcp:inspector mcp/weather

# Local server named "weather"...
php artisan mcp:inspector weather

 It allows users to connect to the server, verify authentication, and test out tools, resources, and prompts

5.2. Unit Tests

Unit tests can be written by invoking the primitive directly on the server class

5.2.1. To test a tool

$response = WeatherServer::tool(CurrentWeatherTool::class, [...])

5.2.2. To test a prompt or resource

$response = WeatherServer::prompt(...)

or

$response = WeatherServer::resource(...)

5.2.3. Authentication can be simulated using the actingAs method

$response = WeatherServer::actingAs($user)->tool(...)

 

Unit test assertions include assertOk() (checks for no errors), assertSee() (checks for specific text content), assertHasErrors(), assertHasNoErrors(), and assertions for metadata like assertName() or assertDescription(). For streaming responses, assertions like assertSentNotification() and assertNotificationCount() are available

/**
 * Test a tool.
 */
public function test_tool(): void
{
    $response = WeatherServer::tool(CurrentWeatherTool::class, [
        'location' => 'New York City',
        'units' => 'fahrenheit',
    ]);

    $response
        ->assertOk()
        ->assertSee('The current weather in New York City is 72°F and sunny.');
}