PHPackages                             kha333n/laravel-code-graph - 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. kha333n/laravel-code-graph

ActiveLibrary

kha333n/laravel-code-graph
==========================

A persistent code graph for Laravel projects. Indexes your codebase into a local SQLite graph and exposes it to AI assistants over MCP, so they get token-efficient, structurally accurate context instead of re-reading files.

v0.1.2(1mo ago)030↑1900%MITPHPPHP ^8.3

Since Apr 8Pushed 1mo agoCompare

[ Source](https://github.com/kha333n/laravel-code-graph)[ Packagist](https://packagist.org/packages/kha333n/laravel-code-graph)[ RSS](/packages/kha333n-laravel-code-graph/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (8)Versions (4)Used By (0)

Laravel Code Graph
==================

[](#laravel-code-graph)

> A persistent, structural map of your Laravel codebase, exposed to AI assistants over MCP — so they get token-efficient, accurate context instead of re-reading whole files.

[![PHP Version](https://camo.githubusercontent.com/ef0054230522e542bc1f908ac005c6c75888dea255bac910f9015e12095e31d7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e332d626c7565)](https://php.net)[![Laravel](https://camo.githubusercontent.com/5b8e585c1631d63a029f7f50b754c8f9a8303cf45ce980700f570cbe5836adf9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d3131253230253743253230313225323025374325323031332d726564)](https://laravel.com)[![License: MIT](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

---

By the AI, For the AI
=====================

[](#by-the-ai-for-the-ai)

### Only AI and God knows how it works, but it works.

[](#only-ai-and-god-knows-how-it-works-but-it-works)

### As per the rule of `If it works, don't touch it` ✨

[](#as-per-the-rule-of-if-it-works-dont-touch-it-)

Why this exists
---------------

[](#why-this-exists)

When you ask an AI assistant (Claude Code, Cursor, Windsurf, Zed, Continue) to make a change in a Laravel project, it typically does this on every turn:

1. `grep` for the symbol you mentioned
2. `Read` whole files to understand the context
3. `Read` more whole files to find the callers
4. `Read` the tests for those files
5. ...and only *then* start editing

That's a lot of tokens spent re-discovering structure that doesn't change between turns. On a real Laravel codebase it adds up to **half or more of every conversation**.

**Laravel Code Graph** parses your project once into a local SQLite graph, keeps it up to date incrementally on every file change, and exposes it to AI assistants via the [Model Context Protocol](https://modelcontextprotocol.io). Instead of grepping and reading whole files, the assistant asks the graph **"where is `ScoringService::score`?"** and gets back:

```
• [method] App\Services\Matching\ScoringService::score
  app/Services/Matching/ScoringService.php:42-58
  public function score(Profile $a, Profile $b): float

```

Then it can `Read` exactly those 16 lines instead of the whole 800-line file.

What it indexes
---------------

[](#what-it-indexes)

A single SQLite file in `.code-graph/index.sqlite` containing:

**Symbols**

- Classes, interfaces, traits, enums, methods, functions
- Each with its FQN, file path, exact line range, signature, and docblock
- Plus a symbol per Blade view file

**Edges (directed relationships)**

- `calls` — method, static, function calls
- `instantiates` — `new Foo(...)` expressions
- `depends_on` — constructor parameter type hints (DI graph)
- `extends` / `implements` / `uses_trait` — class hierarchies
- `has_relation` — Eloquent `hasMany`/`belongsTo`/`morphMany`/etc, with relation type metadata
- `binds` — service container bindings inside service providers
- `as_action` — classes composing the `lorisleiva/laravel-actions` `AsAction` trait
- `route_handler` — `routes/*.php` → controller@method, with HTTP method + URI
- `filament_model` — Filament `Resource` → `Model`
- `migrates_table` — migrations → table pseudo-FQNs
- `blade_includes` — Blade `@include`, `` components, etc.
- `livewire_renders` — `` and `@livewire('foo')` references

**Chunks**

- Symbol-level source slices (one per method, function, small class, or Blade view)
- Mirrored into FTS5 for full-text search by intent or keyword

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

[](#installation)

Requires **PHP 8.3+**, **Laravel 11 / 12 / 13**, and the `pdo_sqlite` extension.

```
composer require kha333n/laravel-code-graph --dev
```

The package auto-discovers via Laravel's package discovery — no service provider registration needed.

Quickstart
----------

[](#quickstart)

```
# Publish config, run the first build, and register the MCP server
php artisan code-graph:install
```

That's it. The install command:

1. Publishes `config/code-graph.php` to your project
2. Builds the initial graph
3. Registers `code-graph` in your project's `.mcp.json` so AI clients can connect (auto-detects Windows + WSL vs native)

After the install you'll see something like:

```
  Files scanned .......................................... 412
  Changed (re-parsed) .................................... 412
  Skipped (unchanged) ...................................... 0
  Symbols emitted ........................................ 2847
  Edges emitted .......................................... 5912
  Chunks emitted ......................................... 2114
  Duration .............................................. 1.84s

```

The index lives at `.code-graph/index.sqlite` — **add `.code-graph/` to your `.gitignore`**.

Subsequent runs only re-parse files that actually changed:

```
  Files scanned .......................................... 412
  Changed (re-parsed) ...................................... 3
  Skipped (unchanged) .................................... 409
  Duration .............................................. 0.01s

```

Connecting to your AI assistant
-------------------------------

[](#connecting-to-your-ai-assistant)

`code-graph:install` does this automatically — it writes a `code-graph` entry into your project's `.mcp.json` (or creates the file if it doesn't exist) and auto-detects whether you're on a native Linux/macOS install or inside WSL on Windows. The resulting entry looks like one of:

```
// native Linux / macOS
{
  "mcpServers": {
    "code-graph": {
      "command": "/usr/bin/php",
      "args": ["/abs/path/to/artisan", "mcp:start", "code-graph"]
    }
  }
}

// Windows + WSL
{
  "mcpServers": {
    "code-graph": {
      "command": "wsl.exe",
      "args": ["/usr/bin/php8.3", "/home/.../artisan", "mcp:start", "code-graph"]
    }
  }
}
```

After installing, **restart your AI assistant** (Claude Code, Cursor, Zed, ...) so it picks up the new MCP server. Verify with the MCP Inspector if needed:

```
php artisan mcp:inspector code-graph
```

If you need to register or re-register the MCP server later (or remove it):

```
php artisan code-graph:install-mcp                 # add or update the entry
php artisan code-graph:install-mcp --force-wsl     # force the WSL command pattern
php artisan code-graph:install-mcp --no-wsl        # force the native pattern
php artisan code-graph:install-mcp --remove        # take it out
```

Existing entries for other servers (like `laravel-boost`) are preserved.

The graph **auto-refreshes on every tool call** (mtime fast path keeps it under a few milliseconds), so the assistant always sees fresh data even right after you save a file.

The MCP tools
-------------

[](#the-mcp-tools)

Every tool returns exact `file:start-end` line ranges so the calling assistant can `Read` only the slice it needs.

### `find-symbol-tool`

[](#find-symbol-tool)

Look up a symbol by name or fully qualified name. Falls back to FTS5 prefix matching if there's no exact hit. By default, also returns the file's `use` statements so you can author a new file from the same template without an extra Read.

```
{ "name": "ScoringService" }
{ "name": "score", "kind": "method" }
{ "name": "App\\Services\\Matching\\ScoringService" }
{ "name": "migration::create_profiles_table" }
{ "name": "config::profile_visibility" }
{ "name": "Profile", "with_imports": false }   // skip imports if you don't need them

```

### `search-code-tool`

[](#search-code-tool)

Keyword and intent search across symbol-level chunks (method bodies, small classes, Blade views) ranked by BM25. Use this when you don't know the exact name but you know what you're looking for.

```
{ "query": "rate limiting middleware" }
{ "query": "send notification" }
{ "query": "Eloquent profile model save" }

```

### `get-callers-tool`

[](#get-callers-tool)

Find every callsite that calls a function, method, or constructor. Pass a class FQN to get callers of any method on that class, or a method FQN for the exact callers.

```
{ "fqn": "App\\Services\\Matching\\ScoringService::score" }
{ "fqn": "App\\Models\\Profile" }
{ "fqn": "?::save" }   // any method named "save"

```

Method calls on dynamic receivers (when the static type isn't resolvable) are recorded with a wildcard receiver `?::methodName`.

### `get-callees-tool`

[](#get-callees-tool)

The inverse: what does this function/method/class call?

```
{ "fqn": "App\\Actions\\MatchProfiles::handle" }
{ "fqn": "App\\Services\\Entitlements\\EntitlementService" }

```

### `get-blast-radius-tool`

[](#get-blast-radius-tool)

Recursive BFS over the call graph going *backwards* from a root symbol — every caller, every caller's caller, up to the requested depth. Use this **before** changing a method to see what might break.

```
{ "fqn": "App\\Models\\Profile::save", "depth": 3 }

```

Results are depth-stratified so you can see which symbols are most directly affected.

### `get-relations-tool`

[](#get-relations-tool)

List Eloquent relations (`hasMany`, `belongsTo`, `morphMany`, etc.) declared on a Model. Returns the relation type, the related model FQN, and the file/line where each relation lives.

```
{ "model": "App\\Models\\Profile" }
{ "model": "App\\Models\\Profile::user" }   // single relation

```

### `get-implementations-tool`

[](#get-implementations-tool)

Find every concrete class that extends, implements, or uses a given class, interface, or trait. The inverse of `get-callers` for inheritance.

```
{ "fqn": "App\\Contracts\\Scorer" }
{ "fqn": "Lorisleiva\\Actions\\Concerns\\AsAction" }

```

### `report-issue-tool` (opt-in)

[](#report-issue-tool-opt-in)

Lets the AI assistant log a case where one of the other tools gave a wrong, missing, incomplete, or misleading result. The package writes the entry to a local markdown file (`.code-graph/feedback.md` by default). Disabled by default — enable it in `config/code-graph.php` if you want to gather a feedback log over time. Local-only; nothing is sent over the network.

```
{
  "tool": "find-symbol-tool",
  "query": "ScoringService",
  "issue": "not_found",
  "expected": "A class symbol",
  "workaround": "grep + Read",
  "notes": "Probably indexed but under a different namespace."
}

```

Commands
--------

[](#commands)

CommandDescription`php artisan code-graph:install`Full setup: publish config, run the first build, and register the MCP server in `.mcp.json`. Pass `--force` to overwrite an existing config or `--skip-mcp` to keep `.mcp.json` untouched.`php artisan code-graph:install-mcp`Just the `.mcp.json` step — register, update, or `--remove` the `code-graph` server entry. Auto-detects Windows/WSL vs native.`php artisan code-graph:build`Re-index the project. Pass `--fresh` to nuke the existing graph and rebuild from scratch.`php artisan code-graph:watch`Long-running incremental indexer for active development. Polls on a 1-second interval; the mtime+size fast path keeps each tick negligible.`php artisan code-graph:wire-claude-md`Insert (or update) an instructions block in your project's `CLAUDE.md` so AI assistants know to prefer code-graph tools over grep/Read. Idempotent. Pass `--remove` to take it out again.`php artisan mcp:start code-graph`Start the local MCP server. Used by AI clients via stdio — you don't normally run this by hand.`php artisan mcp:inspector code-graph`Launch the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) for interactive testing of the tools.Configuration
-------------

[](#configuration)

The published config lives at `config/code-graph.php`:

```
return [
    // Where the SQLite graph file lives.
    'database' => env('CODE_GRAPH_DB', base_path('.code-graph/index.sqlite')),

    // Directories to scan, relative to base_path().
    'scan_paths' => [
        'app',
        'config',
        'database',
        'routes',
        'resources/views',
        'tests',
    ],

    // Substrings of paths to exclude.
    'exclude' => [
        'vendor',
        'storage',
        'bootstrap/cache',
        'node_modules',
        '.code-graph',
    ],

    // File extensions to index.
    'extensions' => ['php'],

    // Optional feedback / analytics layer. Disabled by default. When
    // enabled, the report-issue-tool MCP tool becomes functional and
    // appends entries to the path below. Local-only, never sent over
    // the network.
    'feedback' => [
        'enabled' => env('CODE_GRAPH_FEEDBACK', false),
        'path' => env('CODE_GRAPH_FEEDBACK_PATH', base_path('.code-graph/feedback.md')),
    ],
];
```

You can override the database location with the `CODE_GRAPH_DB` env var if you want the index somewhere other than `.code-graph/`. Set `CODE_GRAPH_FEEDBACK=true` to enable the feedback log.

How it works
------------

[](#how-it-works)

### Indexing pipeline

[](#indexing-pipeline)

1. Walk the configured `scan_paths` with Symfony Finder.
2. For each file: **mtime + size fast path** — if both match the stored values, skip without reading.
3. On a mismatch: read + SHA-256. If the hash matches, just update mtime/size and skip.
4. Otherwise parse with [`nikic/php-parser`](https://github.com/nikic/PHP-Parser) and run a pipeline of small extractors over a single AST traversal:
    - `SymbolVisitor` — class/interface/trait/enum/method/function nodes
    - `InheritanceEdgeVisitor` — `extends`, `implements`, `uses_trait`
    - `CallEdgeVisitor` — calls, instantiations, constructor DI
    - `EloquentRelationVisitor` — `hasMany`/`belongsTo`/etc.
    - `ServiceProviderBindingVisitor` — `bind`/`singleton`/`scoped`/`instance`
    - `AsActionRewriter` — detects the `AsAction` trait and rewrites `Foo::run/dispatch/...` calls into edges targeting `Foo::handle`
    - `FilamentResourceVisitor` — `protected static $model = X::class`
    - `RouteVisitor` — `Route::get/post/...` in `routes/*.php`
    - `MigrationVisitor` — `Schema::create/table` in `database/migrations/`
    - `ChunkExtractor` — slices symbol bodies for full-text search
5. Blade files (`.blade.php`) are handled by a separate extractor that uses regex over the raw text — Blade isn't valid PHP, so nikic's parser doesn't apply.
6. Persist `(file row, symbols, edges, chunks)` to SQLite atomically per file.
7. After every file is processed, run a single `resolveEdges()` pass that joins source/destination FQNs against `symbols.fqn` to fill in numeric symbol IDs — this lets a visitor in file A emit an edge to a symbol that lives in file B without waiting for B to be parsed.

### Storage

[](#storage)

Five tables in a single `.code-graph/index.sqlite` file:

TablePurpose`files`One row per indexed file with sha256 + size + mtime, for incremental change detection`symbols`Class/interface/trait/enum/method/function nodes`edges`Directed relationships between symbols, with optional resolved symbol IDs`chunks`Symbol bodies for the search layer`symbols_fts` / `chunks_fts`FTS5 mirrors for fast text search### Freshness

[](#freshness)

The package guarantees that AI tools always see fresh data:

- **Auto-refresh on every MCP call.** Each tool runs an incremental index pass before answering. Thanks to the mtime + size fast path, the no-op case takes ~1ms even on hundreds of files.
- **In-memory throttle.** If a tool was called less than 250ms ago, the next call reuses the previous freshen — handles the case where an AI client batches several queries in one turn.
- **`code-graph:watch` command** for active development if you want the indexer running continuously in a separate terminal.

### Why this saves tokens

[](#why-this-saves-tokens)

- A `Read` of a 500-line file costs ~3000 tokens. A `find-symbol` call returning a 6-line summary with file:line ranges costs ~50.
- For multi-step tasks (find symbol → find callers → check tests → make change), the graph collapses 4 file reads to 1 grep + 3 line-range reads.
- The chunked search layer means `search-code-tool` returns whole, callable units instead of arbitrary line windows — no more "you read lines 100-200 but the function actually starts at line 95".
- Auto-refresh means the graph stays in sync without per-turn re-indexing prompts cluttering the conversation.

Working with Laravel Boost
--------------------------

[](#working-with-laravel-boost)

If you use [Laravel Boost](https://github.com/laravel/boost) (the official Laravel MCP server), the two are complementary, not overlapping:

LayerSource of truthTool**Versioned package docs** (Laravel, Livewire, Filament, Pest, Spatie...)`composer.lock`Boost `search-docs`**Runtime** (DB schema, last error, logs, browser logs)Live appBoost `database-*`, `last-error`, `browser-logs`**Code structure** (symbols, edges, blast radius, callers, search)Your codebase`laravel-code-graph`Boost owns docs and runtime. `laravel-code-graph` owns structure. Run both side-by-side in your `.mcp.json` and your AI assistant gets the full picture.

Contributing
------------

[](#contributing)

Issues and pull requests are welcome on [GitHub](https://github.com/kha333n/laravel-code-graph). Before opening a PR, please open an issue first to discuss the change — especially if you're touching the storage schema or the MCP tool surface.

To work on the package locally:

```
git clone https://github.com/kha333n/laravel-code-graph.git
cd laravel-code-graph
composer install
vendor/bin/pest
```

The test suite uses `orchestra/testbench` to boot a minimal Laravel app and exercises the indexer end-to-end against fixture files in a temp directory.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance94

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 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

Every ~0 days

Total

3

Last Release

32d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a892740559dde48b168b888bd6d859813fd3d79f5a7c95c67c379405ae3d285c?d=identicon)[kha333n](/maintainers/kha333n)

---

Top Contributors

[![kha333n](https://avatars.githubusercontent.com/u/44539272?v=4)](https://github.com/kha333n "kha333n (10 commits)")

---

Tags

laravelstatic analysismcpaiclaude-codecode-graph

###  Code Quality

TestsPest

Static AnalysisPHPStan

### Embed Badge

![Health badge](/badges/kha333n-laravel-code-graph/health.svg)

```
[![Health](https://phpackages.com/badges/kha333n-laravel-code-graph/health.svg)](https://phpackages.com/packages/kha333n-laravel-code-graph)
```

###  Alternatives

[laravel/boost

Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.

3.4k10.6M272](/packages/laravel-boost)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k43.5M5.2k](/packages/larastan-larastan)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M256](/packages/laravel-dusk)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

71510.9M66](/packages/laravel-mcp)[wnx/laravel-stats

Get insights about your Laravel Project

1.8k1.8M7](/packages/wnx-laravel-stats)[laravel/ai

The official AI SDK for Laravel.

732506.3k60](/packages/laravel-ai)

PHPackages © 2026

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