[KongchangAI]
· 2 min read· 1,347 words

Laravel Meets MCP Servers: A Practical Guide from Install to Test

Laravel Meets MCP Servers: A Practical Guide from Install to Test

Laravel's official MCP package lets PHP developers quickly build protocol-compliant AI tool servers with Artisan and the service container.

This article explains how to use Laravel's official MCP package to build Model Context Protocol-compliant servers so AI models can interact with business code through structured function calling. It covers installation prerequisites (Laravel 12/13, PHP 8.2+), Composer setup and routes/ai.php publishing, generating server classes with Artisan, describing your server to clients via #[Name]/#[Version]/#[Instructions], and choosing between Mcp::web() and Mcp::local(). At the tool level, it stresses that handle() execution logic and schema() input definitions must be implemented as a pair, and that #[Description] and safety annotations directly determine the model's success rate and the client's security policy. Finally, it recommends running php artisan mcp:inspector for interactive self-testing before connecting a real client.

The way AI models interact with external applications is shifting from "chatbox wrappers" to structured function calling, and the Model Context Protocol (MCP) is one of the underlying standards driving that change. It lets models call your code directly instead of relying on a vague natural-language prompt to guess intent. For Laravel developers, this means you can build MCP servers using familiar Artisan commands, the service container, and validation rules—without implementing the protocol spec from scratch.

rss source: How to Connect and Use Laravel with an MCP Server

Why Build an MCP Server in Laravel

The value of MCP lies in embedding AI capabilities into your existing engineering stack. Laravel's official package wraps protocol details into framework-native concepts: scaffolding via Artisan, automatic dependency injection through the service container, and reuse of built-in validation rules and authentication support. You don't hand-write protocol parsing—you just define "tools" and the parameters they accept.

The immediate benefit is maintainability. MCP traffic has different protocol expectations and security requirements than ordinary web requests, so the official package generates a separate routes/ai.php file, isolating it from web.php and api.php to prevent routing and security policies from polluting each other.

What is the Model Context Protocol (MCP)? MCP is an open standard protocol released by Anthropic in late 2024 to define how AI models communicate with external tools and data sources. It uses JSON-RPC 2.0 as its message format and defines three core capabilities: Tools (functions the model can actively invoke), Resources (data the model can read), and Prompts (predefined prompt templates). Unlike traditional "chatbox wrappers," MCP lets models precisely understand the parameter types and constraints each tool accepts through structured schemas, reducing invocation errors caused by guessing. An MCP server is essentially a service that follows this protocol spec; clients (such as Claude Desktop, Cursor, etc.) first fetch the server's capability list, then make calls as needed. Because the protocol itself is language-agnostic, what the Laravel MCP package does is translate protocol details into Artisan commands, the service container, and the middleware system that PHP developers already know.

Environment Requirements and Installation

Before you start, you need a few prerequisites: Laravel 12.x or 13.x, PHP 8.2+, Composer, and a testable MCP client (for example, Claude Desktop, or the built-in Artisan inspector).

Installation is done through Composer, followed by publishing the dedicated route configuration:

composer require laravel/mcp
php artisan vendor:publish --tag=ai-routes

This step generates routes/ai.php in your application. Treat it as the "entry layer" for MCP—all server registrations are centralized here.

Creating and Registering Your First MCP Server

Scaffold a server class with Artisan:

php artisan make:mcp-server WeatherServer

The generated class lives in app/Mcp/Servers/WeatherServer.php and extends Laravel MCP's base Server class. The #[Name], #[Version], and #[Instructions] attributes on the class are not decorative—AI clients read them to determine when to call your server. The original article specifically reminds: #[Instructions] should clearly describe the purpose, not leave a generic placeholder, otherwise the model won't know when to reach for it.

Inside the server there are three arrays: $tools, $resources, and $prompts, corresponding to executable actions, readable resources, and prompt templates, respectively.

Local vs. Remote: Two Ways to Register

Where you register determines whether an AI client can reach your server.

HTTP (remote/production): use Mcp::web() so hosted AI clients can access it through a URL endpoint:

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

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

