fix: list only the called action's arguments on an unknown argument #859

Merged
hexajon merged 2 commits from issue-756-scoped-valid-args into main 2026-10-21 03:25:34 +00:00
Owner

Summary

What was wrong

When a call to an action-dispatched tool carried an unknown argument, the rejection listed every argument the tool accepts across all its actions. For madt_repos that is about seventy names. For example, madt_repos action=search query=madtea rejected query and then listed the arguments of create, merge, mirror, and the other actions. Most of those names do not apply to search.

What changed

  • internal/mcp/registry.go: new method Registry.actionScopedFieldsFor. It reads action (and op) from the raw call arguments. It returns the sorted arguments that this action accepts, plus those of the selected op. It uses opspec.AcceptedParamSet and keeps only names that the tool's input struct records. It lists op only when the action is op-dispatched. It looks in both the consolidated tool slot and the addTool slot, so tools such as madt_worktrees are covered. It reports ok=false when the call has no action, the tool has no action manifest, or the action is not one the tool has.
  • internal/mcp/register.go: installValidationEnrichment now passes the scoped list to formatUnknownArgs. When actionScopedFieldsFor reports ok=false, it falls back to reg.knownFieldsFor, the full field set.
  • The did-you-mean matcher still reads the full field set, so no suggestion changes. This change narrows only the listed fallback.

Tests

All tests are in internal/mcp/arg_scope_test.go and send real calls over an in-memory MCP transport.

  • TestUnknownArgValidList_ScopedToCalledAction: madt_repos action=search with query lists exactly the 18 arguments that search accepts.
  • TestUnknownArgValidList_ScopedToSelectedOp: madt_issues action=body op=append lists op and content, and omits replacements, title, and labels.
  • TestUnknownArgValidList_ScopedOnAddToolManifest: madt_worktrees action=rebase lists exactly action, dir, onto, path, raw_json.
  • TestUnknownArgValidList_FullSetWithoutKnownAction: a call with no action, and a call with an unknown action, both list the full set.
  • TestUnknownArgValidList_ToolWithoutActionsKeepsFullSet: madt_commit still lists its full set.
  • TestUnknownArgValidList_SuggestionUnchanged: a typo of an argument that another action takes (default_brnch) still suggests default_branch, while the search list does not carry it.

Refs #756

./scripts/gate.sh passes on this commit.

Closes #756

## Summary ## What was wrong When a call to an action-dispatched tool carried an unknown argument, the rejection listed every argument the tool accepts across all its actions. For `madt_repos` that is about seventy names. For example, `madt_repos action=search query=madtea` rejected `query` and then listed the arguments of create, merge, mirror, and the other actions. Most of those names do not apply to `search`. ## What changed - `internal/mcp/registry.go`: new method `Registry.actionScopedFieldsFor`. It reads `action` (and `op`) from the raw call arguments. It returns the sorted arguments that this action accepts, plus those of the selected op. It uses `opspec.AcceptedParamSet` and keeps only names that the tool's input struct records. It lists `op` only when the action is op-dispatched. It looks in both the consolidated tool slot and the `addTool` slot, so tools such as `madt_worktrees` are covered. It reports `ok=false` when the call has no action, the tool has no action manifest, or the action is not one the tool has. - `internal/mcp/register.go`: `installValidationEnrichment` now passes the scoped list to `formatUnknownArgs`. When `actionScopedFieldsFor` reports `ok=false`, it falls back to `reg.knownFieldsFor`, the full field set. - The did-you-mean matcher still reads the full field set, so no suggestion changes. This change narrows only the listed fallback. ## Tests All tests are in `internal/mcp/arg_scope_test.go` and send real calls over an in-memory MCP transport. - `TestUnknownArgValidList_ScopedToCalledAction`: `madt_repos action=search` with `query` lists exactly the 18 arguments that `search` accepts. - `TestUnknownArgValidList_ScopedToSelectedOp`: `madt_issues action=body op=append` lists `op` and `content`, and omits `replacements`, `title`, and `labels`. - `TestUnknownArgValidList_ScopedOnAddToolManifest`: `madt_worktrees action=rebase` lists exactly `action`, `dir`, `onto`, `path`, `raw_json`. - `TestUnknownArgValidList_FullSetWithoutKnownAction`: a call with no action, and a call with an unknown action, both list the full set. - `TestUnknownArgValidList_ToolWithoutActionsKeepsFullSet`: `madt_commit` still lists its full set. - `TestUnknownArgValidList_SuggestionUnchanged`: a typo of an argument that another action takes (`default_brnch`) still suggests `default_branch`, while the `search` list does not carry it. Refs #756 `./scripts/gate.sh` passes on this commit. Closes #756
When a call to an action-dispatched tool names a known action, the
unknown-argument rejection now lists the arguments that action accepts
(and the selected op's, for an op-dispatched action), not the tool's
whole field set. A call with no action, or an action the tool does not
have, still lists the full set. The did-you-mean matcher still reads the
full field set, so no suggestion changes.

Refs #756
test: use strings.Cut in the valid-argument parser, as go fix asks
All checks were successful
drift / govulncheck (pull_request) Has been skipped
ci / gate (pull_request) Successful in 41m42s
drift / forgejo-smoke (pull_request) Has been skipped
drift / govulncheck (push) Has been skipped
drift / surface-diff (push) Successful in -30s
drift / forgejo-smoke (push) Has been skipped
ci / gate (push) Successful in -16s
drift / surface-diff (pull_request) Successful in 48s
29ce6bf8b8
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea!859
No description provided.