diff --git a/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContext.java b/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContext.java index 3755660380..fcfcd06b76 100644 --- a/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContext.java +++ b/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContext.java @@ -163,8 +163,6 @@ public Mono elicitationInternal(String message, Type type, Map generateElicitSchema(Type type) { Map schema = jsonHelper.fromJsonToMap(McpJsonSchemaGenerator.generateFromType(type)); - // remove as elicitation schema does not support it - schema.remove("$schema"); return schema; } diff --git a/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContext.java b/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContext.java index ace5a15583..421b8ec3e0 100644 --- a/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContext.java +++ b/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContext.java @@ -213,8 +213,6 @@ private ElicitResult elicitationInternal(String message, Type type, Map generateElicitSchema(Type type) { Map schema = jsonHelper.fromJsonToMap(McpJsonSchemaGenerator.generateFromType(type)); - // remove $schema as elicitation schema does not support it - schema.remove("$schema"); return schema; } diff --git a/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContextTests.java b/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContextTests.java index 4adee7dc5e..ea973d98b4 100644 --- a/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContextTests.java +++ b/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpAsyncRequestContextTests.java @@ -179,6 +179,31 @@ public void testElicitationWithMessageAndMeta() { assertThat(capturedRequest.requestedSchema()).isNotNull(); } + @Test + public void testElicitationPreservesSchemaMetadata() { + ClientCapabilities capabilities = mock(ClientCapabilities.class); + ClientCapabilities.Elicitation elicitation = mock(ClientCapabilities.Elicitation.class); + when(capabilities.elicitation()).thenReturn(elicitation); + when(this.exchange.getClientCapabilities()).thenReturn(capabilities); + + record Person(String name) { + } + + ElicitResult expectedResult = mock(ElicitResult.class); + when(expectedResult.action()).thenReturn(ElicitResult.Action.ACCEPT); + when(expectedResult.content()).thenReturn(Map.of("name", "John")); + when(this.exchange.createElicitation(any(ElicitRequest.class))).thenReturn(Mono.just(expectedResult)); + + StepVerifier.create(this.context.elicit(Person.class)).expectNextCount(1).verifyComplete(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ElicitFormRequest.class); + verify(this.exchange).createElicitation(captor.capture()); + + Map requestedSchema = captor.getValue().requestedSchema(); + assertThat(requestedSchema).containsKeys("$schema", "type", "properties", "required"); + assertThat(((Map) requestedSchema.get("properties")).containsKey("name")).isTrue(); + } + @Test public void testElicitationWithMetadata() { ClientCapabilities capabilities = mock(ClientCapabilities.class); diff --git a/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContextTests.java b/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContextTests.java index 975805ed80..4fb61678e9 100644 --- a/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContextTests.java +++ b/mcp/mcp-annotations/src/test/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContextTests.java @@ -224,6 +224,31 @@ public void testElicitationWithTypeAndMessage() { assertThat(capturedRequest.requestedSchema()).isNotNull(); } + @Test + public void testElicitationPreservesSchemaMetadata() { + ClientCapabilities capabilities = mock(ClientCapabilities.class); + ClientCapabilities.Elicitation elicitation = mock(ClientCapabilities.Elicitation.class); + when(capabilities.elicitation()).thenReturn(elicitation); + when(this.exchange.getClientCapabilities()).thenReturn(capabilities); + + record Person(String name) { + } + + ElicitResult expectedResult = mock(ElicitResult.class); + when(expectedResult.action()).thenReturn(ElicitResult.Action.ACCEPT); + when(expectedResult.content()).thenReturn(Map.of("name", "John")); + when(this.exchange.createElicitation(any(ElicitRequest.class))).thenReturn(expectedResult); + + this.context.elicit(Person.class); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ElicitFormRequest.class); + verify(this.exchange).createElicitation(captor.capture()); + + Map requestedSchema = captor.getValue().requestedSchema(); + assertThat(requestedSchema).containsKeys("$schema", "type", "properties", "required"); + assertThat(((Map) requestedSchema.get("properties")).containsKey("name")).isTrue(); + } + @Test public void testElicitationWithTypeMessageAndMeta() { ClientCapabilities capabilities = mock(ClientCapabilities.class);