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>
136 lines
4.1 KiB
C#
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
|
|
}
|
|
}
|