Skip to main content
Glama
dok4everak47

Laravel MCP Server

by dok4everak47
README.md
# 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

B3.4/5.0

Scored across 23 tools

Disambiguation4/5

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.

Naming Consistency2/5

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.

Tool Count3/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues