opgginc / laravel-mcp-server
This is my package laravel-mcp-server
Installs: 35 244
Dependents: 0
Suggesters: 0
Security: 0
Stars: 329
Watchers: 7
Forks: 31
Open Issues: 3
pkg:composer/opgginc/laravel-mcp-server
Requires
- php: ^8.2
- illuminate/contracts: ^9.0||^10.0||^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9||^3.0
- laravel/lumen-framework: ^9.0||^10.0||^11.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.3||^2.0
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
- spatie/laravel-ray: ^1.41
- dev-main
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.5.5
- v1.5.4
- v1.5.3
- v1.5.2
- v1.5.1
- v1.5.0
- 1.4.2
- 1.4.0
- 1.3.4
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.1
- 1.2.0
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.5
- 1.0.4
- 1.0.3
- v1.0.2
- 1.0.1
- 1.0.0
- dev-develop
- dev-feat/add-json-schema
- dev-ref/chagne-protocol-version
- dev-fix/builder-method
- dev-fix/route-cache
- dev-feat/routing-driven
- dev-dependabot/github_actions/actions/checkout-6
- dev-dependabot/github_actions/actions/setup-node-6
- dev-dependabot/github_actions/stefanzweifel/git-auto-commit-action-7
This package is auto-updated.
Last update: 2026-02-24 17:18:53 UTC
README
Build a route-first MCP server in Laravel and Lumen
English | Português do Brasil | 한국어 | Русский | 简体中文 | 繁體中文 | Polski | Español
Breaking Changes 2.0.0
- Endpoint setup moved from config-driven registration to route-driven registration.
- Streamable HTTP is the only supported transport.
- Server metadata mutators are consolidated into
setServerInfo(...). - Legacy tool transport methods were removed from runtime (
messageType(),ProcessMessageType::SSE).
Full migration guide: docs/migrations/v2.0.0-migration.md
Overview
Laravel MCP Server provides route-based MCP endpoint registration for Laravel and Lumen.
Key points:
- Streamable HTTP transport
- Route-first configuration (
Route::mcp(...)/McpRoute::register(...)) - Tool, resource, resource template, and prompt registration per endpoint
- Route cache compatible endpoint metadata
Requirements
- PHP >= 8.2
- Laravel (Illuminate) >= 9.x
- Lumen >= 9.x (optional)
Quick Start
1) Install
composer require opgginc/laravel-mcp-server
2) Register an endpoint (Laravel)
use Illuminate\Support\Facades\Route; use OPGG\LaravelMcpServer\Enums\ProtocolVersion; use OPGG\LaravelMcpServer\Services\ToolService\Examples\HelloWorldTool; use OPGG\LaravelMcpServer\Services\ToolService\Examples\VersionCheckTool; Route::mcp('/mcp') ->setServerInfo( name: 'OP.GG MCP Server', version: '2.0.0', ) ->setConfig( compactEnumExampleCount: 3, ) ->setProtocolVersion(ProtocolVersion::V2025_11_25) ->enabledApi() ->tools([ HelloWorldTool::class, VersionCheckTool::class, ]);
If you need compatibility with clients that do not support 2025-11-25, set:
->setProtocolVersion(ProtocolVersion::V2025_06_18)
3) Verify
php artisan route:list | grep mcp
php artisan mcp:test-tool --list --endpoint=/mcp
Quick JSON-RPC check:
curl -X POST http://localhost:8000/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Lumen Setup
// bootstrap/app.php $app->withFacades(); $app->withEloquent(); $app->register(OPGG\LaravelMcpServer\LaravelMcpServerServiceProvider::class);
use OPGG\LaravelMcpServer\Routing\McpRoute; use OPGG\LaravelMcpServer\Services\ToolService\Examples\HelloWorldTool; McpRoute::register('/mcp') ->setServerInfo( name: 'OP.GG MCP Server', version: '2.0.0', ) ->tools([ HelloWorldTool::class, ]);
Minimal Security (Production)
Use Laravel middleware on your MCP route group.
use Illuminate\Support\Facades\Route; Route::middleware([ 'auth:sanctum', 'throttle:100,1', ])->group(function (): void { Route::mcp('/mcp') ->setServerInfo( name: 'Secure MCP', version: '2.0.0', ) ->tools([ \App\MCP\Tools\MyCustomTool::class, ]); });
v2.0.0 Migration Notes (from v1.0.0)
- MCP endpoint setup moved from config to route registration.
- Streamable HTTP is the only transport.
- Server metadata mutators are consolidated into
setServerInfo(...). - Tool migration command is available for legacy signatures:
php artisan mcp:migrate-tools
Full guide: docs/migrations/v2.0.0-migration.md
Advanced Features (Quick Links)
- Create tools:
php artisan make:mcp-tool ToolName - Create resources:
php artisan make:mcp-resource ResourceName - Create resource templates:
php artisan make:mcp-resource-template TemplateName - Create prompts:
php artisan make:mcp-prompt PromptName - Create notifications:
php artisan make:mcp-notification HandlerName --method=notifications/method - Generate from OpenAPI:
php artisan make:swagger-mcp-tool <spec-url-or-file> - Export tools to OpenAPI:
php artisan mcp:export-openapi --output=storage/api-docs-mcp/api-docs.json
Code references:
- Tool examples:
src/Services/ToolService/Examples/ - Resource examples:
src/Services/ResourceService/Examples/ - Prompt service:
src/Services/PromptService/ - Notification handlers:
src/Server/Notification/ - Route builder:
src/Routing/McpRouteBuilder.php
Swagger/OpenAPI -> MCP Tool
Generate MCP tools from a Swagger/OpenAPI spec:
# From URL php artisan make:swagger-mcp-tool https://api.example.com/openapi.json # From local file php artisan make:swagger-mcp-tool ./specs/openapi.json
Useful options:
php artisan make:swagger-mcp-tool ./specs/openapi.json \ --group-by=tag \ --prefix=Billing \ --test-api
--group-by:tag,path, ornone--prefix: class-name prefix for generated tools/resources--test-api: test endpoint connectivity before generation
Generation behavior:
- In interactive mode, you can choose Tool or Resource per endpoint.
- In non-interactive mode,
GETendpoints are generated as Resources and other methods as Tools.
Enhanced Interactive Preview
If you run the command without --group-by, the generator shows an interactive preview of folder structure and file counts before creation.
php artisan make:swagger-mcp-tool ./specs/openapi.json
Example preview output:
Choose how to organize your generated tools and resources:
Tag-based grouping (organize by OpenAPI tags)
Total: 25 endpoints -> 15 tools + 10 resources
Examples: Tools/Pet, Tools/Store, Tools/User
Path-based grouping (organize by API path)
Total: 25 endpoints -> 15 tools + 10 resources
Examples: Tools/Api, Tools/Users, Tools/Orders
No grouping (everything in root folder)
Total: 25 endpoints -> 15 tools + 10 resources
Examples: Tools/, Resources/
After generation, register generated tool classes on your MCP endpoint:
use Illuminate\Support\Facades\Route; Route::mcp('/mcp') ->setServerInfo( name: 'Generated MCP Server', version: '2.0.0', ) ->tools([ \App\MCP\Tools\Billing\CreateInvoiceTool::class, \App\MCP\Tools\Billing\UpdateInvoiceTool::class, ]);
MCP Tools -> OpenAPI Export
Export all registered ToolInterface classes (via Route::mcp(...)->tools([...])) to an OpenAPI JSON document using each tool's inputSchema().
Only endpoints configured with ->enabledApi() are included in this export and exposed through POST /tools/{tool_name}.
Operations are grouped by endpoint name using OpenAPI tags.
If multiple endpoints register the same tool name, the operation keeps first-registration behavior and merges all matching endpoint names into tags.
If route registration is missing, the command auto-discovers tools under default paths: app/MCP/Tools and app/Tools.
# Default output: storage/api-docs-mcp/api-docs.json php artisan mcp:export-openapi # Custom output + metadata php artisan mcp:export-openapi \ --output=storage/app/mcp.openapi.json \ --title="MCP Tools API" \ --api-version=2.1.0 # Limit to one endpoint (id or path) php artisan mcp:export-openapi --endpoint=/mcp # Discover tools from additional directory paths php artisan mcp:export-openapi --discover-path=app/MCP/Tools # Existing output is overwritten by default php artisan mcp:export-openapi
Enable Tool API route generation:
use Illuminate\Support\Facades\Route; Route::mcp('/mcp') ->setServerInfo(name: 'OP.GG MCP Server', version: '2.0.0') ->enabledApi() ->tools([ \App\MCP\Tools\GreetingTool::class, ]);
Swagger UI testing tip:
- Exported operations use
query parametersonly (norequestBody) for simpler manual testing. - Required fields from each tool
inputSchema().requiredare reflected in Swagger parameter validation. - Enum fields are exported with
schema.enumso Swagger renders dropdown selections. - Array fields are exported with
style=form+explode=true(repeat key format, e.g.desired_output_fields=items&desired_output_fields=runes). /tools/{tool_name}argument parsing prefers query parameters over body/form payloads to avoid Swagger conflicts.- Enum fields without explicit
default/exampleare auto-filled from the first enum value (or first non-null enum value). - String fields with descriptions like
e.g., en_US, ko_KR, ja_JPauto-infer first sample value asdefaultandexample.
Example Tool Class
<?php namespace App\MCP\Tools; use App\Enums\Platform; use OPGG\LaravelMcpServer\JsonSchema\JsonSchema; use OPGG\LaravelMcpServer\Services\ToolService\ToolInterface; class GreetingTool implements ToolInterface { public function name(): string { return 'greeting-tool'; } public function description(): string { return 'Return a greeting message.'; } public function inputSchema(): array { return [ 'name' => JsonSchema::string() ->description('Developer Name') ->required(), 'platform' => JsonSchema::string() ->enum(Platform::class) ->description('Client platform') ->compact(), ]; } public function annotations(): array { return [ 'readOnlyHint' => true, 'destructiveHint' => false, ]; } public function execute(array $arguments): mixed { return [ 'message' => 'Hello '.$arguments['name'], ]; } }
JsonSchema Builder (Laravel-Style)
This package provides its own JsonSchema builder under the OPGG\LaravelMcpServer namespace.
You can define tool schemas in a Laravel 12-style fluent format while keeping inputSchema(): array.
<?php namespace App\MCP\Tools; use App\Enums\Platform; use OPGG\LaravelMcpServer\JsonSchema\JsonSchema; use OPGG\LaravelMcpServer\Services\ToolService\ToolInterface; class WeatherTool implements ToolInterface { public function name(): string { return 'weather-tool'; } public function description(): string { return 'Get weather by location.'; } public function inputSchema(): array { return [ 'location' => JsonSchema::string() ->description('Location to query') ->required(), 'platform' => JsonSchema::string() ->enum(Platform::class) ->description('Client platform'), 'days' => JsonSchema::integer() ->min(1) ->max(7) ->default(1), ]; } public function annotations(): array { return []; } public function execute(array $arguments): mixed { return ['ok' => true]; } }
Notes:
- Existing full JSON Schema arrays are still supported.
enum()accepts either an array or aBackedEnum::class.compact()can be chained afterenum()to removeenumfrom emitted schema and append a compact hint todescription(compact(),compact(null),compact(3), orcompact('custom hint')).- Default compact example count is
3, and it can be overridden per endpoint viaRoute::mcp(...)->setConfig(compactEnumExampleCount: N). - When exporting (
tools/list, OpenAPI), property maps are automatically normalized to JSON Schema object format.
Example Prompt Class
<?php namespace App\MCP\Prompts; use OPGG\LaravelMcpServer\Services\PromptService\Prompt; class WelcomePrompt extends Prompt { public string $name = 'welcome-user'; public ?string $description = 'Generate a welcome message.'; public array $arguments = [ [ 'name' => 'username', 'description' => 'User name', 'required' => true, ], ]; public string $text = 'Welcome, {username}!'; }
Example Resource Class
<?php namespace App\MCP\Resources; use OPGG\LaravelMcpServer\Services\ResourceService\Resource; class BuildInfoResource extends Resource { public string $uri = 'app://build-info'; public string $name = 'Build Info'; public ?string $mimeType = 'application/json'; public function read(): array { return [ 'uri' => $this->uri, 'mimeType' => $this->mimeType, 'text' => json_encode([ 'version' => '2.0.0', 'environment' => app()->environment(), ], JSON_THROW_ON_ERROR), ]; } }
Register Examples on a Route
use App\MCP\Prompts\WelcomePrompt; use App\MCP\Resources\BuildInfoResource; use App\MCP\Tools\GreetingTool; use Illuminate\Support\Facades\Route; Route::mcp('/mcp') ->setServerInfo( name: 'Example MCP Server', version: '2.0.0', ) ->tools([GreetingTool::class]) ->resources([BuildInfoResource::class]) ->prompts([WelcomePrompt::class]);
Testing and Quality Commands
vendor/bin/pest vendor/bin/phpstan analyse vendor/bin/pint
Translation
pip install -r scripts/requirements.txt export ANTHROPIC_API_KEY='your-api-key' python scripts/translate_readme.py
Translate selected languages:
python scripts/translate_readme.py es ko
License
This project is distributed under the MIT license.
