Add a Model Context Protocol (MCP) server to your Laravel application so AI coding agents can interact with your data, run tools, and consume reusable resources and prompts.
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:
Capability
Description
Tools
Functions an AI client can call — search, update, or integrate with external APIs
<?phpnamespace 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(), ]; }}
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:
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.
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'), ];}
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.
return Response::error('Unable to fetch weather data. Please try again.');
Image and audio responses
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');
Multiple content items
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"), ];}
<?phpnamespace 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, ), ]; }}
Return user and assistant messages from the handle method. Use asAssistant() to mark a message as coming from the assistant:
<?phpnamespace 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 are data or information an AI client can load as context — documentation, configuration, or dynamic application data that improves the quality of AI responses.
<?phpnamespace 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); }}
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.
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:
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:
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.
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:
<?phpnamespace 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.
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:
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.
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>:
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.
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.
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:
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...}
Use the interactive mcp:inspector command to debug your server:
# Web serverphp 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.
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.');});