diff --git a/package-lock.json b/package-lock.json index 2060284..21aa6b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@azure/functions", - "version": "4.7.0", + "version": "4.10.0-experimental.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@azure/functions", - "version": "4.7.0", + "version": "4.10.0-experimental.1", "license": "MIT", "dependencies": { "cookie": "^0.7.0", diff --git a/package.json b/package.json index 10b16f3..e50ef76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@azure/functions", - "version": "4.7.0", + "version": "4.8.0-experimental.0", "description": "Microsoft Azure Functions NodeJS Framework", "keywords": [ "azure", diff --git a/src/app.ts b/src/app.ts index 485dd35..84d4be7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,6 +11,7 @@ import { HttpHandler, HttpMethod, HttpMethodFunctionOptions, + McpToolFunctionOptions, MySqlFunctionOptions, ServiceBusQueueFunctionOptions, ServiceBusTopicFunctionOptions, @@ -145,6 +146,17 @@ export function webPubSub(name: string, options: WebPubSubFunctionOptions): void generic(name, convertToGenericOptions(options, trigger.webPubSub)); } +/** + * Registers an MCP Tool function in your app. + * This function is triggered by MCP Tool events and allows you to define the behavior of the function. + * + * @param name - The name of the function. This must be unique within your app and is primarily used for tracking purposes. + * @param options - Configuration options for the MCP Tool function, including the handler and trigger-specific settings. + */ +export function mcpTool(name: string, options: McpToolFunctionOptions): void { + generic(name, convertToGenericOptions(options, trigger.mcpTool)); +} + export function generic(name: string, options: GenericFunctionOptions): void { if (!hasSetModel) { setProgrammingModel(); diff --git a/src/converters/toMcpToolTriggerOptionsToRpc.ts b/src/converters/toMcpToolTriggerOptionsToRpc.ts new file mode 100644 index 0000000..bdaa833 --- /dev/null +++ b/src/converters/toMcpToolTriggerOptionsToRpc.ts @@ -0,0 +1,152 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. + +import { McpToolProperty, McpToolTriggerOptions, McpToolTriggerOptionsToRpc } from '../../types'; + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. + +/** + * Converts an McpToolTriggerOptions object to an McpToolTriggerOptionsToRpc object. + * + * @param mcpToolTriggerOptions - The input options to be converted. + * @returns The converted McpToolTriggerOptionsToRpc object. + */ +export function converToMcpToolTriggerOptionsToRpc( + mcpToolTriggerOptions: McpToolTriggerOptions +): McpToolTriggerOptionsToRpc { + // Base object for the return value + const baseResult = { + toolName: mcpToolTriggerOptions.toolName, + 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 objects.'); + } + } + + // 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; + + // Define the type of the toolProperties object + type ToolProperties = + | { + _def?: { + typeName?: string; + }; + shape?: ZodObjectShape; + } + | Record; + + 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 = 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, + }; + }); + + console.log('result', { + ...baseResult, + toolProperties: JSON.stringify(result), + }); + + return { + ...baseResult, + toolProperties: JSON.stringify(result), + }; + } + // 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 && + 'propertyValue' in property && + 'description' in property && + typeof (property as McpToolProperty).propertyName === 'string' && + typeof (property as McpToolProperty).propertyValue === 'string' && + typeof (property as McpToolProperty).description === 'string' + ); +} diff --git a/src/trigger.ts b/src/trigger.ts index f709abb..1a25f69 100644 --- a/src/trigger.ts +++ b/src/trigger.ts @@ -12,8 +12,10 @@ import { GenericTriggerOptions, HttpTrigger, HttpTriggerOptions, + McpToolTrigger, + McpToolTriggerOptions, MySqlTrigger, - MySqlTriggerOptions, + MySqlTriggerOptions, ServiceBusQueueTrigger, ServiceBusQueueTriggerOptions, ServiceBusTopicTrigger, @@ -32,6 +34,7 @@ import { WebPubSubTriggerOptions, } from '@azure/functions'; import { addBindingName } from './addBindingName'; +import { converToMcpToolTriggerOptionsToRpc } from './converters/toMcpToolTriggerOptionsToRpc'; export function http(options: HttpTriggerOptions): HttpTrigger { return addTriggerBindingName({ @@ -126,6 +129,20 @@ export function webPubSub(options: WebPubSubTriggerOptions): WebPubSubTrigger { }); } +/** + * Creates an MCP Tool trigger configuration. + * This function is used to define an MCP Tool trigger for an Azure Function. + * + * @param options - The configuration options for the MCP Tool trigger, including tool-specific metadata. + * @returns An MCP Tool trigger object with the specified configuration. + */ +export function mcpTool(options: McpToolTriggerOptions): McpToolTrigger { + return addTriggerBindingName({ + ...converToMcpToolTriggerOptionsToRpc(options), + type: 'mcpToolTrigger', + }); +} + export function generic(options: GenericTriggerOptions): FunctionTrigger { return addTriggerBindingName(options); } diff --git a/types/app.d.ts b/types/app.d.ts index c06c9b1..db3e059 100644 --- a/types/app.d.ts +++ b/types/app.d.ts @@ -6,6 +6,7 @@ import { EventGridFunctionOptions } from './eventGrid'; import { EventHubFunctionOptions } from './eventHub'; import { GenericFunctionOptions } from './generic'; import { HttpFunctionOptions, HttpHandler, HttpMethodFunctionOptions } from './http'; +import { McpToolFunctionOptions } from './mcpTool'; import { MySqlFunctionOptions } from './mySql'; import { ServiceBusQueueFunctionOptions, ServiceBusTopicFunctionOptions } from './serviceBus'; import { SetupOptions } from './setup'; @@ -196,4 +197,6 @@ export function generic(name: string, options: GenericFunctionOptions): void; */ export function webPubSub(name: string, options: WebPubSubFunctionOptions): void; +export function mcpTool(name: string, options: McpToolFunctionOptions): void; + export * as hook from './hooks/registerHook'; diff --git a/types/index.d.ts b/types/index.d.ts index 2676a89..314c4c1 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -17,6 +17,7 @@ export * from './hooks/logHooks'; export * from './http'; export * as input from './input'; export * from './InvocationContext'; +export * from './mcpTool'; export * from './mySql'; export * as output from './output'; export * from './serviceBus'; diff --git a/types/mcpTool.d.ts b/types/mcpTool.d.ts new file mode 100644 index 0000000..91aa366 --- /dev/null +++ b/types/mcpTool.d.ts @@ -0,0 +1,102 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. + +import { FunctionOptions, FunctionResult, FunctionTrigger } from './index'; +import { InvocationContext } from './InvocationContext'; + +/** + * A handler function for MCP Tool triggers. + * + * @param messages - The messages or data received by the trigger. + * @param context - The invocation context for the function. + * @returns A result that can be a promise or a synchronous value. + */ +export type McpToolTriggerHandler = (messages: unknown, context: InvocationContext) => FunctionResult; + +/** + * Configuration options for an MCP Tool function. + * This includes trigger-specific options and general function options. + */ +export interface McpToolFunctionOptions extends McpToolTriggerOptions, Partial { + /** + * The handler function to execute when the trigger is invoked. + */ + handler: McpToolTriggerHandler; + + /** + * The trigger configuration for the MCP Tool. + */ + trigger?: McpToolTrigger; +} + +/** + * Configuration options for an MCP Tool trigger. + * These options define the behavior and metadata for the trigger. + */ +export interface McpToolTriggerOptions { + /** + * The name of the tool associated with the trigger. + * This is typically an app setting or environment variable. + */ + toolName: string; + + /** + * A description of the tool or trigger. + * This provides additional context about the trigger's purpose. + */ + description: string; + + /** + * Additional properties or metadata for the tool. + * This is a dictionary of key-value pairs that can be used to configure the trigger. + */ + toolProperties?: any | McpToolProperty[]; +} + +/** + * Configuration options for an MCP Tool trigger. + * These options define the behavior and metadata for the trigger. + */ +export interface McpToolTriggerOptionsToRpc { + /** + * The name of the tool associated with the trigger. + * This is typically an app setting or environment variable. + */ + toolName: string; + + /** + * A description of the tool or trigger. + * This provides additional context about the trigger's purpose. + */ + description: string; + + /** + * Additional properties or metadata for the tool. + * This is a dictionary of key-value pairs that can be used to configure the trigger. + */ + toolProperties?: string; +} + +/** + * Represents an MCP Tool trigger, combining base function trigger options + * with MCP Tool-specific trigger options. + */ +export type McpToolTrigger = FunctionTrigger & McpToolTriggerOptionsToRpc; + +export interface McpToolProperty { + /** + * The name of the property. + */ + propertyName: string; + + /** + * The value of the property. + */ + propertyValue: string; + + /** + * A description of the property. + * This provides additional context about the purpose or usage of the property. + */ + description: string; +} diff --git a/types/trigger.d.ts b/types/trigger.d.ts index f54764b..e8105d2 100644 --- a/types/trigger.d.ts +++ b/types/trigger.d.ts @@ -7,6 +7,7 @@ import { EventHubTrigger, EventHubTriggerOptions } from './eventHub'; import { GenericTriggerOptions } from './generic'; import { HttpTrigger, HttpTriggerOptions } from './http'; import { FunctionTrigger } from './index'; +import { McpToolFunctionOptions, McpToolTrigger } from './mcpTool'; import { MySqlTrigger, MySqlTriggerOptions } from './mySql'; import { ServiceBusQueueTrigger, @@ -90,6 +91,11 @@ export function mySql(options: MySqlTriggerOptions): MySqlTrigger; */ export function webPubSub(options: WebPubSubTriggerOptions): WebPubSubTrigger; +/** + * [Link to docs and examples](//TODO Add link to docs and examples) + */ +export function mcpTool(options: McpToolFunctionOptions): McpToolTrigger; + /** * A generic option that can be used for any trigger type * Use this method if your desired trigger type does not already have its own method