Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,8 @@
/src/CommunityToolkit.Aspire.Hosting.Chroma/ @Laurianti
/tests/CommunityToolkit.Aspire.Hosting.Chroma.Tests/ @Laurianti

# CommunityToolkit.Aspire.Hosting.Agents

/examples/agents/ @tommasodotNET
/src/CommunityToolkit.Aspire.Hosting.Agents/ @tommasodotNET
/tests/CommunityToolkit.Aspire.Hosting.Agents.Tests/ @tommasodotNET
8 changes: 8 additions & 0 deletions CommunityToolkit.Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
<Folder Name="/examples/adminer/">
<Project Path="examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj" />
</Folder>
<Folder Name="/examples/agents/">
<Project Path="examples/agents/AgentEnvDump/AgentEnvDump.csproj" />
<Project Path="examples/agents/CommunityToolkit.Aspire.Hosting.Agents.AppHost/CommunityToolkit.Aspire.Hosting.Agents.AppHost.csproj" />
<Project Path="examples/agents/McpAgent/McpAgent.csproj" />
<Project Path="examples/agents/ResponsesAgent/ResponsesAgent.csproj" />
</Folder>
<Folder Name="/examples/azure-ext/">
<Project Path="examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/CommunityToolkit.Aspire.Azure.Extensions.AppHost.csproj" />
</Folder>
Expand Down Expand Up @@ -253,6 +259,7 @@
<Project Path="src/CommunityToolkit.Aspire.GoFeatureFlag/CommunityToolkit.Aspire.GoFeatureFlag.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.ActiveMQ/CommunityToolkit.Aspire.Hosting.ActiveMQ.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Adminer/CommunityToolkit.Aspire.Hosting.Adminer.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Agents/CommunityToolkit.Aspire.Hosting.Agents.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.csproj" />
Expand Down Expand Up @@ -334,6 +341,7 @@
<Project Path="tests/CommunityToolkit.Aspire.GoFeatureFlag.Tests/CommunityToolkit.Aspire.GoFeatureFlag.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Agents.Tests/CommunityToolkit.Aspire.Hosting.Agents.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests.csproj" />
Expand Down
10 changes: 8 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageVersion Include="Aspire.Hosting.Keycloak" Version="$(AspireVersion)$(AspirePreviewSuffix)" />
<PackageVersion Include="Aspire.Keycloak.Authentication" Version="$(AspireVersion)$(AspirePreviewSuffix)" />
<PackageVersion Include="Aspire.Hosting.JavaScript" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Foundry" Version="$(AspireVersion)$(AspirePreviewSuffix)" />
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Python" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Rabbitmq" Version="$(AspireVersion)" />
Expand Down Expand Up @@ -49,6 +50,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MEAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.9.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="$(DotNetExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="$(DotNetExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(DotNetExtensionsVersion)" />
Expand Down Expand Up @@ -76,6 +78,8 @@
<ItemGroup Label="Integration Packages">
<!-- External packages -->
<PackageVersion Include="AWSSDK.S3" Version="4.0.104.8" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.21.0" />
<PackageVersion Include="Azure.Provisioning.AppContainers" Version="1.2.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.29.2" />
Expand Down Expand Up @@ -112,13 +116,15 @@
<PackageVersion Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.12" />
<PackageVersion Include="YamlDotNet" Version="18.1.0" />
<PackageVersion Include="Microsoft.Agents.AI.GitHub.Copilot" Version="1.27.0" />
<PackageVersion Include="Microsoft.Agents.AI.Hosting.OpenAI" Version="1.7.0-alpha.260526.1" />
<PackageVersion Include="Squad.Agents.AI" Version="1.0.0-preview.43" />
<PackageVersion Include="SurrealDb.MinimalApis.Extensions" Version="1.0.0" />
<PackageVersion Include="SurrealDb.Net" Version="1.0.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.3.1" />
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.10" />
<PackageVersion Include="ModelContextProtocol" Version="0.4.0-preview.3" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.3" />
<PackageVersion Include="ModelContextProtocol" Version="1.4.1" />
<PackageVersion Include="ModelContextProtocol.Core" Version="1.4.1" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="1.4.1" />
<PackageVersion Include="KurrentDB.Client" Version="1.6.1" />
<PackageVersion Include="SSH.NET" Version="2026.0.0" />
<PackageVersion Include="ChromaDotNet.Client.DependencyInjection" Version="2.18.1" />
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This repository contains the source code for the Aspire Community Toolkit, a col
| - **Learn More**: [`GoFeatureFlag`](src/CommunityToolkit.Aspire.GoFeatureFlag/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.GoFeatureFlag](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.GoFeatureFlag)](https://nuget.org/packages/CommunityToolkit.Aspire.GoFeatureFlag/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.GoFeatureFlag](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.GoFeatureFlag?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.GoFeatureFlag/absoluteLatest) | A GO Feature Flag client that integrates with Aspire, including health checks, logging, and telemetry. |
| - **Learn More**: [`Hosting.ActiveMQ`](src/CommunityToolkit.Aspire.Hosting.ActiveMQ/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.ActiveMQ](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.ActiveMQ)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.ActiveMQ/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.ActiveMQ](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.ActiveMQ?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.ActiveMQ/absoluteLatest) | An Aspire hosting package for hosting ActiveMQ. |
| - **Learn More**: [`Hosting.Adminer`](src/CommunityToolkit.Aspire.Hosting.Adminer/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Adminer](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Adminer)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Adminer/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Adminer](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Adminer?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Adminer/absoluteLatest) | An Aspire hosting integration for the Adminer database management container. |
| - **Learn More**: [`Hosting.Agents`](src/CommunityToolkit.Aspire.Hosting.Agents/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Agents](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Agents)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Agents/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Agents](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Agents?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Agents/absoluteLatest) | An Aspire hosting integration for endpoint-backed AI agents with A2A, OpenAI Responses, AG-UI, ACP, and opt-in MCP tool invocation commands. |
| - **Learn More**: [`Hosting.Azure.Dapr`](src/CommunityToolkit.Aspire.Hosting.Azure.Dapr/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Dapr](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Azure.Dapr)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Dapr/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Dapr](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.Dapr?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Dapr/absoluteLatest) | Azure Dapr support for Aspire. |
| - **Learn More**: [`Hosting.Azure.Dapr.Redis`](src/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis/absoluteLatest) | An Aspire hosting integration for configuring Azure Cache for Redis as a Dapr state store or pub/sub component in your AppHost. |
| - **Learn More**: [`Hosting.Azure.DataApiBuilder`](src/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/README.md) <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder](https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder)](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/) <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder](https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder?label=nuget%20(preview))](https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/absoluteLatest) | An Aspire component leveraging the Data API Builder container. |
Expand Down
12 changes: 12 additions & 0 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ This API is marked as experimental as it does not have a stable implementation,

