Semantic Kernel Plugins Created: 18 Jan 2026 Updated: 18 Jan 2026

Building and Integrating Custom Plugins in Semantic Kernel

While Large Language Models (LLMs) like GPT-4o are incredibly powerful, they have a fundamental limitation: they are "isolated." Out of the box, an LLM cannot know the current time, check your calendar, or interact with your local database.

Semantic Kernel Plugins solve this by acting as a bridge. They allow the AI to "reach out" of its training data and execute specific C# functions to get real-time information or perform actions.

1. The Anatomy of a Plugin

A plugin in Semantic Kernel is essentially a C# class that contains one or more methods marked with specific attributes. These attributes are the "instructions" that help the AI understand what the function does.

  1. [KernelFunction]: This attribute tells the Kernel that the method is available for the AI to call.
  2. [Description]: This is the most critical part. The LLM reads this description to decide if and when it should trigger this function based on the user's prompt.

2. Implementing a Custom Time Plugin

In the example below, we create a TimePlugin. Without this, if you ask an LLM "What time is it?", it might apologize and say it doesn't know. With this plugin, it will call your C# method to get the exact system time.

The Plugin Class

public class TimePlugin
{
[KernelFunction("get_current_time")]
[Description("Gets the current date and time")]
public string GetCurrentTime()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}

[KernelFunction("get_day_of_week")]
[Description("Gets the current day of the week")]
public string GetDayOfWeek()
{
return DateTime.Now.DayOfWeek.ToString();
}
}

3. Orchestration: Auto-Invoking Tools

Once the plugin is defined, it must be registered within the Kernel. The modern way to handle this in .NET is through Automatic Tool Calling. By setting the ToolCallBehavior, we tell the Kernel to automatically execute the C# function if the AI requests it.

The Implementation

// See https://aka.ms/new-console-template for more information


using System.ComponentModel;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY");

if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("Please set the OPEN_AI_KEY environment variable.");
return;
}

var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: apiKey)
.Build();

kernel.Plugins.AddFromType<TimePlugin>("TimePlugin");

var executionSettings = new OpenAIPromptExecutionSettings
{
Temperature = 0.1,
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

var chat = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();

history.AddSystemMessage("""
You are an AI assistant helping to organize daily tasks.
Your task is to break down complex activities into simple,
actionable steps.
Provide only the step-by-step breakdown without any additional
explanations.
""");

while (true)
{
Console.Write(" User >>> ");
var prompt = Console.ReadLine();
if (string.IsNullOrEmpty(prompt)) break;

history.AddUserMessage(prompt);
var response = await chat.GetChatMessageContentAsync(history, executionSettings, kernel);

Console.WriteLine($" Bot >>> {response.Content}");
history.Add(response);
}

public class TimePlugin
{
[KernelFunction("get_current_time")]
[Description("Gets the current date and time")]
public string GetCurrentTime()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}

[KernelFunction("get_day_of_week")]
[Description("Gets the current day of the week")]
public string GetDayOfWeek()
{
return DateTime.Now.DayOfWeek.ToString();
}
}

4. How the "Function Calling" Loop Works

When you use AutoInvokeKernelFunctions, a sophisticated multi-step process occurs:

  1. Intent Detection: The User asks a question (e.g., "Do I have time for a meeting now?").
  2. Tool Selection: The LLM reviews the descriptions of all registered KernelFunctions and decides it needs get_current_time.
  3. Function Call: The LLM sends a request back to the Kernel to "Call function X with parameters Y."
  4. Local Execution: The Kernel executes your C# code locally.
  5. Final Synthesis: The result of the C# code is sent back to the LLM, which then generates a natural language response for the user.

5. Best Practices for Plugin Development

  1. Be Descriptive: The [Description] attribute is basically "Prompt Engineering" for your code. Use clear, unambiguous language so the model knows exactly when to use the tool.
  2. Keep it Simple: Each function should do one thing well. If you have a complex task, break it into multiple KernelFunctions.
  3. Security First: Remember that the LLM is choosing to call these functions. Never give a plugin direct, unvalidated access to destructive operations (like deleting a database) without an additional human-in-the-loop confirmation.
Share this lesson: