a2aproject / a2aproject/a2a-dotnet

[Feat]: Enforce required Message fields at compile-time or runtime to ensure A2A spec compliance

Offen
#198 0 Kommentare 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
C#
Sterne
262
Forks
64
Ø Merge
5 T. 2 Std.
Gemergte PRs (30 T.)
31

Beschreibung

### 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

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.