The API is marked for deprecation and will be removed in a future release.

## CTASPIREAGENTS001

Dashboard MCP tool commands in the Agents hosting integration are experimental and may change or be removed in future updates.

Current usage:

- `WithMcpToolCommands(...)`

These commands use the official MCP SDK's Streamable HTTP transport for local development, not a general-purpose MCP client. They do not acquire credentials or forward dashboard-user identity. Known SDK limitations and dashboard diagnostics are documented in the [Agents integration README](../src/CommunityToolkit.Aspire.Hosting.Agents/README.md#mcp-servers).

Suppress this diagnostic only when you intentionally opt into the experimental API surface.

## CTASPIREPERL001

This API is marked as experimental for the Perl hosting integration and may change or be removed in future updates.
Expand Down
2 changes: 2 additions & 0 deletions examples/agents/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
7 changes: 7 additions & 0 deletions examples/agents/AgentEnvDump/AgentEnvDump.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>

</Project>
5 changes: 5 additions & 0 deletions examples/agents/AgentEnvDump/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

Console.WriteLine($"A2A_JSONRPC_AGENT_AGENTCARD_URL={Environment.GetEnvironmentVariable("A2A_JSONRPC_AGENT_AGENTCARD_URL")}");
await Task.Delay(Timeout.InfiniteTimeSpan);
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Foundry;
using Microsoft.Extensions.Configuration;

#pragma warning disable ASPIREMCP001
#pragma warning disable CTASPIREAGENTS001
#pragma warning disable ASPIREDOTNETPROJECT001

var builder = DistributedApplication.CreateBuilder(args);

IResourceBuilder<IResourceWithConnectionString> project;
IResourceBuilder<IResourceWithConnectionString> chat;

// Existing connections are the safe default: starting the playground must not
// silently create Azure resources or a billable model deployment.
if (builder.Configuration.GetValue("UseExistingFoundry", true))
{
project = builder.AddConnectionString("agentsproject");
chat = builder.AddConnectionString("chat");
}
else
{
var foundry = builder.AddFoundry("agents-foundry");
var foundryProject = foundry.AddProject("agentsproject");
project = foundryProject;
chat = foundryProject.AddModelDeployment("chat", FoundryModel.OpenAI.Gpt41Mini);
}

var a2aAgent = builder.AddUvicornApp("a2a-jsonrpc-agent", "../weather-agent-python", "weather_agent_python.main:app")
.WithUv()
.WithReference(project)
.WithReference(chat)
.WaitFor(chat)
.AsAgent(AgentProtocol.A2A);

builder.AddDotnetProject("responses-agent", new Projects.ResponsesAgent().ProjectPath)
.WithHttpEndpoint(env: "PORT")
.WithReference(chat)
.WithEnvironment("A2A_JSONRPC_AGENT_AGENTCARD_URL",
ReferenceExpression.Create($"{a2aAgent.GetEndpoint("http")}/.well-known/agent-card.json"))
.WaitFor(chat)
.AsAgent(AgentProtocol.Responses, agentName: "responses-agent")
.WithMcpServer()
.WithMcpToolCommands();

