Repository: microsoft/kiota (csharp)

Issue:
Kiota should generate function capabilities from capabilities extension
### Is your feature request related to a problem? Please describe the problem.




### Client library/SDK language

None

### Describe the solution you'd like

When using an OpenAPI description that contains function capabilities in the `x-ai-capabilities` OpenAPI extension, Kiota should add the capabilities in the plugin manifest

Sample OpenAPI:

```yaml
openapi: 3.0.4
info:
  title: Todoist API - Subset - Subset
  description: OpenAPI specification for Todoist REST API.
  version: '2.0'
servers:
  - url: https://api.todoist.com/rest/v2
paths:
  /tasks:
    get:
      summary: Get Active Tasks
      operationId: getActiveTasks
      x-ai-capabilities:
        response_semantics:
          data_path: $.test
          static_template:
            type: AdaptiveCard
            version: 1.5
            body:
              - type: TextBlock
                text: Hello World
          properties:
            title: Card Title
            sub_title: Card Subtitle
            thumbnail_url: https://example.com/image.png
            url: https://example.com
            information_protection_label: general
            template_selector: defaultTemplate
        confirmation:
          type: text
          title: Confirmation Title
          body: Are you sure you want to proceed?
        security_info:
          data_handling:
            - sensitiveData
            - personalData
      parameters:
        - name: project_id
          in: query
          description: Filter tasks by project ID
          explode: false
          schema:
            type: string
        - name: section_id
          in: query
          description: Filter tasks by section ID
          explode: false
          schema:
            type: string
        - name: label
          in: query
          description: Filter tasks by label
          explode: false
          schema:
            type: string
        - name: filter
          in: query
          description: 'Filter tasks by a specified filter (e.g., ''today'')'
          explode: false
          schema:
            type: string
        - name: lang
          in: query
          description: Language for filter interpretation
          explode: false
          schema:
            type: string
            default: en
        - name: ids
          in: query
          description: A comma-separated list of task IDs to retrieve
          explode: false
          schema:
            type: array
            items:
              type: integer
      responses:
        '200':
          description: A JSON-encoded array containing all active tasks
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    content:
                      type: string
                    description:
                      type: string
                    project_id:
                      type: string
                    section_id:
                      type: string
                    parent_id:
                      type: string
                    order:
                      type: integer
                    labels:
                      type: array
                      items:
                        type: string
                    priority:
                      type: integer
                    due:
                      type: object
                      properties:
                        date:
                          type: string
                        is_recurring:
                          type: boolean
                        datetime:
                          type: string
                          format: date-time
                        string:
                          type: string
                        timezone:
                          type: string
                    assignee_id:
                      type: string
                    assigner_id:
                      type: string
                    comment_count:
                      type: integer
                    is_completed:
                      type: boolean
                    url:
                      type: string
                      format: uri
    post:
      summary: Create a New Task
      operationId: createTask
      requestBody:
        content:
          application/json:
            schema:
              required:
                - content
              type: object
              properties:
                content:
                  type: string
                description:
                  type: string
                project_id:
                  type: string
                section_id:
                  type: string
                parent_id:
                  type: string
                order:
                  type: integer
                labels:
                  type: array
                  items:
                    type: string
                priority:
                  type: integer
                due_string:
                  type: string
                due_date:
                  type: string
                due_datetime:
                  type: string
                  format: date-time
                due_lang:
                  type: string
                assignee_id:
                  type: string
        required: true
      responses:
        '200':
          description: The newly created task
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  content:
                    type: string
                  description:
                    type: string
                  project_id:
                    type: string
                  section_id:
                    type: string
                  parent_id:
                    type: string
                  order:
                    type: integer
                  labels:
                    type: array
                    items:
                      type: string
                  priority:
                    type: integer
                  due:
                    type: object
                    properties:
                      date:
                        type: string
                      is_recurring:
                        type: boolean
                      datetime:
                        type: string
                        format: date-time
                      string:
                        type: string
                      timezone:
                        type: string
                  assignee_id:
                    type: string
                  assigner_id:
                    type: string
                  comment_count:
                    type: integer
                  is_completed:
                    type: boolean
                  url:
                    type: string
                    format: uri
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      x-ai-auth-reference-id: bearer-12345
security:
  - BearerAuth: [ ]
```

Expected plugin manifest:
```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json",
  "schema_version": "v2.1",
  "name_for_human": "Todoist API - Subset - Subset",
  "description_for_human": "OpenAPI specification for Todoist REST API.",
  "description_for_model": "OpenAPI specification for Todoist REST API.",
  "contact_email": "publisher-email@example.com",
  "namespace": "sampleplugin",
  "capabilities": {
    "conversation_starters": [
      {
        "text": "Get Active Tasks"
      },
      {
        "text": "Create a New Task"
      }
    ]
  },
  "functions": [
    {
      "name": "createTask",
      "description": "Create a New Task"
    },
    {
      "name": "getActiveTasks",
      "description": "Get Active Tasks",
      "capabilities": {
        "confirmation": {
          "type": "text",
          "title": "Confirmation Title",
          "body": "Are you sure you want to proceed?"
        },
        "response_semantics": {
          "data_path": "$.test",
          "static_template": {"type":"AdaptiveCard","version":1.5,"body":[{"type":"TextBlock","text":"Hello World"}]},
          "properties": {
            "title": "Card Title",
            "subtitle": "Card Subtitle",
            "url": "https://example.com",
            "thumbnail_url": "https://example.com/image.png",
            "information_protection_label": "general",
            "template_selector": "defaultTemplate"
          }
        },
        "security_info": {
          "data_handling": [
            "sensitiveData",
            "personalData"
          ]
        }
      }
    }
  ],
  "runtimes": [
    {
      "type": "OpenApi",
      "auth": {
        "type": "ApiKeyPluginVault",
        "reference_id": "bearer-12345"
      },
      "spec": {
        "url": "sampleplugin-openapi.yml"
      },
      "run_for_functions": [
        "getActiveTasks",
        "createTask"
      ]
    }
  ]
}
```

