-
Notifications
You must be signed in to change notification settings - Fork 429
Add ElicitationRequired to ToolMetadata #1969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -335,7 +335,7 @@ private async Task<CallToolResult> InvokeChildToolAsync( | |
|
|
||
| // Check if this tool requires elicitation for sensitive data | ||
| var metadata = cmd.Metadata; | ||
| if (metadata.Secret) | ||
| if (metadata.Secret || metadata.ElicitationRequired) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check was correctly updated here, but the identical pattern in // CommandFactoryToolLoader.cs:153 (not in this PR's diff)
if (metadata.Secret) // ← missing || metadata.ElicitationRequiredBoth tool loaders independently check metadata before calling Please update if (metadata.Secret || metadata.ElicitationRequired) |
||
| { | ||
| var elicitationResult = await HandleSecretElicitationAsync( | ||
| request, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
| /// <summary> | ||
| /// Gets or sets whether the tool may perform destructive updates to its environment. | ||
| /// </summary> | ||
|
|
@@ -41,18 +27,14 @@ public sealed class ToolMetadata | |
| /// </para> | ||
| /// </remarks> | ||
| [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 | |
| /// </para> | ||
| /// </remarks> | ||
| [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 | |
| /// </para> | ||
| /// </remarks> | ||
| [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 | |
| /// </para> | ||
| /// </remarks> | ||
| [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 | |
| /// </para> | ||
| /// </remarks> | ||
| [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." | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether this tool requires elicitation to gather user input before execution. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If <see langword="true"/>, the tool requires elicitation to gather user input before execution. | ||
| /// If <see langword="false"/>, the tool can be executed without additional user input. | ||
| /// </para> | ||
| /// <para> | ||
| /// This metadata helps MCP clients understand when they need to trigger elicitation flows to gather necessary | ||
| /// information from users before executing the tool. | ||
| /// </para> | ||
| /// <para> | ||
| /// The default is <see langword="false"/>. | ||
| /// </para> | ||
| /// </remarks> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new
The existing |
||
| [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." | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether this tool requires local execution or resources. | ||
| /// </summary> | ||
|
|
@@ -194,20 +188,16 @@ public bool Secret | |
| /// </para> | ||
| /// </remarks> | ||
| [JsonIgnore] | ||
| public bool LocalRequired | ||
| { | ||
| get => _localRequired; | ||
| init => _localRequired = value; | ||
| } | ||
| public bool LocalRequired { get; init; } = false; | ||
|
|
||
| /// <summary> | ||
| /// Gets the localRequired metadata property with value and description for serialization. | ||
| /// </summary> | ||
| [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; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,9 +60,7 @@ public static async Task<ElicitationResponse> RequestElicitationAsync( | |
| /// <param name="server">The MCP server instance.</param> | ||
| /// <returns>True if the client supports elicitation, false otherwise.</returns> | ||
| public static bool SupportsElicitation(this McpServer server) | ||
| { | ||
| return server?.ClientCapabilities?.Elicitation != null; | ||
| } | ||
| => server?.ClientCapabilities?.Elicitation != null; | ||
|
|
||
| /// <summary> | ||
| /// 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) && | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Both Consider adding to both loaders' meta-building sections: if (metadata.ElicitationRequired)
{
meta ??= new();
meta["ElicitationRequiredHint"] = metadata.ElicitationRequired;
}Alternatively, if |
||
| isElicitationRequired; | ||
|
|
||
| return secret || elicitationRequired; | ||
| } | ||
|
|
||
| return false; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update comment as well.