laravel/mcp
Build MCP servers in Laravel so AI clients can securely interact with your app via the Model Context Protocol. Quick setup, Laravel-native conventions, and official Laravel documentation support for exposing tools, resources, and prompts to MCP-compatible clients.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require laravel/mcp
Publish the MCP configuration and migration:
php artisan vendor:publish --provider="Laravel\MCP\MCPServiceProvider" --tag="mcp-config"
php artisan migrate
Configure MCP:
Edit config/mcp.php to define your server's metadata (e.g., name, description, protocolVersion). Example:
'servers' => [
'primary' => [
'name' => 'My Laravel App',
'description' => 'A Laravel-powered MCP server',
'protocolVersion' => '2025-11-25',
'icon' => 'https://example.com/icon.png',
],
],
Define a Resource:
Create a resource using the McpResource macro or trait. Example:
use Laravel\MCP\Resources\Resource;
use Laravel\MCP\Resources\Annotations\Resource as ResourceAnnotation;
#[ResourceAnnotation(
name: 'user',
description: 'User profile resource',
outputSchema: 'user-schema.json'
)]
class UserResource extends Resource
{
public function toArray($request)
{
return [
'id' => auth()->id(),
'name' => auth()->user()->name,
];
}
}
Register the Resource: Bind the resource in a service provider or boot method:
MCP::resource('users', UserResource::class);
Run the MCP Server: Start the MCP server with:
php artisan mcp:serve
Or integrate it into your existing Laravel routes:
MCP::routes();
First Use Case:
Test the server using the mcp:inspect command:
php artisan mcp:inspect
This generates an OpenAPI/Swagger spec for your MCP server, which can be shared with AI clients.
#[Resource] annotations to define metadata (name, description, output schema). Example:
#[Resource(
name: 'invoice',
description: 'Invoice details',
outputSchema: 'invoice-schema.json',
icon: 'https://example.com/invoice-icon.png'
)]
class InvoiceResource extends Resource { ... }
toArray or toJson methods:
public function toArray($request)
{
return Invoice::find($request->invoice_id)->toArray();
}
/invoices/{invoice_id}). Resolve variables in the resource:
public function resolve($request, $invoice_id)
{
return Invoice::findOrFail($invoice_id);
}
MCP::tool('send_email', function (ToolRequest $request) {
Mail::to($request->to)->send(new Email($request->subject, $request->body));
return response()->structured(['status' => 'sent']);
});
assertStructuredContent to validate tool inputs:
MCP::tool('update_user', function (ToolRequest $request) {
MCP::assertStructuredContent($request, [
'type' => 'object',
'properties' => [
'name' => ['type' => 'string'],
'email' => ['type' => 'string', 'format' => 'email'],
],
'required' => ['name', 'email'],
]);
// Proceed with update logic
});
MCP::oauthClientRegistration(function (OAuthRegisterRequest $request) {
$client = Client::create([
'name' => $request->name,
'redirect' => $request->redirect_uri,
'personal_access_client' => true,
]);
return response()->json([
'client_id' => $client->id,
'client_secret' => $client->secret,
'redirect_uri' => $client->redirect,
]);
});
MCP::scope('read:invoices', 'Read invoice data');
MCP::scope('write:invoices', 'Create/update invoices');
Role::Assistant, Role::User) to gate resources/tools:
#[Resource(
name: 'admin_dashboard',
roles: [Role::User]
)]
class AdminDashboardResource extends Resource { ... }
public function toJson($request)
{
return response()->structured([
'type' => 'object',
'properties' => [
'id' => ['type' => 'string'],
'name' => ['type' => 'string'],
],
]);
}
use Laravel\MCP\Resources\ContentTypes\Image;
#[Resource(
name: 'profile_picture',
contentType: Image::class
)]
class ProfilePictureResource extends Resource
{
public function toResponse($request)
{
return response()->file(storage_path('app/public/profile.jpg'));
}
}
MCP::testResource to validate resources:
public function test_user_resource()
{
MCP::testResource('users')
->assertName('user')
->assertOutputSchema('user-schema.json')
->assertSee('name');
}
public function test_send_email_tool()
{
MCP::testTool('send_email')
->withInput(['to' => 'user@example.com', 'subject' => 'Test'])
->assertStatus(200)
->assertStructured(['status' => 'sent']);
}
SessionInitialized):
MCP::onSessionInitialized(function (SessionInitialized $event) {
\Log::info('New MCP session started', ['session_id' => $event->sessionId]);
});
MCP::onToolExecuted(function (ToolExecuted $event) {
\Log::info('Tool executed', [
'tool' => $event->tool,
'input' => $event->input,
]);
});
MCP::tool('process_order', function (ToolRequest $request) {
ProcessOrderJob::dispatch($request->order_id);
return response()->structured(['status' => 'queued']);
});
MCP::tool('notify_user', function (ToolRequest $request) {
Notification::send($request->user, new UserNotification($request->message));
return response()->structured(['status' => 'sent']);
});
public function toArray($request)
{
event(new InvoiceRetrieved($this->invoice));
return $this->invoice->toArray();
}
Route::middleware('api')->group(function () {
MCP::routes(); // MCP routes
Route::get('/api/v1/users', [UserController::class, 'index']); // REST route
});
MCP::tool('rate_limited_tool', function (ToolRequest $request) {
// Tool logic
})->middleware('throttle:10,1');
#[Resource(
name: 'dashboard',
contentType: 'text/html',
uiApp: true
)]
class DashboardResource extends Resource
{
public function toResponse($request)
{
return view('mcp.dashboard');
}
}
How can I help you explore Laravel packages today?