### Additional context

_No response_

Pull request description:
Fixes https://github.com/microsoft/kiota/issues/6369

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ab3a73fa1..0c31836716 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Fixed a bug with http client generation where query parameters were being processed incorrectly. [#6268](https://github.com/microsoft/kiota/issues/6268)
 
 ### Added
+- Added support for generating function capabilities in plugin manifest from x-ai-capabilities OpenApi extension. [#6369](https://github.com/microsoft/kiota/issues/6369)   
 
 - Added a comment on top of every golang file to indicate the file is machine generated so it can be excluded from various tools. [#6363](https://github.com/microsoft/kiota/issues/6363)
 
diff --git a/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs
index cf49565491..3195981b95 100644
--- a/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs
+++ b/src/Kiota.Builder/Extensions/OpenApiSettingsExtensions.cs
@@ -25,6 +25,7 @@ public static void AddPluginsExtensions(this OpenApiReaderSettings settings)
         settings.ExtensionParsers.TryAdd(OpenApiAiRespondingInstructionsExtension.Name, static (i, _) => OpenApiAiRespondingInstructionsExtension.Parse(i));
         settings.ExtensionParsers.TryAdd(OpenApiAiAuthReferenceIdExtension.Name, static (i, _) => OpenApiAiAuthReferenceIdExtension.Parse(i));
         settings.ExtensionParsers.TryAdd(OpenApiAiAdaptiveCardExtension.Name, static (i, _) => OpenApiAiAdaptiveCardExtension.Parse(i));
+        settings.ExtensionParsers.TryAdd(OpenApiAiCapabilitiesExtension.Name, static (i, _) => OpenApiAiCapabilitiesExtension.Parse(i));
     }
 
     public static void AddGenerationExtensions(this OpenApiReaderSettings settings)
diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiAdaptiveCardExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiAdaptiveCardExtension.cs
index 0c2b2e0e04..455db69e2e 100644
--- a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiAdaptiveCardExtension.cs
+++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiAdaptiveCardExtension.cs
@@ -38,14 +38,14 @@ public static OpenApiAiAdaptiveCardExtension Parse(JsonNode source)
     public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
     {
         ArgumentNullException.ThrowIfNull(writer);
+        writer.WriteStartObject();
         if (!string.IsNullOrEmpty(DataPath) && !string.IsNullOrEmpty(File))
         {
-            writer.WriteStartObject();
             writer.WritePropertyName(nameof(DataPath).ToFirstCharacterLowerCase().ToSnakeCase());
             writer.WriteValue(DataPath);
             writer.WritePropertyName(nameof(File).ToFirstCharacterLowerCase());
             writer.WriteValue(File);
-            writer.WriteEndObject();
         }
+        writer.WriteEndObject();
     }
 }
diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiAiCapabilitiesExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiCapabilitiesExtension.cs
new file mode 100644
index 0000000000..5b8a90a51c
--- /dev/null
+++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiAiCapabilitiesExtension.cs
@@ -0,0 +1,395 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using Kiota.Builder.Extensions;
+using Microsoft.OpenApi;
+using Microsoft.OpenApi.Interfaces;
+using Microsoft.OpenApi.Writers;
+
+namespace Kiota.Builder.OpenApiExtensions;
+
+public class OpenApiAiCapabilitiesExtension : IOpenApiExtension
+{
+    public static string Name => "x-ai-capabilities";
+    public ExtensionConfirmation? Confirmation
+    {
+        get; set;
+    }
+    public ExtensionResponseSemantics? ResponseSemantics
+    {
+        get; set;
+    }
+    public ExtensionSecurityInfo? SecurityInfo
+    {
+        get; set;
+    }
+
+    public static OpenApiAiCapabilitiesExtension Parse(JsonNode source)
+    {
+        if (source is not JsonObject rawObject) throw new ArgumentOutOfRangeException(nameof(source));
+        var extension = new OpenApiAiCapabilitiesExtension();
+        if (rawObject.TryGetPropertyValue(nameof(Confirmation).ToFirstCharacterLowerCase(), out var confirmation) && confirmation is JsonObject confirmationObject)
+        {
+            extension.Confirmation = ParseConfirmation(confirmationObject);
+        }
+        if (rawObject.TryGetPropertyValue(nameof(ResponseSemantics).ToFirstCharacterLowerCase().ToSnakeCase(), out var responseSemantics) && responseSemantics is JsonObject responseSemanticsObject)
+        {
+            extension.ResponseSemantics = ParseResponseSemantics(responseSemanticsObject);
+        }
+        if (rawObject.TryGetPropertyValue(nameof(SecurityInfo).ToFirstCharacterLowerCase().ToSnakeCase(), out var securityInfo) && securityInfo is JsonObject securityInfoObject)
+        {
+            extension.SecurityInfo = ParseSecurityInfo(securityInfoObject);
+        }
+        return extension;
+    }
+
+    public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
+    {
+        ArgumentNullException.ThrowIfNull(writer);
+        writer.WriteStartObject();
+        if (ResponseSemantics != null || Confirmation != null || SecurityInfo != null)
+        {
+
+            if (ResponseSemantics is not null)
+            {
+                writer.WritePropertyName(nameof(ResponseSemantics).ToFirstCharacterLowerCase().ToSnakeCase());
+                WriteResponseSemantics(writer, ResponseSemantics);
+            }
+            if (Confirmation is not null)
+            {
+                writer.WritePropertyName(nameof(Confirmation).ToFirstCharacterLowerCase());
+                WriteConfirmation(writer, Confirmation);
+            }
+            if (SecurityInfo is not null)
+            {
+                writer.WritePropertyName(nameof(SecurityInfo).ToFirstCharacterLowerCase().ToSnakeCase());
+                WriteSecurityInfo(writer, SecurityInfo);
+            }
+        }
+        writer.WriteEndObject();
+    }
+
+    private static ExtensionConfirmation ParseConfirmation(JsonObject source)
+    {
+        var confirmation = new ExtensionConfirmation();
+        if (source.TryGetPropertyValue(nameof(Confirmation.Type).ToFirstCharacterLowerCase(), out var type) &&
+            type is JsonValue typeValue && typeValue.GetValueKind() is JsonValueKind.String &&
+            typeValue.TryGetValue<string>(out var typeStrValue))
+        {
+            confirmation.Type = typeStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(Confirmation.Title).ToFirstCharacterLowerCase(), out var title) &&
+            title is JsonValue titleValue && titleValue.GetValueKind() is JsonValueKind.String &&
+            titleValue.TryGetValue<string>(out var titleStrValue))
+        {
+            confirmation.Title = titleStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(Confirmation.Body).ToFirstCharacterLowerCase(), out var body) &&
+            body is JsonValue bodyValue && bodyValue.GetValueKind() is JsonValueKind.String &&
+            bodyValue.TryGetValue<string>(out var bodyStrValue))
+        {
+            confirmation.Body = bodyStrValue;
+        }
+
+        return confirmation;
+    }
+
+    private void WriteConfirmation(IOpenApiWriter writer, ExtensionConfirmation confirmation)
+    {
+        writer.WriteStartObject();
+
+        if (!string.IsNullOrEmpty(confirmation.Type))
+        {
+            writer.WritePropertyName(nameof(Confirmation.Type).ToFirstCharacterLowerCase());
+            writer.WriteValue(confirmation.Type);
+        }
+
+        if (!string.IsNullOrEmpty(confirmation.Title))
+        {
+            writer.WritePropertyName(nameof(Confirmation.Title).ToFirstCharacterLowerCase());
+            writer.WriteValue(confirmation.Title);
+        }
+
+        if (!string.IsNullOrEmpty(confirmation.Body))
+        {
+            writer.WritePropertyName(nameof(Confirmation.Body).ToFirstCharacterLowerCase());
+            writer.WriteValue(confirmation.Body);
+        }
+
+        writer.WriteEndObject();
+    }
+
+    private static ExtensionResponseSemantics ParseResponseSemantics(JsonObject source)
+    {
+        var responseSemantics = new ExtensionResponseSemantics();
+
+        if (source.TryGetPropertyValue(nameof(ResponseSemantics.DataPath).ToFirstCharacterLowerCase().ToSnakeCase(), out var dataPath) &&
+            dataPath is JsonValue dataPathValue && dataPathValue.GetValueKind() is JsonValueKind.String &&
+            dataPathValue.TryGetValue<string>(out var dataPathStrValue))
+        {
+            responseSemantics.DataPath = dataPathStrValue;
+        }
+        if (source.TryGetPropertyValue(nameof(ResponseSemantics.StaticTemplate).ToFirstCharacterLowerCase().ToSnakeCase(), out var staticTemplate) &&
+            staticTemplate is JsonObject staticTemplateObj)
+        {
+            responseSemantics.StaticTemplate = staticTemplateObj;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ResponseSemantics.Properties).ToFirstCharacterLowerCase(), out var properties) &&
+            properties is JsonObject propertiesObj)
+        {
+            responseSemantics.Properties = ParseResponseSemanticsProperties(propertiesObj);
+        }
+
+        if (source.TryGetPropertyValue(nameof(ResponseSemantics.OauthCardPath).ToFirstCharacterLowerCase().ToSnakeCase(), out var OauthCardPath) &&
+            OauthCardPath is JsonValue OauthCardPathValue && OauthCardPathValue.GetValueKind() is JsonValueKind.String &&
+            OauthCardPathValue.TryGetValue<string>(out var OauthCardPathStrValue))
+        {
+            responseSemantics.OauthCardPath = OauthCardPathStrValue;
+        }
+
+        return responseSemantics;
+    }
+
+    private void WriteResponseSemantics(IOpenApiWriter writer, ExtensionResponseSemantics responseSemantics)
+    {
+        writer.WriteStartObject();
+
+        writer.WritePropertyName(nameof(ResponseSemantics.DataPath).ToFirstCharacterLowerCase().ToSnakeCase());
+        writer.WriteValue(responseSemantics.DataPath);
+
+        if (responseSemantics.StaticTemplate != null && responseSemantics.StaticTemplate is JsonObject staticTemplateObj)
+        {
+            writer.WritePropertyName(nameof(ResponseSemantics.StaticTemplate).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteRaw(staticTemplateObj.ToJsonString());
+        }
+
+        if (responseSemantics.Properties != null)
+        {
+            writer.WritePropertyName(nameof(ResponseSemantics.Properties).ToFirstCharacterLowerCase());
+            WriteResponseSemanticsProperties(writer, responseSemantics.Properties);
+        }
+
+        if (!string.IsNullOrEmpty(responseSemantics.OauthCardPath))
+        {
+            writer.WritePropertyName(nameof(ResponseSemantics.OauthCardPath).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteValue(responseSemantics.OauthCardPath);
+        }
+
+        writer.WriteEndObject();
+    }
+
+    private static ExtensionResponseSemanticsProperties ParseResponseSemanticsProperties(JsonObject source)
+    {
+        var properties = new ExtensionResponseSemanticsProperties();
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.Title).ToFirstCharacterLowerCase(), out var title) &&
+            title is JsonValue titleValue && titleValue.GetValueKind() is JsonValueKind.String &&
+            titleValue.TryGetValue<string>(out var titleStrValue))
+        {
+            properties.Title = titleStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.Subtitle).ToFirstCharacterLowerCase(), out var subtitle) &&
+            subtitle is JsonValue subtitleValue && subtitleValue.GetValueKind() is JsonValueKind.String &&
+            subtitleValue.TryGetValue<string>(out var subtitleStrValue))
+        {
+            properties.Subtitle = subtitleStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.Url).ToFirstCharacterLowerCase(), out var url) &&
+            url is JsonValue urlValue && urlValue.GetValueKind() is JsonValueKind.String &&
+            urlValue.TryGetValue<string>(out var urlStrValue))
+        {
+            properties.Url = urlStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.ThumbnailUrl).ToFirstCharacterLowerCase().ToSnakeCase(), out var thumbnailUrl) &&
+            thumbnailUrl is JsonValue thumbnailUrlValue && thumbnailUrlValue.GetValueKind() is JsonValueKind.String &&
+            thumbnailUrlValue.TryGetValue<string>(out var thumbnailUrlStrValue))
+        {
+            properties.ThumbnailUrl = thumbnailUrlStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.InformationProtectionLabel).ToFirstCharacterLowerCase().ToSnakeCase(), out var informationProtectionLabel) &&
+            informationProtectionLabel is JsonValue informationProtectionLabelValue && informationProtectionLabelValue.GetValueKind() is JsonValueKind.String &&
+            informationProtectionLabelValue.TryGetValue<string>(out var informationProtectionLabelStrValue))
+        {
+            properties.InformationProtectionLabel = informationProtectionLabelStrValue;
+        }
+
+        if (source.TryGetPropertyValue(nameof(ExtensionResponseSemanticsProperties.TemplateSelector).ToFirstCharacterLowerCase().ToSnakeCase(), out var templateSelector) &&
+            templateSelector is JsonValue templateSelectorValue && templateSelectorValue.GetValueKind() is JsonValueKind.String &&
+            templateSelectorValue.TryGetValue<string>(out var templateSelectorStrValue))
+        {
+            properties.TemplateSelector = templateSelectorStrValue;
+        }
+
+        return properties;
+    }
+
+    private void WriteResponseSemanticsProperties(IOpenApiWriter writer, ExtensionResponseSemanticsProperties properties)
+    {
+        writer.WriteStartObject();
+
+        if (!string.IsNullOrEmpty(properties.Title))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.Title).ToFirstCharacterLowerCase());
+            writer.WriteValue(properties.Title);
+        }
+
+        if (!string.IsNullOrEmpty(properties.Subtitle))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.Subtitle).ToFirstCharacterLowerCase());
+            writer.WriteValue(properties.Subtitle);
+        }
+
+        if (!string.IsNullOrEmpty(properties.Url))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.Url).ToFirstCharacterLowerCase());
+            writer.WriteValue(properties.Url);
+        }
+
+        if (!string.IsNullOrEmpty(properties.ThumbnailUrl))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.ThumbnailUrl).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteValue(properties.ThumbnailUrl);
+        }
+
+        if (!string.IsNullOrEmpty(properties.InformationProtectionLabel))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.InformationProtectionLabel).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteValue(properties.InformationProtectionLabel);
+        }
+
+        if (!string.IsNullOrEmpty(properties.TemplateSelector))
+        {
+            writer.WritePropertyName(nameof(ExtensionResponseSemanticsProperties.TemplateSelector).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteValue(properties.TemplateSelector);
+        }
+
+        writer.WriteEndObject();
+    }
+
+    private static ExtensionSecurityInfo ParseSecurityInfo(JsonObject source)
+    {
+        var securityInfo = new ExtensionSecurityInfo();
+
+        if (source.TryGetPropertyValue(nameof(ExtensionSecurityInfo.DataHandling).ToFirstCharacterLowerCase().ToSnakeCase(), out var dataHandling) &&
+            dataHandling is JsonArray dataHandlingArray)
+        {
+            var dataHandlingList = new List<string>();
+
+            foreach (var item in dataHandlingArray)
+            {
+                if (item is JsonValue valueItem && valueItem.GetValueKind() is JsonValueKind.String &&
+                    valueItem.TryGetValue<string>(out var strValue))
+                {
+                    dataHandlingList.Add(strValue);
+                }
+            }
+
+            securityInfo.DataHandling = dataHandlingList;
+        }
+
+        return securityInfo;
+    }
+
+    private void WriteSecurityInfo(IOpenApiWriter writer, ExtensionSecurityInfo securityInfo)
+    {
+        writer.WriteStartObject();
+
+        if (securityInfo.DataHandling != null && securityInfo.DataHandling.Count > 0)
+        {
+            writer.WritePropertyName(nameof(securityInfo.DataHandling).ToFirstCharacterLowerCase().ToSnakeCase());
+            writer.WriteStartArray();
+
+            foreach (var item in securityInfo.DataHandling)
+            {
+                writer.WriteValue(item);
+            }
+
+            writer.WriteEndArray();
+        }
+
+        writer.WriteEndObject();
+    }
+}
+
+
+public class ExtensionConfirmation
+{
+    public string? Type
+    {
+        get; set;
+    }
+    public string? Title
+    {
+        get; set;
+    }
+    public string? Body
+    {
+        get; set;
+    }
+}
+
+public class ExtensionResponseSemantics
+{
+    public string DataPath { get; set; } = string.Empty;
+    public object? StaticTemplate
+    {
+        get; set;
+    }
+    public ExtensionResponseSemanticsProperties? Properties
+    {
+        get; set;
+    }
+    public string? OauthCardPath
+    {
+        get; set;
+    }
+}
+
+public class ExtensionResponseSemanticsProperties
+{
+    public string? Title
+    {
+        get; set;
+    }
+    public string? Subtitle
+    {
+        get; set;
+    }
+#pragma warning disable CA1056 // URI-like properties should not be strings
+    public string? Url
+    {
+        get; set;
+    }
+    public string? ThumbnailUrl
+    {
+        get; set;
+    }
+#pragma warning restore CA1056 // URI-like properties should not be strings
+    public string? InformationProtectionLabel
+    {
+        get; set;
+    }
+    public string? TemplateSelector
+    {
+        get; set;
+    }
+}
+
+public class ExtensionSecurityInfo
+{
+#pragma warning disable CA1002 // Do not expose generic lists
+#pragma warning disable CA2227 // Collection properties should be read only
+    public List<string> DataHandling { get; set; } = new List<string>();
+#pragma warning restore CA2227 // Collection properties should be read only
+#pragma warning restore CA1002 // Do not expose generic lists
+}
+
+
diff --git a/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs b/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs
index 6ddab21631..d728fae927 100644
--- a/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs
+++ b/src/Kiota.Builder/OpenApiExtensions/OpenApiLogoExtension.cs
@@ -31,12 +31,12 @@ public static OpenApiLogoExtension Parse(JsonNode source)
     public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
     {
         ArgumentNullException.ThrowIfNull(writer);
+        writer.WriteStartObject();
         if (!string.IsNullOrEmpty(Url))
         {
-            writer.WriteStartObject();
             writer.WritePropertyName(nameof(Url).ToFirstCharacterLowerCase());
             writer.WriteValue(Url);
-            writer.WriteEndObject();
         }
+        writer.WriteEndObject();
     }
 }
diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs
index dec01fc709..cc481b6376 100644
--- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs
+++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs
@@ -654,13 +654,84 @@ rExtRaw is T rExt &&
 
     private static FunctionCapabilities? GetFunctionCapabilitiesFromOperation(OpenApiOperation openApiOperation)
     {
+        var capabilities = GetFunctionCapabilitiesFromCapabilitiesExtension(openApiOperation, OpenApiAiCapabilitiesExtension.Name);
+        if (capabilities != null)
+        {
+            return capabilities;
+        }
+
         var responseSemantics = GetResponseSemanticsFromAdaptiveCardExtension(openApiOperation, OpenApiAiAdaptiveCardExtension.Name);
-        if (responseSemantics is null)
-            return null;
-        return new FunctionCapabilities
+        if (responseSemantics != null)
         {
-            ResponseSemantics = responseSemantics,
-        };
+            return new FunctionCapabilities
+            {
+                ResponseSemantics = responseSemantics
+            };
+        }
+        return null;
+    }
+
+    private static FunctionCapabilities? GetFunctionCapabilitiesFromCapabilitiesExtension(OpenApiOperation openApiOperation, string extensionName)
+    {
+        if (openApiOperation.Extensions is not null &&
+            openApiOperation.Extensions.TryGetValue(extensionName, out var capabilitiesExtension) &&
+            capabilitiesExtension is OpenApiAiCapabilitiesExtension capabilities)
+        {
+            var functionCapabilities = new FunctionCapabilities();
+
+            // Set ResponseSemantics
+            if (capabilities.ResponseSemantics is not null)
+            {
+                var responseSemantics = new ResponseSemantics();
+
+                responseSemantics.DataPath = capabilities.ResponseSemantics.DataPath;
+                if (capabilities.ResponseSemantics.StaticTemplate is not null && capabilities.ResponseSemantics.StaticTemplate is JsonObject staticTemplateObj)
+                {
+                    using JsonDocument doc = JsonDocument.Parse(staticTemplateObj.ToJsonString());
+                    JsonElement staticTemplate = doc.RootElement.Clone();
+                    responseSemantics.StaticTemplate = staticTemplate;
+                }
+                if (capabilities.ResponseSemantics.Properties is not null)
+                {
+                    responseSemantics.Properties = new ResponseSemanticsProperties
+                    {
+                        Title = capabilities.ResponseSemantics.Properties.Title,
+                        Subtitle = capabilities.ResponseSemantics.Properties.Subtitle,
+                        Url = capabilities.ResponseSemantics.Properties.Url,
+                        ThumbnailUrl = capabilities.ResponseSemantics.Properties.ThumbnailUrl,
+                        InformationProtectionLabel = capabilities.ResponseSemantics.Properties.InformationProtectionLabel,
+                        TemplateSelector = capabilities.ResponseSemantics.Properties.TemplateSelector
+                    };
+                }
+                responseSemantics.OAuthCardPath = capabilities.ResponseSemantics.OauthCardPath;
+                functionCapabilities.ResponseSemantics = responseSemantics;
+            }
+
+            // Set Confirmation
+            if (capabilities.Confirmation is not null)
+            {
+                var confirmation = new Confirmation
+                {
+                    Type = capabilities.Confirmation.Type,
+                    Title = capabilities.Confirmation.Title,
+                    Body = capabilities.Confirmation.Body,
+                };
+                functionCapabilities.Confirmation = confirmation;
+            }
+
+            // Set SecurityInfo
+            if (capabilities.SecurityInfo is not null)
+            {
+                var securityInfo = new SecurityInfo
+                {
+                    DataHandling = capabilities.SecurityInfo.DataHandling,
+                };
+                functionCapabilities.SecurityInfo = securityInfo;
+            }
+            return functionCapabilities;
+        }
+
+        return null;
     }
 
     private static ResponseSemantics? GetResponseSemanticsFromAdaptiveCardExtension(OpenApiOperation openApiOperation, string extensionName)


