Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 18 additions & 22 deletions samples/AgentServer/EchoAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ public class EchoAgent
public void Attach(ITaskManager taskManager)
{
taskManager.OnMessageReceived = ProcessMessageAsync;
taskManager.OnAgentCardQuery = GetAgentCardAsync;
}

private Task<A2AResponse> ProcessMessageAsync(MessageSendParams messageSendParams, CancellationToken cancellationToken)
Expand All @@ -34,29 +33,26 @@ private Task<A2AResponse> ProcessMessageAsync(MessageSendParams messageSendParam
return Task.FromResult<A2AResponse>(message);
}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)
public AgentCard Card
Copy link
Collaborator

@Blackhex Blackhex Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the library hint its users how agent implementations should look like by providing abstract agent class demonstrating what are the common patterns? Such class could either have AgentCard Card as abstract property or implement AgentCardProvider. app.MapWellKnownAgentCard() could then accept agent instance instead of card, attaching agent instance to TaskManager instance could be simplified, etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the original design goals was to impose no requirements on the implementation of the agent. An existing agent should be able to adapted to "talk A2A". Requiring an agent to implement a specific base class would force people to create a "proxy agent" to adapt existing agents. That feels pretty intrusive.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for explaining the original intent. The suggestion was meant as an option or a showcase of "good practice" not a requirement though.

{
if (cancellationToken.IsCancellationRequested)
get
{
return Task.FromCanceled<AgentCard>(cancellationToken);
var capabilities = new AgentCapabilities()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not inlining it into AgentCard instance initializer? (The same applies to other agent implementations in samples/ folder.)

{
Streaming = true,
PushNotifications = false,
};

return new AgentCard()
{
Name = "Echo Agent",
Description = "Agent which will echo every message it receives.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
};
}

var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};

return Task.FromResult(new AgentCard()
{
Name = "Echo Agent",
Description = "Agent which will echo every message it receives.",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
});
}
}
40 changes: 18 additions & 22 deletions samples/AgentServer/EchoAgentWithTasks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public void Attach(ITaskManager taskManager)
_taskManager = taskManager;
taskManager.OnTaskCreated = ProcessMessageAsync;
taskManager.OnTaskUpdated = ProcessMessageAsync;
taskManager.OnAgentCardQuery = GetAgentCardAsync;
}

private async Task ProcessMessageAsync(AgentTask task, CancellationToken cancellationToken)
Expand Down Expand Up @@ -41,30 +40,27 @@ private async Task ProcessMessageAsync(AgentTask task, CancellationToken cancell
cancellationToken: cancellationToken);
}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)
public AgentCard Card
{
if (cancellationToken.IsCancellationRequested)
get
{
return Task.FromCanceled<AgentCard>(cancellationToken);
}

var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};
var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};

return Task.FromResult(new AgentCard()
{
Name = "Echo Agent",
Description = "Agent which will echo every message it receives.",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
});
return new AgentCard()
{
Name = "Echo Agent",
Description = "Agent which will echo every message it receives.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
};
}
}

private static TaskState? GetTargetStateFromMetadata(Dictionary<string, JsonElement>? metadata)
Expand Down
8 changes: 4 additions & 4 deletions samples/AgentServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,30 @@
var echoAgent = new EchoAgent();
echoAgent.Attach(taskManager);
app.MapA2A(taskManager, "/echo");
app.MapWellKnownAgentCard(taskManager, "/echo");
app.MapWellKnownAgentCard(echoAgent.Card, "/echo");
app.MapHttpA2A(taskManager, "/echo");
break;

case "echotasks":
var echoAgentWithTasks = new EchoAgentWithTasks();
echoAgentWithTasks.Attach(taskManager);
app.MapA2A(taskManager, "/echotasks");
app.MapWellKnownAgentCard(taskManager, "/echotasks");
app.MapWellKnownAgentCard(echoAgentWithTasks.Card, "/echotasks");
app.MapHttpA2A(taskManager, "/echotasks");
break;

case "researcher":
var researcherAgent = new ResearcherAgent();
researcherAgent.Attach(taskManager);
app.MapA2A(taskManager, "/researcher");
app.MapWellKnownAgentCard(taskManager, "/researcher");
app.MapWellKnownAgentCard(researcherAgent.Card, "/researcher");
break;

case "speccompliance":
var specComplianceAgent = new SpecComplianceAgent();
specComplianceAgent.Attach(taskManager);
app.MapA2A(taskManager, "/speccompliance");
app.MapWellKnownAgentCard(taskManager, "/speccompliance");
app.MapWellKnownAgentCard(specComplianceAgent.Card, "/speccompliance");
break;

default:
Expand Down
40 changes: 18 additions & 22 deletions samples/AgentServer/ResearcherAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public void Attach(ITaskManager taskManager)
var message = ((TextPart?)task.History?.Last()?.Parts?.FirstOrDefault())?.Text ?? string.Empty;
await InvokeAsync(task.Id, message, cancellationToken);
};
_taskManager.OnAgentCardQuery = GetAgentCardAsync;
}

