diff --git a/dotnet/SK-dotnet.slnx b/dotnet/SK-dotnet.slnx
index 835c32f258a1..ea1e02fd7de6 100644
--- a/dotnet/SK-dotnet.slnx
+++ b/dotnet/SK-dotnet.slnx
@@ -27,7 +27,9 @@
-
+
+
+
@@ -37,6 +39,7 @@
+
diff --git a/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/AzureOpenAI_Step04_ToolCall_WithOpenAPI.csproj b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/AzureOpenAI_Step04_ToolCall_WithOpenAPI.csproj
new file mode 100644
index 000000000000..a500b427e031
--- /dev/null
+++ b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/AzureOpenAI_Step04_ToolCall_WithOpenAPI.csproj
@@ -0,0 +1,30 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1707;CA2007;VSTHRD111
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/OpenAPISpec.json b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/OpenAPISpec.json
new file mode 100644
index 000000000000..84715914da63
--- /dev/null
+++ b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/OpenAPISpec.json
@@ -0,0 +1,354 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "Github Versions API",
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "https://api.github.com"
+ }
+ ],
+ "components": {
+ "schemas": {
+ "basic-error": {
+ "title": "Basic Error",
+ "description": "Basic Error",
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "documentation_url": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ }
+ }
+ },
+ "label": {
+ "title": "Label",
+ "description": "Color-coded labels help you categorize and filter your issues (just like labels in Gmail).",
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "Unique identifier for the label.",
+ "type": "integer",
+ "format": "int64",
+ "example": 208045946
+ },
+ "node_id": {
+ "type": "string",
+ "example": "MDU6TGFiZWwyMDgwNDU5NDY="
+ },
+ "url": {
+ "description": "URL for the label",
+ "example": "https://api.github.com/repositories/42/labels/bug",
+ "type": "string",
+ "format": "uri"
+ },
+ "name": {
+ "description": "The name of the label.",
+ "example": "bug",
+ "type": "string"
+ },
+ "description": {
+ "description": "Optional description of the label, such as its purpose.",
+ "type": "string",
+ "example": "Something isn't working",
+ "nullable": true
+ },
+ "color": {
+ "description": "6-character hex code, without the leading #, identifying the color",
+ "example": "FFFFFF",
+ "type": "string"
+ },
+ "default": {
+ "description": "Whether this label comes by default in a new repository.",
+ "type": "boolean",
+ "example": true
+ }
+ },
+ "required": [
+ "id",
+ "node_id",
+ "url",
+ "name",
+ "description",
+ "color",
+ "default"
+ ]
+ },
+ "tag": {
+ "title": "Tag",
+ "description": "Tag",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "v0.1"
+ },
+ "commit": {
+ "type": "object",
+ "properties": {
+ "sha": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
+ },
+ "required": [
+ "sha",
+ "url"
+ ]
+ },
+ "zipball_url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://github.com/octocat/Hello-World/zipball/v0.1"
+ },
+ "tarball_url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://github.com/octocat/Hello-World/tarball/v0.1"
+ },
+ "node_id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "node_id",
+ "commit",
+ "zipball_url",
+ "tarball_url"
+ ]
+ }
+ },
+ "examples": {
+ "label-items": {
+ "value": [
+ {
+ "id": 208045946,
+ "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
+ "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
+ "name": "bug",
+ "description": "Something isn't working",
+ "color": "f29513",
+ "default": true
+ },
+ {
+ "id": 208045947,
+ "node_id": "MDU6TGFiZWwyMDgwNDU5NDc=",
+ "url": "https://api.github.com/repos/octocat/Hello-World/labels/enhancement",
+ "name": "enhancement",
+ "description": "New feature or request",
+ "color": "a2eeef",
+ "default": false
+ }
+ ]
+ },
+ "tag-items": {
+ "value": [
+ {
+ "name": "v0.1",
+ "commit": {
+ "sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc",
+ "url": "https://api.github.com/repos/octocat/Hello-World/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
+ },
+ "zipball_url": "https://github.com/octocat/Hello-World/zipball/v0.1",
+ "tarball_url": "https://github.com/octocat/Hello-World/tarball/v0.1",
+ "node_id": "MDQ6VXNlcjE="
+ }
+ ]
+ }
+ },
+ "parameters": {
+ "owner": {
+ "name": "owner",
+ "description": "The account owner of the repository. The name is not case sensitive.",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "repo": {
+ "name": "repo",
+ "description": "The name of the repository without the `.git` extension. The name is not case sensitive.",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "per-page": {
+ "name": "per_page",
+ "description": "The number of results per page (max 100). For more information, see \"[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api).\"",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 30
+ }
+ },
+ "page": {
+ "name": "page",
+ "description": "The page number of the results to fetch. For more information, see \"[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api).\"",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 1
+ }
+ }
+ },
+ "responses": {
+ "not_found": {
+ "description": "Resource not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/basic-error"
+ }
+ }
+ }
+ }
+ },
+ "headers": {
+ "link": {
+ "example": "; rel=\"next\", ; rel=\"last\"",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "paths": {
+ "/repos/{owner}/{repo}/tags": {
+ "get": {
+ "summary": "List repository tags",
+ "description": "",
+ "tags": [
+ "repos"
+ ],
+ "operationId": "repos/list-tags",
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/repos/repos#list-repository-tags"
+ },
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/owner"
+ },
+ {
+ "$ref": "#/components/parameters/repo"
+ },
+ {
+ "$ref": "#/components/parameters/per-page"
+ },
+ {
+ "$ref": "#/components/parameters/page"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tag"
+ }
+ },
+ "examples": {
+ "default": {
+ "$ref": "#/components/examples/tag-items"
+ }
+ }
+ }
+ },
+ "headers": {
+ "Link": {
+ "$ref": "#/components/headers/link"
+ }
+ }
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": false,
+ "enabledForGitHubApps": true,
+ "category": "repos",
+ "subcategory": "repos"
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/labels": {
+ "get": {
+ "summary": "List labels for a repository",
+ "description": "Lists all labels for a repository.",
+ "tags": [
+ "issues"
+ ],
+ "operationId": "issues/list-labels-for-repo",
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/issues/labels#list-labels-for-a-repository"
+ },
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/owner"
+ },
+ {
+ "$ref": "#/components/parameters/repo"
+ },
+ {
+ "$ref": "#/components/parameters/per-page"
+ },
+ {
+ "$ref": "#/components/parameters/page"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/label"
+ }
+ },
+ "examples": {
+ "default": {
+ "$ref": "#/components/examples/label-items"
+ }
+ }
+ }
+ },
+ "headers": {
+ "Link": {
+ "$ref": "#/components/headers/link"
+ }
+ }
+ },
+ "404": {
+ "$ref": "#/components/responses/not_found"
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": false,
+ "enabledForGitHubApps": true,
+ "category": "issues",
+ "subcategory": "labels"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/Program.cs b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/Program.cs
new file mode 100644
index 000000000000..5898050f5d7a
--- /dev/null
+++ b/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI/Program.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates how to use an Agent with function tools provided via an OpenAPI spec with both Semantic Kernel and Agent Framework.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents;
+using Microsoft.SemanticKernel.Plugins.OpenApi;
+using OpenAI;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ // Create a kernel with an Azure OpenAI chat client.
+ var kernel = Kernel.CreateBuilder().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()).Build();
+
+ // Load the OpenAPI Spec from a file.
+ var plugin = await kernel.ImportPluginFromOpenApiAsync("github", "OpenAPISpec.json");
+
+ // Create the agent, and provide the kernel with the OpenAPI function tools to the agent.
+ var agent = new ChatCompletionAgent()
+ {
+ Kernel = kernel,
+ Instructions = "You are a helpful assistant",
+ Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
+ };
+
+ // Run the agent with the OpenAPI function tools.
+ await foreach (var result in agent.InvokeAsync("Please list the names, colors and descriptions of all the labels available in the microsoft/agent-framework repository on github."))
+ {
+ Console.WriteLine(result.Message);
+ }
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ // Load the OpenAPI Spec from a file.
+ KernelPlugin plugin = await OpenApiKernelPluginFactory.CreateFromOpenApiAsync("github", "OpenAPISpec.json");
+
+ // Convert the Semantic Kernel plugin to Agent Framework function tools.
+ // This requires a dummy Kernel instance, since KernelFunctions cannot execute without one.
+ Kernel kernel = new();
+ List tools = plugin.Select(x => x.WithKernel(kernel)).Cast().ToList();
+
+ // Create the chat client and agent, and provide the OpenAPI function tools to the agent.
+ AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant", tools: tools);
+
+ // Run the agent with the OpenAPI function tools.
+ Console.WriteLine(await agent.RunAsync("Please list the names, colors and descriptions of all the labels available in the microsoft/agent-framework repository on github."));
+}