Test patch:
diff --git a/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiCapabilitiesExtensionTests.cs b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiCapabilitiesExtensionTests.cs
new file mode 100644
index 0000000000..672dc5b2d9
--- /dev/null
+++ b/tests/Kiota.Builder.Tests/OpenApiExtensions/OpenApiAiCapabilitiesExtensionTests.cs
@@ -0,0 +1,240 @@
+﻿using System;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Text.Json.Nodes;
+using System.Threading.Tasks;
+using Kiota.Builder.Configuration;
+using Kiota.Builder.OpenApiExtensions;
+using Microsoft.Extensions.Logging;
+using Microsoft.OpenApi;
+using Microsoft.OpenApi.Writers;
+using Moq;
+using Xunit;
+
+namespace Kiota.Builder.Tests.OpenApiExtensions;
+public sealed class OpenApiAiCapabilitiesExtensionTest : IDisposable
+{
+    private readonly HttpClient _httpClient = new();
+    private readonly string TempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+
+    public void Dispose()
+    {
+        _httpClient.Dispose();
+        if (Directory.Exists(TempDirectory))
+        {
+            Directory.Delete(TempDirectory, true);
+        }
+    }
+
+    [Fact]
+    public void Parses()
+    {
+        var oaiValueRepresentation =
+        """
+        {
+            "response_semantics": {
+                "data_path": "$.items",
+                "static_template": {
+                    "title": "Search for items",
+                    "body": "Here are the items I found for you."
+                },
+                "properties": {
+                    "title": "Some title",
+                    "subtitle": "Some subtitle",
+                    "url": "https://example.com",
+                    "thumbnail_url": "https://example.com/thumbnail.jpg",
+                    "information_protection_label": "confidential"
+                },
+                "oauth_card_path": "oauthCard.json"
+            },
+            "confirmation": {
+                "type": "modal",
+                "title": "Confirm action",
+                "body": "Do you want to proceed?"
+            },
+            "security_info": {
+                "data_handling": ["some data handling"]
+            }
+        }
+        """;
+        using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(oaiValueRepresentation));
+        var oaiValue = JsonNode.Parse(stream);
+        var value = OpenApiAiCapabilitiesExtension.Parse(oaiValue);
+
+        Assert.NotNull(value);
+
+        var responseSemantics = value.ResponseSemantics;
+        var confirmation = value.Confirmation;
+        var securityInfo = value.SecurityInfo;
+        Assert.NotNull(responseSemantics);
+        Assert.NotNull(confirmation);
+        Assert.NotNull(securityInfo);
+
+        Assert.Equal("$.items", responseSemantics.DataPath);
+        Assert.Equal("oauthCard.json", responseSemantics.OauthCardPath);
+        var staticTemplate = responseSemantics.StaticTemplate as JsonObject;
+        Assert.NotNull(staticTemplate);
+        Assert.Equal("Search for items", staticTemplate["title"]?.ToString());
+        Assert.Equal("Here are the items I found for you.", staticTemplate["body"]?.ToString());
+
+        var properties = responseSemantics.Properties;
+        Assert.NotNull(properties);
+        Assert.Equal("Some title", properties.Title);
+        Assert.Equal("Some subtitle", properties.Subtitle);
+        Assert.Equal("https://example.com", properties.Url);
+        Assert.Equal("https://example.com/thumbnail.jpg", properties.ThumbnailUrl);
+        Assert.Equal("confidential", properties.InformationProtectionLabel);
+
+        Assert.Equal("modal", confirmation.Type);
+        Assert.Equal("Confirm action", confirmation.Title);
+        Assert.Equal("Do you want to proceed?", confirmation.Body);
+
+        Assert.Equal("some data handling", securityInfo.DataHandling[0]);
+    }
+
+    [Fact]
+    public async Task ParsesInDocumentAsync()
+    {
+        var documentContent = @"openapi: 3.0.0
+info:
+  title: Test API
+  version: 0.0.0
+servers:
+  - url: https://api.example.com/v1
+    description: Example API
+paths:
+  /items:
+    get:
+      operationId: getItems
+      parameters: []
+      responses:
+        '200':
+          description: The request has succeeded.
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Item'
+      x-ai-capabilities:
+        response_semantics:
+          data_path: $.items
+          static_template:
+            title: Search for items
+            body: Here are the items I found for you.
+          properties:
+            title: Some title
+            subtitle: Some subtitle
+            url: https://example.com
+            thumbnail_url: https://example.com/thumbnail.jpg
+            information_protection_label: confidential
+          oauth_card_path: oauthCard.json
+        confirmation:
+          type: modal
+          title: Confirm action
+          body: Do you want to proceed?
+        security_info:
+          data_handling:
+            - some data handling
+components:
+  schemas:
+    Item:
+      type: object
+      properties:
+        id:
+          type: string
+        name:
+          type: string";
+
+        Directory.CreateDirectory(TempDirectory);
+        var documentPath = Path.Combine(TempDirectory, "document.yaml");
+        await File.WriteAllTextAsync(documentPath, documentContent);
+        var mockLogger = new Mock<ILogger<OpenApiAiCapabilitiesExtension>>();
+        var documentDownloadService = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object);
+        var generationConfig = new GenerationConfiguration { OutputPath = TempDirectory, PluginTypes = [PluginType.APIPlugin] };
+        var (openApiDocumentStream, _) = await documentDownloadService.LoadStreamAsync(documentPath, generationConfig);
+        var document = await documentDownloadService.GetDocumentFromStreamAsync(openApiDocumentStream, generationConfig);
+
+        Assert.NotNull(document);
+        Assert.NotNull(document.Paths);
+        Assert.NotNull(document.Paths["/items"].Operations.FirstOrDefault().Value.Extensions);
+        Assert.True(document.Paths["/items"].Operations.FirstOrDefault().Value.Extensions.TryGetValue(OpenApiAiCapabilitiesExtension.Name, out var capabilitiesExtension));
+        Assert.NotNull(capabilitiesExtension);
+    }
+
+    [Fact]
+    public void Serializes()
+    {
+        var value = new OpenApiAiCapabilitiesExtension
+        {
+            ResponseSemantics = new ExtensionResponseSemantics
+            {
+                DataPath = "$.items",
+                StaticTemplate = new JsonObject
+                {
+                    ["title"] = "Search for items",
+                    ["body"] = "Here are the items I found for you."
+                },
+                Properties = new ExtensionResponseSemanticsProperties
+                {
+                    Title = "Some title",
+                    Subtitle = "Some subtitle",
+                    Url = "https://example.com",
+                    ThumbnailUrl = "https://example.com/thumbnail.jpg",
+                    InformationProtectionLabel = "confidential"
+                },
+                OauthCardPath = "oauthCard.json"
+            },
+            Confirmation = new ExtensionConfirmation
+            {
+                Type = "modal",
+                Title = "Confirm action",
+                Body = "Do you want to proceed?"
+            },
+            SecurityInfo = new ExtensionSecurityInfo
+            {
+                DataHandling = ["some data handling"]
+            }
+        };
+        using var sWriter = new StringWriter();
+        OpenApiJsonWriter writer = new(sWriter, new OpenApiJsonWriterSettings { Terse = true });
+
+
+        value.Write(writer, OpenApiSpecVersion.OpenApi3_0);
+        var result = sWriter.ToString();
+
+        Assert.Contains("\"response_semantics\":", result);
+        Assert.Contains("data_path", result);
+        Assert.Contains("$.items", result);
+        Assert.Contains("static_template", result);
+        Assert.Contains("title", result);
+        Assert.Contains("Search for items", result);
+        Assert.Contains("body", result);
+        Assert.Contains("Here are the items I found for you", result);
+        Assert.Contains("properties", result);
+        Assert.Contains("title", result);
+        Assert.Contains("Some title", result);
+        Assert.Contains("subtitle", result);
+        Assert.Contains("Some subtitle", result);
+        Assert.Contains("url", result);
+        Assert.Contains("https://example.com", result);
+        Assert.Contains("thumbnail_url", result);
+        Assert.Contains("https://example.com/thumbnail.jpg", result);
+        Assert.Contains("information_protection_label", result);
+        Assert.Contains("confidential", result);
+        Assert.Contains("\"oauth_card_path", result);
+        Assert.Contains("oauthCard.json", result);
+        Assert.Contains("\"confirmation\":", result);
+        Assert.Contains("type", result);
+        Assert.Contains("modal", result);
+        Assert.Contains("title", result);
+        Assert.Contains("Confirm action", result);
+        Assert.Contains("body", result);
+        Assert.Contains("Do you want to proceed?", result);
+        Assert.Contains("\"security_info\":", result);
+        Assert.Contains("data_handling", result);
+        Assert.Contains("some data handling", result);
+
+    }
+}
diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs
index e9f12b5ae3..629680b053 100644
--- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs
+++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs
@@ -733,6 +733,193 @@ public async Task GeneratesManifestWithMultipleSecuritySchemesAsync()
 
     #endregion
 
