Skip to content

Commit eaabcae

Browse files
feat: add Microsoft Agent Framework to integrations docs (Python) (CopilotKit#2734)
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
1 parent 1a0d124 commit eaabcae

12 files changed

Lines changed: 987 additions & 644 deletions

File tree

docs/content/docs/microsoft-agent-framework/agent-app-context.mdx

Lines changed: 91 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -56,73 +56,102 @@ This context can then be shared with your AG-UI server and agent logic.
5656
### Consume the data in your AG-UI server
5757
The `context` you register on the frontend is forwarded to your AG-UI server in `ChatOptions.AdditionalProperties["ag_ui_context"]`. Use middleware to access this context and inject it into the agent's conversation.
5858

59-
```csharp title="Program.cs"
60-
using System.Runtime.CompilerServices;
61-
using System.Text;
62-
using Azure.AI.OpenAI;
63-
using Azure.Identity;
64-
using Microsoft.Agents.AI;
65-
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
66-
using Microsoft.AspNetCore.Builder;
67-
using Microsoft.Extensions.AI;
68-
69-
var builder = WebApplication.CreateBuilder(args);
70-
builder.Services.AddAGUI();
71-
var app = builder.Build();
72-
73-
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]!;
74-
string deployment = builder.Configuration["AZURE_OPENAI_DEPLOYMENT_NAME"]!;
75-
76-
// Create the base agent
77-
AIAgent baseAgent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
78-
.GetChatClient(deployment)
79-
.CreateAIAgent(
80-
name: "AGUIAssistant",
81-
instructions: "You are a helpful assistant. Use the provided context about colleagues to answer questions.");
82-
83-
// Wrap the agent with middleware to inject context
84-
AIAgent agent = baseAgent
85-
.AsBuilder()
86-
.Use(runFunc: null, runStreamingFunc: InjectContextMiddleware)
87-
.Build();
88-
89-
// Map the AG-UI endpoint
90-
app.MapAGUI("/", agent);
91-
await app.RunAsync();
92-
93-
// Middleware to inject useCopilotReadable context as a system message
94-
async IAsyncEnumerable<AgentRunResponseUpdate> InjectContextMiddleware(
95-
IEnumerable<ChatMessage> messages,
96-
AgentThread? thread,
97-
AgentRunOptions? options,
98-
AIAgent innerAgent,
99-
CancellationToken cancellationToken)
100-
{
101-
// Extract context from AG-UI additional properties and inject if present
102-
if (options is ChatClientAgentRunOptions { ChatOptions.AdditionalProperties: { } properties } &&
103-
properties.TryGetValue("ag_ui_context", out KeyValuePair<string, string>[]? context) &&
104-
context?.Length > 0)
59+
<Tabs groupId="language" items={['.NET', 'Python']}>
60+
<Tab value=".NET">
61+
```csharp title="Program.cs"
62+
using System.Runtime.CompilerServices;
63+
using System.Text;
64+
using Azure.AI.OpenAI;
65+
using Azure.Identity;
66+
using Microsoft.Agents.AI;
67+
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
68+
using Microsoft.AspNetCore.Builder;
69+
using Microsoft.Extensions.AI;
70+
71+
var builder = WebApplication.CreateBuilder(args);
72+
builder.Services.AddAGUI();
73+
var app = builder.Build();
74+
75+
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]!;
76+
string deployment = builder.Configuration["AZURE_OPENAI_DEPLOYMENT_NAME"]!;
77+
78+
// Create the base agent
79+
AIAgent baseAgent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
80+
.GetChatClient(deployment)
81+
.CreateAIAgent(
82+
name: "AGUIAssistant",
83+
instructions: "You are a helpful assistant. Use the provided context about colleagues to answer questions.");
84+
85+
// Wrap the agent with middleware to inject context
86+
AIAgent agent = baseAgent
87+
.AsBuilder()
88+
.Use(runFunc: null, runStreamingFunc: InjectContextMiddleware)
89+
.Build();
90+
91+
// Map the AG-UI endpoint
92+
app.MapAGUI("/", agent);
93+
await app.RunAsync();
94+
95+
// Middleware to inject useCopilotReadable context as a system message
96+
async IAsyncEnumerable<AgentRunResponseUpdate> InjectContextMiddleware(
97+
IEnumerable<ChatMessage> messages,
98+
AgentThread? thread,
99+
AgentRunOptions? options,
100+
AIAgent innerAgent,
101+
CancellationToken cancellationToken)
105102
{
106-
var contextBuilder = new StringBuilder();
107-
contextBuilder.AppendLine("The following context from the user's application is available:");
108-
foreach (var item in context)
103+
// Extract context from AG-UI additional properties and inject if present
104+
if (options is ChatClientAgentRunOptions { ChatOptions.AdditionalProperties: { } properties } &&
105+
properties.TryGetValue("ag_ui_context", out KeyValuePair<string, string>[]? context) &&
106+
context?.Length > 0)
109107
{
110-
contextBuilder.AppendLine($"- {item.Key}: {item.Value}");
108+
var contextBuilder = new StringBuilder();
109+
contextBuilder.AppendLine("The following context from the user's application is available:");
110+
foreach (var item in context)
111+
{
112+
contextBuilder.AppendLine($"- {item.Key}: {item.Value}");
113+
}
114+
115+
var contextMessage = new ChatMessage(
116+
ChatRole.System,
117+
[new TextContent(contextBuilder.ToString())]);
118+
119+
messages = messages.Append(contextMessage);
111120
}
112121

113-
var contextMessage = new ChatMessage(
114-
ChatRole.System,
115-
[new TextContent(contextBuilder.ToString())]);
116-
117-
messages = messages.Append(contextMessage);
118-
}
119-
120-
await foreach (var update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken))
121-
{
122-
yield return update;
122+
await foreach (var update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken))
123+
{
124+
yield return update;
125+
}
123126
}
124-
}
125-
```
127+
```
128+
</Tab>
129+
<Tab value="Python">
130+
```python title="agent/src/agent.py"
131+
132+
from agent_framework import ChatAgent, ChatClientProtocol
133+
from agent_framework.ag_ui import AgentFrameworkAgent
134+
135+
136+
def create_agent(chat_client: ChatClientProtocol) -> AgentFrameworkAgent:
137+
"""
138+
Minimal agent for agent app context demo (frontend context is forwarded automatically).
139+
"""
140+
base_agent = ChatAgent(
141+
name="sample_agent",
142+
instructions="You are a helpful assistant.",
143+
chat_client=chat_client,
144+
)
145+
146+
return AgentFrameworkAgent(
147+
agent=base_agent,
148+
name="CopilotKitMicrosoftAgentFrameworkAgent",
149+
description="Assistant using app context forwarded from the frontend.",
150+
require_confirmation=False,
151+
)
152+
```
153+
</Tab>
154+
</Tabs>
126155

