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>
161 lines
3.8 KiB
C#
161 lines
3.8 KiB
C#
using System.Text.Json;
|
|
using ColaFlow.Modules.Mcp.Contracts.JsonRpc;
|
|
using FluentAssertions;
|
|
|
|
namespace ColaFlow.Modules.Mcp.Tests.Contracts;
|
|
|
|
/// <summary>
|
|
/// Unit tests for JsonRpcRequest
|
|
/// </summary>
|
|
public class JsonRpcRequestTests
|
|
{
|
|
[Fact]
|
|
public void IsValid_WithValidRequest_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Method = "test_method",
|
|
Id = 1
|
|
};
|
|
|
|
// Act
|
|
var isValid = request.IsValid(out var errorMessage);
|
|
|
|
// Assert
|
|
isValid.Should().BeTrue();
|
|
errorMessage.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValid_WithInvalidJsonRpcVersion_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "1.0",
|
|
Method = "test_method"
|
|
};
|
|
|
|
// Act
|
|
var isValid = request.IsValid(out var errorMessage);
|
|
|
|
// Assert
|
|
isValid.Should().BeFalse();
|
|
errorMessage.Should().Contain("jsonrpc must be exactly '2.0'");
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValid_WithEmptyMethod_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Method = ""
|
|
};
|
|
|
|
// Act
|
|
var isValid = request.IsValid(out var errorMessage);
|
|
|
|
// Assert
|
|
isValid.Should().BeFalse();
|
|
errorMessage.Should().Contain("method is required");
|
|
}
|
|
|
|
[Fact]
|
|
public void IsNotification_WithNoId_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Method = "notification_method",
|
|
Id = null
|
|
};
|
|
|
|
// Act & Assert
|
|
request.IsNotification.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsNotification_WithId_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Method = "regular_method",
|
|
Id = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
request.IsNotification.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void Deserialize_WithValidJson_CreatesRequest()
|
|
{
|
|
// Arrange
|
|
var json = @"{
|
|
""jsonrpc"": ""2.0"",
|
|
""method"": ""test_method"",
|
|
""params"": { ""key"": ""value"" },
|
|
""id"": 1
|
|
}";
|
|
|
|
// Act
|
|
var request = JsonSerializer.Deserialize<JsonRpcRequest>(json);
|
|
|
|
// Assert
|
|
request.Should().NotBeNull();
|
|
request!.JsonRpc.Should().Be("2.0");
|
|
request.Method.Should().Be("test_method");
|
|
request.Params.Should().NotBeNull();
|
|
// Id can be number or string, System.Text.Json deserializes number to JsonElement
|
|
// Just check it's not null
|
|
request.Id.Should().NotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Deserialize_WithoutParams_CreatesRequestWithNullParams()
|
|
{
|
|
// Arrange
|
|
var json = @"{
|
|
""jsonrpc"": ""2.0"",
|
|
""method"": ""test_method"",
|
|
""id"": 1
|
|
}";
|
|
|
|
// Act
|
|
var request = JsonSerializer.Deserialize<JsonRpcRequest>(json);
|
|
|
|
// Assert
|
|
request.Should().NotBeNull();
|
|
request!.Params.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Serialize_IncludesAllFields()
|
|
{
|
|
// Arrange
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Method = "test_method",
|
|
Params = new { key = "value" },
|
|
Id = 1
|
|
};
|
|
|
|
// Act
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
// Assert
|
|
json.Should().Contain("\"jsonrpc\":\"2.0\"");
|
|
json.Should().Contain("\"method\":\"test_method\"");
|
|
json.Should().Contain("\"params\":");
|
|
json.Should().Contain("\"id\":1");
|
|
}
|
|
}
|