Files
ColaFlow/colaflow-api/tests/Modules/Mcp/ColaFlow.Modules.Mcp.Tests/Handlers/InitializeMethodHandlerTests.cs
Yaojia Wang 48a8431e4f feat(backend): Implement MCP Protocol Handler (Story 5.1)
Implemented JSON-RPC 2.0 protocol handler for MCP communication, enabling AI agents to communicate with ColaFlow using the Model Context Protocol.

**Implementation:**
- JSON-RPC 2.0 data models (Request, Response, Error, ErrorCode)
- MCP protocol models (Initialize, Capabilities, ClientInfo, ServerInfo)
- McpProtocolHandler with method routing and error handling
- Method handlers: initialize, resources/list, tools/list, tools/call
- ASP.NET Core middleware for /mcp endpoint
- Service registration and dependency injection setup

**Testing:**
- 28 unit tests covering protocol parsing, validation, and error handling
- Integration tests for initialize handshake and error responses
- All tests passing with >80% coverage

**Changes:**
- Created ColaFlow.Modules.Mcp.Contracts project
- Created ColaFlow.Modules.Mcp.Domain project
- Created ColaFlow.Modules.Mcp.Application project
- Created ColaFlow.Modules.Mcp.Infrastructure project
- Created ColaFlow.Modules.Mcp.Tests project
- Registered MCP module in ColaFlow.API Program.cs
- Added /mcp endpoint via middleware

**Acceptance Criteria Met:**
 JSON-RPC 2.0 messages correctly parsed
 Request validation (jsonrpc: "2.0", method, params, id)
 Error responses conform to JSON-RPC 2.0 spec
 Invalid requests return proper error codes (-32700, -32600, -32601, -32602)
 MCP initialize method implemented
 Server capabilities returned (resources, tools, prompts)
 Protocol version negotiation works (1.0)
 Request routing to method handlers
 Unit test coverage > 80%
 All tests passing

**Story**: docs/stories/sprint_5/story_5_1.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 19:38:34 +01:00

136 lines
4.1 KiB
C#

using System.Text.Json;
using ColaFlow.Modules.Mcp.Application.Handlers;
using ColaFlow.Modules.Mcp.Contracts.Mcp;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using NSubstitute;
namespace ColaFlow.Modules.Mcp.Tests.Handlers;
/// <summary>
/// Unit tests for InitializeMethodHandler
/// </summary>
public class InitializeMethodHandlerTests
{
private readonly ILogger<InitializeMethodHandler> _logger;
private readonly InitializeMethodHandler _sut;
public InitializeMethodHandlerTests()
{
_logger = Substitute.For<ILogger<InitializeMethodHandler>>();
_sut = new InitializeMethodHandler(_logger);
}
[Fact]
public void MethodName_ReturnsInitialize()
{
// Assert
_sut.MethodName.Should().Be("initialize");
}
[Fact]
public async Task HandleAsync_WithValidRequest_ReturnsInitializeResponse()
{
// Arrange
var initRequest = new McpInitializeRequest
{
ProtocolVersion = "1.0",
ClientInfo = new McpClientInfo
{
Name = "Claude Desktop",
Version = "1.0.0"
}
};
// Act
var result = await _sut.HandleAsync(initRequest, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.Should().BeOfType<McpInitializeResponse>();
var response = (McpInitializeResponse)result!;
response.ProtocolVersion.Should().Be("1.0");
response.ServerInfo.Should().NotBeNull();
response.ServerInfo.Name.Should().Be("ColaFlow MCP Server");
response.ServerInfo.Version.Should().Be("1.0.0");
response.Capabilities.Should().NotBeNull();
}
[Fact]
public async Task HandleAsync_ReturnsCapabilitiesWithAllFeaturesSupported()
{
// Arrange
var initRequest = new McpInitializeRequest
{
ProtocolVersion = "1.0",
ClientInfo = new McpClientInfo { Name = "Test", Version = "1.0" }
};
// Act
var result = await _sut.HandleAsync(initRequest, CancellationToken.None);
// Assert
var response = (McpInitializeResponse)result!;
response.Capabilities.Resources.Should().NotBeNull();
response.Capabilities.Resources!.Supported.Should().BeTrue();
response.Capabilities.Tools.Should().NotBeNull();
response.Capabilities.Tools!.Supported.Should().BeTrue();
response.Capabilities.Prompts.Should().NotBeNull();
response.Capabilities.Prompts!.Supported.Should().BeTrue();
}
[Fact]
public async Task HandleAsync_WithNullParams_ReturnsValidResponse()
{
// Act
var result = await _sut.HandleAsync(null, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.Should().BeOfType<McpInitializeResponse>();
}
[Fact]
public async Task HandleAsync_WithObjectParams_DeserializesCorrectly()
{
// Arrange - Simulate how JSON deserialization works with object params
var paramsObj = new
{
protocolVersion = "1.0",
clientInfo = new
{
name = "Claude Desktop",
version = "1.0.0"
}
};
// Act
var result = await _sut.HandleAsync(paramsObj, CancellationToken.None);
// Assert
result.Should().NotBeNull();
var response = (McpInitializeResponse)result!;
response.ProtocolVersion.Should().Be("1.0");
}
[Fact]
public async Task HandleAsync_WithUnsupportedProtocolVersion_StillReturnsResponse()
{
// Arrange
var initRequest = new McpInitializeRequest
{
ProtocolVersion = "2.0", // Unsupported version
ClientInfo = new McpClientInfo { Name = "Test", Version = "1.0" }
};
// Act
var result = await _sut.HandleAsync(initRequest, CancellationToken.None);
// Assert
result.Should().NotBeNull();
var response = (McpInitializeResponse)result!;
response.ProtocolVersion.Should().Be("1.0"); // Server returns its supported version
}
}