Search by

A queryable code graph for Laravel applications: dependency injection, container bindings, routes, views, Eloquent relations and documentation, connected the way generic AST tools cannot see.

Package info

github.com/Nexvia-Solutions/mapin

pkg:composer/nexvia-solutions/mapin

Statistics

Installs: 239

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.6 2026-11-09 02:12 UTC

This package is auto-updated.

Last update: 2026-11-09 02:13:36 UTC


README

A queryable code graph for Laravel applications: dependency injection, container bindings, routes, views, Eloquent relations, Markdown docs and JavaScript-to-route calls, connected the way a generic tree-sitter-based tool cannot see.

English | Español

Why

Generic code graph tools only see what is visible in a single file's own syntax. Most of the wiring that actually matters in a real Laravel application happens through the container, through strings (route('name'), view('name')), and through convention (hasMany, $listen, Route::get), none of which a syntax-only parser ever sees. Measured on a real application of about 900 PHP files, a generic tree-sitter extractor captured static calls and nothing else: zero edges for dependency injection, new, member calls, routes, views, Blade includes or Eloquent relations. Mapin exists to cover exactly that gap, for an AI coding agent asking "what breaks if I change this" over MCP, or a developer asking the same thing from the CLI - and to say so honestly when it does not know: found: false with labelled suggestions, never a look-alike match dressed up as the real answer.

Requirements

  • PHP 8.2 or newer
  • Laravel 11.45+, 12, or 13
  • ext-pdo and ext-sqlite3 (bundled with PHP on almost every install)

The test suite runs on every combination of those: PHP 8.2 to 8.4 against Laravel 11, 12 and 13 (Laravel 13 needs PHP 8.3 or newer). The MCP server comes from the stable laravel/mcp 1.x, which Composer pulls in on its own.

Install

composer require nexvia-solutions/mapin --dev

Nothing else: no minimum-stability change and no extra repository.

The service provider registers itself automatically (Laravel package auto-discovery). Publish the config if you want to change the defaults - indexed paths, storage location, the semantic layer's LLM driver, community detection's hub percentile:

php artisan vendor:publish --tag=mapin-config

Quick start

# Build the graph - full the first time, incremental (by content hash) every time after
php artisan mapin:build --full

# Ask something
php artisan mapin:find BookController
php artisan mapin:find 'BookController::store'
php artisan mapin:route books.store
php artisan mapin:impact 'method:App\Services\Billing::charge'

# Any tool from the full list, JSON always
php artisan mapin:query hubs --arg=limit=10 --json

# Health check before pointing a client at it
php artisan mapin:doctor

find takes any exact name a thing goes by: a class (short name, FQCN, or its last namespace segments), Class::method, a route name or URI, a middleware alias like module.access, a table or view name, or an Artisan command such as emails:send. Each hit carries matched_by, the rule that found it. A partial name is not a hit: you get found: false with suggestions, or pass --fuzzy.

The first build also indexes the framework under vendor/, so on a fresh app it takes under a minute and peaks around 500 MB of memory. PHP's stock CLI limit is 128M, so mapin:build raises its own limit to 1G for that run. It never lowers a limit, and inside a container with a hard memory limit it stays under half of it. Set MAPIN_MEMORY_LIMIT to change it (2G for a very large codebase) or to null to leave PHP's limit alone.

mapin:doctor prints ✓ for a check that passed, ✗ for one that failed and ? for one it cannot answer. The usual ? is graph_staleness when git returns no HEAD commit for the project (not a repository, or a container whose user does not own the checkout): it does not fail the exit code, and it never says "current" without having compared two commits. The unresolved_ratio counts your own files only; the framework under vendor/ is left out, and the whole-graph figure comes beside it as all_files in --json.

Every command supports --json. Every answer carries a graph block reporting how stale it might be (the commit the graph was built from, and how many files changed since) - never a silent guess about how current the answer is.

Keeping the graph current is opt-in, not automatic - run this once per checkout to rebuild it in the background after every commit and merge:

