@@ -713,34 +713,26 @@ public async Task Server_CompleteHandler_ShouldReturnConfigurationSuggestions()
713713
714714 #endregion
715715
716- #region MCP Task Support Tests
716+ #region MCP Task Support Tests (Graceful Degradation)
717717
718- [ Fact ]
719- public void Server_ShouldAdvertiseTasksCapability ( )
720- {
721- // Arrange
722- Assert . NotNull ( _client ) ;
723-
724- // Assert - server should expose tasks capability (InMemoryMcpTaskStore is registered)
725- Assert . NotNull ( _client . ServerCapabilities ) ;
726- Assert . NotNull ( _client . ServerCapabilities . Tasks ) ;
727- }
718+ // Task support is intentionally disabled: MCP SDK v1.1.0's ExecuteToolAsTaskAsync
719+ // disposes the DI scope before the background task resolves services, causing
720+ // ObjectDisposedException on every tool call. Without InMemoryMcpTaskStore,
721+ // the server runs tools synchronously and does not advertise task capabilities.
728722
729723 [ Fact ]
730- public void Server_TasksCapability_ShouldSupportListAndCancel ( )
724+ public void Server_ShouldNotAdvertiseTasksCapability_WhenTaskStoreNotRegistered ( )
731725 {
732726 // Arrange
733727 Assert . NotNull ( _client ) ;
734728
735- // Assert
736- var tasks = _client . ServerCapabilities ? . Tasks ;
737- Assert . NotNull ( tasks ) ;
738- Assert . NotNull ( tasks . List ) ;
739- Assert . NotNull ( tasks . Cancel ) ;
729+ // Assert - no task store means no tasks capability advertised
730+ Assert . NotNull ( _client . ServerCapabilities ) ;
731+ Assert . Null ( _client . ServerCapabilities . Tasks ) ;
740732 }
741733
742734 [ Fact ]
743- public async Task Server_DotnetProject_ShouldHaveTaskSupport ( )
735+ public async Task Server_DotnetProject_ShouldDeclareTaskSupportOptional ( )
744736 {
745737 // Arrange
746738 Assert . NotNull ( _client ) ;
@@ -749,77 +741,24 @@ public async Task Server_DotnetProject_ShouldHaveTaskSupport()
749741 var tools = await _client . ListToolsAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
750742 var projectTool = tools . FirstOrDefault ( t => t . Name == "dotnet_project" ) ;
751743
752- // Assert - dotnet_project should declare TaskSupport = Optional so clients can run
753- // long operations (build, test, publish) as async tasks
744+ // Assert - the tool attribute declares TaskSupport = Optional so it's ready when
745+ // task support is re-enabled by un-commenting the IMcpTaskStore registration in Program.cs
746+ // (pending fix of https://github.com/modelcontextprotocol/csharp-sdk/issues/1430).
747+ // Without a task store registered, the SDK runs it synchronously inline (graceful degradation).
754748 Assert . NotNull ( projectTool ) ;
755749 var execution = projectTool . ProtocolTool . Execution ;
756750 Assert . NotNull ( execution ) ;
757751 Assert . Equal ( ModelContextProtocol . Protocol . ToolTaskSupport . Optional , execution . TaskSupport ) ;
758752 }
759753
760754 [ Fact ]
761- public async Task Server_TaskList_ShouldReturnEmptyWhenNoTasksRunning ( )
755+ public async Task Server_DotnetProject_ShouldWorkSynchronously ( )
762756 {
763- // Arrange
757+ // Arrange - verify tools work reliably via normal tools/call (no tasks)
764758 Assert . NotNull ( _client ) ;
765759
766- // Act
767- var tasks = await _client . ListTasksAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
768-
769- // Assert - no tasks should be running at server start
770- Assert . NotNull ( tasks ) ;
771- }
772-
773- [ Fact ]
774- public async Task Server_DotnetProject_TaskMode_ShouldCompleteSuccessfully ( )
775- {
776- // Arrange - exercise the full task lifecycle: create task → poll → get result.
777- // Uses Build with a nonexistent project so the operation is fast but still
778- // exercises the entire task pipeline including the error-handling filter.
779- Assert . NotNull ( _client ) ;
780-
781- // Act - invoke dotnet_project as task (the dedicated CallToolAsTaskAsync API)
782- var mcpTask = await _client . CallToolAsTaskAsync (
783- "dotnet_project" ,
784- new Dictionary < string , object ? >
785- {
786- [ "action" ] = "Build" ,
787- [ "project" ] = "nonexistent.csproj"
788- } ,
789- cancellationToken : TestContext . Current . CancellationToken ) ;
790-
791- // Assert - task was created successfully
792- Assert . NotNull ( mcpTask ) ;
793- Assert . NotNull ( mcpTask . TaskId ) ;
794- Assert . NotEmpty ( mcpTask . TaskId ) ;
795-
796- // Poll for completion — GetTaskResultAsync blocks until terminal state
797- var resultJson = await _client . GetTaskResultAsync (
798- mcpTask . TaskId ,
799- cancellationToken : TestContext . Current . CancellationToken ) ;
800-
801- // The key validation: the task infrastructure returns a structured result JSON
802- // instead of throwing "unknown error". The underlying tool may report a build
803- // failure (nonexistent project), so the task status may be Completed or Failed —
804- // what matters is that a proper result is returned rather than an opaque error.
805- Assert . NotEqual ( default , resultJson ) ;
806-
807- // Verify the task reached a terminal state
808- var finalTask = await _client . GetTaskAsync ( mcpTask . TaskId , cancellationToken : TestContext . Current . CancellationToken ) ;
809- Assert . NotNull ( finalTask ) ;
810- Assert . True (
811- finalTask . Status == McpTaskStatus . Completed || finalTask . Status == McpTaskStatus . Failed ,
812- $ "Task should be in a terminal state but was: { finalTask . Status } ") ;
813- }
814-
815- [ Fact ]
816- public async Task Server_DotnetProject_TaskMode_ShouldAppearInTaskList ( )
817- {
818- // Arrange - verify that a task-mode call is tracked in the task store.
819- Assert . NotNull ( _client ) ;
820-
821- // Act - start a fast task-mode operation
822- var mcpTask = await _client . CallToolAsTaskAsync (
760+ // Act - standard synchronous tool call
761+ var result = await _client . CallToolAsync (
823762 "dotnet_project" ,
824763 new Dictionary < string , object ? >
825764 {
@@ -828,15 +767,10 @@ public async Task Server_DotnetProject_TaskMode_ShouldAppearInTaskList()
828767 } ,
829768 cancellationToken : TestContext . Current . CancellationToken ) ;
830769
831- Assert . NotNull ( mcpTask ) ;
832-
833- // Wait for it to complete before checking the list
834- await _client . GetTaskResultAsync ( mcpTask . TaskId , cancellationToken : TestContext . Current . CancellationToken ) ;
835-
836- // Assert - the completed task should still be in the list
837- var allTasks = await _client . ListTasksAsync ( cancellationToken : TestContext . Current . CancellationToken ) ;
838- Assert . NotNull ( allTasks ) ;
839- Assert . Contains ( allTasks , t => t . TaskId == mcpTask . TaskId ) ;
770+ // Assert - tool returns a result (may be an error about missing project, but
771+ // the call itself should not throw ObjectDisposedException)
772+ Assert . NotNull ( result ) ;
773+ Assert . NotEmpty ( result . Content ) ;
840774 }
841775
842776 #endregion
0 commit comments