Skip to content

Commit daf37d1

Browse files
committed
fix: key Ajv compile cache on stable raw schema, drop dead dialect code
1 parent e221dd4 commit daf37d1

1 file changed

Lines changed: 19 additions & 27 deletions

File tree

apps/desktop/src/main/runtime-host-native-capabilities.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ import {
3636
type ClientCapabilityServiceOffer,
3737
} from "@maka/runtime-host/protocol";
3838
import { validateTypes } from '@ai-sdk/provider-utils';
39-
import Ajv, { type AnySchema, type ValidateFunction } from 'ajv';
40-
import Ajv2019 from 'ajv/dist/2019.js';
41-
import Ajv2020 from 'ajv/dist/2020.js';
39+
import Ajv2020, { type AnySchema, type ValidateFunction } from 'ajv/dist/2020.js';
4240
import { toJSONSchema, z } from "zod";
4341
import { withBrowserOriginAdmission } from './browser/browser-origin-admission.js';
4442
import type { DesktopTargetScope } from '../shared/runtime-host-identity.js';
@@ -489,35 +487,30 @@ interface JsonSchemaWrapper {
489487
}
490488

491489
// -- JSON Schema argument validation -------------------------------------------
490+
//
491+
// Validation runs against the projected (advertised) schema, not the raw
492+
// MCP schema, so any constraint expressed via non-whitelisted keywords
493+
// (if/then/else, contains, dependentRequired, …) is dropped before Ajv
494+
// sees it. The downstream MCP server re-validates against the full schema.
492495

493496
const jsonSchemaValidatorOptions = {
494497
allErrors: true,
495498
strict: false,
496499
validateFormats: false,
497500
} as const;
498-
const draft7Validator = new Ajv(jsonSchemaValidatorOptions);
499-
const draft2019Validator = new Ajv2019(jsonSchemaValidatorOptions);
500-
const draft2020Validator = new Ajv2020(jsonSchemaValidatorOptions);
501+
const schemaValidator = new Ajv2020(jsonSchemaValidatorOptions);
501502
const compiledSchemas = new WeakMap<object, ValidateFunction>();
502503

503-
function compileJsonSchema(schema: unknown): ValidateFunction | undefined {
504-
if (typeof schema === 'boolean') return draft2020Validator.compile(schema);
505-
if (typeof schema !== 'object' || schema === null || Array.isArray(schema))
506-
return undefined;
507-
const cached = compiledSchemas.get(schema);
504+
function compileJsonSchema(
505+
rawSchema: object,
506+
projected: Record<string, unknown>,
507+
): ValidateFunction | undefined {
508+
const cached = compiledSchemas.get(rawSchema);
508509
if (cached) return cached;
509-
const declaredDialect = (
510-
schema as { readonly $schema?: unknown }
511-
).$schema;
512-
const dialect =
513-
typeof declaredDialect === 'string' ? declaredDialect : '';
514-
const validator = dialect.includes('draft-07')
515-
? draft7Validator
516-
: dialect.includes('2019-09')
517-
? draft2019Validator
518-
: draft2020Validator;
519-
const compiled = validator.compile(schema as AnySchema);
520-
compiledSchemas.set(schema, compiled);
510+
if (typeof projected !== 'object' || projected === null || Array.isArray(projected))
511+
return undefined;
512+
const compiled = schemaValidator.compile(projected as AnySchema);
513+
compiledSchemas.set(rawSchema, compiled);
521514
return compiled;
522515
}
523516

@@ -540,10 +533,9 @@ async function parseNativeToolArguments(
540533
// jsonSchema() wrappers: compile the projected schema and validate.
541534
const wrapper = parameters as JsonSchemaWrapper | undefined;
542535
if (wrapper?.jsonSchema) {
543-
const projected = projectToolInputSchema(
544-
wrapper.jsonSchema as Record<string, unknown>,
545-
);
546-
const validator = compileJsonSchema(projected);
536+
const raw = wrapper.jsonSchema as Record<string, unknown>;
537+
const projected = projectToolInputSchema(raw);
538+
const validator = compileJsonSchema(raw, projected);
547539
if (!validator || validator(args)) return args;
548540
throw new Error(
549541
`Invalid arguments: ${schemaErrorSummary(validator.errors)}`,

0 commit comments

Comments
 (0)