|
| 1 | +// Copyright (c) .NET Foundation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +import { McpToolProperty, McpToolTriggerOptions, McpToolTriggerOptionsToRpc } from '../../types'; |
| 5 | + |
| 6 | +// Copyright (c) .NET Foundation. All rights reserved. |
| 7 | +// Licensed under the MIT License. |
| 8 | + |
| 9 | +/** |
| 10 | + * Converts an McpToolTriggerOptions object to an McpToolTriggerOptionsToRpc object. |
| 11 | + * |
| 12 | + * @param mcpToolTriggerOptions - The input options to be converted. |
| 13 | + * @returns The converted McpToolTriggerOptionsToRpc object. |
| 14 | + */ |
| 15 | +export function converToMcpToolTriggerOptionsToRpc( |
| 16 | + mcpToolTriggerOptions: McpToolTriggerOptions |
| 17 | +): McpToolTriggerOptionsToRpc { |
| 18 | + // Base object for the return value |
| 19 | + const baseResult = { |
| 20 | + toolName: mcpToolTriggerOptions.toolName, |
| 21 | + description: mcpToolTriggerOptions.description, |
| 22 | + }; |
| 23 | + |
| 24 | + // Check for null or undefined toolProperties |
| 25 | + if (!mcpToolTriggerOptions?.toolProperties) { |
| 26 | + return { |
| 27 | + ...baseResult, |
| 28 | + toolProperties: JSON.stringify([]), // Default to an empty array |
| 29 | + }; |
| 30 | + } |
| 31 | + |
| 32 | + // Check if toolProperties is an array of McpToolProperty objects |
| 33 | + if (Array.isArray(mcpToolTriggerOptions.toolProperties)) { |
| 34 | + const isValid = mcpToolTriggerOptions.toolProperties.every(isMcpToolProperty); |
| 35 | + if (isValid) { |
| 36 | + return { |
| 37 | + ...baseResult, |
| 38 | + toolProperties: JSON.stringify(mcpToolTriggerOptions.toolProperties), |
| 39 | + }; |
| 40 | + } else { |
| 41 | + throw new Error( |
| 42 | + 'Invalid toolProperties: Array contains invalid McpToolProperty, please validate the parameters.' |
| 43 | + ); |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + // Handle cases where toolProperties is an object (e.g., Zod schema) |
| 48 | + if (typeof mcpToolTriggerOptions.toolProperties === 'object') { |
| 49 | + // Define the type of the ZodObject shape and ZodPropertyDef |
| 50 | + type ZodPropertyDef = { |
| 51 | + description?: string; |
| 52 | + typeName: string; |
| 53 | + }; |
| 54 | + type ZodObjectShape = Record<string, { _def: ZodPropertyDef }>; |
| 55 | + |
| 56 | + // Define the type of the toolProperties object |
| 57 | + type ToolProperties = |
| 58 | + | { |
| 59 | + _def?: { |
| 60 | + typeName?: string; |
| 61 | + }; |
| 62 | + shape?: ZodObjectShape; |
| 63 | + } |
| 64 | + | Record<string, unknown>; |
| 65 | + |
| 66 | + let isZodObject = false; |
| 67 | + |
| 68 | + const toolProperties = mcpToolTriggerOptions.toolProperties as ToolProperties; |
| 69 | + |
| 70 | + // Check if the object is a ZodObject |
| 71 | + if ((toolProperties?._def as { typeName?: string })?.typeName === 'ZodObject') { |
| 72 | + isZodObject = true; |
| 73 | + } |
| 74 | + |
| 75 | + // Check if shape is a valid ZodObject shape |
| 76 | + const shape: ZodObjectShape | Record<string, unknown> = isZodObject |
| 77 | + ? (toolProperties as { shape: ZodObjectShape }).shape |
| 78 | + : toolProperties; |
| 79 | + |
| 80 | + // Extract properties from the ZodObject shape |
| 81 | + const result = Object.keys(shape).map((propertyName) => { |
| 82 | + const property = shape[propertyName] as { _def: ZodPropertyDef }; |
| 83 | + const description = property?._def?.description || ''; |
| 84 | + const propertyType = getPropertyType(property?._def?.typeName?.toLowerCase() || 'unknown'); // Extract type name or default to "unknown" |
| 85 | + |
| 86 | + return { |
| 87 | + propertyName, |
| 88 | + propertyType, |
| 89 | + description, |
| 90 | + }; |
| 91 | + }); |
| 92 | + |
| 93 | + return { |
| 94 | + ...baseResult, |
| 95 | + toolProperties: JSON.stringify(result), |
| 96 | + }; |
| 97 | + } |
| 98 | + // Handle cases where toolProperties is not an array |
| 99 | + throw new Error('Invalid toolProperties: Expected an array of McpToolProperty objects or zod objects.'); |
| 100 | +} |
| 101 | + |
| 102 | +// Helper function to infer property type from zod schema |
| 103 | +function getPropertyType(zodType: string): string { |
| 104 | + switch (zodType) { |
| 105 | + case 'zodnumber': |
| 106 | + return 'number'; |
| 107 | + case 'zodstring': |
| 108 | + return 'string'; |
| 109 | + case 'zodboolean': |
| 110 | + return 'boolean'; |
| 111 | + case 'zodarray': |
| 112 | + return 'array'; |
| 113 | + case 'zodobject': |
| 114 | + return 'object'; |
| 115 | + case 'zodbigint': |
| 116 | + return 'long'; |
| 117 | + case 'zoddate': |
| 118 | + return 'DateTime'; |
| 119 | + case 'zodtuple': |
| 120 | + return 'Tuple'; |
| 121 | + default: |
| 122 | + console.warn(`Unknown zod type: ${zodType}`); |
| 123 | + return 'unknown'; |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +/** |
| 128 | + * Type guard to check if a given object is of type McpToolProperty. |
| 129 | + * |
| 130 | + * @param property - The object to check. |
| 131 | + * @returns True if the object is of type McpToolProperty, otherwise false. |
| 132 | + * |
| 133 | + * This function ensures that the object: |
| 134 | + * - Is not null and is of type 'object'. |
| 135 | + * - Contains the required properties: 'propertyName', 'propertyValue', and 'description'. |
| 136 | + * - Each of these properties is of the correct type (string). |
| 137 | + */ |
| 138 | +function isMcpToolProperty(property: unknown): property is McpToolProperty { |
| 139 | + return ( |
| 140 | + typeof property === 'object' && |
| 141 | + property !== null && |
| 142 | + 'propertyName' in property && |
| 143 | + 'propertyType' in property && |
| 144 | + 'description' in property && |
| 145 | + typeof (property as McpToolProperty).propertyName === 'string' && |
| 146 | + typeof (property as McpToolProperty).propertyType === 'string' && |
| 147 | + typeof (property as McpToolProperty).description === 'string' |
| 148 | + ); |
| 149 | +} |
0 commit comments