+    #region Function capabilities
+
+    [Fact]
+    public async Task GeneratesManifestWithAiCapabilitiesExtensionAsync()
+    {
+        var simpleDescriptionContent = @"openapi: 3.0.0
+info:
+  title: test
+  version: 1.0
+servers:
+  - url: http://localhost/
+    description: There's no place like home
+paths:
+  /test:
+    get:
+      description: description for test path
+      x-ai-capabilities:
+        response_semantics:
+          data_path: $.test
+          static_template:
+            type: AdaptiveCard
+            version: 1.5
+            body:
+              - type: TextBlock
+                text: Hello World
+          properties:
+            title: Card Title
+            subtitle: Card Subtitle
+            thumbnail_url: https://example.com/image.png
+            url: https://example.com
+            information_protection_label: general
+            template_selector: defaultTemplate
+        confirmation:
+          type: text
+          title: Confirmation Title
+          body: Are you sure you want to proceed?
+        security_info:
+          data_handling:
+            - sensitiveData
+            - personalData
+      responses:
+        '200':
+          description: test
+  /test/{id}:
+    get:
+      summary: description for test path with id
+      operationId: test.WithId
+      parameters:
+      - name: id
+        in: path
+        required: true
+        description: The id of the test
+        schema:
+          type: integer
+          format: int32
+      responses:
+        '200':
+          description: success
+";
+
+        var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+        var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml";
+        await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent);
+        var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger);
+        var outputDirectory = Path.Combine(workingDirectory, "output");
+        var generationConfiguration = new GenerationConfiguration
+        {
+            OutputPath = outputDirectory,
+            OpenAPIFilePath = simpleDescriptionPath,
+            PluginTypes = [PluginType.APIPlugin],
+            ClientClassName = "client",
+            ApiRootUrl = "http://localhost/", //Kiota builder would set this for us
+        };
+        var (openAPIDocumentStream, _) = await openAPIDocumentDS.LoadStreamAsync(simpleDescriptionPath, generationConfiguration, null, false);
+        var openApiDocument = await openAPIDocumentDS.GetDocumentFromStreamAsync(openAPIDocumentStream, generationConfiguration);
+        KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument);
+        var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel);
+
+        var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger);
+        await pluginsGenerationService.GenerateManifestAsync();
+
+        Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName)));
+        Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName)));
+
+        // Validate the v2 plugin
+        var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName));
+        using var jsonDocument = JsonDocument.Parse(manifestContent);
+        var resultingManifest = PluginManifestDocument.Load(jsonDocument.RootElement);
+        Assert.NotNull(resultingManifest.Document);
+        Assert.Equal(OpenApiFileName, resultingManifest.Document.Runtimes.OfType<OpenApiRuntime>().First().Spec.Url);
+        Assert.Equal(2, resultingManifest.Document.Functions.Count);
+
+        // Validate x-ai-capabilities data is correctly parsed
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities);
+
+        // Validate ResponseSemantics
+        Assert.Equal("$.test", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.DataPath);
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.StaticTemplate);
+        Assert.Contains("AdaptiveCard", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.StaticTemplate.ToString());
+
+        // Validate ResponseSemantics.Properties
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties);
+        Assert.Equal("Card Title", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.Title);
+        Assert.Equal("Card Subtitle", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.Subtitle);
+        Assert.Equal("https://example.com/image.png", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.ThumbnailUrl);
+        Assert.Equal("https://example.com", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.Url);
+        Assert.Equal("general", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.InformationProtectionLabel);
+        Assert.Equal("defaultTemplate", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.Properties.TemplateSelector);
+
+        // Validate Confirmation
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities.Confirmation);
+        Assert.Equal("text", resultingManifest.Document.Functions[0].Capabilities.Confirmation.Type);
+        Assert.Equal("Confirmation Title", resultingManifest.Document.Functions[0].Capabilities.Confirmation.Title);
+        Assert.Equal("Are you sure you want to proceed?", resultingManifest.Document.Functions[0].Capabilities.Confirmation.Body);
+
+        // Validate SecurityInfo
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities.SecurityInfo);
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities.SecurityInfo.DataHandling);
+        Assert.Equal(2, resultingManifest.Document.Functions[0].Capabilities.SecurityInfo.DataHandling.Count);
+        Assert.Contains("sensitiveData", resultingManifest.Document.Functions[0].Capabilities.SecurityInfo.DataHandling);
+        Assert.Contains("personalData", resultingManifest.Document.Functions[0].Capabilities.SecurityInfo.DataHandling);
+
+        // Second function has no capabilities
+        Assert.Null(resultingManifest.Document.Functions[1].Capabilities);
+    }
+
+    [Fact]
+    public async Task PrefersCapabilitiesOverAdaptiveCardExtensionAsync()
+    {
+        var simpleDescriptionContent = @"openapi: 3.0.0
+info:
+  title: test
+  version: 1.0
+servers:
+  - url: http://localhost/
+    description: There's no place like home
+paths:
+  /test:
+    get:
+      description: description for test path
+      x-ai-capabilities:
+        response_semantics:
+          data_path: $.capabilities
+          static_template:
+            type: AdaptiveCard
+            version: 1.5
+      x-ai-adaptive-card:
+        data_path: $.adaptiveCard
+        file: path_to_file
+      responses:
+        '200':
+          description: test
+";
+
+        var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+        var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml";
+        await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent);
+        var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger);
+        var outputDirectory = Path.Combine(workingDirectory, "output");
+        var generationConfiguration = new GenerationConfiguration
+        {
+            OutputPath = outputDirectory,
+            OpenAPIFilePath = simpleDescriptionPath,
+            PluginTypes = [PluginType.APIPlugin],
+            ClientClassName = "client",
+            ApiRootUrl = "http://localhost/", //Kiota builder would set this for us
+        };
+        var (openAPIDocumentStream, _) = await openAPIDocumentDS.LoadStreamAsync(simpleDescriptionPath, generationConfiguration, null, false);
+        var openApiDocument = await openAPIDocumentDS.GetDocumentFromStreamAsync(openAPIDocumentStream, generationConfiguration);
+        KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument);
+        var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel);
+
+        var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger);
+        await pluginsGenerationService.GenerateManifestAsync();
+
+        var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName));
+        using var jsonDocument = JsonDocument.Parse(manifestContent);
+        var resultingManifest = PluginManifestDocument.Load(jsonDocument.RootElement);
+
+        // Should use x-ai-capabilities over x-ai-adaptive-card
+        Assert.NotNull(resultingManifest.Document.Functions[0].Capabilities);
+        Assert.Equal("$.capabilities", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.DataPath);
+        Assert.NotEqual("$.adaptiveCard", resultingManifest.Document.Functions[0].Capabilities.ResponseSemantics.DataPath);
+    }
+
+    #endregion
+
     #region Validation
 
     public static TheoryData<string, Action<OpenApiDocument, OpenApiDiagnostic>>