Skip to content

Commit e87623c

Browse files
authored
Merge pull request #895 from cliffhall/fix-default-null-array-input
Handle nullable arrays in tool inputSchemas
2 parents 40045b3 + 54484e7 commit e87623c

File tree

4 files changed

+28
-15
lines changed

4 files changed

+28
-15
lines changed

client/src/components/DynamicJsonForm.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
113113
setJsonError(errorMessage);
114114

115115
// Reset to default for clearly invalid JSON (not just incomplete typing)
116-
const trimmed = jsonString.trim();
117-
if (trimmed.length > 5 && !trimmed.match(/^[\s[{]/)) {
116+
const trimmed = jsonString?.trim();
117+
if (trimmed && trimmed.length > 5 && !trimmed.match(/^[\s[{]/)) {
118118
onChange(generateDefaultValue(schema));
119119
}
120120
}
@@ -155,7 +155,7 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
155155

156156
const formatJson = () => {
157157
try {
158-
const jsonStr = rawJsonValue.trim();
158+
const jsonStr = rawJsonValue?.trim();
159159
if (!jsonStr) {
160160
return;
161161
}
@@ -171,7 +171,7 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
171171
const validateJson = () => {
172172
if (!isJsonMode) return { isValid: true, error: null };
173173
try {
174-
const jsonStr = rawJsonValue.trim();
174+
const jsonStr = rawJsonValue?.trim();
175175
if (!jsonStr) return { isValid: true, error: null };
176176
const parsed = JSON.parse(jsonStr);
177177
// Clear any pending debounced update and immediately update parent

client/src/components/ToolsTab.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,18 @@ const ToolsTab = ({
194194
...params,
195195
[key]: checked
196196
? null
197-
: prop.default !== null
198-
? prop.default
199-
: prop.type === "boolean"
200-
? false
201-
: prop.type === "string"
202-
? ""
203-
: prop.type === "number" ||
204-
prop.type === "integer"
205-
? undefined
206-
: undefined,
197+
: prop.type === "array"
198+
? undefined
199+
: prop.default !== null
200+
? prop.default
201+
: prop.type === "boolean"
202+
? false
203+
: prop.type === "string"
204+
? ""
205+
: prop.type === "number" ||
206+
prop.type === "integer"
207+
? undefined
208+
: undefined,
207209
})
208210
}
209211
/>

client/src/utils/jsonUtils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ export function tryParseJson(str: string): {
8585
success: boolean;
8686
data: JsonValue;
8787
} {
88-
const trimmed = str.trim();
88+
const trimmed = str?.trim();
8989
if (
90+
trimmed &&
9091
!(trimmed.startsWith("{") && trimmed.endsWith("}")) &&
9192
!(trimmed.startsWith("[") && trimmed.endsWith("]"))
9293
) {

client/src/utils/schemaUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType {
235235
return { ...schema, type: "integer", anyOf: undefined, nullable: true };
236236
}
237237

238+
// Handle anyOf with exactly array and null (FastMCP pattern)
239+
if (
240+
schema.anyOf &&
241+
schema.anyOf.length === 2 &&
242+
schema.anyOf.some((t) => (t as JsonSchemaType).type === "array") &&
243+
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
244+
) {
245+
return { ...schema, type: "array", anyOf: undefined, nullable: true };
246+
}
247+
238248
// Handle array type with exactly string and null
239249
if (
240250
Array.isArray(schema.type) &&

0 commit comments

Comments
 (0)