Skip to content

Commit da67ae5

Browse files
committed
Add schema notes for avoiding propertyNames
1 parent 681cbf5 commit da67ae5

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

SCHEMA_NOTES.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Tool Schema Notes
2+
3+
## Avoiding `propertyNames` in JSON Schema
4+
5+
### Problem
6+
7+
When defining tool arguments that accept arbitrary object maps (e.g., `Record<string, object>`), using `z.record()` generates a `propertyNames` constraint in the JSON Schema:
8+
9+
```typescript
10+
// This generates propertyNames
11+
distillation: tool.schema.record(tool.schema.string(), tool.schema.any())
12+
```
13+
14+
Produces:
15+
16+
```json
17+
{
18+
"type": "object",
19+
"propertyNames": { "type": "string" },
20+
"additionalProperties": {}
21+
}
22+
```
23+
24+
The `propertyNames` keyword is not supported by some LLM APIs (Claude, Gemini via Antigravity), causing tool schema validation failures.
25+
26+
### Solution
27+
28+
Use `z.object({}).catchall()` instead of `z.record()`:
29+
30+
```typescript
31+
// This does NOT generate propertyNames
32+
distillation: tool.schema.object({}).catchall(tool.schema.object({}).loose())
33+
```
34+
35+
Produces:
36+
37+
```json
38+
{
39+
"type": "object",
40+
"properties": {},
41+
"additionalProperties": {
42+
"type": "object",
43+
"properties": {},
44+
"additionalProperties": {}
45+
}
46+
}
47+
```
48+
49+
### Pattern Reference
50+
51+
| Use Case | Syntax |
52+
| -------------------------------- | --------------------------------------------------- |
53+
| Any keys, any values | `z.object({}).loose()` |
54+
| Any keys, object values (loose) | `z.object({}).catchall(z.object({}).loose())` |
55+
| Any keys, object values (strict) | `z.object({}).catchall(z.object({ ... }))` |
56+
| Known keys + unknown extras | `z.object({ known: z.string() }).catchall(z.any())` |
57+
58+
### Notes
59+
60+
- `z.record()` always adds `propertyNames` - avoid it for API compatibility
61+
- `.loose()` and `.passthrough()` produce identical JSON Schema output
62+
- `.catchall(valueSchema)` constrains the value type without adding `propertyNames`
63+
- JSON keys are inherently strings, so `propertyNames: { type: "string" }` is redundant anyway

0 commit comments

Comments
 (0)