Laravel is convention heavy, which helps an agent right up until conventions change between major versions, and Laravel 11 moved a lot: no HTTP kernel, a slimmer app skeleton, and middleware plus routing registered in bootstrap/app.php. An agent needs the major version, the queue and cache drivers in use, and whether the frontend is Blade, Livewire, or Inertia, because those three lead to completely different code for the same feature. Eloquent's ergonomics are also its trap: the easy way to write a relationship access is usually the way that generates a query per row.
Scoped read and write access to the application directory.
Laravel spreads one feature across routes, controllers, requests, models, and migrations, so the agent needs to see all five to place code correctly.
History, diffs, and blame.
Blame on composer.json and the app skeleton tells the agent which Laravel major version's conventions this codebase actually follows.
Schema inspection and read queries against the application database.
Real schema access stops the agent from guessing column names that Eloquent's magic accessors would otherwise hide until runtime.
Drives a browser through the running application.
Blade, Livewire, and Inertia flows involve session state and redirects that only a real browser round trip exercises.
Reads issues and stack traces from your Sentry project.
Failed queue jobs and exception traces are where Laravel bugs actually surface, well after the request that caused them.
These are the failures that repeat across sessions, so each one belongs in .mcp.json.
Asked for a feature, an agent writes the validation, the Stripe call, the model writes, and the notification into one controller method, because that is the shortest path to working code. The logic then cannot be reused from a command or a queued job and is painful to test. Add to your rules file: 'Controllers validate and delegate only. Business logic lives in a single purpose action or service class under app/Actions, and the controller method should be under about fifteen lines.'
The agent returns Post::all() from the controller and then reads $post->author->name in the Blade view, which is one extra query per row and looks perfectly clean in both files. Nothing fails, the page just gets slower as data grows. Write: 'Every query that feeds a loop must eager load its relationships with with(). Enable Model::preventLazyLoading in the non-production service provider so this throws during development.'
Agents skip Form Requests and mass assign whatever arrived, relying on $fillable as the only guard. That couples your validation to nothing and turns any new fillable column into an unintended write surface. Add: 'Every write endpoint has a dedicated FormRequest class. Pass $request->validated() into the model, never $request->all() or $request->input() collections.'
Agents create app/Http/Kernel.php to register middleware, add providers to config/app.php, or edit RouteServiceProvider to define route groups. In Laravel 11 and later those files are gone and the same registration happens in bootstrap/app.php, so the added code is simply never executed. State the version in your rules file and add: 'Middleware, exception handling, and routing configuration are registered in bootstrap/app.php. Do not create app/Http/Kernel.php.'
Agents call Mail::send or a third party HTTP client directly in the controller, so the user waits on an external service and a timeout becomes a failed request instead of a retry. Laravel's queue exists for exactly this and costs one interface to adopt. Add: 'Outbound email, webhooks, and third party API calls go into a job implementing ShouldQueue and are dispatched, never executed inline in a controller or an Eloquent event.'
Asked to add a column, an agent often finds the original create_table migration and edits it, which works on its own machine after a fresh migrate:fresh and leaves every teammate and every deployed environment without the column. Write the rule as an absolute: 'Never modify a migration that has been committed. Schema changes always get a new timestamped migration, and migrate:fresh is never suggested for an environment with real data.'