PHPackages                             dpt/mcp-phpstan-warm - 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. [Testing &amp; Quality](/categories/testing)
4. /
5. dpt/mcp-phpstan-warm

ActiveProject[Testing &amp; Quality](/categories/testing)

dpt/mcp-phpstan-warm
====================

Warm-process MCP server for PHPStan. Keeps a PHPStan worker alive across calls via its built-in TCP worker protocol — faster than cold CLI invocations. Compatible with any MCP client (Claude Desktop, Cline, Continue, Zed, custom).

v0.3.0(2w ago)1786↑308.4%proprietaryPHPPHP &gt;=8.2CI passing

Since May 22Pushed 1w ago1 watchersCompare

[ Source](https://github.com/Digital-Process-Tools/mcp-phpstan-warm)[ Packagist](https://packagist.org/packages/dpt/mcp-phpstan-warm)[ Docs](https://github.com/Digital-Process-Tools/mcp-phpstan-warm)[ RSS](/packages/dpt-mcp-phpstan-warm/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (5)Used By (0)

 [![mcp-phpstan-warm — worker stays warm. analysis stays fast.](banner.png)](banner.png)

mcp-phpstan-warm
================

[](#mcp-phpstan-warm)

> **Stop paying PHPStan's cold-start tax on every edit.**A warm-process [MCP](https://modelcontextprotocol.io/) server that keeps a [PHPStan](https://phpstan.org/) worker alive via its built-in TCP worker protocol. Works with every MCP client.

[![Tests](https://github.com/Digital-Process-Tools/mcp-phpstan-warm/actions/workflows/tests.yml/badge.svg)](https://github.com/Digital-Process-Tools/mcp-phpstan-warm/actions/workflows/tests.yml)[![Packagist](https://camo.githubusercontent.com/07a170b1b9239a313ee4610174569126adb866260e1ce6293fe843fc191ce797/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6470742f6d63702d7068707374616e2d7761726d2e737667)](https://packagist.org/packages/dpt/mcp-phpstan-warm)[![PHP](https://camo.githubusercontent.com/344e820b219cee3234648531306104364bd684892ad13c5dc79e66eb82a15b90/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e322532422d626c7565)](https://www.php.net/)[![License](https://camo.githubusercontent.com/b070db700d7549b4414d6c41907c169824e89a1e3e949816af5828685facd257/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d436f6d6d756e6974792d627269676874677265656e)](LICENSE)

[Why](#why) • [Install](#install) • [Use it](#use-it) • [Tools exposed](#tools-exposed) • [How it works](#how-it-works) • [FAQ](#faq)

---

Why
---

[](#why)

[PHPStan](https://phpstan.org/) is one of the most useful tools in modern PHP — static analysis, type inference, dead code detection. It is also slow to **start**.

Every `phpstan analyse foo.php` pays the same toll: bootstrap, config parse, autoloader load, rule compilation. **1-3 seconds before a single check runs.** For agents and validators that run PHPStan after every edit, that cold-start cost dominates wall time.

`mcp-phpstan-warm` uses PHPStan's own built-in `worker` subcommand — the same TCP worker protocol PHPStan uses internally for parallel analysis. **First call boots the worker once. Every subsequent call reuses the live process.**

Install
-------

[](#install)

```
composer global require dpt/mcp-phpstan-warm
```

Makes `mcp-phpstan-warm` available on `$PATH`.

Requires PHP 8.2+. PHPStan ^2.0 is pulled as a real Composer dependency.

Use it
------

[](#use-it)

### Claude Desktop

[](#claude-desktop)

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```
{
  "mcpServers": {
    "phpstan": {
      "command": "mcp-phpstan-warm",
      "args": [
        "--working-dir=/path/to/your/project",
        "--config=/path/to/your/project/phpstan.neon",
        "--paths=src,tests"
      ]
    }
  }
}
```

Restart Claude. Ask: *"Run PHPStan on src/Foo.php"*.

### Cline / Continue / Cursor / Zed / any MCP client

[](#cline--continue--cursor--zed--any-mcp-client)

Same `command` + `args` shape. The server speaks plain MCP over stdio — no client-specific glue.

### Standalone

[](#standalone)

```
mcp-phpstan-warm \
  --working-dir=/path/to/project \
  --config=/path/to/project/phpstan.neon \
  --paths=src,tests
```

Reads MCP JSON-RPC on stdin, writes responses on stdout.

Tools exposed
-------------

[](#tools-exposed)

### `phpstan_analyse`

[](#phpstan_analyse)

Analyse a PHP file with PHPStan.

ArgumentTypeRequiredDescription`path`stringyesAbsolute path to the PHP file to analyse**Important:** The file must be under one of the `--paths` declared at startup. PHPStan's worker fixes its analysed file set at boot — files outside that set will be rejected by the worker.

Returns:

```
{
  "exit_code": 1,
  "errors": [
    {
      "file": "/path/to/src/Foo.php",
      "line": 12,
      "message": "Method Foo::bar() should return string but returns int.",
      "identifier": "return.type"
    }
  ],
  "warm_boot": true
}
```

- `exit_code`: `0` = no errors, `1` = errors found, `-1` = internal error
- `errors`: list of PHPStan errors with file, line, message, and rule identifier
- `warm_boot`: `true` = worker reused (fast path), `false` = cold boot just completed

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

[](#how-it-works)

PHPStan ships a `worker` subcommand used internally for parallel analysis. It connects **to a TCP server** you open, sends a hello handshake, then waits for `{"action":"analyse","files":[...]}` requests and replies with `{"action":"result","result":{errors,...}}`.

`mcp-phpstan-warm` is that TCP server:

```
mcp-phpstan-warm (MCP server, TCP server)
  ├── stream_socket_server tcp://127.0.0.1:0  (random port)
  ├── proc_open: phpstan worker --port  --identifier  -c phpstan.neon
  ├── Accept worker connection
  ├── Verify hello handshake (identifier round-trip)
  ├── Sit idle, ready
  └── On MCP tools/call phpstan_analyse(path):
        send {"action":"analyse","files":[path]}
        receive {"action":"result","result":{...}}
        format errors → return to MCP client

```

Three things worth knowing:

1. **We are the TCP server, PHPStan is the client.** `stream_socket_server('tcp://127.0.0.1:0')` opens on a random port; we pass it to `phpstan worker --port=`. The worker dials us.
2. **The analysed file set is fixed at boot.** PHPStan's worker builds its dependency graph from the `` passed on the command line. Files outside those paths will fail or return dependency errors at analysis time. Pass all relevant paths via `--paths=src,tests` at startup.
3. **Worker death is handled transparently.** If `proc_get_status()` shows the worker died, the next `phpstan_analyse` call respawns it. The `warm_boot: false` flag in the response signals this happened.

FAQ
---

[](#faq)

**Does this replace `vendor/bin/phpstan`?** No. Use it from MCP clients. For one-off CLI runs the regular binary is simpler.

**Can I analyse multiple files at once?** The current tool accepts one file per call. The underlying protocol supports `"files":[...]` arrays — multi-file support can be added as a separate tool.

**Memory?** The daemon sets `memory_limit = -1`. Idle worker ≈ 60-80MB resident depending on project size and PHPStan level.

**Are `ignoreErrors` from my neon respected?** Yes. At worker boot the daemon runs `phpstan dump-parameters --json` once to extract the project's `ignoreErrors` list and caches it. Every `phpstan_analyse` call filters worker results through that list — by identifier, message regex, or path glob — before returning errors to the MCP client. The output matches what `phpstan analyse` would show on the same file.

**Does it survive PHPStan version updates?** The TCP protocol (`hello` / `analyse` / `result`) is PHPStan's internal parallel transport — it's stable across patch versions. Pin a PHPStan version in your `composer.json` if you need determinism.

**Why not just subprocess `phpstan analyse` and cache the result?** You could. But you'd still pay the full cold-start on every cache miss (every new or modified file). The worker mode amortises that cost across the entire session.

Credits
-------

[](#credits)

- **[PHPStan](https://phpstan.org/)** by [Ondřej Mirtes](https://github.com/ondrejmirtes) and contributors — the engine doing all the real work. If you ship PHP, [sponsor him](https://github.com/sponsors/ondrejmirtes).
- **[Model Context Protocol](https://modelcontextprotocol.io/)** by Anthropic — the protocol that makes this kind of tool integration possible.
- **[mcp/sdk](https://github.com/modelcontextprotocol/php-sdk)** — official PHP SDK, used here for stdio transport + tool discovery.

Related
-------

[](#related)

- **[PHPStan docs](https://phpstan.org/user-guide/getting-started)** — config, levels, extensions.
- **[PHPStan on Packagist](https://packagist.org/packages/phpstan/phpstan)** — the upstream package.
- **[mcp-rector-warm](https://github.com/Digital-Process-Tools/mcp-rector-warm)** — same warm-process pattern for Rector.
- **[mcp-phpunit-warm](https://github.com/Digital-Process-Tools/mcp-phpunit-warm)** — same warm-process pattern for PHPUnit.
- **[claude-supertool](https://github.com/Digital-Process-Tools/claude-supertool)** — DPT's batched-ops Claude Code companion; integrates warm-process servers as validators.

License
-------

[](#license)

Community License — see [LICENSE](LICENSE). Built by [Digital Process Tools](https://github.com/Digital-Process-Tools).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance97

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity39

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

Every ~0 days

Total

2

Last Release

18d ago

### Community

Maintainers

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

---

Top Contributors

[![fdaviddpt](https://avatars.githubusercontent.com/u/150798857?v=4)](https://github.com/fdaviddpt "fdaviddpt (2 commits)")

---

Tags

ai-toolsclaudeclaude-codedeveloper-toolsmcpmodel-context-protocolphpphpstanstatic-analysisphpPHPStanstatic analysismcpclaudeModel Context Protocolai-tools

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/dpt-mcp-phpstan-warm/health.svg)

```
[![Health](https://phpackages.com/badges/dpt-mcp-phpstan-warm/health.svg)](https://phpackages.com/packages/dpt-mcp-phpstan-warm)
```

###  Alternatives

[larastan/larastan

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

6.4k51.0M7.4k](/packages/larastan-larastan)[phpstan/phpstan-symfony

Symfony Framework extensions and rules for PHPStan

78973.3M2.0k](/packages/phpstan-phpstan-symfony)[shipmonk/dead-code-detector

Dead code detector to find unused PHP code via PHPStan extension. Can automatically remove dead PHP code. Supports libraries like Symfony, Doctrine, PHPUnit etc. Detects dead cycles. Can detect dead code that is tested.

4753.1M82](/packages/shipmonk-dead-code-detector)[phpstan/phpstan-doctrine

Doctrine extensions for PHPStan

66970.7M1.3k](/packages/phpstan-phpstan-doctrine)[calebdw/larastan

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

15104.9k4](/packages/calebdw-larastan)[shipmonk/phpstan-rules

Various extra strict PHPStan rules we found useful in ShipMonk.

1542.0M166](/packages/shipmonk-phpstan-rules)

PHPackages © 2026

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