Laravel MCP Server by OP.GG

by opgginc

330 455 downloads Not rated yet

About

A Laravel package for implementing secure Model Context Protocol servers using Streamable HTTP and SSE transport, providing real-time communication and a scalable tool system for enterprise environments.

Explore

- 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

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Laravel MCP Server by OP.GG
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

- PHP >= 8.2
- Laravel (Illuminate) >= 9.x
- Lumen >= 9.x (optional)

composer require opgginc/laravel-mcp-server
// 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,
]);

- 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

If one endpoint needs to expose different tool sets based on the incoming URL, attach a dynamic tools resolver to the route.
The resolver owns both the declared tool catalog for the endpoint and the per-request visible subset.

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

Example requests:

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

The same filtered tool set is applied consistently to:
- tools/list
- tools/call
- tools/execute
- POST /tools/{tool_name} when ->enabledApi() is enabled

If the same endpoint also uses POST /tools/{tool_name}, you can optionally expose a public
consumedQueryParameters(): array hook on the resolver for query keys that should be used only
for filtering and not forwarded as tool arguments. This hook is a documented convention and is
not part of DynamicToolResolverInterface; resolvers that omit it will forward those query keys
as tool arguments.

Generate MCP tools from a Swagger/OpenAPI spec:


Export all registered ToolInterface classes (via Route::mcp(...)->tools([...]) or ->dynamicTools(...)) 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.

bash

php artisan mcp:export-openapi --discover-path=app/MCP/Tools

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

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "laravel mcp server by op.gg": {
            "laravel-mcp-server": {
                "command": "python",
                "args": [
                    "scripts/translate_readme.py"
                ]
            }
        }
    }
}

McpServers

{
    "laravel-mcp-server": {
        "command": "python",
        "args": [
            "scripts/translate_readme.py"
        ]
    }
}

<h1 align="center">Laravel MCP Server by OP.GG</h1>

<p align="center">
Build a route-first MCP server in Laravel and Lumen
</p>

<p align="center">
<a href="https://github.com/opgginc/laravel-mcp-server/actions">Build Status</a>
<a href="https://packagist.org/packages/opgginc/laravel-mcp-server">Total Downloads</a>
<a href="https://packagist.org/packages/opgginc/laravel-mcp-server">Latest Stable Version</a>
<a href="https://packagist.org/packages/opgginc/laravel-mcp-server">License</a>
</p>

<p align="center">
<a href="https://op.gg/open-source/laravel-mcp-server">Official Website</a>
</p>

<p align="center">
<a href="README.md">English</a> |
<a href="README.pt-BR.md">Português do Brasil</a> |
<a href="README.ko.md">한국어</a> |
<a href="README.ru.md">Русский</a> |
<a href="README.zh-CN.md">简体中文</a> |
<a href="README.zh-TW.md">繁體中文</a> |
<a href="README.pl.md">Polski</a> |
<a href="README.es.md">Español</a>
</p>

<p align="center">
Laravel MCP Server Demo
</p>

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"}'

Dynamic Tool Filtering by Query String

If one endpoint needs to expose different tool sets based on the incoming URL, attach a dynamic tools resolver to the route.
The resolver owns both the declared tool catalog for the endpoint and the per-request visible subset.

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

Example requests:

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

The same filtered tool set is applied consistently to:
- tools/list
- tools/call
- tools/execute
- POST /tools/{tool_name} when ->enabledApi() is enabled

If the same endpoint also uses POST /tools/{tool_name}, you can optionally expose a public
consumedQueryParameters(): array hook on the resolver for query keys that should be used only
for filtering and not forwarded as tool arguments. This hook is a documented convention and is
not part of DynamicToolResolverInterface; resolvers that omit it will forward those query keys
as tool arguments.

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:

…

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.

Videos about Laravel MCP Server by OP.GG

Relevant YouTube tutorials, setups, and demos