From 28b72b49ff5fed9ad65b664496af946779492648 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:15:38 -0400 Subject: [PATCH] Add ElicitationRequired to ToolMetadata --- .../ToolLoading/NamespaceToolLoader.cs | 2 +- .../src/Commands/ToolMetadata.cs | 130 ++++++++---------- .../src/Commands/ToolMetadataJsonConverter.cs | 8 +- .../McpServerElicitationExtensions.cs | 19 ++- 4 files changed, 78 insertions(+), 81 deletions(-) diff --git a/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs b/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs index 07e06d66c1..cfc45d0281 100644 --- a/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs +++ b/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs @@ -335,7 +335,7 @@ private async Task InvokeChildToolAsync( // Check if this tool requires elicitation for sensitive data var metadata = cmd.Metadata; - if (metadata.Secret) + if (metadata.Secret || metadata.ElicitationRequired) { var elicitationResult = await HandleSecretElicitationAsync( request, diff --git a/core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs b/core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs index 1fb0e54f73..0503bdf23b 100644 --- a/core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs +++ b/core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Text.Json.Serialization; -using Azure.Mcp.Core.Commands; using Microsoft.Mcp.Core.Models.Metadata; namespace Microsoft.Mcp.Core.Commands; @@ -14,19 +13,6 @@ namespace Microsoft.Mcp.Core.Commands; [JsonConverter(typeof(ToolMetadataConverter))] public sealed class ToolMetadata { - private bool _destructive = true; - private bool _idempotent = false; - private bool _openWorld = true; - private bool _readOnly = false; - private bool _secret = false; - private bool _localRequired = false; - - private MetadataDefinition? _destructiveProperty; - private MetadataDefinition? _idempotentProperty; - private MetadataDefinition? _openWorldProperty; - private MetadataDefinition? _readOnlyProperty; - private MetadataDefinition? _secretProperty; - private MetadataDefinition? _localRequiredProperty; /// /// Gets or sets whether the tool may perform destructive updates to its environment. /// @@ -41,18 +27,14 @@ public sealed class ToolMetadata /// /// [JsonIgnore] - public bool Destructive - { - get => _destructive; - init => _destructive = value; - } + public bool Destructive { get; init; } = true; [JsonPropertyName("destructive")] - public MetadataDefinition DestructiveProperty => _destructiveProperty ??= new MetadataDefinition + public MetadataDefinition DestructiveProperty => field ??= new MetadataDefinition { - Value = _destructive, - Description = _destructive + Value = Destructive, + Description = Destructive ? "This tool may delete or modify existing resources in its environment." : "This tool performs only additive updates without deleting or modifying existing resources." }; @@ -70,17 +52,13 @@ public bool Destructive /// /// [JsonIgnore] - public bool Idempotent - { - get => _idempotent; - init => _idempotent = value; - } + public bool Idempotent { get; init; } = false; [JsonPropertyName("idempotent")] - public MetadataDefinition IdempotentProperty => _idempotentProperty ??= new MetadataDefinition + public MetadataDefinition IdempotentProperty => field ??= new MetadataDefinition { - Value = _idempotent, - Description = _idempotent + Value = Idempotent, + Description = Idempotent ? "Running this operation multiple times with the same arguments produces the same result without additional effects." : "Running this operation multiple times with the same arguments may have additional effects or produce different results." }; @@ -98,17 +76,13 @@ public bool Idempotent /// /// [JsonIgnore] - public bool OpenWorld - { - get => _openWorld; - init => _openWorld = value; - } + public bool OpenWorld { get; init; } = true; [JsonPropertyName("openWorld")] - public MetadataDefinition OpenWorldProperty => _openWorldProperty ??= new MetadataDefinition + public MetadataDefinition OpenWorldProperty => field ??= new MetadataDefinition { - Value = _openWorld, - Description = _openWorld + Value = OpenWorld, + Description = OpenWorld ? "This tool may interact with an unpredictable or dynamic set of entities (like web search)." : "This tool's domain of interaction is closed and well-defined, limited to a specific set of entities (like memory access)." }; @@ -130,17 +104,13 @@ public bool OpenWorld /// /// [JsonIgnore] - public bool ReadOnly - { - get => _readOnly; - init => _readOnly = value; - } + public bool ReadOnly { get; init; } = false; [JsonPropertyName("readOnly")] - public MetadataDefinition ReadOnlyProperty => _readOnlyProperty ??= new MetadataDefinition + public MetadataDefinition ReadOnlyProperty => field ??= new MetadataDefinition { - Value = _readOnly, - Description = _readOnly + Value = ReadOnly, + Description = ReadOnly ? "This tool only performs read operations without modifying any state or data." : "This tool may modify its environment and perform write operations (create, update, delete)." }; @@ -162,21 +132,45 @@ public bool ReadOnly /// /// [JsonIgnore] - public bool Secret - { - get => _secret; - init => _secret = value; - } + public bool Secret { get; init; } = false; [JsonPropertyName("secret")] - public MetadataDefinition SecretProperty => _secretProperty ??= new MetadataDefinition + public MetadataDefinition SecretProperty => field ??= new MetadataDefinition { - Value = _secret, - Description = _secret + Value = Secret, + Description = Secret ? "This tool handles sensitive data such as secrets, credentials, keys, or other confidential information." : "This tool does not handle sensitive or secret information." }; + /// + /// Gets or sets whether this tool requires elicitation to gather user input before execution. + /// + /// + /// + /// If , the tool requires elicitation to gather user input before execution. + /// If , the tool can be executed without additional user input. + /// + /// + /// This metadata helps MCP clients understand when they need to trigger elicitation flows to gather necessary + /// information from users before executing the tool. + /// + /// + /// The default is . + /// + /// + [JsonIgnore] + public bool ElicitationRequired { get; init; } = false; + + [JsonPropertyName("elicitationRequired")] + public MetadataDefinition ElicitationRequiredProperty => field ??= new MetadataDefinition + { + Value = ElicitationRequired, + Description = ElicitationRequired + ? "This tool requires elicitation to gather user input before execution." + : "This tool does not require elicitation and can be executed without additional user input." + }; + /// /// Gets or sets whether this tool requires local execution or resources. /// @@ -194,20 +188,16 @@ public bool Secret /// /// [JsonIgnore] - public bool LocalRequired - { - get => _localRequired; - init => _localRequired = value; - } + public bool LocalRequired { get; init; } = false; /// /// Gets the localRequired metadata property with value and description for serialization. /// [JsonPropertyName("localRequired")] - public MetadataDefinition LocalRequiredProperty => _localRequiredProperty ??= new MetadataDefinition + public MetadataDefinition LocalRequiredProperty => field ??= new MetadataDefinition { - Value = _localRequired, - Description = _localRequired + Value = LocalRequired, + Description = LocalRequired ? "This tool is only available when the Azure MCP server is configured to run as a Local MCP Server (STDIO)." : "This tool is available in both local and remote server modes." }; @@ -227,14 +217,16 @@ public ToolMetadata( MetadataDefinition openWorld, MetadataDefinition readOnly, MetadataDefinition secret, - MetadataDefinition localRequired) + MetadataDefinition localRequired, + MetadataDefinition elicitationRequired) { - _destructive = destructive?.Value ?? true; - _idempotent = idempotent?.Value ?? false; - _openWorld = openWorld?.Value ?? true; - _readOnly = readOnly?.Value ?? false; - _secret = secret?.Value ?? false; - _localRequired = localRequired?.Value ?? false; + Destructive = destructive?.Value ?? true; + Idempotent = idempotent?.Value ?? false; + OpenWorld = openWorld?.Value ?? true; + ReadOnly = readOnly?.Value ?? false; + Secret = secret?.Value ?? false; + LocalRequired = localRequired?.Value ?? false; + ElicitationRequired = elicitationRequired?.Value ?? false; } } diff --git a/core/Microsoft.Mcp.Core/src/Commands/ToolMetadataJsonConverter.cs b/core/Microsoft.Mcp.Core/src/Commands/ToolMetadataJsonConverter.cs index dcb86afc57..912a9ff443 100644 --- a/core/Microsoft.Mcp.Core/src/Commands/ToolMetadataJsonConverter.cs +++ b/core/Microsoft.Mcp.Core/src/Commands/ToolMetadataJsonConverter.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; using Azure.Mcp.Core.Areas.Server; -using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Models.Metadata; -namespace Azure.Mcp.Core.Commands; +namespace Microsoft.Mcp.Core.Commands; /// /// Custom JSON converter for that handles serialization and deserialization @@ -35,7 +33,8 @@ MetadataDefinition GetMetadata(string name, bool defaultValue) GetMetadata("openWorld", true), GetMetadata("readOnly", false), GetMetadata("secret", false), - GetMetadata("localRequired", false) + GetMetadata("localRequired", false), + GetMetadata("elicitationRequired", false) ); } @@ -55,6 +54,7 @@ void WriteMetadata(string name, MetadataDefinition def) WriteMetadata("readOnly", value.ReadOnlyProperty); WriteMetadata("secret", value.SecretProperty); WriteMetadata("localRequired", value.LocalRequiredProperty); + WriteMetadata("elicitationRequired", value.ElicitationRequiredProperty); writer.WriteEndObject(); } diff --git a/core/Microsoft.Mcp.Core/src/Extensions/McpServerElicitationExtensions.cs b/core/Microsoft.Mcp.Core/src/Extensions/McpServerElicitationExtensions.cs index 8faabad7c4..22f48bfa7d 100644 --- a/core/Microsoft.Mcp.Core/src/Extensions/McpServerElicitationExtensions.cs +++ b/core/Microsoft.Mcp.Core/src/Extensions/McpServerElicitationExtensions.cs @@ -60,9 +60,7 @@ public static async Task RequestElicitationAsync( /// The MCP server instance. /// True if the client supports elicitation, false otherwise. public static bool SupportsElicitation(this McpServer server) - { - return server?.ClientCapabilities?.Elicitation != null; - } + => server?.ClientCapabilities?.Elicitation != null; /// /// Checks if elicitation should be triggered for a tool based on its metadata. @@ -81,10 +79,17 @@ public static bool ShouldTriggerElicitation(this McpServer server, string toolNa // Check if the metadata indicates this is a secret/sensitive tool if (toolMetadata is JsonObject jsonMetadata) { - return jsonMetadata.TryGetPropertyValue("Secret", out var secretValue) && - secretValue is JsonValue jsonValue && - jsonValue.TryGetValue(out bool isSecret) && - isSecret; + var secret = jsonMetadata.TryGetPropertyValue("Secret", out var secretValue) && + secretValue is JsonValue jsonValue && + jsonValue.TryGetValue(out bool isSecret) && + isSecret; + + var elicitationRequired = jsonMetadata.TryGetPropertyValue("ElicitationRequired", out var elicitationValue) && + elicitationValue is JsonValue elicitationJsonValue && + elicitationJsonValue.TryGetValue(out bool isElicitationRequired) && + isElicitationRequired; + + return secret || elicitationRequired; } return false;