Local development: use Mcp::local(), suitable for scenarios where the client and server run on the same machine, such as a local AI coding assistant:

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

The trade-off between the two is straightforward: once you expose it to hosted AI clients, you must use web() and consider the rate-limiting middleware; if you're just debugging locally, local() is simpler.

Building a Tool the AI Can Call

A Tool is a concrete action the model can invoke. Generate one with Artisan:

php artisan make:mcp-tool CurrentWeatherTool

A tool class needs to implement two parts: the execution logic handle() and the input schema schema().

#[Description('Fetches the current weather forecast for a specified location.')]
#[IsIdempotent]
#[IsReadOnly]
class CurrentWeatherTool extends Tool
{
    public function handle(Request $request): Response
    {
        $location = $request->get('location');
        return Response::text("The weather in {$location} is sunny.");
    }

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

Descriptions and Safety Annotations Are Not Optional

The original article offers two practical recommendations worth highlighting separately.

First, descriptions are the interface. AI models read #[Description] along with each field's description to decide how to call the tool. Leaving them blank or writing generic descriptions is equivalent to making the model guess parameter meanings, which noticeably raises the failure rate of invocations.

Second, safety annotations are signals for the client. #[IsReadOnly], #[IsIdempotent], and #[IsDestructive] inform the client before execution whether the tool modifies data or can be called repeatedly. For tools that modify or delete data, these annotations are especially important.

Once defined, add the tool class to the server's $tools array to activate it:

protected array $tools = [
    CurrentWeatherTool::class,
];

What the three safety annotations actually mean. #[IsReadOnly] means the tool only reads data and produces no side effects, so the client can safely invoke it in a read-only context; #[IsIdempotent] means the result is the same no matter how many times it's called, which suits query and computation operations and lets the client safely repeat calls on retries; #[IsDestructive] warns that the tool may modify or delete data, and the client will typically require explicit user confirmation before executing. These three annotations essentially pass a "risk level" signal to the AI client, letting it decide whether an additional approval step is needed before execution. From an engineering perspective, annotations are a lightweight form of contract declaration—they don't replace permission checks at the code level, but complement them: the annotation tells the client "how dangerous this operation is," while server-side authentication and authorization decide "whether this request is allowed to execute."

Self-Test Before Connecting to a Real Client

Before connecting to a real AI client like Claude Desktop, verify your configuration with the built-in Artisan inspector:


**How the MCP Inspector works.** The interactive session started by `php artisan mcp:inspector` is essentially a built-in MCP client. It initiates a handshake with your server following the protocol spec, requests the capability list, displays the JSON Schema for each tool, and lets you manually fill in parameters to trigger real calls. This means you can independently validate whether your server's protocol implementation is correct, without relying on Claude Desktop or any external AI client. Common troubleshooting scenarios include: a missing `required` declaration on a schema field causing calls to be rejected, overly vague description text causing the model to pick the wrong tool, and whether exceptions thrown inside `handle()` are correctly translated into protocol-level error responses. It's recommended to make inspector testing a fixed step in your development workflow—run it after every tool-definition change, before connecting a real client.
# For Web server registration
php artisan mcp:inspector mcp/weather

# For local server registration
php artisan mcp:inspector weather

This opens an interactive test session where you can inspect the tool list, view schemas, and manually trigger test calls. Compared to connecting a client and troubleshooting via error messages, this step pinpoints schema definition mistakes or missing parameters much faster.

Who This Approach Suits

If your team already uses Laravel and wants to let AI assistants directly read or operate on business data, MCP offers a path that doesn't require inventing your own protocol. It reuses your existing validation, authentication, and dependency injection systems, at the cost of managing a separate set of entry points in routes/ai.php.

One caveat: the weather tool shown in the original article is only a skeleton—the data-fetching logic inside handle() was omitted. In production, a tool's safety annotations, parameter validation, and permission boundaries all need to be redesigned around your actual business—especially for tools that write data, where annotations like #[IsDestructive] should be treated as the bare minimum, not the entire defense.

#Laravel MCP#Model Context Protocol#Laravel AI#MCP server#PHP AI tools
Share:

Related articles