Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel MCP
https://github.com/laravel/mcp
Admin
Laravel MCP enables the rapid development of Model Context Protocol (MCP) servers, allowing AI
...
Tokens:
4,354
Snippets:
21
Trust Score:
9.5
Update:
1 week ago
Context
Skills
Chat
Benchmark
81.1
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel MCP Laravel MCP is a first-party Laravel package that enables rapid development of MCP (Model Context Protocol) servers for Laravel applications. MCP servers allow AI clients like Claude, Cursor, and other AI-powered tools to interact with your Laravel application through the standardized Model Context Protocol, exposing tools, resources, and prompts that AI agents can discover and use. The package provides a fluent, Laravel-style API for defining MCP primitives (Tools, Resources, and Prompts), handling requests with validation and authentication, returning various response types (text, JSON, images, audio), and testing your MCP servers. It supports both HTTP and stdio transports, OAuth 2.0 authentication via Laravel Passport, streaming responses, structured content with JSON schemas, and autocompletion for prompt arguments. ## Creating an MCP Server Create an MCP server class that registers your tools, resources, and prompts. Servers define the capabilities exposed to AI clients. ```php <?php namespace App\Mcp\Servers; use Laravel\Mcp\Server; use Laravel\Mcp\Server\Attributes\Instructions; use Laravel\Mcp\Server\Attributes\Name; use Laravel\Mcp\Server\Attributes\Version; #[Name('My Application Server')] #[Version('1.0.0')] #[Instructions('This MCP server provides tools to interact with the application, including user management and data queries.')] class AppServer extends Server { protected array $tools = [ \App\Mcp\Tools\CreateUserTool::class, \App\Mcp\Tools\SearchOrdersTool::class, ]; protected array $resources = [ \App\Mcp\Resources\SystemConfigResource::class, ]; protected array $prompts = [ \App\Mcp\Prompts\CodeReviewPrompt::class, ]; } ``` ## Registering Server Routes Register your MCP server for HTTP access using the Mcp facade. Place this in your `routes/ai.php` file. ```php <?php use Laravel\Mcp\Facades\Mcp; use App\Mcp\Servers\AppServer; // Register an HTTP endpoint for AI clients Mcp::web('/mcp/app', AppServer::class); // Register a local stdio server for CLI tools Mcp::local('app-server', AppServer::class); // Enable OAuth routes for authentication (requires Laravel Passport) Mcp::oauthRoutes(); ``` ## Creating a Tool Tools are callable functions that AI agents can invoke. Define input schemas using Laravel's JSON Schema builder and return responses. ```php <?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('Creates a new user in the system with the specified details.')] class CreateUserTool extends Tool { public function handle(Request $request): Response { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users,email', 'role' => 'sometimes|string|in:admin,user,editor', ]); $user = \App\Models\User::create([ 'name' => $validated['name'], 'email' => $validated['email'], 'role' => $validated['role'] ?? 'user', 'password' => bcrypt(str()->random(16)), ]); return Response::text("User created successfully with ID: {$user->id}"); } public function schema(JsonSchema $schema): array { return [ 'name' => $schema->string() ->description('The full name of the user') ->required(), 'email' => $schema->string() ->description('The email address for the user account') ->required(), 'role' => $schema->string() ->description('The role to assign: admin, user, or editor') ->enum(['admin', 'user', 'editor']), ]; } } ``` ## Creating a Tool with Output Schema Define an output schema to describe the structured data your tool returns. Use `Response::structured()` for typed responses. ```php <?php namespace App\Mcp\Tools; use Illuminate\Contracts\JsonSchema\JsonSchema; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\ResponseFactory; use Laravel\Mcp\Server\Attributes\Description; use Laravel\Mcp\Server\Tool; #[Description('Retrieves weather data for a specified location.')] class GetWeatherTool extends Tool { public function handle(Request $request): ResponseFactory { $city = $request->get('city'); // Simulate fetching weather data $weatherData = [ 'city' => $city, 'temperature' => 22.5, 'conditions' => 'Partly cloudy', 'humidity' => 65, 'wind_speed' => 12.3, ]; return Response::structured($weatherData); } public function schema(JsonSchema $schema): array { return [ 'city' => $schema->string() ->description('The city name to get weather for') ->required(), ]; } public function outputSchema(JsonSchema $schema): array { return [ 'city' => $schema->string()->description('City name')->required(), 'temperature' => $schema->number()->description('Temperature in Celsius')->required(), 'conditions' => $schema->string()->description('Weather conditions')->required(), 'humidity' => $schema->integer()->description('Humidity percentage'), 'wind_speed' => $schema->number()->description('Wind speed in km/h'), ]; } } ``` ## Creating a Streaming Tool Return a Generator to stream multiple responses with progress notifications. ```php <?php namespace App\Mcp\Tools; use Generator; 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('Processes multiple items with progress updates.')] class BatchProcessTool extends Tool { public function handle(Request $request): Generator { $items = $request->get('items', []); $total = count($items); foreach ($items as $index => $item) { // Process item... sleep(1); // Simulate work $progress = (($index + 1) / $total) * 100; yield Response::notification('stream/progress', [ 'progress' => $progress, 'message' => "Processing item " . ($index + 1) . " of {$total}: {$item}", ]); } yield Response::text("Successfully processed {$total} items."); } public function schema(JsonSchema $schema): array { return [ 'items' => $schema->array() ->items($schema->string()) ->description('List of items to process') ->required(), ]; } } ``` ## Creating a Resource Resources expose data to AI clients. Define a URI and return content when the resource is read. ```php <?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\Attributes\Uri; use Laravel\Mcp\Server\Resource; #[Description('Current system configuration and status information.')] #[Uri('config://system/status')] #[MimeType('application/json')] class SystemConfigResource extends Resource { public function handle(Request $request): Response { $config = [ 'app_name' => config('app.name'), 'environment' => config('app.env'), 'debug_mode' => config('app.debug'), 'php_version' => PHP_VERSION, 'laravel_version' => app()->version(), 'database_connection' => config('database.default'), 'cache_driver' => config('cache.default'), 'queue_connection' => config('queue.default'), ]; return Response::json($config); } } ``` ## Creating a Resource Template Resource templates use URI patterns with placeholders to expose dynamic content. ```php <?php namespace App\Mcp\Resources; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Attributes\Description; use Laravel\Mcp\Server\Contracts\HasUriTemplate; use Laravel\Mcp\Server\Resource; use Laravel\Mcp\Support\UriTemplate; #[Description('Access user profile data by user ID.')] class UserProfileResource extends Resource implements HasUriTemplate { protected string $mimeType = 'application/json'; public function uriTemplate(): UriTemplate { return new UriTemplate('users://{userId}/profile'); } public function handle(Request $request): Response { $userId = $request->get('userId'); $user = \App\Models\User::findOrFail($userId); return Response::json([ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'created_at' => $user->created_at->toISOString(), 'orders_count' => $user->orders()->count(), ]); } } ``` ## Creating a Prompt Prompts provide reusable instructions or templates for AI agents. Define arguments that can be passed when the prompt is invoked. ```php <?php namespace App\Mcp\Prompts; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Attributes\Description; use Laravel\Mcp\Server\Prompt; use Laravel\Mcp\Server\Prompts\Argument; #[Description('Instructions for performing a thorough code review.')] class CodeReviewPrompt extends Prompt { public function arguments(): array { return [ new Argument( name: 'language', description: 'The programming language of the code', required: true ), new Argument( name: 'focus_areas', description: 'Specific areas to focus on (security, performance, style)', required: false ), ]; } public function handle(Request $request): Response { $language = $request->get('language'); $focusAreas = $request->get('focus_areas', 'general best practices'); $instructions = <<<PROMPT You are reviewing {$language} code. Focus on: {$focusAreas}. Please check for: 1. Code correctness and logic errors 2. Security vulnerabilities 3. Performance optimizations 4. Code style and readability 5. Proper error handling 6. Test coverage suggestions Provide specific line numbers and concrete suggestions for improvements. PROMPT; return Response::text($instructions); } } ``` ## Adding Autocompletion to Prompts Implement the Completable interface to provide argument autocompletion suggestions. ```php <?php namespace App\Mcp\Prompts; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Attributes\Description; use Laravel\Mcp\Server\Completions\CompletionHelper; use Laravel\Mcp\Server\Completions\CompletionResponse; use Laravel\Mcp\Server\Contracts\Completable; use Laravel\Mcp\Server\Prompt; use Laravel\Mcp\Server\Prompts\Argument; #[Description('Generate a report for a specific project and time period.')] class ProjectReportPrompt extends Prompt implements Completable { public function arguments(): array { return [ new Argument('project', 'Project identifier', required: true), new Argument('period', 'Time period for the report', required: true), ]; } public function complete(string $argument, string $value, array $context): CompletionResponse { return match ($argument) { 'project' => $this->completeProject($value), 'period' => CompletionResponse::match([ 'last-week', 'last-month', 'last-quarter', 'year-to-date', 'all-time', ]), default => CompletionResponse::empty(), }; } protected function completeProject(string $value): CompletionResponse { $projects = \App\Models\Project::pluck('slug')->toArray(); $filtered = CompletionHelper::filterByPrefix($projects, $value); return CompletionResponse::match($filtered); } public function handle(Request $request): Response { $project = $request->get('project'); $period = $request->get('period'); return Response::text("Generate a comprehensive report for project '{$project}' covering the '{$period}' period."); } } ``` ## Response Types The Response class provides multiple factory methods for different content types. ```php <?php use Laravel\Mcp\Response; // Plain text response $text = Response::text('Hello, this is a text response.'); // Error response (marked as error in MCP protocol) $error = Response::error('Something went wrong: invalid input provided.'); // JSON response (automatically serialized) $json = Response::json(['status' => 'success', 'count' => 42]); // Structured response with schema validation $structured = Response::structured([ 'user_id' => 123, 'name' => 'John Doe', 'active' => true, ]); // Binary blob response $blob = Response::blob(file_get_contents('/path/to/file.bin')); // Image response (base64 encoded) $image = Response::image( data: file_get_contents('/path/to/image.png'), mimeType: 'image/png' ); // Audio response (base64 encoded) $audio = Response::audio( data: file_get_contents('/path/to/audio.wav'), mimeType: 'audio/wav' ); // Load from Laravel storage $fromStorage = Response::fromStorage('images/photo.jpg', 's3'); // Multiple responses combined $multiple = Response::make([ Response::text('First part of the response.'), Response::text('Second part of the response.'), ]); // Response with metadata $withMeta = Response::text('Result data') ->withMeta('processingTime', '150ms') ->withMeta(['cached' => true, 'source' => 'database']); ``` ## Request Handling The Request class provides methods to access and validate input arguments. ```php <?php use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; class ExampleTool extends Tool { public function handle(Request $request): Response { // Get a single value with default $name = $request->get('name', 'Guest'); // Get typed values $count = $request->integer('count', 10); $enabled = $request->boolean('enabled', false); $date = $request->date('created_at'); // Get all arguments $all = $request->all(); // Get specific keys only $subset = $request->all(['name', 'email']); // Validate with Laravel validation rules $validated = $request->validate([ 'name' => 'required|string|max:100', 'email' => 'required|email', 'age' => 'nullable|integer|min:0|max:150', ]); // Access authenticated user (requires OAuth setup) $user = $request->user(); // Access session ID $sessionId = $request->sessionId(); // Access request metadata $meta = $request->meta(); // For resources with URI templates $uri = $request->uri(); return Response::text("Processed request for: {$name}"); } } ``` ## Conditional Tool Registration Use the `shouldRegister` method to conditionally register tools based on runtime conditions. ```php <?php namespace App\Mcp\Tools; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Attributes\Description; use Laravel\Mcp\Server\Tool; #[Description('Administrative tool for managing system settings.')] class AdminSettingsTool extends Tool { public function shouldRegister(Request $request): bool { // Only register for authenticated admin users $user = $request->user(); return $user && $user->hasRole('admin'); } public function handle(Request $request): Response { $setting = $request->get('setting'); $value = $request->get('value'); config([$setting => $value]); return Response::text("Setting '{$setting}' updated to '{$value}'."); } } ``` ## Testing MCP Servers Laravel MCP provides a fluent testing API for verifying tools, resources, and prompts. ```php <?php use App\Mcp\Servers\AppServer; use App\Mcp\Tools\CreateUserTool; use App\Mcp\Prompts\CodeReviewPrompt; use App\Mcp\Resources\UserProfileResource; use App\Models\User; // Test a tool with arguments it('creates a user successfully', function () { AppServer::tool(CreateUserTool::class, [ 'name' => 'John Doe', 'email' => 'john@example.com', 'role' => 'editor', ]) ->assertOk() ->assertSee('User created successfully') ->assertDontSee('error'); }); // Test validation errors it('validates required fields', function () { AppServer::tool(CreateUserTool::class, [ 'email' => 'invalid-email', ]) ->assertHasErrors(['name', 'email']); }); // Test structured content it('returns weather data with correct structure', function () { AppServer::tool(GetWeatherTool::class, ['city' => 'London']) ->assertOk() ->assertStructuredContent([ 'city' => 'London', 'temperature' => 22.5, 'conditions' => 'Partly cloudy', ]); }); // Test with authenticated user it('allows admin access', function () { $admin = User::factory()->create(['role' => 'admin']); AppServer::actingAs($admin) ->tool(AdminSettingsTool::class, ['setting' => 'app.name', 'value' => 'New Name']) ->assertOk() ->assertAuthenticated() ->assertAuthenticatedAs($admin); }); // Test a prompt it('generates code review instructions', function () { AppServer::prompt(CodeReviewPrompt::class, [ 'language' => 'PHP', 'focus_areas' => 'security', ]) ->assertOk() ->assertSee('PHP') ->assertSee('security') ->assertName('code-review-prompt') ->assertDescription('Instructions for performing a thorough code review.'); }); // Test a resource it('returns user profile data', function () { $user = User::factory()->create(); AppServer::resource(UserProfileResource::class, ['userId' => $user->id]) ->assertOk() ->assertSee($user->name) ->assertSee($user->email); }); // Test completions it('provides project completions', function () { AppServer::completion(ProjectReportPrompt::class, 'project', 'web') ->assertHasCompletions(['web-app', 'web-api']) ->assertCompletionCount(2); }); // Test notifications in streaming responses it('sends progress notifications', function () { AppServer::tool(BatchProcessTool::class, ['items' => ['a', 'b', 'c']]) ->assertOk() ->assertNotificationCount(3) ->assertSentNotification('stream/progress', ['progress' => 100]); }); ``` ## Artisan Commands Generate MCP components using Artisan commands. ```bash # Create a new MCP server php artisan make:mcp-server AppServer # Create a new tool php artisan make:mcp-tool CreateUserTool # Create a new resource php artisan make:mcp-resource UserProfileResource # Create a new prompt php artisan make:mcp-prompt CodeReviewPrompt # Start a local stdio server php artisan mcp:start app-server # Launch MCP Inspector for debugging php artisan mcp:inspector php artisan mcp:inspector --host=127.0.0.1 --port=8080 ``` ## Configuration Publish and customize the MCP configuration file. ```php <?php // config/mcp.php return [ /* |-------------------------------------------------------------------------- | Redirect Domains |-------------------------------------------------------------------------- | | These domains are permitted for OAuth client redirect URIs. | Use "*" to allow all domains (not recommended for production). | */ 'redirect_domains' => [ 'https://your-app.com', 'http://localhost', // '*', // Allow all (development only) ], ]; ``` ## Summary Laravel MCP enables seamless integration between AI agents and Laravel applications through the Model Context Protocol. The primary use cases include exposing application functionality as callable tools (database operations, API integrations, business logic), providing contextual resources (configuration, documentation, user data), and defining reusable prompts with argument completion for AI interactions. The package follows Laravel conventions with attribute-based configuration, dependency injection, and comprehensive testing utilities. Integration patterns typically involve creating a central MCP server class that registers domain-specific tools, resources, and prompts, then exposing it via HTTP endpoints or stdio for different AI clients. For production deployments, OAuth authentication via Laravel Passport secures access, while the testing API ensures reliability. The streaming response support enables long-running operations with progress feedback, and structured content with output schemas provides type-safe data exchange between AI agents and your application.