organiseyou / mcp-server
Drop-in MCP (Model Context Protocol) server for Laravel applications
Requires
- php: ^8.2
- laravel/framework: ^11.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
README
A Model Context Protocol (MCP) server for Laravel built around runtime tool registration. Tools are registered programmatically at request time rather than declared upfront as static classes, making this package the right choice when your tool surface is dynamic — multi-tenant platforms, schema-driven APIs, feature-flagged environments, or anything else where you don't know your tools at boot time.
If your tools are static and known at boot time, laravel/mcp (the official first-party package) is likely the better fit.
Requirements
- PHP 8.2+
- Laravel 11+
Installation
composer require organiseyou/mcp-server
The service provider and Mcp facade are registered automatically via Laravel's package discovery.
Publish the config file:
php artisan vendor:publish --tag=mcp-config
Configuration
config/mcp.php:
return [ 'server' => [ 'name' => env('APP_NAME', 'Laravel'), 'version' => '1.0.0', ], 'transport' => [ 'http' => true, // expose POST /mcp 'http_path' => '/mcp', 'http_middleware' => [], // e.g. ['auth:sanctum'] ], 'tools' => [ // App\Mcp\Tools\StaticTool::class, ], ];
Registering Tools
Inline via the Mcp facade (dynamic)
Register tools anywhere that runs before the request is handled — a service provider, middleware, or a tenant boot hook:
use OrganiseYou\McpServer\Facades\Mcp; // Single tool Mcp::tool('add_numbers') ->description('Add two numbers together and return the sum.') ->inputSchema([ 'type' => 'object', 'properties' => [ 'a' => ['type' => 'number', 'description' => 'First operand.'], 'b' => ['type' => 'number', 'description' => 'Second operand.'], ], 'required' => ['a', 'b'], ]) ->handle(fn (array $input) => $input['a'] + $input['b']);
A multi-tenant example where tools are generated from tenant configuration:
// In a tenant boot hook or middleware foreach ($tenant->enabledDataSources() as $source) { Mcp::tool("query_{$source->slug}") ->description("Query the {$source->name} dataset. " . $source->description) ->inputSchema($source->buildInputSchema()) ->handle(fn (array $input) => $source->query($input)); }
Class-based tools (static)
For tools that are the same across all tenants or contexts, implement OrganiseYou\McpServer\Contracts\McpTool and register the class in config/mcp.php:
<?php namespace App\Mcp\Tools; use App\Models\Order; use OrganiseYou\McpServer\Contracts\McpTool; class GetOrderTool implements McpTool { public function __construct(private OrderRepository $orders) {} public function name(): string { return 'get_order'; } public function description(): string { return 'Retrieve an order by its ID, including line items and shipping status.'; } public function inputSchema(): array { return [ 'type' => 'object', 'properties' => [ 'order_id' => [ 'type' => 'integer', 'description' => 'The ID of the order to retrieve.', ], ], 'required' => ['order_id'], ]; } public function __invoke(array $input): mixed { $order = Order::with('items')->findOrFail($input['order_id']); return [ 'id' => $order->id, 'status' => $order->status, 'total' => $order->total, 'items' => $order->items->map(fn ($i) => [ 'sku' => $i->sku, 'quantity' => $i->quantity, ])->all(), ]; } }
// config/mcp.php 'tools' => [ App\Mcp\Tools\GetOrderTool::class, ],
Tools are resolved from the Laravel container, so constructor dependencies are injected automatically.
Transports
HTTP (default)
When mcp.transport.http is true, the package registers a POST /mcp route. Any MCP client that supports HTTP can point to https://your-app.test/mcp.
To secure the endpoint, add middleware in config/mcp.php:
'http_middleware' => ['auth:sanctum'],
Stdio (Claude Desktop / local clients)
Run the artisan command to start an stdio MCP server:
php artisan mcp:serve
Add it to your claude_desktop_config.json:
{
"mcpServers": {
"my-laravel-app": {
"command": "php",
"args": ["/path/to/your/app/artisan", "mcp:serve"]
}
}
}
Protocol
The server speaks JSON-RPC 2.0 and implements the MCP 2024-11-05 protocol version. Supported methods:
| Method | Description |
|---|---|
initialize |
Handshake, returns server info and capabilities |
ping |
Health check |
tools/list |
List all registered tools |
tools/call |
Invoke a tool by name |
Example HTTP request
curl -s -X POST https://your-app.test/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_order",
"description": "Retrieve an order by its ID, including line items and shipping status.",
"inputSchema": {
"type": "object",
"properties": {
"order_id": { "type": "integer", "description": "The ID of the order to retrieve." }
},
"required": ["order_id"]
}
}
]
}
}
Calling a tool:
curl -s -X POST https://your-app.test/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_order","arguments":{"order_id":42}}}'
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\"id\":42,\"status\":\"shipped\",\"total\":99.95,\"items\":[{\"sku\":\"WIDGET-1\",\"quantity\":2}]}"
}
],
"isError": false
}
}
Batch requests are also supported — send an array of JSON-RPC objects and receive an array of responses.
Testing
composer install ./vendor/bin/phpunit
License
MIT