builder.AddNodeApp("vercel-ai-responses-agent", "../vercel-ai-responses-agent", "app.ts")
.WithRunScript("start")
.WithNpm(installCommand: "ci")
.WithHttpEndpoint(env: "PORT")
.WithReference(chat)
.WaitFor(chat)
.AsAgent(AgentProtocol.Responses, agentName: "vercel-ai-responses-agent");

builder.AddDotnetProject("mcp-agent", new Projects.McpAgent().ProjectPath)
.WithHttpEndpoint(env: "PORT")
.WithReference(chat)
.WaitFor(chat)
.WithMcpServer()
.WithMcpToolCommands();

builder.AddUvicornApp("agui-acp-agent", "../agui-acp-agent-python", "agui_acp_agent.main:app")
.WithUv()
.WithReference(project)
.WithReference(chat)
.WaitFor(chat)
.AsAgent(AgentProtocol.AgUi)
.AsAgent(AgentProtocol.Acp, agentName: "agui-acp-agent");

builder.AddDotnetProject("agent-env-dump", new Projects.AgentEnvDump().ProjectPath)
.WithEnvironment("A2A_JSONRPC_AGENT_AGENTCARD_URL",
ReferenceExpression.Create($"{a2aAgent.GetEndpoint("http")}/.well-known/agent-card.json"))
.WaitFor(a2aAgent);

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Aspire.AppHost.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<UserSecretsId>e1c0f8c6-8b6b-4e42-84f3-59c0135a60ce</UserSecretsId>
<NoWarn>$(NoWarn);ASPIRECOMPUTE001;ASPIREPIPELINES001;ASPIREAZURE001;ASPIREPIPELINES003</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.Dotnet" />
<PackageReference Include="Aspire.Hosting.Foundry" />
<PackageReference Include="Aspire.Hosting.JavaScript" />
<PackageReference Include="Aspire.Hosting.Python" />
<ProjectReference Include="../../../src/CommunityToolkit.Aspire.Hosting.Agents/CommunityToolkit.Aspire.Hosting.Agents.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\AgentEnvDump\AgentEnvDump.csproj" />
<ProjectReference Include="..\McpAgent\McpAgent.csproj" />
<ProjectReference Include="..\ResponsesAgent\ResponsesAgent.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:43799;http://localhost:43800",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21012",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22201"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:43800",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19104",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20135",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
}
}
}
}
10 changes: 10 additions & 0 deletions examples/agents/McpAgent/McpAgent.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="ModelContextProtocol.AspNetCore" />
</ItemGroup>

</Project>
74 changes: 74 additions & 0 deletions examples/agents/McpAgent/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Data.Common;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Server;

var builder = WebApplication.CreateBuilder(args);
var chatConnectionString = builder.Configuration.GetConnectionString("chat")
?? throw new InvalidOperationException("Connection string 'chat' is not configured.");

DbConnectionStringBuilder chatConnectionBuilder = new() { ConnectionString = chatConnectionString };
var endpoint = GetRequiredConnectionValue(chatConnectionBuilder, "Endpoint");
var deploymentName = GetRequiredConnectionValue(chatConnectionBuilder, "Deployment");

builder.WebHost.UseUrls($"http://+:{Environment.GetEnvironmentVariable("PORT") ?? "8080"}");

QuestionTools.Initialize(
new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsIChatClient());

builder.Services
.AddMcpServer()
.WithHttpTransport()
.WithTools<QuestionTools>();

var app = builder.Build();

app.MapGet("/health", () => Results.Ok("Healthy"));
app.MapMcp("/mcp");

app.Run();

static string GetRequiredConnectionValue(DbConnectionStringBuilder connectionBuilder, string key)
{
if (!connectionBuilder.TryGetValue(key, out var rawValue) || rawValue is null)
{
throw new InvalidOperationException($"Connection string is missing '{key}'.");
}

var value = rawValue.ToString();
if (string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException($"Connection string has an empty '{key}' value.");
}

return value;
}

[McpServerToolType]
public sealed class QuestionTools
{
private static IChatClient? s_chatClient;

public static void Initialize(IChatClient chatClient)
{
s_chatClient = chatClient;
}

[McpServerTool(Name = "answer_question")]
[Description("Uses the Foundry model deployment to answer a question.")]
public static async Task<string> AnswerQuestion([Description("The question to answer.")] string question)
{
var chatClient = s_chatClient ?? throw new InvalidOperationException("The chat client is not initialized.");
var response = await chatClient.GetResponseAsync(
$"You are a concise MCP agent. Answer this question: {question}");

return response.Text;
}
}
Loading
Loading