fosseva/laravel-web-mcp
fosseva/laravel-web-mcp helps Laravel applications act as web-based MCP servers, exposing tools and resources to AI clients through the Model Context Protocol. It simplifies adding MCP endpoints using familiar Laravel conventions.
This is the first public release (v0.1.0-alpha.1) of fosseva/laravel-web-mcp, an alpha-tagged package that exposes a Laravel application over the Model Context Protocol (MCP) so MCP clients (Claude Desktop, MCP Inspector, IDE agents) can call into your app as tools/resources. There are no release notes beyond the changelog link, so treat the README and the package source as the source of truth — nothing below should be taken as a guaranteed API contract for an alpha.
composer require fosseva/laravel-web-mcp:0.1.0-alpha.1
Pin the exact version. Do not use ^0.1 while in alpha.php artisan vendor:publish --tag="web-mcp-config" (confirm the tag name in the README), then open config/web-mcp.php. This is where transport, route prefix, middleware, and tool registration almost certainly live. Read it before writing any tool code — it tells you more about the package's shape than the docs will.php artisan route:list | grep -i mcp. You want to know the endpoint path, the HTTP verbs, and which middleware stack it sits behind. This is the single most useful debugging step.src/ for the service provider and any Tools/, Resources/, Prompts/, or Server directories. That tells you whether tools are classes, closures, or config entries.mcpServers entry using the HTTP transport) at the endpoint and confirm the tool appears in tools/list and returns a well-formed result from tools/call.Wrap existing application logic, don't reimplement it. The healthiest pattern is a thin tool class that validates input, delegates to an existing Action/Service/Query class, and formats the response. Your MCP surface should be a second adapter over the same domain code your HTTP controllers use — never a parallel implementation with its own business rules.
Separate read and write tool namespaces. Name tools by intent and scope: orders.get, orders.list, orders.cancel. Prefixing by domain makes it obvious to both humans and models which tools are related, and makes it trivial to gate a whole namespace behind a permission check or feature flag.
Gate write tools behind explicit authorization, per tool. Route-level middleware authenticates the connection; it does not authorize the action. Resolve the acting user in middleware, then assert permission inside each mutating tool ($user->can('cancel', $order)). Agents will call the tool you gave them — the tool is the security boundary.
Treat input schemas as validation contracts. Define the JSON input schema and the Laravel validation rules from the same shape. Validate with the Validator facade and translate failures into MCP tool errors rather than letting a ValidationException bubble up and produce an HTML/JSON error page the client can't parse.
Keep transports simple in dev. If the package supports both stateless HTTP POST and a streaming/SSE transport, start with the stateless path — it behaves like a normal Laravel request, works behind your existing middleware, and is far easier to reproduce with curl. Move to streaming only when you actually need server-initiated messages or long-running tool output.
Test with raw JSON-RPC. Feature tests are cheap here: $this->postJson('/mcp', ['jsonrpc' => '2.0', 'id' => 1, 'method' => 'tools/list']) and assert the tool appears; then call each tool with valid and invalid payloads. Cover the auth path explicitly — an unauthenticated tools/call must fail with a structured error, not a redirect to a login page.
Use resources for context, prompts for workflows. Read-only data the model needs repeatedly (schemas, enum lists, current user profile, config) belongs in resources. Multi-step templated workflows belong in prompts. Reserve tools for actions with side effects or parameters.
Environment-gate the whole surface. Register the routes and service provider behind a config flag so production only exposes what you intend, and staging can be permissive. An MCP endpoint is an API — it deserves the same deliberate exposure decision as any other route group.
0.1.0-alpha.1 is a naming convention, not a stability promise. Pin the exact version in composer.json, and read the diff — not just the changelog — before bumping. Expect constructor signatures, config keys, and route names to move.web middleware group includes VerifyCsrfToken. A machine client has no session and no token. Ensure the MCP route sits on api middleware or is explicitly CSRF-exempt; otherwise every call returns 419 and the failure looks like an auth problem.code and message. Unhandled exceptions go through Laravel's exception handler and may return a redirect, an HTML error page, or a non-conforming JSON body. Catch broad exceptions in the tool layer and return structured errors; log the full trace server-side where the client can't see it.description and parameter descriptions are what the model uses to decide when to call. Vague descriptions produce wrong tool selection. State preconditions and side effects plainly: "Cancels an order. Irreversible. Requires the order to be in pending status."Log context plus a middleware is enough.How can I help you explore Laravel packages today?