Laravel MCP Server

Um pacote Laravel para construir servidores seguros do Model Context Protocol com comunicação em tempo real usando SSE.

Documentação

Laravel MCP Server by OP.GG

Construa um servidor MCP com foco em rotas em Laravel e Lumen

Build Status Total Downloads Latest Stable Version License

Site Oficial

Inglês | Português do Brasil | Coreano | Russo | Chinês Simplificado | Chinês Tradicional | Polonês | Espanhol

Laravel MCP Server Demo

Mudanças de Quebra 2.0.0

  • A configuração de endpoints passou de registro baseado em configuração para registro baseado em rotas.
  • Streamable HTTP é o único transporte suportado.
  • Os mutadores de metadados do servidor são consolidados em setServerInfo(...).
  • Métodos de transporte de ferramentas legados foram removidos do runtime (messageType(), ProcessMessageType::SSE).

Guia completo de migração: docs/migrations/v2.0.0-migration.md

Visão Geral

O Laravel MCP Server fornece registro de endpoints MCP baseado em rotas para Laravel e Lumen.

Pontos-chave:

  • Transporte HTTP Streamable
  • Configuração rota-primeiro (Route::mcp(...) / McpRoute::register(...))
  • Registro de ferramentas, recursos, modelos de recursos e prompts por endpoint
  • Metadados de endpoint compatíveis com cache de rotas

Requisitos

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

Início Rápido

1) Instalar

composer require opgginc/laravel-mcp-server

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

Se você precisar de compatibilidade com clientes que não suportam 2025-11-25, defina:

->setProtocolVersion(ProtocolVersion::V2025_06_18)

3) Verificar

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

Verificação 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"}'

Filtragem Dinâmica de Ferramentas por Query String

Se um endpoint precisar expor diferentes conjuntos de ferramentas com base na URL recebida, anexe um resolvedor dinâmico de ferramentas à rota. O resolvedor possui tanto o catálogo de ferramentas declarado para o endpoint quanto o subconjunto visível por requisição.

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

Exemplos de requisições:

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

O mesmo conjunto de ferramentas filtrado é aplicado consistentemente a:

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

Se o mesmo endpoint também usar POST /tools/{tool_name}, você pode opcionalmente expor um hook público consumedQueryParameters(): array no resolvedor para chaves de query que devem ser usadas apenas para filtragem e não encaminhadas como argumentos de ferramenta. Este hook é uma convenção documentada e não faz parte de DynamicToolResolverInterface; resolvedores que o omitirem encaminharão essas chaves de query como argumentos de ferramenta.

Configuração do 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,
    ]);

Segurança Mínima (Produção)

Use middleware do Laravel no seu grupo de rotas 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 Migração v2.0.0 (a partir da v1.0.0)

  • A configuração de endpoints MCP passou de configuração para registro de rotas.
  • Streamable HTTP é o único transporte.
  • Os mutadores de metadados do servidor são consolidados em setServerInfo(...).
  • O comando de migração de ferramentas está disponível para assinaturas legadas:
php artisan mcp:migrate-tools

Guia completo: docs/migrations/v2.0.0-migration.md

Recursos Avançados (Links Rápidos)

  • Criar ferramentas: php artisan make:mcp-tool ToolName
  • Criar recursos: php artisan make:mcp-resource ResourceName
  • Criar modelos de recursos: php artisan make:mcp-resource-template TemplateName
  • Criar prompts: php artisan make:mcp-prompt PromptName
  • Criar notificações: php artisan make:mcp-notification HandlerName --method=notifications/method
  • Gerar a partir do OpenAPI: php artisan make:swagger-mcp-tool <spec-url-or-file>
  • Exportar ferramentas para OpenAPI: php artisan mcp:export-openapi --output=storage/api-docs-mcp/api-docs.json

Referências de código:

  • Exemplos de ferramentas: src/Services/ToolService/Examples/
  • Exemplos de recursos: src/Services/ResourceService/Examples/
  • Serviço de prompts: src/Services/PromptService/
  • Manipuladores de notificações: src/Server/Notification/
  • Construtor de rotas: src/Routing/McpRouteBuilder.php

