Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
545 changes: 318 additions & 227 deletions package-lock.json

Large diffs are not rendered by default.

137 changes: 21 additions & 116 deletions src/converters/toMcpToolTriggerOptionsToRpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

import { McpToolProperty, McpToolTriggerOptions, McpToolTriggerOptionsToRpc } from '../../types';
import { normalizeToolProperties } from '../utils/toolProperties';

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License.
Expand All @@ -21,129 +22,33 @@ export function converToMcpToolTriggerOptionsToRpc(
description: mcpToolTriggerOptions.description,
};

// Check for null or undefined toolProperties
if (!mcpToolTriggerOptions?.toolProperties) {
return {
...baseResult,
toolProperties: JSON.stringify([]), // Default to an empty array
};
}

// Check if toolProperties is an array of McpToolProperty objects
if (Array.isArray(mcpToolTriggerOptions.toolProperties)) {
const isValid = mcpToolTriggerOptions.toolProperties.every(isMcpToolProperty);
if (isValid) {
return {
...baseResult,
toolProperties: JSON.stringify(mcpToolTriggerOptions.toolProperties),
};
} else {
throw new Error(
'Invalid toolProperties: Array contains invalid McpToolProperty, please validate the parameters.'
);
// Try to normalize tool properties first (handles both array and fluent formats)
let normalizedProperties: McpToolProperty[] | undefined;
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
normalizedProperties = normalizeToolProperties(mcpToolTriggerOptions.toolProperties);
} catch (error) {
// Re-throw validation errors from normalizeToolProperties
if (
error instanceof Error &&
(error.message.includes('Property type is required') ||
error.message.includes('Property type must be specified'))
) {
throw error;
}
normalizedProperties = undefined;
}

// Handle cases where toolProperties is an object (e.g., Zod schema)
if (typeof mcpToolTriggerOptions.toolProperties === 'object') {
// Define the type of the ZodObject shape and ZodPropertyDef
type ZodPropertyDef = {
description?: string;
typeName: string;
};
type ZodObjectShape = Record<string, { _def: ZodPropertyDef }>;

// Define the type of the toolProperties object
type ToolProperties =
| {
_def?: {
typeName?: string;
};
shape?: ZodObjectShape;
}
| Record<string, unknown>;

let isZodObject = false;

const toolProperties = mcpToolTriggerOptions.toolProperties as ToolProperties;

// Check if the object is a ZodObject
if ((toolProperties?._def as { typeName?: string })?.typeName === 'ZodObject') {
isZodObject = true;
}

// Check if shape is a valid ZodObject shape
const shape: ZodObjectShape | Record<string, unknown> = isZodObject
? (toolProperties as { shape: ZodObjectShape }).shape
: toolProperties;

// Extract properties from the ZodObject shape
const result = Object.keys(shape).map((propertyName) => {
const property = shape[propertyName] as { _def: ZodPropertyDef };
const description = property?._def?.description || '';
const propertyType = getPropertyType(property?._def?.typeName?.toLowerCase() || 'unknown'); // Extract type name or default to "unknown"

return {
propertyName,
propertyType,
description,
};
});

// If we successfully normalized the properties, use them
if (normalizedProperties !== undefined) {
return {
...baseResult,
toolProperties: JSON.stringify(result),
toolProperties: JSON.stringify(normalizedProperties),
};
}
// Handle cases where toolProperties is not an array
throw new Error('Invalid toolProperties: Expected an array of McpToolProperty objects or zod objects.');
}

// Helper function to infer property type from zod schema
function getPropertyType(zodType: string): string {
switch (zodType) {
case 'zodnumber':
return 'number';
case 'zodstring':
return 'string';
case 'zodboolean':
return 'boolean';
case 'zodarray':
return 'array';
case 'zodobject':
return 'object';
case 'zodbigint':
return 'long';
case 'zoddate':
return 'DateTime';
case 'zodtuple':
return 'Tuple';
default:
console.warn(`Unknown zod type: ${zodType}`);
return 'unknown';
}
}

/**
* Type guard to check if a given object is of type McpToolProperty.
*
* @param property - The object to check.
* @returns True if the object is of type McpToolProperty, otherwise false.
*
* This function ensures that the object:
* - Is not null and is of type 'object'.
* - Contains the required properties: 'propertyName', 'propertyValue', and 'description'.
* - Each of these properties is of the correct type (string).
*/
function isMcpToolProperty(property: unknown): property is McpToolProperty {
return (
typeof property === 'object' &&
property !== null &&
'propertyName' in property &&
'propertyType' in property &&
'description' in property &&
typeof (property as McpToolProperty).propertyName === 'string' &&
typeof (property as McpToolProperty).propertyType === 'string' &&
typeof (property as McpToolProperty).description === 'string'
// Handle cases where toolProperties is not an array
throw new Error(
`Invalid toolProperties for tool '${mcpToolTriggerOptions.toolName}': Expected an array of McpToolProperty or ToolProps objects or ToolProps need a type defined.`
);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { InvocationContext } from './InvocationContext';
export * as output from './output';
export * as trigger from './trigger';
export { Disposable } from './utils/Disposable';
export { arg } from './utils/toolProperties';

export enum SqlChangeOperation {
Insert = 0,
Expand Down
Loading
Loading