PHPackages                             palactix/eloquent-mcp - 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. [Database &amp; ORM](/categories/database)
4. /
5. palactix/eloquent-mcp

ActiveLibrary[Database &amp; ORM](/categories/database)

palactix/eloquent-mcp
=====================

Auto-generate MCP tools from Eloquent models. Add one trait — get a full AI-accessible CRUD server.

v0.1.0(1mo ago)12↓100%MITPHPPHP ^8.2

Since May 5Pushed 1mo agoCompare

[ Source](https://github.com/palactix/eloquent-mcp)[ Packagist](https://packagist.org/packages/palactix/eloquent-mcp)[ Docs](https://github.com/palactix/eloquent-mcp)[ RSS](/packages/palactix-eloquent-mcp/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

Eloquent MCP
============

[](#eloquent-mcp)

The API Resources layer for MCP. Auto-generate [Model Context Protocol](https://modelcontextprotocol.io) tools from Eloquent models. Add one trait to a model and AI agents can list, find, create, update, and delete records — with full control over what they can see, search, and write.

[![Latest Version](https://camo.githubusercontent.com/ca38c366e1ff02a11bc1ad027f57850799de11651bd024bcebed8b1f981c7e83/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f70616c61637469782f656c6f7175656e742d6d63702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/palactix/eloquent-mcp)[![PHP Version](https://camo.githubusercontent.com/24a3635a50b05483af8c61f37c80cd30bc5bd12ed08cc17e55db5cc07419e7af/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f70616c61637469782f656c6f7175656e742d6d63702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/palactix/eloquent-mcp)[![License](https://camo.githubusercontent.com/4669490098c0e2154b8a1d73df5cdf4e0a0daaae32004ad9f5c5905ea91a6cb0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f70616c61637469782f656c6f7175656e742d6d63702e7376673f7374796c653d666c61742d737175617265)](LICENSE)

```
class Post extends Model
{
    use HasMcpTools; // that's it
}
```

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

[](#requirements)

- PHP 8.2+
- Laravel 12 or 13
- [`laravel/mcp`](https://github.com/laravel/mcp)

The Problem
-----------

[](#the-problem)

Laravel's official `laravel/mcp` package gives you the primitives:

- Tool
- Resource
- Server But exposing models to AI requires writing repetitive tool classes. For example:

ModelsRequired Tool Classes1 model5 classes5 models25 classes10 models**50 classes**This is the same boilerplate Laravel has eliminated everywhere else:

- Controllers → `make:controller --resource`
- Admin panels → Filament / Nova
- APIs → API Resources But MCP still requires manual wiring.

---

The Solution
------------

[](#the-solution)

`eloquent-mcp` generates MCP tools directly from your Eloquent models.

Add:

```
use Palactix\EloquentMcp\HasMcpTools;
```

Get:

```
list_posts
find_post
create_post
update_post
delete_post

```

No manual tool classes. No manual schemas. No duplicated logic.

---

Why This Matters
----------------

[](#why-this-matters)

MCP is quickly becoming the **standard interface for AI agents**. Without automation, exposing Laravel models to AI requires:

- Writing repetitive tool classes
- Maintaining JSON schemas manually
- Duplicating authorization logic `eloquent-mcp` applies Laravel's philosophy to MCP:
- Convention over configuration
- Declarative models
- Minimal boilerplate
- Explicit security boundaries

---

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

[](#installation)

```
composer require palactix/eloquent-mcp
```

The service provider is auto-discovered. No other setup needed.

Basic Usage
-----------

[](#basic-usage)

### 1. Add the trait to a model

[](#1-add-the-trait-to-a-model)

```
use Palactix\EloquentMcp\HasMcpTools;

class Post extends Model
{
    use HasMcpTools;

    protected $fillable = ['title', 'body', 'status', 'published_at'];
}
```

With no other configuration this exposes all five tools (`list_posts`, `find_post`, `create_post`, `update_post`, `delete_post`) using the fillable fields as the schema.

### 2. Create an MCP server

[](#2-create-an-mcp-server)

```
use Palactix\EloquentMcp\EloquentMcpServer;

class MyMcpServer extends EloquentMcpServer
{
    protected string $name = 'My App MCP Server';
    protected string $version = '1.0.0';

    protected array $modelClasses = [
        Post::class,
    ];
}
```

### 3. Register a route

[](#3-register-a-route)

Create `routes/ai.php` (auto-loaded by `laravel/mcp`):

```
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp', MyMcpServer::class)
    ->middleware(['auth:sanctum']);
```

### 4. Test it

[](#4-test-it)

```
# List all registered tools
curl -s -X POST https://your-app.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq .

# List posts
curl -s -X POST https://your-app.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_posts","arguments":{"per_page":5}}}' | jq .
```

---

Advanced Usage
--------------

[](#advanced-usage)

### Restricting which tools are exposed

[](#restricting-which-tools-are-exposed)

By default all five operations are exposed. Restrict them with `$mcpTools`:

```
class Post extends Model
{
    use HasMcpTools;

    // Read-only — AI can browse but not modify
    protected static array $mcpTools = ['list', 'find'];
}
```

Available values: `list`, `find`, `create`, `update`, `delete`.

---

### Controlling field visibility

[](#controlling-field-visibility)

Use three separate field lists to control what the AI can see, search, and write:

```
class Post extends Model
{
    use HasMcpTools;

    /** Fields returned in responses */
    protected static array $mcpVisible = [
        'id', 'title', 'status', 'published_at', 'created_at',
    ];

    /** Fields the AI can filter by in list operations */
    protected static array $mcpSearchable = ['status'];

    /** Fields the AI can set on create/update */
    protected static array $mcpWritable = ['title', 'body', 'status', 'published_at'];
}
```

> **Rule of thumb:** keep `$mcpWritable` minimal. Never include fields like `user_id` or `tenant_id` — inject those server-side via `mcpBeforeCreate()`.

---

### Custom labels and tool names

[](#custom-labels-and-tool-names)

By default the tool names are derived from the class name (`Post` → `list_posts`, `find_post`). Override them when the class name doesn't match what the AI should see:

```
class WorkspaceMember extends Model
{
    use HasMcpTools;

    // AI-facing name for a single record
    protected static string $mcpLabel = 'client';
    protected static string $mcpPluralLabel = 'clients';

    // Used in tool names: list_clients, find_client
    protected static string $mcpSlug = 'client';
    protected static string $mcpPluralSlug = 'clients';
}
```

This produces tools named `list_clients` and `find_client` instead of `list_workspace_members`.

---

### Multi-tenancy / scoping queries

[](#multi-tenancy--scoping-queries)

Override `mcpScope()` to isolate data per tenant, user, or any other boundary. Every list, find, update, and delete query passes through this scope.

```
class Post extends Model
{
    use HasMcpTools;

    public static function mcpScope(Builder $query, ?Request $request = null): Builder
    {
        // Scope all queries to the authenticated user
        return $query->where('user_id', $request->user()->id);
    }
}
```

#### Multi-tenant route example

[](#multi-tenant-route-example)

With a `{workspace}` route parameter that resolves to a `Workspace` model:

```
// routes/ai.php
Mcp::web('/mcp/{workspace}', WorkspaceMcpServer::class)
    ->middleware(['auth:sanctum'])
    ->where('workspace', '[a-z0-9-]+');
```

```
class Post extends Model
{
    use HasMcpTools;

    public static function mcpScope(Builder $query, ?Request $request = null): Builder
    {
        $workspace = request()->route('workspace');

        if ($workspace instanceof Workspace) {
            $query->where('workspace_id', $workspace->id);
        }

        return $query;
    }
}
```

---

### Injecting server-side fields on create

[](#injecting-server-side-fields-on-create)

Fields like `user_id`, `workspace_id`, or `created_by_id` should never be settable by AI. Inject them in `mcpBeforeCreate()`, which runs before the record is created and receives the full MCP request:

```
class Post extends Model
{
    use HasMcpTools;

    protected static array $mcpWritable = ['title', 'body', 'status']; // no user_id here

    public static function mcpBeforeCreate(array $data, Request $request): array
    {
        $data['user_id'] = $request->user()->id;
        return $data;
    }
}
```

For multi-tenant apps, resolve the tenant here too — `mcpScope()` is **not** called during create:

```
public static function mcpBeforeCreate(array $data, Request $request): array
{
    $workspace = Workspace::where('slug', request()->route('workspace'))->firstOrFail();

    request()->attributes->set('workspace', $workspace); // cache for mcpCreate()

    $data['workspace_id'] = $workspace->id;
    $data['created_by_id'] = $request->user()->id;

    return $data;
}
```

---

### Replacing create/update/delete with business logic

[](#replacing-createupdatedelete-with-business-logic)

When a simple Eloquent operation isn't enough — dispatching a job, sending a notification, calling a service — override the action hook instead of using the default:

#### `mcpCreate()` — custom creation logic

[](#mcpcreate--custom-creation-logic)

```
class Order extends Model
{
    use HasMcpTools;

    public static function mcpCreate(array $data): static
    {
        // Run through the order service instead of direct Eloquent create
        return app(OrderService::class)->createFromMcp($data);
    }
}
```

#### `mcpUpdate()` — custom update logic

[](#mcpupdate--custom-update-logic)

```
class Post extends Model
{
    use HasMcpTools;

    public static function mcpUpdate(self $record, array $data): void
    {
        // Reschedule the publishing job whenever scheduled_at changes
        $record->update($data);

        if (isset($data['scheduled_at'])) {
            PublishPostJob::dispatch($record)->delay($record->scheduled_at);
        }
    }
}
```

#### `mcpDelete()` — custom delete logic

[](#mcpdelete--custom-delete-logic)

```
class Post extends Model
{
    use HasMcpTools;

    public static function mcpDelete(self $record): void
    {
        // Cancel any pending jobs before deleting
        CancelPostJobs::dispatch($record);
        $record->delete();
    }
}
```

---

### Eager loading relationships

[](#eager-loading-relationships)

Use `$mcpWith` to eager-load relationships into every response:

```
class Post extends Model
{
    use HasMcpTools;

    protected static array $mcpWith = ['author', 'tags'];

    protected static array $mcpVisible = [
        'id', 'title', 'status', 'author', 'tags', 'created_at',
    ];
}
```

---

### Date range filtering

[](#date-range-filtering)

The list tool supports `"from..to"` syntax for any searchable date field automatically:

```
# Posts scheduled in May 2026
{
  "name": "list_posts",
  "arguments": {
    "scheduled_at": "2026-05-01..2026-05-31"
  }
}
```

---

### UUID primary keys

[](#uuid-primary-keys)

Models using Laravel's `HasUuids` trait are detected automatically. The `id` parameter in `find` and `update` tools will use `string` type instead of `integer`:

```
class Post extends Model
{
    use HasMcpTools;
    use HasUuids; // detected automatically — no extra config needed
}
```

---

### Providing AI behavioral guidelines

[](#providing-ai-behavioral-guidelines)

Set `$instructions` on your server to guide how the AI agent should use the tools:

```
class MyMcpServer extends EloquentMcpServer
{
    protected string $name = 'My App MCP Server';
    protected string $version = '1.0.0';

    protected string $instructions = where('user_id', $request->user()->id);
    }

    /** Mutate data before create (inject server-side fields) */
    public static function mcpBeforeCreate(array $data, Request $request): array
    {
        $data['user_id'] = $request->user()->id;
        return $data;
    }

    /** Replace the create operation */
    public static function mcpCreate(array $data): static
    {
        return static::create($data); // default
    }

    /** Replace the update operation */
    public static function mcpUpdate(self $record, array $data): void
    {
        $record->update($data); // default
    }

    /** Replace the delete operation */
    public static function mcpDelete(self $record): void
    {
        $record->delete(); // default — respects SoftDeletes if present
    }
}
```

---

Hook Reference
--------------

[](#hook-reference)

HookCalled whenDefault behaviour`mcpScope($query, $request)`Every list, find, update, deleteNo-op (returns query unchanged)`mcpBeforeCreate($data, $request)`Before create, after field collectionReturns data unchanged`mcpCreate($data)`Create operation`static::create($data)``mcpUpdate($record, $data)`Update operation`$record->update($data)``mcpDelete($record)`Delete operation`$record->delete()`---

Generated Tool Names
--------------------

[](#generated-tool-names)

Given a model class `WorkspacePost` with default configuration:

OperationTool nameList`list_workspace_posts`Find`find_workspace_post`Create`create_workspace_post`Update`update_workspace_post`Delete`delete_workspace_post`Override `$mcpSlug` / `$mcpPluralSlug` to change these.

---

Credits
-------

[](#credits)

Built by [Palactix](https://palactix.com) — social media infrastructure for agencies.

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance93

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

36d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7fb6defa1ec234bbcbb710eb6af1eac4e982806a06597871f7f2a25d3dbcc197?d=identicon)[jitendra@palactix.com](/maintainers/jitendra@palactix.com)

---

Top Contributors

[![jitu-git](https://avatars.githubusercontent.com/u/19606386?v=4)](https://github.com/jitu-git "jitu-git (5 commits)")

---

Tags

laravelmcpaieloquenttoolsModel Context Protocol

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/palactix-eloquent-mcp/health.svg)

```
[![Health](https://phpackages.com/badges/palactix-eloquent-mcp/health.svg)](https://phpackages.com/packages/palactix-eloquent-mcp)
```

###  Alternatives

[kirschbaum-development/eloquent-power-joins

The Laravel magic applied to joins.

1.6k29.9M42](/packages/kirschbaum-development-eloquent-power-joins)[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k8.0M84](/packages/mongodb-laravel-mongodb)[spatie/laravel-sluggable

Generate slugs when saving Eloquent models

1.5k12.4M291](/packages/spatie-laravel-sluggable)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[watson/validating

Eloquent model validating trait.

9743.4M53](/packages/watson-validating)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8733.1M23](/packages/yajra-laravel-oci8)

PHPackages © 2026

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