make-runner-mcp 2.1.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/LICENSE +21 -0
- package/MAKEFILE-GUIDE.md +359 -0
- package/README.md +482 -0
- package/package.json +30 -0
- package/server.js +1027 -0
- package/skills/fix-makefile-links/SKILL.md +169 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Davinder S. Mahal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Adapting a project's Makefile for make-runner-mcp
|
|
2
|
+
|
|
3
|
+
**Audience: an AI coding agent**, of any capability level. Give this file
|
|
4
|
+
to an agent (attach it, paste it, or point at its path) with an instruction
|
|
5
|
+
like: *"Follow MAKEFILE-GUIDE.md exactly, in order, to make this project's
|
|
6
|
+
Makefile(s) compatible with make-runner-mcp."* The agent should be working
|
|
7
|
+
inside the target project's own repo — this file is not about
|
|
8
|
+
make-runner-mcp's own code.
|
|
9
|
+
|
|
10
|
+
This guide is written to be followed literally, step by step, with
|
|
11
|
+
copy-pasteable commands, rather than inferred from general Make knowledge.
|
|
12
|
+
**If a step's outcome is ambiguous, or you are not sure what to do, stop and
|
|
13
|
+
say so instead of guessing.** Every step below tells you exactly what to run
|
|
14
|
+
and what "stop and ask a human" looks like for that step.
|
|
15
|
+
|
|
16
|
+
## 0. The rule, stated once, precisely
|
|
17
|
+
|
|
18
|
+
An AI agent calling a target through make-runner-mcp may pass:
|
|
19
|
+
|
|
20
|
+
| Allowed | Example | Not allowed | Why |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| A flag from this exact list: `-n --dry-run --just-print --recon -B --always-make -k --keep-going -s --silent --quiet -i --ignore-errors -q --question` | `-n` | Any other flag, e.g. `-C`, `-f`, `-j`, `--eval` | Only these are allowlisted; everything else is rejected |
|
|
23
|
+
| `VAR=value`, where `value` matches `^[A-Za-z0-9 _.,+/@:^~=-]*$` | `ARGS=install symfony/console --no-dev` | `VAR=value` where value contains `` ; & | $ ` ' " < > ( ) { } # \ * ? [ ] `` or a newline | Those characters could break out of the recipe line make later runs through a real shell |
|
|
24
|
+
| `VAR=value` for any variable name **except**: `SHELL MAKE MAKEFLAGS MAKEFILES MAKELEVEL MAKECMDGOALS VPATH GPATH PATH` | `ARGS=...`, `ENV=staging` | `SHELL=...`, `PATH=...`, etc. | These control how make itself executes, not what a target does |
|
|
25
|
+
| — | — | Any bare word with no `VAR=` in front of it, e.g. `install`, `--no-dev`, `symfony/console` on its own | make would treat it as an *extra build goal*, which could run a second, unvetted target |
|
|
26
|
+
|
|
27
|
+
Everything in this guide exists to get a project's Makefile to fit inside
|
|
28
|
+
this table without changing what `make <target>` does for a human typing it
|
|
29
|
+
directly.
|
|
30
|
+
|
|
31
|
+
## 1. Find every file that defines targets
|
|
32
|
+
|
|
33
|
+
Run, from the project root:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
test -f Makefile && echo "root Makefile: OK" || echo "STOP: no root Makefile — make-runner-mcp requires <project root>/Makefile to exist"
|
|
37
|
+
grep -n '^\s*\(-\|s\)\?include\s' Makefile
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The second command lists any `include`/`-include`/`sinclude` lines in the
|
|
41
|
+
root Makefile. Open every file they point to, and repeat the same `grep`
|
|
42
|
+
on each of those (includes can nest). Write down the full list of files —
|
|
43
|
+
you'll re-check all of them in the steps below. If any include target uses
|
|
44
|
+
a `$(...)` variable in its path (e.g. `include $(ENV).mk`), make-runner-mcp
|
|
45
|
+
cannot resolve it either — note this file as **not scanned**, and flag it
|
|
46
|
+
in your final report (§8).
|
|
47
|
+
|
|
48
|
+
make-runner-mcp also follows the catch-all forwarding idiom automatically
|
|
49
|
+
— a bare `%:` rule whose recipe runs `$(MAKE) -C <dir> ...` — so targets
|
|
50
|
+
only reachable that way don't need anything from this guide either; see
|
|
51
|
+
§2's note on it.
|
|
52
|
+
|
|
53
|
+
### 1a. When neither `include` nor forwarding actually connects the files
|
|
54
|
+
|
|
55
|
+
Two cases §1 flags as unresolvable — an include path built from a `$(VAR)`,
|
|
56
|
+
or a second Makefile that's genuinely a separate, independently-invoked
|
|
57
|
+
Makefile with no real `include`/forwarding link to the root one at all —
|
|
58
|
+
mean make-runner-mcp can't discover that file's targets by parsing, because
|
|
59
|
+
real `make -f Makefile <target>` wouldn't find them either. For exactly
|
|
60
|
+
this situation (not as a substitute for a real `include` you could add
|
|
61
|
+
instead — prefer that when it's an option, since it keeps the linked file's
|
|
62
|
+
variables in scope), add a plain comment to the file make-runner-mcp does
|
|
63
|
+
parse:
|
|
64
|
+
|
|
65
|
+
```makefile
|
|
66
|
+
## make-runner: also-read docker/Makefile
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This is a hint to make-runner-mcp only — it has no effect on what `make`
|
|
70
|
+
itself does with this file, so a human running `make docker-stuff` by hand
|
|
71
|
+
sees no change in behavior. One or more space-separated paths are allowed
|
|
72
|
+
on the same line, each resolved relative to the file the comment is in
|
|
73
|
+
(same resolution rule as `include`). A target found this way is executed
|
|
74
|
+
directly against that other file (`make -f docker/Makefile <target>`, run
|
|
75
|
+
from `docker/`'s own directory) rather than through the root Makefile — so
|
|
76
|
+
it will **not** see variables the root Makefile sets for it (unlike a real
|
|
77
|
+
`include`), and its own tool description in the MCP tool list will say
|
|
78
|
+
`via docker/Makefile` so a calling agent knows it's a separate file. If a
|
|
79
|
+
target's recipe actually depends on a variable only the root Makefile
|
|
80
|
+
defines, this marker isn't the right fix — restructure so that variable is
|
|
81
|
+
set inside the linked file itself, or use a real `include` instead.
|
|
82
|
+
|
|
83
|
+
## 2. Detect the incompatible pattern
|
|
84
|
+
|
|
85
|
+
Run this across the root Makefile and every included file you found in
|
|
86
|
+
step 1 (repeat per file, or point `grep -r` at the whole project if all
|
|
87
|
+
your make files live under one directory):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
grep -n "MAKECMDGOALS" Makefile
|
|
91
|
+
grep -n 'filter-out \$@' Makefile
|
|
92
|
+
grep -n "wordlist" Makefile
|
|
93
|
+
grep -n "^%:" Makefile
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Any hit means at least one target relies on bare-word passthrough (the
|
|
97
|
+
`$(MAKECMDGOALS)` / catch-all-rule pattern) — this is what §0's "no bare
|
|
98
|
+
words" rule breaks. For each hit, find which real target's recipe consumes
|
|
99
|
+
it (read a few lines above/below the match) and add that target's name to
|
|
100
|
+
a list: **targets to convert**.
|
|
101
|
+
|
|
102
|
+
A `^%:` hit whose recipe contains `$(MAKE)` with a `-C` flag (forwarding
|
|
103
|
+
any undefined goal to another directory's Makefile, e.g. `%:` →
|
|
104
|
+
`@$(MAKE) -C docker $@`) is a *different*, already-supported idiom —
|
|
105
|
+
make-runner-mcp discovers and exposes those forwarded targets
|
|
106
|
+
automatically. Do not add it to **targets to convert**, and skip §4's
|
|
107
|
+
"remove the dead catch-all rule" for it. Only a `%:` rule that swallows
|
|
108
|
+
bare-word `$(MAKECMDGOALS)` arguments (typically paired with an `@:`
|
|
109
|
+
no-op recipe, no `$(MAKE) -C ...` call) is the incompatible pattern this
|
|
110
|
+
guide's §3–4 converts or removes.
|
|
111
|
+
|
|
112
|
+
If none of the four commands return anything, skip to §6 — there's nothing
|
|
113
|
+
to convert, only the checks in §5–§7 still apply.
|
|
114
|
+
|
|
115
|
+
## 3. Convert each target on your list
|
|
116
|
+
|
|
117
|
+
For **each** target name in "targets to convert," do exactly this:
|
|
118
|
+
|
|
119
|
+
1. Open the file it's defined in.
|
|
120
|
+
2. Find its recipe (the indented line(s) under `target:`).
|
|
121
|
+
3. Replace every use of `$(MAKECMDGOALS)`, `$(filter-out $@,$(MAKECMDGOALS))`,
|
|
122
|
+
or similar with a single variable named `ARGS`.
|
|
123
|
+
4. Rewrite (or write, if missing) its `##` comment to say, verbatim style:
|
|
124
|
+
`## <what it does>. Pass args via ARGS, e.g. ARGS="<realistic example>"`
|
|
125
|
+
|
|
126
|
+
Template — replace the bracketed parts:
|
|
127
|
+
|
|
128
|
+
```makefile
|
|
129
|
+
# BEFORE
|
|
130
|
+
[TARGET]: ## [old comment, if any]
|
|
131
|
+
[COMMAND] $(filter-out $@,$(MAKECMDGOALS))
|
|
132
|
+
|
|
133
|
+
# AFTER
|
|
134
|
+
[TARGET]: ## [what it does]. Pass args via ARGS, e.g. ARGS="[realistic example value]"
|
|
135
|
+
[COMMAND] $(ARGS)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Concrete worked example:
|
|
139
|
+
|
|
140
|
+
```makefile
|
|
141
|
+
# BEFORE
|
|
142
|
+
composer: ## Run composer
|
|
143
|
+
docker compose exec app composer $(filter-out $@,$(MAKECMDGOALS))
|
|
144
|
+
|
|
145
|
+
# AFTER
|
|
146
|
+
composer: ## Run composer inside the app container. Pass args via ARGS, e.g. ARGS="install symfony/console --no-dev"
|
|
147
|
+
docker compose exec app composer $(ARGS)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Do not rename the target itself. Do not change what command it runs
|
|
151
|
+
(`docker compose exec app composer ...` stays the same) — only how it
|
|
152
|
+
receives its arguments changes.
|
|
153
|
+
|
|
154
|
+
## 4. Remove the dead catch-all rule, if it's now unused
|
|
155
|
+
|
|
156
|
+
After converting every target on your list, run step 2's commands again:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
grep -n "MAKECMDGOALS" Makefile
|
|
160
|
+
grep -n "^%:" Makefile
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
If `^%:` still has a match and nothing else references `MAKECMDGOALS`
|
|
164
|
+
anymore, that catch-all rule (and its `@:` no-op recipe, and any
|
|
165
|
+
`.PHONY` entry naming it) is now dead code — delete it. If `MAKECMDGOALS`
|
|
166
|
+
still has matches, another target still depends on it: go back to §3 and
|
|
167
|
+
convert that one too, then re-run this check.
|
|
168
|
+
|
|
169
|
+
## 5. Check every target name against the hard-coded denylist
|
|
170
|
+
|
|
171
|
+
make-runner-mcp always blocks target names containing `deploy`, `destroy`,
|
|
172
|
+
`prod`, `publish`, or `release` as a substring (checked after removing any
|
|
173
|
+
`-`, `_`, `.` characters), plus anything starting with `rm-`. Run this to
|
|
174
|
+
find every target name and flag likely false positives:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
grep -hoE '^[A-Za-z0-9][A-Za-z0-9_.-]*:($|[^=])' Makefile | sed 's/:.*//' | sort -u | while read -r t; do
|
|
178
|
+
norm=$(echo "$t" | tr -d '_.-' | tr '[:upper:]' '[:lower:]')
|
|
179
|
+
case "$norm" in
|
|
180
|
+
*deploy*|*destroy*|*prod*|*publish*|*release*)
|
|
181
|
+
echo "CHECK: '$t' will be blocked (matches denylist word)" ;;
|
|
182
|
+
esac
|
|
183
|
+
case "$t" in
|
|
184
|
+
rm-*) echo "CHECK: '$t' will be blocked (starts with rm-)" ;;
|
|
185
|
+
esac
|
|
186
|
+
done
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
(The `:($|[^=])` — colon followed by end-of-line or a non-`=` character — is
|
|
190
|
+
deliberate, not decoration: it's what keeps a variable assignment like
|
|
191
|
+
`RELEASE_TAG := v1.0.0` from being misread as a target named `RELEASE_TAG`
|
|
192
|
+
and wrongly flagged here. A plain `name:` pattern would catch that false
|
|
193
|
+
positive whenever there's no space before `:=`.)
|
|
194
|
+
|
|
195
|
+
For every line this prints: **do not rename the target yourself.** Decide
|
|
196
|
+
only whether it's a correct block (a real deploy/destroy/publish/release
|
|
197
|
+
target — leave it, this is working as intended) or a false positive (an
|
|
198
|
+
unrelated target that happens to contain the substring, e.g.
|
|
199
|
+
`producer-report`, `reproduce-fixture`). List every false positive in your
|
|
200
|
+
final report (§8) as `NEEDS HUMAN DECISION` — a human decides whether to
|
|
201
|
+
rename it.
|
|
202
|
+
|
|
203
|
+
## 6. Check for reliance on reserved variables
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
grep -nE '\$\((SHELL|MAKE|MAKEFLAGS|MAKEFILES|MAKELEVEL|MAKECMDGOALS|VPATH|GPATH|PATH)\)' Makefile
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A match by itself is not necessarily a problem — Makefiles routinely use
|
|
210
|
+
`$(MAKE)` for recursive calls or `$(SHELL)` internally. It's only a problem
|
|
211
|
+
if a target's **documentation, comment, or README instructions tell a
|
|
212
|
+
caller to override one of these on the command line** (e.g. "run `make
|
|
213
|
+
build SHELL=/bin/bash`"). If you find that, list it in your final report as
|
|
214
|
+
`NEEDS HUMAN DECISION` — this pattern cannot be supported through
|
|
215
|
+
make-runner-mcp and the target needs to be restructured so callers don't
|
|
216
|
+
need to override that variable.
|
|
217
|
+
|
|
218
|
+
## 7. Backfill missing or oversized descriptions
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
grep -nE '^[A-Za-z0-9][A-Za-z0-9_.-]*:($|[^=])' Makefile | grep -v '##'
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Every line this prints is a target with no `##` comment — it will show up
|
|
225
|
+
to an AI agent as a generic `Run 'make <name>'` description instead of a
|
|
226
|
+
useful one. Add one: `target: ## <one-sentence description>`. Keep each
|
|
227
|
+
comment under 200 characters (longer ones get truncated) and don't put
|
|
228
|
+
secrets, internal hostnames, or anything sensitive in it — the comment is
|
|
229
|
+
sent to every calling agent on every tool listing, whether or not the
|
|
230
|
+
target is ever invoked.
|
|
231
|
+
|
|
232
|
+
## 8. Validate your own `ARGS` example values
|
|
233
|
+
|
|
234
|
+
For every `ARGS="..."` example you wrote into a comment in §3, check the
|
|
235
|
+
example text itself against this pattern (letters, digits, spaces, and
|
|
236
|
+
`` _ . , + / @ : ^ ~ = - `` only):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
python3 -c "import re,sys; s=sys.argv[1]; print('OK' if re.fullmatch(r'[A-Za-z0-9 _.,+/@:^~=-]*', s) else 'INVALID')" 'install symfony/console --no-dev'
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
(Swap in each example value you wrote.) If it prints `INVALID`, your
|
|
243
|
+
example itself wouldn't work through make-runner-mcp — pick a different,
|
|
244
|
+
representative example, or note in your final report that this target's
|
|
245
|
+
real-world arguments don't fit the safe character set and needs a human
|
|
246
|
+
decision (do not try to work around this — see §9).
|
|
247
|
+
|
|
248
|
+
## 9. Test your changes
|
|
249
|
+
|
|
250
|
+
**Always do this — it needs nothing but `make` itself:**
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
make -n [target] ARGS="[the example value from its comment]"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`-n` is a dry run: it prints the command make *would* run without running
|
|
257
|
+
it. Confirm the printed line has your `ARGS` value substituted in the
|
|
258
|
+
right place, and that the target isn't silently doing nothing. Do this for
|
|
259
|
+
every target you touched.
|
|
260
|
+
|
|
261
|
+
**If make-runner-mcp is available in this environment** (i.e. you can `cd`
|
|
262
|
+
into its repo and run Node), do this deeper check too — it exercises the
|
|
263
|
+
actual server, not just `make` directly:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
cd /path/to/make-runner-mcp
|
|
267
|
+
PROJECT_DIR=/path/to/the/project node -e '
|
|
268
|
+
const { spawn } = require("child_process");
|
|
269
|
+
// MCP_TRANSPORT=stdio is required here: make-runner-mcp defaults to the
|
|
270
|
+
// http transport (for sandboxed-agent use, see its own README), and this
|
|
271
|
+
// probe talks to it over stdin/stdout, not a network port.
|
|
272
|
+
const child = spawn("node", ["server.js"], {
|
|
273
|
+
env: { ...process.env, PROJECT_DIR: process.env.PROJECT_DIR, MCP_TRANSPORT: "stdio" },
|
|
274
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
275
|
+
});
|
|
276
|
+
let buf = "";
|
|
277
|
+
child.stdout.on("data", (d) => {
|
|
278
|
+
buf += d.toString();
|
|
279
|
+
let i;
|
|
280
|
+
while ((i = buf.indexOf("\n")) >= 0) {
|
|
281
|
+
const line = buf.slice(0, i);
|
|
282
|
+
buf = buf.slice(i + 1);
|
|
283
|
+
if (line.trim()) console.log(line);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
function send(o) { child.stdin.write(JSON.stringify(o) + "\n"); }
|
|
287
|
+
send({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "t", version: "1" } } });
|
|
288
|
+
setTimeout(() => send({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }), 300);
|
|
289
|
+
// Replace TOOL_NAME and the ARGS example with the target you are testing:
|
|
290
|
+
setTimeout(() => send({ jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "make__TOOL_NAME", arguments: { args: ["ARGS=install symfony/console --no-dev"] } } }), 700);
|
|
291
|
+
setTimeout(() => { child.kill(); process.exit(0); }, 1200);
|
|
292
|
+
'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Check the `tools/list` response includes the target you converted, with
|
|
296
|
+
the description you wrote, and the `tools/call` response shows the
|
|
297
|
+
expected output with no `isError: true`.
|
|
298
|
+
|
|
299
|
+
If neither `make` nor Node/make-runner-mcp is available in your
|
|
300
|
+
environment at all, say so explicitly in your final report rather than
|
|
301
|
+
claiming you tested something you didn't run.
|
|
302
|
+
|
|
303
|
+
## 10. Final report — always produce this
|
|
304
|
+
|
|
305
|
+
End your work with a plain-text summary in this exact shape:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
CONVERTED (MAKECMDGOALS -> ARGS):
|
|
309
|
+
- <target>: <one line: what changed>
|
|
310
|
+
- ...
|
|
311
|
+
|
|
312
|
+
REMOVED:
|
|
313
|
+
- <catch-all rule / dead code removed, or "none">
|
|
314
|
+
|
|
315
|
+
DESCRIPTIONS ADDED/UPDATED:
|
|
316
|
+
- <target>: <new comment text>
|
|
317
|
+
- ...
|
|
318
|
+
|
|
319
|
+
NEEDS HUMAN DECISION:
|
|
320
|
+
- <target>: <exact reason — denylist false positive / reserved-variable dependency / args don't fit the safe character set / include path uses a variable / etc.>
|
|
321
|
+
- ...
|
|
322
|
+
|
|
323
|
+
TESTED:
|
|
324
|
+
- <target>: <"dry-run only" | "dry-run + inspector" | "not tested — reason">
|
|
325
|
+
- ...
|
|
326
|
+
|
|
327
|
+
NOT TOUCHED (and why):
|
|
328
|
+
- <target>: <e.g. "already uses ARGS-style args, no change needed">
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Do not omit sections — write "none" where nothing applies. This is what a
|
|
332
|
+
human reviews to decide what to actually merge.
|
|
333
|
+
|
|
334
|
+
## 11. Guardrails — do not do these, under any circumstance
|
|
335
|
+
|
|
336
|
+
- **Don't rename or restructure a target to dodge the hard-coded
|
|
337
|
+
denylist** (`deploy`, `destroy`, `prod`, `publish`, `release`, `rm-*`).
|
|
338
|
+
If it's genuinely one of those operations, leave it blocked — that's the
|
|
339
|
+
system working correctly. Only §5's narrow false-positive case goes in
|
|
340
|
+
your report, and even then a human decides, not you.
|
|
341
|
+
- **Don't try to route around the `VAR=value` character restriction** —
|
|
342
|
+
no base64/hex-encoding a value and decoding it in the recipe, no string
|
|
343
|
+
concatenation tricks, no alternate delimiters. If a target's real
|
|
344
|
+
arguments don't fit the safe character set (§8), report it as
|
|
345
|
+
`NEEDS HUMAN DECISION`. Do not invent a workaround.
|
|
346
|
+
- **Don't edit `.mcp-make-config.json` as a substitute for fixing a
|
|
347
|
+
target.** That file only narrows which targets are exposed
|
|
348
|
+
(`deny`/`allow`) and which env vars reach `make` (`envAllowlist`) — it
|
|
349
|
+
has no effect on argument handling and won't make an incompatible target
|
|
350
|
+
work.
|
|
351
|
+
- **Don't edit make-runner-mcp's own `server.js`** from inside a target
|
|
352
|
+
project's repo to widen the allowed flags or reserved-variable list. If
|
|
353
|
+
a project genuinely needs something currently blocked, that's a
|
|
354
|
+
deliberate, separate change to make-runner-mcp itself — report it, don't
|
|
355
|
+
make it yourself.
|
|
356
|
+
- **Don't mark something as tested if you didn't actually run a command
|
|
357
|
+
and see its output.** If you couldn't test (missing tooling, no
|
|
358
|
+
container running, etc.), say so plainly in §10 rather than guessing
|
|
359
|
+
that it probably works.
|