Commit b16b2c5
fix(mcp): keep the MCP chip and refusal message honest about destructive tools
PR #31 review (Copilot). Both findings had one root: a destructiveHint tool can
never be enabled by the allow-list (classifyMcpTool forces 'ask' regardless), yet
two callers spoke as if it could.
1. The startup chip counted allow-listed tools by calling classifyMcpTool WITHOUT
annotations, while runTool calls it WITH them. So a destructive-but-allow-listed
tool was counted as "allow-listed" in the chip and then refused at call time —
the chip lied. Now threads the same route annotations into the count.
2. The refusal message unconditionally told the model to add the tool to
levelcode.ai.mcp.toolPolicy as "allow". For a destructive tool that is
impossible, so the model would allow-list it, retry, and be refused again.
Root-cause fix rather than two patches: classifyMcpTool now also returns
policyCanAllow (false only for the destructive tighten-path), and the refusal
message moves OUT of agent.js into explainMcpRefusal() beside the classifier —
the message that drifted from the policy now branches solely on the verdict, in
the same module, and is unit-testable off the editor (agent.js requires vscode).
agent.js just calls it.
Verified:
- 37 tests (2 new + policyCanAllow assertions on the existing POLICY cases).
- Both fixes mutation-checked: a destructive verdict claiming policyCanAllow:true
fails; a message that ignores the discriminator (the original bug) fails.
- End-to-end against the S2 fixture with 'boom' marked destructive: the exact
chip-count logic returns 0/3 for an allow-listed destructive tool, the message
omits any toolPolicy instruction, and a plain allow-listed tool still counts 1
and would run. The chip-count line itself lives in agent.js and so is covered by
that integration run + reading, not CI — same as the router glue.
- Full gate: 18 suites, 0 failures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1 parent ea02d1a commit b16b2c5
3 files changed
Lines changed: 78 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| |||
457 | 457 | | |
458 | 458 | | |
459 | 459 | | |
460 | | - | |
461 | | - | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
462 | 464 | | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
| 465 | + | |
467 | 466 | | |
468 | 467 | | |
469 | 468 | | |
| |||
543 | 542 | | |
544 | 543 | | |
545 | 544 | | |
546 | | - | |
547 | | - | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
548 | 552 | | |
549 | 553 | | |
550 | 554 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
309 | 309 | | |
310 | 310 | | |
311 | 311 | | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
312 | 318 | | |
313 | 319 | | |
314 | 320 | | |
315 | | - | |
| 321 | + | |
316 | 322 | | |
317 | 323 | | |
318 | 324 | | |
319 | | - | |
| 325 | + | |
320 | 326 | | |
321 | 327 | | |
322 | 328 | | |
323 | | - | |
324 | | - | |
| 329 | + | |
| 330 | + | |
325 | 331 | | |
326 | | - | |
327 | | - | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
328 | 355 | | |
329 | 356 | | |
330 | 357 | | |
331 | | - | |
| 358 | + | |
332 | 359 | | |
333 | 360 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
250 | 250 | | |
251 | 251 | | |
252 | 252 | | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
253 | 265 | | |
254 | 266 | | |
255 | 267 | | |
256 | 268 | | |
257 | 269 | | |
258 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
259 | 290 | | |
260 | 291 | | |
261 | 292 | | |
| |||
0 commit comments