Repository navigation
Conversation
params and only constrain what the model writes, but nothing constrained what it reads back. A published record can carry far more than an agent needs: a helpdesk ticket may list its custom fields twice, under two names. Each extra part is something the model may misread or quote, and the only way around it was a Python tool. omit names dotted paths to drop, by operationId, stepping through every array on the way. The where_empty form keeps an array and drops only the elements whose key holds nothing, because the fields that do hold a value are often what a lookup needs. false and 0 count as values. validate refuses only a path the success responses rule out: one through a declared scalar, or past an object that allows no other properties. Published response schemas commonly leave out fields the service sends, so an undeclared name proves nothing; refusing it would reject the very paths omit exists for. Dropping runs inside the tool, so the redaction callback still sees, and masks, whatever is left. 🤖 Generated with Claude Code
haruotsu
force-pushed
the
openapi-omit
branch
from
November 10, 2026 07:18
3f9a161 to
74a9035
Compare
OpenAPI 3.1 writes a nullable value as a list of types, such as ["object", "null"]. The omit check looked the type up in a set, so a path through such a value stopped validate with a TypeError, and where_empty on a nullable array was refused as if it named no array at all. A null holds no properties, so whenever there is a value, it is what the one other type says; that type is what the check now reads. A list of several other types is left unchosen, as oneOf is. The messages also put "an" before a type that starts with a vowel. 🤖 Generated with Claude Code
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.
Why
paramsandonlyconstrain what the model writes to an operation, but nothing constrained what it reads back. A published record can carry far more than an agent needs. Zendesk's published Support API description is a public example: a ticket (ShowTicket) carries its custom field values twice, ascustom_fieldsand asfields, which the description calls an alias. Every extra part of a result is something the model may misread or quote, and the only way to keep it out was to replace the block with a Python tool.What changes
omiton anopenapi:block, keyed by operationId likeparams,onlyanddescribe. Each entry is a dotted path, or an object withpathandwhere_empty:Runtime. The tool drops the paths from the JSON result before returning it. Every array on the way is stepped through element by element, so
results.fieldsdrops the key from each search result. A result that does not carry the path (an error body, a record in another shape, a 204) is left as it came; nothing raises.where_emptykeeps the array atpathand drops only the elements whose key holds nothing:null,"",[],{}, or no such key at all.falseand0are values and stay, since they are meaningful answers in general; an element that is not an object has no key to judge by and stays too. The fields that do hold a value are often exactly what a lookup needs, so dropping the whole array is not enough.validate refuses an
omitentry for an operation the block does not select, as it does forparams. It also holds each path against the operation's 2xx JSON response schemas, but only refuses what they rule out for certain: a path through a value declared as a scalar, a name an object withadditionalProperties: falsedoes not declare,where_emptyon a value declared as something other than an array, orwhere_emptyon an array whose elements are declared as scalars. A name the schema merely leaves undeclared passes: published response schemas routinely under-declare. In the same public Zendesk description,SearchResultObjectdeclares nine shared properties for results it says may be tickets, users, groups, or organizations, so refusing undeclared names would reject the very pathsomitexists for. With several success responses, a path is refused only when all of them rule it out;$refand one level ofallOfare followed, andoneOf/anyOfare never chosen between. An OpenAPI 3.1 type list such as["object", "null"]is read as its one non-null type; a list of several is not chosen between either.Redaction is unchanged. Dropping runs inside the tool and can only remove keys or array elements, never rename or add anything; the
after_tool_callbackstill redacts whatever is left, as for every other tool.Write blocks may declare
omitas well; nothing about it is specific to reads.Schema.
omitmaps operation ids to non-empty, unique lists. A path is dot-separated with no empty segment. The object form requires bothpathandwhere_empty, andwhere_emptyis a single key (no dots).README documents
omit, what counts as empty, and whatvalidatedoes and does not refuse.Not part of this: replacing field ids in a result with their names. That would take another request per result; an instruction can carry the mapping.
Compatibility
Additive. Declarations without
omitbuild the same tools. An agent that usesomithas to be archived with a gete that includes this change, since the runtime inside the archive is what applies it.Testing
null, a scalar where an object was expected, a top-level list, plain text) come back unchanged;where_emptydropsnull,"",[],{}and missing keys while keepingfalse,0, values and non-object elements; awhere_emptypath that holds no array is left alone;omiton one operation leaves another's result whole; through a built agent, theafter_tool_callbackstill redacts a key the omitted result carries.omitfor an unselected operation; an undeclared name passes; a path through a declared scalar (reached through$ref), past a closed object, or below an array of scalars is refused; arrays are followed throughitems;where_emptyon a declared non-array or on an array of scalars is refused; no JSON success response, or a second 2xx response that may hold the path, lets it pass; an array that is its ownitems(a YAML alias can build one) ends the walk instead of looping; a nullable 3.1 type list is followed as its other type, and a list of several types rules nothing out.where_empty, a dottedwhere_emptyand an unknown key are refused.pytest,ruff check,ruff format --checkandmypypass locally.