mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 04:07:05 +00:00
🧩 fix: Normalize Malformed MCP Required Schemas (#14771)
This commit is contained in:
parent
8f1f43f33e
commit
861cfe8a3c
2 changed files with 70 additions and 0 deletions
|
|
@ -2121,6 +2121,50 @@ describe('normalizeJsonSchema', () => {
|
|||
expect(result.additionalProperties).toEqual({ type: 'string', enum: ['val'] });
|
||||
});
|
||||
|
||||
it('should drop malformed required keywords recursively', () => {
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
required: { type: 'boolean' },
|
||||
config: {
|
||||
type: 'object',
|
||||
properties: { host: { type: 'string' } },
|
||||
required: { host: true },
|
||||
},
|
||||
},
|
||||
oneOf: [{ type: 'object', required: 'name' }],
|
||||
items: { type: 'object', required: null },
|
||||
required: {},
|
||||
};
|
||||
|
||||
expect(normalizeJsonSchema(schema)).toEqual({
|
||||
type: 'object',
|
||||
properties: {
|
||||
required: { type: 'boolean' },
|
||||
config: {
|
||||
type: 'object',
|
||||
properties: { host: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
oneOf: [{ type: 'object' }],
|
||||
items: { type: 'object' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should normalize required arrays to unique property names', () => {
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
age: { type: 'number' },
|
||||
},
|
||||
required: ['name', 42, null, 'age', 'name', { key: 'value' }],
|
||||
};
|
||||
|
||||
expect(normalizeJsonSchema(schema).required).toEqual(['name', 'age']);
|
||||
expect(normalizeJsonSchema({ type: 'object', required: [] }).required).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle null, undefined, and primitive inputs safely', () => {
|
||||
expect(normalizeJsonSchema(null as any)).toBeNull();
|
||||
expect(normalizeJsonSchema(undefined as any)).toBeUndefined();
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ export function resolveJsonSchemaRefs<T extends Record<string, unknown>>(
|
|||
* - Strips vendor extension fields (`x-*` prefixed keys, e.g. `x-google-enum-descriptions`)
|
||||
* - Strips `definitions` and `$`-prefixed schema keywords (`$defs`, `$schema`,
|
||||
* `$id`, `$comment`, ...) that may survive ref resolution
|
||||
* - Drops malformed `required` values and filters arrays to property names
|
||||
*
|
||||
* Beyond LLM compatibility, dropping every `$`-prefixed keyword also makes the
|
||||
* output safe to persist: MongoDB rejects field names beginning with `$`, so a
|
||||
|
|
@ -375,6 +376,23 @@ const SCHEMA_MAP_KEYWORDS = new Set([
|
|||
/** Keywords whose value is an array of subschemas. */
|
||||
const SCHEMA_LIST_KEYWORDS = new Set(['oneOf', 'anyOf', 'allOf', 'prefixItems']);
|
||||
|
||||
function normalizeRequired(value: unknown): string[] | undefined {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const required: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const entry of value) {
|
||||
if (typeof entry !== 'string' || seen.has(entry)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(entry);
|
||||
required.push(entry);
|
||||
}
|
||||
return required;
|
||||
}
|
||||
|
||||
export function normalizeJsonSchema<T extends Record<string, unknown>>(schema: T): T {
|
||||
if (!schema || typeof schema !== 'object') {
|
||||
return schema;
|
||||
|
|
@ -416,6 +434,14 @@ export function normalizeJsonSchema<T extends Record<string, unknown>>(schema: T
|
|||
continue;
|
||||
}
|
||||
|
||||
if (key === 'required') {
|
||||
const required = normalizeRequired(value);
|
||||
if (required) {
|
||||
result[key] = required;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
SCHEMA_MAP_KEYWORDS.has(key) &&
|
||||
value &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue