Skip to content

Type error in convertToolProperties with exactOptionalPropertyTypes: true #389

@jonasschultheiss

Description

@jonasschultheiss

Bug Description

When using TypeScript with exactOptionalPropertyTypes: true (a strict compiler option), the @azure/functions package fails to compile due to a type incompatibility in src/utils/toolProperties.ts.

Error Message

node_modules/@azure/functions/src/utils/toolProperties.ts:226:5 - error TS2322: Type '{ propertyName: string; propertyType: string; description: string; isRequired: boolean | undefined; isArray: boolean | undefined; }[]' is not assignable to type 'McpToolProperty[]'.
  Type '{ propertyName: string; propertyType: string; description: string; isRequired: boolean | undefined; isArray: boolean | undefined; }' is not assignable to type 'McpToolProperty' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
    Types of property 'isRequired' are incompatible.
      Type 'boolean | undefined' is not assignable to type 'boolean'.
        Type 'undefined' is not assignable to type 'boolean'.

226     return Object.entries(args).map(([propertyName, property]) => ({
        ~~~~~~

Environment

  • @azure/functions version: 4.7.2 (also tested with 4.9.0)
  • TypeScript version: 5.9.3
  • Compiler option: exactOptionalPropertyTypes: true

Root Cause

In src/utils/toolProperties.ts at line 226-232, the convertToolProperties function creates objects with properties that may be undefined:

return Object.entries(args).map(([propertyName, property]) => ({
    propertyName,
    propertyType: property.propertyType,
    description: property.description || '',
    isRequired: property.isRequired,  // Type: boolean | undefined
    isArray: property.isArray,        // Type: boolean | undefined
}));

However, the target interface McpToolProperty (in types/mcpTool.d.ts) defines these as optional properties:

export interface McpToolProperty {
    propertyName: string;
    propertyType: string;
    description?: string;
    isRequired?: boolean;
    isArray?: boolean;
}

With exactOptionalPropertyTypes: true, TypeScript distinguishes between:

  • isRequired?: boolean → property can be omitted, but when present must be boolean
  • isRequired: boolean | undefined → property must be present, but can be boolean | undefined

Assigning a value that might be undefined to an optional property is not allowed under this strict mode.

Suggested Fix

The fix is to only include the properties when they have defined values. Here's the corrected code:

export function convertToolProperties(args: Args): McpToolProperty[] {
    return Object.entries(args).map(([propertyName, property]) => {
        const result: McpToolProperty = {
            propertyName,
            propertyType: property.propertyType,
            description: property.description || '',
        };
        
        if (property.isRequired !== undefined) {
            result.isRequired = property.isRequired;
        }
        
        if (property.isArray !== undefined) {
            result.isArray = property.isArray;
        }
        
        return result;
    });
}

Alternatively, use the nullish coalescing operator to provide defaults:

return Object.entries(args).map(([propertyName, property]) => ({
    propertyName,
    propertyType: property.propertyType,
    description: property.description || '',
    isRequired: property.isRequired ?? true,  // Default to true
    isArray: property.isArray ?? false,       // Default to false
}));

Workaround

For users encountering this issue, you can add skipLibCheck: true to your tsconfig.json compilerOptions to bypass type checking of library files.

Additional Context

The exactOptionalPropertyTypes option is part of TypeScript's strict mode family and helps catch subtle bugs related to optional properties. While it's a strict option, library code should ideally be compatible with all strict mode settings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions