# Laravel Mcp Server

> 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.

- **Type:** MCP server
- **Install:** `agentstack add mcp-opgginc-laravel-mcp-server`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [opgginc](https://agentstack.voostack.com/s/opgginc)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [opgginc](https://github.com/opgginc)
- **Source:** https://github.com/opgginc/laravel-mcp-server
- **Website:** https://op.gg/open-source/laravel-mcp-server

## Install

```sh
agentstack add mcp-opgginc-laravel-mcp-server
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

Laravel MCP Server by OP.GG

  Build a route-first MCP server in Laravel and Lumen

Official Website

  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](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

```bash
composer require opgginc/laravel-mcp-server
```

### 2) Register an endpoint (Laravel)

```php
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:

```php
->setProtocolVersion(ProtocolVersion::V2025_06_18)
```

### 3) Verify

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

Quick JSON-RPC check:

```bash
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.

```php
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'];
    }
}
```

```php
Route::mcp('/mcp/voice/lol/live')
    ->setServerInfo(
        name: 'OP.GG MCP Server - Voice lol Live',
        version: '1.0.0',
    )
    ->dynamicTools(LolPhaseToolResolver::class);
```

Example requests:

```text
/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

```php
// bootstrap/app.php
$app->withFacades();
$app->withEloquent();
$app->register(OPGG\LaravelMcpServer\LaravelMcpServerServiceProvider::class);
```

```php
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.

```php
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:

```bash
php artisan mcp:migrate-tools
```

Full guide: [docs/migrations/v2.0.0-migration.md](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 `
- 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:

```bash
# 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:

```bash
php artisan make:swagger-mcp-tool ./specs/openapi.json \
  --group-by=tag \
  --prefix=Billing \
  --test-api
```

- `--group-by`: `tag`, `path`, or `none`
- `--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, `GET` endpoints 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.

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

Example preview output:

```text
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:

```php
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([...])` 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
# 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:

```php
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 parameters` only (no `requestBody`) for simpler manual testing.
- Required fields from each tool `inputSchema().required` are reflected in Swagger parameter validation.
- Enum fields are exported with `schema.enum` so 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`/`example` are 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_JP` auto-infer first sample value as `default` and `example`.

## Example Tool Class

```php
 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
 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 a `BackedEnum::class`.
- `compact()` can be chained after `enum()` to remove `enum` from emitted schema and append a compact hint to `description` (`compact()`, `compact(null)`, `compact(3)`, or `compact('custom hint')`).
- Default compact example count is `3`, and it can be overridden per endpoint via `Route::mcp(...)->setConfig(compactEnumExampleCount: N)`.
- When exporting (`tools/list`, OpenAPI), property maps are automatically normalized to JSON Schema object format.

## Example Prompt Class

```php
 'username',
            'description' => 'User name',
            'required' => true,
        ],
    ];

    public string $text = 'Welcome, {username}!';
}
```

## Example Resource Class

```php
 $this->uri,
            'mimeType' => $this->mimeType,
            'text' => json_encode([
                'version' => '2.0.0',
                'environment' => app()->environment(),
            ], JSON_THROW_ON_ERROR),
        ];
    }
}
```

## Register Examples on a Route

```php
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

```bash
vendor/bin/pest
vendor/bin/phpstan analyse
vendor/bin/pint
```

## Translation

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

Translate selected languages:

```bash
python scripts/translate_readme.py es ko
```

## License

This project is distributed under the MIT license.

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [opgginc](https://github.com/opgginc)
- **Source:** [opgginc/laravel-mcp-server](https://github.com/opgginc/laravel-mcp-server)
- **License:** MIT
- **Homepage:** https://op.gg/open-source/laravel-mcp-server

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-opgginc-laravel-mcp-server
- Seller: https://agentstack.voostack.com/s/opgginc
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
