phpstan-mcp-mx 0.15.0 → 0.17.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/README.md CHANGED
@@ -1,42 +1,45 @@
1
1
  # phpstan-mcp-mx
2
2
 
3
- Een MCP-server die [PHPStan](https://phpstan.org) ontsluit als tools voor Claude. Eén server
4
- bedient meerdere PHP-projecten tegelijk; welke dat zijn, leg je zelf vast.
3
+ An MCP server that exposes [PHPStan](https://phpstan.org) as tools for Claude. One server
4
+ serves several PHP projects at once; you decide which ones.
5
5
 
6
- ## Installatie
6
+ The server's own output — messages, tool descriptions, prompts and reports — is in Dutch.
7
+ The examples below show that output verbatim, so you can recognise it when you see it.
7
8
 
8
- De server draait als los proces dat je MCP-client opstart. Installeer hem één keer globaal
9
- en meld hem daarna aan bij de client die je gebruikt — Claude Code en Claude Desktop houden
10
- elk hun eigen lijst bij, dus voor allebei doe je dat apart.
9
+ ## Installation
10
+
11
+ The server runs as a separate process that your MCP client starts. Install it globally once
12
+ and then register it with the client you use — Claude Code and Claude Desktop each keep their
13
+ own list, so you do this separately for both.
11
14
 
12
15
  ```bash
13
16
  npm install -g phpstan-mcp-mx
14
17
  ```
15
18
 
16
- Vervang hieronder overal `/pad/naar/projecten` door de map waar je PHP-projecten in staan.
17
- Die is verplicht; zie [Configuratie](#configuratie) voor waarom.
19
+ Below, replace `/path/to/projects` everywhere with the directory that holds your PHP
20
+ projects. It is required; see [Configuration](#configuration) for why.
18
21
 
19
22
  ### Claude Code
20
23
 
21
- Met `--scope user` is de server in al je projecten beschikbaar wat je wilt bij één server
22
- die meerdere projecten bedient.
24
+ With `--scope user` the server is available in all your projectswhich is what you want
25
+ from one server that serves several projects.
23
26
 
24
27
  ```bash
25
28
  claude mcp add phpstan --scope user \
26
- --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
29
+ --env PHPSTAN_MCP_ROOTS=/path/to/projects \
27
30
  -- phpstan-mcp-mx
28
31
  ```
29
32
 
30
- Zonder globale installatie kan het ook rechtstreeks van de registry; dat kost wel elke
31
- sessiestart een npx-resolutie:
33
+ Without a global installation you can also run it straight from the registry; that does cost
34
+ an npx resolution at every session start:
32
35
 
33
36
  ```bash
34
37
  claude mcp add phpstan --scope user \
35
- --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
38
+ --env PHPSTAN_MCP_ROOTS=/path/to/projects \
36
39
  -- npx -y phpstan-mcp-mx
37
40
  ```
38
41
 
39
- Nagaan of het gelukt is:
42
+ To check that it worked:
40
43
 
41
44
  ```bash
42
45
  claude mcp get phpstan
@@ -49,77 +52,77 @@ phpstan:
49
52
  Type: stdio
50
53
  Command: phpstan-mcp-mx
51
54
  Environment:
52
- PHPSTAN_MCP_ROOTS=/pad/naar/projecten
55
+ PHPSTAN_MCP_ROOTS=/path/to/projects
53
56
  ```
54
57
 
55
- `claude mcp get` en `claude mcp list` verbinden echt met de server in plaats van alleen de
56
- configuratie terug te lezen ze melden dus of hij ook daadwerkelijk start. Binnen een
57
- sessie doet `/mcp` hetzelfde. Vraag daarna om de projectenlijst: komt die leeg terug terwijl
58
- de verbinding staat, dan wijzen je roots naar de verkeerde map en is er niets mis met de
59
- server zelf.
58
+ `claude mcp get` and `claude mcp list` actually connect to the server instead of just reading
59
+ back the configurationso they tell you whether it really starts. Inside a session, `/mcp`
60
+ does the same. Then ask for the project list: if it comes back empty while the connection is
61
+ up, your roots point to the wrong directory and there is nothing wrong with the server
62
+ itself.
60
63
 
61
64
  ### Claude Desktop
62
65
 
63
- Desktop leest zijn servers uit `claude_desktop_config.json`:
66
+ Desktop reads its servers from `claude_desktop_config.json`:
64
67
 
65
- | besturingssysteem | pad |
68
+ | operating system | path |
66
69
  | --- | --- |
67
70
  | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
68
71
  | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
69
72
  | Linux | `~/.config/Claude/claude_desktop_config.json` |
70
73
 
71
- Bestaat dat bestand nog niet, maak het dan zelf aan:
74
+ If that file doesn't exist yet, create it yourself:
72
75
 
73
76
  ```json
74
77
  {
75
78
  "mcpServers": {
76
79
  "phpstan": {
77
80
  "command": "phpstan-mcp-mx",
78
- "env": { "PHPSTAN_MCP_ROOTS": "/pad/naar/projecten" }
81
+ "env": { "PHPSTAN_MCP_ROOTS": "/path/to/projects" }
79
82
  }
80
83
  }
81
84
  }
82
85
  ```
83
86
 
84
- Staat er al een `mcpServers`-blok, zet `phpstan` er dan naast in plaats van het bestand te
85
- vervangenje overschrijft anders de servers die er al in staan.
87
+ If there already is an `mcpServers` block, add `phpstan` next to the existing entries instead
88
+ of replacing the file otherwise you overwrite the servers that are already in it.
86
89
 
87
- Desktop leest dat bestand alleen bij het opstarten, en op macOS is het venster sluiten geen
88
- afsluiten: quit de app volledig (⌘Q) en start hem opnieuw. Daarna staat `phpstan` tussen de
89
- beschikbare tools in het gesprek.
90
+ Desktop only reads that file at startup, and on macOS closing the window is not quitting:
91
+ quit the app completely (⌘Q) and start it again. After that, `phpstan` shows up among the
92
+ available tools in the conversation.
90
93
 
91
- Heb je Desktop al ingericht en wil je hetzelfde in Claude Code, dan neemt
92
- `claude mcp add-from-claude-desktop` die servers over (alleen macOS en WSL).
94
+ If you have already set up Desktop and want the same in Claude Code,
95
+ `claude mcp add-from-claude-desktop` copies those servers over (macOS and WSL only).
93
96
 
94
- Werkt het in je terminal wel maar in Desktop niet, ga dan meteen naar de volgende sectie:
95
- een GUI-app erft je shell-profiel niet, en dat is hier veruit de meest voorkomende oorzaak.
97
+ If it works in your terminal but not in Desktop, go straight to the next section: a GUI app
98
+ does not inherit your shell profile, and that is by far the most common cause here.
96
99
 
97
- ### Als de server niet start: PATH
100
+ ### If the server won't start: PATH
98
101
 
99
- Krijg je iets als `env: node: No such file or directory`, of blijft de server op "failed to
100
- connect" staan, dan is dit vrijwel altijd de oorzaak.
102
+ If you get something like `env: node: No such file or directory`, or the server stays on
103
+ "failed to connect", this is almost always the cause.
101
104
 
102
- Het commando `phpstan-mcp-mx` is een script dat begint met `#!/usr/bin/env node`, en `npx`
103
- is zelf ook een commando dat gevonden moet worden. Allebei werken ze in je terminal omdat je
104
- shell-profiel Node op `PATH` zet. Maar de MCP-server wordt gestart door een ánder programma,
105
- en dat erft je shell-profiel niet altijd een GUI-app op macOS krijgt vaak alleen een kale
106
- `PATH` met `/usr/bin` en `/bin`. Node staat daar niet: Homebrew zet hem in
107
- `/opt/homebrew/bin`, en nvm, fnm, asdf en volta ergens onder je home.
105
+ The `phpstan-mcp-mx` command is a script that starts with `#!/usr/bin/env node`, and `npx`
106
+ is itself a command that has to be found. Both work in your terminal because your shell
107
+ profile puts Node on `PATH`. But the MCP server is started by a *different* program, and that
108
+ one does not always inherit your shell profilea GUI app on macOS often gets only a bare
109
+ `PATH` with `/usr/bin` and `/bin`. Node isn't there: Homebrew puts it in `/opt/homebrew/bin`,
110
+ and nvm, fnm, asdf and volta somewhere under your home directory.
108
111
 
109
- De oplossing is absolute paden gebruiken, zodat er niets meer opgezocht hoeft te worden:
112
+ The fix is to use absolute paths, so nothing needs to be looked up any more:
110
113
 
111
114
  ```bash
112
- command -v node # bijv. /opt/homebrew/bin/node
113
- npm root -g # bijv. /opt/homebrew/lib/node_modules
115
+ command -v node # e.g. /opt/homebrew/bin/node
116
+ npm root -g # e.g. /opt/homebrew/lib/node_modules
114
117
  ```
115
118
 
116
119
  ```bash
117
120
  claude mcp add phpstan --scope user \
118
- --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
121
+ --env PHPSTAN_MCP_ROOTS=/path/to/projects \
119
122
  -- /opt/homebrew/bin/node /opt/homebrew/lib/node_modules/phpstan-mcp-mx/dist/index.js
120
123
  ```
121
124
 
122
- En voor Claude Desktop:
125
+ And for Claude Desktop:
123
126
 
124
127
  ```json
125
128
  {
@@ -127,15 +130,15 @@ En voor Claude Desktop:
127
130
  "phpstan": {
128
131
  "command": "/opt/homebrew/bin/node",
129
132
  "args": ["/opt/homebrew/lib/node_modules/phpstan-mcp-mx/dist/index.js"],
130
- "env": { "PHPSTAN_MCP_ROOTS": "/pad/naar/projecten" }
133
+ "env": { "PHPSTAN_MCP_ROOTS": "/path/to/projects" }
131
134
  }
132
135
  }
133
136
  }
134
137
  ```
135
138
 
136
- Zelf nagaan of het aan `PATH` ligt, zonder de MCP-client erbij te halen. Het binary wordt
137
- eerst in je eigen shell opgezocht en daarna met een kale `PATH` gestart anders is het
138
- commando zelf al onvindbaar en meet je iets anders:
139
+ You can check whether `PATH` is the problem yourself, without involving the MCP client. The
140
+ binary is looked up in your own shell first and then started with a bare `PATH` — otherwise
141
+ the command itself can't be found and you are measuring something else:
139
142
 
140
143
  ```bash
141
144
  echo '' | env -i PATH=/usr/bin:/bin "$(command -v phpstan-mcp-mx)"
@@ -145,8 +148,8 @@ echo '' | env -i PATH=/usr/bin:/bin "$(command -v phpstan-mcp-mx)"
145
148
  env: node: No such file or directory
146
149
  ```
147
150
 
148
- Komt dat eruit, dan weet je genoeg. Dezelfde test op de absolute variant hoort gewoon te
149
- starten:
151
+ If that is what comes out, you know enough. The same test on the absolute variant should
152
+ start normally:
150
153
 
151
154
  ```bash
152
155
  echo '' | env -i PATH=/usr/bin:/bin \
@@ -157,195 +160,217 @@ echo '' | env -i PATH=/usr/bin:/bin \
157
160
  phpstan-mcp 0.1.0 luistert op stdio, 0 root(s) geconfigureerd
158
161
  ```
159
162
 
160
- (Nul roots klopt hier: `env -i` gooit ook `PHPSTAN_MCP_ROOTS` weg. Het gaat er alleen om dát
161
- hij start.)
163
+ (Zero roots is correct here: `env -i` also throws away `PHPSTAN_MCP_ROOTS`. All that matters
164
+ is *that* it starts.)
165
+
166
+ ### Updating
162
167
 
163
- ### Vanuit de repo
168
+ A global installation does not update itself:
169
+
170
+ ```bash
171
+ npm install -g phpstan-mcp-mx@latest
172
+ ```
173
+
174
+ Then start a **new session** in your client. Just restarting is not enough: a server that is
175
+ already running keeps the code it loaded at startup, and the client holds on to the tool list
176
+ from the start of the session. New tools are then missing, and an argument the client doesn't
177
+ know yet (such as `runId`) is silently dropped.
178
+
179
+ If you register the server through `npx`, use `phpstan-mcp-mx@latest` instead of the bare
180
+ name; otherwise npx may keep using an older version from its cache. `server_config` shows
181
+ which version is running.
182
+
183
+ ### From the repository
164
184
 
165
185
  ```bash
166
186
  npm install && npm run build
167
187
  claude mcp add phpstan --scope user \
168
- --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
169
- -- node /pad/naar/phpstan-mcp-mx/dist/index.js
188
+ --env PHPSTAN_MCP_ROOTS=/path/to/projects \
189
+ -- node /path/to/phpstan-mcp-mx/dist/index.js
170
190
  ```
171
191
 
172
- Let op dat je dan vastzit aan die map: verplaats of verwijder je hem, dan faalt de server in
173
- al je projecten.
192
+ Note that you are then tied to that directory: move or delete it, and the server fails in
193
+ all your projects.
174
194
 
175
- ## Configuratie
195
+ ## Configuration
176
196
 
177
- Alles kan als env-variabele in je MCP-config, of in één configbestand. **De omgeving wint van
178
- het bestand**: die staat bij één client, het bestand geldt voor de hele machine.
197
+ Everything can be set as an env variable in your MCP config, or in a single config file.
198
+ **The environment wins over the file**: the environment belongs to one client, the file
199
+ applies to the whole machine.
179
200
 
180
- ### Een configbestand
201
+ ### A config file
181
202
 
182
- `~/.config/phpstan-mcp/config.json` (of `$XDG_CONFIG_HOME/phpstan-mcp/config.json`, of het
183
- pad in `PHPSTAN_MCP_CONFIG`):
203
+ `~/.config/phpstan-mcp/config.json` (or `$XDG_CONFIG_HOME/phpstan-mcp/config.json`, or the
204
+ path in `PHPSTAN_MCP_CONFIG`):
184
205
 
185
206
  ```json
186
207
  {
187
- "roots": ["/Users/jij/projecten"],
208
+ "roots": ["/Users/you/projects"],
188
209
  "memoryLimit": "2G",
189
210
  "maxParallel": 2,
190
211
  "tmpDir": "project",
191
212
  "allowWrites": false,
192
213
  "runners": {
193
- "/Users/jij/projecten/admin": {
214
+ "/Users/you/projects/admin": {
194
215
  "command": ["docker", "exec", "-w", "/var/www/html", "admin-app"],
195
216
  "workdir": "/var/www/html"
196
217
  }
197
218
  },
198
219
  "projects": {
199
- "/Users/jij/projecten/shop": { "memoryLimit": "4G", "timeoutSeconds": 900 }
220
+ "/Users/you/projects/shop": { "memoryLimit": "4G", "timeoutSeconds": 900 }
200
221
  }
201
222
  }
202
223
  ```
203
224
 
204
- Daarmee is je MCP-registratie één regel zonder `--env`, en staat de configuratie op één plek
205
- in plaats van verdeeld over zeven variabelen.
225
+ That makes your MCP registration a single line without `--env`, and keeps the configuration
226
+ in one place instead of spread over seven variables.
206
227
 
207
- Een bestand dat er niet is, is geen fout: dan geldt alleen de omgeving. Een bestand dat er
208
- wél is maar niet klopt, laat de server niet startendan heeft iemand iets bedoeld wat
209
- anders stilzwijgend niet gebeurt.
228
+ A file that doesn't exist is not an error: then only the environment applies. A file that
229
+ *does* exist but is invalid stops the server from startingsomeone meant something there
230
+ that would otherwise silently not happen.
210
231
 
211
- ### `PHPSTAN_MCP_ROOTS` (verplicht)
232
+ ### `PHPSTAN_MCP_ROOTS` (required)
212
233
 
213
- `PHPSTAN_MCP_ROOTS` is verplicht: een lijst absolute paden, gescheiden door een dubbele punt
214
- (een puntkomma op Windows). Alles daaronder mag geanalyseerd worden, alles daarbuiten niet.
234
+ `PHPSTAN_MCP_ROOTS` is required: a list of absolute paths, separated by a colon (a semicolon
235
+ on Windows). Everything below them may be analysed, nothing outside them.
215
236
 
216
237
  ```
217
- PHPSTAN_MCP_ROOTS=/Users/jij/projecten:/Users/jij/werk/klant-x
238
+ PHPSTAN_MCP_ROOTS=/Users/you/projects:/Users/you/work/client-x
218
239
  ```
219
240
 
220
- Een root hoeft niet zelf een project te zijn. De server zoekt tot vier niveaus diep naar
221
- mappen met een `composer.json`, stopt zodra hij er een vindt, en slaat `vendor/`,
222
- `node_modules/` en verborgen mappen over. Een indeling als `~/projecten/<klant>/<project>`
223
- werkt dus met `~/projecten` als enige root.
241
+ A root doesn't have to be a project itself. The server searches up to four levels deep for
242
+ directories with a `composer.json`, stops as soon as it finds one, and skips `vendor/`,
243
+ `node_modules/` and hidden directories. A layout like `~/projects/<client>/<project>` works
244
+ with `~/projects` as the only root.
224
245
 
225
- ### `PHPSTAN_MCP_MAX_PARALLEL` (optioneel)
246
+ ### `PHPSTAN_MCP_MAX_PARALLEL` (optional)
226
247
 
227
- Hoeveel analyses er tegelijk mogen draaien, over projecten heen. Standaard twee.
248
+ How many analyses may run at the same time, across projects. Two by default.
228
249
 
229
250
  ```
230
251
  PHPSTAN_MCP_MAX_PARALLEL=4
231
252
  ```
232
253
 
233
- Analyses binnen hetzelfde project gaan altijd op volgordePHPStans resultcache ligt in het
234
- project en twee gelijktijdige runs schrijven daar overheen. Deze grens gaat over de machine
235
- als geheel: zonder hem start een model dat na `list_projects` tien projecten aanroept tien
236
- PHPStan-processen, die elk hun eigen parallelle workers opzetten. Met
237
- `PHPSTAN_MCP_MEMORY_LIMIT=2G` is dat tien keer dat plafond.
254
+ Analyses within the same project always run in ordertwo simultaneous runs would overwrite
255
+ each other's result cache. This limit is about the machine as a whole: without it, a model
256
+ that calls ten projects after `list_projects` starts ten PHPStan processes, each spinning up
257
+ its own parallel workers. With `PHPSTAN_MCP_MEMORY_LIMIT=2G` that is ten times that ceiling.
238
258
 
239
- Een onbruikbare waarde wordt geweigerd in plaats van stil vervangen door de standaard, en
240
- komt als `LET OP` in de opstartregel:
259
+ An unusable value is rejected instead of silently replaced by the default, and shows up as
260
+ `LET OP` ("attention") in the startup line:
241
261
 
242
262
  ```
243
263
  phpstan-mcp 0.3.1 luistert op stdio, 1 root(s) geconfigureerd, hoogstens 2 tegelijk
244
264
  ```
245
265
 
246
- ### `PHPSTAN_MCP_TMPDIR` (optioneel)
266
+ ### `PHPSTAN_MCP_TMPDIR` (optional)
247
267
 
248
- Waar PHPStan zijn tijdelijke bestanden neerzet, en daarmee waar de resultcache landt.
268
+ Where PHPStan puts its temporary files, and therefore where the result cache ends up.
249
269
 
250
- | waarde | betekenis |
270
+ | value | meaning |
251
271
  | --- | --- |
252
- | `project` (standaard) | elk project een eigen map onder de tijdelijke map van het systeem |
253
- | `shared` | niets zetten; wat het systeem toch al gebruikt |
272
+ | `project` (default) | each project gets its own directory under the system temp directory |
273
+ | `shared` | set nothing; whatever the system uses anyway |
254
274
 
255
- De standaard lost een stil probleem op. PHPStans `tmpDir` is standaard
256
- `sys_get_temp_dir()/phpstan`, dus élk project zonder eigen `tmpDir` in zijn config deelt één
257
- `resultCache.php`. Twee projecten na elkaar maken dan elkaars cache ongeldig, en de cache
258
- de reden dat een tweede analyse snel is — doet niets meer. Gemeten op twee echte projecten,
259
- met een ander project ertussendoor:
275
+ The default solves a silent problem. PHPStan's `tmpDir` defaults to
276
+ `sys_get_temp_dir()/phpstan`, so *every* project without its own `tmpDir` in its config
277
+ shares a single `resultCache.php`. Two projects in a row then invalidate each other's cache,
278
+ and the cache the reason a second analysis is fast stops doing anything. Measured on two
279
+ real projects, with another project in between:
260
280
 
261
281
  ```
262
- eigen map: 1s → (ander project) → 1s
263
- gedeelde map: 5s → (ander project) → 3s
282
+ own directory: 1s → (other project) → 1s
283
+ shared directory: 5s → (other project) → 3s
264
284
  ```
265
285
 
266
- Zet je `tmpDir` zelf in je `phpstan.neon`, dan wint die: een configuratie die je expliciet
267
- hebt gemaakt gaat vóór wat deze server aandraagt.
286
+ The directory name also contains a hash of `composer.lock`. After a `composer update`,
287
+ PHPStan therefore starts in a fresh directory: a result cache from before such an update
288
+ used to make PHPStan crash with a message that pointed nowhere. The server leaves old
289
+ directories in place; the system cleans those up itself.
290
+
291
+ If you set `tmpDir` yourself in your `phpstan.neon`, that wins: a configuration you made
292
+ explicitly takes precedence over what this server provides.
268
293
 
269
- Kies `shared` als je wil dat de server dezelfde cache gebruikt als je terminal, of als het
270
- zetten van `TMPDIR` iets in je project stoort bij Larastan draait er een hele
271
- Laravel-applicatie in hetzelfde proces mee.
294
+ Choose `shared` if you want the server to use the same cache as your terminal, or if setting
295
+ `TMPDIR` disturbs something in your project — with Larastan, an entire Laravel application
296
+ runs in the same process.
272
297
 
273
- ### `PHPSTAN_MCP_RUNNERS` (optioneel)
298
+ ### `PHPSTAN_MCP_RUNNERS` (optional)
274
299
 
275
- Sommige projecten zijn op de host niet te analyseren. Larastan start je Laravel-applicatie
276
- écht op, en mist de host bijvoorbeeld `ext-redis`, dan klapt de bootstrap en stopt de analyse
277
- terwijl dezelfde repo in zijn container probleemloos doorloopt.
300
+ Some projects cannot be analysed on the host. Larastan really boots your Laravel
301
+ application, and if the host lacks, say, `ext-redis`, the bootstrap fails and the analysis
302
+ stops while the same repository runs fine inside its container.
278
303
 
279
- Met deze variabele zeg je per project waar PHPStan gedraaid moet worden:
304
+ With this variable you say, per project, where PHPStan should run:
280
305
 
281
306
  ```json
282
307
  {
283
- "/Users/jij/projecten/admin": {
308
+ "/Users/you/projects/admin": {
284
309
  "command": ["docker", "exec", "-w", "/var/www/html", "admin-app"],
285
310
  "workdir": "/var/www/html"
286
311
  }
287
312
  }
288
313
  ```
289
314
 
290
- `command` is het commando dat ervóór komt; `workdir` is het pad waarop diezelfde projectmap
291
- in die omgeving staat. Dat tweede is geen detail: paden die je meegeeft worden ernaartoe
292
- vertaald, en de bestandspaden in het antwoord komen terug als host-paden anders krijg je
293
- `/var/www/html/app/User.php` terug en kun je dat bestand niet openen.
315
+ `command` is the command that goes in front; `workdir` is the path at which that same
316
+ project directory lives inside that environment. The second one is not a detail: paths you
317
+ pass in are translated to it, and file paths in the response come back as host paths
318
+ otherwise you get `/var/www/html/app/User.php` back and can't open that file.
294
319
 
295
- `list_projects` vermeldt per project welke omgeving geldt, en met een runner hoeft er op de
296
- host geen `vendor/bin/phpstan` te staan. Alles loopt dan via de runner: analyseren, maar ook
297
- `diagnose_project`, `clear_result_cache` en `generate_baseline`. Alleen `unmatched_ignores`
298
- werkt niet met een runner (zie daar).
320
+ `list_projects` shows which environment applies to each project, and with a runner there is
321
+ no need for `vendor/bin/phpstan` on the host. Everything then goes through the runner:
322
+ analysing, but also `diagnose_project`, `clear_result_cache`, `generate_baseline` and
323
+ `unmatched_ignores`.
299
324
 
300
- `workdir` is een POSIX-pad, ook als de host Windows is: de omgeving is een Linux-container
301
- of WSL. De projectsleutel mag in de vorm van je platform staan (`C:\Users\jij\admin` of
302
- `C:/Users/jij/admin`, met of zonder slash erachter); hij wordt vergeleken na het oplossen
303
- van symlinks, net als het `project`-argument zelf.
325
+ `workdir` is a POSIX path, even when the host is Windows: the environment is a Linux
326
+ container or WSL. The project key may be written in your platform's form
327
+ (`C:\Users\you\admin` or `C:/Users/you/admin`, with or without a trailing slash); it is
328
+ compared after resolving symlinks, just like the `project` argument itself.
304
329
 
305
- **Het commando komt uit de serverconfiguratie, nooit uit een tool-aanroep.** Net als de roots
306
- is dit iets wat een mens één keer vastlegt: een model kiest alleen een project, en of daar
307
- een omgeving bij hoort ligt dan al vast. Zou een aanroeper het commando mogen meegeven, dan
308
- bepaalt die wat er uitgevoerd wordt en is de whitelist zinloos.
330
+ **The command comes from the server configuration, never from a tool call.** Like the roots,
331
+ this is something a human sets once: a model only picks a project, and whether an
332
+ environment belongs to it is already decided. If a caller could pass in the command, the
333
+ caller would decide what gets executed and the whitelist would be pointless.
309
334
 
310
- ### Waarom die lijst verplicht is
335
+ ### Why that list is required
311
336
 
312
- PHPStan is geen passieve parser. Het laadt de autoloader van het project, voert
313
- `bootstrapFiles` uit de configuratie uit en laadt extensies als PHP-code. "Analyseer map X"
314
- betekent daarmee "voer de PHP-code uit die in X's configuratie staat" — en het pad komt van
315
- een taalmodel. De lijst met roots maakt expliciet en menselijk vastgesteld wat binnen bereik
316
- ligt.
337
+ PHPStan is not a passive parser. It loads the project's autoloader, executes
338
+ `bootstrapFiles` from the configuration and loads extensions as PHP code. "Analyse directory
339
+ X" therefore means "execute the PHP code that X's configuration points to" — and the path
340
+ comes from a language model. The list of roots makes what is in reach explicit and decided
341
+ by a human.
317
342
 
318
- ### `PHPSTAN_MCP_MEMORY_LIMIT` (optioneel)
343
+ ### `PHPSTAN_MCP_MEMORY_LIMIT` (optional)
319
344
 
320
- Klapt PHPStan op de PHP-geheugenlimiet, dan kan een aanroep `memoryLimit` meegeven maar
321
- dat gebeurt pas nadat de analyse al mislukt is. Met deze variabele leg je de standaard één
322
- keer vast, naast je roots:
345
+ If PHPStan runs into the PHP memory limit, a call can pass `memoryLimit` — but only after
346
+ the analysis has already failed. With this variable you set the default once, next to your
347
+ roots:
323
348
 
324
349
  ```
325
350
  PHPSTAN_MCP_MEMORY_LIMIT=2G
326
351
  ```
327
352
 
328
- Dat wordt `--memory-limit=2G` op elke aanroep die zelf niets meegeeft; een `memoryLimit` in
329
- de aanroep gaat vóór. Toegestaan is wat PHPStan zelf accepteert: een getal in bytes,
330
- optioneel met `K`, `M` of `G`, of `-1` voor onbeperkt. Iets anders wordt geweigerd met een
331
- melding in plaats van doorgegeven, want PHPStan zou er per analyse op klappen.
353
+ That becomes `--memory-limit=2G` on every call that doesn't pass one itself; a `memoryLimit`
354
+ in the call takes precedence. Allowed is whatever PHPStan itself accepts: a number of bytes,
355
+ optionally with `K`, `M` or `G`, or `-1` for unlimited. Anything else is rejected with a
356
+ message instead of passed on, because PHPStan would crash on it for every analysis.
332
357
 
333
- Dit is de enige manier om het bij de server vast te leggen. PHPStan kent er geen
334
- `phpstan.neon`-parameter voor (`Unexpected item 'parameters › memoryLimit'`) en ook geen
335
- eigen env-variabele; het alternatief is `memory_limit` in je `php.ini`, en dat geldt dan
336
- voor élke PHP-CLI op je machine.
358
+ This is the only way to set it at the server level. PHPStan has no `phpstan.neon` parameter
359
+ for it (`Unexpected item 'parameters › memoryLimit'`) and no env variable of its own; the
360
+ alternative is `memory_limit` in your `php.ini`, which then applies to *every* PHP CLI on
361
+ your machine.
337
362
 
338
- Heeft één project meer nodig dan de rest, zet het dan bij dat project in
339
- `PHPSTAN_MCP_PROJECTS` in plaats van deze waarde voor iedereen op te hogen — met
340
- `PHPSTAN_MCP_MAX_PARALLEL=2` is een machinebrede 4G twee keer 4G.
363
+ If one project needs more than the rest, set it for that project in `PHPSTAN_MCP_PROJECTS`
364
+ instead of raising this value for everyone with `PHPSTAN_MCP_MAX_PARALLEL=2`, a
365
+ machine-wide 4G is two times 4G.
341
366
 
342
- ### `PHPSTAN_MCP_PROJECTS` (optioneel)
367
+ ### `PHPSTAN_MCP_PROJECTS` (optional)
343
368
 
344
- Standaarden voor één project, als JSON — of onder `projects` in het configbestand:
369
+ Defaults for a single project, as JSON — or under `projects` in the config file:
345
370
 
346
371
  ```json
347
372
  {
348
- "/Users/jij/projecten/shop": {
373
+ "/Users/you/projects/shop": {
349
374
  "memoryLimit": "4G",
350
375
  "timeoutSeconds": 900,
351
376
  "config": "phpstan-ci.neon"
@@ -353,222 +378,254 @@ Standaarden voor één project, als JSON — of onder `projects` in het configbe
353
378
  }
354
379
  ```
355
380
 
356
- | Sleutel | Betekenis |
381
+ | Key | Meaning |
357
382
  | --- | --- |
358
- | `memoryLimit` | `--memory-limit` voor dit project, in dezelfde vorm als `PHPSTAN_MCP_MEMORY_LIMIT` |
359
- | `timeoutSeconds` | timeout voor dit project, van 1 tot en met 1800 |
360
- | `config` | configbestand binnen het project, als PHPStan dat niet zelf vindt |
383
+ | `memoryLimit` | `--memory-limit` for this project, in the same form as `PHPSTAN_MCP_MEMORY_LIMIT` |
384
+ | `timeoutSeconds` | timeout for this project, from 1 up to and including 1800 |
385
+ | `config` | config file inside the project, if PHPStan doesn't find it by itself |
361
386
 
362
- De volgorde is steeds dezelfde: wat de aanroep meegeeft, dan de standaard van het project,
363
- dan die van de hele server. Een standaard is dus geen plafond.
387
+ The order is always the same: what the call passes, then the project default, then the
388
+ server-wide one. A default is therefore not a ceiling.
364
389
 
365
- `config` gaat door dezelfde padcontrole als een `config` uit een aanroep en moet binnen het
366
- project liggen; een neon kan PHP-code laten uitvoeren. Hij telt overal als config van het
367
- project: bij de melding dat er een baseline meedoet en bij `unmatched_ignores`.
390
+ `config` goes through the same path check as a `config` from a call and must lie inside the
391
+ project; a neon file can cause PHP code to be executed. It counts everywhere as *the* config
392
+ of the project: for the notice that a baseline is included and for `unmatched_ignores`.
368
393
 
369
- Een onbekende sleutel wordt geweigerd, niet genegeerd: `memorylimit` met een kleine l zou
370
- anders stil niets doen. `list_projects` en `server_config` tonen welke standaarden er gelden,
371
- en een kapotte waarde staat daar als probleem bij de lijst met projecten blijft werken.
394
+ An unknown key is rejected, not ignored: `memorylimit` with a lowercase l would otherwise
395
+ silently do nothing. `list_projects` and `server_config` show which defaults apply, and a
396
+ broken value is listed there as a problemthe project list keeps working.
372
397
 
373
398
  ## Tools
374
399
 
375
- Elke tool verwacht een expliciet `project`-pad. De server heeft geen "huidig project": dat
376
- zou toestand zijn die het model niet in zijn context ziet staan, met stilzwijgend het
377
- verkeerde project als gevolg.
400
+ Every tool expects an explicit `project` path. The server has no "current project": that
401
+ would be state the model doesn't see in its context, with the wrong project silently
402
+ analysed as a result.
378
403
 
379
404
  ### `list_projects`
380
405
 
381
- Somt de projecten op die binnen bereik liggen, met per project of PHPStan geïnstalleerd is
382
- en welk configbestand er ligt. Begin hierhet `path` uit het resultaat is wat de andere
383
- tools als `project` verwachten.
406
+ Lists the projects that are in reach, with for each project whether PHPStan is installed and
407
+ which config file it has. Start herethe `path` from the result is what the other tools
408
+ expect as `project`.
384
409
 
385
410
  ### `analyse_project`
386
411
 
387
- | Argument | Verplicht | Betekenis |
412
+ | Argument | Required | Meaning |
388
413
  | --- | --- | --- |
389
- | `project` | ja | absoluut pad naar de projectroot |
390
- | `paths` | nee | paden binnen het project; leeg betekent: volg `phpstan.neon` |
391
- | `level` | nee | level-override, `0` t/m `10` of `max` |
392
- | `config` | nee | ander configbestand, bijv. `phpstan-strict.neon`; moet binnen het project liggen |
393
- | `memoryLimit` | nee | bijv. `1G`, als PHPStan op de PHP-geheugenlimiet klapt |
394
- | `timeoutSeconds` | nee | standaard 300 |
395
- | `identifiers` | nee | alleen deze identifiers in de pagina, bijv. `["argument.type"]` |
396
- | `offset` / `limit` | nee | welk deel van de bevindingen je terugkrijgt |
397
-
398
- `config` gaat door dezelfde padcontrole als `project`. Dat is geen formaliteit: een `.neon`
399
- kan `bootstrapFiles` en extensies aanwijzen, dus een vrij te kiezen configpad zou een route
400
- zijn om willekeurige PHP-code te laten uitvoeren langs de whitelist heen.
401
-
402
- `identifiers` filtert alleen de *pagina*; de samenvatting blijft over alles tellen, zodat
403
- zichtbaar blijft wat je wegliet. Een waarde matcht exact (`argument.type`) of als groep
404
- (`missingType` matcht `missingType.return` en `missingType.parameter`, maar `argument` matcht
405
- niet `argumentx.iets`).
414
+ | `project` | yes | absolute path to the project root |
415
+ | `paths` | no | paths inside the project; empty means: follow `phpstan.neon` |
416
+ | `level` | no | level override, `0` to `10` or `max` |
417
+ | `config` | no | a different config file, e.g. `phpstan-strict.neon`; must lie inside the project |
418
+ | `memoryLimit` | no | e.g. `1G`, if PHPStan hits the PHP memory limit |
419
+ | `timeoutSeconds` | no | 300 by default |
420
+ | `identifiers` | no | only these identifiers in the page, e.g. `["argument.type"]` |
421
+ | `offset` / `limit` | no | which part of the findings you get back |
422
+ | `runId` | no | from an earlier response: the next page from the same result (see [What you get back](#what-you-get-back)) |
423
+
424
+ `config` goes through the same path check as `project`. That is not a formality: a `.neon`
425
+ can point to `bootstrapFiles` and extensions, so a freely chosen config path would be a way
426
+ to have arbitrary PHP code executed around the whitelist.
427
+
428
+ `identifiers` only filters the *page*; the summary keeps counting everything, so you can
429
+ still see what you left out. A value matches exactly (`argument.type`) or as a group
430
+ (`missingType` matches `missingType.return` and `missingType.parameter`, but `argument` does
431
+ not match `argumentx.something`).
406
432
 
407
433
  ### `analyse_file`
408
434
 
409
- Zelfde argumenten, plus een verplichte `path` — één bestand of map binnen het project. Het
410
- pad mag relatief aan de projectroot zijn of absoluut, maar moet er wel binnen liggen.
435
+ Same arguments, plus a required `path` — a single file or directory inside the project. The
436
+ path may be relative to the project root or absolute, but it has to lie inside it.
411
437
 
412
438
  ### `analyse_changed`
413
439
 
414
- Analyseert de PHP-bestanden die volgens git gewijzigd zijn. Bedoeld om te zien of een eigen
415
- wijziging fouten oplevert, zonder het hele project door te rekenen en zonder dat het
416
- antwoord volloopt met wat er al jaren stond.
440
+ 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
442
+ filling up with what has been there for years.
417
443
 
418
- | Argument | Verplicht | Betekenis |
444
+ | Argument | Required | Meaning |
419
445
  | --- | --- | --- |
420
- | `project` | ja | absoluut pad naar de projectroot |
421
- | `base` | nee | commit, branch of tag om tegen te vergelijken (standaard `HEAD`) |
422
- | `staged` | nee | alleen wat gestaged is (`git diff --cached`), voor een check vóór een commit |
423
- | `onlyChangedLines` | nee | alleen bevindingen op regels die gewijzigd zijn |
424
-
425
- Verder dezelfde argumenten als `analyse_project`, op `paths` na. Met `base=main` krijg je de
426
- hele branch in plaats van alleen je onopgeslagen werk.
427
-
428
- Elke bevinding zegt met `onChangedLine` of hij op een gewijzigde regel staat; in de tekst
429
- staat er een `+` voor. Een gewijzigd bestand bevat vaak ook fouten die er al stonden, en
430
- zonder dat onderscheid ga je die repareren of stop je te vroeg. Let wel: een bevinding op een
431
- ongewijzigde regel kan nog steeds uit je wijziging volgeneen aangepaste signatuur geeft
432
- fouten bij de aanroepen, en die regels heb je niet aangeraakt.
433
-
434
- Twee dingen die het meeneemt en die je makkelijk mist: **nieuwe bestanden die nog niet aan
435
- git toegevoegd zijn** (`git diff` kent die niet, dus er is een tweede uitvraag voor) en
436
- **niets uit `resources/views`** — een `.blade.php` eindigt op `.php`, maar is een template
437
- die PHPStan bij een normale run nooit ziet, omdat de config die map niet in `paths` noemt.
438
-
439
- Elk pad dat git noemt gaat door dezelfde controle als een pad dat het model zelf geeft. Wat
440
- daar niet doorkomteen kapotte symlink, een bestand dat buiten het project uitwijst komt
441
- als "overgeslagen" bij het antwoord te staan in plaats van er stil uit te vallen.
442
-
443
- Staat het project niet onder git, dan zegt de tool dat en verwijst hij naar `analyse_project`
444
- met expliciete `paths`.
446
+ | `project` | yes | absolute path to the project root |
447
+ | `base` | no | commit, branch or tag to compare against (`HEAD` by default) |
448
+ | `staged` | no | only what is staged (`git diff --cached`), for a check before a commit |
449
+ | `onlyChangedLines` | no | only findings on lines that were changed |
450
+
451
+ Otherwise the same arguments as `analyse_project`, except `paths`. With `base=main` you get
452
+ the whole branch instead of just your uncommitted work.
453
+
454
+ Each finding says with `onChangedLine` whether it is on a changed line; in the text it is
455
+ prefixed with a `+`. A changed file often also contains errors that were already there, and
456
+ without that distinction you either start fixing those or stop too early. Keep in mind: a
457
+ finding on an unchanged line can still follow from your change a modified signature causes
458
+ errors at the call sites, and you didn't touch those lines.
459
+
460
+ Two things it includes that are easy to miss: **new files not yet added to git** (`git diff`
461
+ doesn't know them, so there is a second query for them) and **nothing from
462
+ `resources/views`** — a `.blade.php` ends in `.php`, but it is a template that PHPStan never
463
+ sees in a normal run, because the config doesn't list that directory in `paths`.
464
+
465
+ Every path git reports goes through the same check as a path the model supplies itself.
466
+ Whatever doesn't passa broken symlink, a file pointing outside the project — is listed as
467
+ "skipped" in the response instead of silently dropping out.
468
+
469
+ If the project is not under git, the tool says so and points to `analyse_project` with
470
+ explicit `paths`.
471
+
472
+ **What the change hides** is listed under "Ter info" (for your information): new
473
+ `@phpstan-ignore` comments (with file, line, identifier and reason) and ignored errors added
474
+ to a baseline that is included — even if no PHP file changed otherwise. An ignore whose reason
475
+ is the only thing that changed does not count as new. The notice doesn't pass judgement: an
476
+ ignore can be justified, the point is that it doesn't happen silently. Ignored errors that
477
+ disappeared from the baseline are listed too.
445
478
 
446
479
  ### `server_config`
447
480
 
448
- Geeft terug waarmee déze server draait: de projectmappen die binnen bereik liggen, de
449
- standaard geheugenlimiet, hoeveel analyses er tegelijk mogen, waar de tijdelijke bestanden
450
- heen gaan en of schrijvende tools aanstaan.
481
+ Returns what *this* server is running with: the project directories in reach, the default
482
+ memory limit, how many analyses may run at once, where temporary files go and whether
483
+ writing tools are enabled.
451
484
 
452
485
  ```
453
- phpstan-mcp 0.7.1
486
+ phpstan-mcp 0.15.0
454
487
 
455
488
  Projectmappen:
456
- /Users/jij/projecten
457
- /bestaat/niet — bestaat niet
489
+ /Users/you/projects
490
+ /does/not/exist — bestaat niet
458
491
 
459
492
  Geheugenlimiet: 2G
460
493
  Tegelijk: 2
461
494
  Tijdelijke map: project
462
495
  Schrijvende tools: uit
496
+ Eigen omgeving: geen
497
+
498
+ Standaarden per project (een tool-argument gaat voor):
499
+ /Users/you/projects/shop: geheugen 4G, timeout 900s
463
500
  ```
464
501
 
465
- Die informatie stond tot nu toe alleen in de opstartregel op stderr — zichtbaar in je
466
- clientlog, onzichtbaar voor het model. Een geweigerde instelling wordt gemeld in plaats van
467
- weggelaten: staat `PHPSTAN_MCP_MAX_PARALLEL` op onzin, dan is dat veld `null` en staat de
468
- reden onder "Geweigerde instellingen", terwijl de rest gewoon leesbaar blijft.
502
+ Until now that information was only in the startup line on stderr — visible in your client
503
+ log, invisible to the model. A rejected setting is reported instead of omitted: if
504
+ `PHPSTAN_MCP_MAX_PARALLEL` is set to nonsense, that field is `null` and the reason appears
505
+ under "Geweigerde instellingen" (rejected settings), while the rest stays readable.
469
506
 
470
- Heeft een project eigen standaarden (`PHPSTAN_MCP_PROJECTS`), dan staan die er per project
471
- onder.
507
+ If a project has its own defaults (`PHPSTAN_MCP_PROJECTS`), they are listed per project.
472
508
 
473
- Let op wat er *niet* in staat: dit is een vaste lijst van instellingen en geen doorgeefluik
474
- naar de omgeving. In dezelfde omgeving zitten tokens; die horen niet in een
475
- toolantwoord.
509
+ Note what is *not* in there: this is a fixed list of settings and not a pass-through to the
510
+ environment. The same environment holds tokens; those don't belong in a tool response.
476
511
 
477
512
  ### `diagnose_project`
478
513
 
479
- Vraagt PHPStan zelf om zijn opstelling: PHP- en PHPStan-versie, de configs die
480
- Composer-pakketten meebrengen (zoals Larastan) en of de turbo-extensie aanstaat. Draait geen
481
- analyse.
514
+ Asks PHPStan itself for its setup: PHP and PHPStan version, the configs that Composer
515
+ packages bring along (such as Larastan) and whether the turbo extension is enabled. Does not
516
+ run an analysis.
517
+
518
+ Use this when one project behaves differently from another, before you start doubting the
519
+ code. That is not theoretical: this is how it turned out that one project had a dependency
520
+ (`laravel/pao`) that intercepted PHPStan's output.
482
521
 
483
- Gebruik dit als een project zich anders gedraagt dan een ander project, vóórdat je aan de
484
- code gaat twijfelen. Dat is niet theoretisch: hiermee bleek dat één project een dependency
485
- had (`laravel/pao`) die de uitvoer van PHPStan onderschepte.
522
+ Below PHPStan's own text comes the configuration as PHPStan ends up using it:
523
+
524
+ ```
525
+ Uiteindelijke configuratie, volgens PHPStan zelf:
526
+ Level: 10
527
+ Paden: app, tests
528
+ Uitgesloten: geen
529
+ Negeerregels: 1312 (verouderde worden níet gemeld; unmatched_ignores zoekt ze op)
530
+ Tijdelijke map: /tmp/phpstan-mcp/cms-0d03c8f34a81/phpstan
531
+ Bootstrap: vendor/larastan/larastan/bootstrap.php
532
+ ```
533
+
534
+ The level shows up here even when it is set in a shared config from `vendor/`, and the temp
535
+ directory is the one `clear_result_cache` clears. If the application doesn't boot (Larastan
536
+ without the extensions the app needs), this part is missing, with the reason stated.
486
537
 
487
538
  ### `clear_result_cache`
488
539
 
489
- Leegt PHPStans resultcache, zodat de volgende analyse alles opnieuw doorrekent. Nuttig als
490
- een uitslag niet klopt met de code — bijvoorbeeld na een wijziging aan een extensie of een
491
- geïncludeerde config.
540
+ Clears PHPStan's result cache, so the next analysis recomputes everything. Useful when a
541
+ result doesn't match the code — for example after a change to an extension or an included
542
+ config.
492
543
 
493
- Die cache ligt standaard **niet** in het project maar in de tijdelijke map van het systeem
494
- (`sys_get_temp_dir()/phpstan`), en wordt dan gedeeld met elk ander project dat geen eigen
495
- `tmpDir` zet. Bovendien volgt die map `TMPDIR`, dus wat de server leegt hoeft niet dezelfde
496
- map te zijn als wat je terminal gebruikt. Het antwoord noemt daarom de map die geleegd is:
544
+ By default that cache does **not** live in the project but in the system temp directory
545
+ (`sys_get_temp_dir()/phpstan`), and is then shared with every other project that doesn't set
546
+ its own `tmpDir`. On top of that, the directory follows `TMPDIR`, so what the server clears
547
+ need not be the same directory your terminal uses. The response therefore names the directory
548
+ that was cleared:
497
549
 
498
550
  ```
499
551
  Result cache cleared from directory:
500
552
  /var/tmp/phpstan
501
553
  ```
502
554
 
503
- Wil je per project een eigen cache, zet dan `tmpDir` in je `phpstan.neon`.
555
+ If you want a separate cache per project, set `tmpDir` in your `phpstan.neon`.
504
556
 
505
557
  ### `baseline_summary`
506
558
 
507
- Leest de `phpstan-baseline*.neon` van een project en telt wat er genegeerd wordt, per
508
- identifier en per bestand. Draait geen analyse, dus het antwoord is er meteen.
559
+ Reads a project's `phpstan-baseline*.neon` and counts what is being ignored, per identifier
560
+ and per file. Does not run an analysis, so the answer is immediate.
561
+
562
+ Note the distinction: what is in here is exactly what `analyse_project` does *not* report. A
563
+ project that includes its baseline only reports the errors that are new — PHPStan already
564
+ makes that comparison itself.
509
565
 
510
- Let op het onderscheid: wat hier in staat is precies wat `analyse_project` *niet* meldt. Een
511
- project dat zijn baseline includet, rapporteert alleen de fouten die er nieuw bij komen
512
- die vergelijking maakt PHPStan zelf al.
566
+ With `path` (e.g. `"app/Models/User.php"`) you also get the entries for that one file: per
567
+ entry the identifier, the count and the pattern. That file doesn't have to exist any more
568
+ it is precisely a deleted file that the entries doing nothing belong to.
513
569
 
514
570
  ### `unmatched_ignores`
515
571
 
516
- Zoekt de negeerregels die niets meer doen: baseline-regels voor fouten die allang opgelost
517
- zijn, en regels waarvan de `count` hoger is dan wat er nog voorkomt. Veel projecten zetten
518
- `reportUnmatchedIgnoredErrors: false`, en dan groeit dat ongemerktop één project bleken
519
- 554 van de 2116 baseline-regels niets meer te matchen.
572
+ Finds the ignore rules that no longer do anything: baseline entries for errors that were
573
+ fixed long ago, and entries whose `count` is higher than what still occurs. Many projects set
574
+ `reportUnmatchedIgnoredErrors: false`, and then this grows unnoticedon one project, 554 of
575
+ the 2116 baseline entries turned out to match nothing any more.
520
576
 
521
- | Argument | Verplicht | Betekenis |
577
+ | Argument | Required | Meaning |
522
578
  | --- | --- | --- |
523
- | `project` | ja | absoluut pad naar de projectroot |
524
- | `memoryLimit` | nee | dit is een volledige analyse, dus die kan meer nodig hebben |
525
- | `timeoutSeconds` | nee | standaard 300 |
526
- | `offset` / `limit` | nee | welk deel van de regels je terugkrijgt |
579
+ | `project` | yes | absolute path to the project root |
580
+ | `memoryLimit` | no | this is a full analysis, so it may need more |
581
+ | `timeoutSeconds` | no | 300 by default |
582
+ | `offset` / `limit` | no | which part of the entries you get back |
527
583
 
528
- Dit is een volledige analyse, met een tijdelijke config die die van het project includet en
529
- de vlag omzet. Die config staat in de tijdelijke map van de server en niet in het project,
530
- dus er verandert niets aan je repository. PHPStans resultcache blijft bruikbaar: de vlag
531
- verandert niets aan wat er geanalyseerd wordt, dus de volgende gewone analyse is net zo snel.
584
+ This is a full analysis, with a temporary config that includes the project's config and flips
585
+ the flag. That config lives in the server's temp directory and not in the project, so nothing
586
+ in your repository changes. PHPStan's result cache stays usable: the flag doesn't change what
587
+ is analysed, so the next regular analysis is just as fast.
532
588
 
533
- Per regel staat erbij of hij uit de baseline komt. Patronen zonder pad komen apart; die staan
534
- in `ignoreErrors` van je config of van een config die een pakket meebrengt, en daar helpt een
535
- nieuwe baseline niet.
589
+ Each entry says whether it comes from the baseline. Patterns without a path are listed
590
+ separately; they live in `ignoreErrors` of your config or of a config a package brings along,
591
+ and a new baseline doesn't help there.
536
592
 
537
- Er is bewust geen `level` en geen `paths`: op een ander niveau zijn het andere fouten, en bij
538
- een deel van het project lijkt alles daarbuiten verouderd.
593
+ There is deliberately no `level` and no `paths`: on a different level the errors are
594
+ different, and with only part of the project, everything outside it looks stale.
539
595
 
540
- Werkt niet op een project met een eigen uitvoeringsomgeving (`PHPSTAN_MCP_RUNNERS`): de
541
- tijdelijke config staat op de host en is in een container niet te zien. De tool zegt dat met
542
- reden `unsupported_with_runner`.
596
+ With an execution environment (`PHPSTAN_MCP_RUNNERS`) the temporary config goes into the
597
+ temporary directory of *that* environment (`${TMPDIR:-/tmp}` inside the container), because a
598
+ container can't see the host's temporary directory — only the project directory is mounted.
599
+ That needs a `sh` in the environment; without one, the tool says so with the reason
600
+ `unsupported_with_runner`. PHPStan can't read its config from stdin, so there is no way
601
+ around writing a small file somewhere; it is never written into the project itself.
543
602
 
544
- ### `generate_baseline` (alleen met schrijfrechten)
603
+ ### `generate_baseline` (only with write access)
545
604
 
546
- Zet alle huidige fouten in een baseline, zodat een volgende analyse alleen meldt wat er nieuw
547
- bij komt. Bedoeld om een bestaand project onder PHPStan te krijgen niet om fouten weg te
548
- werken.
605
+ Puts all current errors into a baseline, so that the next analysis only reports what is new.
606
+ Meant for bringing an existing project under PHPStan not for making errors go away.
549
607
 
550
- Deze tool bestaat alleen als de server met `PHPSTAN_MCP_ALLOW_WRITES=1` gestart is:
608
+ This tool only exists if the server was started with `PHPSTAN_MCP_ALLOW_WRITES=1`:
551
609
 
552
610
  ```bash
553
611
  claude mcp add phpstan --scope user \
554
- --env PHPSTAN_MCP_ROOTS=/pad/naar/projecten \
612
+ --env PHPSTAN_MCP_ROOTS=/path/to/projects \
555
613
  --env PHPSTAN_MCP_ALLOW_WRITES=1 \
556
614
  -- phpstan-mcp-mx
557
615
  ```
558
616
 
559
- Zonder die variabele wordt hij niet geregistreerd hij staat dan dus ook niet in de
560
- toollijst, in plaats van dat hij bestaat en bij elke aanroep weigert. Alle andere tools
561
- blijven read-only.
617
+ Without that variable it isn't registeredso it doesn't appear in the tool list at all,
618
+ rather than existing and refusing every call. All other tools stay read-only.
562
619
 
563
- | Argument | Verplicht | Betekenis |
620
+ | Argument | Required | Meaning |
564
621
  | --- | --- | --- |
565
- | `project` | ja | absoluut pad naar de projectroot |
566
- | `file` | nee | doelbestand binnen het project (standaard `phpstan-baseline.neon`) |
567
- | `allowEmpty` | nee | ook schrijven als er niets te negeren valt |
622
+ | `project` | yes | absolute path to the project root |
623
+ | `file` | no | target file inside the project (`phpstan-baseline.neon` by default) |
624
+ | `allowEmpty` | no | write even if there is nothing to ignore |
568
625
 
569
- Verder dezelfde `paths`, `level`, `config` en `memoryLimit` als een analyse. Het antwoord
570
- zegt erbij wat er nog moet gebeuren, want een baseline die nergens ingesloten wordt doet
571
- niets:
626
+ Otherwise the same `paths`, `level`, `config` and `memoryLimit` as an analysis. The response
627
+ says what still needs to happen, because a baseline that isn't included anywhere does
628
+ nothing:
572
629
 
573
630
  ```
574
631
  5 fout(en) in phpstan-baseline.neon gezet.
@@ -582,37 +639,37 @@ includes:
582
639
 
583
640
  ## Prompts
584
641
 
585
- Werkpatronen die alleen bestaande tools combineren. Een prompt en geen tool, omdat een tool
586
- context kost in elke sessie en een prompt alleen als je hem kiest. Elke vangrail in de teksten
587
- komt uit een proefrun op een echt project die zonder die zin misging.
588
-
589
- - **`phpstan_wijziging_nakijken`** — je eigen wijziging nakijken met `analyse_changed`,
590
- beginnen bij de bevindingen op gewijzigde regels, en herhalen tot het schoon is (zonder
591
- `runId`, anders zie je de uitslag van vóór je reparatie). Met `base` (bijv. `main`) voor
592
- een hele branch vóór een PR.
593
- - **`phpstan_level_verhogen`** wat een level hoger kost: twee tellingen met `limit: 1` en
594
- het verschil per soort. Het level staat vaak in een gedeelde config in `vendor/`, en
595
- `ignore.*`-bevindingen tellen niet mee: dat zijn inline `@phpstan-ignore`-regels die op een
596
- ander level niets te negeren hebben.
597
- - **`phpstan_baseline_afbouwen`** — wat er in de baseline verouderd is (`unmatched_ignores`),
598
- wat een nieuwe baseline zou verstoppen (`analyse_project`), en de goedkoopste échte afbouw.
599
- Opschonen en verstoppen blijven twee dingen: `generate_baseline` wordt alleen voorgesteld
600
- als er niets te verstoppen valt, en nooit uit zichzelf aangeroepen.
601
- - **`phpstan_soort_wegwerken`** — een opruimronde voor één identifier of een groep als
602
- `missingType`, met de `runId` binnen de ronde en zonder `runId` na elke reparatie. Zonder
603
- `identifier` kiest het model een soort: eerst wat bij de bron op te lossen is, anders een
604
- soort die in één of twee bestanden zit.
605
- - **`phpstan_projecten_vergelijken`** — twee projecten naast elkaar (`diagnose_project`,
606
- level, baseline, runners en standaarden), en alleen de verschillen. Verklaart geen verschil
607
- het gedrag, dan is een verouderde resultcache de volgende verdachte.
608
-
609
- Alle prompts nemen een optioneel `project` (bij de vergelijking `projectA` en `projectB`), en
610
- een client die aanvulling ondersteunt krijgt daar een keuzelijst uit `list_projects`.
611
-
612
- ## Als een tool mislukt
613
-
614
- Een mislukte aanroep geeft twee blokken terug: de melding in gewone taal, en een JSON-blok
615
- met velden waarop je kunt sturen.
642
+ Workflows that only combine existing tools. A prompt rather than a tool, because a tool costs
643
+ context in every session and a prompt only when you pick it. Every guardrail in the texts
644
+ comes from a trial run on a real project that went wrong without that sentence.
645
+
646
+ - **`phpstan_wijziging_nakijken`** (review your change) review your own change with
647
+ `analyse_changed`, start with the findings on changed lines, and repeat until it is clean
648
+ (without `runId`, otherwise you see the result from before your fix). With `base` (e.g.
649
+ `main`) for a whole branch before a PR.
650
+ - **`phpstan_level_verhogen`** (raise the level) what one level higher costs: two counts
651
+ with `limit: 1` and the difference per kind. The level is often set in a shared config in
652
+ `vendor/`, and `ignore.*` findings don't count: those are inline `@phpstan-ignore` comments
653
+ that have nothing to ignore on a different level.
654
+ - **`phpstan_baseline_afbouwen`** (shrink the baseline) what in the baseline is stale
655
+ (`unmatched_ignores`), what a new baseline would hide (`analyse_project`), and the cheapest
656
+ real reduction. Cleaning up and hiding stay two separate things: `generate_baseline` is only
657
+ suggested when there is nothing to hide, and never called on its own initiative.
658
+ - **`phpstan_soort_wegwerken`** (clear one kind) a clean-up round for one identifier or a
659
+ group such as `missingType`, with the `runId` within the round and without `runId` after
660
+ every fix. Without `identifier` the model picks a kind: first what can be fixed at the
661
+ source, otherwise a kind that sits in one or two files.
662
+ - **`phpstan_projecten_vergelijken`** (compare projects) two projects side by side
663
+ (`diagnose_project`, level, baseline, runners and defaults), showing only the differences.
664
+ If no difference explains the behaviour, a stale result cache is the next suspect.
665
+
666
+ All prompts take an optional `project` (for the comparison, `projectA` and `projectB`), and a
667
+ client that supports completion gets a picklist from `list_projects` there.
668
+
669
+ ## When a tool fails
670
+
671
+ A failed call returns two blocks: the message in plain language, and a JSON block with fields
672
+ you can act on.
616
673
 
617
674
  ```json
618
675
  {
@@ -625,20 +682,20 @@ met velden waarop je kunt sturen.
625
682
  }
626
683
  ```
627
684
 
628
- `reason` komt uit een gesloten verzameling, zodat je erop kunt matchen zonder de tekst te
629
- lezen: `no_roots_configured`, `project_outside_roots`, `project_not_found`,
630
- `path_outside_project`, `path_not_found`, `phpstan_missing`, `config_missing`,
631
- `bootstrap_failed`, `baseline_unreadable`, `invalid_setting`, `memory_limit`, `timeout`,
632
- `cancelled`, `git_missing`, `not_a_git_repository`, `unknown_base`, `unsupported_with_runner`,
633
- `unusable_output` en `phpstan_failed`.
685
+ `reason` comes from a closed set, so you can match on it without reading the text:
686
+ `no_roots_configured`, `project_outside_roots`, `project_not_found`, `path_outside_project`,
687
+ `path_not_found`, `phpstan_missing`, `config_missing`, `bootstrap_failed`,
688
+ `baseline_unreadable`, `invalid_setting`, `memory_limit`, `timeout`, `cancelled`,
689
+ `git_missing`, `not_a_git_repository`, `unknown_base`, `unsupported_with_runner`,
690
+ `unusable_output` and `phpstan_failed`.
634
691
 
635
- De velden staan bewust in `content` en niet in `structuredContent`: dat laatste wordt door
636
- MCP-clients gevalideerd tegen het schema van het *geslaagde* antwoord.
692
+ The fields are deliberately in `content` and not in `structuredContent`: MCP clients validate
693
+ the latter against the schema of the *successful* response.
637
694
 
638
- ## Wat je terugkrijgt
695
+ ## What you get back
639
696
 
640
- Een analyse levert eerst tellingen over álle bevindingen en daarna één pagina met de
641
- bevindingen zelf (standaard 50, hoogstens 500):
697
+ An analysis first returns counts over *all* findings and then one page with the findings
698
+ themselves (50 by default, 500 at most):
642
699
 
643
700
  ```
644
701
  25 bevinding(en) in 11 bestand(en).
@@ -656,43 +713,43 @@ src/Support/Filters/DealFilters.php
656
713
  42 argument.type Parameter #1 $id expects int, string given.
657
714
  ```
658
715
 
659
- Bij meer resultaten eindigt de tekst met de letterlijke vervolgaanroep:
716
+ When there are more results, the text ends with the literal follow-up call:
660
717
 
661
718
  ```
662
719
  Nog 12 bevinding(en). Roep dezelfde tool opnieuw aan met offset=50 en runId="kX3v9aQp"; dan
663
720
  komt de rest uit dit resultaat in plaats van uit een nieuwe analyse.
664
721
  ```
665
722
 
666
- Met die `runId` komt de volgende pagina uit hetzelfde resultaat. Zonder zou elke pagina een
667
- nieuwe analyse zijn, en wie tussendoor een bestand aanpast krijgt dan pagina's met
668
- verschillende totalen. Alleen `offset`, `limit`, `identifiers` en `onlyChangedLines` mogen
669
- anders zijn. Hoort de `runId` bij andere argumenten, is hij verlopen (na tien minuten) of
670
- onbekend, dan volgt een nieuwe analyse met een melding erbij in plaats van een fout.
723
+ With that `runId`, the next page comes from the same result. Without it, every page would be a
724
+ new analysis, and if you change a file in between you get pages with different totals. Only
725
+ `offset`, `limit`, `identifiers` and `onlyChangedLines` may differ. If the `runId` belongs to
726
+ different arguments, has expired (after ten minutes) or is unknown, a new analysis follows
727
+ with a notice instead of an error.
671
728
 
672
- Een pagina uit een eerder resultaat noemt hoe oud dat resultaat is. Analyseer na een
673
- reparatie dus zónder `runId`: anders zie je de uitslag van vóór de reparatie.
729
+ A page from an earlier result states how old that result is. So after a fix, analyse
730
+ *without* `runId`: otherwise you see the result from before the fix.
674
731
 
675
- ### Annuleren
732
+ ### Cancelling
676
733
 
677
- Annuleert je client het verzoek, dan wordt de hele procesgroep van PHPStan opgeruimd in
678
- plaats van dat hij doorrekent voor een antwoord dat niemand meer leest. Een analyse die nog
679
- in de rij stond wordt niet meer gestart.
734
+ If your client cancels the request, PHPStan's whole process group is cleaned up instead of
735
+ it computing on for an answer nobody will read. An analysis still waiting in the queue is not
736
+ started.
680
737
 
681
- ### Als de analyse niet volledig is
738
+ ### When the analysis is incomplete
682
739
 
683
- PHPStan kan geldige JSON teruggeven en er tegelijk op stderr bij zeggen dat hij het niet
684
- heeft afgemaakt. Dat komt bovenaan het antwoord te staan, vóór alles:
740
+ PHPStan can return valid JSON and at the same time say on stderr that it didn't finish. That
741
+ goes at the very top of the response, before anything else:
685
742
 
686
743
  ```
687
744
  LET OP — PHPStan meldde dit naast het resultaat:
688
745
  - Result is incomplete because of severe errors.
689
746
  ```
690
747
 
691
- Dat onderscheid is wezenlijk: zonder die regel is een lege uitslag niet te onderscheiden van
692
- een schone. Staat er een waarschuwing, dan leest het antwoord ook niet meer "Geen
693
- bevindingen" maar "Verder geen bevindingen".
748
+ That distinction is essential: without that line, an empty result can't be told apart from a
749
+ clean one. When there is a warning, the response also no longer reads "Geen bevindingen" (no
750
+ findings) but "Verder geen bevindingen" (no further findings).
694
751
 
695
- Doet er een baseline mee, dan staat dat er ook bij:
752
+ If a baseline is included, that is stated too:
696
753
 
697
754
  ```
698
755
  Ter info:
@@ -700,12 +757,12 @@ Ter info:
700
757
  rapport. Gebruik baseline_summary om te zien wat er in staat.
701
758
  ```
702
759
 
703
- Dat is het verschil tussen "geen bevindingen" en "geen bevindingen, plus duizend die niet
704
- gemeld worden". De check kijkt of je config het bestand echt includeerteen baseline die
705
- naast `phpstan.neon` ligt zonder erin te staan, doet niets.
760
+ That is the difference between "no findings" and "no findings, plus a thousand that aren't
761
+ reported". The check looks at whether your config really includes the filea baseline
762
+ sitting next to `phpstan.neon` without being included does nothing.
706
763
 
707
- PHPStans eigen aanwijzingen bij het lezen van bevindingen staan apart in `instructions` en
708
- worden onder het rapport getoond:
764
+ PHPStan's own guidance for reading findings is kept separately in `instructions` and shown
765
+ below the report:
709
766
 
710
767
  ```
711
768
  PHPStan zegt hierbij:
@@ -713,45 +770,54 @@ PHPStan zegt hierbij:
713
770
  Do not add type casts just to silence errors.
714
771
  ```
715
772
 
716
- Dat blok is elke run hetzelfde en zegt niets over déze analyse, dus het telt niet mee als
717
- waarschuwingen het wordt niet afgekapt, want een half blok aanwijzingen helpt niemand.
773
+ That block is the same every run and says nothing about *this* analysis, so it doesn't count
774
+ as a warning and it isn't truncated, because half a block of guidance helps nobody.
718
775
 
719
- Het `warnings`-veld staat los van `generalErrors`: dat tweede is PHPStans eigen
720
- `errors`-array uit de JSON. Beide lijsten zijn begrensd op tien meldingen van elk hoogstens
721
- 1000 tekens een mislukte Laravel-bootstrap levert anders een lap tekst op die het hele
722
- antwoord vult. Hoeveel er weggelaten is staat in `omitted`, en in de tekst als "nog N niet
723
- getoond".
776
+ The `warnings` field is separate from `generalErrors`: the latter is PHPStan's own `errors`
777
+ array from the JSON. Both lists are capped at ten messages of at most 1000 characters each —
778
+ a failed Laravel bootstrap otherwise produces a wall of text that fills the whole response.
779
+ How many were left out is in `omitted`, and in the text as "nog N niet getoond" (N more not
780
+ shown).
724
781
 
725
- ## Vereisten
782
+ ## Requirements
726
783
 
727
- - Node 20 of nieuwer
728
- - PHPStan geïnstalleerd in het project zelf (`vendor/bin/phpstan`); de server brengt geen
729
- eigen PHPStan mee, want de versie en de extensies horen bij het project
784
+ - Node 20 or newer
785
+ - PHPStan installed in the project itself (`vendor/bin/phpstan`); the server doesn't bring
786
+ its own PHPStan, because the version and the extensions belong to the project
730
787
 
731
- ## Bekende scherpe randen
788
+ ## Known sharp edges
732
789
 
733
- - **De PHP-geheugenlimiet.** PHPStan klapt daar met een groot project makkelijk overheen;
734
- `memoryLimit: "1G"` lost dat meestal op. De crash komt terug als een gewone fout, met
735
- PHPStans eigen uitleg erbij.
736
- - **Larastan start je Laravel-applicatie.** Lukt dat nietontbrekende `.env`, database niet
737
- bereikbaardan faalt de analyse met de uitleg van Larastan zelf.
738
- - **Analyses in hetzelfde project draaien na elkaar,** omdat PHPStans resultcache in het
739
- project ligt. Verschillende projecten draaien wel tegelijk.
790
+ - **The PHP memory limit.** PHPStan easily exceeds it on a large project;
791
+ `memoryLimit: "1G"` usually solves that. The crash comes back as a regular error, with
792
+ PHPStan's own explanation attached.
793
+ - **Larastan boots your Laravel application.** If that failsmissing `.env`, database not
794
+ reachablethe analysis fails with Larastan's own explanation.
795
+ - **Analyses in the same project run one after another,** because they share one result cache
796
+ (per project, in the temp directory not in the project itself). Different projects do run
797
+ in parallel.
798
+ - **A stale result cache** can make PHPStan crash with a message that seems to make no sense.
799
+ After a `composer update` that no longer happens, because the cache directory follows
800
+ `composer.lock`; if it happens some other way anyway, `clear_result_cache` fixes it. The
801
+ error message itself points to it.
802
+ - **If it works in your terminal but not through the client,** the server is running with a
803
+ different environment. The Claude app, for example, starts it without `TMPDIR`, and then the
804
+ per-project cache lives under `/tmp/phpstan-mcp/` instead of under your shell's temp
805
+ directory.
740
806
 
741
- ## Ontwikkelen
807
+ ## Development
742
808
 
743
809
  ```bash
744
810
  npm run build # tsc → dist/
745
- npm run typecheck # inclusief de tests
811
+ npm run typecheck # including the tests
746
812
  npm run lint
747
813
  npm test
748
- npm run inspect # MCP Inspector tegen de gebouwde server
814
+ npm run inspect # MCP Inspector against the built server
749
815
  ```
750
816
 
751
- Dezelfde vier stappen draaien in CI op Node 24, en nog een keer bij een release. `engines`
752
- belooft `>=20`; die ondergrens is bij het opzetten van CI eenmalig in Docker nagelopen en
753
- draait niet mee bij elke build.
817
+ The same four steps run in CI on Node 24, and once more on a release. `engines` promises
818
+ `>=20`; that lower bound was checked once in Docker when CI was set up and does not run with
819
+ every build.
754
820
 
755
- ## Licentie
821
+ ## License
756
822
 
757
823
  MIT