fix: validate MCP tool arguments (#2123) (#2128)

This commit is contained in:
Carlos Florêncio
2026-08-01 18:18:08 +01:00
committed by GitHub
parent 5be05726b9
commit 1bccadf8f9
17 changed files with 921 additions and 22 deletions
@@ -0,0 +1,59 @@
---
status: shipped
created: 2026-08-01
owner: kandev
---
# MCP Tool Argument Validation
Decision: [ADR-2026-08-01-validate-mcp-tool-arguments](../../decisions/2026-08-01-validate-mcp-tool-arguments.md)
## Why
Agents and external MCP clients need a failed tool call to be distinguishable from a successful operation. Silently ignoring an incorrect argument can create tasks, start agents, or mutate configuration without the caller's intended data while still reporting success.
## What
- Every Kandev MCP tool validates its arguments against the schema registered for its current MCP mode before running its handler.
- Missing required arguments, values of the wrong declared type, violated declared constraints, and unknown top-level arguments return a tool error and cause no handler or backend side effect.
- Parameterless tools accept omitted arguments or an empty object and reject any supplied field.
- Validation follows mode changes: after the server changes its registered tool set, calls use the replacement tools' schemas.
- Nested configuration objects remain open when their schema intentionally permits arbitrary keys.
- `create_task_kandev` advertises `prompt` for the text delivered to a newly started agent. It accepts the former `description` name as an unadvertised compatibility alias, without adding a second field or explanation to the tool schema. A call containing both names fails rather than choosing one silently.
- The backend task action continues receiving the text in its existing `description` field; this naming convergence changes only the MCP boundary.
## API surface
The advertised MCP `tools/list` schemas do not gain repeated unknown-argument metadata or the legacy `create_task_kandev.description` compatibility alias. The server applies these rules when handling `tools/call`:
1. Normalize an explicitly supported compatibility alias, if any.
2. Validate the resulting arguments against the currently registered tool schema with the root object closed to unknown fields.
3. Invoke the handler only when validation succeeds.
Validation failures are returned as MCP tool error results. They identify the invalid argument location and violated constraint without returning sensitive argument values.
## Failure modes
- If tool arguments fail validation, Kandev returns an error and does not invoke the handler.
- If a registered tool schema cannot compile, calls to that tool fail closed and Kandev logs the schema defect; test coverage prevents shipping an uncompilable built-in schema.
- If both `prompt` and compatibility alias `description` are supplied to `create_task_kandev`, Kandev returns an error and creates no task.
- Validation errors do not echo complete prompts, credentials, configuration values, or other argument contents.
## Scenarios
- **GIVEN** any registered Kandev MCP tool, **WHEN** a caller omits one of its schema-required arguments, **THEN** the call returns a tool error and its handler is not invoked.
- **GIVEN** any registered Kandev MCP tool, **WHEN** a caller supplies a value with the wrong schema type, **THEN** the call returns a tool error and its handler is not invoked.
- **GIVEN** any registered Kandev MCP tool, **WHEN** a caller supplies an unknown top-level argument, **THEN** the call returns a tool error naming its location and its handler is not invoked.
- **GIVEN** a parameterless tool, **WHEN** a caller omits arguments or supplies `{}`, **THEN** the handler runs normally; **WHEN** it supplies any field, **THEN** the call fails.
- **GIVEN** a tool with an intentionally open nested configuration map, **WHEN** a caller supplies an arbitrary key inside that map, **THEN** validation accepts the nested key.
- **GIVEN** the server changes MCP mode, **WHEN** a caller invokes a tool in the replacement set, **THEN** validation uses that tool's replacement schema.
- **GIVEN** a caller supplies advertised `prompt` to `create_task_kandev`, **WHEN** validation runs, **THEN** the prompt is forwarded unchanged through the existing backend `description` field.
- **GIVEN** a caller supplies only legacy `description` to `create_task_kandev`, **WHEN** validation runs, **THEN** the value is accepted as `prompt` and forwarded unchanged.
- **GIVEN** a caller supplies both `prompt` and `description` to `create_task_kandev`, **WHEN** validation runs, **THEN** the call returns an error and creates no task.
## Out of scope
- Advertising the legacy `description` compatibility alias alongside `prompt`.
- Changing the semantic business rules enforced inside individual handlers and services.
- Closing arbitrary-key nested configuration maps that are intentionally open.
- Changing MCP transport, authorization, tool availability by mode, or backend action payloads.