pull/715/head
Travis Fischer 2025-06-06 19:17:38 +07:00
rodzic 8aca815ccf
commit 6a985e5ccb
5 zmienionych plików z 56 dodań i 21 usunięć

Wyświetl plik

@ -1,10 +1,10 @@
import type { ContentfulStatusCode } from 'hono/utils/http-status'
import { Validator } from '@agentic/json-schema'
import { HttpError } from '@agentic/platform-core'
import { assert, HttpError } from '@agentic/platform-core'
import plur from 'plur'
/**
* Validates `data` against the provided JSON schema object.
* Validates `data` against the provided JSON schema.
*
* This method uses a fork of `@cfworker/json-schema`. It does not use `ajv`
* because `ajv` is not supported on CF workers due to its dynamic code
@ -14,9 +14,7 @@ import plur from 'plur'
* not running on CF workers, consider using `validateJsonSchemaObject` from
* `@agentic/platform-openapi-utils`.
*/
export function cfValidateJsonSchemaObject<
T extends Record<string, any> = Record<string, any>
>({
export function cfValidateJsonSchema<T = unknown>({
schema,
data,
coerce = false,
@ -25,16 +23,29 @@ export function cfValidateJsonSchemaObject<
errorStatusCode = 400
}: {
schema: any
data: Record<string, unknown>
data: unknown
coerce?: boolean
strictAdditionalProperties?: boolean
errorMessage?: string
errorStatusCode?: ContentfulStatusCode
}): T {
// Special-case check for required fields to give better error messages.
if (schema.required && Array.isArray(schema.required)) {
assert(schema, 400, '`schema` is required')
const isSchemaObject =
typeof schema === 'object' &&
!Array.isArray(schema) &&
schema.type === 'object'
const isDataObject = typeof data === 'object' && !Array.isArray(data)
if (isSchemaObject && !isDataObject) {
throw new HttpError({
statusCode: 400,
message: `${errorMessage ? errorMessage + ': ' : ''}Data must be an object according to its schema.`
})
}
// Special-case check for required fields to give better error messages
if (isSchemaObject && Array.isArray(schema.required)) {
const missingRequiredFields: string[] = schema.required.filter(
(field: string) => (data as T)[field] === undefined
(field: string) => (data as Record<string, unknown>)[field] === undefined
)
if (missingRequiredFields.length > 0) {
@ -48,11 +59,12 @@ export function cfValidateJsonSchemaObject<
// Special-case check for additional top-level fields to give better error
// messages.
if (
isSchemaObject &&
schema.properties &&
(schema.additionalProperties === false ||
(schema.additionalProperties === undefined && strictAdditionalProperties))
) {
const extraProperties = Object.keys(data).filter(
const extraProperties = Object.keys(data as Record<string, unknown>).filter(
(key) => !schema.properties[key]
)

Wyświetl plik

@ -2,7 +2,7 @@ import type { AdminDeployment, Tool } from '@agentic/platform-types'
import { assert } from '@agentic/platform-core'
import type { GatewayHonoContext, McpToolCallResponse } from './types'
import { cfValidateJsonSchemaObject } from './cf-validate-json-schema-object'
import { cfValidateJsonSchema } from './cf-validate-json-schema'
export async function createHttpResponseFromMcpToolCallResponse(
_ctx: GatewayHonoContext,
@ -35,7 +35,7 @@ export async function createHttpResponseFromMcpToolCallResponse(
)
// Validate tool response against the tool's output schema.
const toolCallResponseContent = cfValidateJsonSchemaObject({
const toolCallResponseContent = cfValidateJsonSchema({
schema: tool.outputSchema,
data: toolCallResponse.structuredContent as Record<string, unknown>,
coerce: false,

Wyświetl plik

@ -2,7 +2,7 @@ import type { AdminDeployment, Tool } from '@agentic/platform-types'
import { assert } from '@agentic/platform-core'
import type { GatewayHonoContext } from './types'
import { cfValidateJsonSchemaObject } from './cf-validate-json-schema-object'
import { cfValidateJsonSchema } from './cf-validate-json-schema'
export async function getToolArgsFromRequest(
ctx: GatewayHonoContext,
@ -48,7 +48,7 @@ export async function getToolArgsFromRequest(
}
// Validate incoming request params against the tool's input schema.
const incomingRequestArgs = cfValidateJsonSchemaObject({
const incomingRequestArgs = cfValidateJsonSchema<Record<string, any>>({
schema: tool.inputSchema,
data: incomingRequestArgsRaw,
errorMessage: `Invalid request parameters for tool "${tool.name}"`,

Wyświetl plik

@ -22,6 +22,18 @@ server.addTool({
}
})
server.addTool({
name: 'add2',
description: 'TODO',
parameters: z.object({
a: z.number(),
b: z.number()
}),
execute: async (args) => {
return String(args.a + args.b)
}
})
await server.start({
transportType: 'httpStream',
httpStream: {

Wyświetl plik

@ -1,4 +1,4 @@
import { hashObject, HttpError } from '@agentic/platform-core'
import { assert, hashObject, HttpError } from '@agentic/platform-core'
import { betterAjvErrors } from '@apideck/better-ajv-errors'
import Ajv, { type ValidateFunction } from 'ajv'
import addFormats from 'ajv-formats'
@ -19,7 +19,7 @@ const globalAjv = new Ajv({
addFormats(globalAjv)
/**
* Validates `data` against the provided JSON schema object.
* Validates `data` against the provided JSON schema.
*
* This method uses `ajv` and is therefore not compatible with CF workers due
* to its use of code generation and evaluation.
@ -29,9 +29,7 @@ addFormats(globalAjv)
*
* @see https://github.com/ajv-validator/ajv/issues/2318
*/
export function validateJsonSchemaObject<
T extends Record<string, unknown> = Record<string, unknown>
>({
export function validateJsonSchema<T = unknown>({
schema,
data,
ajv = globalAjv,
@ -42,10 +40,23 @@ export function validateJsonSchemaObject<
ajv?: Ajv
errorMessage?: string
}): T {
assert(schema, 400, '`schema` is required')
const isSchemaObject =
typeof schema === 'object' &&
!Array.isArray(schema) &&
schema.type === 'object'
const isDataObject = typeof data === 'object' && !Array.isArray(data)
if (isSchemaObject && !isDataObject) {
throw new HttpError({
statusCode: 400,
message: `${errorMessage ? errorMessage + ': ' : ''}Data must be an object according to its schema.`
})
}
// Special-case check for required fields to give better error messages
if (Array.isArray(schema.required)) {
if (isSchemaObject && Array.isArray(schema.required)) {
const missingRequiredFields: string[] = schema.required.filter(
(field: string) => (data as T)[field] === undefined
(field: string) => (data as Record<string, unknown>)[field] === undefined
)
if (missingRequiredFields.length > 0) {