Skip to content
Closed
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
1 change: 0 additions & 1 deletion docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ For enum types, the SDK supports several schema formats:
- **TitledSingleSelectEnumSchema**: A single-select enum with separate display titles for each option (using JSON Schema `oneOf` with `const` and `title`).
- **UntitledMultiSelectEnumSchema**: A multi-select enum allowing multiple values to be selected.
- **TitledMultiSelectEnumSchema**: A multi-select enum with display titles for each option.
- **LegacyTitledEnumSchema** (deprecated): The legacy enum schema using `enumNames` for backward compatibility.

The server can request a single input or multiple inputs at once.
To help distinguish multiple inputs, each input has a unique name.
Expand Down
4 changes: 1 addition & 3 deletions src/Common/Obsoletions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,5 @@ namespace ModelContextProtocol;
/// </remarks>
internal static class Obsoletions
{
public const string LegacyTitledEnumSchema_DiagnosticId = "MCP9001";
public const string LegacyTitledEnumSchema_Message = "The EnumSchema and LegacyTitledEnumSchema APIs are deprecated as of specification version 2025-11-25 and will be removed in a future major version. See SEP-1330 for more information.";
public const string LegacyTitledEnumSchema_Url = "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1330";
// MCP9001 was used for LegacyTitledEnumSchema (removed) - do not reuse
}
97 changes: 7 additions & 90 deletions src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ public string Mode
/// <value>
/// Possible values are <see cref="StringSchema"/>, <see cref="NumberSchema"/>, <see cref="BooleanSchema"/>,
/// <see cref="UntitledSingleSelectEnumSchema"/>, <see cref="TitledSingleSelectEnumSchema"/>,
/// <see cref="UntitledMultiSelectEnumSchema"/>, <see cref="TitledMultiSelectEnumSchema"/>,
/// and <see cref="LegacyTitledEnumSchema"/> (deprecated).
/// <see cref="UntitledMultiSelectEnumSchema"/>, and <see cref="TitledMultiSelectEnumSchema"/>.
/// </value>
[JsonPropertyName("requestedSchema")]
public RequestSchema? RequestedSchema { get; set; }
Expand Down Expand Up @@ -132,8 +131,7 @@ public IDictionary<string, PrimitiveSchemaDefinition> Properties
/// Represents a restricted subset of JSON Schema:
/// <see cref="StringSchema"/>, <see cref="NumberSchema"/>, <see cref="BooleanSchema"/>,
/// <see cref="UntitledSingleSelectEnumSchema"/>, <see cref="TitledSingleSelectEnumSchema"/>,
/// <see cref="UntitledMultiSelectEnumSchema"/>, <see cref="TitledMultiSelectEnumSchema"/>,
/// or <see cref="LegacyTitledEnumSchema"/> (deprecated).
/// <see cref="UntitledMultiSelectEnumSchema"/>, or <see cref="TitledMultiSelectEnumSchema"/>.
/// </summary>
[JsonConverter(typeof(Converter))]
public abstract class PrimitiveSchemaDefinition
Expand All @@ -153,9 +151,6 @@ protected private PrimitiveSchemaDefinition()
TitledSingleSelectEnumSchema { Default: { } s } => JsonSerializer.SerializeToElement(s, McpJsonUtilities.JsonContext.Default.String),
UntitledMultiSelectEnumSchema { Default: { } a } => JsonSerializer.SerializeToElement(a, McpJsonUtilities.JsonContext.Default.IListString),
TitledMultiSelectEnumSchema { Default: { } a } => JsonSerializer.SerializeToElement(a, McpJsonUtilities.JsonContext.Default.IListString),
#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
LegacyTitledEnumSchema { Default: { } s } => JsonSerializer.SerializeToElement(s, McpJsonUtilities.JsonContext.Default.String),
#pragma warning restore MCP9001
_ => null,
};