Swagger/OpenAPI -> Ferramenta MCP

Gere ferramentas MCP a partir de uma especificação 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

Opções úteis:

php artisan make:swagger-mcp-tool ./specs/openapi.json \
  --group-by=tag \
  --prefix=Billing \
  --test-api
  • --group-by: tag, path ou none
  • --prefix: prefixo de nome de classe para ferramentas/recursos gerados
  • --test-api: testar conectividade do endpoint antes da geração

Comportamento de geração:

  • No modo interativo, você pode escolher Ferramenta ou Recurso por endpoint.
  • No modo não interativo, endpoints GET são gerados como Recursos e outros métodos como Ferramentas.

Pré-visualização Interativa Aprimorada

Se você executar o comando sem --group-by, o gerador mostra uma pré-visualização interativa da estrutura de pastas e contagens de arquivos antes da criação.

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

Exemplo de saída da pré-visualização:

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/

Após a geração, registre as classes de ferramentas geradas no seu 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,
    ]);

Ferramentas MCP -> Exportação OpenAPI

Exporte todas as classes ToolInterface registradas (via Route::mcp(...)->tools([...]) ou ->dynamicTools(...)) para um documento JSON OpenAPI usando o inputSchema() de cada ferramenta. Apenas endpoints configurados com ->enabledApi() são incluídos nesta exportação e expostos através de POST /tools/{tool_name}. As operações são agrupadas por endpoint name usando OpenAPI tags. Se vários endpoints registrarem o mesmo nome de ferramenta, a operação mantém o comportamento de primeiro registro e mescla todos os nomes de endpoints correspondentes em tags. Se o registro de rotas estiver ausente, o comando descobre automaticamente ferramentas em caminhos padrão: app/MCP/Tools e 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

Habilite a geração de rotas da API de Ferramentas:

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

Dica de teste no Swagger UI:

  • Operações exportadas usam apenas query parameters (sem requestBody) para testes manuais mais simples.
  • Campos obrigatórios de cada ferramenta inputSchema().required são refletidos na validação de parâmetros do Swagger.
  • Campos enum são exportados com schema.enum para que o Swagger renderize seleções suspensas.
  • Campos de array são exportados com style=form + explode=true (formato de chave repetida, ex.: desired_output_fields=items&desired_output_fields=runes).
  • A análise de argumentos /tools/{tool_name} prefere parâmetros de query em vez de payloads de corpo/formulário para evitar conflitos com o Swagger.
  • Campos enum sem default/example explícitos são preenchidos automaticamente com o primeiro valor enum (ou o primeiro valor enum não nulo).
  • Campos de string com descrições como e.g., en_US, ko_KR, ja_JP inferem automaticamente o primeiro valor de amostra como default e example.

Exemplo de Classe de Ferramenta

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

Construtor JsonSchema (Estilo Laravel)

Este pacote fornece seu próprio construtor JsonSchema sob o namespace OPGG\LaravelMcpServer. Você pode definir esquemas de ferramentas em um formato fluente estilo Laravel 12 enquanto mantém 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:

  • Arrays completos de JSON Schema existentes ainda são suportados.
  • enum() aceita um array ou um BackedEnum::class.
  • compact() pode ser encadeado após enum() para remover enum do esquema emitido e anexar uma dica compacta a description (compact(), compact(null), compact(3) ou compact('custom hint')).
  • A contagem padrão de exemplos compactos é 3, e pode ser substituída por endpoint via Route::mcp(...)->setConfig(compactEnumExampleCount: N).
  • Ao exportar (tools/list, OpenAPI), os mapas de propriedades são automaticamente normalizados para o formato de objeto JSON Schema.

Exemplo de Classe 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}!';
}

Exemplo de Classe 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 Exemplos em uma Rota

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 Teste e Qualidade

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

Tradução

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

Traduza idiomas selecionados:

python scripts/translate_readme.py es ko

Licença

Este projeto é distribuído sob a licença MIT.