DEV Community

Cover image for Integrate a Copilot Studio Agent into an Existing .NET App Using Agents SDK
Vimal
Vimal

Posted on

Integrate a Copilot Studio Agent into an Existing .NET App Using Agents SDK

Introduction

If your application already has an API, the fastest way to add AI is to integrate an agent created using Microsoft Copilot studio.
Microsoft Copilot studio is an agent building platform that offers integration of agents into your applications using Microsoft Agents SDK.

This walkthrough shows how to wire a Copilot Studio agent into an existing .NET application using the Microsoft Agents SDK approach.

Pre-requisites

  • Agent created using Microsoft Copilot studio
  • Agent is attached to a knowledge source (SharePoint, Web, Azure Search, 3rd party APIs)
  • .NET API with an app registration having API permissions Copilot Studio.Copilots.Invoke

TL;DR

  • Add CopilotStudioDocumentRetrievalService to your existing interface.
  • Retrieve Agent settings from Copilot Studio
  • Support both user passthrough and OBO token flow.
  • Stream agent output through SSE for chat-like UX.

Architecture: Existing App + Copilot Studio Agent

The architecture consists of three main components

  1. .NET API with SSE Endpoint
    Create an endpoint that provides a response as and when the API receives a response from Copilot agent. This is very important to provide notifications to the client as when response is available.

  2. Retrieval Layer using Agents SDK (This is the Copilot client)
    This layer invokes the Copilot agent using the Agents SDK leveraging the configuration settings for the agent.

  3. A Copilot Agent created using Microsoft Copilot Studio attached to a knowledge source that acts as the grounding data.

Step 1: Add the SDK dependencies

  • Microsoft.Agents.CopilotStudio.Client Microsoft 365 Agents SDK for invoking Copilot agent
  • Microsoft.Identity.Client for authentication
<ItemGroup>
  <PackageReference Include="Microsoft.Agents.CopilotStudio.Client" Version="1.3.176" />
  <PackageReference Include="Microsoft.Identity.Client" Version="4.78.0" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

Step 2: Retrieve Agent settings from Copilot studio

Navigate to the Copilot agent, Select Settings > Advanced. Under Metadata, you need the Environment ID, Tenant ID, and Schema name of the agent. Copy these metadata values and add it to app settings.


{
  "Retrieval": {
    "Provider": "CopilotStudio"
  },
  "CopilotStudio": {
    "EnvironmentId": "<env-id>",
    "SchemaName": "<agent-schema-name>",
    "AuthMode": "User",
    "TenantId": "<tenant-id>",
    "AppClientId": "<app-client-id>",
    "AppClientSecret": ""
  }
}
Enter fullscreen mode Exit fullscreen mode

Retrieve and add AppClientId (client Id) and AppClientSecret (secret) of the App registration that has API permissions to invoke Copilot agent. App registration requires the following API permissions:

Power Platform API > CopilotStudio.Copilots.Invoke

Add strongly typed Copilot settings for configuration using Options pattern

public class CopilotStudioOptions
{
    public string EnvironmentId { get; set; } = string.Empty;
    public string SchemaName { get; set; } = string.Empty;
    public string AuthMode { get; set; } = "App";
    public string TenantId { get; set; } = string.Empty;
    public string AppClientId { get; set; } = string.Empty;
    public string AppClientSecret { get; set; } = string.Empty;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Register DI for CopilotStudio

builder.Services.Configure<CopilotStudioOptions>(
    builder.Configuration.GetSection("CopilotStudio"));

builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();

builder.Services.AddKeyedScoped<IDocumentStreamingService, CopilotStudioDocumentRetrievalService>("Copilot");

Enter fullscreen mode Exit fullscreen mode

Step 4: Initialize Copilot client

Initialize the Copilot client using the configuration values that was originally copied from the Copilot agent metadata.

connectionSettings = new ConnectionSettings
{
    EnvironmentId = options.EnvironmentId,
    SchemaName = options.AgentIdentifier,
    Cloud = PowerPlatformCloud.Prod,
    CopilotAgentType = AgentType.Published
};

scope = CopilotClient.ScopeFromSettings(connectionSettings);
Enter fullscreen mode Exit fullscreen mode

Use AskQuestionAsync(...) to stream activity text and map it into your existing result model.

Step 5: Intialize conversation with Copilot

Initialize a conversation session with Copilot before asking a question:

This is a setup phase before actually asking a question. Copilot needs to establish a conversation context (session ID, connection state, etc.) before handling queries. The code waits for this initialization to complete, then proceeds to AskQuestionAsync to get actual results.


await foreach (var startupActivity in copilotClient.StartConversationAsync(emitStartConversationEvent: true, cancellationToken: cancellationToken))
Enter fullscreen mode Exit fullscreen mode
  • StartConversationAsync to initialize a new conversation session with Copilot
  • emitStartConversationEvent: true tells Copilot to emit initialization events (e.g., handshake messages, acknowledgments)
  • await foreach iterates over the stream of activities Copilot sends during startup
  • cancellationToken allows the operation to be cancelled if needed

Once the session is initialized use AskQuestionAsync that takes the query parameter to get actual results, use await to asynchronously iterate over activities returned from Copilot as they arrive

await foreach (var activity in copilotClient.AskQuestionAsync(query, conversationId: null, ct: cancellationToken))
Enter fullscreen mode Exit fullscreen mode

Step 6: Expose SSE endpoint for near real-time responses

Add SSE specific code in the controller to process and send real-time data to client

Response.ContentType = "text/event-stream"
Tells the client this HTTP response is an SSE stream, so data can arrive continuously instead of all at once.

Response.Headers.Append("Cache-Control", "no-cache")
Prevents caching so the browser/proxy always receives fresh streamed events.

await foreach (var activity in retrievalService.StreamActivitiesAsync(...))
Reads activities one-by-one asynchronously from the service as they become available.

JsonSerializer.Serialize(activity)
Converts each activity object into JSON text.

await Response.WriteAsync($"data: {json}\n\n", ...)
Writes each item in SSE format.
Each SSE message must start with data: and end with a blank line (\n\n), which signals one complete event to the client.

Overall, this is a streaming endpoint that pushes incremental activity updates to the frontend in real time.

Response.ContentType = "text/event-stream";
Response.Headers.Append("Cache-Control", "no-cache");

await foreach (var activity in retrievalService.StreamActivitiesAsync(request.Query, top, cancellationToken))
{
    var json = JsonSerializer.Serialize(activity);
    await Response.WriteAsync($"data: {json}\n\n", cancellationToken);
}
Enter fullscreen mode Exit fullscreen mode

Quick test commands

curl -X POST "https://localhost:5001/api/query" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <user-token>" \
  -d '{"query":"Summarize the onboarding flow","top":5}'
Enter fullscreen mode Exit fullscreen mode
curl -N "https://localhost:5001/api/query/stream?query=What%20changed%20in%20the%20last%20release%3F&agentType=Copilot"
Enter fullscreen mode Exit fullscreen mode

Final thought

Copilot Studio integration is the easiest to introduce AI capabilities to existing application. Use Copilot studio to create agents and integrate the agent using Microsoft Agents SDK.

Top comments (0)