Expand Down Expand Up @@ -329,27 +324,12 @@ public sealed class Converter : JsonConverter<PrimitiveSchemaDefinition>
}
else if (enumValues is not null)
{
if (enumNames is not null)
// UntitledSingleSelectEnumSchema (enumNames is ignored if present)
psd = new UntitledSingleSelectEnumSchema
{
// LegacyTitledEnumSchema is deprecated but supported for backward compatibility.
#pragma warning disable MCP9001
psd = new LegacyTitledEnumSchema
#pragma warning restore MCP9001
{
Enum = enumValues,
EnumNames = enumNames,
Default = defaultString,
};
}
else
{
// UntitledSingleSelectEnumSchema
psd = new UntitledSingleSelectEnumSchema
{
Enum = enumValues,
Default = defaultString,
};
}
Enum = enumValues,
Default = defaultString,
};
}
else
{
Expand Down Expand Up @@ -642,25 +622,6 @@ public override void Write(Utf8JsonWriter writer, PrimitiveSchemaDefinition valu
}
break;

#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
case LegacyTitledEnumSchema legacyTitledEnum:
#pragma warning restore MCP9001
if (legacyTitledEnum.Enum is not null)
{
writer.WritePropertyName("enum");
JsonSerializer.Serialize(writer, legacyTitledEnum.Enum, McpJsonUtilities.JsonContext.Default.IListString);
}
if (legacyTitledEnum.EnumNames is not null)
{
writer.WritePropertyName("enumNames");
JsonSerializer.Serialize(writer, legacyTitledEnum.EnumNames, McpJsonUtilities.JsonContext.Default.IListString);
}
if (legacyTitledEnum.Default is not null)
{
writer.WriteString("default", legacyTitledEnum.Default);
}
break;

default:
throw new JsonException($"Unexpected schema type: {value.GetType().Name}");
}
Expand Down Expand Up @@ -1008,48 +969,4 @@ public override string Type
public IList<string>? Default { get; set; }
}

/// <summary>
/// Represents a legacy schema for an enum type with enumNames.
/// </summary>
/// <remarks>
/// This schema is deprecated in favor of <see cref="TitledSingleSelectEnumSchema"/>.
/// </remarks>
[Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)]
public sealed class LegacyTitledEnumSchema : PrimitiveSchemaDefinition
{
/// <inheritdoc/>
[JsonPropertyName("type")]
public override string Type
{
get => "string";
set
{
if (value is not "string")
{
throw new ArgumentException("Type must be 'string'.", nameof(value));
}
}
}

/// <summary>Gets or sets the list of allowed string values for the enum.</summary>
[JsonPropertyName("enum")]
[field: MaybeNull]
public IList<string> Enum
{
get => field ??= [];
set
{
Throw.IfNull(value);
field = value;
}
}

/// <summary>Gets or sets optional display names corresponding to the enum values.</summary>
[JsonPropertyName("enumNames")]
public IList<string>? EnumNames { get; set; }

/// <summary>Gets or sets the default value for the enum.</summary>
[JsonPropertyName("default")]
public string? Default { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,6 @@ public static async Task<string> ElicitationSep1330Enums(
new() { Const = "value3", Title = "Third Option" }
]
},
#pragma warning disable MCP9001
["legacyEnum"] = new ElicitRequestParams.LegacyTitledEnumSchema()
{
Description = "Choose a legacy option",
Enum = ["opt1", "opt2", "opt3"],
EnumNames = ["Option One", "Option Two", "Option Three"]
},
#pragma warning restore MCP9001
["untitledMulti"] = new ElicitRequestParams.UntitledMultiSelectEnumSchema()
{
Description = "Choose multiple options",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,26 +282,6 @@ public static void TitledMultiSelectEnumSchema_Default_Null_DoesNotSerialize()
Assert.DoesNotContain("\"default\"", json);
}

#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
[Fact]
public static void LegacyTitledEnumSchema_Default_Null_DoesNotSerialize()
{
// Arrange
var schema = new ElicitRequestParams.LegacyTitledEnumSchema
{
Title = "Legacy Options",
Enum = ["option1", "option2"],
EnumNames = ["Option 1", "Option 2"]
};

// Act - serialize as base type to use the converter
string json = JsonSerializer.Serialize<ElicitRequestParams.PrimitiveSchemaDefinition>(schema, McpJsonUtilities.DefaultOptions);

// Assert
Assert.DoesNotContain("\"default\"", json);
}
#pragma warning restore MCP9001

[Fact]
public static void RequestSchema_WithAllDefaultTypes_Serializes_Correctly()
{
Expand Down
88 changes: 0 additions & 88 deletions tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,92 +277,4 @@ public void MultiSelectEnum_WithAnyOf_Deserializes_As_TitledMultiSelect()
Assert.Equal("Administrator", result.Items.AnyOf[0].Title);
Assert.Equal(["user"], result.Default);
}

#pragma warning disable MCP9001 // EnumSchema and LegacyTitledEnumSchema are deprecated but supported for backward compatibility
[Fact]
public void LegacyTitledEnumSchema_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.LegacyTitledEnumSchema
{
Title = "Environment",
Description = "Deployment environment",
Enum = ["dev", "staging", "prod"],
EnumNames = ["Development", "Staging", "Production"],
Default = "staging"
};

// Act
string json = JsonSerializer.Serialize<ElicitRequestParams.PrimitiveSchemaDefinition>(schema, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<ElicitRequestParams.PrimitiveSchemaDefinition>(json, McpJsonUtilities.DefaultOptions);

// Assert
Assert.NotNull(deserialized);
var result = Assert.IsType<ElicitRequestParams.LegacyTitledEnumSchema>(deserialized);
Assert.Equal("string", result.Type);
Assert.Equal("Environment", result.Title);
Assert.Equal("Deployment environment", result.Description);
Assert.Equal(["dev", "staging", "prod"], result.Enum);
Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
Assert.Equal("staging", result.Default);
Assert.Contains("\"enumNames\":[\"Development\",\"Staging\",\"Production\"]", json);
}

[Fact]
public void LegacyTitledEnumSchema_Direct_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.LegacyTitledEnumSchema
{
Title = "Environment",
Description = "Deployment environment",
Enum = ["dev", "staging", "prod"],
EnumNames = ["Development", "Staging", "Production"],
Default = "staging"
};

// Act
string json = JsonSerializer.Serialize<ElicitRequestParams.PrimitiveSchemaDefinition>(schema, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<ElicitRequestParams.PrimitiveSchemaDefinition>(json, McpJsonUtilities.DefaultOptions);

// Assert
Assert.NotNull(deserialized);
var result = Assert.IsType<ElicitRequestParams.LegacyTitledEnumSchema>(deserialized);
Assert.Equal("string", result.Type);
Assert.Equal("Environment", result.Title);
Assert.Equal("Deployment environment", result.Description);
Assert.Equal(["dev", "staging", "prod"], result.Enum);
Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
Assert.Equal("staging", result.Default);
Assert.Contains("\"enumNames\":[\"Development\",\"Staging\",\"Production\"]", json);
}

[Fact]
public void Enum_WithEnumNames_Deserializes_As_LegacyTitledEnumSchema()
{
// Arrange - JSON with enumNames should deserialize as (deprecated) LegacyTitledEnumSchema
string json = """
{
"type": "string",
"title": "Environment",
"description": "Deployment environment",
"enum": ["dev", "staging", "prod"],
"enumNames": ["Development", "Staging", "Production"],
"default": "staging"
}
""";
// Act
var deserialized = JsonSerializer.Deserialize<ElicitRequestParams.PrimitiveSchemaDefinition>(json, McpJsonUtilities.DefaultOptions);

// Assert
Assert.NotNull(deserialized);
var result = Assert.IsType<ElicitRequestParams.LegacyTitledEnumSchema>(deserialized);
Assert.Equal("string", result.Type);
Assert.Equal("Environment", result.Title);
Assert.Equal("Deployment environment", result.Description);
Assert.Equal(["dev", "staging", "prod"], result.Enum);
Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
Assert.Equal("staging", result.Default);
}
#pragma warning restore MCP9001
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,39 +257,4 @@ public static void TitledMultiSelectEnumSchema_UnknownProperties_AreIgnored()
Assert.Contains("optionX", enumSchema.Default);
Assert.Contains("optionZ", enumSchema.Default);
}

#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
[Fact]
public static void LegacyTitledEnumSchema_UnknownProperties_AreIgnored()
{
const string json = """
{
"type": "string",
"enum": ["option1", "option2"],

"unknownNull": null,
"unknownEmptyObject": {},
"unknownObject": {"a": 1},
"unknownEmptyArray": [],
"unknownArray": [1, 2, 3],
"unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},

"enumNames": ["Option 1", "Option 2"],
"default": "option2"
}
""";

var result = JsonSerializer.Deserialize<ElicitRequestParams.PrimitiveSchemaDefinition>(
json,
McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
var enumSchema = Assert.IsType<ElicitRequestParams.LegacyTitledEnumSchema>(result);
Assert.Equal("string", enumSchema.Type);
Assert.Equal(2, enumSchema.Enum.Count);
Assert.Contains("option1", enumSchema.Enum);
Assert.Contains("option2", enumSchema.Enum);
Assert.Contains("Option 1", enumSchema.EnumNames!);
Assert.Contains("Option 2", enumSchema.EnumNames!);
}
#pragma warning restore MCP9001
}
Loading