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
Inglês | Português do Brasil | Coreano | Russo | Chinês Simplificado | Chinês Tradicional | Polonês | Espanhol
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/listtools/calltools/executePOST /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,pathounone--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
GETsã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(semrequestBody) para testes manuais mais simples. - Campos obrigatórios de cada ferramenta
inputSchema().requiredsão refletidos na validação de parâmetros do Swagger. - Campos enum são exportados com
schema.enumpara 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/exampleexplí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_JPinferem automaticamente o primeiro valor de amostra comodefaulteexample.
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 umBackedEnum::class.compact()pode ser encadeado apósenum()para removerenumdo esquema emitido e anexar uma dica compacta adescription(compact(),compact(null),compact(3)oucompact('custom hint')).- A contagem padrão de exemplos compactos é
3, e pode ser substituída por endpoint viaRoute::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.