FastMCP 4.2のスキル配信を4.1.8と比べて検証|新しいSkillsDirectoryProviderの使い方と、4.3で削除予定の旧APIの置き換え
FastMCP 4.2.0で、MCPサーバーからスキルを配る仕組みが新しくなった
PythonでMCPサーバーを作るフレームワークFastMCPの4.2.0が、2026年11月14日(日本時間)に公開されました(4.2.0のリリースノート)。MCP(Model Context Protocol)は、AIアプリがツールやデータにつなぐための共通の通信規格です。4.2.0では、MCPサーバーから「スキル」を配る仕組みが作り直されました。
スキルは、AIエージェントに読ませる作業手順を、SKILL.mdというファイルと付属のファイルにまとめたフォルダーです。SKILL.mdの先頭には、スキルの名前(name)と、どんなときに使うかの説明(description)を書きます。4.2.0の新しいSkillsDirectoryProvider(スキルのフォルダーを公開する部品)を使うサーバーは、MCPの拡張仕様「Skills extension」(SEP-2640)に沿ってスキルの一覧を返し、クライアントは受け取ったファイルを1つずつハッシュ(中身が1バイトでも違うと変わる値)で照合できるようになりました。4.1のスキル用のクラスと関数は非推奨になり、リリースノートと警告文によると4.3で削除される予定です。
FastMCPのドキュメントは、このSkills extensionへの対応を実験的(experimental)としていて、リリースノートも、対応するクライアントはまだそろっていないと書いています。そのためFastMCPは、自前のクライアントでスキルを受け取り、AIエージェントが読むフォルダーへ書き出す関数も用意しました。
この記事では、4.1.8と4.2.0をPyPIから手元に入れ、同じスキルのフォルダーで動きを比べました。新しい書き方、規則に合わないフォルダーの扱い、受け取る側の照合と書き出し、4.1の書き方を置き換える手順、版の違うサーバーとクライアントの組み合わせを順に説明します。4.1でexperimental_skills=Trueを付けていたサーバーは、4.2.0へ上げた時点で動きが変わるので、別に取り上げます。
要点
4.2.0のスキル配信は、起動時の検査と受け取り時の照合が加わった
4.1.8と4.2.0に同じスキルのフォルダーを渡して比べた結果です。
新しい書き方
fastmcp.skills.providerのSkillsDirectoryProvider
深い階層のスキルも見つけ、ファイルごとの大きさとハッシュを付けて一覧を返します。
起動時の検査
新しいproviderは、規則に合わないフォルダーがあるとサーバーを起動させない
エラーにはフォルダーごとの直し方が出ます。4.1.8の旧いproviderは、同じフォルダーを公開したまま起動しました。
受け取る側
read_skillが照合し、to_directoryで書き出す
ハッシュと合わないファイルを返すサーバーでは、ValueErrorで止まりました。
4.1の書き方
既定の書き方は4.2でも動き、警告が置き換え先を示す
experimental_skills=Trueを付けたコードは、上げた時点で新しいproviderに変わります。4.2のクライアントは、4.1.8のサーバーからもスキルを受け取れました。
新しいSkillsDirectoryProviderは、フォルダーのスキルをハッシュ付きで公開する
4.2.0では、スキルのフォルダーを公開する部品がfastmcp.skills.providerのSkillsDirectoryProviderに変わりました。FastMCPでは、サーバーに機能を足すこうした部品を「provider」と呼びます。検証に使ったフォルダーは次の形です。release-notesは付属のテンプレートを1つ持ち、deploy-checkはopsの下に1階層深く置いています。
skills/
├── release-notes/
│ ├── SKILL.md
│ └── templates/summary.md
└── ops/
└── deploy-check/
└── SKILL.md
release-notes/SKILL.mdの中身です。先頭の---で囲んだ部分に、nameとdescriptionを書きます。
---
name: release-notes
description: Use when writing release notes from a list of merged pull requests. Groups changes and fills the summary template.
---
# Release notes
1. Group the merged pull requests into Features, Fixes and Breaking changes.
2. Fill `templates/summary.md` with one line per change.
サーバーのコードでは、FastMCPにSkillsDirectoryProviderを1つ足します。引数には、スキルのフォルダーを並べた親のフォルダー(ルート)を渡します。
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.skills.provider import SkillsDirectoryProvider
mcp = FastMCP("team-skills")
mcp.add_provider(SkillsDirectoryProvider(Path(__file__).parent / "skills"))
if __name__ == "__main__":
mcp.run()
4.2.0のfastmcp listには、サーバーが公開するスキルを表示する--skillsが加わりました。仮想環境を有効にした状態(fastmcpコマンドがPATHで見つかる状態)で実行すると、2つのスキルと各ファイルの大きさが並びます(ログの行は省いています)。仮想環境を有効にせず、.venv/bin/fastmcp list ...のようにパスを指定して実行すると、サーバーを起動する段階でfastmcpコマンドが見つからず、Error: Client failed to connect: [Errno 2] No such file or directory: 'fastmcp'で止まりました。4.1.8のfastmcp listはUnknown option: --skills.を返します。
$ fastmcp list server.py --skills
No tools found.
Skills (2)
skill://ops/deploy-check/SKILL.md
Use before deploying to staging. Lists the checks to run and the commands
for each.
skill://ops/deploy-check/SKILL.md (196 bytes)
skill://release-notes/SKILL.md
Use when writing release notes from a list of merged pull requests. Groups
changes and fills the summary template.
skill://release-notes/SKILL.md (308 bytes)
skill://release-notes/templates/summary.md (53 bytes)
スキルがサーバーからエージェントの手元へ届くまでの流れを、手元で確かめた順に並べます。
4.2.0でスキルが届くまでの4段階
1. フォルダーを読む(サーバー)
SKILL.mdのあるフォルダーごとに1つのスキルになります。URIはルートからのパスと同じで、deploy-checkはskill://ops/deploy-check/SKILL.mdです。
2. 一覧を返す(サーバー)
Skills extensionのskills/listの要求に、名前、説明、ファイルごとの大きさとSHA-256のハッシュを返します。各ファイルと、ファイルの一覧(manifest)はskill://のリソースとしても読めます。
3. 照合して受け取る(クライアントのread_skill)
manifestにあるファイルをすべて読み、大きさとハッシュがmanifestに書かれた値と同じかを確かめてから、Skillとして返します。
4. 書き出す(Skillのto_directory)
照合済みのスキルを、AIエージェントがスキルを読むフォルダーへ、元と同じファイル構成で書き出します。
4.2.0のSkillsDirectoryProviderは、フォルダーをサーバーの起動時に1回だけ読みます。起動後にSKILL.mdを書き換えても、サーバーは起動時の中身を返し続けました。reload=Trueを付けると要求のたびに読み直し、書き換えた中身が次の読み出しから返りました。reload=TrueのままSKILL.mdのnameとdescriptionを消すと、ログにERROR Keeping the last valid skillsと出て、直前の正しい中身を返し続けます。
新しいproviderは、規則に合わないスキルがあると起動を止める
新しいSkillsDirectoryProviderは、SKILL.mdがAgent Skillsの仕様に合っているかを、サーバーを作る時点で確かめます。nameは小文字の英字・数字・ハイフンの64文字以内で、先頭と末尾のハイフンやハイフンの連続は使えず、フォルダー名と同じにします。descriptionは1〜1024文字で書きます。名前が.で始まるフォルダーは読み飛ばします。
大文字と下線を使ったRelease_Notesと、descriptionのないno-descriptionの2つのフォルダーを用意し、4.1.8の旧いprovider(experimental_skillsなし)と4.2.0の新しいproviderに渡しました。
4.1.8(旧いSkillsDirectoryProvider、experimental_skillsなし)
2つとも、警告なしで公開した
リソースにskill://Release_Notes/SKILL.mdとskill://no-description/SKILL.mdが並びました。
4.2.0(新しいSkillsDirectoryProvider)
ValueErrorで止まり、サーバーを作れない
2つのフォルダーそれぞれに、理由と直し方が1行ずつ出ました。
4.2.0のエラーです(フォルダーのパスは<skills>と縮めています)。Fix:のあとに、フォルダーの名前の直し方と、SKILL.mdの先頭に書く内容が出ます。
ValueError: 2 skill folder(s) are not valid Agent Skills (https://agentskills.io/specification):
- <skills>/Release_Notes: name must be 1-64 lowercase letters, digits, or hyphens; it cannot start or end with a hyphen or contain consecutive hyphens. Fix: rename the folder to 'release-notes' and start SKILL.md with '---\nname: release-notes\ndescription: <what it does and when to use it>\n---'
- <skills>/no-description: description must contain 1-1024 characters. Fix: start SKILL.md with '---\nname: no-description\ndescription: <what it does and when to use it>\n---'
4.2.0の環境で旧いproviderに同じフォルダーを渡すと、2つを公開したまま、非推奨の警告の最後にFix these folders first:と、上と同じフォルダーと直し方を並べました。規則に合うフォルダーだけなら、警告の最後はEvery skill folder found already passes.です。4.2へ上げたら、新しいproviderに書き換える前にこの警告を一度読むと、起動しなくなるフォルダーを先に直せます。
4.1でSkillsDirectoryProvider(root, experimental_skills=True)と書き、SkillsExtensionを登録していたサーバーは、上げた時点で動きが変わります。4.2.0では、この呼び出しが新しいproviderを返すからです。4.1.8では、同じコードに規則に合わない2つのフォルダーを渡してもサーバーは起動し、ログにWARNING Omitting invalid skillと出して、Skills extensionの一覧から2つを外しました。4.2.0では、コードを変えなくても同じValueErrorで止まり、サーバーは起動しません。
どちらの書き方でも、4.2の環境で次の1行を実行すると、本番のサーバーを動かす前にフォルダーを検査できます。"skills"は、サーバーに渡しているルートのパスに置き換えます。規則に合うフォルダーなら、見つかったスキルのパスの一覧を表示して終了コード0で終わり、合わないフォルダーがあれば上と同じValueErrorと直し方を出して終了コード1で終わりました。
$ python -c 'import asyncio; from fastmcp.skills.provider import SkillsDirectoryProvider; print(sorted(asyncio.run(SkillsDirectoryProvider("skills").load_skills())))'
['ops/deploy-check', 'release-notes']
ルートのパスを書き間違えた場合と、スキルのない空のフォルダーを渡した場合は、どちらもエラーにならず[]と表示して終了コード0で終わりました。一覧に、配っているスキルがすべて並んでいるかを確かめます。
read_skillで照合し、to_directoryでフォルダーへ書き出す
受け取る側では、クライアントのread_skillにスキルのSKILL.mdのURIを渡します。返ってくるSkillのto_directoryで、スキルをフォルダーへ書き出せます。サーバーのスキルを全部、手元のフォルダーへ写すスクリプトです。サーバーはstdio(標準入出力)でつなぎ、引数でサーバーを動かすPythonとスクリプト、書き出し先を渡します。
import asyncio
import sys
from pathlib import Path
from fastmcp import Client
from fastmcp.client.transports import StdioTransport
async def main(server_python: str, server_script: str, target: Path) -> None:
transport = StdioTransport(command=server_python, args=[server_script])
async with Client(transport) as client:
for resource in await client.list_skill_resources():
skill = await client.read_skill(str(resource.uri))
path = skill.to_directory(target / skill.name, overwrite=True)
print(f"{resource.uri} -> {path}")
asyncio.run(main(sys.argv[1], sys.argv[2], Path(sys.argv[3])))
4.2.0の新しいサーバーへ向けて実行すると、2つのスキルを書き出しました。deploy-checkはサーバー上ではopsの下にありますが、書き出し先ではnameと同じ名前のフォルダーになります。
skill://ops/deploy-check/SKILL.md -> out/.agents/skills/deploy-check
skill://release-notes/SKILL.md -> out/.agents/skills/release-notes
list_skill_resourcesは、skill://のリソースからSKILL.mdを探す関数で、Skills extensionに対応していないサーバーにも使えます。Skills extensionで一覧を受け取るlist_skillsは、クライアントを作るときにextensions=[SkillsClientExtension()]を渡します。SkillsClientExtensionはfrom fastmcp.skills import SkillsClientExtensionで読み込みます。渡さないとRuntimeError: This client did not opt into the Skills extension; pass SkillsClientExtension() in Client(extensions=[...])で止まりました。
照合と書き出しで止まる場面を、3つ試しました。
ハッシュと合わないファイル
read_skillがValueErrorで止まる
テンプレートの1語を同じ長さで書き換え、manifestのハッシュとは違う中身を返すサーバーでは、Skill file 'templates/summary.md' does not match its manifest digest。同じファイルをread_resourceで直接読むと、書き換えた中身がそのまま返りました。
書き出し先のフォルダー名
スキルのnameと同じ名前にする
notesへ書き出そうとすると、Skill folder 'notes' must match the skill's name 'release-notes'のValueErrorになりました。
2回目の書き出し
overwrite=Trueはフォルダーを丸ごと置き換える
指定なしではFileExistsError。overwrite=Trueでは、書き出したあとに手で足したlocal-note.txtも消えました。4.1のdownload_skill(..., overwrite=True)は、同じファイルを残しました。
ハッシュの照合はread_skillの中で行われるので、スキルを受け取るコードはread_resourceで1ファイルずつ読むより、read_skillでまとめて受け取ります。overwrite=Trueは手元の編集も消すため、手で書き足すスキルは、サーバーから写すフォルダーとは別の場所に置きます。
ハッシュが一致して分かるのは、サーバーが返した一覧とファイルの中身が食い違っていないことです。一覧もファイルも同じサーバーが返すため、SEP-2640は、ハッシュの一致を安全の保証として扱わないよう定めています。エージェントのフォルダーへ書き出すスキルは、信頼できるサーバーから受け取り、中身を一度読んでから使います。
4.1の既定の書き方は4.2でも動き、警告が置き換え先を示す
experimental_skillsを付けない4.1の書き方で作ったサーバーとクライアントの関数は、4.2.0でもそのまま動きました。旧いproviderを作ったときと旧い関数を呼んだときにFastMCPDeprecationWarningが出て、置き換え先のコードを示します。4.1.8では、同じコードで警告は出ません。手元の4.2.0で警告が示した置き換え先を、表にまとめます。
表の見方:左が4.1の書き方、右が4.2.0の警告が示した置き換え先です。左の書き方は、4.3で削除される予定です。
| 4.1の書き方 | 4.2での置き換え先 |
|---|---|
from fastmcp.server.providers.skills import SkillsDirectoryProvider |
from fastmcp.skills.provider import SkillsDirectoryProvider |
SkillProvider(path)(1つのスキルのフォルダーを公開) |
SkillCatalogProvider([Skill.from_directory(path)]) |
SkillsDirectoryProvider(root, experimental_skills=True) |
SkillsDirectoryProvider(root)。4.2.0では、この呼び出しがすでに新しいproviderを返すので、規則に合わないフォルダーがあるとここでValueErrorになります。SkillsExtensionの登録も要らなくなりました |
fastmcp.utilities.skills.list_skills(client) |
await client.list_skill_resources()、Skills extensionに対応したサーバーならawait client.list_skills() |
fastmcp.utilities.skills.download_skill(...) |
(await client.read_skill('skill://<name>/SKILL.md')).to_directory(target_dir / '<name>') |
fastmcp.utilities.skills.sync_skills(client, target_dir) |
list_skill_resources()の各URIをread_skillで受け取り、to_directoryで書き出す(この記事の同期スクリプトの形) |
Skill.model_validate(...)(4.1の通信用の形) |
MCPSkill.model_validate(...)(from fastmcp.skills import MCPSkill) |
4.2では、fastmcp.skills.Skillはスキルを作るためのクラスになり、4.1で通信用の型だったものはMCPSkillに名前が変わりました。型注釈やisinstanceでSkillを通信用の型として使っているコードは、4.2ではまだ通りますが、4.3までにMCPSkillへ書き換えます。ClaudeSkillsProviderやCodexSkillsProviderのように、エージェントごとのフォルダーを決め打ちしたproviderも、そのフォルダーを新しいSkillsDirectoryProviderに渡す形へ置き換えます(FastMCPのドキュメントの移行表)。
experimental_skillsを付けない4.1の旧いproviderは、ルートのすぐ下のフォルダーだけをスキルとして公開します。opsの下のdeploy-checkは、この書き方では4.1.8でも4.2.0でも出ませんでした。新しいproviderは深い階層のSKILL.mdも探すので、書き換えた直後からdeploy-checkも公開されます。4.1でexperimental_skills=Trueを付けていたサーバーは、4.1.8の時点で深い階層のスキルも公開していました。
4.2のクライアントは、4.1のサーバーからもスキルを受け取れる
サーバーとクライアントを別々に上げる場合に備えて、版の違う組み合わせをstdioでつないで試しました。
結論:4.2のクライアントでlist_skill_resourcesとread_skillを使えば、サーバーが4.1.8でも4.2.0でもスキルを受け取れました。
クライアント4.2.0 → サーバー4.1.8(旧いprovider、experimental_skillsなし)
一覧を受け取る関数
list_skillsはRuntimeError: The server does not advertise the Skills extension。list_skill_resourcesはrelease-notesの1件
スキルを受け取る関数
read_skillで2ファイルを受け取れた
クライアント4.2.0 → サーバー4.1.8(experimental_skills=TrueとSkillsExtension)
一覧を受け取る関数
list_skills、list_skill_resourcesとも2件(deploy-checkを含む)
スキルを受け取る関数
read_skillで2つとも受け取れた
クライアント4.2.0 → サーバー4.2.0(新しいprovider)
一覧を受け取る関数
list_skills、list_skill_resourcesとも2件
スキルを受け取る関数
read_skillで受け取れた
4.1.8の関数 → サーバー4.2.0(新しいprovider)
一覧を受け取る関数
list_skillsは2件。名前の欄に、deploy-checkだけops/deploy-checkとパスが入る
スキルを受け取る関数
download_skill(client, "deploy-check", ...)はResource not found: 'skill://deploy-check/_manifest'。"ops/deploy-check"と渡すと受け取れた
experimental_skillsを付けずに作った4.1.8のサーバーはSkills extensionを広告しないため、list_skillsはRuntimeErrorで止まります。read_skillは、FastMCPのサーバーがファイルの一覧として公開する_manifestのリソースを使って照合するので、このサーバーからもハッシュを確かめて受け取れました。4.1.8の関数のまま4.2.0のサーバーにつなぐ場合は、深い階層のスキルをlist_skillsが返したパスのままdownload_skillへ渡します。この方法では、書き出し先もops/deploy-checkのように深い階層のフォルダーになります。
4.2へ上げる前に確かめる4つのこと
ここまでの結果から、4.1でスキルを配っているサーバーを4.2へ上げるときの確認を、順に並べます。
上げる前の確認
1. Pythonが3.11以上か
PyPIの登録情報では、4.2.0は>=3.11、4.1.8は>=3.10です。3.10で動かしている環境は、Pythonから上げます。
2. 4.2の環境で、新しいproviderにフォルダーを検査させる
規則に合わないフォルダーの節で示した1行で、新しいproviderにルートのフォルダーを検査させ、表示された一覧に配っているスキルがすべて並ぶかを確かめます。ValueErrorが出たら、Fix:のとおりにフォルダー名とSKILL.mdのname・descriptionを直します。experimental_skills=Trueを付けた4.1のサーバーは、上げた時点で新しいproviderに変わるので、本番で上げる前にこの検査を通します。
3. ルートの深い階層に、配る予定のないSKILL.mdがないか
新しいproviderは深い階層のスキルも公開します。配る予定のないスキルがルートの下にあれば、書き換える前に別のフォルダーへ移します。
4. 受け取る側のコードを、read_skillとto_directoryへ書き換えたか
fastmcp.utilities.skillsのdownload_skillとsync_skillsを、list_skill_resources・read_skill・to_directoryの組み合わせへ書き換えます。この形なら、4.1.8のサーバーからも受け取れました。
4.2.0のスキル配信で増えたのは、配る側の起動時の検査と、受け取る側のハッシュの照合です。チームのスキルを1台のMCPサーバーにまとめておけば、同期スクリプトで各自の手元へ同じ中身を写せ、受け取る側ではmanifestのハッシュと合わないファイルを書き出す前に止められます。4.1の書き方は4.3で使えなくなる予定なので、4.2へ上げたら警告を手がかりに書き換えておきます。
参考資料
- FastMCP 4.2.0のリリースノート(2026年11月14日、日本時間): スキルの仕組みの変更点、上げる前に確かめる3点
- FastMCPのドキュメント「Skills」:
Skillの作り方、各provider、クライアントの関数、4.1からの移行表 - SEP-2640「Skills Extension」: MCPのSkills extension
- Agent Skillsの仕様:
SKILL.mdのnameとdescriptionの決まり - PyPIのfastmcp 4.2.0
検証環境
- macOS 27.0.1(Apple Silicon)、Python 3.13.14(uv 0.11.32の仮想環境)
- FastMCP 4.1.8と4.2.0(PyPI、どちらもmcp 2.3.0)
- 検証日: 2026年11月14日
この記事は、AIが調査と下書きを担当し、上記の環境で実際に動かして結果を確かめ、下書きとは別の担当が公開前に本文と出典を確認しています。制作と訂正の方針は編集・訂正方針で公開しています。












