a2aproject / a2aproject/a2a-dotnet
[Feat]: Enforce required Message fields at compile-time or runtime to ensure A2A spec compliance
- 主要言語
- C#
- スター
- 262
- フォーク
- 64
- 平均マージ
- 5日 2時間
- マージ済み PR(30日)
- 31
説明
### Is your feature request related to a problem? Please describe.
The A2A specification (v0.3.0) defines `Message.messageId` as a **required field** (Section 6.4):
```typescript
export interface Message {
readonly role: "user" | "agent";
parts: Part[];
messageId: string; // Required - must be a UUID
taskId?: string; // Optional
contextId?: string; // Optional
kind: "message";
}
```
However, the current C# SDK allows developers to construct `AgentMessage` (or `Message`) objects without providing a `messageId`, which results in **spec non-compliant responses** that may be rejected by clients or cause interoperability issues.
### Example of Current Problematic Code
```csharp
await _taskManager.UpdateStatusAsync(taskId, TaskState.InputRequired, new AgentMessage()
{
Parts = [new TextPart() { Text = "When ready say go ahead" }],
},
cancellationToken: cancellationToken);
```
This compiles and runs without errors, but produces a message with a missing or empty `messageId`, violating the A2A spec.
## Impact
- **Spec non-compliance**: Messages without `messageId` violate the A2A protocol
- **Interoperability issues**: Clients expecting spec-compliant messages may reject responses
- **Developer confusion**: No indication that a required field is missing until runtime testing
- **Inconsistent examples**: Some SDK examples (like `EchoAgent.cs`) include `MessageId`, while developers writing new agents may forget to include it
### Describe the solution you'd like
Add compile-time or runtime enforcement for required Message fields. Consider one or more of these approaches:
### Option 1: Use C# 11 `required` Keyword (Preferred)
```csharp
public class AgentMessage
{
public required string MessageId { get; init; }
public required MessageRole Role { get; init; }
public required Part[] Parts { get; init; }
public string? TaskId { get; init; }
public string? ContextId { get; init; }
public string Kind { get; init; } = "message";
}
```
**Benefits:**
- ✅ Compile-time errors when required fields are missing
- ✅ Clear intent in the type system
- ✅ IDE support (IntelliSense shows required fields)
- ✅ No runtime overhead
**Requirement:** Requires C# 11+ (.NET 7+), which the SDK already targets (.NET 8/9)
### Describe alternatives you've considered
### Option 2: Runtime Validation
Add validation in the `TaskManager` or serialization layer:
```csharp
private void ValidateMessage(AgentMessage message)
{
if (string.IsNullOrWhiteSpace(message.MessageId))
{
throw new ArgumentException(
"Message.MessageId is required by A2A specification (Section 6.4). " +
"Assign a unique identifier using: MessageId = Guid.NewGuid().ToString()",
nameof(message));
}
// Validate other required fields...
}
```
### Option 3: Builder Pattern with Validation
Provide a fluent builder that ensures required fields:
```csharp
var message = AgentMessage.Create()
.WithMessageId(Guid.NewGuid().ToString()) // Required
.WithRole(MessageRole.Agent) // Required
.WithParts(new TextPart { Text = "..." }) // Required
.WithTaskId(taskId) // Optional
.WithContextId(contextId) // Optional
.Build();
```
### Option 4: Analyzer Package (Advanced)
Create a Roslyn analyzer that warns at compile-time when `MessageId` is not set during object initialization.
## Recommended Approach
**Combination of Option 1 + Option 2:**
1. Use `required` keyword for compile-time safety (Option 1)
2. Add runtime validation as a safety net (Option 2)
3. Update all SDK examples and documentation to show the correct pattern
This provides defense-in-depth and matches the strictness of the Node.js SDK while leveraging C#'s type system.
## Additional Recommendations
### Also Consider for Optional But Recommended Fields
While `taskId` and `contextId` are optional per the spec schema, **all official spec examples include them** (Section 9.4). Consider:
- Adding XML documentation comments indicating these are "optional but recommended"
- Providing helper methods that make it easy to include them
- Adding analyzer warnings (not errors) when they're omitted
```csharp
///
/// Optional but recommended: Links this message to its parent task.
/// All A2A specification examples include this field.
///
public string? TaskId { get; init; }
```
### Additional context
Used GitHub Copilot for analysis
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct
コントリビューションガイド
評価
この issue はまだ評価されていません。