An OpenAI function-calling definition is a thin wrapper around a JSON Schema. The wrapper carries the name and description; the schema sits inside a parameters field and describes the arguments.
The name is an identifier rather than prose. It must be a valid function-style name, and it is what appears in the model response when a call is made, so it should be descriptive enough to distinguish between several tools. The description is prose, and it is genuinely important: with several tools available, the description is what the model uses to decide which one applies.
The parameters field holds a standard JSON Schema object. One detail catches people out: the $schema keyword does not belong here. The API expects the schema body, not a standalone schema document, and including $schema is a common cause of a rejected definition.
Keep argument descriptions specific. A parameter described as "the query" gives the model very little; "the search query, as the user phrased it, without added keywords" gives it something to follow. The same principle that applies to structured output applies here, because it is the same mechanism.
Strict mode, where available, enforces the schema rather than treating it as a suggestion, and it imposes its own requirements — additionalProperties must be false, and every property must be listed as required. If you intend to use it, design the schema for it from the start rather than retrofitting.
The practical workflow is to build and validate the schema first, confirm it describes the arguments you want, and only then wrap it in the provider format. A schema that is wrong will be wrong in either wrapper.