Seamlessly integrate the Model Context Protocol (MCP) into your Laravel applications.
This package is a Laravel compatible wrapper for the powerful php-mcp/server
library. It allows you to effortlessly expose parts of your Laravel application as MCP Tools, Resources, and Prompts, enabling standardized communication with AI assistants like Anthropic's Claude, Cursor IDE, and others.
Key Features:
- Effortless Integration: Designed from the ground up for Laravel, leveraging its service container, configuration, caching, logging, and Artisan console.
- Fluent Element Definition: Define MCP elements programmatically with a clean, Laravely API using the
Mcp
Facade (e.g.,Mcp::tool(...)->description(...)
). - Attribute-Based Discovery: Alternatively, use PHP 8 attributes (
#[McpTool]
, etc.) on your classes and methods, then run a simple Artisan command to discover and cache them. - Flexible Transports:
- Integrated HTTP+SSE: Serve MCP requests directly through your Laravel application's routes, ideal for many setups.
- Dedicated HTTP+SSE Server: Launch a high-performance, standalone ReactPHP-based HTTP server via an Artisan command for demanding scenarios.
- STDIO: Run an MCP server over standard input/output, perfect for CLI-driven clients.
- Robust Configuration: Manage all aspects of your MCP server via the
config/mcp.php
file. - Artisan Commands: Includes commands for serving, discovering elements, and listing registered components.
- Event-Driven Updates: Integrates with Laravel's event system to notify clients of dynamic changes to your MCP elements.
This package utilizes php-mcp/server
v2.1.0+ which supports the 2024-11-05
version of the Model Context Protocol.
- PHP >= 8.1
- Laravel >= 10.0
php-mcp/server
^2.1.0 (automatically installed)
-
Require the Package:
composer require php-mcp/laravel
-
Publish Configuration:
php artisan vendor:publish --provider="PhpMcp\Laravel\Server\McpServiceProvider" --tag="mcp-config"
All MCP server settings are managed in config/mcp.php
. Here are the key sections:
server
: Basic server identity settingsname
: Your MCP server's name (default: 'Laravel MCP')version
: Server version numberinstructions
: Optional initialization instructions for clients
discovery
: Controls how MCP elements are discoveredbase_path
: Root directory for scanning (defaults tobase_path()
)directories
: Paths to scan for MCP attributes (default:['app/Mcp']
)exclude_dirs
: Directories to skip during scans (e.g., 'vendor', 'tests', etc.)definitions_file
: Path to manual element definitions (default:routes/mcp.php
)auto_discover
: Enable automatic discovery in development (default:true
)save_to_cache
: Cache discovery results (default:true
)
transports
: Available communication methodsstdio
: CLI-based transportenabled
: Enable themcp:serve
command withstdio
option.
http_dedicated
: Standalone HTTP serverenabled
: Enable themcp:serve
command withhttp
option.host
,port
,path_prefix
settings
http_integrated
: Laravel route-based serverenabled
: Serve through Laravel routesroute_prefix
: URL prefix (default: 'mcp')middleware
: Applied middleware (default: 'web')
cache
: Caching configurationstore
: Laravel cache store to usettl
: Cache lifetime in seconds
pagination_limit
: Maximum items returned in list operations
capabilities
: Toggle MCP features- Enable/disable tools, resources, prompts
- Control subscriptions and change notifications
logging
: Server logging configurationchannel
: Laravel log channellevel
: Default log level
Review the published config/mcp.php
file for detailed documentation of all available options and their descriptions.
PHP MCP Laravel provides two approaches to define your MCP elements: manual registration using a fluent API or attribute-based discovery.
The recommended approach is using the fluent Mcp
facade to manually register your elements in routes/mcp.php
(this path can be changed in config/mcp.php via the discovery.definitions_file key).
Mcp::tool([MyHandlers::class, 'calculateSum']);
Mcp::resource( 'status://app/health', [MyHandlers::class, 'getAppStatus']);
Mcp::prompt(MyInvokableTool::class);
Mcp::resourceTemplate('user://{id}/data', [MyHandlers::class, 'getUserData']);
The facade provides several registration methods, each with optional fluent configuration methods:
Defines an action or function the MCP client can invoke. Register a tool by providing either:
- Just the handler:
Mcp::tool(MyTool::class)
- Name and handler:
Mcp::tool('my_tool', [MyClass::class, 'method'])
Available configuration methods:
name()
: Override the inferred namedescription()
: Set a custom description
Defines a specific, static piece of content or data identified by a URI. Register a resource by providing:
$uri
(required
): The unique URI for this resource instance (e.g.,config://app/settings
).$handler
: The handler that will return the resource's content.
Available configuration methods:
name(string $name): self
: Sets a human-readable name. Inferred if omitted.description(string $description): self
: Sets a description. Inferred if omitted.mimeType(string $mimeType): self
: Specifies the resource's MIME type. Can sometimes be inferred from the handler's return type or content.size(?int $size): self
: Specifies the resource size in bytes, if known.annotations(array $annotations): self
: Adds MCP-standard annotations (e.g., ['audience' => ['user']]).
Defines a handler for resource URIs that contain variable parts, allowing dynamic resource instance generation. Register a resource template by providing:
$uriTemplate
(required
): The URI template string (RFC 6570
), e.g.,user://{userId}/profile
.$handler
: The handler method. Its parameters must match the variables in the$uriTemplate
.
Available configuration methods:
name(string $name): self
: Sets a human-readable name for the template type.description(string $description): self
: Sets a description for the template.mimeType(string $mimeType): self
: Sets a default MIME type for resources resolved by this template.annotations(array $annotations): self
: Adds MCP-standard annotations.
Defines a generator for MCP prompt messages, often used to construct conversations for an LLM. Register a prompt by providing just the handler, or the name and handler.
$name
(optional
): The MCP prompt name. Inferred if omitted.$handler
: The handler method. Its parameters become the prompt's input arguments.
The package automatically resolves handlers through Laravel's service container, allowing you to inject dependencies through constructor injection. Each registration method accepts either an invokable class or a [class, method]
array.
The fluent methods like description()
, name()
, and mimeType()
are optional. When omitted, the package intelligently infers these values from your handler's method signatures, return types, and DocBlocks. Use these methods only when you need to override the automatically generated metadata.
Manually registered elements are always available regardless of cache status and take precedence over discovered elements with the same identifier.
As an alternative, you can use PHP 8 attributes to mark your methods or invokable classes as MCP elements. That way, you don't have to manually register them in the definitions file:
namespace App\Mcp;
use PhpMcp\Server\Attributes\McpTool;
use PhpMcp\Server\Attributes\McpResource;
class DiscoveredElements
{
#[McpTool(name: 'echo_discovered')]
public function echoMessage(string $message): string
{
return "Discovered echo: {$message}";
}
#[McpResource(uri: 'status://server/health', mimeType: 'application/json')]
public function getServerHealth(): array
{
return ['status' => 'healthy', 'uptime' => 123];
}
}
When auto_discover
enabled in your config, these elements are automatically discovered when needed. For production or to manually trigger discovery, run:
php artisan mcp:discover
This command scans the configured directories, registers the discovered elements, and caches the results for improved performance. Use the --no-cache
flag to skip caching or --force
to perform a fresh scan regardless of cache status.
See the php-mcp/server
documentation for detailed information on attribute parameters and return value formatting.
PHP MCP Laravel offers three transport options to serve your MCP elements.
The most convenient option for getting started is serving MCP directly through your Laravel application's routes:
// Client connects to: http://your-app.test/mcp/sse
// No additional processes needed
Configuration:
- Ensure
mcp.transports.http_integrated.enabled
istrue
in your config - The package registers routes at
/mcp/sse
(GET) and/mcp/message
(POST) by default - You can customize the prefix, middleware, and domain in
config/mcp.php
CSRF Protection: You must exclude the MCP message endpoint from CSRF verification:
For Laravel 11+:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
config('mcp.transports.http_integrated.route_prefix') . '/message',
]);
})
For Laravel 10 and below:
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'mcp/message', // Adjust if you changed the route prefix
];
Server Environment Considerations: Standard synchronous servers like PHP's built-in server or basic PHP-FPM setups can struggle with SSE connections. For eg, a single PHP-FPM worker will be tied up for each active SSE connection. For production, consider using Laravel Octane with Swoole/RoadRunner or properly configured Nginx with sufficient PHP-FPM workers.
For production environments or high-traffic applications, the dedicated HTTP server provides better performance and isolation:
php artisan mcp:serve --transport=http
This launches a standalone ReactPHP-based HTTP server specifically for MCP traffic:
Configuration:
- Ensure
mcp.transports.http_dedicated.enabled
istrue
in your config - Default server listens on
127.0.0.1:8090
with path prefix/mcp
- Configure through any of these methods:
- Environment variables:
MCP_HTTP_DEDICATED_HOST
,MCP_HTTP_DEDICATED_PORT
,MCP_HTTP_DEDICATED_PATH_PREFIX
- Edit values directly in
config/mcp.php
- Override at runtime:
--host=0.0.0.0 --port=8091 --path-prefix=custom_mcp
- Environment variables:
This is a blocking, long-running process that should be managed with Supervisor, systemd, or Docker in production environments.
Ideal for Cursor IDE and other MCP clients that directly launch server processes:
php artisan mcp:serve
# or explicitly:
php artisan mcp:serve --transport=stdio
Client Configuration: Configure your MCP client to execute this command directly. For example, in Cursor:
// .cursor/mcp.json
{
"mcpServers": {
"my-laravel-stdio": {
"command": "php",
"args": [
"/full/path/to/your/laravel/project/artisan",
"mcp:serve"
]
}
}
}
Important: When using STDIO transport, your handler code must not write to STDOUT using echo, print, or similar functions. Use Laravel's logger or STDERR for any debugging output.
To see which MCP elements your server has registered (both manual and discovered/cached):
php artisan mcp:list
# Specific types:
php artisan mcp:list tools
php artisan mcp:list resources
# JSON output:
php artisan mcp:list --json
If your available MCP elements or resource content change while the server is running, you can notify connected clients (most relevant for HTTP transports).
-
List Changes (Tools, Resources, Prompts): Dispatch the corresponding Laravel event. The package includes listeners to send the appropriate MCP notification.
use PhpMcp\Laravel\Server\Events\ToolsListChanged; use PhpMcp\Laravel\Server\Events\ResourcesListChanged; use PhpMcp\Laravel\Server\Events\PromptsListChanged; ToolsListChanged::dispatch(); // ResourcesListChanged::dispatch(); // PromptsListChanged::dispatch();
-
Specific Resource Content Update: Dispatch the
PhpMcp\Laravel\Server\Events\ResourceUpdated
event with the URI of the changed resource.use PhpMcp\Laravel\Server\Events\ResourceUpdated; $resourceUri = 'file:///path/to/updated_file.txt'; // ... your logic that updates the resource ... ResourceUpdated::dispatch($resourceUri);
The
McpNotificationListener
will handle sending thenotifications/resource/updated
MCP notification to clients subscribed to that URI.
For your application tests, you can mock the Mcp
facade or specific MCP handlers as needed. When testing MCP functionality itself, consider integration tests that make HTTP requests to your integrated MCP endpoints (if used) or command tests for Artisan commands.
Please see CONTRIBUTING.md in the main php-mcp/server
repository for general contribution guidelines. For issues or PRs specific to this Laravel package, please use this repository's issue tracker.
The MIT License (MIT). See LICENSE.