- name
- agents-sdk-dotnet
- description
- Use when any code imports Microsoft.Agents.Hosting.AspNetCore, Microsoft.Agents.Builder, or related Agents SDK packages, or when the user is building, configuring, or asking questions about a Microsoft 365 Agents SDK agent in C# / .NET. Trigger on questions about appsettings.json, connection configuration, AgentApplication patterns, OAuth sign-in flows, storage backends, cards, streaming, or local testing with the Agents Playground — even if no code exists yet and the user is planning or asking how to get started.
## Overview
The Microsoft 365 Agents SDK builds multichannel agents for Teams, Copilot Studio, and web chat.
| Package | Purpose |
|---|---|
| `Microsoft.Agents.Hosting.AspNetCore` | ASP.NET Core hosting, auth, endpoint mapping |
| `Microsoft.Agents.Authentication.Msal` | MSAL-based auth (client credentials, OBO) |
| `Microsoft.Agents.Builder` | Core: AgentApplication, ITurnContext, ITurnState |
| `Microsoft.Agents.Core` | Models, activity types, channel accounts |
| `Microsoft.Agents.Storage` | IStorage, MemoryStorage |
| `Microsoft.Agents.Extensions.Teams` | Teams-specific models and extensions |
| `Microsoft.Agents.Builder.Dialogs` | Dialog system (waterfall, prompts) |
| `Microsoft.Agents.AI` | AI-specific features |
**Minimal project requires only:** `Microsoft.Agents.Hosting.AspNetCore` + `Microsoft.Agents.Authentication.Msal`. The others are transitive dependencies pulled in automatically.
**Always use the latest non-beta (stable) version.** Do not use `--prerelease` unless specifically needed.
Requires .NET 8+.
## Azure Resources Required
**Microsoft Entra App Registration**
- `ClientId` — Application (client) ID
- `ClientSecret` — Certificates & secrets
- `TenantId` — Directory (tenant) ID
**Azure Bot Resource**
- Messaging endpoint: `https://<your-host>/api/messages`
- Microsoft App ID must match `ClientId`
Local dev: Set `TokenValidation:Enabled` to `false`. No Azure Bot needed until deployment.
## Configuration (appsettings.json)
### Single connection (most common)
**appsettings.json** — no secret here (safe to commit):
```json
{
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "ClientSecret",
"AuthorityEndpoint": "https://login.microsoftonline.com/<tenantId>",
"ClientId": "<appId>",
"Scopes": ["https://api.botframework.com/.default"]
}
}
},
"ConnectionsMap": [
{
"ServiceUrl": "*",
"Connection": "ServiceConnection"
}
],
"TokenValidation": {
"Enabled": true,
"Audiences": ["<appId>"],
"TenantId": "<tenantId>"
}
}
```
**appsettings.Development.json** — secret lives here (excluded via `.gitignore`):
```json
{
"Connections": {
"ServiceConnection": {
"Settings": {
"ClientSecret": "<secret>"
}
}
},
"TokenValidation": {
"Enabled": false
}
}
```
### How ConnectionsMap works
Each entry maps a `ServiceUrl` pattern to a named connection. The first matching entry wins.
- `ServiceUrl: "*"` — matches any service URL (use as the default/fallback)
- Other values are treated as regex patterns
### Multiple connections (different identities per channel)
```json
{
"Connections": {
"MainConn": {
"Settings": {
"AuthType": "ClientSecret",
"AuthorityEndpoint": "https://login.microsoftonline.com/<tenantId>",
"ClientId": "<app-id-1>",
"ClientSecret": "<secret-1>",
"Scopes": ["https://api.botframework.com/.default"]
}
},
"TeamsConn": {
"Settings": {
"AuthType": "ClientSecret",
"AuthorityEndpoint": "https://login.microsoftonline.com/<tenantId>",
"ClientId": "<app-id-2>",
"ClientSecret": "<secret-2>",
"Scopes": ["https://api.botframework.com/.default"]
}
}
},
"ConnectionsMap": [
{
"ServiceUrl": "https://smba.trafficmanager.net/.*",
"Connection": "TeamsConn"
},
{
"ServiceUrl": "*",
"Connection": "MainConn"
}
]
}
```
### Auth type variants
**UserManagedIdentity** (no secret, Azure-hosted only):
```json
{
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "UserManagedIdentity",
"ClientId": "<msi-clientId>",
"Scopes": ["https://api.botframework.com/.default"]
}
}
}
}
```
**FederatedCredentials** (no secret, App Registration + MSI):
```json
{
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "FederatedCredentials",
"AuthorityEndpoint": "https://login.microsoftonline.com/<tenantId>",
"ClientId": "<appId>",
"FederatedClientId": "<msi-clientId>",
"Scopes": ["https://api.botframework.com/.default"]
}
}
}
}
```
### Available connection settings fields
`AuthType`, `AuthorityEndpoint`, `ClientId`, `ClientSecret`, `TenantId`, `FederatedClientId`, `Scopes`, `CertificateSubject`, `CertificateThumbprint`
### Local development (disable token validation)
Put in `appsettings.Development.json` (not `appsettings.json`):
```json
{
"TokenValidation": {
"Enabled": false
}
}
```
## Quick Start
**Prerequisite:** Copy [`AspNetExtensions.cs`](https://github.com/microsoft/Agents-for-net/blob/main/src/samples/Shared/AspNetExtensions.cs) into your project. This provides `AddAgentAspNetAuthentication` for JWT token validation.
**Program.cs:**
```csharp
using Microsoft.Agents.Hosting.AspNetCore;
using Microsoft.Agents.Storage;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient();
builder.AddAgent<MyAgent>();
builder.Services.AddSingleton<IStorage, MemoryStorage>();
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapAgentRootEndpoint();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());
app.Run();
```
**MyAgent.cs** — put the `AgentApplication` subclass in its own file (see [AgentApplication Patterns](#agentapplication-patterns) below for routing, state, and auth examples).
**Properties/launchSettings.json:**
```json
{
"profiles": {
"MyAgent": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:3979;http://localhost:3978"
}
}
}
```
Run: `dotnet run`
**.gitignore** — ensure `appsettings.Development.json` is excluded (it contains secrets):
```
appsettings.Development.json
```
**Teams app manifest (if targeting Teams):** Copy the `appManifest/` folder from [`Agents-for-net/src/samples/EmptyAgent/appManifest`](https://github.com/microsoft/Agents-for-net/tree/main/src/samples/EmptyAgent/appManifest) into your project. Then update `manifest.json`:
- Replace all `${{AAD_APP_CLIENT_ID}}` with your bot's Client ID
- Set `name.short` and `name.full` to your bot's display name
## Program.cs Structure
### Minimal setup
```csharp
using Microsoft.Agents.Hosting.AspNetCore;
using Microsoft.Agents.Storage;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient();
builder.AddAgent<MyAgent>();
builder.Services.AddSingleton<IStorage, MemoryStorage>();
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapAgentRootEndpoint();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());
app.Run();
```
### With proactive messaging endpoints
```csharp
app.MapAgentProactiveEndpoints<MyAgent>(requireAuth: !app.Environment.IsDevelopment());
```
### With custom routes
```csharp
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapAgentRootEndpoint();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());
app.MapGet("/health", () => Results.Json(new { ok = true }));
app.Run();
```
### Agent class decorators
```csharp
[Agent(name: "MyAgent", description: "A helpful agent", version: "1.0")]
[AgentInterface(protocol: AgentTransportProtocol.ActivityProtocol, path: "/api/messages")]
public class MyAgent : AgentApplication
{
// ...
}
```
### Important: AddAgent vs AddAgentApplicationOptions
- `builder.AddAgent<T>()` — registers the agent class. **Always required.**
- Do **not** call `AddAgentApplicationOptions()` separately unless you have a specific reason — `AddAgent` handles it.
### Important: MapAgentEndpoints vs MapAgentApplicationEndpoints
- `MapAgentApplicationEndpoints` — for `AgentApplication` subclasses (modern pattern)
- `MapAgentEndpoints` — for `ActivityHandler` / `TeamsActivityHandler` compat layer bots
Using the wrong one causes runtime routing failures with no clear error.
## Validating Your Configuration
### 1. Validate bot credentials (ClientId / ClientSecret / TenantId)
```bash
curl -s -X POST \
"https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" \
-d "grant_type=client_credentials\
&client_id=$clientId\
&client_secret=$clientSecret\
&scope=https://api.botframework.com/.default" \
| jq '{token_type, expires_in, error, error_description}'
```
Common errors:
- `AADSTS700016` — `ClientId` not found in tenant (wrong ID or wrong tenant)
- `AADSTS7000215` — invalid `ClientSecret` (expired or incorrect)
- `AADSTS90002` — `TenantId` not found
### 2. Validate the agent is running and reachable
```bash
curl -s -o /dev/null -w "%{http_code}" \
-X POST http://localhost:3978/api/messages \
-H "Content-Type: application/json" \
-d '{}'
```
- `401` — agent is running; JWT auth rejected the empty request (expected)
- `000` or connection refused — agent is not running or wrong port
- `200` — agent is running with auth disabled (local dev with `TokenValidation:Enabled = false`)
### 3. Validate an OAuth connection name
OAuth connection names can only be tested end-to-end via:
**Azure Portal → Your Bot Resource → Settings → OAuth Connection Settings → [your connection] → Test Connection**
## Local Testing with Agents Playground
The Agents Playground lets you test locally without deploying to Azure.
**Install:**
```bash
npm install -g agentsplayground
```
**Run against an anonymous agent:**
```bash
agentsplayground -c emulator
```
Then start your agent separately with `dotnet run`.
**With authentication:**
```bash
agentsplayground -c msteams \
--client-id <your-app-id> \
--client-secret <your-secret> \
--tenant-id <your-tenant-id>
```
**Channel options** (`-c`): `msteams`, `webchat`, `directline`, `emulator`, `agents`
If the playground connects but messages don't get responses, add a fallback handler to confirm:
```csharp
OnActivity(ActivityTypes.Message, async (ctx, state, ct) =>
{
await ctx.SendActivityAsync($"Echo: {ctx.Activity.Text}", cancellationToken: ct);
}, rank: RouteRank.Last);
```
## AgentApplication Patterns
**Routing**
```csharp
OnMessage("/cmd", handler); // exact command
Voir sur GitHub