Pre-validate inner tool args at the worker-tool gateway#25
Merged
Conversation
Closes the PR #21 follow-up. Today the gateway only enforces the allowlist; bad args fell through to the inner tool which returned its own ad-hoc error envelope. Workers got inconsistent refusal shapes across rejection reasons (allowlist vs. schema vs. hop budget vs. cost cap) and the inner tool burned code paths handling garbage. The fix: - Look up the inner tool's `inputSchema` from `TOOLS[name]` and run the existing boundary validator `_validate_input` BEFORE dispatch. - On validation failure, return a refusal payload carrying a structured `schema_error` field with the validator message — the worker can read it programmatically and self-correct on the next hop (within the remaining hop budget). - Emit a `worker_inner_validation_fail` ndjson event so operators can see when a worker is repeatedly misusing inner tools. Validation order is: allowlist -> input schema -> session_id injection -> dispatch. session_id is added AFTER the schema check so a missing session_id arg never trips the validator (it's always optional on the inner tools). API change: - `_worker_tools_refusal` grows an optional `schema_error` keyword argument; the payload gains a `schema_error` field when set. - New helper `_worker_tool_input_schema(name)` for the lookup (defensive against module-load order: returns empty dict if TOOLS isn't bound yet). Tests (scripts/test_worker_tool_arg_validation.py): - `fetch` with no `url`: refusal with schema_error mentioning the missing field - `fetch.url` wrong type: refusal mentioning type + field - `verify` missing `checks`: refusal - `verify.checks` wrong type: refusal mentioning array - Unknown extra arg on `fetch` (additionalProperties=false): refusal - VALID `fetch` args dispatch normally with session_id injected - VALID `verify` args dispatch normally - Allowlist refusal still works AND has no `schema_error` field (it short-circuits before the schema check) - End-to-end recovery: worker emits invalid call, gets refusal with schema_error, recovers on next hop with valid args; only the valid call reaches the inner tool Full suite (37 scripts) passes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the PR #21 follow-up. The gateway now validates inner tool args against the tool's `inputSchema` BEFORE dispatching — workers get a clean, structured refusal (with a `schema_error` field) instead of whatever ad-hoc error envelope the inner tool happens to produce.
Validation order: allowlist → input schema → session_id injection → dispatch. session_id is added AFTER the schema check so a missing session_id never trips the validator.
Refusal payload extended: `_worker_tools_refusal(name, reason, hint, *, schema_error=None)`. When the refusal stems from schema validation, the worker sees the validator message in a dedicated `schema_error` field and can self-correct on the next hop within the remaining hop budget.
Telemetry: `worker_inner_validation_fail` ndjson event fires per rejected call so operators can spot workers repeatedly misusing inner tools.
Test plan
🤖 Generated with Claude Code