Semantic Kernel Plugins Created: 03 Feb 2026 Updated: 03 Feb 2026

Building OpenAPI Plugins in Semantic Kernel: A Todo API Integration

One of the most powerful features of Microsoft Semantic Kernel is its ability to automatically generate plugins from OpenAPI specifications. This capability allows developers to seamlessly integrate existing REST APIs into their AI-powered applications without writing manual wrapper code.

In this article, we'll demonstrate how to create an OpenAPI plugin that communicates with a Todo API, enabling an AI assistant to manage tasks through natural language commands.

What is OpenAPI?

OpenAPI (formerly known as Swagger) is a specification format for describing HTTP APIs. It provides a standardized, machine-readable, and human-friendly way to define:

  1. API endpoints and operations
  2. Request/response parameters
  3. Data schemas
  4. Authentication methods

When Semantic Kernel imports an OpenAPI specification, it automatically:

  1. Discovers all available endpoints
  2. Creates corresponding kernel functions
  3. Maps parameters and return types
  4. Enables AI models to call these functions

Architecture Overview

+---------------------+ +----------------------+ +-----------------+
| Semantic Kernel |---->| OpenAPI Plugin |---->| Todo API |
| + AI Model | | (Auto-generated) | | (REST API) |
+---------------------+ +----------------------+ +-----------------+
| | |
| Natural Language | HTTP Requests | Database
| "Show my pending tasks" | GET /api/todos/pending | Operations
v v v

Part 1: Building the Todo API

First, let's create a minimal API with OpenAPI/Swagger support:

Project Setup

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
</ItemGroup>
</Project>

API Implementation

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<TodoDbContext>(options =>
options.UseInMemoryDatabase("TodoDb"));

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new()
{
Title = "Todo API",
Version = "v1",
Description = "A Todo API for Semantic Kernel OpenAPI Plugin demo"
});
});

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

var todoGroup = app.MapGroup("/api/todos").WithTags("Todos");

// GET all todos
todoGroup.MapGet("/", async (TodoDbContext db) =>
await db.Todos.ToListAsync())
.WithName("GetAllTodos")
.WithDescription("Gets all todo items")
.WithOpenApi();

// GET pending todos
todoGroup.MapGet("/pending", async (TodoDbContext db) =>
await db.Todos.Where(t => !t.IsCompleted).ToListAsync())
.WithName("GetPendingTodos")
.WithDescription("Gets all pending todo items")
.WithOpenApi();

// GET completed todos
todoGroup.MapGet("/completed", async (TodoDbContext db) =>
await db.Todos.Where(t => t.IsCompleted).ToListAsync())
.WithName("GetCompletedTodos")
.WithDescription("Gets all completed todo items")
.WithOpenApi();

// GET todo by id
todoGroup.MapGet("/{id:int}", async (int id, TodoDbContext db) =>
await db.Todos.FindAsync(id) is Todo todo
? Results.Ok(todo)
: Results.NotFound())
.WithName("GetTodoById")
.WithDescription("Gets a todo item by id")
.WithOpenApi();

// POST create todo
todoGroup.MapPost("/", async (CreateTodoRequest request, TodoDbContext db) =>
{
var todo = new Todo
{
Title = request.Title,
Description = request.Description,
CreatedAt = DateTime.UtcNow
};
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/api/todos/{todo.Id}", todo);
})
.WithName("CreateTodo")
.WithDescription("Creates a new todo item")
.WithOpenApi();

// DELETE todo
todoGroup.MapDelete("/{id:int}", async (int id, TodoDbContext db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
})
.WithName("DeleteTodo")
.WithDescription("Deletes a todo item")
.WithOpenApi();

app.Run();

public class Todo
{
public int Id { get; set; }
public required string Title { get; set; }
public string? Description { get; set; }
public bool IsCompleted { get; set; }
public DateTime CreatedAt { get; set; }
}

public record CreateTodoRequest(string Title, string? Description);

public class TodoDbContext(DbContextOptions<TodoDbContext> options)
: DbContext(options)
{
public DbSet<Todo> Todos => Set<Todo>();
}

Key Points for OpenAPI Compatibility

Method Purpose
.WithName()Sets the operation ID (becomes function name)
.WithDescription()Describes the operation (helps AI understand)
.WithOpenApi()Includes endpoint in OpenAPI spec

Part 2: Creating the OpenAPI Plugin

Now let's create a Semantic Kernel application that consumes this API:

Required Package

<PackageReference Include="Microsoft.SemanticKernel.Plugins.OpenApi" Version="1.68.0-preview" />

Plugin Implementation

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.OpenApi;

var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: Environment.GetEnvironmentVariable("OPEN_AI_KEY")!);

var kernel = builder.Build();

// Import plugin from OpenAPI specification
#pragma warning disable SKEXP0040

var todoPlugin = await kernel.CreatePluginFromOpenApiAsync(
pluginName: "TodoAPI",
uri: new Uri("https://localhost:7001/swagger/v1/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters
{
EnableDynamicPayload = true,
EnablePayloadNamespacing = true
});

kernel.Plugins.Add(todoPlugin);

Invoking Functions Directly

// Get all todos
var allTodos = await kernel.InvokeAsync(todoPlugin["GetAllTodos"]);
Console.WriteLine($"All Todos: {allTodos}");

// Get todo by ID
var todo = await kernel.InvokeAsync(
todoPlugin["GetTodoById"],
new KernelArguments { ["id"] = 1 });
Console.WriteLine($"Todo #1: {todo}");

// Create a new todo
var newTodo = await kernel.InvokeAsync(
todoPlugin["CreateTodo"],
new KernelArguments
{
["payload"] = """{"title": "New Task", "description": "Created via SK"}"""
});
Console.WriteLine($"Created: {newTodo}");

Part 3: AI-Powered Task Management

The real power comes when combining OpenAPI plugins with AI:

var executionSettings = new PromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var response = await kernel.InvokePromptAsync(
"""
You are a helpful task management assistant.
Use the TodoAPI functions to help users manage their tasks.
User: Show me my pending tasks and create a reminder to review them tomorrow.
""",
new KernelArguments(executionSettings));

Console.WriteLine(response);

How It Works

  1. AI Receives Request: The model receives the user's natural language request
  2. Function Discovery: AI examines available functions from the OpenAPI plugin
  3. Automatic Invocation: AI decides which functions to call and with what parameters
  4. Response Generation: AI combines API results into a natural language response

OpenApiFunctionExecutionParameters

This class provides important configuration options:

Parameter Description
EnableDynamicPayloadAllows dynamic JSON payloads for POST/PUT
EnablePayloadNamespacingNamespaces payload parameters
AuthCallbackAuthentication callback for secured APIs
OperationsToExcludeList of operations to hide from the plugin
HttpClientCustom HTTP client for requests

Authentication Example

var parameters = new OpenApiFunctionExecutionParameters
{
AuthCallback = async (request, cancellationToken) =>
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", "your-token");
}
};

Expected Output

[Package] Plugin: TodoAPI
Functions discovered: 6

[Function] GetAllTodos
Description: Gets all todo items

[Function] GetPendingTodos
Description: Gets all pending todo items

[Function] GetCompletedTodos
Description: Gets all completed todo items

[Function] GetTodoById
Description: Gets a todo item by id
Parameters:
- id (required): Format - int32.

[Function] CreateTodo
Description: Creates a new todo item
Parameters:
- payload (required): The request body

[Function] DeleteTodo
Description: Deletes a todo item
Parameters:
- id (required): Format - int32.

[Test] TEST 1: Get All Todos
Result: [{"id":1,"title":"Learn Semantic Kernel",...},...]

[AI] AI Response:
Here are your pending tasks:
1. Learn Semantic Kernel - Study plugins and functions
2. Build OpenAPI Plugin - Create a plugin from OpenAPI spec

You have 2 pending tasks to complete. I recommend starting with
"Learn Semantic Kernel" as it will help you better understand
the OpenAPI Plugin task.

Benefits of OpenAPI Plugins

BenefitDescription
Zero BoilerplateNo manual HTTP client code needed
Type SafetyParameters are validated against schema
AI IntegrationFunctions are automatically available to AI
MaintainabilityAPI changes reflect automatically
DocumentationFunction descriptions come from OpenAPI spec

Best Practices

  1. Use Descriptive Names: Operation IDs become function names
  2. Write Clear Descriptions: AI uses descriptions to understand functions
  3. Include Parameter Docs: Help AI provide correct parameter values
  4. Handle Errors: Implement proper error responses in your API
  5. Secure Your API: Use authentication for production APIs
  6. Test Independently: Verify API works before integrating with SK

Conclusion

OpenAPI plugins in Semantic Kernel provide a powerful bridge between existing REST APIs and AI-powered applications. By following the OpenAPI specification and using descriptive metadata, you can create intelligent assistants that naturally interact with your services.

The Todo API example demonstrates how a simple CRUD API can become an AI-accessible service, enabling natural language task management. This pattern can be applied to any OpenAPI-compliant service, from internal microservices to third-party APIs.


Share this lesson: