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

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

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). 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.

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.

@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.

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 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 file with YAML front-matter:


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

   └─ hello.py              # File-based script

The SKILL.md file defines the skill metadata and instructions:

---
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 files and loads any accompanying resources and scripts matching DEFAULT_SCRIPT_EXTENSIONS (.py by default).

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:

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.

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:

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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →