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.
Files changed (98) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +128 -79
  3. package/dist/baseline.d.ts +1 -1
  4. package/dist/baseline.js +27 -15
  5. package/dist/baseline.js.map +1 -1
  6. package/dist/changed.d.ts +5 -5
  7. package/dist/changed.js +68 -35
  8. package/dist/changed.js.map +1 -1
  9. package/dist/compare.d.ts +3 -3
  10. package/dist/compare.js +14 -8
  11. package/dist/compare.js.map +1 -1
  12. package/dist/composer.d.ts +2 -2
  13. package/dist/composer.js +49 -27
  14. package/dist/composer.js.map +1 -1
  15. package/dist/config.d.ts +4 -3
  16. package/dist/config.js +11 -7
  17. package/dist/config.js.map +1 -1
  18. package/dist/defaults.d.ts +1 -1
  19. package/dist/defaults.js +3 -2
  20. package/dist/defaults.js.map +1 -1
  21. package/dist/dumptype.d.ts +1 -1
  22. package/dist/dumptype.js +20 -12
  23. package/dist/dumptype.js.map +1 -1
  24. package/dist/failure.d.ts +2 -2
  25. package/dist/failure.js.map +1 -1
  26. package/dist/index.js +27 -4
  27. package/dist/index.js.map +1 -1
  28. package/dist/php.d.ts +46 -0
  29. package/dist/php.js +100 -0
  30. package/dist/php.js.map +1 -0
  31. package/dist/phpstan/error.js.map +1 -1
  32. package/dist/phpstan/output.d.ts +2 -2
  33. package/dist/phpstan/output.js +16 -10
  34. package/dist/phpstan/output.js.map +1 -1
  35. package/dist/phpstan/parameters.d.ts +2 -2
  36. package/dist/phpstan/parameters.js +1 -1
  37. package/dist/phpstan/parameters.js.map +1 -1
  38. package/dist/phpstan/process.d.ts +16 -5
  39. package/dist/phpstan/process.js +48 -19
  40. package/dist/phpstan/process.js.map +1 -1
  41. package/dist/phpstan/queue.d.ts +1 -1
  42. package/dist/phpstan/queue.js +6 -4
  43. package/dist/phpstan/queue.js.map +1 -1
  44. package/dist/phpstan.d.ts +6 -6
  45. package/dist/phpstan.js +51 -31
  46. package/dist/phpstan.js.map +1 -1
  47. package/dist/project.d.ts +7 -3
  48. package/dist/project.js +60 -21
  49. package/dist/project.js.map +1 -1
  50. package/dist/report.d.ts +5 -5
  51. package/dist/report.js +47 -28
  52. package/dist/report.js.map +1 -1
  53. package/dist/root-source.d.ts +43 -0
  54. package/dist/root-source.js +124 -0
  55. package/dist/root-source.js.map +1 -0
  56. package/dist/roots.d.ts +13 -1
  57. package/dist/roots.js +24 -7
  58. package/dist/roots.js.map +1 -1
  59. package/dist/runner.d.ts +3 -3
  60. package/dist/runner.js +17 -10
  61. package/dist/runner.js.map +1 -1
  62. package/dist/runs.d.ts +3 -3
  63. package/dist/runs.js +9 -6
  64. package/dist/runs.js.map +1 -1
  65. package/dist/schema-dialect.d.ts +2 -2
  66. package/dist/schema-dialect.js +8 -5
  67. package/dist/schema-dialect.js.map +1 -1
  68. package/dist/server.d.ts +3 -2
  69. package/dist/server.js +10 -6
  70. package/dist/server.js.map +1 -1
  71. package/dist/tmpdir.d.ts +3 -3
  72. package/dist/tmpdir.js +10 -7
  73. package/dist/tmpdir.js.map +1 -1
  74. package/dist/tools/analysis.d.ts +4 -4
  75. package/dist/tools/analysis.js +7 -5
  76. package/dist/tools/analysis.js.map +1 -1
  77. package/dist/tools/inspect.d.ts +7 -7
  78. package/dist/tools/inspect.js +111 -45
  79. package/dist/tools/inspect.js.map +1 -1
  80. package/dist/tools/prompts.d.ts +5 -5
  81. package/dist/tools/prompts.js +15 -15
  82. package/dist/tools/prompts.js.map +1 -1
  83. package/dist/tools/shared.d.ts +4 -4
  84. package/dist/tools/shared.js +13 -11
  85. package/dist/tools/shared.js.map +1 -1
  86. package/dist/tools/writes.d.ts +3 -3
  87. package/dist/tools/writes.js +4 -4
  88. package/dist/tools/writes.js.map +1 -1
  89. package/dist/tools.d.ts +2 -2
  90. package/dist/tools.js +2 -1
  91. package/dist/tools.js.map +1 -1
  92. package/dist/unmatched.d.ts +3 -3
  93. package/dist/unmatched.js +35 -20
  94. package/dist/unmatched.js.map +1 -1
  95. package/dist/version.d.ts +2 -2
  96. package/dist/version.js +2 -2
  97. package/dist/version.js.map +1 -1
  98. 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 roots, standaard
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 Claude Code and Claude Desktop each keep their
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. It is required; see [Configuration](#configuration) for why.
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 which is what you want
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 so they tell you whether it really starts. Inside a session, `/mcp`
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 otherwise you overwrite the servers that are already in it.
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 a GUI app on macOS often gets only a bare
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` otherwise
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.20.0 listening on stdio, 0 root(s) configured, at most 2 at a time
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
- (Zero roots is correct here: `env -i` also throws away `PHPSTAN_MCP_ROOTS`. All that matters
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 someone meant something there
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` (required)
232
+ ### `PHPSTAN_MCP_ROOTS` (recommended)
230
233
 
231
- `PHPSTAN_MCP_ROOTS` is required: a list of absolute paths, separated by a colon (a semicolon
232
- on Windows). Everything below them may be analysed, nothing outside them.
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 two simultaneous runs would overwrite
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 the reason a second analysis is fast stops doing anything. Measured on two
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 with Larastan, an entire Laravel application
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 while the same repository runs fine inside its container.
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 that list is required
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" and the path
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` but only after
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 with `PHPSTAN_MCP_MAX_PARALLEL=2`, a
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 or under `projects` in the config file:
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 the project list keeps working.
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` path. The server has no "current project": that
398
- would be state the model doesn't see in its context, with the wrong project silently
399
- analysed as a result.
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 the `path` from the result is what the other tools
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 | absolute path to the project root |
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` a single file or directory inside the project. The
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 and without the response
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 | absolute path to the project root |
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 a modified signature causes
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 a test in a
475
- project that only analyses `src` isn't covered by that, and the response names it; leave out
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 with a warm result cache one to five seconds on real projects, depending on
479
- how many files depend on what you changed and every old error in the project shows up under
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** that one file replaced by its version at `base`, via
482
- PHPStan's editor mode and compares the two:
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 editor mode
493
- replaces one file per run and then one analysis cannot tell which findings your change
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`** a `.blade.php` ends in `.php`, but it is a template that PHPStan never
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 a broken symlink, a file pointing outside the project is listed as
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 even if no PHP file changed otherwise. An ignore whose reason
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 what you need
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 | absolute path to the project root |
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 one line per expression and has PHPStan read that
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 the
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 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 visible in your client
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 the reason a developer can see different results than the server does:
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 it explains more than anything else:
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 never run. A
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 for example after a change to an extension or an included
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 PHPStan already
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 the third place where
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) counted in all PHP files git knows, also outside the analysed paths. These are hidden too, and a new baseline does not touch them.
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 `null`, not zero, because zero
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 on one project, 554 of
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 | absolute path to the project root |
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 only the project directory is mounted.
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 not for making errors go away.
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 so it doesn't appear in the tool list at all,
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 | absolute path to the project root |
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`** review your own change with
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`** what one level higher costs: two counts
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`** what in the baseline is stale
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`** a clean-up round for one identifier or a
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`** two projects side by side
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 and a
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 so one analysis
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 touch a
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 PHPStan reported this alongside the result:
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 a baseline
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 and it isn't truncated, because half a block of guidance helps nobody.
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 missing `.env`, database not
956
- reachable the analysis fails with Larastan's own explanation.
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 not in the project itself). Different projects do run
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
@@ -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 het hoeft dus niet te bestaan, en dat is goed: een baseline
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[];