PHPackages                             cloudstudio/laravel-codemode - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. cloudstudio/laravel-codemode

ActiveLibrary[API Development](/categories/api)

cloudstudio/laravel-codemode
============================

Code Mode pattern for Laravel MCP — 2 tools to explore and call any API

432PHP

Since Feb 22Pushed 4mo agoCompare

[ Source](https://github.com/cloudstudio/laravel-codemode)[ Packagist](https://packagist.org/packages/cloudstudio/laravel-codemode)[ RSS](/packages/cloudstudio-laravel-codemode/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Code Mode
=================

[](#laravel-code-mode)

**2 tools instead of 200.** Give any AI assistant full access to your Laravel API through just two MCP tools.

Instead of registering one MCP tool per endpoint (which doesn't scale), Code Mode lets the AI write JavaScript that runs in a secure V8 sandbox. It discovers your API by querying the OpenAPI spec, then calls endpoints with a simple `api()` function — all without the spec ever entering the AI's context window.

```
Traditional MCP          Code Mode
────────────────         ────────────────
GET /users        →      search  (explore spec)
POST /users       →      execute (call any endpoint)
GET /users/{id}   →
PUT /users/{id}   →      That's it. 2 tools.
DELETE /users/{id}→
GET /products     →
POST /products    →
...26 more tools  →

```

Why Code Mode?
--------------

[](#why-code-mode)

MetricTraditional (1 tool per endpoint)Code ModeTools registered26+ (grows with API)**2** (fixed)Tool calls for a cross-entity report~14 sequential calls**1** execute callOpenAPI spec in AI context~31,700 tokens every message**0 tokens** (lives in sandbox)Can join data across endpoints?Manually, with many round-trips**Yes** — write JS that calls multiple endpointsCan paginate automatically?No (1 page per tool call)**Yes** — loop inside executeCan do computed fields / aggregations?No**Yes** — full JavaScript### Real-world example

[](#real-world-example)

> "Show me total revenue per product category with the number of orders in each"

**Traditional approach**: 14 sequential tool calls (list products, list orders page by page, return raw data, hope the AI can compute the rest).

**Code Mode**: 1 search + 1 execute:

```
// Single execute call — the AI writes this
const [products, orders] = await Promise.all([
  api('GET', '/products'),
  api('GET', '/orders')
]);

const revenue = {};
for (const o of orders.data) {
  const product = products.data.find(p => p.id === o.product_id);
  const cat = product?.category || 'unknown';
  revenue[cat] = revenue[cat] || { category: cat, revenue: 0, orders: 0 };
  revenue[cat].revenue += o.total;
  revenue[cat].orders++;
}
Object.values(revenue).sort((a, b) => b.revenue - a.revenue)
```

Result:

```
[
  { "category": "electronics", "revenue": 24531.80, "orders": 87 },
  { "category": "books", "revenue": 12090.50, "orders": 142 },
  { "category": "clothing", "revenue": 8721.30, "orders": 63 }
]
```

See [BENCHMARKS.md](BENCHMARKS.md) for more examples.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 12+
- Node.js 22 (for the V8 sandbox — `isolated-vm` is not yet compatible with Node 24)
- [`laravel/mcp`](https://github.com/laravel/mcp) ^0.5
- An OpenAPI spec source (see [OpenAPI Spec](#openapi-spec) below)

Installation
------------

[](#installation)

### 1. Install the official Laravel MCP package (if you haven't already)

[](#1-install-the-official-laravel-mcp-package-if-you-havent-already)

```
composer require laravel/mcp
php artisan install:mcp
```

This sets up the MCP server infrastructure. See the [laravel/mcp docs](https://github.com/laravel/mcp) for details.

### 2. Install Code Mode

[](#2-install-code-mode)

```
composer require cloudstudio/laravel-codemode
php artisan codemode:install
```

The install command will:

1. Publish `config/codemode.php`
2. Copy the sandbox to your project root (`sandbox/`)
3. Run `npm install` inside the sandbox (installs `isolated-vm` and `acorn`)
4. Publish MCP routes if not already present

Add the sandbox dependencies to your `.gitignore`:

```
/sandbox/node_modules
```

### 3. Set up your OpenAPI spec

[](#3-set-up-your-openapi-spec)

Code Mode needs an OpenAPI spec to let the AI discover your endpoints. The easiest way is [Scramble](https://scramble.dedoc.co/) — it auto-generates the spec from your Laravel controllers and form requests:

```
composer require dedoc/scramble
```

That's it. Scramble works out of the box with no configuration. Code Mode will auto-detect it.

> **Don't use Scramble?** You can point to any OpenAPI JSON file — see [OpenAPI Spec](#openapi-spec) below.

### 4. Configure your API prefix

[](#4-configure-your-api-prefix)

If your API routes are prefixed (e.g. `/api/v1/users`), set the prefix so the AI can use short paths like `/users`:

```
CODEMODE_API_PREFIX=/api
```

### 5. Start the MCP server

[](#5-start-the-mcp-server)

```
# Local mode (for Claude Code, Cursor, etc.)
php artisan mcp:start codemode

# Or via HTTP (for web-based AI clients)
# The route is auto-registered at /mcp/codemode
```

### 6. Connect your AI client

[](#6-connect-your-ai-client)

#### Claude Code / Claude Desktop

[](#claude-code--claude-desktop)

Add to your MCP config (`~/.claude/mcp.json` or Claude Desktop settings):

```
{
  "mcpServers": {
    "codemode": {
      "command": "php",
      "args": ["artisan", "mcp:start", "codemode"],
      "cwd": "/path/to/your/laravel/project"
    }
  }
}
```

#### Cursor

[](#cursor)

Add to `.cursor/mcp.json` in your project:

```
{
  "mcpServers": {
    "codemode": {
      "command": "php",
      "args": ["artisan", "mcp:start", "codemode"],
      "cwd": "/path/to/your/laravel/project"
    }
  }
}
```

Now ask your AI anything about your API — it will use `search` and `execute` automatically.

How It Works
------------

[](#how-it-works)

### Tool 1: `search` — Explore the API

[](#tool-1-search--explore-the-api)

The AI writes JavaScript that runs against your full OpenAPI spec (available as the `spec` variable). The spec never enters the AI's context — it lives entirely inside the sandbox.

```
// List all endpoints
Object.entries(spec.paths).map(([p, ops]) => ({ path: p, methods: Object.keys(ops) }))
```

```
// Find product-related endpoints
Object.entries(spec.paths)
  .filter(([p]) => p.includes('product'))
  .map(([p, ops]) => ({ path: p, methods: Object.keys(ops) }))
```

```
// Get the request body schema for creating a product
spec.paths['/products'].post.requestBody.content['application/json'].schema
```

### Tool 2: `execute` — Call the API

[](#tool-2-execute--call-the-api)

The AI writes JavaScript using `await api(method, path, data)`:

```
// Simple GET
const res = await api('GET', '/products');
pluck(res.data, ['id', 'name', 'price'])
```

```
// POST with body
const product = await api('POST', '/products', { name: 'Widget', price: 29.99 });
pick(product.data, ['id', 'name'])
```

```
// Complex: parallel requests + data joining
const [orders, users] = await Promise.all([
  api('GET', '/orders'),
  api('GET', '/users')
]);
const userMap = Object.fromEntries(users.data.map(u => [u.id, u.name]));
orders.data.slice(0, 5).map(o => ({
  order_id: o.id,
  customer: userMap[o.user_id],
  total: o.total,
  status: o.status
}))
```

### Sandbox Helpers

[](#sandbox-helpers)

Two helpers keep responses compact (critical for staying within AI context limits):

- **`pick(obj, keys)`** — Select specific keys from an object:

    ```
    pick(user, ['name', 'email'])  // { name: 'John', email: 'john@example.com' }
    ```
- **`pluck(arr, keys)`** — Select keys from every item in an array:

    ```
    pluck(users, ['id', 'name'])  // [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
    ```
- **`console.log()`** — Debug output (appears in a separate logs section).

Configuration
-------------

[](#configuration)

### API Settings

[](#api-settings)

```
// config/codemode.php

'api' => [
    'base_url' => env('CODEMODE_API_URL', env('APP_URL')),
    'prefix'   => env('CODEMODE_API_PREFIX', ''),
    'headers'  => [],
],
```

KeyDescriptionDefault`base_url`Where API calls are sent`APP_URL``prefix`Auto-prepended to paths (e.g. `/api/v1`)`''``headers`Static headers for every request`[]`### OpenAPI Spec

[](#openapi-spec)

```
'spec' => [
    'source'     => env('CODEMODE_SPEC_SOURCE', 'auto'),
    'url'        => env('CODEMODE_SPEC_URL'),
    'path'       => env('CODEMODE_SPEC_PATH'),
    'cache'      => true,
    'cache_path' => storage_path('app/openapi-spec.json'),
],
```

SourceHow it worksSetup`auto` (default)Tries Scramble → URL → fileJust install Scramble`scramble`Generates spec from your controllers at runtime`composer require dedoc/scramble``url`Fetches spec from a remote URLSet `CODEMODE_SPEC_URL=https://...``file`Reads a local JSON fileSet `CODEMODE_SPEC_PATH=docs/openapi.json`The spec is resolved (all `$ref` pointers flattened) and cached on first load. To rebuild:

```
rm storage/app/openapi-spec.json
```

### Sandbox

[](#sandbox)

```
'sandbox' => [
    'timeout'     => 5000,       // ms
    'memory'      => 64,         // MB (V8 heap limit)
    'path'        => base_path('sandbox'),
    'node_binary' => env('CODEMODE_NODE_BINARY', 'node'),
],
```

### Excluding HTTP Methods

[](#excluding-http-methods)

Hide destructive endpoints from the AI:

```
CODEMODE_EXCLUDE_METHODS=delete
```

### Debug Mode

[](#debug-mode)

```
CODEMODE_DEBUG=true
CODEMODE_DEBUG_CHANNEL=daily
```

Logs every sandbox execution (code in, result out). Bearer tokens are automatically redacted.

Extending Code Mode
-------------------

[](#extending-code-mode)

### Custom Authentication (per-request)

[](#custom-authentication-per-request)

Override `resolveApiContext()` to inject dynamic auth headers:

```
use Cloudstudio\LaravelCodemode\Tools\ExecuteTool;

class MyExecuteTool extends ExecuteTool
{
    protected function resolveApiContext(): array
    {
        return [
            'baseUrl' => 'https://api.example.com',
            'headers' => [
                'Authorization' => 'Bearer ' . auth()->user()->api_token,
            ],
            'prefix' => '/v2',
        ];
    }
}
```

### Custom Server

[](#custom-server)

Extend CodeModeServer to inject context, swap tools, or customize instructions:

```
use Cloudstudio\LaravelCodemode\Servers\CodeModeServer;
use Cloudstudio\LaravelCodemode\Tools\SearchTool;

class MyServer extends CodeModeServer
{
    protected string $name = 'My API';

    protected array $tools = [
        SearchTool::class,
        MyExecuteTool::class,
    ];

    protected function boot(): void
    {
        parent::boot();

        $this->instructions .= "\n\n## Your Context";
        $this->instructions .= "\n- User: " . auth()->user()?->name;
        $this->instructions .= "\n- Role: " . auth()->user()?->role;
    }
}
```

Then disable auto-registration and register your own:

```
// config/codemode.php
'auto_register' => false,
```

```
// AppServiceProvider or routes/mcp.php
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/my-api', MyServer::class);
Mcp::local('my-api', MyServer::class);
```

### Extension Points

[](#extension-points)

Extension PointHowUse Case`ExecuteTool::resolveApiContext()`Override in subclassPer-user auth, dynamic base URL`CodeModeServer::boot()`Override in subclassInject user context into instructions`CodeModeServer::$tools`Set in subclassSwap tool implementationsConfig values`.env` / `config/codemode.php`Spec source, sandbox limits, API prefixSecurity
--------

[](#security)

- **V8 isolation**: The sandbox uses `isolated-vm` — code cannot access the filesystem, network (except via `api()`), or Node.js APIs.
- **Arbitrary paths**: The `api()` function can call any endpoint. Restrict access via `exclude_methods`, API middleware, or custom `resolveApiContext()` logic.
- **Auth redaction**: Debug logs automatically strip Bearer tokens.
- **Error sanitization**: API responses with `exception` keys have stack traces removed before reaching the AI.
- **No DELETE by default?** Not restricted. Use `CODEMODE_EXCLUDE_METHODS=delete` to hide destructive endpoints from the spec.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE) for details.

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance52

Moderate activity, may be stable

Popularity9

Limited adoption so far

Community4

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3589377?v=4)[Toni Soriano](/maintainers/cloudstudio)[@cloudstudio](https://github.com/cloudstudio)

### Embed Badge

![Health badge](/badges/cloudstudio-laravel-codemode/health.svg)

```
[![Health](https://phpackages.com/badges/cloudstudio-laravel-codemode/health.svg)](https://phpackages.com/packages/cloudstudio-laravel-codemode)
```

###  Alternatives

[exsyst/swagger

A php library to manipulate Swagger specifications

35916.4M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24016.2M20](/packages/hubspot-api-client)[pocketmine/bedrock-protocol

An implementation of the Minecraft: Bedrock Edition protocol in PHP

172445.0k15](/packages/pocketmine-bedrock-protocol)[botman/driver-telegram

Telegram driver for BotMan

93459.5k6](/packages/botman-driver-telegram)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
