Laravel MCP Server

Un paquete de Laravel para construir servidores seguros del Model Context Protocol con comunicación en tiempo real usando SSE.

Documentación

Laravel MCP Server de OP.GG

Construye un servidor MCP orientado a rutas en Laravel y Lumen

Build Status Total Downloads Latest Stable Version License

Sitio web oficial

English | Português do Brasil | 한국어 | Русский | 简体中文 | 繁體中文 | Polski | Español

Laravel MCP Server Demo

Cambios importantes en 2.0.0

  • La configuración de endpoints pasó de registrarse mediante configuración a registrarse mediante rutas.
  • Streamable HTTP es el único transporte compatible.
  • Los mutadores de metadatos del servidor se consolidan en setServerInfo(...).
  • Los métodos de transporte de herramientas heredados se eliminaron del runtime (messageType(), ProcessMessageType::SSE).

Guía de migración completa: docs/migrations/v2.0.0-migration.md

Resumen

Laravel MCP Server proporciona registro de endpoints MCP basado en rutas para Laravel y Lumen.

Puntos clave:

  • Transporte Streamable HTTP
  • Configuración orientada a rutas (Route::mcp(...) / McpRoute::register(...))
  • Registro de herramientas, recursos, plantillas de recursos y prompts por endpoint
  • Metadatos de endpoint compatibles con caché de rutas

Requisitos

  • PHP >= 8.2
  • Laravel (Illuminate) >= 9.x
  • Lumen >= 9.x (opcional)

Inicio rápido

1) Instalación

composer require opgginc/laravel-mcp-server

2) Registrar un 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,
    ]);

Si necesitas compatibilidad con clientes que no admiten 2025-11-25, configura:

->setProtocolVersion(ProtocolVersion::V2025_06_18)

3) Verificación

php artisan route:list | grep mcp
php artisan mcp:test-tool --list --endpoint=/mcp

Verificación rápida de JSON-RPC:

curl -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Filtrado dinámico de herramientas por cadena de consulta

Si un endpoint necesita exponer diferentes conjuntos de herramientas según la URL entrante, adjunta un resolvedor de herramientas dinámicas a la ruta. El resolvedor posee tanto el catálogo de herramientas declarado para el endpoint como el subconjunto visible por solicitud.

use OPGG\LaravelMcpServer\Data\ToolResolutionContext;
use OPGG\LaravelMcpServer\Routing\McpEndpointDefinition;
use OPGG\LaravelMcpServer\Services\ToolService\DynamicToolResolverInterface;

final class LolPhaseToolResolver implements DynamicToolResolverInterface
{
    public function declaredTools(McpEndpointDefinition $endpoint): array
    {
        return [
            \App\MCP\Tools\LolSearchChampionMetaTool::class,
            \App\MCP\Tools\LolGetChampionAnalysisTool::class,
            \App\MCP\Tools\LolGetLiveItemRecommendationsTool::class,
        ];
    }

    public function resolve(
        McpEndpointDefinition $endpoint,
        ToolResolutionContext $context,
    ): array {
        return match ($context->queryParameters['phase'] ?? null) {
            'lobby' => [
                \App\MCP\Tools\LolSearchChampionMetaTool::class,
                \App\MCP\Tools\LolGetChampionAnalysisTool::class,
            ],
            'inprogress' => [
                \App\MCP\Tools\LolGetLiveItemRecommendationsTool::class,
            ],
            default => $this->declaredTools($endpoint),
        };
    }

    public function consumedQueryParameters(): array
    {
        return ['phase'];
    }
}
Route::mcp('/mcp/voice/lol/live')
    ->setServerInfo(
        name: 'OP.GG MCP Server - Voice lol Live',
        version: '1.0.0',
    )
    ->dynamicTools(LolPhaseToolResolver::class);

Ejemplos de solicitudes:

/mcp/voice/lol/live?phase=lobby
/mcp/voice/lol/live?phase=inprogress

El mismo conjunto de herramientas filtrado se aplica de manera consistente a:

  • tools/list
  • tools/call
  • tools/execute
  • POST /tools/{tool_name} cuando ->enabledApi() está habilitado

Si el mismo endpoint también usa POST /tools/{tool_name}, puedes exponer opcionalmente un hook público consumedQueryParameters(): array en el resolvedor para claves de consulta que deben usarse solo para filtrar y no reenviarse como argumentos de herramientas. Este hook es una convención documentada y no forma parte de DynamicToolResolverInterface; los resolvedores que lo omitan reenviarán esas claves de consulta como argumentos de herramientas.

Configuración de Lumen

// 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,
    ]);

Seguridad mínima (Producción)

Usa middleware de Laravel en tu grupo de rutas MCP.

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,
        ]);
});

Notas de migración a v2.0.0 (desde v1.0.0)

  • La configuración de endpoints MCP pasó de configuración a registro de rutas.
  • Streamable HTTP es el único transporte.
  • Los mutadores de metadatos del servidor se consolidan en setServerInfo(...).
  • El comando de migración de herramientas está disponible para firmas heredadas:
php artisan mcp:migrate-tools

Guía completa: docs/migrations/v2.0.0-migration.md

Funciones avanzadas (Enlaces rápidos)

  • Crear herramientas: php artisan make:mcp-tool ToolName
  • Crear recursos: php artisan make:mcp-resource ResourceName
  • Crear plantillas de recursos: php artisan make:mcp-resource-template TemplateName
  • Crear prompts: php artisan make:mcp-prompt PromptName
  • Crear notificaciones: php artisan make:mcp-notification HandlerName --method=notifications/method
  • Generar desde OpenAPI: php artisan make:swagger-mcp-tool <spec-url-or-file>
  • Exportar herramientas a OpenAPI: php artisan mcp:export-openapi --output=storage/api-docs-mcp/api-docs.json

Referencias de código:

  • Ejemplos de herramientas: src/Services/ToolService/Examples/
  • Ejemplos de recursos: src/Services/ResourceService/Examples/
  • Servicio de prompts: src/Services/PromptService/
  • Manejadores de notificaciones: src/Server/Notification/
  • Constructor de rutas: src/Routing/McpRouteBuilder.php

Swagger/OpenAPI -> Herramienta MCP

Genera herramientas MCP desde una especificación Swagger/OpenAPI:

# 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

Opciones útiles:

php artisan make:swagger-mcp-tool ./specs/openapi.json \
  --group-by=tag \
  --prefix=Billing \
  --test-api
  • --group-by: tag, path, o none
  • --prefix: prefijo de nombre de clase para herramientas/recursos generados
  • --test-api: probar la conectividad del endpoint antes de la generación

Comportamiento de generación:

  • En modo interactivo, puedes elegir Herramienta o Recurso por endpoint.
  • En modo no interactivo, los endpoints GET se generan como Recursos y los demás métodos como Herramientas.

Vista previa interactiva mejorada

Si ejecutas el comando sin --group-by, el generador muestra una vista previa interactiva de la estructura de carpetas y el número de archivos antes de la creación.

php artisan make:swagger-mcp-tool ./specs/openapi.json

Ejemplo de salida de vista previa:

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/

Después de la generación, registra las clases de herramientas generadas en tu endpoint MCP:

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,
    ]);

Exportación de Herramientas MCP -> OpenAPI

Exporta todas las clases ToolInterface registradas (mediante Route::mcp(...)->tools([...]) o ->dynamicTools(...)) a un documento JSON OpenAPI usando el inputSchema() de cada herramienta. Solo los endpoints configurados con ->enabledApi() se incluyen en esta exportación y se exponen a través de POST /tools/{tool_name}. Las operaciones se agrupan por name del endpoint usando tags de OpenAPI. Si varios endpoints registran el mismo nombre de herramienta, la operación mantiene el comportamiento del primer registro y fusiona todos los nombres de endpoints coincidentes en tags. Si falta el registro de rutas, el comando descubre automáticamente las herramientas en rutas predeterminadas: app/MCP/Tools y 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

Habilita la generación de rutas de la API de herramientas:

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,
    ]);

Consejo para probar con Swagger UI:

  • Las operaciones exportadas usan solo query parameters (sin requestBody) para pruebas manuales más simples.
  • Los campos obligatorios de cada inputSchema().required de herramienta se reflejan en la validación de parámetros de Swagger.
  • Los campos de enumeración se exportan con schema.enum para que Swagger muestre menús desplegables.
  • Los campos de arreglo se exportan con style=form + explode=true (formato de clave repetida, p. ej., desired_output_fields=items&desired_output_fields=runes).
  • El análisis de argumentos de /tools/{tool_name} prefiere parámetros de consulta sobre cargas útiles de cuerpo/formulario para evitar conflictos con Swagger.
  • Los campos de enumeración sin default/example explícitos se autocompletan con el primer valor de enumeración (o el primer valor de enumeración no nulo).
  • Los campos de cadena con descripciones como e.g., en_US, ko_KR, ja_JP infieren automáticamente el primer valor de muestra como default y example.

Clase de ejemplo de herramienta

<?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'],
        ];
    }
}

Constructor de JsonSchema (Estilo Laravel)

Este paquete proporciona su propio constructor de JsonSchema bajo el espacio de nombres OPGG\LaravelMcpServer. Puedes definir esquemas de herramientas en un formato fluido estilo Laravel 12 mientras mantienes 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];
    }
}

Notas:

  • Los arreglos completos de JSON Schema existentes siguen siendo compatibles.
  • enum() acepta un arreglo o un BackedEnum::class.
  • compact() se puede encadenar después de enum() para eliminar enum del esquema emitido y agregar una pista compacta a description (compact(), compact(null), compact(3), o compact('custom hint')).
  • El número predeterminado de ejemplos compactos es 3, y se puede sobrescribir por endpoint mediante Route::mcp(...)->setConfig(compactEnumExampleCount: N).
  • Al exportar (tools/list, OpenAPI), los mapas de propiedades se normalizan automáticamente al formato de objeto JSON Schema.

Clase de ejemplo de prompt

<?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}!';
}

Clase de ejemplo de recurso

<?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),
        ];
    }
}

Registrar ejemplos en una ruta

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]);

Comandos de prueba y calidad

vendor/bin/pest
vendor/bin/phpstan analyse
vendor/bin/pint

Traducción

pip install -r scripts/requirements.txt
export ANTHROPIC_API_KEY='your-api-key'
python scripts/translate_readme.py

Traducir idiomas seleccionados:

python scripts/translate_readme.py es ko

Licencia

Este proyecto se distribuye bajo la licencia MIT.