# Laravel Vurb

> Turn any Laravel app into a production MCP Server. Zero TypeScript.

- **Type:** MCP server
- **Install:** `agentstack add mcp-vinkius-labs-laravel-vurb`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [vinkius-labs](https://agentstack.voostack.com/s/vinkius-labs)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [vinkius-labs](https://github.com/vinkius-labs)
- **Source:** https://github.com/vinkius-labs/laravel-vurb

## Install

```sh
agentstack add mcp-vinkius-labs-laravel-vurb
```

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

## About

**Turn any Laravel app into a production MCP Server. Zero TypeScript.**
PHP 8.2+ Attributes · Presenters that control what the LLM sees · PII Redaction · Eloquent Model Bridge · FSM State Gates · One command — every AI connects.

[](https://packagist.org/packages/vinkius-labs/laravel-vurb)
[](https://www.php.net/)
[](https://laravel.com/)
[](https://modelcontextprotocol.io/)
[](https://github.com/vinkius-labs/laravel-vurb/actions)
[](https://github.com/vinkius-labs/laravel-vurb/actions)
[](LICENSE)
[](llms.txt)
[](https://github.com/vinkius-labs/vurb.ts)

[Vurb.ts Docs](https://vurb.vinkius.com/) · [Quick Start](#quick-start) · [Architecture](#architecture) · [Testing](#testing--mva-assertions) · [llms.txt](llms.txt)

🤖 **Try it right now — zero install:**
[▶ Open in Claude](https://claude.ai/new?q=You+MUST+use+the+Laravel+Vurb+package+%28composer%3A+vinkius-labs%2Flaravel-vurb%29.+Read+the+MANDATORY+architectural+spec+at+https%3A%2F%2Fraw.githubusercontent.com%2Fvinkius-labs%2Flaravel-vurb%2Fmain%2Fllms.txt+%E2%80%94+this+is+NOT+optional.+ALL+code+MUST+follow+the+Laravel+Vurb+MVA+pattern%3A+VurbPresenter+for+egress+control%2C+VurbTool+extending+classes+with+PHP+Attributes+%28%23%5BParam%5D%2C+%23%5BPresenter%5D%2C+%23%5BDescription%5D%2C+%23%5BInstructions%5D%29%2C+VurbRouter+for+grouping.+DO+NOT+use+raw+MCP+SDK%2C+JSON.stringify%2C+or+manual+tool+handlers.+If+ANY+tool+lacks+a+Presenter%2C+your+output+is+WRONG.+Task%3A+Build+MCP+tools+for+patient+records+with+Eloquent.+Redact+SSN+and+diagnosis+from+LLM+output.+Add+an+FSM+that+gates+discharge+tools+until+attending+physician+signs+off.) · [▶ Open in ChatGPT](https://chatgpt.com/?q=You+MUST+use+the+Laravel+Vurb+package+%28composer%3A+vinkius-labs%2Flaravel-vurb%29.+Read+the+MANDATORY+architectural+spec+at+https%3A%2F%2Fraw.githubusercontent.com%2Fvinkius-labs%2Flaravel-vurb%2Fmain%2Fllms.txt+%E2%80%94+this+is+NOT+optional.+ALL+code+MUST+follow+the+Laravel+Vurb+MVA+pattern%3A+VurbPresenter+for+egress+control%2C+VurbTool+extending+classes+with+PHP+Attributes+%28%23%5BParam%5D%2C+%23%5BPresenter%5D%2C+%23%5BDescription%5D%2C+%23%5BInstructions%5D%29%2C+VurbRouter+for+grouping.+DO+NOT+use+raw+MCP+SDK%2C+JSON.stringify%2C+or+manual+tool+handlers.+If+ANY+tool+lacks+a+Presenter%2C+your+output+is+WRONG.+Task%3A+Build+MCP+tools+for+patient+records+with+Eloquent.+Redact+SSN+and+diagnosis+from+LLM+output.+Add+an+FSM+that+gates+discharge+tools+until+attending+physician+signs+off.)

---

## Why Laravel Vurb?

Your Laravel app already has the business logic — Eloquent models, policies, middleware, jobs. Why rewrite it in TypeScript just to connect to an AI agent?

**Laravel Vurb bridges PHP to the [Model Context Protocol](https://modelcontextprotocol.io/).** Write a PHP class. Decorate with attributes. Run `php artisan vurb:serve`. Claude, Cursor, GitHub Copilot, Windsurf — every MCP-compatible client connects instantly.

```
┌─────────────────┐       ┌──────────────────┐       ┌──────────────────┐
│  AI Agent        │ MCP   │  Vurb.ts Daemon   │ HTTP  │  Laravel App     │
│  (Cursor, Claude │◄─────►│  (auto-managed)   │◄─────►│  (your tools)    │
│   Copilot, etc.) │ stdio │  via npx          │ JSON  │  via bridge      │
└─────────────────┘       └──────────────────┘       └──────────────────┘
```

No Node.js knowledge required. No daemon configuration. The package handles everything.

---

## Zero Learning Curve — Ship a SKILL.md, Not a Tutorial

Every package you've adopted followed the same loop: read the docs, study the conventions, hit an edge case, search GitHub issues, re-read the docs. Weeks before your first PR. Your AI coding agent does the same — it hallucinates raw MCP SDK patterns or invents Laravel conventions because it has no formal contract to work from.

Laravel Vurb ships a **[SKILL.md](https://agentskills.io)** — a machine-readable architectural contract that your AI agent ingests before writing a single line. Not a tutorial. Not a "getting started guide" the LLM will paraphrase loosely. A **structural specification**: every PHP Attribute, every VurbTool method, every Presenter composition rule, every middleware signature, every name-inference convention. The agent doesn't approximate — it compiles against the spec.

The agent reads `SKILL.md` and produces:

```php
// app/Vurb/Presenters/PatientPresenter.php — generated by your AI agent
class PatientPresenter extends VurbPresenter
{
    public function toArray($request): array
    {
        return [
            'id'        => $this->id,
            'name'      => $this->name,
            'status'    => $this->status,
            'physician' => $this->attending_physician,
        ];
        // ssn, diagnosis, internal_notes — physically absent from response
    }

    public function systemRules(): array
    {
        return [
            'HIPAA: diagnosis visible in UI blocks but NEVER in conversation text.',
            'Always confirm physician identity before discharge actions.',
        ];
    }

    public function suggestActions(): array
    {
        if ($this->status === 'cleared') {
            return [['tool' => 'patients.discharge', 'reason' => 'Physician has signed off']];
        }
        return [['tool' => 'patients.sign_off', 'reason' => 'Awaiting physician sign-off']];
    }
}
```

```php
// app/Vurb/Tools/Patients/DischargePatient.php — generated by your AI agent

#[Description('Discharge a patient after physician sign-off')]
#[Instructions('NEVER call without verifying physician sign-off. Always confirm patient identity.')]
#[Presenter(PatientPresenter::class)]
#[FsmBind(states: ['cleared'], event: 'DISCHARGE')]
class DischargePatient extends VurbTool
{
    public function verb(): string { return 'mutation'; }

    public function handle(
        #[Param(description: 'Patient ID', example: 'PAT-001')]
        string $id,
    ): Patient {
        $patient = Patient::findOrFail($id);
        $patient->update(['status' => 'discharged', 'discharged_at' => now()]);
        return $patient;
    }
}
```

Correct Presenter with egress firewall — SSN and diagnosis physically stripped. FSM gating that makes `patients.discharge` invisible until physician sign-off. JIT system rules. Suggested actions computed from patient state. **First pass — no corrections.**

This works on Cursor, Claude Code, GitHub Copilot, Windsurf, Cline — any agent that can read a file. The `SKILL.md` is the single source of truth: the agent doesn't need to have been trained on Laravel Vurb, it just needs to read the spec.

> **You don't learn Laravel Vurb. You don't teach your agent Laravel Vurb.** You hand it a 400-line contract. It writes the server. You review the PR.

> 💡 The links above inject a **super prompt** that forces the AI to read [`llms.txt`](llms.txt) before writing code — guaranteeing correct MVA patterns, not hallucinated syntax.

When you install the package, the `SKILL.md` and `llms.txt` are automatically published to your project:

```bash
php artisan vurb:install
# → llms.txt copied to project root
# → .claude/skills/laravel-vurb-development/ created with SKILL.md + reference examples
```

You can also publish them individually:

```bash
php artisan vendor:publish --tag=vurb-llms      # llms.txt → project root
php artisan vendor:publish --tag=vurb-skills     # SKILL.md → .claude/skills/
```

---

## Table of Contents

- [Zero Learning Curve — Ship a SKILL.md, Not a Tutorial](#zero-learning-curve--ship-a-skillmd-not-a-tutorial)
- [Quick Start](#quick-start)
- [How It Works — The Bridge Architecture](#how-it-works--the-bridge-architecture)
- [Writing Tools](#writing-tools)
    - [Your First Tool](#your-first-tool)
    - [Name Inference](#name-inference)
    - [Semantic Verbs](#semantic-verbs)
    - [PHP Attributes — Full Control](#php-attributes--full-control)
    - [Dependency Injection in Handlers](#dependency-injection-in-handlers)
- [Presenters — Control What the LLM Sees](#presenters--control-what-the-llm-sees)
- [Routers — Group & Namespace Tools](#routers--group--namespace-tools)
- [Middleware](#middleware)
- [Eloquent Model Bridge](#eloquent-model-bridge)
- [FSM State Gate — Temporal Tool Governance](#fsm-state-gate--temporal-tool-governance)
- [DLP Redaction — PII Never Reaches the LLM](#dlp-redaction--pii-never-reaches-the-llm)
- [Governance & Lockfile](#governance--lockfile)
- [Observability — Telescope & Pulse](#observability--telescope--pulse)
- [Testing — MVA Assertions](#testing--mva-assertions)
- [Configuration Reference](#configuration-reference)
- [Artisan Commands](#artisan-commands)
- [Architecture](#architecture)
- [Ecosystem](#ecosystem)
- [Contributing](#contributing)
- [License](#license)

---

## Quick Start

```bash
composer require vinkius-labs/laravel-vurb
php artisan vurb:install
```

The installer publishes config, creates `app/Vurb/Tools/`, installs the Node.js daemon, and generates a secure internal token.

Generate your first tool:

```bash
php artisan vurb:make-tool GetCustomerProfile --query
```

```php
// app/Vurb/Tools/GetCustomerProfile.php

namespace App\Vurb\Tools;

use Vinkius\Vurb\Attributes\Param;
use Vinkius\Vurb\Tools\VurbTool;

class GetCustomerProfile extends VurbTool
{
    public function description(): string
    {
        return 'Retrieve a customer profile by ID.';
    }

    public function verb(): string
    {
        return 'query';
    }

    public function handle(
        #[Param(description: 'The customer ID', example: 42)]
        int $id,
    ): array {
        $customer = \App\Models\Customer::findOrFail($id);

        return $customer->only(['id', 'name', 'plan', 'created_at']);
    }
}
```

Start the server:

```bash
php artisan vurb:serve
```

That's it. Your Laravel app is now an MCP server. Connect any AI client.

---

## How It Works — The Bridge Architecture

Laravel Vurb uses a **thin daemon bridge** — a lightweight [Vurb.ts](https://github.com/vinkius-labs/vurb.ts) process that speaks MCP natively over stdio/HTTP. The daemon reads a compiled **Schema Manifest** from your PHP tool definitions and proxies every tool call back to Laravel over HTTP.

```
                          Schema Manifest (JSON)
                        ┌───────────────────────┐
                        │  tools, presenters,    │
php artisan vurb:serve  │  models, FSM, state    │  VURB_DAEMON_READY
        │               │  sync, skills          │        │
        ▼               └───────────┬───────────┘        ▼
┌──────────────┐                    │            ┌──────────────┐
│   Laravel    │  POST /_vurb/...   │            │  Vurb.ts     │
│   Bridge     │◄───────────────────┤────────────│  Daemon      │
│   Controller │  (X-Vurb-Token)    │            │  (npx tsx)   │
└──────────────┘                    │            └──────────────┘
                                    │                    ▲
                                    │               MCP  │
                                    │                    │
                              ┌─────┴──────┐    ┌───────┴──────┐
                              │  Manifest   │    │  AI Client   │
                              │  Compiler   │    │  (Cursor,    │
                              │             │    │   Claude,    │
                              └─────────────┘    │   Copilot)   │
                                                 └──────────────┘
```

**Key design decisions:**

- **No Node.js knowledge required** — the daemon is auto-installed and managed via `npx`
- **Timing-safe token authentication** — bridge endpoints are protected with `X-Vurb-Token`
- **PHP reflection → JSON Schema** — your typed `handle()` parameters become the tool's input schema automatically
- **Zero config manifests** — the compiler reads your tool classes, attributes, and router structure

---

## Writing Tools

### Your First Tool

Every tool extends `VurbTool` and implements `handle()`:

```php
use Vinkius\Vurb\Tools\VurbTool;

class ListOrders extends VurbTool
{
    public function description(): string
    {
        return 'List recent orders for a customer.';
    }

    public function handle(int $customer_id, int $limit = 10): array
    {
        return Order::where('customer_id', $customer_id)
            ->latest()
            ->limit($limit)
            ->get()
            ->toArray();
    }
}
```

**That's it.** The reflection engine reads your type hints:

- `int $customer_id` → `{ "type": "integer", "description": "customer_id" }` (required)
- `int $limit = 10` → `{ "type": "integer", "description": "limit" }` (optional, default: 10)

### Name Inference

Tool names are auto-inferred from the class name. No manual wiring:

| Class Name           | Inferred Name           |
| :------------------- | :---------------------- |
| `GetCustomerProfile` | `customers.get_profile` |
| `CreateInvoice`      | `invoices.create`       |
| `ListOrders`         | `orders.list`           |
| `SearchProducts`     | `products.search`       |
| `ProcessPayment`     | `payments.process`      |
| `SendNotification`   | `notifications.send`    |

Override with `public function name(): string { return 'my.custom_name'; }`.

### Semantic Verbs

Every tool declares its intent. The daemon uses this for MCP annotations:

```php
public function verb(): string
{
    return 'query';     // read-only, idempotent, cacheable
    return 'mutation';  // writes data, destructive, invalidates cache
    return 'action';    // side-effect (email, webhook), idempotent
}
```

### PHP Attributes — Full Control

Decorate tools and parameters with attributes for precise schema generation:

```php
use Vinkius\Vurb\Attributes\{Tool, Param, Description, Instructions, Tags, Presenter, Cached, Invalidates, FsmBind, Concurrency};

#[Description('Deep search across all customer records')]
#[Instructions('Only call when the user explicitly asks for a customer lookup. Never infer customer IDs.')]
#[Tags('crm', 'search')]
#[Cached(ttl: 120)]
#[Concurrency(max: 3)]
class SearchCustomers extends VurbTool
{
    public function description(): string
    {
        return 'Search customers by name or email.';
    }

    public function verb(): string
    {
        return 'query';
    }

    public function handle(
        #[Param(description: 'Search query — name, email, or phone', example: 'jane.doe@acme.com')]
        string $query,

        #[Param(description: 'Max results to return')]
        int $limit = 20,
    ): array {
        return Customer::search($query)->take($limit)->get()->toArray();
    }
}
```

| Attribute         | Target       | Purpose                                                      |
| :---------------- | :----------- | :----------------------------------------------------------- |
| `#[Tool]`         | Class        | Override `name()`, `description()`                           |
| `#[Param]`        | Parameter    | Description, example, enum items                             |
| `#[Description]`  | Class/Method | Override description string                                  |
| `#[Instructions]` | Class        | Anti-hallucination instructions injected into LLM context    |
| `#[Tags]`         | Class        | Capability filtering tags                                    |
| `#[Presenter]`    | Class        | Link to Presenter class                                      |
| `#[Cached]`       | Class        | Cache tool results (optional TTL)                            |
| `#[Stale]`        | Class        | Mark as ephemeral (always refetch)                           |
| `#[Invalidates]`  | Class        | Mutation invalidation patterns (`'customers.*'`)             |
| `#[FsmBind]`      | Class        | FSM state restriction (tool only visible in specific states) |
| `#[Concurrency]`  | Class        | Max parallel executions                                      |
| `#[AgentLimit]`   | Class        | Rate limit per agent session                                 |
| `#[Hidden]`       | Parameter    | Exclude from LLM-visible schema

…

## Source & license

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

- **Author:** [vinkius-labs](https://github.com/vinkius-labs)
- **Source:** [vinkius-labs/laravel-vurb](https://github.com/vinkius-labs/laravel-vurb)
- **License:** MIT

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:** no
- **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-vinkius-labs-laravel-vurb
- Seller: https://agentstack.voostack.com/s/vinkius-labs
- 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%.
