FastMCP 4.2: skills over MCP
fix a skill on the server, and every agent has the fix
A skill teaches an agent how to use your tools, but the two usually ship separately. The tools live on your MCP server. The skill gets installed as files: copied into each person's skills folder, or bundled into a plugin and published to a marketplace. Every install is a snapshot. When you change the server, the skills already installed don't change with it, so people keep following the old instructions until each of them reinstalls or a new plugin version ships.
MCP is how AI apps connect to things that live somewhere else. With the new Skills extension, a server can serve its skills alongside its tools, and agents read them from the server when they need them. FastMCP 4.2 makes that server. Our support for the extension is experimental while clients catch up.
Say your deploy skill still tells agents to run a migration step you dropped last month. Someone fixes it and redeploys the team's MCP server, and the next deploy any agent runs, on anyone's laptop, follows the corrected steps.
pip install fastmcp -UServe the folder you already have
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.skills.provider import SkillsDirectoryProvider
mcp = FastMCP("Team Skills", providers=[SkillsDirectoryProvider(Path("skills"))])Every subfolder with a SKILL.md becomes a skill. FastMCP checks each one against the Agent Skills spec when the server starts, and a broken one stops startup with an error telling you what to fix (better than finding out from a confused agent). It reads the folders once at startup, so restart the server after you change a skill, or pass reload=True while you're editing.
For clients that can't read skills yet
VS Code reads skills straight off an MCP server as of 1.146, in preview behind the chat.agentHost.mcpSkills.enabled setting. Most other clients don't yet, but they can all call tools. Add ResourcesAsTools and the server also exposes list_resources and read_resource tools, so the agent reads the same live copy:
from fastmcp.server.transforms import ResourcesAsTools
mcp.add_transform(ResourcesAsTools(mcp))For agents that only load skills from a folder, FastMCP's client can copy a server's skills into it:
import asyncio
from pathlib import Path
from fastmcp import Client
from fastmcp.skills import SkillsClientExtension
target = Path.home() / ".claude" / "skills"
client = Client("http://localhost:8000/mcp", extensions=[SkillsClientExtension()])
async def main():
async with client:
for entry in await client.list_skills():
skill = await client.read_skill(entry.uri)
skill.to_directory(target / skill.name, overwrite=True)
asyncio.run(main())It checks every file's size and SHA-256 against the server's manifest before writing anything. What it installs becomes instructions your agent follows, so only install skills from servers you trust.
overwrite=True replaces the whole local folder with the same name, so local edits and extra files in it are deleted. Change the skill on the server, not on your laptop. Re-run it when the skills change, or run it from cron or a session-start hook so nobody has to remember. It won't remove a skill the server stopped serving; delete that folder yourself.
Skills don't have to be files
from fastmcp import FastMCP, Skill
from fastmcp.skills.provider import SkillCatalogProvider
release_notes = Skill(
name="release-notes",
description="Write release notes from merged pull requests.",
instructions="# Release notes\n\nGroup changes by theme. Follow template.md.\n",
files={"template.md": "## Highlights\n\n## Fixes\n"},
)
mcp = FastMCP("Team Skills", providers=[SkillCatalogProvider([release_notes])])A Skill is a plain Python object, so its instructions and files can come from anywhere your code can read. Subclass SkillCatalogProvider and override load_skills() if they live in a database.
Why not just use the core SDK?
The core MCP SDK gives you the protocol: a call to list skills and a call to fetch one. That's the right scope for an SDK! It also means you're on your own for loading skills from somewhere, checking they're valid, deciding who sees which ones, and getting them onto agents that don't speak the protocol yet.
Discovery only lists a skill the caller can read, so the AuthMiddleware rules that already guard your resources decide which team sees which skill. Skills also come along when you mount a server or put a proxy in front of one with skills=True, and ClientGroup lists them across several servers. The skills docs cover each.
Exciting plumbing
Outside of skills, dependencies can live in the type annotation now, so your type checker stops thinking config is a Depends object:
from typing import Annotated
from fastmcp import FastMCP
from fastmcp.dependencies import Depends
mcp = FastMCP("Annotated Deps Demo")
def get_config() -> dict:
return {"api_url": "https://api.example.com", "timeout": 30}
Config = Annotated[dict, Depends(get_config)]
@mcp.tool
async def fetch_data(query: str, config: Config) -> str:
return f"Fetching '{query}' from {config['api_url']}"A dependency can also own the tool argument it reads via CallArgument (thanks Kludex for asking). FastMCP adds the argument to the tool's schema and passes the value only to the dependency, so the tool doesn't carry a parameter it never uses.
Completion no longer suggests values for prompts or templates the caller can't see, so it doesn't leak their names. ctx.info() and the other logging helpers now work inside background tasks. The release notes list the OpenAPI, proxy and auth fixes.
Upgrading
4.2 drops Python 3.10, so you need 3.11 or newer. The 4.1 skills API still works (with warnings) until 4.3, and the old Skill wire model is now MCPSkill. The skills docs have a migration table.
Thank you
129 people contributed to 4.2, 55 of them for the first time. Thanks to Jake Kaplan for getting skills discovery through proxies.
Get started
Happy (skill) engineering!