127156
<Callout type="info">
128157
Context registered with `useCopilotReadable` is automatically forwarded by the AG-UI protocol in `ChatOptions.AdditionalProperties["ag_ui_context"]` as an array of key-value pairs (description → value). The middleware extracts and injects this context into the conversation.

docs/content/docs/microsoft-agent-framework/auth.mdx

Lines changed: 127 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,63 +28,134 @@ Pass your authentication token via the `headers` prop:
2828

2929
## Backend Setup
3030

31-
Configure JWT bearer authentication in your AG-UI server:
31+
Configure authentication in your AG-UI server:
3232

33-
```csharp title="Program.cs"
34-
using Microsoft.Agents.AI;
35-
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
36-
using Microsoft.AspNetCore.Authentication.JwtBearer;
37-
using OpenAI;
33+
<Tabs groupId="language" items={['.NET', 'Python']}>
34+
<Tab value=".NET">
35+
```csharp title="Program.cs"
36+
using Microsoft.Agents.AI;
37+
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
38+
using Microsoft.AspNetCore.Authentication.JwtBearer;
39+
using OpenAI;
3840

39-
var builder = WebApplication.CreateBuilder(args);
41+
var builder = WebApplication.CreateBuilder(args);
4042

41-
// Configure JWT authentication
42-
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
43-
.AddJwtBearer(options =>
44-
{
45-
options.Authority = builder.Configuration["JwtAuthority"];
46-
options.Audience = builder.Configuration["JwtAudience"];
47-
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
43+
// Configure JWT authentication
44+
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
45+
.AddJwtBearer(options =>
4846
{
49-
ValidateIssuer = true,
50-
ValidateAudience = true,
51-
ValidateLifetime = true,
52-
ValidateIssuerSigningKey = true
53-
};
54-
});
55-
56-
builder.Services.AddAuthorization();
57-
58-
var app = builder.Build();
59-
60-
app.UseAuthentication();
61-
app.UseAuthorization();
62-
63-
// Create and map your agent
64-
string githubToken = builder.Configuration["GitHubToken"]!;
65-
var openAI = new OpenAIClient(
66-
new System.ClientModel.ApiKeyCredential(githubToken),
67-
new OpenAIClientOptions { Endpoint = new Uri("https://models.inference.ai.azure.com") }
68-
);
69-
var agent = openAI.GetChatClient("gpt-4o-mini")
70-
.CreateAIAgent(name: "AGUIAssistant", instructions: "You are a helpful assistant.");
71-
72-
app.MapAGUI("/", agent).RequireAuthorization();
73-
74-
await app.RunAsync();
75-
```
47+
options.Authority = builder.Configuration["JwtAuthority"];
48+
options.Audience = builder.Configuration["JwtAudience"];
49+
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
50+
{
51+
ValidateIssuer = true,
52+
ValidateAudience = true,
53+
ValidateLifetime = true,
54+
ValidateIssuerSigningKey = true
55+
};
56+
});
57+
58+
builder.Services.AddAuthorization();
59+
60+
var app = builder.Build();
61+
62+
app.UseAuthentication();
63+
app.UseAuthorization();
64+
65+
// Create and map your agent
66+
string githubToken = builder.Configuration["GitHubToken"]!;
67+
var openAI = new OpenAIClient(
68+
new System.ClientModel.ApiKeyCredential(githubToken),
69+
new OpenAIClientOptions { Endpoint = new Uri("https://models.inference.ai.azure.com") }
70+
);
71+
var agent = openAI.GetChatClient("gpt-4o-mini")
72+
.CreateAIAgent(name: "AGUIAssistant", instructions: "You are a helpful assistant.");
73+
74+
app.MapAGUI("/", agent).RequireAuthorization();
75+
76+
await app.RunAsync();
77+
```
78+
</Tab>
79+
<Tab value="Python">
80+
```python title="agent/src/main.py (excerpt)"
81+
from __future__ import annotations
82+
import os
83+
from fastapi import FastAPI, HTTPException, Request, status
84+
from fastapi.middleware.cors import CORSMiddleware
85+
from agent_framework import ChatClientProtocol
86+
from agent_framework.azure import AzureOpenAIChatClient
87+
from agent_framework.openai import OpenAIChatClient
88+
from azure.identity import DefaultAzureCredential
89+
from agent_framework.ag_ui import add_agent_framework_fastapi_endpoint
90+
from agent import create_agent
91+
92+
app = FastAPI(title="CopilotKit + Microsoft Agent Framework (Python)")
93+
app.add_middleware(
94+
CORSMiddleware,
95+
allow_origins=["*"],
96+
allow_credentials=True,
97+
allow_methods=["*"],
98+
allow_headers=["*"],
99+
)
100+
101+
REQUIRED_BEARER_TOKEN = os.getenv("AUTH_BEARER_TOKEN")
102+
103+
@app.middleware("http")
104+
async def auth_middleware(request: Request, call_next):
105+
# Protect the AG-UI endpoint if a token is configured
106+
if REQUIRED_BEARER_TOKEN and request.url.path == "/":
107+
auth_header = request.headers.get("Authorization", "")
108+
if not auth_header.startswith("Bearer "):
109+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing bearer token")
110+
token = auth_header.split(" ", 1)[1].strip()
111+
if token != REQUIRED_BEARER_TOKEN:
112+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
113+
return await call_next(request)
114+
115+
# Build a chat client (same pattern as the Quickstart)
116+
def _build_chat_client() -> ChatClientProtocol:
117+
if bool(os.getenv("AZURE_OPENAI_ENDPOINT")):
118+
deployment_name = os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME", "gpt-4o-mini")
119+
return AzureOpenAIChatClient(
120+
credential=DefaultAzureCredential(),
121+
deployment_name=deployment_name,
122+
endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
123+
)
124+
if bool(os.getenv("OPENAI_API_KEY")):
125+
return OpenAIChatClient(
126+
model_id=os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-4o-mini"),
127+
api_key=os.getenv("OPENAI_API_KEY"),
128+
)
129+
raise RuntimeError("Set AZURE_OPENAI_* or OPENAI_API_KEY in agent/.env")
130+
131+
chat_client = _build_chat_client()
132+
my_agent = create_agent(chat_client)
133+
add_agent_framework_fastapi_endpoint(app=app, agent=my_agent, path="/")
134+
```
135+
</Tab>
136+
</Tabs>
76137

77138
### Configuration
78139

79-
Add JWT settings to your `appsettings.json`:
140+
Add settings to your server configuration:
80141

81-
```json title="appsettings.json"
82-
{
83-
"JwtAuthority": "https://login.microsoftonline.com/{your-tenant-id}/v2.0",
84-
"JwtAudience": "api://{your-client-id}",
85-
"GitHubToken": "your-github-token-here"
86-
}
87-
```
142+
<Tabs groupId="language" items={['.NET', 'Python']}>
143+
<Tab value=".NET">
144+
```json title="appsettings.json"
145+
{
146+
"JwtAuthority": "https://login.microsoftonline.com/{your-tenant-id}/v2.0",
147+
"JwtAudience": "api://{your-client-id}",
148+
"GitHubToken": "your-github-token-here"
149+
}
150+
```
151+
</Tab>
152+
<Tab value="Python">
153+
```bash title="agent/.env"
154+
# Simple shared-secret example for demo purposes
155+
AUTH_BEARER_TOKEN=super-secret-demo-token
156+
```
157+
</Tab>
158+
</Tabs>
88159

89160
### CORS (if needed)
90161

@@ -116,6 +187,13 @@ app.UseAuthorization();
116187
- Implement role-based access control in your agents
117188
- Use HTTPS in production
118189

190+
<Callout type="warning" title="Avoid shared-secret bearer tokens in production">
191+
Examples that validate a bearer token against a single shared secret (e.g., an environment variable) are for local demos only.
192+
For production, use proper authentication:
193+
- .NET: Validate JWTs with `Microsoft.AspNetCore.Authentication.JwtBearer` (as shown above), backed by your IdP (e.g., Entra ID).
194+
- Python: Use OAuth 2.0 / OpenID Connect JWT validation or an API gateway that validates tokens before requests reach your AG‑UI server.
195+
</Callout>
196+
119197
## Troubleshooting
120198

121199
**Token not reaching server**: Verify the `Authorization` header is set in `<CopilotKit>` and forwarded through any proxies.

0 commit comments

Comments
 (0)