PHP code example of projectsaturnstudios / laravel-vibes

1. Go to this page and download the library: Download projectsaturnstudios/laravel-vibes 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/ */

    

projectsaturnstudios / laravel-vibes example snippets


return [
    'service_info' => [
        'server_name' => env('VIBE_SVC_NAME', 'laravel-vibes-server'),
        'server_version' => env('VIBE_SVC_VERSION', '1.0.0'),
        'heartbeat_interval' => 20, // seconds
        'catch_exceptions' => false, // Catch exceptions in MCP event loop
    ],
    
    'features' => [
        'tools' => true,      // Tool registration and execution (fully implemented)
        'resources' => false, // Resource discovery and access (partial implementation)
        'prompts' => false,   // Prompt templates and execution (partial implementation)
        'samples' => false,   // Model behavior configuration (partial implementation)
        'roots' => false,     // Custom workflow entry points (partial implementation)
    ],
    
    'auto_discover_all_primitives' => [app()->path()],
    'auto_discover_base_path' => base_path(),
    
    // Tool repository and route configurations...
];

use App\Tools\TextAnalysisTool;
use App\Tools\DataFetchTool;

// In a service provider's boot method
public function boot()
{
    $agency = app('the-agency');
    
    $agency->addTools([
        TextAnalysisTool::class,
        DataFetchTool::class,
    ]);
}

'tools' => [
    'text-analysis' => \App\Tools\TextAnalysisTool::class,
    'data-fetch' => \App\Tools\DataFetchTool::class,
],



namespace App\Tools;

use ProjectSaturnStudios\Vibes\Primitives\Tools\Data\VibeTool;

class WeatherTool extends VibeTool
{
    protected string $name = 'weather';

    public static function getMetadata(): array
    {
        return [
            'name' => 'weather',
            'description' => 'Get current weather for a location',
            'parameters' => [
                'type' => 'object',
                'properties' => [
                    'location' => [
                        'type' => 'string',
                        'description' => 'City name or location'
                    ]
                ],
                '

// Frontend JavaScript
const eventSource = new EventSource('/mcp/sse');
const sessionId = generateUniqueId();

eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Received update:', data);
};

// POST to /mcp/sse/messages
{
    "jsonrpc": "2.0",
    "method": "run_tool",
    "session_id": "session-12345",
    "id": "req-1",
    "params": {
        "name": "weather",
        "input": {
            "location": "San Francisco"
        }
    }
}

// config/vibes.php
'auto_discover_all_primitives' => [app()->path()], // Directories to scan for primitives
'auto_discover_base_path' => base_path(), // Base path for relative directories

protected function discoverPrimitiveHandlers() :  void
{
    $agency = app(TheAgency::class);

    $cachedPrimitiveHandlers = $this->getCachedPrimitiveHandlers();

    if (! is_null($cachedPrimitiveHandlers)) {
        $agency->addPrimitiveHandlers($cachedPrimitiveHandlers);
        return;
    }

    (new PrimitiveHandlerDiscoveryService)
        ->within(config('vibes.auto_discover_all_primitives'))
        ->useBasePath(config('vibes.auto_discover_base_path', base_path()))
        ->ignoringFiles(Composer::getAutoloadedFiles(base_path('composer.json')))
        ->addToTheAgency($agency);
}



namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use ProjectSaturnStudios\Vibes\TheAgency;
use App\MCP\Tools\WeatherTool;
use App\MCP\Tools\TranslationTool;
use App\MCP\Resources\UserResource;
use App\MCP\Prompts\GreetingPrompt;

class MCPServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Get the agency singleton
        $agency = app(TheAgency::class);
        
        // Register tools
        $agency->addTools([
            WeatherTool::class,
            TranslationTool::class,
        ]);
        
        // Register other primitives when implemented
        // $agency->addResources([
        //    UserResource::class,
        // ]);
        
        // $agency->addPrompts([
        //    GreetingPrompt::class,
        // ]);
    }
}

'providers' => [
    // Other service providers...
    App\Providers\MCPServiceProvider::class,
],

// Register the command in your service provider
$this->app->booted(function () {
    $this->app['events']->listen('cache:clearing', function () {
        // Clear cached primitives
        @unlink($this->app['config']['vibes.service_info.cache_path'].'/vibes.php');
    });
});



namespace Tests\Unit\Tools;

use Tests\TestCase;
use App\Tools\WeatherTool;
use ProjectSaturnStudios\Vibes\TheAgency;
use ProjectSaturnStudios\Vibes\Exceptions\InvalidToolParameters;

class WeatherToolTest extends TestCase
{
    protected TheAgency $agency;
    protected WeatherTool $weatherTool;
    
