diff --git a/apps/gateway/src/lib/create-request-for-openapi-operation.ts b/apps/gateway/src/lib/create-request-for-openapi-operation.ts index cec05f73..4d47c907 100644 --- a/apps/gateway/src/lib/create-request-for-openapi-operation.ts +++ b/apps/gateway/src/lib/create-request-for-openapi-operation.ts @@ -50,9 +50,7 @@ export async function createRequestForOpenAPIOperation( assert(incomingRequestParamsRaw, 400, 'Invalid empty request body') } - // TODO: Validate incoming request params against the tool's input JSON schema - // TODO: we want to coerce data types to match the schema for booleans, dates, etc - // Currently, these will fail if given as body params, for instance, on the origin server. + // Validate incoming request params against the tool's input JSON schema. const incomingRequestParams = cfValidateJsonSchemaObject({ schema: tool.inputSchema, data: incomingRequestParamsRaw, @@ -95,18 +93,27 @@ export async function createRequestForOpenAPIOperation( if (bodyParams.length > 0) { body = JSON.stringify( Object.fromEntries( - bodyParams.map(([key]) => [key, incomingRequestParams[key]]) + bodyParams + .map(([key]) => [key, incomingRequestParams[key]]) + // Prune undefined values. We know these aren't required fields, + // because the incoming request params have already been validated + // against the tool's input schema. + .filter(([, value]) => value !== undefined) ) ) headers['content-type'] ??= 'application/json' } else if (formDataParams.length > 0) { - body = JSON.stringify( - Object.fromEntries( - formDataParams.map(([key]) => [key, incomingRequestParams[key]]) - ) - ) + // TODO: Double-check FormData usage. + const formData = new FormData() + for (const [key] of formDataParams) { + const value = incomingRequestParams[key] + if (value !== undefined) { + formData.append(key, value) + } + } + body = formData.toString() headers['content-type'] ??= 'application/x-www-form-urlencoded' } diff --git a/packages/openapi-utils/src/get-tools-from-openapi-spec.ts b/packages/openapi-utils/src/get-tools-from-openapi-spec.ts index 8cfda9e5..d5f86b44 100644 --- a/packages/openapi-utils/src/get-tools-from-openapi-spec.ts +++ b/packages/openapi-utils/src/get-tools-from-openapi-spec.ts @@ -20,6 +20,7 @@ import { camelCase, mergeJsonSchemaObjects } from './utils' const jsonContentType = 'application/json' const multipartFormData = 'multipart/form-data' +// const applicationFormUrlEncoded = 'application/x-www-form-urlencoded' const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'trace'] as const const paramSources = ['body', 'formData', 'header', 'path', 'query'] as const @@ -41,20 +42,6 @@ export async function getToolsFromOpenAPISpec( const tools: Tool[] = [] const toolToOperationMap: Record = {} - const requestBodyJsonSchemaPaths = [ - 'requestBody', - 'content', - jsonContentType, - 'schema' - ] - - const requestBodyFormDataJsonSchemaPaths = [ - 'requestBody', - 'content', - multipartFormData, - 'schema' - ] - const operationResponsePaths = [ ['responses', '200', 'content', jsonContentType, 'schema'], ['responses', '201', 'content', jsonContentType, 'schema'] @@ -62,8 +49,10 @@ export async function getToolsFromOpenAPISpec( ] const operationRequestPaths = [ - requestBodyJsonSchemaPaths, - requestBodyFormDataJsonSchemaPaths + ['requestBody', 'content', jsonContentType, 'schema'], + ['requestBody', 'content', multipartFormData, 'schema'] + // TODO: Support application/x-www-form-urlencoded bodies + // ['requestBody', 'content', applicationFormUrlEncoded, 'schema'] ] const operationNames = new Set()