Skip to content

Commit 5b32cd9

Browse files
authored
Merge pull request #1871 from vvoland/fix-notion-openai
Fix `mcp/notion` not working with OpenAI models
2 parents 8c26e5b + 75ae90f commit 5b32cd9

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

pkg/model/provider/openai/schema.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func ConvertParametersToSchema(params any) (shared.FunctionParameters, error) {
2020
}
2121

2222
// walkSchema calls fn on the given schema node, then recursively walks into
23-
// properties, anyOf/oneOf/allOf variants, and array items.
23+
// properties, anyOf/oneOf/allOf variants, array items, and additionalProperties.
2424
func walkSchema(schema map[string]any, fn func(map[string]any)) {
2525
fn(schema)
2626

@@ -45,6 +45,11 @@ func walkSchema(schema map[string]any, fn func(map[string]any)) {
4545
if items, ok := schema["items"].(map[string]any); ok {
4646
walkSchema(items, fn)
4747
}
48+
49+
// additionalProperties can be a boolean or an object schema
50+
if additionalProps, ok := schema["additionalProperties"].(map[string]any); ok {
51+
walkSchema(additionalProps, fn)
52+
}
4853
}
4954

5055
// makeAllRequired makes all object properties "required" throughout the schema,

pkg/model/provider/openai/schema_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,48 @@ func TestMakeAllRequired_ArrayItems(t *testing.T) {
167167
assert.Contains(t, itemRequired, "name")
168168
}
169169

170+
func TestMakeAllRequired_AdditionalProperties(t *testing.T) {
171+
// Reproduces the Notion MCP tool schema where additionalProperties
172+
// contains an object schema with its own properties (like bulleted_list_item).
173+
// OpenAI requires all properties in additionalProperties schemas to also
174+
// be listed in the required array.
175+
schema := shared.FunctionParameters{
176+
"type": "object",
177+
"properties": map[string]any{
178+
"children": map[string]any{
179+
"type": "object",
180+
"additionalProperties": map[string]any{
181+
"type": "object",
182+
"properties": map[string]any{
183+
"bulleted_list_item": map[string]any{"type": "string"},
184+
"numbered_list_item": map[string]any{"type": "string"},
185+
},
186+
"required": []any{"bulleted_list_item"},
187+
},
188+
},
189+
},
190+
"required": []any{"children"},
191+
}
192+
193+
updated := makeAllRequired(schema)
194+
195+
// additionalProperties object: all properties must be required
196+
children := updated["properties"].(map[string]any)["children"].(map[string]any)
197+
additionalProps := children["additionalProperties"].(map[string]any)
198+
additionalRequired := additionalProps["required"].([]any)
199+
assert.Len(t, additionalRequired, 2)
200+
assert.Contains(t, additionalRequired, "bulleted_list_item")
201+
assert.Contains(t, additionalRequired, "numbered_list_item")
202+
203+
// numbered_list_item was not originally required, so its type should be nullable
204+
numberedListItem := additionalProps["properties"].(map[string]any)["numbered_list_item"].(map[string]any)
205+
assert.Equal(t, []string{"string", "null"}, numberedListItem["type"])
206+
207+
// bulleted_list_item was originally required, so its type should be unchanged
208+
bulletedListItem := additionalProps["properties"].(map[string]any)["bulleted_list_item"].(map[string]any)
209+
assert.Equal(t, "string", bulletedListItem["type"])
210+
}
211+
170212
func TestRemoveFormatFields(t *testing.T) {
171213
schema := map[string]any{
172214
"type": "object",

0 commit comments

Comments
 (0)