phpstan-mcp-mx 0.21.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -1
- package/README.md +128 -79
- package/dist/baseline.d.ts +1 -1
- package/dist/baseline.js +27 -15
- package/dist/baseline.js.map +1 -1
- package/dist/changed.d.ts +5 -5
- package/dist/changed.js +68 -35
- package/dist/changed.js.map +1 -1
- package/dist/compare.d.ts +3 -3
- package/dist/compare.js +14 -8
- package/dist/compare.js.map +1 -1
- package/dist/composer.d.ts +2 -2
- package/dist/composer.js +49 -27
- package/dist/composer.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.js +11 -7
- package/dist/config.js.map +1 -1
- package/dist/defaults.d.ts +1 -1
- package/dist/defaults.js +3 -2
- package/dist/defaults.js.map +1 -1
- package/dist/dumptype.d.ts +1 -1
- package/dist/dumptype.js +20 -12
- package/dist/dumptype.js.map +1 -1
- package/dist/failure.d.ts +2 -2
- package/dist/failure.js.map +1 -1
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/php.d.ts +46 -0
- package/dist/php.js +100 -0
- package/dist/php.js.map +1 -0
- package/dist/phpstan/error.js.map +1 -1
- package/dist/phpstan/output.d.ts +2 -2
- package/dist/phpstan/output.js +16 -10
- package/dist/phpstan/output.js.map +1 -1
- package/dist/phpstan/parameters.d.ts +2 -2
- package/dist/phpstan/parameters.js +1 -1
- package/dist/phpstan/parameters.js.map +1 -1
- package/dist/phpstan/process.d.ts +16 -5
- package/dist/phpstan/process.js +48 -19
- package/dist/phpstan/process.js.map +1 -1
- package/dist/phpstan/queue.d.ts +1 -1
- package/dist/phpstan/queue.js +6 -4
- package/dist/phpstan/queue.js.map +1 -1
- package/dist/phpstan.d.ts +6 -6
- package/dist/phpstan.js +51 -31
- package/dist/phpstan.js.map +1 -1
- package/dist/project.d.ts +7 -3
- package/dist/project.js +60 -21
- package/dist/project.js.map +1 -1
- package/dist/report.d.ts +5 -5
- package/dist/report.js +47 -28
- package/dist/report.js.map +1 -1
- package/dist/root-source.d.ts +43 -0
- package/dist/root-source.js +124 -0
- package/dist/root-source.js.map +1 -0
- package/dist/roots.d.ts +13 -1
- package/dist/roots.js +24 -7
- package/dist/roots.js.map +1 -1
- package/dist/runner.d.ts +3 -3
- package/dist/runner.js +17 -10
- package/dist/runner.js.map +1 -1
- package/dist/runs.d.ts +3 -3
- package/dist/runs.js +9 -6
- package/dist/runs.js.map +1 -1
- package/dist/schema-dialect.d.ts +2 -2
- package/dist/schema-dialect.js +8 -5
- package/dist/schema-dialect.js.map +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.js +10 -6
- package/dist/server.js.map +1 -1
- package/dist/tmpdir.d.ts +3 -3
- package/dist/tmpdir.js +10 -7
- package/dist/tmpdir.js.map +1 -1
- package/dist/tools/analysis.d.ts +4 -4
- package/dist/tools/analysis.js +7 -5
- package/dist/tools/analysis.js.map +1 -1
- package/dist/tools/inspect.d.ts +7 -7
- package/dist/tools/inspect.js +111 -45
- package/dist/tools/inspect.js.map +1 -1
- package/dist/tools/prompts.d.ts +5 -5
- package/dist/tools/prompts.js +15 -15
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/shared.d.ts +4 -4
- package/dist/tools/shared.js +13 -11
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/writes.d.ts +3 -3
- package/dist/tools/writes.js +4 -4
- package/dist/tools/writes.js.map +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +2 -1
- package/dist/tools.js.map +1 -1
- package/dist/unmatched.d.ts +3 -3
- package/dist/unmatched.js +35 -20
- package/dist/unmatched.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes per version. Generated by semantic-release from the commit
|
|
4
4
|
history since 0.10.0; the versions before that were written by hand.
|
|
5
5
|
|
|
6
|
+
# [0.23.0](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/compare/v0.22.0...v0.23.0) (2026-09-12)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* accept a project relative to the client's working directory ([e2025d9](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/commit/e2025d990ef4a65658fe5db08f75249a450d0116))
|
|
12
|
+
|
|
13
|
+
# [0.22.0](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/compare/v0.21.1...v0.22.0) (2026-09-12)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* start PHPStan without a detached console on Windows ([20cd44c](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/commit/20cd44c260823a5fc06744c98a82411e5e0e51bc))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* wip ([0cc18dc](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/commit/0cc18dc211d09c87b54efdaacc0667414e7c1df2))
|
|
24
|
+
|
|
6
25
|
## [0.21.1](https://codeberg.org/MartijnSchimmel/phpstan-mcp-mx/compare/v0.21.0...v0.21.1) (2026-09-11)
|
|
7
26
|
|
|
8
27
|
|
|
@@ -159,7 +178,7 @@ history since 0.10.0; the versions before that were written by hand.
|
|
|
159
178
|
|
|
160
179
|
## 0.8.0
|
|
161
180
|
|
|
162
|
-
- Nieuwe tool `server_config`: met welke instellingen deze server draait
|
|
181
|
+
- Nieuwe tool `server_config`: met welke instellingen deze server draait roots, standaard
|
|
163
182
|
geheugenlimiet, hoeveel analyses tegelijk, waar de tijdelijke bestanden heen gaan en of
|
|
164
183
|
schrijvende tools aanstaan. Een geweigerde instelling wordt gemeld in plaats van
|
|
165
184
|
verzwegen.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ serves several PHP projects at once; you decide which ones.
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
8
8
|
The server runs as a separate process that your MCP client starts. Install it globally once
|
|
9
|
-
and then register it with the client you use
|
|
9
|
+
and then register it with the client you use Claude Code and Claude Desktop each keep their
|
|
10
10
|
own list, so you do this separately for both.
|
|
11
11
|
|
|
12
12
|
```bash
|
|
@@ -14,11 +14,13 @@ npm install -g phpstan-mcp-mx
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Below, replace `/path/to/projects` everywhere with the directory that holds your PHP
|
|
17
|
-
projects.
|
|
17
|
+
projects. In Claude Code you can also leave `PHPSTAN_MCP_ROOTS` out: the server then works in
|
|
18
|
+
the directory the session runs in. See [Configuration](#configuration) for why there is always
|
|
19
|
+
a limit.
|
|
18
20
|
|
|
19
21
|
### Claude Code
|
|
20
22
|
|
|
21
|
-
With `--scope user` the server is available in all your projects
|
|
23
|
+
With `--scope user` the server is available in all your projects which is what you want
|
|
22
24
|
from one server that serves several projects.
|
|
23
25
|
|
|
24
26
|
```bash
|
|
@@ -53,7 +55,7 @@ phpstan:
|
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
`claude mcp get` and `claude mcp list` actually connect to the server instead of just reading
|
|
56
|
-
back the configuration
|
|
58
|
+
back the configuration so they tell you whether it really starts. Inside a session, `/mcp`
|
|
57
59
|
does the same. Then ask for the project list: if it comes back empty while the connection is
|
|
58
60
|
up, your roots point to the wrong directory and there is nothing wrong with the server
|
|
59
61
|
itself.
|
|
@@ -82,7 +84,7 @@ If that file doesn't exist yet, create it yourself:
|
|
|
82
84
|
```
|
|
83
85
|
|
|
84
86
|
If there already is an `mcpServers` block, add `phpstan` next to the existing entries instead
|
|
85
|
-
of replacing the file
|
|
87
|
+
of replacing the file otherwise you overwrite the servers that are already in it.
|
|
86
88
|
|
|
87
89
|
Desktop only reads that file at startup, and on macOS closing the window is not quitting:
|
|
88
90
|
quit the app completely (⌘Q) and start it again. After that, `phpstan` shows up among the
|
|
@@ -102,7 +104,7 @@ If you get something like `env: node: No such file or directory`, or the server
|
|
|
102
104
|
The `phpstan-mcp-mx` command is a script that starts with `#!/usr/bin/env node`, and `npx`
|
|
103
105
|
is itself a command that has to be found. Both work in your terminal because your shell
|
|
104
106
|
profile puts Node on `PATH`. But the MCP server is started by a *different* program, and that
|
|
105
|
-
one does not always inherit your shell profile
|
|
107
|
+
one does not always inherit your shell profile a GUI app on macOS often gets only a bare
|
|
106
108
|
`PATH` with `/usr/bin` and `/bin`. Node isn't there: Homebrew puts it in `/opt/homebrew/bin`,
|
|
107
109
|
and nvm, fnm, asdf and volta somewhere under your home directory.
|
|
108
110
|
|
|
@@ -134,7 +136,7 @@ And for Claude Desktop:
|
|
|
134
136
|
```
|
|
135
137
|
|
|
136
138
|
You can check whether `PATH` is the problem yourself, without involving the MCP client. The
|
|
137
|
-
binary is looked up in your own shell first and then started with a bare `PATH`
|
|
139
|
+
binary is looked up in your own shell first and then started with a bare `PATH` otherwise
|
|
138
140
|
the command itself can't be found and you are measuring something else:
|
|
139
141
|
|
|
140
142
|
```bash
|
|
@@ -154,10 +156,10 @@ echo '' | env -i PATH=/usr/bin:/bin \
|
|
|
154
156
|
```
|
|
155
157
|
|
|
156
158
|
```
|
|
157
|
-
phpstan-mcp 0.
|
|
159
|
+
phpstan-mcp 0.23.0 listening on stdio, no roots configured, so the client's directories (MCP roots) are in scope, at most 2 at a time
|
|
158
160
|
```
|
|
159
161
|
|
|
160
|
-
(
|
|
162
|
+
(No roots is correct here: `env -i` also throws away `PHPSTAN_MCP_ROOTS`. All that matters
|
|
161
163
|
is *that* it starts.)
|
|
162
164
|
|
|
163
165
|
### Updating
|
|
@@ -207,6 +209,7 @@ path in `PHPSTAN_MCP_CONFIG`):
|
|
|
207
209
|
"maxParallel": 2,
|
|
208
210
|
"tmpDir": "project",
|
|
209
211
|
"allowWrites": false,
|
|
212
|
+
"php": "/opt/homebrew/opt/php@8.4/bin/php",
|
|
210
213
|
"runners": {
|
|
211
214
|
"/Users/you/projects/admin": {
|
|
212
215
|
"command": ["docker", "exec", "-w", "/var/www/html", "admin-app"],
|
|
@@ -223,13 +226,13 @@ That makes your MCP registration a single line without `--env`, and keeps the co
|
|
|
223
226
|
in one place instead of spread over seven variables.
|
|
224
227
|
|
|
225
228
|
A file that doesn't exist is not an error: then only the environment applies. A file that
|
|
226
|
-
*does* exist but is invalid stops the server from starting
|
|
229
|
+
*does* exist but is invalid stops the server from starting someone meant something there
|
|
227
230
|
that would otherwise silently not happen.
|
|
228
231
|
|
|
229
|
-
### `PHPSTAN_MCP_ROOTS` (
|
|
232
|
+
### `PHPSTAN_MCP_ROOTS` (recommended)
|
|
230
233
|
|
|
231
|
-
`PHPSTAN_MCP_ROOTS` is
|
|
232
|
-
|
|
234
|
+
`PHPSTAN_MCP_ROOTS` is a list of absolute paths, separated by a colon (a semicolon on
|
|
235
|
+
Windows). Everything below them may be analysed, nothing outside them.
|
|
233
236
|
|
|
234
237
|
```
|
|
235
238
|
PHPSTAN_MCP_ROOTS=/Users/you/projects:/Users/you/work/client-x
|
|
@@ -240,6 +243,25 @@ directories with a `composer.json`, stops as soon as it finds one, and skips `ve
|
|
|
240
243
|
`node_modules/` and hidden directories. A layout like `~/projects/<client>/<project>` works
|
|
241
244
|
with `~/projects` as the only root.
|
|
242
245
|
|
|
246
|
+
Without `PHPSTAN_MCP_ROOTS`, the directories the client shares through MCP roots are the
|
|
247
|
+
limit instead. Claude Code shares the directory the session runs in, so there the server
|
|
248
|
+
reaches exactly the project you started it in. A client that shares no directories leaves
|
|
249
|
+
nothing in reach: then the server refuses every project and says why. Set the variable when
|
|
250
|
+
one server should reach several projects, or with such a client.
|
|
251
|
+
|
|
252
|
+
### The client's directory and relative paths
|
|
253
|
+
|
|
254
|
+
A `project` may also be relative: `.` is the directory the client shares, and `packages/api`
|
|
255
|
+
starts from there. This uses MCP roots, not the directory the server process happens to run
|
|
256
|
+
in a client that doesn't start the server in your project (Claude Desktop) would otherwise
|
|
257
|
+
silently point `.` somewhere else. It needs exactly one shared directory: with none there is
|
|
258
|
+
nothing to resolve against, and with several (Claude Code after `/add-dir`) it would be a
|
|
259
|
+
guess; both are refused with a message asking for the full path.
|
|
260
|
+
|
|
261
|
+
With `PHPSTAN_MCP_ROOTS` set, that list stays the limit: `.` then only works if the client's
|
|
262
|
+
directory lies within it. `list_projects` and `server_config` show which list applies and
|
|
263
|
+
what `.` resolves to.
|
|
264
|
+
|
|
243
265
|
### `PHPSTAN_MCP_MAX_PARALLEL` (optional)
|
|
244
266
|
|
|
245
267
|
How many analyses may run at the same time, across projects. Two by default.
|
|
@@ -248,7 +270,7 @@ How many analyses may run at the same time, across projects. Two by default.
|
|
|
248
270
|
PHPSTAN_MCP_MAX_PARALLEL=4
|
|
249
271
|
```
|
|
250
272
|
|
|
251
|
-
Analyses within the same project always run in order
|
|
273
|
+
Analyses within the same project always run in order two simultaneous runs would overwrite
|
|
252
274
|
each other's result cache. This limit is about the machine as a whole: without it, a model
|
|
253
275
|
that calls ten projects after `list_projects` starts ten PHPStan processes, each spinning up
|
|
254
276
|
its own parallel workers. With `PHPSTAN_MCP_MEMORY_LIMIT=2G` that is ten times that ceiling.
|
|
@@ -272,7 +294,7 @@ Where PHPStan puts its temporary files, and therefore where the result cache end
|
|
|
272
294
|
The default solves a silent problem. PHPStan's `tmpDir` defaults to
|
|
273
295
|
`sys_get_temp_dir()/phpstan`, so *every* project without its own `tmpDir` in its config
|
|
274
296
|
shares a single `resultCache.php`. Two projects in a row then invalidate each other's cache,
|
|
275
|
-
and the cache
|
|
297
|
+
and the cache the reason a second analysis is fast stops doing anything. Measured on two
|
|
276
298
|
real projects, with another project in between:
|
|
277
299
|
|
|
278
300
|
```
|
|
@@ -289,14 +311,36 @@ If you set `tmpDir` yourself in your `phpstan.neon`, that wins: a configuration
|
|
|
289
311
|
explicitly takes precedence over what this server provides.
|
|
290
312
|
|
|
291
313
|
Choose `shared` if you want the server to use the same cache as your terminal, or if setting
|
|
292
|
-
`TMPDIR` disturbs something in your project
|
|
314
|
+
`TMPDIR` disturbs something in your project with Larastan, an entire Laravel application
|
|
293
315
|
runs in the same process.
|
|
294
316
|
|
|
317
|
+
### `PHPSTAN_MCP_PHP` (optional; needed on Windows unless `php.exe` is on your PATH)
|
|
318
|
+
|
|
319
|
+
The PHP binary that starts PHPStan on the host, as an absolute path:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
PHPSTAN_MCP_PHP=C:\Users\you\.config\herd\bin\php85\php.exe
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
`vendor/bin/phpstan` is a PHP script with a `#!/usr/bin/env php` line. macOS and Linux start
|
|
326
|
+
that by themselves; Windows does not. On Windows the server therefore starts
|
|
327
|
+
`php.exe vendor/bin/phpstan …`, just like Composer's own `phpstan.bat` does, but without a
|
|
328
|
+
shell in between. It looks for `php.exe` on your `PATH`; if it can't find one, every analysis
|
|
329
|
+
of a project without a runner fails with a message that points here.
|
|
330
|
+
|
|
331
|
+
Herd puts a `php.bat` on your `PATH` and not a `php.exe`. A batch file can't be started
|
|
332
|
+
without a shell, so it doesn't count: point this setting at the `php.exe` inside Herd's
|
|
333
|
+
`bin\php85` (or whichever version you use) instead. A `.bat` or `.cmd` here is rejected with
|
|
334
|
+
that explanation.
|
|
335
|
+
|
|
336
|
+
On macOS and Linux you can use it to pick a specific PHP version; without it, the script's
|
|
337
|
+
own `#!/usr/bin/env php` decides. `server_config` shows which PHP is used.
|
|
338
|
+
|
|
295
339
|
### `PHPSTAN_MCP_RUNNERS` (optional)
|
|
296
340
|
|
|
297
341
|
Some projects cannot be analysed on the host. Larastan really boots your Laravel
|
|
298
342
|
application, and if the host lacks, say, `ext-redis`, the bootstrap fails and the analysis
|
|
299
|
-
stops
|
|
343
|
+
stops while the same repository runs fine inside its container.
|
|
300
344
|
|
|
301
345
|
With this variable you say, per project, where PHPStan should run:
|
|
302
346
|
|
|
@@ -329,17 +373,18 @@ this is something a human sets once: a model only picks a project, and whether a
|
|
|
329
373
|
environment belongs to it is already decided. If a caller could pass in the command, the
|
|
330
374
|
caller would decide what gets executed and the whitelist would be pointless.
|
|
331
375
|
|
|
332
|
-
### Why
|
|
376
|
+
### Why there is always a list
|
|
333
377
|
|
|
334
378
|
PHPStan is not a passive parser. It loads the project's autoloader, executes
|
|
335
379
|
`bootstrapFiles` from the configuration and loads extensions as PHP code. "Analyse directory
|
|
336
|
-
X" therefore means "execute the PHP code that X's configuration points to"
|
|
380
|
+
X" therefore means "execute the PHP code that X's configuration points to" and the path
|
|
337
381
|
comes from a language model. The list of roots makes what is in reach explicit and decided
|
|
338
|
-
by a human
|
|
382
|
+
outside the model: by a human in `PHPSTAN_MCP_ROOTS`, or by the client that shares the
|
|
383
|
+
directory it was started in. Neither can come from a tool call.
|
|
339
384
|
|
|
340
385
|
### `PHPSTAN_MCP_MEMORY_LIMIT` (optional)
|
|
341
386
|
|
|
342
|
-
If PHPStan runs into the PHP memory limit, a call can pass `memoryLimit`
|
|
387
|
+
If PHPStan runs into the PHP memory limit, a call can pass `memoryLimit` but only after
|
|
343
388
|
the analysis has already failed. With this variable you set the default once, next to your
|
|
344
389
|
roots:
|
|
345
390
|
|
|
@@ -358,12 +403,12 @@ alternative is `memory_limit` in your `php.ini`, which then applies to *every* P
|
|
|
358
403
|
your machine.
|
|
359
404
|
|
|
360
405
|
If one project needs more than the rest, set it for that project in `PHPSTAN_MCP_PROJECTS`
|
|
361
|
-
instead of raising this value for everyone
|
|
406
|
+
instead of raising this value for everyone with `PHPSTAN_MCP_MAX_PARALLEL=2`, a
|
|
362
407
|
machine-wide 4G is two times 4G.
|
|
363
408
|
|
|
364
409
|
### `PHPSTAN_MCP_PROJECTS` (optional)
|
|
365
410
|
|
|
366
|
-
Defaults for a single project, as JSON
|
|
411
|
+
Defaults for a single project, as JSON or under `projects` in the config file:
|
|
367
412
|
|
|
368
413
|
```json
|
|
369
414
|
{
|
|
@@ -390,25 +435,29 @@ of the project: for the notice that a baseline is included and for `unmatched_ig
|
|
|
390
435
|
|
|
391
436
|
An unknown key is rejected, not ignored: `memorylimit` with a lowercase l would otherwise
|
|
392
437
|
silently do nothing. `list_projects` and `server_config` show which defaults apply, and a
|
|
393
|
-
broken value is listed there as a problem
|
|
438
|
+
broken value is listed there as a problem the project list keeps working.
|
|
394
439
|
|
|
395
440
|
## Tools
|
|
396
441
|
|
|
397
|
-
Every tool expects an explicit `project
|
|
398
|
-
|
|
399
|
-
|
|
442
|
+
Every tool expects an explicit `project`: an absolute path, or a path relative to the
|
|
443
|
+
directory the client shares (`.` for that directory itself, see
|
|
444
|
+
[above](#the-clients-directory-and-relative-paths)). The server keeps no "current project"
|
|
445
|
+
between calls: that would be state the model doesn't see in its context, with the wrong
|
|
446
|
+
project silently analysed as a result. The model names the project in every call, and every
|
|
447
|
+
answer names its full path.
|
|
400
448
|
|
|
401
449
|
### `list_projects`
|
|
402
450
|
|
|
403
451
|
Lists the projects that are in reach, with for each project whether PHPStan is installed and
|
|
404
|
-
which config file it has. Start here
|
|
405
|
-
expect as `project`.
|
|
452
|
+
which config file it has. Start here the `path` from the result is what the other tools
|
|
453
|
+
expect as `project`. It also says where the list comes from (`rootsFrom`: `PHPSTAN_MCP_ROOTS`
|
|
454
|
+
or the client) and what `.` resolves to (`workingDirectory`).
|
|
406
455
|
|
|
407
456
|
### `analyse_project`
|
|
408
457
|
|
|
409
458
|
| Argument | Required | Meaning |
|
|
410
459
|
| --- | --- | --- |
|
|
411
|
-
| `project` | yes |
|
|
460
|
+
| `project` | yes | path to the project root, absolute or relative to the client's directory |
|
|
412
461
|
| `paths` | no | paths inside the project; empty means: follow `phpstan.neon` |
|
|
413
462
|
| `level` | no | level override, `0` to `10` or `max` |
|
|
414
463
|
| `config` | no | a different config file, e.g. `phpstan-strict.neon`; must lie inside the project |
|
|
@@ -432,18 +481,18 @@ not match `argumentx.something`).
|
|
|
432
481
|
|
|
433
482
|
### `analyse_file`
|
|
434
483
|
|
|
435
|
-
Same arguments, plus a required `path`
|
|
484
|
+
Same arguments, plus a required `path` a single file or directory inside the project. The
|
|
436
485
|
path may be relative to the project root or absolute, but it has to lie inside it.
|
|
437
486
|
|
|
438
487
|
### `analyse_changed`
|
|
439
488
|
|
|
440
489
|
Analyses the PHP files that git considers changed. Meant for checking whether your own
|
|
441
|
-
change introduces errors, without analysing the whole project
|
|
490
|
+
change introduces errors, without analysing the whole project and without the response
|
|
442
491
|
filling up with what has been there for years.
|
|
443
492
|
|
|
444
493
|
| Argument | Required | Meaning |
|
|
445
494
|
| --- | --- | --- |
|
|
446
|
-
| `project` | yes |
|
|
495
|
+
| `project` | yes | path to the project root, absolute or relative to the client's directory |
|
|
447
496
|
| `base` | no | commit, branch or tag to compare against (`HEAD` by default) |
|
|
448
497
|
| `staged` | no | only what is staged (`git diff --cached`), for a check before a commit |
|
|
449
498
|
| `onlyChangedLines` | no | only findings on lines that were changed |
|
|
@@ -455,7 +504,7 @@ the whole branch instead of just your uncommitted work.
|
|
|
455
504
|
Each finding says with `onChangedLine` whether it is on a changed line; in the text it is
|
|
456
505
|
prefixed with a `+`. A changed file often also contains errors that were already there, and
|
|
457
506
|
without that distinction you either start fixing those or stop too early. Keep in mind: a
|
|
458
|
-
finding on an unchanged line can still follow from your change
|
|
507
|
+
finding on an unchanged line can still follow from your change a modified signature causes
|
|
459
508
|
errors at the call sites, and you didn't touch those lines.
|
|
460
509
|
|
|
461
510
|
Those call sites are often in files you didn't change at all, and then this tool doesn't even
|
|
@@ -471,15 +520,15 @@ src/Use.php (not changed)
|
|
|
471
520
|
4 arguments.count Function greet invoked with 1 parameter, 2 required.
|
|
472
521
|
```
|
|
473
522
|
|
|
474
|
-
PHPStan then analyses the `paths` from your config. A changed file outside them
|
|
475
|
-
project that only analyses `src`
|
|
523
|
+
PHPStan then analyses the `paths` from your config. A changed file outside them a test in a
|
|
524
|
+
project that only analyses `src` isn't covered by that, and the response names it; leave out
|
|
476
525
|
`scope` to analyse the changed files themselves.
|
|
477
526
|
|
|
478
|
-
That is slower
|
|
479
|
-
how many files depend on what you changed
|
|
527
|
+
That is slower with a warm result cache one to five seconds on real projects, depending on
|
|
528
|
+
how many files depend on what you changed and every old error in the project shows up under
|
|
480
529
|
"not changed" too. **With exactly one changed file, the server therefore also analyses the
|
|
481
|
-
project as it was before your change**
|
|
482
|
-
PHPStan's editor mode
|
|
530
|
+
project as it was before your change** that one file replaced by its version at `base`, via
|
|
531
|
+
PHPStan's editor mode and compares the two:
|
|
483
532
|
|
|
484
533
|
```
|
|
485
534
|
Compared with the project before your change (src/Lib.php at HEAD): 0 fixed, 1 new, 3 still open.
|
|
@@ -489,18 +538,18 @@ New:
|
|
|
489
538
|
|
|
490
539
|
On a real project, adding a required parameter to a method called in four other files gave
|
|
491
540
|
exactly those four calls as new, and the 25 findings that were already there as still open.
|
|
492
|
-
That costs a second run. With more than one changed file it can't be done
|
|
493
|
-
replaces one file per run
|
|
541
|
+
That costs a second run. With more than one changed file it can't be done editor mode
|
|
542
|
+
replaces one file per run and then one analysis cannot tell which findings your change
|
|
494
543
|
caused; a finding that names what you changed is the strongest hint, and after a fix
|
|
495
544
|
`compareTo` tells exactly what is new. A `compareTo` you pass yourself takes precedence.
|
|
496
545
|
|
|
497
546
|
Two things it includes that are easy to miss: **new files not yet added to git** (`git diff`
|
|
498
547
|
doesn't know them, so there is a second query for them) and **nothing from
|
|
499
|
-
`resources/views`**
|
|
548
|
+
`resources/views`** a `.blade.php` ends in `.php`, but it is a template that PHPStan never
|
|
500
549
|
sees in a normal run, because the config doesn't list that directory in `paths`.
|
|
501
550
|
|
|
502
551
|
Every path git reports goes through the same check as a path the model supplies itself.
|
|
503
|
-
Whatever doesn't pass
|
|
552
|
+
Whatever doesn't pass a broken symlink, a file pointing outside the project is listed as
|
|
504
553
|
"skipped" in the response instead of silently dropping out.
|
|
505
554
|
|
|
506
555
|
If the project is not under git, the tool says so and points to `analyse_project` with
|
|
@@ -508,20 +557,20 @@ explicit `paths`.
|
|
|
508
557
|
|
|
509
558
|
**What the change hides** is listed under "Note": new
|
|
510
559
|
`@phpstan-ignore` comments (with file, line, identifier and reason) and ignored errors added
|
|
511
|
-
to a baseline that is included
|
|
560
|
+
to a baseline that is included even if no PHP file changed otherwise. An ignore whose reason
|
|
512
561
|
is the only thing that changed does not count as new. The notice doesn't pass judgement: an
|
|
513
562
|
ignore can be justified, the point is that it doesn't happen silently. Ignored errors that
|
|
514
563
|
disappeared from the baseline are listed too.
|
|
515
564
|
|
|
516
565
|
### `inspect_type`
|
|
517
566
|
|
|
518
|
-
Shows the type PHPStan infers for one or more expressions at a given line
|
|
567
|
+
Shows the type PHPStan infers for one or more expressions at a given line what you need
|
|
519
568
|
before fixing a type error, and exactly what PHPStan's own guidance asks you not to override
|
|
520
569
|
with a cast, `assert()` or an inline `@var`.
|
|
521
570
|
|
|
522
571
|
| Argument | Required | Meaning |
|
|
523
572
|
| --- | --- | --- |
|
|
524
|
-
| `project` | yes |
|
|
573
|
+
| `project` | yes | path to the project root, absolute or relative to the client's directory |
|
|
525
574
|
| `path` | yes | one PHP file inside the project |
|
|
526
575
|
| `line` | yes | the types are those right before this line runs |
|
|
527
576
|
| `expressions` | yes | 1 to 10 PHP expressions, e.g. `["$user", "$request->input('id')"]` |
|
|
@@ -537,7 +586,7 @@ Types right before line 37 of src/Actions/Contract/BuildContractRowsAction.php:
|
|
|
537
586
|
```
|
|
538
587
|
|
|
539
588
|
The project is not changed. The server inserts `\PHPStan\dumpType(<expression>);` before
|
|
540
|
-
`line` in a temporary copy of the file
|
|
589
|
+
`line` in a temporary copy of the file one line per expression and has PHPStan read that
|
|
541
590
|
copy in place of the original (PHPStan's editor mode, `--tmp-file` and `--instead-of`). The
|
|
542
591
|
copy lives in the server's temp directory, or with `PHPSTAN_MCP_RUNNERS` in the temp
|
|
543
592
|
directory of that environment, like the config of `unmatched_ignores`. Only that one file is
|
|
@@ -545,7 +594,7 @@ analysed, so it also works for a file outside the `paths` of your config and doe
|
|
|
545
594
|
warm result cache; on a real project it took one to two seconds.
|
|
546
595
|
|
|
547
596
|
`line` has to be the first line of a statement inside a function or method. Anywhere else —
|
|
548
|
-
in the middle of an array literal that spans several lines, directly in a class body
|
|
597
|
+
in the middle of an array literal that spans several lines, directly in a class body the
|
|
549
598
|
inserted line breaks the file, and you get PHPStan's parse errors back instead of types, with
|
|
550
599
|
the line numbers of your original file. A file under `excludePaths` fails with the reason
|
|
551
600
|
`nothing_to_analyse`. Editor mode needs a recent PHPStan; an older one fails with a message
|
|
@@ -562,7 +611,7 @@ phpstan-mcp 0.20.0
|
|
|
562
611
|
|
|
563
612
|
Project directories:
|
|
564
613
|
/Users/you/projects
|
|
565
|
-
/does/not/exist
|
|
614
|
+
/does/not/exist does not exist
|
|
566
615
|
|
|
567
616
|
Memory limit: 2G
|
|
568
617
|
Parallel: 2
|
|
@@ -574,7 +623,7 @@ Per-project defaults (a tool argument takes precedence):
|
|
|
574
623
|
/Users/you/projects/shop: memory 4G, timeout 900s
|
|
575
624
|
```
|
|
576
625
|
|
|
577
|
-
Until now that information was only in the startup line on stderr
|
|
626
|
+
Until now that information was only in the startup line on stderr visible in your client
|
|
578
627
|
log, invisible to the model. A rejected setting is reported instead of omitted: if
|
|
579
628
|
`PHPSTAN_MCP_MAX_PARALLEL` is set to nonsense, that field is `null` and the reason appears
|
|
580
629
|
under "Rejected settings", while the rest stays readable.
|
|
@@ -611,7 +660,7 @@ directory is the one `clear_result_cache` clears. If the application doesn't boo
|
|
|
611
660
|
without the extensions the app needs), this part is missing, with the reason stated.
|
|
612
661
|
|
|
613
662
|
Last come the Composer scripts that run PHPStan, and how they differ from the way this server
|
|
614
|
-
runs it
|
|
663
|
+
runs it the reason a developer can see different results than the server does:
|
|
615
664
|
|
|
616
665
|
```
|
|
617
666
|
Composer scripts that run PHPStan:
|
|
@@ -623,7 +672,7 @@ Composer scripts that run PHPStan:
|
|
|
623
672
|
```
|
|
624
673
|
|
|
625
674
|
If a framework is installed whose magic PHPStan can't see without an extension, and that
|
|
626
|
-
extension isn't loaded, that comes first
|
|
675
|
+
extension isn't loaded, that comes first it explains more than anything else:
|
|
627
676
|
|
|
628
677
|
```
|
|
629
678
|
Missing PHPStan extensions:
|
|
@@ -636,7 +685,7 @@ they are in nearly every project, often as a dependency of a dependency, and a n
|
|
|
636
685
|
project is noise. What is loaded comes from PHPStan's own diagnosis, so an extension included by
|
|
637
686
|
hand counts too.
|
|
638
687
|
|
|
639
|
-
Scripts are recognised by their command, not their name, and are only read
|
|
688
|
+
Scripts are recognised by their command, not their name, and are only read never run. A
|
|
640
689
|
script that goes through `make` or another script can't be read without running it, so it is
|
|
641
690
|
left out. Only what changes the result is compared: the config, the memory limit, the level,
|
|
642
691
|
explicit paths and `--autoload-file`. The server never takes these over by itself: that would
|
|
@@ -645,7 +694,7 @@ let the project decide how the server starts PHPStan.
|
|
|
645
694
|
### `clear_result_cache`
|
|
646
695
|
|
|
647
696
|
Clears PHPStan's result cache, so the next analysis recomputes everything. Useful when a
|
|
648
|
-
result doesn't match the code
|
|
697
|
+
result doesn't match the code for example after a change to an extension or an included
|
|
649
698
|
config.
|
|
650
699
|
|
|
651
700
|
By default that cache does **not** live in the project but in the system temp directory
|
|
@@ -667,22 +716,22 @@ Reads a project's `phpstan-baseline*.neon` and counts what is being ignored, per
|
|
|
667
716
|
and per file. Does not run an analysis, so the answer is immediate.
|
|
668
717
|
|
|
669
718
|
Note the distinction: what is in here is exactly what `analyse_project` does *not* report. A
|
|
670
|
-
project that includes its baseline only reports the errors that are new
|
|
719
|
+
project that includes its baseline only reports the errors that are new PHPStan already
|
|
671
720
|
makes that comparison itself.
|
|
672
721
|
|
|
673
|
-
It also counts the inline `@phpstan-ignore` comments in the code
|
|
722
|
+
It also counts the inline `@phpstan-ignore` comments in the code the third place where
|
|
674
723
|
errors get hidden, next to the baseline and `ignoreErrors` in the config, and the only one
|
|
675
724
|
nothing counted until now:
|
|
676
725
|
|
|
677
726
|
```
|
|
678
|
-
18 inline @phpstan-ignore comment(s) in 14 file(s)
|
|
727
|
+
18 inline @phpstan-ignore comment(s) in 14 file(s) counted in all PHP files git knows, also outside the analysed paths. These are hidden too, and a new baseline does not touch them.
|
|
679
728
|
11 have no identifier (@phpstan-ignore-line, -next-line): they ignore every error on their line.
|
|
680
729
|
1 name an identifier but give no reason in parentheses.
|
|
681
730
|
```
|
|
682
731
|
|
|
683
732
|
That is counted with `git grep`, so `vendor/`, ignored files and Blade templates stay out,
|
|
684
733
|
and files not yet added to git count. Without git, the baseline part still works and the
|
|
685
|
-
response says why the inline comments were not counted
|
|
734
|
+
response says why the inline comments were not counted `null`, not zero, because zero
|
|
686
735
|
would claim nothing is hidden.
|
|
687
736
|
|
|
688
737
|
With `path` (e.g. `"app/Models/User.php"`) you also get the entries for that one file: per
|
|
@@ -693,12 +742,12 @@ it is precisely a deleted file that the entries doing nothing belong to.
|
|
|
693
742
|
|
|
694
743
|
Finds the ignore rules that no longer do anything: baseline entries for errors that were
|
|
695
744
|
fixed long ago, and entries whose `count` is higher than what still occurs. Many projects set
|
|
696
|
-
`reportUnmatchedIgnoredErrors: false`, and then this grows unnoticed
|
|
745
|
+
`reportUnmatchedIgnoredErrors: false`, and then this grows unnoticed on one project, 554 of
|
|
697
746
|
the 2116 baseline entries turned out to match nothing any more.
|
|
698
747
|
|
|
699
748
|
| Argument | Required | Meaning |
|
|
700
749
|
| --- | --- | --- |
|
|
701
|
-
| `project` | yes |
|
|
750
|
+
| `project` | yes | path to the project root, absolute or relative to the client's directory |
|
|
702
751
|
| `memoryLimit` | no | this is a full analysis, so it may need more |
|
|
703
752
|
| `timeoutSeconds` | no | 300 by default |
|
|
704
753
|
| `offset` / `limit` | no | which part of the entries you get back |
|
|
@@ -717,7 +766,7 @@ different, and with only part of the project, everything outside it looks stale.
|
|
|
717
766
|
|
|
718
767
|
With an execution environment (`PHPSTAN_MCP_RUNNERS`) the temporary config goes into the
|
|
719
768
|
temporary directory of *that* environment (`${TMPDIR:-/tmp}` inside the container), because a
|
|
720
|
-
container can't see the host's temporary directory
|
|
769
|
+
container can't see the host's temporary directory only the project directory is mounted.
|
|
721
770
|
That needs a `sh` in the environment; without one, the tool says so with the reason
|
|
722
771
|
`unsupported_with_runner`. PHPStan can't read its config from stdin, so there is no way
|
|
723
772
|
around writing a small file somewhere; it is never written into the project itself.
|
|
@@ -725,7 +774,7 @@ around writing a small file somewhere; it is never written into the project itse
|
|
|
725
774
|
### `generate_baseline` (only with write access)
|
|
726
775
|
|
|
727
776
|
Puts all current errors into a baseline, so that the next analysis only reports what is new.
|
|
728
|
-
Meant for bringing an existing project under PHPStan
|
|
777
|
+
Meant for bringing an existing project under PHPStan not for making errors go away.
|
|
729
778
|
|
|
730
779
|
This tool only exists if the server was started with `PHPSTAN_MCP_ALLOW_WRITES=1`:
|
|
731
780
|
|
|
@@ -736,12 +785,12 @@ claude mcp add phpstan --scope user \
|
|
|
736
785
|
-- phpstan-mcp-mx
|
|
737
786
|
```
|
|
738
787
|
|
|
739
|
-
Without that variable it isn't registered
|
|
788
|
+
Without that variable it isn't registered so it doesn't appear in the tool list at all,
|
|
740
789
|
rather than existing and refusing every call. All other tools stay read-only.
|
|
741
790
|
|
|
742
791
|
| Argument | Required | Meaning |
|
|
743
792
|
| --- | --- | --- |
|
|
744
|
-
| `project` | yes |
|
|
793
|
+
| `project` | yes | path to the project root, absolute or relative to the client's directory |
|
|
745
794
|
| `file` | no | target file inside the project (`phpstan-baseline.neon` by default) |
|
|
746
795
|
| `allowEmpty` | no | write even if there is nothing to ignore |
|
|
747
796
|
|
|
@@ -764,23 +813,23 @@ Workflows that only combine existing tools. A prompt rather than a tool, because
|
|
|
764
813
|
context in every session and a prompt only when you pick it. Every guardrail in the texts
|
|
765
814
|
comes from a trial run on a real project that went wrong without that sentence.
|
|
766
815
|
|
|
767
|
-
- **`phpstan_review_change`**
|
|
816
|
+
- **`phpstan_review_change`** review your own change with
|
|
768
817
|
`analyse_changed`, start with the findings on changed lines, and repeat until it is clean
|
|
769
818
|
(without `runId`, otherwise you see the result from before your fix). With `base` (e.g.
|
|
770
819
|
`main`) for a whole branch before a PR.
|
|
771
|
-
- **`phpstan_raise_level`**
|
|
820
|
+
- **`phpstan_raise_level`** what one level higher costs: two counts
|
|
772
821
|
with `limit: 1` and the difference per kind. The level is often set in a shared config in
|
|
773
822
|
`vendor/`, and `ignore.*` findings don't count: those are inline `@phpstan-ignore` comments
|
|
774
823
|
that have nothing to ignore on a different level.
|
|
775
|
-
- **`phpstan_shrink_baseline`**
|
|
824
|
+
- **`phpstan_shrink_baseline`** what in the baseline is stale
|
|
776
825
|
(`unmatched_ignores`), what a new baseline would hide (`analyse_project`), and the cheapest
|
|
777
826
|
real reduction. Cleaning up and hiding stay two separate things: `generate_baseline` is only
|
|
778
827
|
suggested when there is nothing to hide, and never called on its own initiative.
|
|
779
|
-
- **`phpstan_fix_identifier`**
|
|
828
|
+
- **`phpstan_fix_identifier`** a clean-up round for one identifier or a
|
|
780
829
|
group such as `missingType`, with the `runId` within the round and without `runId` after
|
|
781
830
|
every fix. Without `identifier` the model picks a kind: first what can be fixed at the
|
|
782
831
|
source, otherwise a kind that sits in one or two files.
|
|
783
|
-
- **`phpstan_compare_projects`**
|
|
832
|
+
- **`phpstan_compare_projects`** two projects side by side
|
|
784
833
|
(`diagnose_project`, level, baseline, runners and defaults), showing only the differences.
|
|
785
834
|
If no difference explains the behaviour, a stale result cache is the next suspect.
|
|
786
835
|
|
|
@@ -846,7 +895,7 @@ Same message more than once (one fix may cover several; check that they point to
|
|
|
846
895
|
```
|
|
847
896
|
|
|
848
897
|
On one real project, the top two lines pointed to the same method signature: 8 of its 25
|
|
849
|
-
findings. Messages are compared exactly, without normalising names or numbers away
|
|
898
|
+
findings. Messages are compared exactly, without normalising names or numbers away and a
|
|
850
899
|
generic message such as `Possibly invalid array key type mixed.` can still have separate
|
|
851
900
|
causes, so it is a pointer, not a promise.
|
|
852
901
|
|
|
@@ -859,7 +908,7 @@ then comes from this result instead of a new analysis.
|
|
|
859
908
|
|
|
860
909
|
With that `runId`, the next page comes from the same result. Without it, every page would be a
|
|
861
910
|
new analysis, and if you change a file in between you get pages with different totals. Only
|
|
862
|
-
`offset`, `limit`, `identifiers`, `onlyIn` and `onlyChangedLines` may differ
|
|
911
|
+
`offset`, `limit`, `identifiers`, `onlyIn` and `onlyChangedLines` may differ so one analysis
|
|
863
912
|
of the whole project can be read directory by directory without running PHPStan again. If the `runId` belongs to
|
|
864
913
|
different arguments, has expired (after ten minutes) or is unknown, a new analysis follows
|
|
865
914
|
with a notice instead of an error.
|
|
@@ -887,7 +936,7 @@ note that which one it is cannot be told apart.
|
|
|
887
936
|
|
|
888
937
|
The comparison counts over all findings, not just the page, and only needs the same arguments
|
|
889
938
|
as the earlier analysis (`offset`, `limit` and `identifiers` may differ). With
|
|
890
|
-
`analyse_changed`, a file that only one of the two analyses covered is not compared
|
|
939
|
+
`analyse_changed`, a file that only one of the two analyses covered is not compared touch a
|
|
891
940
|
second file while fixing, and its existing errors would otherwise show up as new. Those are
|
|
892
941
|
counted separately. A `runId` stays usable for comparing for an hour, longer than for paging:
|
|
893
942
|
the fixing itself sits between the two analyses.
|
|
@@ -904,7 +953,7 @@ PHPStan can return valid JSON and at the same time say on stderr that it didn't
|
|
|
904
953
|
goes at the very top of the response, before anything else:
|
|
905
954
|
|
|
906
955
|
```
|
|
907
|
-
WARNING
|
|
956
|
+
WARNING PHPStan reported this alongside the result:
|
|
908
957
|
- Result is incomplete because of severe errors.
|
|
909
958
|
```
|
|
910
959
|
|
|
@@ -921,7 +970,7 @@ Note:
|
|
|
921
970
|
```
|
|
922
971
|
|
|
923
972
|
That is the difference between "no findings" and "no findings, plus a thousand that aren't
|
|
924
|
-
reported". The check looks at whether your config really includes the file
|
|
973
|
+
reported". The check looks at whether your config really includes the file a baseline
|
|
925
974
|
sitting next to `phpstan.neon` without being included does nothing.
|
|
926
975
|
|
|
927
976
|
PHPStan's own guidance for reading findings is kept separately in `instructions` and shown
|
|
@@ -934,7 +983,7 @@ PHPStan adds:
|
|
|
934
983
|
```
|
|
935
984
|
|
|
936
985
|
That block is the same every run and says nothing about *this* analysis, so it doesn't count
|
|
937
|
-
as a warning
|
|
986
|
+
as a warning and it isn't truncated, because half a block of guidance helps nobody.
|
|
938
987
|
|
|
939
988
|
The `warnings` field is separate from `generalErrors`: the latter is PHPStan's own `errors`
|
|
940
989
|
array from the JSON. Both lists are capped at ten messages of at most 1000 characters each —
|
|
@@ -952,10 +1001,10 @@ How many were left out is in `omitted`, and in the text as "N more message(s) no
|
|
|
952
1001
|
- **The PHP memory limit.** PHPStan easily exceeds it on a large project;
|
|
953
1002
|
`memoryLimit: "1G"` usually solves that. The crash comes back as a regular error, with
|
|
954
1003
|
PHPStan's own explanation attached.
|
|
955
|
-
- **Larastan boots your Laravel application.** If that fails
|
|
956
|
-
reachable
|
|
1004
|
+
- **Larastan boots your Laravel application.** If that fails missing `.env`, database not
|
|
1005
|
+
reachable the analysis fails with Larastan's own explanation.
|
|
957
1006
|
- **Analyses in the same project run one after another,** because they share one result cache
|
|
958
|
-
(per project, in the temp directory
|
|
1007
|
+
(per project, in the temp directory not in the project itself). Different projects do run
|
|
959
1008
|
in parallel.
|
|
960
1009
|
- **A stale result cache** can make PHPStan crash with a message that seems to make no sense.
|
|
961
1010
|
After a `composer update` that no longer happens, because the cache directory follows
|
package/dist/baseline.d.ts
CHANGED
|
@@ -100,7 +100,7 @@ export interface BaselineFileEntry {
|
|
|
100
100
|
*
|
|
101
101
|
* Nodig omdat de samenvatting per soort en per bestand apart telt: welke soorten in
|
|
102
102
|
* wélk bestand zitten, stond alleen in het baseline-bestand zelf. Het pad wordt alleen
|
|
103
|
-
* vergeleken, niet geopend
|
|
103
|
+
* vergeleken, niet geopend het hoeft dus niet te bestaan, en dat is goed: een baseline
|
|
104
104
|
* noemt soms een bestand dat al weg is.
|
|
105
105
|
*/
|
|
106
106
|
export declare function baselineEntriesFor(project: ResolvedProject, path: string): BaselineFileEntry[];
|