PHP code example of opgginc / laravel-mcp-server

1. Go to this page and download the library: Download opgginc/laravel-mcp-server library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

opgginc / laravel-mcp-server example snippets




namespace OPGG\LaravelMcpServer\Services\ToolService;

interface ToolInterface
{
    public function getName(): string;
    public function getDescription(): string;
    public function getInputSchema(): array;
    public function getAnnotations(): array;
    public function execute(array $arguments): mixed;
}



namespace OPGG\LaravelMcpServer\Services\ToolService;

use OPGG\LaravelMcpServer\Enums\ProcessMessageType;

interface ToolInterface
{
    public function messageType(): ProcessMessageType; // New method
    public function name(): string;                     // Renamed
    public function description(): string;              // Renamed
    public function inputSchema(): array;               // Renamed
    public function annotations(): array;               // Renamed
    public function execute(array $arguments): mixed;   // No change
}

use OPGG\LaravelMcpServer\Services\ToolService\ToolInterface;

class MyOldTool implements ToolInterface
{
    public function getName(): string { return 'MyOldTool'; }
    public function getDescription(): string { return 'This is my old tool.'; }
    public function getInputSchema(): array { return []; }
    public function getAnnotations(): array { return []; }
    public function execute(array $arguments): mixed { /* ... */ }
}

use OPGG\LaravelMcpServer\Services\ToolService\ToolInterface;
use OPGG\LaravelMcpServer\Enums\ProcessMessageType; // Import the enum

class MyNewTool implements ToolInterface
{
    /**
     * @deprecated since v1.3.0, use isStreaming() instead. Will be removed in v2.0.0
     */
    public function messageType(): ProcessMessageType
    {
        return ProcessMessageType::HTTP;
    }

    public function isStreaming(): bool
    {
        return false; // Most tools should return false
    }

    public function name(): string { return 'MyNewTool'; }
    public function description(): string { return 'This is my new tool.'; }
    public function inputSchema(): array { return []; }
    public function annotations(): array { return []; }
    public function execute(array $arguments): mixed { /* ... */ }
}

// config/mcp-server.php

'middlewares' => [
    // PRODUCTION CONFIGURATION (Choose one or combine):
    'auth:sanctum',      // For Laravel Sanctum (recommended)
    // 'auth:api',        // For Laravel Passport
    // 'custom.mcp.auth', // For custom authentication
    'throttle:100,1',     // Rate limiting (100 requests per minute)
    'cors',               // CORS support if needed
],

// In your application code or tinker
$user = User::find(1);
$token = $user->createToken('MCP Client')->plainTextToken;

// Use this token in your MCP client configuration

// app/Http/Middleware/McpApiKeyAuth.php


namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class McpApiKeyAuth
{
    public function handle(Request $request, Closure $next)
    {
        $apiKey = $request->header('X-MCP-API-Key');
        
        // Validate against environment variable or database
        if ($apiKey !== config('mcp.api_key')) {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => [
                    'code' => -32001,
                    'message' => 'Unauthorized: Invalid API key'
                ]
            ], 401);
        }
        
        return $next($request);
    }
}

// app/Http/Kernel.php
protected $routeMiddleware = [
    // ... other middleware
    'mcp.auth' => \App\Http\Middleware\McpApiKeyAuth::class,
];

// config/mcp-server.php
'middlewares' => [
    'mcp.auth',        // Your custom API key middleware
    'throttle:100,1',  // Rate limiting
],

// .env file
MCP_API_KEY=your-secure-api-key-here

// app/Http/Middleware/McpIpWhitelist.php
class McpIpWhitelist
{
    public function handle(Request $request, Closure $next)
    {
        $allowedIps = config('mcp.allowed_ips', []);
        
        if (!empty($allowedIps) && !in_array($request->ip(), $allowedIps)) {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => [
                    'code' => -32004,
                    'message' => 'Access denied from this IP address'
                ]
            ], 403);
        }
        
        return $next($request);
    }
}

// app/Http/Middleware/McpRoleAuth.php
class McpRoleAuth
{
    public function handle(Request $request, Closure $next, string $role)
    {
        $user = auth()->user();
        
        if (!$user || !$user->hasRole($role)) {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => [
                    'code' => -32003,
                    'message' => 'Forbidden: Insufficient permissions'
                ]
            ], 403);
        }
        
        return $next($request);
    }
}

// app/Http/Middleware/McpAuditLog.php
class McpAuditLog
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);
        
        Log::channel('mcp_audit')->info('MCP Request', [
            'method' => $request->method(),
            'path' => $request->path(),
            'ip' => $request->ip(),
            'user_id' => auth()->id(),
            'payload' => $request->json()->all(),
            'status' => $response->getStatusCode(),
            'timestamp' => now(),
        ]);
        
        return $response;
    }
}

// config/mcp-server.php
'middlewares' => array_filter([
    // Always apply rate limiting
    'throttle:' . env('MCP_RATE_LIMIT', '60') . ',1',
    
    // Authentication only in non-local environments
    env('APP_ENV') !== 'local' ? 'auth:sanctum' : null,
    
    // IP whitelisting for production
    env('APP_ENV') === 'production' ? 'mcp.ip.whitelist' : null,
    
    // Audit logging for production
    env('APP_ENV') === 'production' ? 'mcp.audit' : null,
    
    // CORS if needed
    env('MCP_CORS_ENABLED', false) ? 'cors' : null,
]),

'middlewares' => [
    'mcp.api.key',      // Simple API key
    'mcp.ip.whitelist', // Restrict to internal IPs
    'throttle:1000,1',  // Higher rate limit for internal use
]

'middlewares' => [
    'auth:sanctum',     // User authentication
    'throttle:60,1',    // Stricter rate limiting
    'cors',             // CORS for web clients
    'mcp.audit',        // Audit all requests
]

'middlewares' => [
    'auth:api',         // OAuth2 via Passport
    'throttle:100,1',   // Moderate rate limiting
    'mcp.partner.acl',  // Partner-specific access control
    'mcp.audit',        // Full audit trail
]

// config/mcp-server.php

// Allow access from all domains (default)
'domain' => null,

// Restrict to a single domain
'domain' => 'api.example.com',

// Restrict to multiple domains
'domain' => ['api.example.com', 'admin.example.com'],

// Single API subdomain
'domain' => 'api.op.gg',

// Multiple subdomains for different environments
'domain' => ['api.op.gg', 'staging-api.op.gg'],

// Multi-tenant architecture
'domain' => ['tenant1.op.gg', 'tenant2.op.gg', 'tenant3.op.gg'],

// Different services on different domains
'domain' => ['api.op.gg', 'api.kargn.as'],

use OPGG\LaravelMcpServer\Services\ToolService\ToolInterface;

class MyCustomTool implements ToolInterface
{
    // Tool implementation
}



namespace OPGG\LaravelMcpServer\Services\ToolService;

use OPGG\LaravelMcpServer\Enums\ProcessMessageType;

interface ToolInterface
{
    /**
     * @deprecated since v1.3.0, use isStreaming() instead. Will be removed in v2.0.0
     */
    public function messageType(): ProcessMessageType;

    // NEW in v1.3.0: Determines if this tool ucture.
    public function inputSchema(): array;

    // Provides a way to add arbitrary metadata or annotations to your tool.
    public function annotations(): array;

    // The core logic of your tool. Receives validated arguments and returns the result.
    public function execute(array $arguments): mixed;
}

public function inputSchema(): array
{
    return [
        'type' => 'object',
        'properties' => [
            'userId' => [
                'type' => 'integer',
                'description' => 'The unique identifier for the user.',
            ],
            'includeDetails' => [
                'type' => 'boolean',
                'description' => 'Whether to include extended details in the response.',
                'default' => false, // You can specify default values
            ],
        ],
        '

// Inside your execute() method:
$validator = Validator::make($arguments, [
    'userId' => ['ew JsonRpcErrorException(
        message: $validator->errors()->toJson(),
        code: JsonRpcErrorCode::INVALID_REQUEST
    );
}
// Proceed with validated $arguments['userId'] and $arguments['

public function annotations(): array
{
    return [
        'title' => 'User Profile Fetcher',
        'readOnlyHint' => true,        // Tool only reads user data
        'destructiveHint' => false,    // Tool doesn't delete or modify data
        'idempotentHint' => true,      // Safe to call multiple times
        'openWorldHint' => false,      // Tool only accesses local database
    ];
}

// Database query tool
public function annotations(): array
{
    return [
        'title' => 'Database Query Tool',
        'readOnlyHint' => true,
        'destructiveHint' => false,
        'idempotentHint' => true,
        'openWorldHint' => false,
    ];
}

// Post deletion tool
public function annotations(): array
{
    return [
        'title' => 'Blog Post Deletion Tool',
        'readOnlyHint' => false,
        'destructiveHint' => true,     // Can delete posts
        'idempotentHint' => false,     // Deleting twice has different effects
        'openWorldHint' => false,
    ];
}

// API integration tool
public function annotations(): array
{
    return [
        'title' => 'Weather API',
        'readOnlyHint' => true,
        'destructiveHint' => false,
        'idempotentHint' => true,
        'openWorldHint' => true,       // Accesses external weather API
    ];
}

public function annotations(): array
{
    return [
        // Standard MCP annotations
        'title' => 'Custom Tool',
        'readOnlyHint' => true,

        // Custom annotations for your application
        'category' => 'data-analysis',
        'version' => '2.1.0',
        'author' => 'Data Team',
        '

use OPGG\LaravelMcpServer\Services\PromptService\Prompt;

class WelcomePrompt extends Prompt
{
    public string $name = 'welcome-user';
    
    public ?string $description = 'A customizable welcome message for users';
    
    public array $arguments = [
        [
            'name' => 'username',
            'description' => 'The name of the user to welcome',
            '

abstract class NotificationHandler
{
    // Required: Message type (usually ProcessMessageType::HTTP)
    protected const MESSAGE_TYPE = ProcessMessageType::HTTP;
    
    // Required: The notification method to handle  
    protected const HANDLE_METHOD = 'notifications/your_method';
    
    // Required: Execute the notification logic
    abstract public function execute(?array $params = null): void;
}

// File upload progress tracking
class UploadProgressHandler extends NotificationHandler
{
    protected const MESSAGE_TYPE = ProcessMessageType::HTTP;
    protected const HANDLE_METHOD = 'notifications/upload_progress';

    public function execute(?array $params = null): void
    {
        $token = $params['progressToken'] ?? null;
        $progress = $params['progress'] ?? 0;
        $total = $params['total'] ?? 100;
        
        if ($token) {
            Cache::put("upload_progress_{$token}", [
                'progress' => $progress,
                'total' => $total,
                'percentage' => $total ? round(($progress / $total) * 100, 2) : 0,
                'updated_at' => now()
            ], 3600);
            
            // Broadcast real-time update
            broadcast(new UploadProgressUpdated($token, $progress, $total));
        }
    }
}

// User activity and audit logging
class UserActivityHandler extends NotificationHandler
{
    protected const MESSAGE_TYPE = ProcessMessageType::HTTP;
    protected const HANDLE_METHOD = 'notifications/user_activity';

    public function execute(?array $params = null): void
    {
        UserActivity::create([
            'user_id' => $params['userId'] ?? null,
            'action' => $params['action'] ?? 'unknown',
            'resource' => $params['resource'] ?? null,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'metadata' => $params['metadata'] ?? [],
            'created_at' => now()
        ]);
        
        // Trigger security alerts for sensitive actions
        if (in_array($params['action'] ?? '', ['delete', 'export', 'admin_access'])) {
            SecurityAlert::dispatch($params);
        }
    }
}

// Background task triggering
class TaskTriggerHandler extends NotificationHandler
{
    protected const MESSAGE_TYPE = ProcessMessageType::HTTP;
    protected const HANDLE_METHOD = 'notifications/trigger_task';

    public function execute(?array $params = null): void
    {
        $taskType = $params['taskType'] ?? null;
        $taskData = $params['data'] ?? [];
        
        match ($taskType) {
            'send_email' => SendEmailJob::dispatch($taskData),
            'generate_report' => GenerateReportJob::dispatch($taskData),
            'sync_data' => DataSyncJob::dispatch($taskData),
            'cleanup' => CleanupJob::dispatch($taskData),
            default => Log::warning("Unknown task type: {$taskType}")
        };
    }
}

// In AppServiceProvider or dedicated MCP service provider
public function boot()
{
    $server = app(MCPServer::class);
    
    // Register built-in handlers (optional - they're registered by default)
    $server->registerNotificationHandler(new InitializedHandler());
    $server->registerNotificationHandler(new ProgressHandler());
    $server->registerNotificationHandler(new CancelledHandler());
    $server->registerNotificationHandler(new MessageHandler());
    
    // Register custom handlers
    $server->registerNotificationHandler(new UploadProgressHandler());
    $server->registerNotificationHandler(new UserActivityHandler());
    $server->registerNotificationHandler(new TaskTriggerHandler());
}

public function execute(?array $params = null): void
{
    // Validate andler: Missing  // Validate parameter types
    if (!is_numeric($params['userId'])) {
        Log::warning('UserActivityHandler: userId must be numeric', $params);
        return;
    }
    
    // Safe parameter extraction with defaults
    $userId = (int) $params['userId'];
    $action = $params['action'] ?? 'unknown';
    $metadata = $params['metadata'] ?? [];
    
    // Process notification...
}

'sse_adapter' => 'redis',
'adapters' => [
    'redis' => [
        'prefix' => 'mcp_sse_',    // Prefix for Redis keys
        'connection' => 'default', // Redis connection from database.php
        'ttl' => 100,              // Message TTL in seconds
    ],
],

// Old approach (deprecated)
public function messageType(): ProcessMessageType
{
    return ProcessMessageType::HTTP;
}

// New approach (v1.3.0+)
public function isStreaming(): bool
{
    return false; // Use false for HTTP, true for streaming
}
bash
php artisan mcp:migrate-tools {path?}
bash
php artisan mcp:migrate-tools
bash
php artisan mcp:migrate-tools path/to/your/tools
bash
   php artisan vendor:publish --provider="OPGG\LaravelMcpServer\LaravelMcpServerServiceProvider"
   
bash
php artisan make:mcp-tool MyCustomTool
bash
php artisan make:swagger-mcp-tool petstore.json

🗂️ Choose how to organize your generated tools and resources:

Tag-based grouping (organize by OpenAPI tags)
📊 Total: 25 endpoints → 15 tools + 10 resources

  📁 Pet/ (8 tools, 4 resources)
     └─ CreatePetTool.php (POST /pet)
     └─ UpdatePetTool.php (PUT /pet)
     └─ ... and 10 more files
  📁 Store/ (5 tools, 3 resources)  
     └─ PlaceOrderTool.php (POST /store/order)
     └─ GetInventoryResource.php (GET /store/inventory)
     └─ ... and 6 more files
  📁 User/ (2 tools, 3 resources)
     └─ CreateUserTool.php (POST /user)
     └─ GetUserByNameResource.php (GET /user/{username})
     └─ ... and 3 more files

Path-based grouping (organize by API path)  
📊 Total: 25 endpoints → 15 tools + 10 resources

  📁 Pet/ (12 files from /pet)
     └─ PostPetTool.php (POST /pet)
     └─ GetPetByIdResource.php (GET /pet/{petId})
     └─ ... and 10 more files
  📁 Store/ (8 files from /store)
     └─ PostStoreOrderTool.php (POST /store/order)  
     └─ GetStoreInventoryResource.php (GET /store/inventory)
     └─ ... and 6 more files

No grouping (everything in root folder)
📊 Total: 25 endpoints → 15 tools + 10 resources

  📁 Tools/ (15 files directly in root)
     └─ CreatePetTool.php (POST /pet)
     └─ UpdatePetTool.php (PUT /pet/{petId})
     └─ ... and 13 more files
  📁 Resources/ (10 files directly in root)
     └─ GetPetByIdResource.php (GET /pet/{petId})
     └─ GetStoreInventoryResource.php (GET /store/inventory)  
     └─ ... and 8 more files

Choose grouping method:
  [0] Tag-based grouping
  [1] Path-based grouping  
  [2] No grouping
 > 0
bash
php artisan make:mcp-resource SystemLogResource
php artisan make:mcp-resource-template UserLogTemplate
bash
php artisan make:mcp-prompt WelcomePrompt
bash
php artisan make:mcp-notification ProgressHandler --method=notifications/progress