Microsoft Agent Framework Agents Created: 16 Feb 2026 Updated: 16 Feb 2026

Message Types in the Microsoft Agent Framework

Input and output from agents in the Microsoft Agent Framework are represented as messages. Each message is subdivided into content items. The framework uses the ChatMessage class and the AIContent hierarchy from the Microsoft.Extensions.AI abstractions.

Understanding these types is essential because agents don’t just return plain text — responses may contain tool calls, binary data, URIs, and more. Knowing how to construct input messages and inspect output content items is the foundation of working with the framework.

ChatMessage — The Message Container

Every interaction with an agent revolves around ChatMessage objects. A ChatMessage has two key properties:

  1. Role — who created the message (ChatRole.User, ChatRole.Assistant, ChatRole.System, ChatRole.Tool)
  2. Contents — a collection of AIContent items that make up the message’s payload
// A simple user message with text
var message = new ChatMessage(ChatRole.User, "What is the weather like in Amsterdam?");

Console.WriteLine(message.Role); // "user"
Console.WriteLine(message.Contents.Count); // 1 (a single TextContent)

AIContent — The Base Content Class

All content items in a ChatMessage inherit from AIContent. The framework provides several built-in subclasses, and providers can add their own.

TypeDescriptionDirection
TextContentTextual content — the most common type. Contains the text result from an agent or text input from a user.Input & Output
DataContentBinary content for images, audio, or video data.Input & Output
UriContentA URL pointing at hosted content such as an image, audio, or video.Input & Output
FunctionCallContentA request by the model to invoke a function tool.Output
FunctionResultContentThe result of a function tool invocation.Output

TextContent — The Most Common Type

TextContent is what you’ll encounter most often. When you send a text prompt, the framework wraps it in a TextContent. When the agent responds with text, that’s also a TextContent.

// Input: a user message with TextContent
var input = new ChatMessage(ChatRole.User, "What is the weather like in Amsterdam?");

// Output: the agent's response contains TextContent
AgentResponse response = await agent.RunAsync(input);

// The .Text property aggregates ALL TextContent from ALL messages
Console.WriteLine(response.Text);

The .Text shortcut works by iterating through every ChatMessage in response.Messages and concatenating all TextContent items. You can do this manually to understand what happens under the hood:

var manualText = new StringBuilder();
foreach (var msg in response.Messages)
{
foreach (var content in msg.Contents)
{
if (content is TextContent tc)
{
manualText.Append(tc.Text);
}
}
}

// manualText.ToString() == response.Text → true

DataContent — Binary Data

DataContent carries binary data with a MIME type. Use it to send images, audio, or video to vision/audio-capable models:

byte[] imageBytes = File.ReadAllBytes("photo.png");

var message = new ChatMessage(ChatRole.User,
[
new TextContent("What do you see in this image?"),
new DataContent(imageBytes, "image/png")
]);

// The message now has 2 content items: TextContent + DataContent

DataContent is useful when you have the actual file bytes in memory. For hosted content, prefer UriContent instead.

UriContent — URLs to Hosted Content

UriContent points at hosted resources like images or documents. The model will fetch the content from the URL during processing:

var message = new ChatMessage(ChatRole.User,
[
new TextContent("Describe this photo:"),
new UriContent("https://example.com/amsterdam-canal.jpg", "image/jpeg")
]);

Use UriContent when working with hosted assets to avoid embedding large binary data directly in the message.

FunctionCallContent & FunctionResultContent — Tool Interactions

When an agent has access to function tools, responses may include FunctionCallContent (a request to call a function) and FunctionResultContent (the result of that call). These appear in the Contents collection alongside TextContent.

foreach (var message in response.Messages)
{
foreach (var content in message.Contents)
{
switch (content)
{
case FunctionCallContent fc:
Console.WriteLine($"Tool call: {fc.Name}({fc.Arguments})");
Console.WriteLine($"Call ID: {fc.CallId}");
break;

case FunctionResultContent fr:
Console.WriteLine($"Tool result for call {fr.CallId}: {fr.Result}");
break;
}
}
}