php artisan mapin:install-hooks
# behind Docker instead of a bare host PHP?
php artisan mapin:install-hooks --command="docker exec <container> php artisan mapin:build"

Use it from Claude Code, Cursor, or any MCP client

php artisan mapin:mcp

starts a stdio MCP server exposing every query as a tool, driven by the exact same code the CLI uses - an MCP answer and a mapin:query answer to the same question never diverge. See docs/MCP.md for client configuration (Claude Code's .mcp.json, Cursor's .cursor/mcp.json, including the Docker-exec variant for a containerized application).

Using Mapin with Laravel Boost

Boost tells your agent what your app has. Mapin tells it what depends on what, before it touches anything. They are meant to be used together.

Laravel Boost gives an agent your app's version, schema, logs, config and docs. It does not trace callers, impact or container bindings, which is the part Mapin covers. Mapin ships an agent skill that Boost discovers by itself:

composer require laravel/boost nexvia-solutions/mapin --dev
php artisan boost:install

boost:install lists Mapin under "Which third-party AI guidelines/skills would you like to install?", so select it there. php artisan boost:list-skills shows mapin among the skills, and the installed copy lands in your agent's skills folder (.claude/skills/mapin/ for Claude Code). With -n Boost installs only what boost.json already lists, so add "packages": ["nexvia-solutions/mapin"] to it first. The skill tells the agent when to reach for the graph (before editing or deleting a symbol, to see who uses it and what breaks) and how to read found: false. Boost only picks up skills from packages your project requires directly, which is what composer require --dev does.

Boost registers its own MCP server. Mapin's is a second one, added the usual way (see above and docs/MCP.md).

What it builds

  • PHP: classes, methods, functions, calls, new, constructor and method injection, container bindings, Eloquent relations, table mapping ($table and convention), migrations, dispatched jobs and events, listeners, observers, scheduled commands.
  • Routes and views: every registered route to its handler and middleware, Blade includes, components, and route()/view() calls from both PHP and Blade.
  • Markdown: a doc/section node per file and heading, links between docs, and mentions of a known class, route, table, view or file - ambiguous short names land in unresolved, never a guess.
  • JavaScript: fetch/axios/jQuery $.ajax calls with a literal URL, matched to the route they request.
  • Analysis: Louvain community detection and hub/bridge degree over the call and injection graph, run on demand (mapin:communities), never during a plain build.
  • Semantic docs (opt-in, mapin:docs --with-llm): concepts and relations between them, extracted from Markdown section text only - never PHP source, and never without the flag. Local by default (NullClient); one provider driver ships (Ollama).
  • Query misses (mapin:misses): every query that came back found: false gets tracked locally, exportable to a JSONL file inside your own project so a team can review real gaps together. Purely local - no network call, ever, for any installation.
  • Build warnings (mapin:warnings): every file that fails to parse or compile during a build (never aborts the build itself) is tracked the same way, one row per distinct problem rather than once per build it keeps recurring on - exportable to the same kind of JSONL file, purely local.

Every query tool is listed in docs/MCP.md; the full behavioral contract, every node and edge type, and the reasoning behind each design decision live in SPEC.md.

Boundaries this package holds itself to

  • Never sends code to an LLM without the explicit --with-llm flag, and only Markdown section text even then - never PHP source.
  • Never writes outside its own configured storage directory (storage/mapin/ by default). No files in your project root, no edits to your .gitignore.
  • Never executes your application's business logic: booting the kernel to read the router and the container is as far as it goes.
  • Never answers with an approximate match without saying so.

The full list, with the reasoning behind each one, is in SPEC.md section 13.

Documentation

  • docs/MCP.md - MCP server setup and the full tool list
  • docs/EXTENDING.md - adding your own extractor or query tool
  • SPEC.md - the complete specification: graph model, every extractor and resolution rule, the query contract, and a phase-by-phase account of what was built, what was verified against a real application, and every real bug found along the way
  • CONTRIBUTING.md - development setup, running the test suite, code style

License

MIT. See LICENSE.