// This is the main entry point for the agent. It is called when a task is created or updated.
Expand Down Expand Up @@ -143,29 +142,26 @@ await _taskManager.ReturnArtifactAsync(
_agentStates[taskId] = AgentState.WaitingForFeedbackOnPlan;
}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)
public AgentCard Card
{
if (cancellationToken.IsCancellationRequested)
get
{
return Task.FromCanceled<AgentCard>(cancellationToken);
}

var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};
var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};

return Task.FromResult(new AgentCard()
{
Name = "Researcher Agent",
Description = "Agent which conducts research.",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
});
return new AgentCard()
{
Name = "Researcher Agent",
Description = "Agent which conducts research.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
};
}
}
}
40 changes: 18 additions & 22 deletions samples/AgentServer/SpecComplianceAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public class SpecComplianceAgent

public void Attach(ITaskManager taskManager)
{
taskManager.OnAgentCardQuery = GetAgentCard;
taskManager.OnTaskCreated = OnTaskCreatedAsync;
taskManager.OnTaskUpdated = OnTaskUpdatedAsync;
_taskManager = taskManager;
Expand Down Expand Up @@ -39,29 +38,26 @@ private async Task OnTaskUpdatedAsync(AgentTask task, CancellationToken cancella
}
}

private Task<AgentCard> GetAgentCard(string agentUrl, CancellationToken cancellationToken)
public AgentCard Card
{
if (cancellationToken.IsCancellationRequested)
get
{
return Task.FromCanceled<AgentCard>(cancellationToken);
var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};

return new AgentCard()
{
Name = "A2A Specification Compliance Agent",
Description = "Agent to run A2A specification compliance tests.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
};
}

var capabilities = new AgentCapabilities()
{
Streaming = true,
PushNotifications = false,
};

return Task.FromResult(new AgentCard()
{
Name = "A2A Specification Compliance Agent",
Description = "Agent to run A2A specification compliance tests.",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [],
});
}
}
2 changes: 1 addition & 1 deletion samples/SemanticKernelAgent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
var taskManager = new TaskManager();
agent.Attach(taskManager);
app.MapA2A(taskManager, string.Empty);
app.MapWellKnownAgentCard(taskManager, string.Empty);
app.MapWellKnownAgentCard(SemanticKernelTravelAgent.Card, string.Empty);

await app.RunAsync();
60 changes: 28 additions & 32 deletions samples/SemanticKernelAgent/SemanticKernelTravelAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ public void Attach(ITaskManager taskManager)
_taskManager = taskManager;
taskManager.OnTaskCreated = ExecuteAgentTaskAsync;
taskManager.OnTaskUpdated = ExecuteAgentTaskAsync;
taskManager.OnAgentCardQuery = GetAgentCardAsync;
}

public async Task ExecuteAgentTaskAsync(AgentTask task, CancellationToken cancellationToken)
Expand Down Expand Up @@ -168,43 +167,40 @@ public async Task ExecuteAgentTaskAsync(AgentTask task, CancellationToken cancel
await _taskManager.UpdateStatusAsync(task.Id, TaskState.Completed, cancellationToken: cancellationToken);
}

public static Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)
public static AgentCard Card
{
if (cancellationToken.IsCancellationRequested)
get
{
return Task.FromCanceled<AgentCard>(cancellationToken);
}

var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};

var skillTripPlanning = new AgentSkill()
{
Id = "trip_planning_sk",
Name = "Semantic Kernel Trip Planning",
Description = "Handles comprehensive trip planning, including currency exchanges, itinerary creation, sightseeing, dining recommendations, and event bookings using Frankfurter API for currency conversions.",
Tags = ["trip", "planning", "travel", "currency", "semantic-kernel"],
Examples =
[
"I am from Korea. Plan a budget-friendly day trip to Dublin including currency exchange.",
var skillTripPlanning = new AgentSkill()
{
Id = "trip_planning_sk",
Name = "Semantic Kernel Trip Planning",
Description = "Handles comprehensive trip planning, including currency exchanges, itinerary creation, sightseeing, dining recommendations, and event bookings using Frankfurter API for currency conversions.",
Tags = ["trip", "planning", "travel", "currency", "semantic-kernel"],
Examples =
[
"I am from Korea. Plan a budget-friendly day trip to Dublin including currency exchange.",
"I am from Korea. What's the exchange rate and recommended itinerary for visiting Galway?",
],
};
};

return Task.FromResult(new AgentCard()
{
Name = "SK Travel Agent",
Description = "Semantic Kernel-based travel agent providing comprehensive trip planning services including currency exchange and personalized activity planning.",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [skillTripPlanning],
});
return new AgentCard()
{
Name = "SK Travel Agent",
Description = "Semantic Kernel-based travel agent providing comprehensive trip planning services including currency exchange and personalized activity planning.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [skillTripPlanning],
};
}
}

#region private
Expand Down
Loading
Loading