| name | mcp-development |
| description | Use this skill for Cachet's Laravel MCP server. Trigger when creating or editing MCP tools, resources, prompts, server registration, schemas, annotations, authentication, authorization, visibility, or MCP tests. Do not use for non-Laravel MCP projects or generic AI features without MCP. |
| license | MIT |
| metadata | {"author":"laravel"} |
MCP Development
Documentation First
CRITICAL: Always use search-docs BEFORE writing MCP code. The documentation is version-specific, comprehensive, and always up-to-date.
search-docs(['mcp tools', 'mcp resources', 'mcp validation'])
Core Package Conventions
- Keep the server and primitives under
src/Mcp in the Cachet\\Mcp namespace.
- Register the web server through
CachetCoreServiceProvider; do not create routes/ai.php or modify a host application.
- Do not use
make:mcp-* generators for final code. They target Testbench's host skeleton. Follow sibling tools and create the package file in src/Mcp.
- Keep server availability, authentication, rate limiting, token abilities, and record visibility aligned with
config/cachet.php, the MCP middleware, and existing concerns.
- Every write tool must guard the matching Sanctum ability in both
handle() and shouldRegister().
- Use the Laravel MCP annotation matching the side effect, including
#[IsReadOnly], #[IsIdempotent], and #[IsDestructive] where applicable.
- Put feature coverage under
tests/Feature/Mcp and architecture coverage under tests/Architecture/McpTest.php.
Quick Reference
Package Layout
Inspect the closest tool and its feature test before adding a primitive. Reuse GuardsMcpAbilities, PresentsResources, visibility scopes, pagination, and domain actions instead of duplicating those mechanics.
Basic Tool Implementation
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;
class MyTool extends Tool
{
protected string $description = 'Tool description for LLM';
public function schema(JsonSchema $schema): array
{
return [
'param' => $schema->string()->required(),
];
}
public function handle(Request $request): Response
{
return Response::text($request->get('param'));
}
}
Basic Resource Implementation
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Resource;
class MyResource extends Resource
{
protected string $description = 'Resource description';
protected string $uri = 'file://path/to/resource';
protected string $mimeType = 'text/markdown';
public function handle(): Response
{
return Response::text($content);
}
}
Response Methods
Response::text('Text content');
Response::error('Error message');
Response::structured(['key' => 'value']);
Testing MCP Primitives
Test tools, resources, and prompts directly on their server:
$response = CachetServer::tool(MyTool::class, ['param' => 'value']);
$response->assertOk()->assertSee('Expected text');
Sanctum::actingAs($user, ['resource.manage']);
$response = CachetServer::tool(MyTool::class, [...]);
$response->assertOk();
$response->assertSee('text');
$response->assertHasErrors();
$response->assertHasNoErrors();
$response->assertName('tool-name');
$response->assertSentNotification('event/type', ['data' => 'value']);
For write tools, also assert the database change, side effects, missing ability, and whether the tool is hidden from callers without that ability. Test status-page visibility whenever the tool reads a scoped record.
MCP Inspector
Test interactively using the inspector:
vendor/bin/testbench route:list --name=cachet.mcp
vendor/bin/testbench mcp:inspector mcp
The inspector is interactive and may install frontend tooling. Run it only when interactive debugging is needed.
Available Features
The following features exist—use search-docs for implementation details:
- Tools:
schema(), validation, annotations (#[IsReadOnly], #[IsDestructive], etc.)
- Resources: URI templates (
HasUriTemplate), Dynamic resources
- Prompts: Arguments, multi-message responses
- All primitives: Dependency injection,
shouldRegister(), validation
- Responses: Text, error, structured, streaming, metadata
- Server registration: Web routes, local routes, OAuth
Critical Imports
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Server\Prompt;
use Illuminate\Contracts\JsonSchema\JsonSchema;
Common Pitfalls
- Not using
search-docs before implementation
- Wrong imports:
Laravel\Mcp\Server\Request (wrong) vs Laravel\Mcp\Request (correct)
- Forgetting
schema() method for tools with parameters
- Missing required properties:
$description, $uri, $mimeType
- Wrong response pattern:
new Response() instead of Response::text()
- Generating final package code into Testbench's application skeleton
- Registering a second server in
routes/ai.php instead of extending CachetServer
- Exposing a write tool without the matching Sanctum ability and registration guard
- Returning records without Cachet's visibility scope
- Running
mcp:start command locally (hangs waiting for stdin)