Functions in Semantic Kernel — Overview

Functions in Semantic Kernel — Overview

⚡ Functions Overview

Semantic Kernel (SK) lets you compose an agent from different kinds of functions. A function is just a capability the Kernel can invoke — like “summarize text”, “get calendar events”, or “post to CRM”. Functions can be defined in code, by a prompt, from OpenAPI specs, or via the Microsoft Copilot Protocol (MCP). Plugins are just named collections of functions.

Function types at a glance

TypeDescriptionDefinition SourceTypical Use
🔹 Native (Code) FunctionA normal method in your host language (C#, Python, JS, etc.) exposed to SK.Written directly in code.Logic like calling an API, reading a file, or performing calculations.
🔹 Prompt FunctionA function defined by a text prompt template (can include system prompt + inputs).Defined in .txt, .skprompt.txt, or inline string templates.LLM-powered behaviors (“Summarize this email”, “Translate text”).
🔹 OpenAPI FunctionA function defined from an OpenAPI/Swagger specification.Imported from a JSON/YAML OpenAPI file or URL.Connect to REST APIs declaratively.
🔹 MCP (Microsoft Copilot Protocol) FunctionA function exposed via MCP to talk to Copilot plugins or MCP-enabled services.Registered via the MCP interface/endpoint.Connect external copilots or services (e.g., M365 Copilot, Fabric).
🔹 PluginA named collection (folder) of functions (any type above).Folder structure or declarative registration.Group related functions (“CalendarPlugin”, “CRMPlugin”).

Native (code) functions

Use native functions when you need determinism, local IO, or secure service access.

  • Pros: Full control, testable, easy debugging, no model cost, strong typing, secure secrets handling.
  • Cons: You must write the logic; less flexible than prompts for text reasoning.

C# example:

public class MathPlugin
{
    [KernelFunction("add")]
    public double Add(double x, double y) => x + y;
}

var builder = Kernel.CreateBuilder();
builder.Plugins.AddFromType<MathPlugin>();
var kernel = builder.Build();

DI lifetimes for native functions (.NET)

You’re referring to the three standard service lifetimes in .NET dependency injection (DI) — they define how long a service instance lives and when it’s created/disposed.

LifetimeKeywordDescriptionTypical Use
🟦 SingletonAddSingleton<TService, TImplementation>()Created once for the entire application lifetime (shared instance).Shared configuration, logging, caching, HTTP clients.
🟩 ScopedAddScoped<TService, TImplementation>()Created once per request (or per scope).Web request–specific services, database contexts (e.g., EF Core DbContext).
🟨 TransientAddTransient<TService, TImplementation>()Created every time it’s requested.Lightweight, stateless services, formatters, calculators, etc.

Example:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConfigService, ConfigService>();
    services.AddScoped<IUserRepository, UserRepository>();
    services.AddTransient<IEmailFormatter, EmailFormatter>();
}
  • IConfigService → one instance for the entire app.
  • IUserRepository → new instance per HTTP request.
  • IEmailFormatter → new instance every time it’s injected.

Bonus tip (hosted services): when using BackgroundService, create a scope to resolve scoped services:

using (var scope = _serviceProvider.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
    // use db here
}

OBO (on‑behalf‑of) auth flow tip

  • Keep HttpClient creation via IHttpClientFactory (registered as singleton factory) and inject typed clients into functions.
  • Use a scoped token acquisition service (e.g., ITokenAcquisition from Microsoft.Identity.Web) so each request/user gets the right access token.
  • Never capture a scoped service in a singleton; inject abstractions where needed and create scopes when running outside HTTP pipelines.

Prompt functions

Prompt functions are text templates executed by an LLM.

  • Pros: Fast to prototype, flexible, great for summarization, extraction, translation.
  • Cons: Non‑deterministic, requires model access, prompt/guardrail engineering, higher latency/cost.

Example:

const string prompt = """
Summarize the following text in one sentence:
{{$input}}
""";

var summarize = kernel.CreateFunctionFromPrompt(prompt, "summarize");
var result = await kernel.InvokeAsync(summarize, new() { ["input"] = text });

OpenAPI functions

Import REST APIs as callable functions.

  • Pros: Declarative, discoverable, encourages least‑privilege API usage, consistent shapes.
  • Cons: Spec quality matters; some APIs need auth wiring; advanced flows may still need native code.
var weather = await kernel.ImportPluginFromOpenApiAsync(
    "weather",
    new Uri("https://api.weatherapi.com/v1/openapi.json")
);
var forecast = await kernel.InvokeAsync(weather["getForecast"], new() { ["location"] = "Vienna" });

OpenAPI vs. Native — when to choose what?

  • Choose OpenAPI if:
    • The API has a solid OpenAPI spec you trust.
    • You want declarative, low‑code connect‑and‑call.
    • Governance requires explicit API contracts.
  • Choose Native if:
    • You need custom auth flows (e.g., complex OBO, mTLS), retries, or circuit breakers.
    • Business rules or transformations are complex.
    • You need full testability and rich telemetry.

MCP (Microsoft Copilot Protocol) functions

Connect SK to Copilot plugins or MCP‑enabled services (e.g., Microsoft 365 Copilot, Fabric).

  • Pros: Standardized capability discovery and invocation; enterprise‑grade connectors.
  • Cons: Requires MCP endpoints and proper app registration/permissions.
var builder = Kernel.CreateBuilder();
// Example shape — actual registration depends on SDK updates
builder.AddMcpServer("fabric", new Uri("https://fabric.microsoft.com/mcp"));
var kernel = builder.Build();

Plugins (function collections)

A plugin is a named group of functions (any mix of types) plus metadata.

  • Pros: Organization, reuse, permissions scoping, easy enable/disable.
  • Cons: Keep boundaries clear; over‑large plugins become hard to reason about.

Typical folder:

/Plugins
 └── CalendarPlugin
     ├── GetEvents.skprompt.txt
     ├── CreateEvent.skprompt.txt
     └── manifest.json

Register:

kernel.Plugins.AddFromPromptDirectory("Plugins/CalendarPlugin");