# How to Use the Skills System with SkillScript and SkillResource in the Agent Framework

> Learn to extend Microsoft Agent Framework with the skills system SkillScript SkillResource and SkillsProvider for reusable agent capabilities and LLM integration.

- Repository: [Microsoft/agent-framework](https://github.com/microsoft/agent-framework)
- Tags: how-to-guide
- Published: 2026-04-05

---

**The Skills system in the Microsoft Agent Framework lets you extend agents with reusable capabilities by defining `Skill` containers that hold static or dynamic `SkillResource` objects for data lookup and `SkillScript` callables for executable actions, all bridged to the LLM through the `SkillsProvider` class.**

The Microsoft Agent Framework provides a modular **skills system** that enables you to package reusable capabilities into self-describing units. By combining `SkillScript` for executable logic and `SkillResource` for structured data access, you can create both code-defined and file-based skills that agents invoke through automatic tool discovery according to the `microsoft/agent-framework` source code.

## Understanding Core Components in [`_skills.py`](https://github.com/microsoft/agent-framework/blob/main/_skills.py)

The core implementation resides in [[`python/packages/core/agent_framework/_skills.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_skills.py)](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_skills.py). Three classes work together to form the skills architecture:

- **`Skill`** – The top-level container holding a name, description, instructions, and optional collections of resources or scripts
- **`SkillResource`** – Either a static text block supplied at construction time or a callable registered with `@skill.resource` that returns data on demand when the LLM calls `read_skill_resource`
- **`SkillScript`** – An executable routine that the agent invokes via `run_skill_script`, defined either as a code-defined Python callable decorated with `@skill.script` or discovered automatically from files on disk

## Defining Code-Defined Skills with Python

Code-defined skills are instantiated directly in Python and passed to `SkillsProvider`, allowing you to mix dynamic logic with static configuration.

### Creating Static SkillResources

Attach fixed content to a skill using the `SkillResource` class with the `content` parameter. These resources are returned verbatim when the LLM requests them.

```python
from agent_framework import Skill, SkillResource
from textwrap import dedent

unit_converter = Skill(
    name="unit-converter",
    description="Convert between common units",
    content="Use this skill when the user asks to convert units.",
    resources=[
        SkillResource(
            name="conversion-tables",
            content=dedent("""\
                | From       | To         | Factor   |
                |------------|------------|----------|
                | miles      | kilometres | 1.60934 |
                | pounds     | kilograms  | 0.453592|
            """),
        ),
    ],
)

```

### Registering Dynamic Resources with @skill.resource

For data that must be generated at runtime, register a callable with the `@skill.resource` decorator. The framework executes this function when the LLM calls `read_skill_resource`, forwarding any runtime arguments passed via `function_invocation_kwargs`.

```python
@unit_converter.resource(name="conversion-policy", description="Formatting policy")
def conversion_policy(**kwargs):
    # Access runtime parameters passed during agent.run()

    precision = kwargs.get("precision", 4)
    return f"Decimal places: {precision}"

```

### Implementing Executable Logic with @skill.script

**SkillScripts** expose behavior the agent can trigger. Decorate Python functions with `@skill.script` to register executable routines that accept parameters from the LLM plus optional runtime keyword arguments.

```python
import json

@unit_converter.script(name="convert", description="Convert value using factor")
def convert_units(value: float, factor: float, **kwargs):
    precision = kwargs.get("precision", 4)
    result = round(value * factor, precision)
    return json.dumps({"value": value, "result": result})

```

## Loading File-Based Skills from Disk

The framework automatically discovers skills from directories containing a [`SKILL.md`](https://github.com/microsoft/agent-framework/blob/main/SKILL.md) file, allowing you to manage skills as static assets separate from your application code.

### The SKILL.md Convention and Directory Structure

Create a directory structure where each skill lives in its own folder containing a [`SKILL.md`](https://github.com/microsoft/agent-framework/blob/main/SKILL.md) file with YAML front-matter:

```

my_skills/
└─ greet/
   ├─ SKILL.md
   ├─ greetings.md          # Static resource

   └─ hello.py              # File-based script

```

The [`SKILL.md`](https://github.com/microsoft/agent-framework/blob/main/SKILL.md) file defines the skill metadata and instructions:

```markdown
---
name: greeting
description: Generate a friendly greeting
---
Use the **greetings** resource for tone and call the **hello** script to produce a personalized message.

```

### Automatic Discovery via SkillsProvider

Point `SkillsProvider` to the parent directory containing your skill folders. The framework parses [`SKILL.md`](https://github.com/microsoft/agent-framework/blob/main/SKILL.md) files and loads any accompanying resources and scripts matching `DEFAULT_SCRIPT_EXTENSIONS` (`.py` by default).

```python
from agent_framework import SkillsProvider

provider = SkillsProvider(skill_paths="./my_skills")

```

**Note:** File-based scripts require a `SkillScriptRunner` to execute. If you omit a runner, the provider raises an error when the LLM attempts to invoke file-based scripts.

## Wiring Skills to Your Agent with SkillsProvider

`SkillsProvider` acts as a `ContextProvider` that bridges your skills to the agent, exposing three automatic tools: `load_skill`, `read_skill_resource`, and `run_skill_script`.

### Exposing Tools to the LLM

Instantiate `SkillsProvider` with your code-defined skills and optional file-based paths, then add it to the agent's `context_providers` list:

```python
from agent_framework import Agent, SkillsProvider
from agent_framework.foundry import FoundryChatClient

provider = SkillsProvider(
    skills=[unit_converter],           # Code-defined skills

    skill_paths="./my_file_skills",    # Optional file-based discovery

)

async with Agent(
    client=client,
    instructions="You are a helpful assistant.",
    context_providers=[provider],
) as agent:
    response = await agent.run(
        "How many kilometres is 26.2 miles?",
        function_invocation_kwargs={"precision": 2},
    )

```

When the agent runs, the provider automatically injects an XML-escaped list of available skills into the system prompt, enabling the LLM to discover and invoke them.

### Implementing Human-in-the-Loop Approval

For sensitive operations, enable `require_script_approval=True` to force the agent to pause and request human approval before executing any `SkillScript`. The framework returns a `function_approval_request` that the host application must approve or reject.

```python
provider = SkillsProvider(
    skills=[deployment_skill],
    require_script_approval=True,
)

# During execution...

while result.user_input_requests:
    for req in result.user_input_requests:
        print(f"Approval needed for: {req.function_call.name}")
        # Host application decides whether to approve

        response = req.to_function_approval_response(approved=True)
        result = await agent.run(response, session=session)

```

## Complete Implementation Example

This complete example demonstrates a code-defined skill with static resources, dynamic resources, and executable scripts, including runtime parameter passing:

```python
from agent_framework import Skill, SkillResource, SkillsProvider, Agent
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from textwrap import dedent
import json
import os
import asyncio

# 1. Define the skill with static resources

unit_converter = Skill(
    name="unit-converter",
    description="Convert between metric and imperial units",
    content=dedent("""\
        Use this skill for unit conversions.
        1. Check conversion-tables resource for the factor.
        2. Call the convert script with value and factor.
    """),
    resources=[
        SkillResource(
            name="conversion-tables",
            content=dedent("""\
                miles to km: 1.60934
                kg to lbs: 2.20462
            """),
        ),
    ],
)

# 2. Add a dynamic resource that uses runtime kwargs

@unit_converter.resource(name="policy")
def get_policy(**kwargs):
    precision = kwargs.get("precision", 4)
    return f"Use {precision} decimal places."

# 3. Add an executable script

@unit_converter.script(name="convert")
def convert(value: float, factor: float, **kwargs):
    precision = kwargs.get("precision", 4)
    return json.dumps({"result": round(value * factor, precision)})

async def main():
    client = FoundryChatClient(
        project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
        credential=AzureCliCredential(),
    )
    
    provider = SkillsProvider(skills=[unit_converter])
    
    async with Agent(
        client=client,
        instructions="You are a unit conversion assistant.",
        context_providers=[provider],
    ) as agent:
        result = await agent.run(
            "Convert 26.2 miles to kilometres",
            function_invocation_kwargs={"precision": 2},
        )
        print(result)

if __name__ == "__main__":
    asyncio.run(main())

```

## Summary

- **Skill** containers defined in [`_skills.py`](https://github.com/microsoft/agent-framework/blob/main/_skills.py) bundle together instructions, `SkillResource` objects for data, and `SkillScript` objects for actions
- **SkillResources** can be static strings supplied at construction time or dynamic callables registered with `@skill.resource` that execute when the LLM calls `read_skill_resource`
- **SkillScripts** expose executable logic via `@skill.script` decorators for code-defined functions or through file-based scripts discovered from disk
- **`SkillsProvider`** bridges skills to agents as a `ContextProvider`, automatically advertising available capabilities and optionally enforcing human-in-the-loop approval via `require_script_approval=True`
- Runtime parameters flow through `function_invocation_kwargs` in `agent.run()` and arrive as `**kwargs` in dynamic resources and scripts

## Frequently Asked Questions

### What is the difference between SkillResource and SkillScript?

**SkillResource** provides data or reference material that the LLM reads using the `read_skill_resource` tool, returning either static content or the result of a dynamic callable. **SkillScript** provides executable behavior that the LLM triggers with the `run_skill_script` tool, performing calculations, API calls, or other actions and returning results to the conversation.

### How do I pass runtime parameters to skill resources and scripts？

Pass parameters via the `function_invocation_kwargs` dictionary in your `agent.run()` call. The framework forwards these as `**kwargs` to any dynamic resource callable or script function that accepts them, allowing you to customize behavior like precision, user preferences, or contextual data without modifying the skill definition.

### Can I mix code-defined and file-based skills in the same agent？

Yes. The `SkillsProvider` accepts both a `skills` list for code-defined instances and a `skill_paths` parameter for directory discovery. According to the source code in [`_skills.py`](https://github.com/microsoft/agent-framework/blob/main/_skills.py), these approaches are supported simultaneously, allowing you to combine dynamic Python logic with static skill assets managed on disk.

### What happens if I don't provide a SkillScriptRunner for file-based scripts？

If you attempt to execute a file-based script without supplying a `SkillScriptRunner` to `SkillsProvider`, the framework raises a clear error indicating that the script cannot be executed. Code-defined scripts run in-process and do not require a runner, but external script files need a runner to handle execution in a subprocess or other environment.