These content types are not included in the .Text property. If you only use .Text, you’ll get the text result; tool call details must be inspected separately through .Contents.

Inspecting All Content Types with Pattern Matching

The recommended way to handle the full range of content types is C# pattern matching with a switch expression:

foreach (var message in response.Messages)
{
Console.WriteLine($"Role: {message.Role}, Items: {message.Contents.Count}");

foreach (var content in message.Contents)
{
switch (content)
{
case TextContent tc:
Console.WriteLine($" TextContent: {tc.Text}");
break;
case DataContent dc:
Console.WriteLine($" DataContent: {dc.MediaType}, {dc.Data.Length} bytes");
break;
case UriContent uc:
Console.WriteLine($" UriContent: {uc.Uri}");
break;
case FunctionCallContent fc:
Console.WriteLine($" FunctionCallContent: {fc.Name}({fc.Arguments})");
break;
case FunctionResultContent fr:
Console.WriteLine($" FunctionResultContent: {fr.Result}");
break;
case UsageContent us:
Console.WriteLine($" UsageContent: In={us.Details.InputTokenCount}, Out={us.Details.OutputTokenCount}");
break;
default:
Console.WriteLine($" {content.GetType().Name} (custom/unknown)");
break;
}
}
}

Content Types in Streaming Responses

Streaming chunks (AgentResponseUpdate) also carry content items via update.Contents. Each chunk may contain a TextContent fragment, a tool call, or other content:

var contentTypeCounts = new Dictionary<string, int>();

await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(prompt))
{
Console.Write(update.Text); // TextContent shortcut

foreach (var content in update.Contents)
{
var typeName = content.GetType().Name;
contentTypeCounts[typeName] = contentTypeCounts.GetValueOrDefault(typeName) + 1;
}
}

// Print content type distribution
foreach (var (type, count) in contentTypeCounts)
Console.WriteLine($" {type}: {count}");

Building Multi-Content Input Messages

A ChatMessage can contain multiple content items. This is how you send multimodal input (text + image) or provide conversation history:

Multimodal Input

// Text + image via URL
var message = new ChatMessage(ChatRole.User,
[
new TextContent("Based on this photo, what season is it?"),
new UriContent("https://example.com/amsterdam.jpg", "image/jpeg")
]);

// Text + image via binary data
var message2 = new ChatMessage(ChatRole.User,
[
new TextContent("What do you see?"),
new DataContent(imageBytes, "image/png")
]);

Conversation History

var history = new List<ChatMessage>
{
new(ChatRole.User, "What is the weather in Amsterdam in December?"),
};

var firstResponse = await agent.RunAsync(history);

// Add the agent's response and a follow-up question
history.AddRange(firstResponse.Messages);
history.Add(new ChatMessage(ChatRole.User, "Should I pack an umbrella?"));

var followUp = await agent.RunAsync(history);

Content Type Reference

TypeKey PropertiesIn .Text?Typical Use
TextContent.TextYesUser prompts, agent answers
DataContent.Data, .MediaTypeNoSending images/audio/video as bytes
UriContent.Uri, .MediaTypeNoPointing at hosted resources
FunctionCallContent.Name, .CallId, .ArgumentsNoAgent requests a tool invocation
FunctionResultContent.CallId, .ResultNoResult of a tool invocation
UsageContent.Details.InputTokenCount, .Details.OutputTokenCountNoToken usage statistics

Key Takeaways

  1. Messages are containers, content items are the payload — a ChatMessage holds a Role and a collection of AIContent items.
  2. TextContent is the most common type — it carries both user input and agent output text. The .Text shortcut aggregates all TextContent items.
  3. DataContent and UriContent enable multimodal input — send images, audio, or video to capable models via bytes or URLs.
  4. FunctionCallContent and FunctionResultContent represent tool interactions — these appear when the agent invokes function tools and are not included in .Text.
  5. Use pattern matching on .Contents — C# pattern matching is the idiomatic way to handle the different AIContent subclasses in both non-streaming and streaming responses.


Share this lesson: