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).

Maintainers

Package info

github.com/Digital-Process-Tools/mcp-phpstan-warm

Type:project

pkg:composer/dpt/mcp-phpstan-warm

Statistics

Installs: 81

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.3.0 2026-05-22 16:31 UTC

This package is auto-updated.

Last update: 2026-05-22 16:32:16 UTC


README

mcp-phpstan-warm — worker stays warm. analysis stays fast.

mcp-phpstan-warm

Stop paying PHPStan's cold-start tax on every edit. A warm-process MCP server that keeps a PHPStan worker alive via its built-in TCP worker protocol. Works with every MCP client.

Tests Packagist PHP License

WhyInstallUse itTools exposedHow it worksFAQ

Why

PHPStan 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

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

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

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

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

phpstan_analyse

Analyse a PHP file with PHPStan.

Argument Type Required Description
path string yes Absolute 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

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 <port> --identifier <hex> -c phpstan.neon <paths>
  ├── 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=<N>. The worker dials us.

  2. The analysed file set is fixed at boot. PHPStan's worker builds its dependency graph from the <paths> 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

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

Related

License

Community License — see LICENSE. Built by Digital Process Tools.