{"slug": "agent-skills-in-microsoft-agent-framework", "title": "Agent Skills in Microsoft Agent Framework", "summary": "The Microsoft Agent Framework has added skills support in beta, built around progressive disclosure that allows agents to load domain expertise on demand rather than including all capabilities in the system prompt. Once a skill is loaded within a single session, its full content remains in the conversation for the entire session duration without eviction or budget limits. A developer confirmed this behavior by building a console application running against a local Ollama model, tracing every HTTP call to observe how skills are triggered and retained.", "body_md": "The [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) recently added **skills** support, built around *progressive disclosure* (still in beta). The [Give Your Agents Domain Expertise with Agent Skills](https://devblogs.microsoft.com/agent-framework/give-your-agents-domain-expertise-with-agent-skills-in-microsoft-agent-framework/) devblog is an excellent introduction, so I won't re-tread the basics here.\n\nIf you've used skills in a coding agent, the idea is familiar: a skill is just a folder — a `SKILL.md`\n\nmanifest plus reference documents and scripts — that the agent discovers and pulls in *only when it needs to*. Instead of stuffing every capability into the system prompt, the agent sees a lightweight catalog of skill names and descriptions, and loads the full content on demand. That's the whole point of progressive disclosure: an agent's context is a budget, and skills are a way to spend it lazily.\n\nIn practice that part just works: when a request matches a skill, the model is nudged to call the built-in `load_skill`\n\ntool, and the framework returns the skill's full content for the model to use. Triggering and loading behave exactly as advertised.\n\nBut spending the budget is only half the story. Once a skill's content is loaded, *where does it actually live — and is it ever dropped?* The docs are silent on this, and it's the question the rest of this post digs into.\n\nThe short answer: within a session, it isn't dropped at all. Starting a new session drops everything, of course — that much is obvious. The part worth knowing is what happens *inside* a single session: once loaded, a skill's full content stays in the conversation for the entire life of that session. There's no budget, no sliding window, no eviction. The rest of the post shows how I confirmed this, and why it matters.\n\nThe sample is a tiny console app running entirely against a local [Ollama](https://ollama.com) model — no cloud keys, and every HTTP call is traced so I can see exactly what goes over the wire ([complete sample code](https://github.com/StormHub/stormhub/tree/main/resources/2026-06-02/AgentSkillsDemo)). There's a single skill on disk:\n\n```\nskills/unit-converter/\n├── SKILL.md                        # name + description + usage steps\n└── references/conversion-table.md  # the actual conversion factors\n```\n\nWiring it into the agent is one line — `AgentSkillsProvider`\n\nis just an `AIContextProvider`\n\n:\n\n``` js\nvar agentOptions = new ChatClientAgentOptions\n{\n    Name = \"UnitConverterAgent\",\n    ChatOptions = new ChatOptions\n    {\n        Instructions = \"You are a helpful assistant that can convert units. ...\",\n        Tools = [AIFunctionFactory.Create(Tool.Convert)]\n    },\n    AIContextProviders = [skillsProvider],   // <-- skills plug in here\n};\n```\n\nOn every request, that provider does two things. First, it injects a **catalog** of skills — names and descriptions only — into the system prompt. That's the entire \"advertisement\" the model sees up front; no factors, no usage steps:\n\n```\n<available_skills>\n  <skill>\n    <name>unit-converter</name>\n    <description>Convert between common units using a multiplication factor.\n      Use when asked to convert miles, kilometers, pounds, or kilograms.</description>\n  </skill>\n</available_skills>\n```\n\nSecond, it registers three tools the model can call to pull in more on demand: `load_skill`\n\n, `read_skill_resource`\n\n, and `run_skill_script`\n\n.\n\nTo watch the triggering happen, I don't need to read the trace — the framework lets you intercept every tool call with function-invocation middleware. `AIAgentBuilder.Use(...)`\n\nwraps the agent and hands you each call before it runs:\n\n``` js\nvar agent = chatClient.AsAIAgent(agentOptions);\n\nreturn new AIAgentBuilder(agent)\n    .Use(async (_, ctx, next, ct) =>\n    {\n        if (ctx.Function.Name is \"load_skill\" or \"read_skill_resource\" or \"run_skill_script\")\n        {\n            Console.WriteLine($\"Skill triggered: {ctx.Function.Name}({ctx.Arguments.GetValueOrDefault(\"skillName\")})\");\n        }\n        return await next(ctx, ct);\n    })\n    .Build();\n```\n\nThe three skill tools are supplied by the provider, but they flow through the same function-invoking pipeline as my own `Convert`\n\ntool — so this one interceptor sees them all, and I just filter by name.\n\nNow I ask a question that needs the skill:\n\nHow many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?\n\nand the triggering shows up live:\n\n```\nSkill triggered: load_skill(unit-converter)\nSkill triggered: read_skill_resource(unit-converter)\nAgent: A marathon of 26.2 miles is approximately 42.16 kilometers, and 75 kilograms is approximately 165.35 pounds.\n```\n\nSo the disclosure unfolds in stages, exactly as designed:\n\n`unit-converter`\n\nis relevant, and calls `load_skill(\"unit-converter\")`\n\n.`SKILL.md`\n\nas the tool result. Its usage steps tell the model to consult `references/conversion-table.md`\n\n.`read_skill_resource`\n\nto pull that reference, then runs the actual conversion.Each step pulls in a little more context, only when it is needed. This is progressive disclosure working as promised — the part the docs cover well. The interesting question is what happens to all that loaded content next.\n\nSo where does that loaded content go? Straight into the session history — and it stays. After the run I read the history back and tagged the skill messages:\n\n```\n===== Session history after run: 8 messages =====\n  [ 1] [SKILL] assistant call -> load_skill\n  [ 2] [SKILL] tool      tool result          ← the full SKILL.md body\n  [ 3] [SKILL] assistant call -> read_skill_resource\n  [ 4] [SKILL] tool      tool result          ← the reference content\n  ...\n```\n\nThe `load_skill`\n\nbody and the reference are sitting right there as ordinary tool messages, and nothing removes them. That's the part to take away: within a session, loaded skill content lives **forever**. It's not the skills provider holding on to it — `load_skill`\n\njust returns a normal tool message, and a tool message is history like any other. So every subsequent turn on that session re-sends the whole thing. No budget, no sliding window, no eviction; the only thing that clears it is starting a new session.\n\nSkills can be large, so on a long-lived session this adds up fast: you can't keep carrying every loaded skill forward. The fix is compaction, and the framework ships it out of the box. `CompactionProvider`\n\nis just another `AIContextProvider`\n\nyou add alongside the skills provider, and `SummarizationCompactionStrategy`\n\n*summarizes* older history instead of dropping it — and it groups messages so a `load_skill`\n\ncall is never split from its result.\n\nI don't want to compact on *every* turn, though — only when there's actually skill content to reclaim. A `CompactionTrigger`\n\nis just a predicate over the message groups, so I gate it on whether a skill tool was called:\n\n``` js\nCompactionTrigger skillsTriggered = index =>\n    index.Groups.SelectMany(g => g.Messages).Any(History.MentionsSkillTool);\n\nAIContextProviders =\n[\n    skillsProvider,\n    new CompactionProvider(\n        new SummarizationCompactionStrategy(chatClient, skillsTriggered, minimumPreservedGroups: 2)),\n];\n```\n\nCompaction runs before each turn. On a fresh first turn there's nothing to compact; once a skill has been loaded, the next turn triggers a one-off summarization call and the bulky `SKILL.md`\n\nbody drops out of what's sent to the model — replaced by a short summary, while the conversation keeps going. Spend the budget lazily on the way in, reclaim it automatically on the way out.\n\nPlease feel free to reach out on twitter [@roamingcode](https://twitter.com/roamingcode)", "url": "https://wpnews.pro/news/agent-skills-in-microsoft-agent-framework", "canonical_source": "https://dev.to/stormhub/agent-skills-in-microsoft-agent-framework-2gof", "published_at": "2026-06-04 05:52:25+00:00", "updated_at": "2026-06-04 06:12:27.263554+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-products", "large-language-models", "natural-language-processing"], "entities": ["Microsoft Agent Framework", "Microsoft"], "alternates": {"html": "https://wpnews.pro/news/agent-skills-in-microsoft-agent-framework", "markdown": "https://wpnews.pro/news/agent-skills-in-microsoft-agent-framework.md", "text": "https://wpnews.pro/news/agent-skills-in-microsoft-agent-framework.txt", "jsonld": "https://wpnews.pro/news/agent-skills-in-microsoft-agent-framework.jsonld"}}