    protected function setUp(): void
    {
        parent::setUp();
        
        $this->agency = app(TheAgency::class);
        $this->weatherTool = new WeatherTool();
        
        // Register the tool with TheAgency
        $this->agency->addTool($this->weatherTool);
    }
    
    /** @test */
    public function it_has_correct_metadata()
    {
        $metadata = WeatherTool::getMetadata();
        
        $this->assertEquals('weather', $metadata['name']);
        $this->assertArrayHasKey('description', $metadata);
        $this->assertArrayHasKey('parameters', $metadata);
        $this->assertArrayHasKey('properties', $metadata['parameters']);
        $this->assertArrayHasKey('location', $metadata['parameters']['properties']);
    }
    
    /** @test */
    public function it_handles_valid_location()
    {
        $result = $this->weatherTool->handle('San Francisco');
        
        $this->assertIsArray($result);
        $this->assertArrayHasKey('location', $result);
        $this->assertArrayHasKey('temperature', $result);
        $this->assertArrayHasKey('conditions', $result);
        $this->assertEquals('San Francisco', $result['location']);
    }
    
    /** @test */
    public function it_throws_exception_for_invalid_location()
    {
        $this->expectException(InvalidToolParameters::class);
        
        $this->weatherTool->handle('NonExistentLocation123456789');
    }
    
    /** @test */
    public function it_is_registered_with_the_agency()
    {
        $tool = $this->agency->getTool('weather');
        
        $this->assertNotNull($tool);
        $this->assertInstanceOf(WeatherTool::class, $tool);
    }
}



namespace Tests\Unit\Tools;

use Tests\TestCase;
use App\Tools\ExternalApiTool;
use Illuminate\Support\Facades\Http;
use ProjectSaturnStudios\Vibes\Exceptions\ToolExecutionError;

class ExternalApiToolTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        
        // Mock external API responses
        Http::fake([
            'api.example.com/data*' => Http::response([
                'success' => true,
                'data' => [
                    'id' => 123,
                    'name' => 'Test Item',
                    'value' => 99.95
                ]
            ], 200),
            'api.example.com/error*' => Http::response([
                'success' => false,
                'error' => 'Resource not found'
            ], 404),
        ]);
    }
    
    /** @test */
    public function it_fetches_and_formats_external_data()
    {
        $tool = new ExternalApiTool();
        $result = $tool->handle('data', ['id' => 123]);
        
        $this->assertIsArray($result);
        $this->assertTrue($result['success']);
        $this->assertEquals('Test Item', $result['data']['name']);
        $this->assertEquals(99.95, $result['data']['value']);
    }
    
    /** @test */
    public function it_handles_api_errors_appropriately()
    {
        $this->expectException(ToolExecutionError::class);
        
        $tool = new ExternalApiTool();
        $tool->handle('error', ['id' => 999]);
    }
    
    /** @test */
    public function it_retries_on_temporary_failures()
    {
        Http::fake([
            // First call fails, second succeeds
            'api.example.com/flaky*' => Http::sequence()
                ->push(['error' => 'Server busy'], 503)
                ->push(['success' => true, 'data' => ['name' => 'Flaky Test']], 200),
        ]);
        
        $tool = new ExternalApiTool();
        $result = $tool->handle('flaky', ['id' => 456]);
        
        $this->assertTrue($result['success']);
        $this->assertEquals('Flaky Test', $result['data']['name']);
    }
}



namespace Tests\Feature;

use Tests\TestCase;
use App\Tools\CalculatorTool;
use ProjectSaturnStudios\Vibes\TheAgency;
use ProjectSaturnStudios\Vibes\VibeSesh;

class AgencyToolIntegrationTest extends TestCase
{
    protected TheAgency $agency;
    
    protected function setUp(): void
    {
        parent::setUp();
        
        $this->agency = app(TheAgency::class);
        
        // Register test tools
        $this->agency->addTool(CalculatorTool::class);
    }
    
    /** @test */
    public function it_executes_calculator_tool_through_agency()
    {
        // Create a test session
        $session = new VibeSesh('test-session-123');
        
        // Prepare a tool execution request
        $request = [
            'jsonrpc' => '2.0',
            'method' => 'run_tool',
            'id' => 'req-'.time(),
            'session_id' => $session->getId(),
            'params' => [
                'name' => 'calculator',
                'input' => [
                    'operation' => 'add',
                    'a' => 5,
                    'b' => 7
                ]
            ]
        ];
        
        // Process through TheAgency
        $response = $this->agency->processRequest($request, $session);
        
        // Verify response
        $this->assertEquals('2.0', $response['jsonrpc']);
        $this->assertEquals($request['id'], $response['id']);
        $this->assertEquals(12, $response['result']['sum']);
    }
    
    /** @test */
    public function it_returns_proper_error_for_invalid_tool_name()
    {
        $session = new VibeSesh('test-session-123');
        
        $request = [
            'jsonrpc' => '2.0',
            'method' => 'run_tool',
            'id' => 'req-'.time(),
            'session_id' => $session->getId(),
            'params' => [
                'name' => 'non-existent-tool',
                'input' => []
            ]
        ];
        
        $response = $this->agency->processRequest($request, $session);
        
        $this->assertArrayHasKey('error', $response);
        $this->assertEquals(-32601, $response['error']['code']);
    }
}



namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Testing\TestResponse;
use Illuminate\Support\Facades\Event;
use App\Events\AgentConnected;

class SSEConnectionTest extends TestCase
{
    /** @test */
    public function it_establishes_sse_connection_successfully()
    {
        // Listen for connection events
        Event::fake([AgentConnected::class]);
        
        // Make request to SSE endpoint
        $response = $this->get('/mcp/sse', [
            'Accept' => 'text/event-stream',
            'Cache-Control' => 'no-cache',
            'X-Requested-With' => 'XMLHttpRequest',
        ]);
        
        // Check connection setup
        $response->assertStatus(200);
        $response->assertHeader('Content-Type', 'text/event-stream');
        $response->assertHeader('Cache-Control', 'no-cache, private');
        $response->assertHeader('Connection', 'keep-alive');
        
        // Confirm response has SSE format
        $this->assertStringContainsString('data:', $response->getContent());
        $this->assertStringContainsString('id:', $response->getContent());
        $this->assertStringContainsString('event: endpoint-info', $response->getContent());
        
        // Verify event was dispatched
        Event::assertDispatched(AgentConnected::class);
    }
    
    /** @test */
    public function it_



namespace Tests\Unit\Services;

use Tests\TestCase;
use App\Services\ClaudeService;
use Illuminate\Support\Facades\Http;
use ProjectSaturnStudios\Vibes\TheAgency;
use App\Tools\CalculatorTool;

class ClaudeServiceTest extends TestCase
{
    protected ClaudeService $service;
    
    protected function setUp(): void
    {
        parent::setUp();
        
        // Get TheAgency and register tools
        $agency = app(TheAgency::class);
        $agency->addTool(CalculatorTool::class);
        
        // Create service with mocked dependencies
        $this->service = app(ClaudeService::class);
        
        // Mock Claude API responses
        Http::fake([
            'https://api.anthropic.com/v1/messages' => Http::response([
                'id' => 'msg_01234567',
                'model' => 'claude-3-opus-20240229',
                'content' => [
                    ['type' => 'text', 'text' => 'This is a test response from Claude']
                ],
                'tool_calls' => [
                    [
                        'id' => 'call_01234567',
                        'name' => 'calculator',
                        'parameters' => [
                            'operation' => 'multiply',
                            'a' => 4,
                            'b' => 5
                        ]
                    ]
                ]
            ], 200),
        ]);
    }
    
    /** @test */
    public function it_sends_request_to_claude_api()
    {
        $response = $this->service->createMessage('Test prompt');
        
        $this->assertIsArray($response);
        $this->assertArrayHasKey('content', $response);
        $this->assertArrayHasKey('tool_calls', $response);
        $this->assertEquals('This is a test response from Claude', $response['content'][0]['text']);
    }
    
    /** @test */
    public function it_handles_tool_calls()
    {
        // Get a sample tool call from the API response
        $response = $this->service->createMessage('Test prompt');
        $toolCall = $response['tool_calls'][0];
        
        // Process the tool call
        $result = $this->service->handleToolCall($toolCall, 'test-convo-123');
        
        $this->assertIsArray($result);
        $this->assertEquals('success', $result['status']);
        $this->assertEquals(20, $result['result']['product']); // 4 * 5 = 20
    }
}

// In LaravelVibesServiceProvider.php
RateLimiter::for('mcp-agent', function (Request $request) {
    return [
        Limit::perMinute(60)->by($request->session()->getId()),
        Limit::perDay(1000)->by($request->session()->getId()),
    ];
});

// Before Laravel 12
Route::middleware(['mcp-agent', 'throttle:mcp'])->prefix('mcp')->group(function () {
    Route::get('/sse', [MCPAgentEntryController::class, 'open_a_channel']);
    Route::post('/sse/messages', [MCPAgentEntryController::class, 'asController']);
});

// With Laravel 12
Route::middleware(['mcp-agent', 'throttle:mcp'])->group(function () {
    Route::prefix('mcp')->group(function () {
        Route::get('/sse', [MCPAgentEntryController::class, 'open_a_channel']);
        Route::post('/sse/messages', [MCPAgentEntryController::class, 'asController']);
    });
});

readonly class ToolMetadata
{
    public function __construct(
        public string $name,
        public string $description,
        public array $parameters,
        public array $returns
    ) {}
}

enum ToolStatus: string
{
    case ACTIVE = 'active';
    case DEPRECATED = 'deprecated';
    case EXPERIMENTAL = 'experimental';
}

   // Laravel 11.x
   public function handle($parameter1, $parameter2)
   
   // Laravel 12.x
   public function handle(string $parameter1, ?array $parameter2 = null)
   

Route::get('/mcp/sse', [MCPAgentEntryController::class, 'open_a_channel'])
    ->middleware([ScaffoldSSEConnection::class]);

// Update service bindings in your ServiceProvider
$this->app->bind(TheAgency::class, CustomAgency::class);
$this->app->bind(ToolRepository::class, CustomToolRepository::class);

// Laravel 12 now uses the dispatchSync method instead of dispatchNow
ProcessAgentMessageJob::dispatchSync($message);

// config/vibes.php
'tool_execution' => [
    'timeout' => 5, // Maximum time (seconds) for tool execution
    'max_memory' => '256M', // Memory limit for tool execution
    'cache_ttl' => 60, // Cache tool results for similar inputs (seconds)
    'batch_size' => 10, // Batch size for processing multiple tool calls
],



namespace App\Tools;

use ProjectSaturnStudios\Vibes\Primitives\Tools\Data\VibeTool;
use Illuminate\Contracts\Queue\ShouldQueue;

class LongRunningProcessTool extends VibeTool implements ShouldQueue
{
    public $timeout = 300; // 5 minutes
    public $tries = 3;
    public $backoff = 30;
    
    // Tool implementation...
}

// In app/Http/Kernel.php
protected $middleware = [
    // Other middleware...
    \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
    \ProjectSaturnStudios\Vibes\Http\Middleware\CompressResponse::class,
];

// config/logging.php
'channels' => [
    'vibes' => [
        'driver' => 'daily',
        'path' => storage_path('logs/vibes.log'),
        'level' => env('VIBES_LOG_LEVEL', 'warning'),
        'days' => 14,
    ],
],

// In a service provider
$this->app->singleton('vibes.telemetry', function () {
    return new \App\Services\VibeTelemetryService(
        endpoint: config('vibes.telemetry.endpoint'),
        projectId: config('vibes.telemetry.project_id')
    );
});

// In your custom tool
public function handle(string $input)
{
    app('vibes.telemetry')->recordToolExecution($this->getName(), start_time: microtime(true));
    
    // Tool implementation...
    
    app('vibes.telemetry')->recordToolCompletion($this->getName(), microtime(true) - $startTime);
}

// config/vibes.php
'resources' => [
    'max_sse_connections' => 1000, // Maximum concurrent SSE connections
    'sse_timeout' => 3600, // Maximum SSE connection lifetime (seconds)
    'max_concurrent_tools' => 100, // Maximum concurrent tool executions
    'rate_limits' => [
        'tool_execution' => 60, // Maximum tool calls per minute per session
        'message_size' => 1024 * 1024, // Maximum message size in bytes
    ],
],

// config/cors.vibes.php
return [
    'paths' => ['mcp/*'],
    'allowed_methods' => ['GET', 'POST'],
    'allowed_origins' => ['https://your-trusted-domain.com'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['X-Requested-With', 'Content-Type', 'Origin', 'Authorization'],
    'exposed_headers' => [],
    'max_age' => 86400, // 24 hours
    'supports_credentials' => true,
];

public function handle(string $input)
{
    $validated = Validator::make(['input' => $input], [
        'input' => '

RateLimiter::for('mcp-agent', function (Request $request) {
    return [
        Limit::perMinute(60)->by($request->session()->getId()), // Regular operations
        Limit::perMinute(10)->by($request->session()->getId())->for(function ($request) {
            return $request->input('method') === 'run_tool'; // Tool executions
        }),
        Limit::perDay(1000)->by($request->ip()), // Daily limit
    ];
});
bash
php artisan vendor:publish --tag="vibes"
bash
php artisan cache:clear
bash
   php artisan vendor:publish --tag=vibes-config --force
   
bash
# Laravel Forge - Recommended PHP-FPM config
php_value[session.cache_limiter] = nocache
php_value[output_buffering] = 0
php_value[zlib.output_compression] = Off
php_value[implicit_flush] = On
php_value[max_execution_time] = 0
dockerfile
# Dockerfile optimized for Laravel Vibes
FROM php:8.2-fpm

# Install dependencies
RUN apt-get update && apt-get install -y \
    libzip-dev \
    zip \
    unzip \
    && docker-php-ext-install zip pdo_mysql pcntl

# Install Redis extension
RUN pecl install redis && docker-php-ext-enable redis

# Configure PHP for SSE
RUN echo "output_buffering = Off" >> /usr/local/etc/php/conf.d/docker-php-sse.ini \
    && echo "zlib.output_compression = Off" >> /usr/local/etc/php/conf.d/docker-php-sse.ini \
    && echo "implicit_flush = On" >> /usr/local/etc/php/conf.d/docker-php-sse.ini

# Copy application
COPY . /var/www
WORKDIR /var/www

# Install Composer dependencies
RUN composer install --optimize-autoloader --no-dev

# Optimize Laravel
RUN php artisan optimize \
    && php artisan route:cache \
    && php artisan config:cache \
    && php artisan view:cache

# Expose port
EXPOSE 9000

CMD ["php-fpm"]
yaml
option_settings:
  aws:elasticbeanstalk:container:php:phpini:
    document_root: /public
    memory_limit: 256M
    zlib.output_compression: "Off"
    output_buffering: "0"
    max_execution_time: 300

container_commands:
  01_optimize:
    command: "php artisan optimize"
  02_route_cache:
    command: "php artisan route:cache"
  03_config_cache:
    command: "php artisan config:cache"
  04_view_cache:
    command: "php artisan view:cache"
  05_vibes_cache:
    command: "php artisan vibes:cache-primitives"