Skip to main content
Glama
marcelosmbr2

Laravel MCP Server

by marcelosmbr2
README.md
# Project Manager — Laravel MCP Server

A small **project & task manager** exposed as a [Model Context Protocol](https://modelcontextprotocol.io)
(MCP) server, built with [`laravel/mcp`](https://github.com/laravel/mcp). It is a learning-oriented
reference that exercises every core MCP primitive — **Tools**, **Resources**, and **Prompts** —
over a deliberately simple domain (projects that contain tasks).

An MCP client (Claude Desktop, Cursor, Claude Code, or the built-in inspector) connects to the
server over **stdio** and lets an AI create and track tasks, pull read-only context, and run
reusable prompt templates.

## Domain

- **Project** — `name`, `description`, and many tasks.
- **Task** — belongs to a project; has a `title`, `description`, `due_date`, a `status`
  (`pending`, `in_progress`, `done`) and a `priority` (`low`, `medium`, `high`, `critical`).
  A task is **overdue** when its due date has passed and it is not yet done.

Priorities carry a numeric `weight` (low = 1 … critical = 4) used to sort reports and triage
overdue work.

## Project structure

```
app/
├── Enums/          TaskStatus, TaskPriority (with label() / weight() helpers)
├── Models/         Project, Task
├── Mcp/
│   ├── Servers/    ProjectManagerServer — registers all primitives
│   ├── Tools/      The 5 callable actions
│   ├── Resources/  The 5 read-only resources
│   └── Prompts/    The 4 prompt templates
└── Support/        ReportBuilder — shared report aggregation
```

## MCP primitives

### 🔧 Tools — actions the AI can invoke

| Tool | Description |
| --- | --- |
| `create-task-tool` | Create a task under a project (title, description, priority, due date). |
| `list-tasks-tool` | List tasks with optional filters: project, status, priority, overdue. |
| `update-task-status-tool` | Move a task to a new status and report the before → after change. |
| `delete-task-tool` | Delete a task by id. |
| `generate-report-tool` | Aggregate tasks into a portfolio report as **Markdown** or **JSON**. |

### 📚 Resources — read-only context addressed by URI

| Resource | URI | Description |
| --- | --- | --- |
| System documentation | `documentation` | Onboarding overview of the server and its entities. |
| Priority rules | `priority-rules` | The priority levels, their weights, and when to use each. |
| Task details | `task://tasks/{task_id}` | Full detail for a single task (templated). |
| Project summary | `project://projects/{project_id}` | Task counts by status, overdue count, next due (templated). |
| Report | `report://reports/{format}` | Whole-portfolio report in `json` or `markdown` (templated). |

The report tool and the report resource share a single [`ReportBuilder`](app/Support/ReportBuilder.php)
so their numbers can never drift apart.

### 💬 Prompts — reusable, argument-driven templates

| Prompt | Arguments | Description |
| --- | --- | --- |
| `plan-project-prompt` | `goal` (required), `deadline` | Break a goal into a project plus milestone tasks. |
| `decompose-task-prompt` | `task_id` (required) | Split one real task into concrete subtasks. |
| `review-overdue-tasks-prompt` | `project_id` | Triage and re-prioritize overdue tasks. |
| `daily-summary-prompt` | `project_id` | Produce a stand-up style daily summary. |

## Getting started

```bash
composer install
php artisan migrate

# Seed at least one project so tasks have somewhere to live:
php artisan tinker --execute "App\Models\Project::factory()->create(['name' => 'Website Redesign']);"
```

The server is registered in [`routes/ai.php`](routes/ai.php) and exposed over stdio:

```php
Mcp::local('project-manager', ProjectManagerServer::class);
```

## Running & testing the server

```bash
# Interactive debugging — drive tools, resources and prompts by hand:
php artisan mcp:inspector project-manager

# Run the server over stdio (this is the command MCP clients launch;
# on its own it will appear to hang, waiting for JSON-RPC on stdin):
php artisan mcp:start project-manager
```

### Connecting from an AI client

**Claude Code**

```bash
claude mcp add project-manager -- php "<full-path>/artisan" mcp:start project-manager
```

**Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "project-manager": {
      "command": "php",
      "args": ["<full-path>/artisan", "mcp:start", "project-manager"]
    }
  }
}
```

Passing the full path to `artisan` lets Laravel resolve the project root regardless of the
client's working directory.