Files
ColaFlow/colaflow-api/tests/Modules/Mcp/ColaFlow.Modules.Mcp.Tests/Contracts/JsonRpcRequestTests.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

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");
}
}