Laravel MCP Server
# Cortex Agent Runtime — MCP-native AI Agent Framework
**Before: AI doesn't understand your project. After: AI can analyze, generate and debug applications.**
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that turns any coding agent (Claude, Cursor, Codex, OpenCode, and more) into an autonomous engineering brain for the projects you work on. The runtime is **project-type agnostic**: it loads a set of *domains* based on what your project is, so the same runtime works across Laravel apps, Node projects, and anything else.
## Runtime
- **Project-type detection** — `core/detector.ts` inspects the project and activates the matching domains. Generic tools are always loaded; the Laravel domain activates when `composer.json` + `artisan` are present.
- **Domain registration** — `core/registry.ts` exposes `registerDomain(manifest)`; `listTools()` / `callTool()` stay stable so the MCP surface doesn't change between projects.
- **Safe by default** — local-only, offline, no telemetry. Command whitelists, dangerous-command blocking, and sensitive-data redaction are layered in.
## Built-in Domains
| Domain | When it loads | Tools |
|--------|---------------|-------|
| Generic | Always | `gitStatus`, `fileSearch`, `projectTree` |
| Laravel | `composer.json` + `artisan` exist | artisan, schema, model, routes, migrations, CRUD/feature/API generators, debug workflow, intentPlanner, workflowStatus, context, and more |
### Generic domain
| Tool | Description |
|------|-------------|
| `gitStatus` | Git status summary (branch, staged/unstaged changes) |
| `fileSearch` | Search files by glob, excluding `.git` / `node_modules` / `vendor` |
| `projectTree` | Two-level directory tree of the project |
| `listRoles` | List the roles available in this project and the tools each role owns |
### Laravel domain
| Tool | Description |
|------|-------------|
| `artisan` | Run whitelisted `php artisan` commands |
| `migrateStatus` | Migration status |
| `envInfo` / `envInfoSafe` | Environment info (safe variant redacts secrets) |
| `cache` | Clear/cache config, routes, views |
| `configGet` | Inspect config values |
| `schema` | List tables / columns |
| `model` | Scan Eloquent models |
| `log` | Recent log entries |
| `routeList` | Routes with name/URI/method filters |
| `runTest` | Run PHPUnit tests |
| `frontendScanner` | Scan views/js/css structure |
| `makeModel` / `makeController` / `makeMigration` | Scaffold classes |
| `migrationAnalyzer` | Parse migrations into schema |
| `composerAnalyzer` | Project dependencies |
| `projectContext` | Full project context (cached by file mtime) |
| `crudGenerator` | Full CRUD generator |
| `createFeature` | CRUD + Blade views |
| `apiGenerator` | REST API generator (optional Sanctum auth) |
| `debugWorkflow` | Error location, diagnosis, fix suggestions |
| `intentPlanner` | Natural-language request → executable plan |
| `workflowStatus` | List/inspect/resume/rollback runs |
| `contextSource` | Show whether each piece of project context came from cache or was rebuilt live |
| `listRoles` | List the roles available in this project and the tools each role owns |
## Why agents trust their answers
Two design principles borrowed from real-world system-prompt architecture:
- **Roles, not tool lists.** `listRoles` tells the agent who it can be in this project — an *explorer* (read-only investigation), an *engineer* (build and fix), or a *maintainer* (ops and verification) — and which tools each role owns. Instead of facing a flat wall of tools, the agent picks a role and stays inside its boundary. Fewer missteps, clearer intent.
- **Know where your facts come from.** `contextSource` shows whether each module of project context was served from cache or rebuilt in real time, and context itself is assembled through a priority chain (`cache → live → safe default`) instead of "the agent figures it out". When the agent answers, it knows how fresh the facts are — and so do you.
**Before:** an agent stares at 29 tools and stale context it can't date-check.
**After:** it knows its role, its toolset, and the freshness of everything it reads.
## Quick Start
```bash
npm install
npm start
```
Set the project path to work against:
```bash
# Any project (Node, Laravel, ...)
CORTEX_PROJECT_PATH=/path/to/project npm start
# Laravel-specific path resolution (backward compatible)
LARAVEL_PROJECT_PATH=/path/to/laravel-app npm start
# Nothing set → process.cwd()
```
Run the server with a single MCP request to see which tools a project exposes:
```bash
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | \
CORTEX_PROJECT_PATH=/path/to/node-project npx tsx src/index.ts
# → only generic tools
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | \
CORTEX_PROJECT_PATH=/path/to/laravel-app npx tsx src/index.ts
# → generic + laravel tools
```
### With OpenCode
Add to `~/.config/opencode/opencode.jsonc`:
```jsonc
{
"mcp": {
"cortex": {
"type": "local",
"command": ["node", "/path/to/cortex-agent-runtime/dist/index.js"],
"environment": { "CORTEX_PROJECT_PATH": "/path/to/project" }
}
}
}
```
### With Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"cortex": {
"command": "node",
"args": ["/path/to/cortex-agent-runtime/dist/index.js"]
}
}
}
```
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `CORTEX_PROJECT_PATH` | — | Project path (takes priority) |
| `LARAVEL_PROJECT_PATH` | `process.cwd()` | Backward-compatible Laravel project path |
| `PHP_PATH` | `php` | PHP executable path (Laravel domain) |
| `LLM_API_KEY` | *(empty)* | Enables the LLM semantic layer for `intentPlanner` |
| `LLM_BASE_URL` | `https://api.deepseek.com/v1` | OpenAI-compatible API base URL |
| `LLM_MODEL` | `deepseek-chat` | LLM model used for intent analysis |
## Architecture
```
src/
├── index.ts # entry: detect → load domains → register → start MCP
├── core/ # framework layer — project-type agnostic
│ ├── registry.ts # ToolRegistry: registerDomain / listTools / callTool / roles
│ ├── source-chain.ts # resolveChain: priority fallback chain (cache → live → default)
│ ├── mcp.ts # getConfig / getLogger / runCommand
│ ├── logger.ts # leveled logger
│ ├── detector.ts # detectDomains(projectPath) → DomainManifest[]
│ ├── glob.ts # minimal `*` / `**` glob
│ ├── context/ # generic context interface
│ └── tools/ # shared tools (listRoles)
└── domains/
├── generic/ # always loaded: gitStatus / fileSearch / projectTree / listRoles
└── laravel/ # tools / workflows / context / security / planner / manifest
```
Each domain exports a `DomainManifest` (`id`, `name`, `detect`, `getTools`, `getHandlers`, `getProjectPath?`, `roles?`). Roles declare who the agent can be in this project (explorer / engineer / maintainer) and which tools each role owns — the agent can call `listRoles` instead of guessing. The Laravel domain keeps its own runtime (`domains/laravel/mcp.ts`), tools, workflows, context, security, and planner — the pre-existing 24-tool surface is unchanged.
## Requirements
- Node.js 18+
- PHP 8.1+ (for the Laravel domain)
## Pi Extension (port of the MCP tools)
Same toolset, as a [pi coding agent](https://github.com/earendil-works/pi-coding-agent) extension: **34 of the 35 tools** migrated from the MCP server to native pi custom tools, minus `intentPlanner` (pi is itself an LLM; natural-language → plan is redundant there). Source: [`pi-extension/cortex-laravel/`](pi-extension/cortex-laravel/).
### Install
```bash
# either copy …
cp -R pi-extension/cortex-laravel ~/.pi/agent/extensions/
# … or symlink (always in sync with this repo)
ln -s "$(pwd)/pi-extension/cortex-laravel" ~/.pi/agent/extensions/cortex-laravel
```
Then restart pi (or `/reload`). The extension registers 34 tools:
| Domain | Tools |
|--------|-------|
| Generic (5) | `gitStatus`, `fileSearch`, `projectTree`, `listRoles`, `toolStats` |
| Laravel core (5) | `artisan`, `migrateStatus`, `schema`, `model`, `routeList` |
| Laravel ops (12) | `envInfo`, `envInfoSafe`, `cache`, `configGet`, `log`, `runTest`, `makeModel`, `makeController`, `makeMigration`, `migrationAnalyzer`, `composerAnalyzer`, `frontendScanner` |
| Laravel workflows (7) | `crudGenerator`, `createFeature`, `apiGenerator`, `debugWorkflow`, `workflowStatus`, `projectContext`, `contextSource` |
| Orchestration (5) | `taskStatus`, `taskMetrics`, `policyGet`, `taskAccept`, `taskAdvance` |
Project path resolution: `CORTEX_PROJECT_PATH` / `LARAVEL_PROJECT_PATH` env, falling back to pi's session `cwd`. Laravel tools self-guard with a clear error on non-Laravel projects; orchestration tools read/write the same `.htask/` layout as the MCP server (and the standalone `htask` CLI).
### Differences vs the MCP server
- **Safety** — same command whitelist + dangerous-pattern validator (two layers), plus a confirm gate on `taskAdvance` / `taskAccept` / `workflowStatus` (resume·rollback): set `CORTEX_CONFIRM_OFF=1` to allow in scripts.
- **Laravel 11+ compatibility** — `config:get` removed upstream falls back to tinker; API routes auto-create via `artisan install:api` when `routes/api.php` is absent.
- **nix flake projects** — `resolvePhpPath` probes known nix install paths (not only `nix` in `PATH`).
- **Errors** — thrown errors surface as tool errors; tool results are plain text (no MCP JSON-RPC wrapper).
## Development
```bash
npm install
npm run typecheck # tsc --noEmit (type-check only)
npm test # run all tests
npm run build # compile to dist/
npm start # node dist/index.js
npm run dev # npx tsx src/index.ts (hot reload)
```
## License
MIT
TDQS
Scored across 23 tools
Most tools have clearly distinct purposes, but there is overlap between envInfo/envInfoSafe and between migrationAnalyzer/schema for inspecting database structure. The three generators (crudGenerator, createFeature, apiGenerator) also have similar scopes, though descriptions clarify differences.
Tool names use inconsistent patterns: some are camelCase verbs (runTest, makeModel), some are noun-first (envInfo, configGet), some are single nouns (artisan, model, log), and some are compound names (migrationAnalyzer, crudGenerator). No consistent verb_noun convention.
23 tools is on the heavy side; while each has a purpose, the server covers many development workflows, making it borderline but manageable. Not excessive enough for a 2, but more than the typical well-scoped server.
The toolset covers a wide range of Laravel tasks: environment info, migrations, schema inspection, code generation, testing, debugging, and project context. Minor gaps like missing seeding/rollback commands can be worked around via artisan, so not severely incomplete.