Semantic Kernel Plugins
Plugins are the fundamental building blocks for extending Semantic Kernel applications. They group related functions—both native code and AI‑prompted logic—into reusable, discoverable modules that agents and workflows can call. This handbook article is a practical, implementation‑focused guide to designing, registering, organising, and invoking plugins. You’ll learn how plugins expose tools, how the Kernel manages their lifecycle, and how to apply patterns that make your AI applications modular, secure, and performant.
What Are Plugins in Semantic Kernel
A plugin is a container for one or more functions. It provides a namespace, a set of related capabilities, and metadata that helps the AI model decide when and how to use those capabilities. Plugins turn isolated functions into a coherent toolset that the Kernel can discover and invoke automatically.
In Semantic Kernel, you can build plugins in several ways:
- As a class with methods decorated as
@kernel_function(Python) or[KernelFunction](.NET/Java). - As a directory of prompt templates (YAML + text files) that define prompt‑based functions.
- As an OpenAPI specification that the Kernel converts into callable functions.
- As a hybrid combining both native methods and prompt templates.
A simple Python plugin example:
from semantic_kernel.functions import kernel_function
from datetime import datetime
class TimePlugin:
@kernel_function(description="Get the current date and time")
def get_current_time(self) -> str:
return datetime.now().isoformat()
Once registered with the Kernel, the function becomes available as TimePlugin.get_current_time. The AI model can then decide to call it when a user asks “What time is it?”
Why Plugins Matter
Plugins bring software engineering best practices to AI applications:
- Modular development – Group functions by domain (e.g.,
CalendarPlugin,OrderPlugin), making the codebase easier to navigate. - Reusability – A well‑designed plugin can be shared across multiple agents or applications.
- Maintainability – Isolated plugins can be updated or replaced without affecting unrelated logic.
- Tool integration – Encapsulate external API calls, database access, or complex computations behind a clean function interface.
- Separation of concerns – Native functions handle deterministic business logic; prompt functions handle AI‑driven text generation. Plugins keep these worlds organised.
For the LLM, plugins are the tool definitions; the Kernel automatically generates JSON schemas from the function signatures and descriptions, enabling the model to select and call the right tool.
Plugins vs Functions
It’s important to distinguish between the two concepts:
| Concept | Scope | Example |
|---|---|---|
| Function | A single executable unit of logic. | get_current_time, summarize_text |
| Plugin | A collection of related functions. | TimePlugin containing get_current_time and get_current_date |
Functions are the “what”; plugins are the “how they’re organised”. You never invoke a plugin directly—you always invoke a specific function inside a plugin.
Core Plugin Concepts
| Concept | Purpose |
|---|---|
| Plugin | Container that groups functions and provides a namespace. |
| Function | An individual operation—native code or a prompt template. |
| Invocation | The act of calling a function through the Kernel. |
| Parameters | Typed inputs that the function receives from the caller or the AI model. |
| Connectors | Optional services (database clients, API wrappers) that plugins use internally. |
All these are managed by the Kernel, which acts as the dependency injection container and execution engine.
Plugin Structure
A typical plugin organizes functions under a common namespace. Whether you implement it as a class or as a directory, the structure is similar.
TimePlugin (namespace)
├── get_current_time (function)
├── get_current_date (function)
└── convert_timezone (function)
In a Python class:
class TimePlugin:
@kernel_function(description="Get current time")
def get_current_time(self) -> str: ...
@kernel_function(description="Get current date")
def get_current_date(self) -> str: ...
@kernel_function(description="Convert time between zones")
def convert_timezone(self, time: str, from_tz: str, to_tz: str) -> str: ...
For prompt‑based plugins, you create a directory containing a config.yaml and a skprompt.txt (or use YAML‑only syntax). The directory name becomes the plugin name, and each YAML file defines a function.
Types of Plugins
Native Plugins
Native plugins contain functions written in your application language. They are ideal for:
- Deterministic business logic (calculations, validations)
- Access to internal services (CRM, ERP)
- Database queries
- External API calls (wrapping
httpx,requests, etc.)
Example: an order lookup plugin.
class OrderPlugin:
def __init__(self, api_client):
self.api_client = api_client
@kernel_function(description="Lookup an order by ID and return its status")
async def get_order_status(self, order_id: str) -> str:
response = await self.api_client.get(f"/orders/{order_id}")
return response.json()["status"]
The Kernel injects dependencies if you use its built‑in dependency injection (in .NET) or if you pass them via the constructor and register the plugin object manually in Python.
Prompt‑Based Plugins
These plugins contain functions defined entirely as prompt templates. They rely on an AI service to generate output. Use them for:
- Summarisation
- Translation
- Classification
- Content generation
Example: a YAML‑defined summarization function.
Directory SummarizePlugin/Summarize/config.yaml:
name: Summarize
description: Summarize a long text into a concise paragraph
template: |
Summarize the following text in 2-3 sentences:
{{$input}}
template_format: semantic-kernel
execution_settings:
default:
model_id: gpt-4o
temperature: 0.3
max_tokens: 200
Load it into the Kernel:
kernel.add_plugin_from_prompt_directory("./Plugins/SummarizePlugin")
The Kernel will expose a function SummarizePlugin.Summarize. When invoked, it renders the template, calls the AI service, and returns the generated text.
Hybrid Plugins
A hybrid plugin mixes native functions and prompt functions in the same namespace. This is useful when you need AI‑driven logic combined with deterministic steps. For example, a CustomerSupportPlugin might have a native function to fetch order data and a prompt function to craft a polite response.
In a class, you can define a prompt function by adding a kernel_function decorator to a method that returns a PromptTemplateConfig or by using the create_function_from_prompt method. Alternatively, you can combine a class-based plugin with additional prompt functions loaded from a directory, sharing the same namespace.
Plugin Registration
Plugins must be registered with the Kernel before they can be used. The Kernel acts as a registry and a service container.
Registering a native plugin instance:
plugin = TimePlugin()
kernel.add_plugin(plugin, plugin_name="Time")
Registering from a prompt directory:
kernel.add_plugin_from_prompt_directory("./Plugins/WriterPlugin", plugin_name="Writer")
Registering from an OpenAPI specification (requires extension):
// .NET example
await kernel.ImportPluginFromOpenApiAsync("PetStore", new Uri("https://petstore.swagger.io/v2/swagger.json"));
Once registered, all functions in the plugin become available through the Kernel. The LLM will see them when function calling is enabled, and you can also invoke them programmatically.
Lifecycle management: The plugin instance lives as long as the Kernel. For plugins that hold external connections, ensure you clean up resources when the Kernel is disposed (in .NET) or manually close connections in Python when the application shuts down.
Function Exposure
For a function to be callable by the AI model, it must have clear metadata:
- Name – derived from the method name or the YAML file.
- Description – a natural‑language explanation of what the function does. This is the most critical piece; the model uses it to decide when to call the function.
- Parameters – each parameter’s name, type, and description (via docstrings, type hints, or dedicated attributes).
- Return type – the output description, often inferred from the docstring or explicitly set.
Example: richly described function.
@kernel_function(
name="get_weather",
description="Get the current weather for a city and optional country code."
)
def get_weather(
city: Annotated[str, "The city name, e.g., 'London'"],
country: Annotated[str, "Optional two-letter country code"] = "US"
) -> Annotated[str, "A brief weather report"]:
...
The Kernel automatically builds the JSON schema for the function. Good descriptions make the difference between the model correctly calling the function or ignoring it.
Plugin Invocation Lifecycle
When the Kernel needs to invoke a function (whether triggered by an AI model’s function call or directly by your code), it follows a consistent lifecycle:
For agents using function calling, this lifecycle is embedded in a larger loop: the AI decides to call a function, the Kernel executes it, the result is added to the chat history, and the AI continues reasoning.
Input and Output Handling
Functions receive input through parameters, and they return output as strings (or objects that the Kernel converts to strings). For prompt functions, the entire context is passed via KernelArguments, which contains key‑value pairs like {{$input}} and named variables.
Structured inputs: use Pydantic models in Python (or [Description] attributes in .NET) to define complex parameter schemas. The Kernel will parse the JSON provided by the LLM into the expected types.
Structured outputs: native functions can return structured data (e.g., a JSON string). Prompt functions return raw text; you can post‑process it in a subsequent native function.
Validation: input validation should be performed inside the function. If the LLM passes invalid arguments, the function can return an error message, which the LLM may use to self‑correct.
Plugin Design Patterns
| Pattern | Description | Example |
|---|---|---|
| Utility Plugin | Small, stateless functions that provide common services. | DateTimePlugin, MathPlugin |
| Business Service Plugin | Encapsulates a business domain, often with dependencies. | OrderPlugin that uses an order service client. |
| Data Access Plugin | Functions that read/write to databases or file systems. | CustomerDbPlugin with get_customer_by_id. |
| Workflow Plugin | Orchestrates multi‑step processes; may call other plugins internally. | ReportGenerationPlugin that calls a data plugin and a formatting plugin. |
| Adapter Plugin | Wraps an external API or legacy system. | CRMPlugin that translates between internal models and the CRM API. |
Each pattern helps maintain a clean separation between AI orchestration and business logic.
Tool Calling Through Plugins
In Semantic Kernel, tools are essentially the functions exposed by plugins. When you create an agent (e.g., ChatCompletionAgent), you pass it a list of plugins or enable automatic function calling. The Kernel then:
- Sends the model a list of all available functions (with names, descriptions, and parameter schemas).
- The model returns a function call request if it deems a tool is needed.
- The Kernel executes the function (native or prompt).
- The result is injected into the conversation history as a “tool” message.
- The model continues until it produces a final answer.
This is the same pattern used by other frameworks. Semantic Kernel’s advantage is that its plugin model fits naturally into enterprise dependency injection and service lifecycles.
Plugin Security
Plugins often have access to sensitive operations. Implement security at multiple levels:
- Input validation – sanitise and validate all parameters before using them. Reject suspicious inputs early.
- Permission control – in enterprise apps, use role‑based access inside the function. Check the user’s claims passed via the
KernelArgumentsbefore executing sensitive operations. - Secret management – never hard‑code API keys. Use environment variables, Azure Key Vault, or the Kernel’s service provider to inject secrets.
- Output validation – sanitise function outputs before passing them back to the AI, especially if they will be displayed to users or used in prompts.
- External service protection – wrap external calls with retry logic, timeouts, and circuit breakers. Avoid exposing internal URLs or allowing SSRF.
Plugin Performance Optimization
Since plugins run synchronously within an agent turn, they must be fast to avoid delaying the user. Optimization checklist:
- Cache results of deterministic, frequently‑called functions (e.g., product catalog). Use a local in‑memory cache or Redis.
- Use async functions for I/O‑bound operations (
async def) to avoid blocking the event loop. - Limit payload sizes: return summaries or truncated data rather than large raw responses.
- Set timeouts on external API calls to prevent hanging.
- Batch multiple independent calls where possible (e.g., gather all order details in one call).
- Profile native functions to identify bottlenecks.
Common Beginner Mistakes
- Large monolithic plugins – one
GeneralPluginwith 30 functions. Break them into domain‑specific plugins. - Poor function naming – names like
do_stufforhandlegive the LLM no signal. Use descriptive verb phrases. - Weak descriptions – missing or vague descriptions cause the model to ignore or misuse functions.
- Excessive plugin coupling – plugins that depend on each other tightly become hard to test. Use dependency injection.
- Missing validation – trusting LLM‑generated arguments can lead to crashes or security issues.
- No error handling – letting exceptions bubble up from native functions causes the agent run to fail.
Best Practices
- Keep plugins focused – one domain concept per plugin.
- Design reusable functions – stateless, idempotent, and well‑documented.
- Use clear metadata – invest in good descriptions and parameter annotations.
- Validate inputs – at the beginning of each function.
- Monitor execution – log function calls, arguments (sanitised), durations, and errors.
- Keep business logic isolated – native functions should be testable independently of the Kernel.
- Version your plugins – store prompt YAML files and class definitions in source control; use tags for releases.
Practical Example: Customer Support Plugin Suite
Let’s build a support system with three plugins: CustomerPlugin (fetch customer data), BillingPlugin (invoice lookup), and KnowledgePlugin (search FAQs). These will be used by an agent.
1. Define the plugins
CustomerPlugin (native):
from semantic_kernel.functions import kernel_function
class CustomerPlugin:
def __init__(self, crm_client):
self.crm = crm_client
@kernel_function(description="Get customer details by ID")
async def get_customer(self, customer_id: str) -> str:
data = await self.crm.fetch(customer_id)
return f"Name: {data['name']}, Tier: {data['tier']}"
BillingPlugin (native):
class BillingPlugin:
def __init__(self, billing_service):
self.billing = billing_service
@kernel_function(description="Get invoice details by invoice number")
async def get_invoice(self, invoice_id: str) -> str:
result = await self.billing.lookup(invoice_id)
return json.dumps(result)
KnowledgePlugin (prompt‑based): a directory containing SearchFAQ/config.yaml:
name: SearchFAQ
description: Search the FAQ for a given query and return the most relevant article
template: |
Given the FAQ article: {{$input}}
Answer the user's question based on this article.
(We’d load this from a prompt directory.)
2. Register plugins with the Kernel
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(service_id="gpt", ...))
kernel.add_plugin(CustomerPlugin(crm_client), "Customer")
kernel.add_plugin(BillingPlugin(billing_service), "Billing")
kernel.add_plugin_from_prompt_directory("./Plugins/KnowledgePlugin", "Knowledge")
3. Create an agent
from semantic_kernel.agents import ChatCompletionAgent
agent = ChatCompletionAgent(
kernel=kernel,
name="SupportAgent",
instructions="You are a support assistant. Use the available plugins to answer user questions.",
plugins=["Customer", "Billing", "Knowledge"]
)
4. Handle a user request
response = await agent.get_response(messages="I need invoice INV-100 for customer CUST-50.")
What happens:
- The AI receives the instruction, chat history, and the three plugins with their function descriptions.
- It recognises that it needs
Billing.get_invoice(arguments:invoice_id="INV-100") and maybeCustomer.get_customer(customer_id="CUST-50"). - The Kernel invokes these native functions sequentially (or in parallel if the model requests multiple calls), gathering invoice data and customer details.
- The results are added to the conversation; the AI then asks the knowledge plugin for any relevant FAQ about invoices.
- Finally, it crafts a response that includes the invoice status, customer name, and a helpful FAQ snippet.
The plugin architecture keeps the agent’s logic clean and the functions reusable across different agents or workflows.
Plugins and Other Semantic Kernel Concepts
- Core Concepts – The Kernel, AI services, and memory form the context in which plugins operate.
- Planners – Planners automatically chain multiple plugin functions to achieve a goal.
- Memory – Plugins can use memory to store and retrieve facts.
- Production – Production deployment requires securing, monitoring, and scaling plugins.
- MCP – MCP servers can be wrapped as plugins, giving the Kernel access to a standardised tool ecosystem.
Plugins vs Tool Systems in Other Frameworks
| Framework | Tool abstraction | Organisation | Reusability model |
|---|---|---|---|
| Semantic Kernel | Functions grouped into Plugins | Strong namespacing, dependency injection | Plugins can be shared as NuGet/pip packages; native integration with enterprise services |
| OpenAI Agents SDK | Tools (function_tool) | Flat list per agent; no built-in grouping | Tools are Python functions; no formal plugin container |
| LangGraph / LangChain | Tools (BaseTool) | Tools can be organised in Toolkits or custom lists | Toolkits group related tools; can be loaded from community packages |
| CrewAI | Tools (BaseTool) | Tools attached directly to agents; no separate plugin model | Tools are reused across agents by assignment |
| AutoGen | Function tools (FunctionTool) | Tools registered with agents; no container abstraction | Tools are callable objects; can be shared by passing instances |
Semantic Kernel’s plugin model provides the strongest organisational structure and is designed from the ground up for enterprise dependency injection and lifecycle management.
FAQ
1. What is a Plugin in Semantic Kernel?
A plugin is a container for related functions. It provides a namespace and grouping for native code and prompt‑based functions.
2. How do Plugins work?
You create a class with @kernel_function methods or a directory of YAML prompt templates, then register them with the Kernel. The Kernel makes them available for programmatic invocation or via AI function calling.
3. How are Plugins different from Functions?
A function is a single operation; a plugin is a collection of functions. Plugins provide organisation and namespace.
4. How do I register Plugins?
Use kernel.add_plugin(instance, plugin_name) for native plugins, kernel.add_plugin_from_prompt_directory(path) for prompt‑based plugins, or import from OpenAPI specs.
5. Can Plugins call external APIs?
Yes. Native plugins can use any HTTP client or SDK to access external services. It’s a common pattern to wrap APIs as plugin functions.
6. How are Plugins secured?
Implement input validation, output sanitisation, and permission checks inside the function. Use secret managers for credentials and never hard‑code them.
7. Are Plugins production‑ready?
Yes. Semantic Kernel is used in enterprise production environments. Plugins can be monitored, logged, and deployed as part of a microservice.
8. Can I mix native and prompt functions in one plugin?
Yes, a hybrid plugin can contain both. The Kernel handles each appropriately.
9. How does the AI model know which plugin functions are available?
When you create an agent with function calling enabled, the Kernel sends a JSON schema of all available functions (from the specified plugins) along with the prompt.
10. Can I use dependency injection with plugins?
In .NET, yes, using standard IServiceCollection. In Python, you manage dependencies manually (pass them to the constructor or inject via a custom service provider).
11. How do I test plugins?
Instantiate the plugin class and call its functions directly. For prompt functions, test the rendered template with a mock AI service.
12. What is the best practice for naming plugins and functions?
Use PascalCase for plugins (OrderPlugin) and verb‑noun for functions (get_order_status). Keep names descriptive and consistent.
13. Can plugins be shared across projects?
Yes, you can package plugin classes and prompt directories as Python packages or NuGet libraries and import them into multiple kernels.
14. How does Semantic Kernel handle conflicting function names?
Each plugin has a unique namespace. Functions are fully qualified as pluginName.functionName. Conflict only occurs if you register two plugins with the same name.
15. Can I dynamically load plugins at runtime?
Yes, you can call kernel.add_plugin(...) at any point before invocation. This allows conditional loading based on configuration or user permissions.
Conclusion
Semantic Kernel Plugins provide a robust, enterprise‑ready model for organising AI capabilities. By grouping functions into cohesive namespaces, you gain modularity, reusability, and a clean separation of concerns. The Kernel’s integration with dependency injection and its automatic function‑calling orchestration make plugins the natural way to extend agent behaviour.
Key takeaways:
- Plugins are collections of native and/or prompt functions.
- They are registered with the Kernel and exposed through metadata.
- The invocation lifecycle is handled automatically, whether called by code or by an AI model.
- Good descriptions and parameter annotations are critical for AI‑driven selection.
- Security and performance must be built into plugin functions from day one.
- The plugin model is the primary tool integration layer in Semantic Kernel.
Continue your journey with these handbook articles:
- Semantic Kernel Core Concepts – the foundation of the Kernel and execution model.
- Semantic Kernel Planners – chain plugin functions into multi‑step plans.
- Semantic Kernel Memory – give your plugins long‑term context.
- Semantic Kernel Production – deploy, monitor, and secure your plugin‑powered agents.
For cross‑framework tool comparisons, explore the LangGraph, CrewAI, AutoGen, and OpenAI Agents SDK guides. The framework comparison can help you choose. To standardize tool integration, see the MCP Guide.
Now, build your plugins—and let the Kernel orchestrate them into intelligent, connected applications.