← Academy 日本語で学ぶ(一覧へ戻る)

introduction to mcp

Anthropic Academy コースの日本語ナレッジ(学習用)/ 全 13 項目

Introduction to Model Context Protocol 全文ナレッジ(DA式・日本語)

Anthropic Academy 公式コース「Introduction to Model Context Protocol」の全レッスンを DA式完全版(4層)でナレッジ化したもの。各動画ノート:日本語ナレッジ(要点整理)→ 実務ポイント → 本文(時系列・日本語逐語+画面差し込み+実物コード)→ 英語逐語(ASR全文)

  • 修了証: https://verify.skilljar.com/c/jghs9suauav4(NAOKI NAKASHIMA / 2026-07-03 / Anthropic Education)
  • 動画12本 計61.5分(12,102語)+ サーベイ + Final assessment 7問全問正答で合格
  • 生成: jwplayer 署名付き HLS → ffmpeg → mlx_whisper large-v3-turbo(srt)+ シーン検出フレーム326枚の画面読解 → 4並列DA式統合 → 2パス検証(ギャップ199枚再検証)+整合性検収(2026-07-03)
  • 演習素材(実物): assets/cli_project/(スターター)・assets/cli_project_COMPLETE/(講義完成形)

目次

Introduction

# ノート 時間 ひとこと
01 Welcome to the course 1:04 コース5テーマ+前提(Python/uv)
02 Introducing MCP 4:40 MCP=通信レイヤー。「ツールコードを自分で書き維持する」問題を解く
03 MCP clients 4:56 transport agnostic(stdio/HTTP)・4メッセージ型・シーケンス図

Hands-on with MCP Servers

# ノート 時間 ひとこと
04 Project setup 3:09 CLI チャットボット題材。「実務ではクライアントかサーバー片方だけ書く」
05 Defining tools with MCP 7:03 FastMCP+@mcp.tool で read_doc_contents / edit_document 実装
06 The server inspector 3:52 mcp dev mcp_server.py → Inspector(port 6274/6277)で接続・実行検証

Connecting with MCP Clients

# ノート 時間 ひとこと
07 Implementing a client 7:26 MCPClient=ClientSession ラッパー・list_tools / call_tool
08 Defining resources 9:45 リソース=GET 相当。直接型 docs://documents とテンプレート型 {doc_id}
09 Accessing resources 4:38 クライアント側 read_resource・@オートコンプリート(ツール不使用の対比)
10 Defining prompts 7:44 @mcp.prompt でユーザー起動ワークフロー(/format)
11 Prompts in the client 3:01 list_prompts / get_prompt・/format plan.md E2E
12 MCP review 4:12 3プリミティブの制御主体: tools=モデル/resources=アプリ/prompts=ユーザー

Final assessment 7問(完全収録・全選択肢+正答)

Q1. You're building an MCP client to connect your application to an MCP server. What are the two main components you need? - A frontend and a backend / A database and a web server / A REST API and a GraphQL endpoint - ✅ An MCP Client class and a Client Session

Q2. Your MCP client needs to find out what tools are available from an MCP server. What message type should it send? - ✅ ListToolsRequest / CallToolRequest / ToolDiscoveryRequest / GetToolsMessage

Q3. You've built an MCP server and want to test if your tools work correctly before connecting to a full application. What's the easiest way to do this? - Write separate test scripts for each tool / Test everything manually in the terminal / Connect directly to Claude first - ✅ Use the built-in MCP Inspector with mcp dev mcp_server.py

Q4. You're building a chat app where users ask Claude about their GitHub data. Without MCP, what's the main problem you'd face? - GitHub doesn't allow API access / Claude can't understand GitHub data / Users can't ask questions about repositories - ✅ You'd have to write and maintain all the GitHub tool code yourself

Q5. Users should be able to click a button to trigger a "summarize document" workflow. Which MCP primitive should you use? - Resources - because you need to fetch document data / Functions - because it involves processing / Tools - because the AI needs new capabilities - ✅ Prompts - because users control when to start the workflow

Q6. You're using the Python MCP SDK to create a tool that reads files. What's the easiest way to define this tool? - ✅ Use the @mcp.tool() decorator on a Python function / Create a separate configuration file / Write JSON schemas manually / Send HTTP requests to register the tool

Q7. You want to create a resource that fetches different documents based on their ID, like docs://documents/report.pdf. What type of resource should you use? - ✅ A templated resource with parameters in the URI / A tool instead of a resource / A direct resource with a static URI / A database query resource

(受験結果: 全問正答で合格 "You have passed!" / 2026-07-03)

講義で実際に書かれたコード=スターターとの差分(全量)

assets/cli_projectassets/cli_project_COMPLETE の diff は mcp_server.py と mcp_client.py の2ファイルのみ。mcp_server.py 側の追加(コースの実装全量):

  • @mcp.tool ×2 — read_doc_contents(doc_id で辞書から返す)・edit_document(old_str/new_str 置換。Field(description=...) で引数を説明)
  • @mcp.resource ×2 — docs://documents(直接型・application/json・ID一覧)・docs://documents/{doc_id}(テンプレート型・text/plain・本文)
  • @mcp.prompt ×1 — format(Markdown 整形のユーザー起動ワークフロー。base.UserMessage(prompt) を返す)

クライアント側の実装(list_tools / call_tool / read_resource / list_prompts / get_prompt)は各ノートの実物コード引用を参照。

逐語データ

  • transcripts/ — ASR 原文 txt+タイムスタンプ付き srt(12本)
↑ 目次へ戻る

Introduction to MCP | Lesson 01「Welcome to the course」(全文ナレッジ)

動画 1分04秒 / 講師: Stephen Grider(ASR 表記は "Steven Greider"・正しくは Stephen Grider)/ 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

コースの位置づけ

  • Model Context Protocol(MCP)の「入門(getting started)」に特化したコース。講師は Stephen Grider。
  • MCP について「知っておくべきことすべて」を、概要→アーキテクチャ→サーバーの3コンポーネントの順で案内する構成。

コースで扱う5テーマ(Course goals スライドの定義そのまま)

  1. Overview of MCP — MCP がどんな問題を解決するのかを理解する
  2. MCP Clients and Servers — サーバーが resources・tools などをホストし、クライアントがサーバーを利用する、という責務分担
  3. Tools — サーバー上に実装され、LLM の能力を拡張する。言語モデルに消費される(使われる)ことを想定したコンポーネント
  4. Resources — サーバーがあらゆる種類のドキュメント・情報をクライアントに届けるための仕組み
  5. Prompts — サーバーがクライアントに「事前最適化済みプロンプト(チューニング済みの指示)」を提供する仕組み

受講の前提条件(What you need)

  • Python の基礎知識(基本構文・ループ・関数レベル)
  • ローカルの Python 実行環境(実際にコードを書くため)
  • uv CLI:プロジェクト管理に uv を使用。未導入なら今のうちに導入推奨。インストール手順は github.com/astral-sh/uv

実務ポイント(このレッスンから持ち帰るもの)

  1. MCP サーバーの3大コンポーネントは Tools/Resources/Prompts。「Tools=LLM が使う」「Resources=データをクライアントへ渡す」「Prompts=調整済み指示を渡す」という役割区分を最初に頭に入れると以降の講義が整理しやすい。
  2. クライアントとサーバーの責務分担(Servers host, Clients use)がこのコース全体の背骨。
  3. ハンズオン前提の準備は3点セット:Python 基礎+ローカル Python+uv CLIgithub.com/astral-sh/uv)。コースは uv でプロジェクト管理する。
  4. 学習順序(概要→クライアント/サーバー→Tools→Resources→Prompts)は、MCP を人に説明するときの標準的な導入ルートとしてそのまま流用できる。

本文(時系列・日本語逐語+画面差し込み)

🖥 [0:00] 画面: スライド「Course goals」(Anthropic ロゴ入り)。右側に5つの角丸ボックス: - Overview of MCP — Understand what problems MCP solves - MCP Clients and Servers — Servers host resources, tools, and more. Clients use servers. - Tools — Tools are implemented on a server and extend an LLM's capabilities - Resources — Servers can deliver any kind of document or information to clients - Prompts — Servers can provide clients with pre-optimized prompts

[0:00] モデル・コンテキスト・プロトコル(Model Context Protocol)入門にフォーカスした、このコースへようこそ。

[0:04] 私の名前はスティーブン・グライダー(Stephen Grider)です。MCP にまつわる、知っておくべきことすべてを私がご案内していきます。

[0:08] まずは、これからの動画でいったい何を扱うのかを正確に理解するところから始めましょう。

[0:13] 最初に MCP の概要をつかみ、MCP がどんな問題を解決するために作られたのかを理解します。

[0:18] 次に、MCP クライアントと MCP サーバーの責務を扱いながら、MCP アーキテクチャの基礎を見始めます。

[0:25] その後は、MCP サーバーの3つの基本コンポーネントに進みます。

[0:29] まずツール(tools)を見ます。これは言語モデルに消費される(使われる)ことを想定したものです。

[0:34] その後にリソース(resources)を学びます。これはサーバーがクライアントとデータを共有できるようにするものです。

[0:38] そして最後にプロンプト(prompts)。言語モデルにチューニング済みの指示(tuned directions)を与えるものです。

🖥 [0:43] 画面: スライドが「What you need」に切り替わり、右側に2つのボックスが表示: - Basic Python fundamentals — Essential syntax, loops, functions - Python installation with the UV CLI — Install directions for UV at github.com/astral-sh/uv

(下部にさらに2つの項目枠がごく薄くフェード表示されているが、文字は判読不能)

[0:43] さて、こなすべき内容がたくさんあるので、皆さんの側にいくつか必要条件があります。

[0:48] 第一に、基本的な Python の知識が必要です。

[0:51] 第二に、実際にいくらかコードを書き出していくので、ローカルに Python がインストールされている必要があります。

[0:56] プロジェクトの管理には uv CLI を使います。もしまだインストールしていなければ、今のうちに入れておくことをおすすめします。

[1:02] 画面にそのリンクを載せています。

英語逐語(ASR全文)

Welcome to this course focused on getting started with model context protocol. My name is Steven Greider, and I'll be guiding you through everything you need to know around MCP. Let's first begin by understanding exactly what we'll cover in the coming videos.

We'll first get an overview on MCP and understand what problems it is intended to solve. We'll then start to look at the basics of MCP architecture by covering the responsibilities of MCP clients and servers. Then it is on to the three basic components of an MCP server. First, we'll look at tools, which are intended to be consumed by language models. After that, we'll study resources, which allow a server to share data with clients. And finally, prompts, which will provide tuned directions to language models.

Now, there's a lot of content for us to get through, so there are a couple of requirements on your side. First, you need to have some basic Python knowledge. Second, we will be writing out some code, and this will require a local Python installation. We will be managing our project with the UV CLI, and if you don't have it installed, I recommend grabbing it now. I've got a link to it on the screen.

↑ 目次へ戻る

Introduction to MCP | Lesson 02「Introducing MCP」(全文ナレッジ)

動画 4分40秒 / 講師: Stephen Grider(コース講師)/ 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

MCP の定義

  • MCP は「通信レイヤー」。開発者が大量の面倒なコード(tedious code)を書かずに、Claude にコンテキストとツールを提供できるように設計されている。
  • 定番の構成図:クライアント(MCP Client)サーバー(MCP Server) の2大要素。サーバー内部には Tools/Prompts/Resources という内部コンポーネントが入り、各 MCP サーバーは外部サービス(Outside Service)につながる。

サンプルアプリで理解する(GitHub チャットボット)

  • 題材:ユーザーが自分の GitHub データについて Claude とチャットできるチャットインターフェース。
  • 「全リポジトリ横断でオープンな PR は?」のような質問に対し、Claude が ツールを使って GitHub にアクセスして回答する想定。実装は「ツール一式(a set of tools)」で行うことになる。
  • 問題点:GitHub にはリポジトリ・PR・イシュー・プロジェクト等の膨大な機能があり、完全なチャットボットには途方もない数のツール(スキーマ+関数)を自作する必要がある。これは全部、開発者が書き・テストし・保守するコード。
  • この「開発者に大量のインテグレーションを保守させる負担」こそ、MCP が解決を狙う主要課題。

MCP サーバーの本質

  • MCP は ツールの定義と実行の負担を、自分のサーバーから「MCP サーバー」へ移す。ツールは MCP サーバーの内部で実装・実行される。
  • MCP サーバー=外部サービスへのインターフェース。例:GitHub MCP サーバーは GitHub のデータと機能へのアクセスを提供する。GitHub まわりの大量の機能を「ツール一式」として MCP サーバーに包み込む。
  • 利点=開発者がツールスキーマや関数を自作しなくてよい。

よくある3つの質問(Common Questions スライド)

  1. 誰が MCP サーバーを作るのか? → 誰でも作れる。ただしサービス提供者自身が公式実装を出すことが非常に多い(例:AWS が公式 MCP サーバーを公開し、多様なツールを同梱するようなケース)。
  2. API 直接呼び出しと何が違うのか? → MCP サーバーは「ツールスキーマ+関数」を提供してくれる。直接 API を呼ぶなら、そのスキーマと関数実装を自分で書くことになる。MCP サーバーを挟むことで、その分の手間を節約できる。
  3. 「MCP とツールユースは同じもの」という批判 → MCP を理解していない人からよく出る誤解。MCP サーバーとツールユースは別物だが補完関係。MCP の考え方は「ツールの関数とスキーマを自分で書かず、他者が書いて MCP サーバーに包んだものを使う」。つまり MCP サーバーの話は「実際の作業を誰がやるか」の話。

実務ポイント(このレッスンから持ち帰るもの)

  1. MCP はツールユースの置き換えではなく「ツールの定義・実行の所有者を移す」仕組み。「MCP=tool use」と混同した批判には「補完関係・誰が実装するかの話」と切り返せる。
  2. 外部サービス統合を検討するとき、まずサービス公式の MCP サーバー実装の有無を確認する(提供者自身の公式実装が多い)。
  3. API 直叩きとの差分は「schema+fn を自分が書くかどうか」だけ——工数見積り・意思決定はこの1点で判断できる。
  4. フル機能の統合を自作すると「書く・テストする・保守する」の3重負担がツール数分だけ膨らむ。統合の保守負担が大きいほど MCP 採用のリターンが大きい
  5. アーキテクチャ図の読み方:Our Server の中に MCP Client が住み、MCP Client が複数の MCP サーバー(各サーバーが Tools/Prompts/Resources を持ち外部サービスへつながる)に接続する、が基本形。

本文(時系列・日本語逐語+画面差し込み)

🖥 [0:00] 画面: MCP の基本アーキテクチャ図(Anthropic ロゴ入りスライド)。 - 左:大きな枠「Our Server」、その内部に「MCP Client」ボックス - 右:「MCP Server」枠が上下に2つ。それぞれ内部に「Tools」「Prompts」「Resources」の3ボックス - MCP Client から2つの MCP Server へ曲線矢印、各 MCP Server から右の「Outside Service」へ矢印

[0:00] このモジュールでは、モデル・コンテキスト・プロトコル(Model Context Protocol)に集中して取り組みます。

[0:04] MCP は、開発者であるあなたが大量の面倒なコードを書かなくても済むように、Claude にコンテキストとツールを提供するために設計された通信レイヤーです。

[0:13] MCP を使い始めると、このような見た目の図を非常に頻繁に目にすることになります。

[0:17] この図は MCP の2つの主要な要素、すなわちクライアントとサーバーを示しています。

[0:22] サーバーの中には多くの場合、tools・resources・prompts と名付けられた複数の内部コンポーネントが含まれています。

[0:28] さて、ここには用語がたくさん出てきました。

[0:30] そこで、これらすべてを理解しやすくするために、私たちが小さなアプリを作っているところを想像し、そこに MCP がどうはまるのかを見ていきます。

🖥 [0:38] 画面: スライド「Sample App」。 - 箇条書き:「Chat interface, using a LLM with tools that can access a user's Github account」/「Claude will need a set of tools to access the user's data」 - 右側(赤茶色の背景パネル):チャット UI のモック。吹き出し You:「What open pull requests are there across all my repositories?」、AI:「…」、下部にテキスト入力欄と Send ボタン

[0:37] サンプルアプリは、また別のチャットインターフェースにします。

[0:41] ユーザーが自分の GitHub データについて Claude とチャットできるようにするものです。

[0:45] たとえばユーザーが「私のすべてのリポジトリを横断して、どのオープンなプルリクエストがあるか?」のような質問をしたら、

[0:50] 期待される動きとしては、Claude はおそらくツールを使って GitHub にアクセスし、そのユーザーのアカウントを見て、どんなオープンなプルリクエストがあるか、あるいはオープンなリポジトリなり何なりを確認するはずです。

🖥 [0:53] 画面: 図が展開。左にチャット UI、中央に「Our Server」枠。その内部に2つのツール: - tool get_repos() —(黒いピル型ボタン)schemafn - tool get_pull_requests() —(黒いピル型ボタン)schemafn

両ツールから右の「Github API」(赤茶色ボックス)へ矢印

[1:02] ここでのポイントは、これをおそらく「ツール一式(a set of tools)」を使って実装するだろう、ということです。

[1:07] ここで手短に触れておきたいのは、GitHub には膨大な量の機能があるということです。

[1:12] リポジトリ、プルリクエスト、イシュー、プロジェクト、その他大量のものがあります。

🖥 [1:10] 画面: スライド「Tool Functions」。 - 箇条書き:「To handle all of Github's functionality, we'd have to create an incredible number of tool schemas and functions」/太字「This is all code that we (developers) have to write, test, and maintain」 - 右:「Our Server」枠の中にツールが8個並ぶ(各ツールに schemafn の黒ピル):get_repos() / list_repos() / create_repos() / search_issues() / update_issue() / create_issue() / get_issue() / create_file()

[1:18] ですから、完全な GitHub チャットボットを作るには、実際のところ途方もない数のツールを実装しなければなりません。

[1:24] あのサンプルアプリを作ろうと思ったら、これらのスキーマとこれらの関数をすべて自分たちで書く責任を負うことになります。

[1:29] そしてこれはすべて、あなたや私のような開発者が、書き、テストし、保守しなければならないコードです。

[1:34] それは大変な労力であり、大きな負担が私たちにのしかかることになります。

[1:41] 開発者に大きなインテグレーション群を保守させるというこの課題こそ、モデル・コンテキスト(・プロトコル)が解決を目指す主要な困難のひとつです。

🖥 [1:50] 画面: スライド「Model Context Protocol」。サブタイトル:「Shifts the burden of tool definitions and execution onto MCP Servers.」 図:左「Our Server」枠の中に tool get_repos()schemafn)。右上に空の「MCP Server」枠(双方向矢印で接続)、右下に「Claude」ボックス(双方向矢印で接続)

[1:49] MCP は、ツールを定義し実行する負担を、あなたのサーバーから「MCP サーバー」と呼ばれる別のものへと移します。

[1:57] つまり、ここにあるこのツールを、もはやあなたや私が実装しなくてよくなるのです。

[2:00] 代わりに、それはこの MCP サーバーの内部という、どこか別の場所で実装され、実行されることになります。

🖥 [2:06] 画面: スライド「MCP Servers」。サブタイトル:「MCP Servers provide access to data or functionality implemented by some outside service.」 図:tool get_repos()schemafn)が「MCP Server for Github」枠の内部へ移動。MCP Server for Github から右の「Github」(赤茶色ボックス)へ矢印。左「Our Server」は MCP Server for Github および下の「Claude」と双方向矢印で接続

[2:05] MCP サーバーは、外部サービスへのインターフェースのようなものだと考えることができます。

[2:10] たとえば GitHub MCP サーバーがあれば、それはまさに GitHub が提供するデータと機能へのアクセスを提供してくれます。

[2:15] 私たちは実質的に、GitHub まわりの大量の機能をまとめて包み込み、ツール一式という形でこの MCP サーバーの中に配置しているのです。

[2:26] さて、この時点で私たちは MCP サーバーが何であるかについて、ごく基本的な理解を得ました。MCP サーバーは、何らかの外部サービスに関連する機能を公開するツール一式へのアクセスを与えてくれます。

[2:37] そしてここでの利点は、あなたや私が、これらの様々なツールスキーマや関数などをすべて自作しなくてよい、ということです。

[2:42] この基本的な理解ができたところで、MCP サーバーについて初めて学ぶ多くの人が抱く、非常によくある質問にいくつか答えておきたいと思います。

🖥 [2:50] 画面: スライド「Common Questions」。3行の Q&A テーブル: | 質問 | 回答 | |---|---| | Who authors the MCP Server? | Anyone! Often the service provider itself will make their own MCP implementation. You can make a MCP server to wrap up access to some service. | | How is using an MCP Server different from just calling a service's API directly? | MCP Servers provide tool schemas + functions. / If you want to directly call an API directly, you'll be authoring those on your own. | | Sounds like MCP Servers and tool use are the same thing. | MCP Servers provide tool schemas + functions already defined for you. |

[2:52] 必ずと言っていいほど出てくる、よくある質問が3つあります。

[2:53] 1つ目のよくある質問は「これらの MCP サーバーは誰が作るのか?」です。答えは「誰でも」。MCP サーバーの実装は誰でも作れます。

[2:58] ただし、サービス提供者が自ら公式実装を作っているケースが非常に多いことに気づくでしょう。

[3:03] たとえば AWS が、自社の公式 MCP サーバー実装をリリースすることに決めて、その中にあなたが使える多種多様なツールが入っている、といった具合です。

[3:15] 2つ目のよくある質問は「MCP サーバーを使うことは、サービスの API を直接呼ぶことと何が違うのか?」です。

[3:22] さきほど見たとおり、GitHub のような API を直接呼びたいのであれば、このツールを自分たちで書かなければなりません。

🖥 [3:29] 画面: 「MCP Servers」スライド上でアニメーション。tool get_repos()schemafn)が MCP サーバー枠から出て「Our Server」枠の内部へ戻り、Our Server から「Github」へ直接矢印が伸びる(MCP Server 枠は消える)。下の「Claude」との双方向矢印は維持

[3:29] そうすれば GitHub を直接呼べます。では、ここで私たちは何を得たのでしょうか?

[3:38] 実際に変わったのは、スキーマを自分で書き、関数の実装も自分で書くことになった、という点だけです。

[3:43] つまり、MCP サーバーを組み込むだけで、私たちはいくらかの時間を節約できているわけです。

🖥 [3:46] 画面: 「Common Questions」テーブルに戻る(内容は [2:50] と同一)

[3:48] 最後のよくある質問は、質問というより、MCP のまわりで人々が口にするよくある批判です。

[3:54] そしてこの批判はたいてい、MCP が何なのかをあまり理解していない人たちから出てきます。

[3:59] 「MCP とツールユース(tool use)は同じものだ」と言っている人を、非常によく見かけるはずです。

[4:05] ですが、いまお示ししたとおり、MCP サーバーとツールユースは補完関係にあります。両者は別物ですが、補完し合うものです。

[4:16] MCP の背後にある考え方は、ツールの関数とツールのスキーマを自分で書かなくてよい、というものです。それは他の誰かによってあなたのために行われ、この MCP サーバーの中に包み込まれています。

[4:27] ですから、ある水準では、ええ、確かに似ています。どちらの場合もツールユースの話をしているのですから。しかし MCP サーバーの話は、本質的には「実際の作業を誰がやっているのか」という話なのです。

[4:37] もしこの批判を見かけたら——繰り返しになりますが——それはたいてい、その人が MCP の本質をよく理解していないことが原因です。

英語逐語(ASR全文)

In this module, we are going to focus on model context protocol. MCP is a communication layer designed to provide Claude with context and tools without requiring you, the developer, to write a bunch of tedious code. When you first get started with MCP, you will see diagrams that look like this very often. It shows two major elements of MCP, namely the client and the server. The server often contains a number of internal components, named tools, resources, and prompts. Now there's a lot of terminology here. So to help you understand all of this, we're going to imagine that we are building a small app and see how MCP fits into it.

Our sample app is going to be another chat interface. It's going to allow a user to chat with Claude about their GitHub data. So if a user asks a question like, what open pull request do I have across all my different repositories? the expectation is that Claude is probably going to make use of a tool to reach out to GitHub, access the user's account, and see what open pull requests they have, maybe open repositories or whatever else. The point here is that we would implement this probably by using a set of tools.

Now one thing I want to mention really quickly is that GitHub has a tremendous amount of functionality. There are repositories, pull requests, issues, projects, and tons of other things. So to have a complete GitHub chatbot, we would really have to author a tremendous number of tools. If we wanted to build that sample app, we would be on the hook for authoring all these schemas and all these functions. And this is all code that you and I as developers would have to write, test and maintain. That's a lot of effort, a lot of burden being placed on us. This challenge of making developers maintain a big set of integrations is one of the primary difficulties that model context aims to solve.

MCP shifts the burden of defining and running tools from your server to something else called an MCP server. So no longer would you and I have to author this tool right here. Instead, it would be authored and executed somewhere else inside of this MCP server. These MCP servers can really be thought of as like an interface to some outside service. So I might have a GitHub MCP server that provides access to data and functionality provided by specifically GitHub. We're essentially wrapping up a ton of functionality around GitHub and placing it into this MCP server in the form of a set of tools. So at this point, we have a very basic understanding of what a MCP server is. It gives us access to a set of tools that exposes functionality related to some outside service. And the benefit here is that you and I do not have to author all these different tool schemas and functions and so on.

Now that we have this basic understanding, I want to address some very common questions that a lot of people have when they first learn about MCP servers. So three common questions that seem to always come up. The first common question is who authors these MCP servers? And the answer is anyone. Anyone can make an MCP server implementation. But very often you will find that service providers make their own official implementation. So for example, AWS might decide to release their own official MCP server implementation and inside of it, it might have a wide variety of different tools available for you to use.

The second common question is how is using a MCP server different than just calling a services API directly? Well, as we just saw, if we wanted to call a API directly, such as GitHub, then we would have to author this tool ourselves. And now we can call GitHub directly. So what did we gain here? Well, all that really changed was we are now having to author the schema ourselves and the function implementation ourselves. So simply by adding in the MCP server, we are saving ourselves a little bit of time.

The final common question is more of a common criticism that you're going to see people have around MCP. And this criticism is most often coming from people who don't quite understand what MCP is all about. So very often you will see people saying MCP and tool use are the same thing. Well, as I have just laid out to you, MCP servers and tool use, they are complementary. They are different things, but they are complementary. The idea behind MCP is that you do not have to author the tool function and the tool schema. That is something that's done for you by someone else and is being wrapped up inside of this MCP server. So at some level, yeah, they're kind of similar because we are talking about tool use in both cases, but MCP servers are really talking about who is doing the actual work. So if you ever see this criticism, again, it's usually because people don't quite understand what MCP is all about.

↑ 目次へ戻る

Introduction to MCP | Lesson 03「MCP clients」(全文ナレッジ)

動画 4分56秒 / 講師: Stephen Grider(コース講師)/ 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

MCP クライアントの役割

  • MCP クライアント=自分のサーバーと MCP サーバーの間の通信手段。MCP サーバーが実装するすべてのツールへの「アクセスポイント」になる。
  • クライアント自身はツールを実行しない。メッセージの仲介役であり、実行するのは MCP サーバー。

トランスポート非依存(Transport Agnostic)

  • MCP はトランスポート非依存=クライアントとサーバーはさまざまなプロトコルで通信できる
  • 現在非常に一般的な構成:MCP サーバーを MCP クライアントと同じ物理マシン上で動かし、標準入出力(stdio)で通信する。本コースのセクション後半でセットアップするのもこの構成。
  • 他にも HTTP/WebSocket など、多数の方式で接続可能。

MCP のメッセージ(spec で定義)

  • 接続確立後、クライアントとサーバーはメッセージ交換で通信する。許されるメッセージの種類は MCP の仕様(spec) にすべて定義されている。
  • 本コースで注目する4つ:
  • ListToolsRequest(クライアント→サーバー):「提供しているツールを列挙して」
  • ListToolsResult(サーバー→クライアント):提供可能な全ツールのリスト
  • CallToolRequest(クライアント→サーバー):「この引数でこのツールを実行して」
  • CallToolResult(サーバー→クライアント):ツール実行の結果

エンドツーエンドのフロー(6者のシーケンス:User/Our Server/MCP Client/MCP Server/Github/Claude)

  1. ユーザーが自分のサーバーへ質問を送る(例:「私はどんなリポジトリを持っている?」)
  2. サーバーは Claude へリクエストを送る前に「寄り道」:MCP クライアントにツール一覧を依頼 → クライアントが ListToolsRequest を MCP サーバーへ → ListToolsResult が返る → クライアントがツール一覧をサーバーへ戻す
  3. サーバーが クエリ+ツール一覧 を Claude へ送信
  4. Claude が「ツールを呼びたい」と判断し tool use メッセージパートで応答
  5. サーバーは(自分では実行せず)MCP クライアントへ実行を依頼 → クライアントが CallToolRequest を MCP サーバーへ → MCP サーバーが GitHub へ実リクエスト → GitHub がリポジトリ一覧を応答 → MCP サーバーが結果を CallToolResult に包んで返す → クライアントがサーバーへ渡す
  6. サーバーは tool result をユーザーメッセージ内に入れて Claude へ追いリクエスト → Claude が最終回答(「あなたのリポジトリは…」)を生成 → サーバーがユーザーへ返す - このフローは複雑だが、後で自作の MCP クライアント/サーバーを実装するときに全部品が再登場するため先に全体像を見せている。

実務ポイント(このレッスンから持ち帰るもの)

  1. 役割の切り分けを正確に:Our Server=会話とループの制御/MCP Client=メッセージの仲介・アクセスポイント/MCP Server=ツールの実装と実行。デバッグ時に「どの層の問題か」を即切り分けられる。
  2. stdio はローカル同居構成の標準トランスポート。まずローカル+stdio で組み、必要になったら HTTP/WebSocket 等へ差し替えられる(transport agnostic)。
  3. 4つの基本メッセージ型(ListToolsRequest/Result・CallToolRequest/Result) が MCP 通信の骨格。ログを読むときもこの4種を軸に追う。
  4. LLM への1回目のリクエスト前に必ず ListTools の往復が入る(クエリ+ツール一覧をセットで送るため)。「Claude に何のツールを見せるか」はこの時点で決まる。
  5. Claude API 側の tool use ループ(tool_use → tool_result をユーザーメッセージで返す)と、MCP 側のメッセージ往復が二重ループとして噛み合う構造を押さえると、自作クライアント実装がそのまま書ける。

本文(時系列・日本語逐語+画面差し込み)

🖥 [0:00] 画面: スライド「MCP Client」。サブタイトル:「The MCP Client provides communication between your server and a MCP Server」 図:左「Our Server」枠の中に「MCP Client」ボックス。双方向矢印で「MCP Server for Github」枠(内部に tool get_repos()schemafn)へ接続し、そこから右の「Github」(赤茶色ボックス)へ矢印

[0:00] 次に調べていくモデル・コンテキスト・プロトコルの構成要素は、クライアントです。

[0:04] クライアントの目的は、あなたのサーバーと MCP サーバーの間の通信手段を提供することです。このクライアントが、そのサーバーによって実装されたすべてのツールへのアクセスポイントになります。

🖥 [0:14] 画面: スライド「Transport Agnostic」。サブタイトル:「Communication between the Client and Server can be done over many different protocols」 図:「Our Server」内の「MCP Client」と「MCP Server for GithubRunning locally」を結ぶ双方向矢印、ラベルは「Standard IO

[0:14] さて、MCP はトランスポート非依存(transport agnostic)です。これは小難しい用語ですが、クライアントとサーバーがさまざまな異なるプロトコルで通信できる、というだけの意味です。

[0:25] 現時点で MCP サーバーを動かす非常に一般的な方法は、MCP クライアントと同じ物理マシン上で動かすことです。そしてこの2つが同じマシンで動いているなら、標準入出力(standard input/output)を介して通信できます。

[0:34] それこそが、このセクションの後半で私たちがセットアップしていくものです。

🖥 [0:39] 画面: 同じ構図のスライド(タイトルは「MCP Client」表記)。矢印のラベルが「HTTP」に切り替わる(右枠は「MCP Server for Github」、Running locally 表記なし)

[0:39] ただし、MCP クライアントと MCP サーバーを接続する方法は、ほかにもあります。HTTP や WebSocket、あるいは他のいくつもの方式・技術でも接続できるのです。

🖥 [0:52] 画面: スライド「MCP Communication」。サブタイトル:「The MCP specification defines different types of messages that can be exchanged」 図:「Our Server」内の「MCP Client」→「MCP Server for Github」へ ListToolsRequest(注記イタリック:Give me a list of tools you provide)。逆向きに ListToolsResult(注記:Here are the tools I can run

[0:51] クライアントとサーバーの間で接続が確立されると、両者はメッセージを交換することで通信します。

[0:57] やり取りが許されるメッセージが正確に何かは、すべて MCP の仕様(spec)の中で定義されています。

[1:02] 私たちが注目していくメッセージ型のいくつかが、list tools request と list tools result です。

[1:09] ご想像のとおり、ListToolsRequest はクライアントからサーバーへ送られ、サーバーが提供するすべての異なるツールを列挙するよう求めます。

[1:13] サーバーはそれに対して ListToolsResult メッセージで応答します。その中には、サーバーが提供できるすべての異なるツールのリストが入っています。

🖥 [1:23] 画面: 同じ「MCP Communication」スライド。メッセージが CallToolRequest(注記:Please run a particular tool with these arguments)と CallToolResult(注記:Here's the result of the tool run)に切り替わる

[1:18] もう2つ、あなたと私がよく見ることになるメッセージ型が、call tool request と call tool result です。

[1:28] 前者は、何らかの特定の引数を付けてツールを実行するようサーバーに求め、後者にはそのツール実行の結果が入ります。

[1:35] さて、この時点で、サーバーとクライアントという考え方は手に入りました。しかし、これらすべてが実際どう一緒に動くのかは、おそらくまだあまりクリアではないと思います。

[1:44] そこで、この動画の残りでやることはこうです。たくさんの登場者の間で行われる、あるやり取りの実例を最初から最後まで歩いて確認していきます。

🖥 [1:50] 画面: 空のシーケンス図。上部に6つのライフライン(点線の縦線付きボックス):User/Our Server/MCP Client/MCP Server/Github/Claude (以降のフレームでは列順が …MCP Server/Claude/Github に並び替わる)

[1:53] ですので、これはやや込み入ったプロセスになりますが、ユーザー、私たちが組み立てているサーバー、MCP クライアント、MCP サーバー、データを取りに行く相手のプロバイダーとしての GitHub、そして Claude——この間で行われる通信を想像していきます。

🖥 [2:09] 画面: シーケンス図に最初の矢印。User → Our Server:「What repositories do I have?

[2:05] では、始めましょう。あくまでステップ・バイ・ステップです。最初に起きると想定されるのは、ユーザーが「私はどんなリポジトリを持っていますか?」のような、何らかのクエリや質問を私たちのサーバーに送信することです。

[2:14] この時点で、Claude へリクエストを送るのは私たちのサーバーの仕事になります。

[2:18] ただし、そのリクエストの中には、Claude がアクセスできるすべての異なるツールを列挙して入れておきたいのです。

[2:22] そこで、私たちのサーバーが Claude へリクエストを送れるようになる前に、まず MCP クライアントと(MCP)サーバーを経由する、ちょっとした寄り道をすることになります。

[2:29] 起きることはこうです。

🖥 [2:40] 画面: シーケンス図に追加。Our Server → MCP Client:「I need a list of tools to send to Claude」、続いて MCP Client → MCP Server: ListToolsRequest(赤茶色ボックス)

[2:31] サーバーは、ユーザーのクエリと一緒に Claude へ送るためのツール一覧を確認する必要がある、と気づきます。

[2:37] そこで、MCP クライアントにツール一覧の取得を依頼します。

[2:40] MCP クライアントは、それを受けて ListToolsRequest を(MCP)サーバーへ送ります。

[2:45] そしてサーバーは ListToolsResult で応答します。

🖥 [2:54] 画面: シーケンス図に追加。MCP Server → MCP Client: ListToolsResultMCP Client → Our Server:「Here are the tools」。続いて Our Server → Claude へ長い矢印、ラベル Query + Tools(ベージュのボックス)

[2:48] これで MCP クライアントはツール一覧を手に入れたので、そのツール一覧をサーバーへ返します。

[2:54] そしていま、私たちのサーバーは、Claude への最初のリクエストを行うために必要なものをすべて持っています。ユーザーからの元のメッセージと、同梱すべきツール一覧の両方です。

[3:03] というわけで、私たちのサーバーは、そのクエリとツール一式を添えて Claude へリクエストを送ることができます。

[3:08] Claude はそれらのツールを眺めて、こう気づきます。「そうだ、ユーザーのこの元の質問に答えるためには、ぜひツールを呼びたい」。

🖥 [3:23] 画面: シーケンス図に追加。Claude → Our Server へ戻り矢印、ラベル ToolUse(ベージュのボックス)。続いて Our Server → MCP Client:「Please run this tool with these args

[3:12] そこで Claude は、何らかの tool use メッセージパートで応答します。

[3:17] この時点で私たちのサーバーは、Claude がツールを実行したがっていることに気づきます。しかし、私たちのサーバーはもう、ツールの実行を実質的に担当していません。代わりに、私たちのツールは MCP サーバーによって実行されることになります。

[3:34] そこで、Claude が求めているツールを実行するために、私たちのサーバーは MCP クライアントに対して、Claude から提供された特定の引数を付けてツールを実行するよう依頼します。

[3:44] ただし、MCP クライアントが実際にツールを実行するわけではありません。MCP クライアントは CallToolRequest を MCP サーバーへ送ります。

🖥 [3:51] 画面: シーケンス図に追加。MCP Client → MCP Server: CallToolRequest(赤茶色ボックス)、MCP Server → Github:「Request to Github」、Github → MCP Server:「Response」、MCP Server → MCP Client: CallToolResultMCP Client → Our Server:「Here's the result of running the tool

[3:45] MCP サーバーはそのリクエストを受け取り、GitHub へ追加のリクエストを送ります。

[3:56] ここが、この特定のユーザーに属するリポジトリの一覧を、実際に取得する場所です。

[4:01] GitHub はそのリポジトリ一覧を応答として返します。すると MCP サーバーは、そのデータを CallToolResult の中に包み込み、MCP クライアントへ送り返します。

[4:08] 次に MCP クライアントが、今度はその結果を私たちのサーバーへ手渡します。

🖥 [4:21] 画面: シーケンス図が完成。Our Server → Claude へ矢印、ラベル toolResult(ベージュのボックス)。最後に Claude → Our Server → User へ戻り矢印:「Your repositories are...

[4:14] さあ、私たちのサーバーはリポジトリ一覧を持ちました。そして、tool result をユーザーメッセージの中に格納した形で、Claude へ追いかけのリクエストを送ることができます。

[4:27] この tool result には、Claude が求めていたリポジトリ一覧が入っています。

[4:32] そしていまや Claude は、最終的な応答を組み立てるために必要な情報をすべて持っています。

[4:37] そこで Claude は「あなたのリポジトリは〜です」といったテキストを書き出し、それを私たちのサーバーへ送り返し、サーバーはそれをユーザーへと送り返します。

[4:42] さて、このフロー——ええ、確かにかなり複雑です。これをお見せした理由は、この少し後で、あなたと私が自作のカスタム MCP クライアントと MCP サーバーを実装し始めるときに、これらのさまざまな部品すべてを再び目にすることになるからです。

英語逐語(ASR全文)

The next portion of model context protocol that we're going to investigate is the client. The purpose of the client is to provide a means of communication between your server and a MCP server. This client is going to be your access point to all the tools implemented by that server.

Now MCP is transport agnostic. This is a fancy term that just says that the client and the server can communicate over a variety of different protocols. A very common way to run a MCP server right now is on the same physical machine as the MCP client. And if these two things are running on the same machine, then they can communicate over standard input output. And that's what we are going to be setting up later on inside this section. There are, however, other ways we can connect the MCP client with the MCP server. So they can also connect over HTTP or web sockets or any of a number of other varieties or techniques.

Once a connection has been formed between the client and the server, they communicate by exchanging messages. The exact messages that are allowed are all defined inside of the MCP spec. Some of the message types that you and I are going to be focusing are the list tools request and the list tools result. As you guessed, list tools request is sent from the client to the server and asks the server to list out all the different tools that it provides. The server would then respond with a list tools result message, which contains a list of all the different tools that it can provide. Two other common message types that you and I are going to see are the call tool request and call tool result. The first will ask the server to run a tool with some particular arguments and the second will contain the result of the tool run.

Now at this point in time we've got this idea of a server and a client but I suspect it's probably not really clear how all this stuff really works together. So here's what we're going to do in the remainder of this video. We are going to walk through a example call between a lot of different things. So this will be kind of an involved process, but we're going to imagine the communication that goes on between a user, our server that we're putting together, a MCP client, the MCP server, GitHub as some provider that we're trying to access some data from, and Claude.

So let's get to it. Again, step by step. First thing we would expect to happen is a user to submit some kind of query or question to our server, like what repositories do I have? At this point, it would be up to our server to make a request off to Claude. But in that request, we want to list out all the different tools that Claude has access to. So before our server can make the request off to Claude, it's first going to go through a little side detour through the MCP client and the server. So here's what happens. The server is going to realize that it needs to see a list of tools to send off to Claude, along with the user's query. So it's going to ask the MCP client to get a list of tools. The MCP client, in turn, is going to send a list tools request off to the server. And the server will respond with a list tools result. Now that our MCP client has a list of the tools, it will give that list of tools back to the server. And now our server has everything it needs to make an initial request off to Claude. It has both the original message from the user and a list of tools to include.

So our server can make a request off to Claude with that query and the set of tools. Claude is going to take a look at the tools and realize, you know what, in order to answer the user's original question right here, I really want to call a tool. So Claude would respond with some tool use message part. At this point, our server is going to realize that Claude wants to run a tool, but our server is no longer really in charge of executing any tools. Instead, our tools are going to be executed by the MCP server. So in order to run the tool that Claude is asking for, our server is going to ask the MCP client to run a tool with some particular arguments that were provided by Claude. The MCP client, however, doesn't actually run the tool. It's going to send a call tool request off to the MCP server. The MCP server will receive that request and make a follow-up request off to GitHub. So this is where we would actually be getting a list of repositories that belong to this particular user. GitHub would respond with that list of repositories. Then the MCP server would wrap up that data inside of a call tool result and send that back to the MCP client. Then the MCP client in turn would hand the result off to our server.

Now our server has the list of repositories and it can make a follow-up request to Claude with the tool result parked inside of a user message. So this tool result would include the list of repositories that Claude was asking for. And now Claude has all the information it needs to formulate a final response. So it'll write out some text of something like your repositories are, and then send that back to our server, and our server would send it on back to our user. All right, so this flow, yes, it is rather complicated. The reason I want to show you this is that we are going to see all these different pieces as you and I start to implement our own custom mcp client and mcp server a little bit later on

↑ 目次へ戻る

Introduction to MCP | Lesson 04「Project setup」(全文ナレッジ)

動画 3分09秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • このモジュールで作るもの=CLIベースのチャットボット。ユーザーがドキュメント群(偽データ・メモリ内のみ保持)とチャットできる。
  • Claude がドキュメントを読める編集できる
  • @doc_name でドキュメントをメンション→その内容が自動でコンテキストに含まれる
  • /command_nameコマンドを実行できる
  • 構成:自作の小さな MCP クライアントが、自作のカスタム MCP サーバーに接続する。サーバーには当面ツールを2つ実装(①ドキュメント内容を読むツール、②ドキュメント内容を更新するツール)。
  • 最重要の注意(Important Note):通常のプロジェクトでは MCP クライアント MCP サーバーのどちらか一方を実装する。
  • サーバーだけ作る例=自社サービスへのアクセスを世界中の開発者に配布する
  • クライアントだけ作る例=他のエンジニアが実装済みの外部 MCP サーバーに接続する
  • 本講座では理解のために両方を1プロジェクトで作る(実務の標準形ではない)。
  • セットアップ手順:動画添付の cli_project.zip をダウンロード→展開→そのディレクトリでエディタを開く→README.md の指示に従い .envANTHROPIC_API_KEY を設定→uv あり/なしで依存をインストール→uv run main.py(uv 不使用なら python main.py)で起動→チャットプロンプトが出れば成功。

実務ポイント(このレッスンから持ち帰るもの)

  1. 「クライアントとサーバーは普通どちらか片方だけ実装する」が MCP 開発の基本姿勢。教材が両方作るのは学習目的の特例——実案件では自分がどちら側を書いているのかを最初に確定させる。
  2. スターター構成は main.py(CLI 入口)/mcp_client.pymcp_server.pycore/pyproject.tomluv.lock(uv 前提)/.env(API キー)——MCP 学習プロジェクトの最小テンプレートとしてそのまま流用できる。
  3. API キーは .envANTHROPIC_API_KEY="" に入れる方式。コードに直書きしない型を最初から踏襲。
  4. 起動確認は「チャットプロンプトが出る→ what's 1+1 に即応答」のスモークテストで行う(機能追加前に土台の動作を必ず確認する習慣)。

本文(時系列・日本語逐語+画面差し込み)

[0:00] MCP のいくつかの側面をより深く理解するために、これから自分たち自身の CLI ベースのチャットボットを実装していきます。

[0:06] これによって、クライアントとサーバーが実際にどう連携して動くのか、より良いイメージが持てるようになります。

[0:10] このビデオでは、少しだけプロジェクトのセットアップを行い、これから何を作るのかを正確に理解してもらいたいと思います。

[0:15] こちらに、これから作るものについてのプロダクト説明がたくさんあります。時間をかけて全部見ていきますが、今は高レベルの理解だけしてもらえれば大丈夫です。

🖥 [0:15] 画面: スライド「Project!」(左=箇条書き、右=ターミナルのモックアップ)

Project!

・CLI based chatbot that allows users to chat with a set of documents
・Claude should be able to read a document
・Claude should be able to edit a document
・Users can “mention” a document by writing out “@doc_name”
    ○ The doc’s contents will automatically be included as context
・Users can run a “command” with “/command_name”

(右のターミナルモック)
> Can you please summarize the contents of @▌
    deposition.md   Resource
    design.md       Resource
    financials.md   Resource
    outlook.md      Resource
    plan.md         Resource
    spec.md         Resource

[0:23] さて、申し上げたとおり、CLI ベースのチャットボットになります。

[0:27] ユーザーがドキュメントのコレクションを扱えるようにします。

[0:30] これらは偽のドキュメントで、メモリ上に保存されるだけです。

[0:33] 小さな MCP クライアントを作り、それが自分たちのカスタム MCP サーバーに接続するようにします。

[0:40] 今のところ、このサーバーには2つのツールを実装します。

[0:43] 1つはドキュメントの内容を読むツール、もう1つはドキュメントの内容を更新するツールです。

🖥 [0:33〜0:50] 画面: 構成図スライド

[Our Server]                    [Our MCP Server]
  ┌─────────────┐                ┌──────────────────────┐      ─ document.pdf
  │ Our         │  ←──────→      │ Tool to read a doc    │──┬── ─ spreadsheet.xlsx
  │ MCP Client  │                │ Tool to update a doc  │  └── ─ report.txt
  └─────────────┘                └──────────────────────┘       ─ spec.md

[0:49] 繰り返しますが、これらのドキュメントは右側にあるものです。すべて偽物なので、メモリ内にのみ永続化されます。それだけです。

[0:55] さて、先に進む前に、非常に重要な注意点があります。このプロセス全体について、本当に理解しておいてほしいことです。

[1:03] それは、通常のプロジェクトでは、一般的にクライアントか MCP サーバーのどちらか一方を実装する、ということです。

🖥 [0:56] 画面: スライド「Important Note!」

Important Note!
Normally a project will implement either an MCP client or an MCP server.
Our project will implement both just so we understand how they work

[Our Server]                     ┌─────────────┐
  ┌────────────┐                 │             │
  │ MCP Client │   ←──────→      │  MCP Server │
  └────────────┘                 └─────────────┘

[1:11] つまり実際のプロジェクトでは、MCP サーバーだけを作って世界に配布し、自分たちが構築したサービスに開発者がアクセスできるようにする、ということがあります。

[1:19] あるいは、MCP クライアントだけを作るプロジェクトかもしれません。

[1:24] その場合の意図は、他のエンジニアによってすでに実装されている外部の MCP サーバーに接続することです。

[1:31] このプロジェクトでは、クライアントとサーバーの両方を作ります。1つのプロジェクトでそれをやることで、これらが実際にどう連携して動くのかを、より深く理解してもらうためです。

[1:44] さて、この断り書きが済んだので、少しセットアップをしていきましょう。このビデオに添付されている cli_project.zip というファイルがあるはずです。

[1:50] その中に、このプロジェクトのスターターコードが入っています。

[1:53] その zip ファイルをダウンロードして展開し、そのプロジェクトディレクトリの中でコードエディタを開いてください。

🖥 [1:52] 画面: macOS Finder(Downloads フォルダ)。ダウンロード直後の cli_project.zip が1つだけ表示されている(=動画添付 zip の実ファイル名が cli_project.zip であることを画面で確認できる)。

[1:59] 少し時間を節約するため、私はすでにそれを済ませています。

[2:02] つまり、すでにその小さなプロジェクトの中でコードエディタを開いています。

🖥 [2:02] 画面: VS Code のエクスプローラー(プロジェクト名 mcp)

mcp/
├── core/
├── .env
├── .gitignore
├── main.py
├── mcp_client.py
├── mcp_server.py
├── pyproject.toml
├── README.md
└── uv.lock

[2:06] このプロジェクトの中では、README.md ファイルを見ることをおすすめします。

[2:10] この中に、セットアップの手順を書いておきました。

[2:12] このプロジェクト内の .env ファイルに API キーを入れる手順を案内してくれますし、

🖥 [2:10] 画面: README.md(VS Code で表示・見えている範囲の転記)

- Python 3.10+
- Anthropic API Key

## Setup

### Step 1: Configure the environment variables

1. Create or edit the `.env` file in the project root and verify that the
   following variables are set correctly:

```
ANTHROPIC_API_KEY=""  # Enter your Anthropic API secret key
```

### Step 2: Install dependencies

#### Option 1: Setup with uv (Recommended)

[uv](https://github.com/astral-sh/uv) is a fast Python package installer and
resolver.

[2:18] uv を使う場合と使わない場合の両方について、依存関係のインストール手順も案内してくれます。

[2:24] このセットアップをすべて終えたら、すぐにスタータープロジェクトを実行できます。

[2:28] そのためには、ターミナルの中で、自分のプロジェクトディレクトリの中にいることを確認してください。

[2:34] 私はプロジェクトを mcp と名付けました。その中に、すべてのプロジェクトファイルとフォルダがあります。

[2:39] プロジェクトを実行するには、uv を使っている場合は uv run main.py を実行します。

[2:45] uv を使っていない場合は、単に python main.py です。

[2:49] 私は uv を使っているので、uv run main.py を実行します。

🖥 [2:39〜2:52] 画面: ターミナル(プロンプトは「(mcp) → mcp」=venv名+カレントディレクトリ名)

(mcp) → mcp  ls
__pycache__    main.py          mcp_server.py
core           mcp_client.py    README.md
(mcp) → mcp  uv run main.py▌

[2:54] 実行すると、チャットプロンプトが表示されるはずです。「1足す1は?」と聞くと、

[3:00] すぐに応答が返ってくるはずです。

[3:03] セットアップは以上です。ここからは、このアプリケーションに新しい機能を追加していくことに集中できます。

英語逐語(ASR全文)

To better understand some aspects of MCP, we are going to start to implement our own CLI-based chatbot. This is going to give us a better idea of how clients and servers actually work together. In this video, I want to do a little bit of project setup and just help you understand exactly what we're going to make. I've got a lot of product description over here of what we're going to build. We're going to go through all this over time. For right now, I just want you to get a high-level understanding. So, as I mentioned, it's going to be a CLI-based chatbot. We're going to allow users to work with a collection of documents. These are going to be fake documents. They're just going to be stored in memory. We're going to build out a small MCP client that is going to connect to our own custom MCP server. For right now, this server is going to have two tools implemented inside of it. One tool to read the contents of a document and one tool to update the contents of a document. Again, these documents are here on the right hand side. They're all fake, so they are going to be persisted only in memory. That's it. Now, before we go any further, there is a very important note, something I really want you to understand around this entire process. And that is that on a normal project, typically we would be implementing either a client or an MCP server. So on a real project, we might be authoring just an MCP server to distribute to the world and allow developers to access some service we have built up. Alternatively, we might be building a project where we make only a MCP client. and the intent here would be that we would be connecting to some outside MCP servers that have already been implemented by some other engineers. So in this project we are making both a client and a server and we're just doing that in one project so you get a better understanding of how this stuff actually works together. All right now that we have this disclaimer out of the way let's go through just a little bit of setup. Attached to this video you should find a file named cliproject.zip. Inside there is some starter code for our project. Make sure you download that zip file, extract it, and then open up your code editor inside of that project directory. Just to save a little bit of time, I have already done so. So I've already got my code editor open inside of that small project. Inside this project, I would encourage you to take a look at the readme.emd file. Inside here, I've placed some setup directions. So it's going to walk you through the process of making sure you put your API key into the .emd file inside this project. and it's also going to walk you through the process of installing dependencies, either with UV or without UV. Once you have gone through all this setup, you can then run the starter project right away. To do so, inside of your terminal, make sure that you are inside of your project directory, so I called my project MCP, and inside there I've got all my different project files and folders. To run the project, we will run uv run main.py if you are making use of UV. If you are not making use of UV, then it'll be just python main.py. Now I'm making use of uv so I'm going to do a uv run main.py and then when I run that I should see a chat prompt appear and if I ask what's one plus one I should see a response rather quickly. That is it for our setup so now we can start to focus on adding in some new features to this application.

(注:ASR は「readme.emd」「.emd」と誤認識しているが、画面上の実体は README.md.env。「cliproject.zip」は cli_project.zip。)

↑ 目次へ戻る

Introduction to MCP | Lesson 05「Defining tools with MCP」(全文ナレッジ)

動画 7分03秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • このレッスンのゴール:CLI チャットボット用 MCP サーバーに最初の2ツールを実装する。
  • read_doc_contents=ドキュメントを読む(doc_iddocs 辞書の値を返す)
  • edit_document=ドキュメントを編集する(old_strnew_str の単純な find & replace)
  • 公式 MCP Python SDK(FastMCP)の価値
  • mcp = FastMCP("DocumentMCP", log_level="ERROR")1行でサーバーが立つ
  • @mcp.tool(name=..., description=...) デコレータ+Pydantic の Field(description=...) 付き引数を書くだけで、SDK が裏側でツールの JSON スキーマを自動生成し Claude に渡せる——手書きの巨大 JSON スキーマが不要になる
  • ツール定義の構成要素(このレッスンで明示されたもの):①ツール名 ②ツール説明 ③引数 ④引数の型 ⑤引数の説明。
  • description の作法:本来は「Claude がいつこのツールを使うべきか」が明確になるよう肉付けした説明を書くべき(講座では時間節約のため簡素にしている)。
  • 防御的実装:Claude が存在しないドキュメントを要求するケースに備え、if doc_id not in docs: raise ValueError(f"Doc with id {doc_id} not found") を両ツールに入れる。
  • edit_document内容を返さない(編集するだけ)。old_str は「空白含め完全一致」が必要。

実務ポイント(このレッスンから持ち帰るもの)

  1. ツールは「デコレータ+型ヒント+Field」で宣言的に定義——JSON スキーマは SDK 生成に任せる。これが MCP サーバー開発の標準形。
  2. description はツール選択の精度を決める。実務では「いつ使うか」まで書き込む(講座の簡素版をそのまま真似ない)。引数側にも Field(description=...) を必ず付ける。
  3. 入力バリデーション→ValueError 送出のパターンをすべてのツールに敷く。エラーメッセージに識別子(doc_id)を含めると Claude 側が自己修正しやすい。
  4. edit_documentold_strMust match exactly, including whitespace」は Claude Code の Edit ツールと同じ設計思想——完全一致置換 API の説明文の書き方としてそのまま使える。
  5. 実物完成形は assets/cli_project_COMPLETE/mcp_server.py。動画では from pydantic import Field をファイル先頭に足すが、完成形ファイルでは docs 定義の後(23行目)に置かれている(動作は同じ)。

本文(時系列・日本語逐語+画面差し込み)

[0:00] それでは、CLI チャットボット用の MCP サーバーを作り始めましょう。

[0:04] 見てきたとおり、CLI 自体はすでに動いていて、Claude とチャットすることはできますが、まだ MCP サーバーに関する追加機能は何も紐づいていません。

[0:12] そこで、今のところ2つのツールを持つ、この MCP サーバーを追加する作業をしていきます。

[0:17] 1つはドキュメントを読むツール、もう1つはドキュメントの内容を更新するツールです。

🖥 [0:17] 画面: 構成図スライド(再掲)——「Our MCP Server」の中に Tool to read a doc/Tool to update a doc、右に document.pdf/spreadsheet.xlsx/report.txt/spec.md

[0:22] サーバーの実装は、プロジェクトのルートディレクトリにある mcp_server.py ファイルの中に置きます。

[0:29] この中で、基本的な MCP サーバーをセットアップする作業は、すでに少し済ませてあります。

[0:33] それから、メモリ内にのみ存在するドキュメントのコレクションを定義しました。

[0:38] そして最後に、いくつかの TODO 項目をまとめておきました。

🖥 [0:29] 画面: VS Code mcp_server.py(スターターコード全文)

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("DocumentMCP", log_level="ERROR")


docs = {
    "deposition.md": "This deposition covers the testimony of Angela Smith, P.E.",
    "report.pdf": "The report details the state of a 20m condenser tower.",
    "financials.docx": "These financials outline the project's budget and expenditures.",
    "outlook.pdf": "This document presents the projected future performance of the system.",
    "plan.md": "The plan outlines the steps for the project's implementation.",
    "spec.txt": "These specifications define the technical requirements for the equipment.",
}

# TODO: Write a tool to read a doc
# TODO: Write a tool to edit a doc
# TODO: Write a resource to return all doc id's
# TODO: Write a resource to return the contents of a particular doc
# TODO: Write a prompt to rewrite a doc in markdown format
# TODO: Write a prompt to summarize a doc

[0:41] これらは、あなたと私がこのファイルの中で完了させていくタスクです。

[0:44] 今は、先ほど言ったとおり、最初の2項目、つまり2つのツールを書き出すことだけに取り組みます。

[0:50] さて、私たちは以前にもツールを書いたことがありますが、あそこには多くの構文がありましたよね。あの大きな JSON スキーマです。

[0:58] でも、ここで良いニュースがあります。

[1:00] このプロジェクトでは、公式の MCP Python SDK を使います。

[1:05] ここで使っている、この mcp パッケージがそれです。

[1:09] この MCP パッケージは、たった1行のコードで MCP サーバーを作ってくれます。ちょうどそこに見えているとおりです。

[1:17] この SDK は、ツールの定義も本当に簡単にしてくれます。

[1:20] ツールを定義するには、こちらの右側に見えているものを書き出すだけです。

🖥 [1:00〜1:45] 画面: スライド「MCP SDK」

MCP SDK

・The MCP project provides SDK’s for building servers and clients
  in a variety of languages
・Our project is using the Python MCP SDK
・The Python SDK makes it very easy to declare tools
@mcp.tool(
    name="add_ints",
    description="Add two integers together",
)
def tool_fn(
    a=Field(description="First number to add"),
    b=Field(description="Second number to add"),
) -> int:
    return a + b

[1:24] これで、add_ints(整数を足す)という名前のツールが、この説明と、渡すことが必須の2つの引数付きで作られます。

[1:36] このようなツール定義を書き出すと、舞台裏で MCP がツールの JSON スキーマを生成してくれて、それを Claude に渡すことができます。

[1:42] というわけで、見てのとおり、ツールを定義するといった基本的なことが、すぐにずっと簡単になり始めます。

[1:47] さて、言ったとおり、最初のタスクはこの2つの異なるツールを実装することです。

[1:51] さっそく mcp_server.py ファイルに戻りましょう。

[1:56] そして、最初のツール、ドキュメントを読むツールから実装を始めます。

[1:59] ここでの唯一のゴールは、何らかのドキュメントの名前を受け取って、その内容を返すことです。

[2:05] すべてのドキュメントは、すでにこの docs 辞書の中に置かれています。

[2:08] キーはドキュメントの ID、実質的には名前です。値はドキュメントの内容です。

[2:14] つまり、私たちのツールは本当にシンプルです。

[2:16] これらの文字列のうちの1つを受け取り、この docs 辞書の中の該当する値を見て、それを返す。やることはそれだけです。

[2:26] これを実装するために、最初の TODO を見つけて、そのすぐ下に @mcp.tool と書いて新しいツールを定義します。

[2:34] このツールに read_doc_contents という名前を付け、説明は「ドキュメントの内容を読み、文字列として返す(Read the contents of a document and return it as a string.)」とします。

[2:47] 覚えておいてください。完璧な世界では、Claude がこのツールをいつ使うべきかが超明確になるように、ここには本当に肉付けした説明を書きます。

[2:55] でも今は、いつものように時間を少し節約して、大量のテキストを打たなくて済むように、非常にシンプルな説明のままにしておきます。

[3:01] 次に、実際のツール関数を定義します。これは、このツールを実行すると決めたときに実行される関数です。

[3:06] read_document と呼ぶことにします。doc_id という引数を受け取り、それは文字列(str)で、

[3:14] それを Field に設定し、説明を「読み取るドキュメントの ID(Id of the document to read)」とします。

🖥 [3:14] 画面: VS Code・入力途中(Field をこれから書くところ。タブに未保存マーク、Pyright が F に赤波線)

# TODO: Write a tool to read a doc
@mcp.tool(
    name="read_doc_contents",
    description="Read the contents of a document and return it as a string."
)
def read_document(
    doc_id: str = F▌
):

[3:23] それから、この Field クラスを先頭でインポートしておく必要があります。

[3:26] なので、先頭に行って from pydantic import Field というインポートを追加します。

[3:36] それから、ここに戻って、関数本体の中に実際の実装を入れていきます。

[3:41] 最初にやるのは、Claude が実際には存在しないドキュメントを要求したケースにきちんと対処することです。

[3:47] if doc_id not in docs

[3:51] つまり、指定されたドキュメント ID がこの辞書のキーとして見つからなければ、

[3:56] ValueError を、f 文字列「doc with id {doc_id} not found」付きで送出します。

[4:06] そのチェックを通過したら、実際のドキュメントを返します。return docs[doc_id] です。

🖥 [4:09] 画面: VS Code・1本目のツール完成時点

@mcp.tool(
    name="read_doc_contents",
    description="Read the contents of a document and return it as a string."
)
def read_document(
    doc_id: str = Field(description="Id of the document to read")
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    return docs[doc_id]

📄 実物コード(cli_project_COMPLETE/mcp_server.py 27〜37行目)——完成形リポジトリでは同じツールが次の形で確定している:

@mcp.tool(
    name="read_doc_contents",
    description="Read the contents of a document and return it as a string.",
)
def read_document(
    doc_id: str = Field(description="Id of the document to read"),
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    return docs[doc_id]

[4:13] 以上です。ツールを定義するのに必要なのはこれだけです。

[4:16] ツールの名前、その説明、期待される引数、その型、そしてその引数の説明も指定しました。

[4:25] これらのさまざまなデコレータや Field 型などはすべて、この Python MCP SDK によってまとめて取り込まれ、JSON スキーマを生成してくれます。

[4:36] 最初のツールを実装したので、そこの TODO を削除します。

[4:39] 次に、もう1つのツール、ドキュメントを編集するツールを実装します。まったく同じプロセスを繰り返します。

[4:46] mcp.tool と書き、名前は edit_document、説明は

[4:55] 「ドキュメントのコンテキスト……失礼、内容の中の文字列を新しい文字列で置き換えて、ドキュメントを編集する(Edit a document by replacing a string in the documents content with a new string)」とします。

[5:03] 実装のほうは、この関数を edit_doc……いや、一貫性を持たせて edit_document と呼びましょう。

[5:11] そして、ここではいくつかの引数を受け取ります。最初はドキュメント ID、次に検索する古い文字列、そして古い文字列を置き換える新しい文字列です。

[5:22] 全部書き出しましょう。doc_id は文字列で、説明は「編集されるドキュメントの ID(Id of the document that will be edited)」。

[5:28] old_str は文字列で、

[5:43] 説明は「置き換えるテキスト。空白を含めて正確に一致しなければならない(The text to replace. Must match exactly, including whitespace)」。

[5:53] そして new_str は、

[5:58] 「古いテキストの代わりに挿入する新しいテキスト(The new text to insert in place of the old text)」。

🖥 [5:58〜6:10] 画面: VS Code・2本目のツールのシグネチャ入力完了時点(説明文の右端は画面外。if doc_id を打ちかけ)

@mcp.tool(
    name="edit_document",
    description="Edit a document by replacing a string in the documents content wi…"
)
def edit_document(
    doc_id:str = Field(description="Id of the document that will be edited"),
    old_str: str = Field(description="The text to replace. Must match exactly, incl…"),
    new_str: str = Field(description="The new text to insert in place of the old te…"),
):
    if doc_id

[6:02] つまり、ここでのドキュメント編集は、ごくシンプルな検索と置換(find and replace)です。それだけです。

[6:07] もう一度、この中で、Claude が実際に存在するドキュメントを要求していることを確認します。

[6:13] if doc_id not in docs なら raise ValueError、f 文字列「doc with id not found」付きで。

[6:25] そして、正しいドキュメントが見つかった場合、編集はこうやります。

[6:27] docs[doc_id] = docs[doc_id].replace(old_str, new_str)

🖥 [6:27〜6:55] 画面: VS Code・2本目のツール完成時点

@mcp.tool(
    name="edit_document",
    description="Edit a document by replacing a string in the documents content wi…"
)
def edit_document(
    doc_id:str = Field(description="Id of the document that will be edited"),
    old_str: str = Field(description="The text to replace. Must match exactly, incl…"),
    new_str: str = Field(description="The new text to insert in place of the old te…"),
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    docs[doc_id] = docs[doc_id].replace(old_str, new_str)

🖥 [6:55] 画面: ファイル先頭には from pydantic import Field が追記済み

from pydantic import Field
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("DocumentMCP", log_level="ERROR")

📄 実物コード(cli_project_COMPLETE/mcp_server.py 40〜56行目)——完成形での edit_document 全文(動画で画面外に切れていた description の全文もここで確認できる):

@mcp.tool(
    name="edit_document",
    description="Edit a document by replacing a string in the documents content with a new string",
)
def edit_document(
    doc_id: str = Field(description="Id of the document that will be edited"),
    old_str: str = Field(
        description="The text to replace. Must match exactly, including whitespace"
    ),
    new_str: str = Field(
        description="The new text to insert in place of the old text"
    ),
):
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")

    docs[doc_id] = docs[doc_id].replace(old_str, new_str)

[6:40] 以上です。というわけで、あっという間に2つのツール実装を組み上げました。

[6:45] 何度でも繰り返しますが、この MCP Python SDK でツールを定義するのは、スキーマ定義を手で書き出すよりずっと簡単です。

[6:53] 両方のツールがそろったので、そこの TODO を削除します。

[6:59] さて、良いスタートです。MCP サーバーを組み上げ、その中に2つのツールを実装しました。

📄 実物コード(参考・cli_project_COMPLETE/mcp_server.py の残り)——このレッスン以降の TODO(リソース2本・プロンプト1本+stdio 起動)は完成形で次のように実装される。Lesson 05 時点では未実装:

@mcp.resource("docs://documents", mime_type="application/json")
def list_docs() -> list[str]:
    return list(docs.keys())


@mcp.resource("docs://documents/{doc_id}", mime_type="text/plain")
def fetch_doc(doc_id: str) -> str:
    if doc_id not in docs:
        raise ValueError(f"Doc with id {doc_id} not found")
    return docs[doc_id]


@mcp.prompt(
    name="format",
    description="Rewrites the contents of the document in Markdown format.",
)
def format_document(
    doc_id: str = Field(description="Id of the document to format"),
) -> list[base.Message]:
    ...


if __name__ == "__main__":
    mcp.run(transport="stdio")

英語逐語(ASR全文)

Let's start to make an MCP server for our CLI chatbot. As you saw, the CLI itself already works, and we can already chat with Claude, but there's no additional functionality around the MCP server tied to it just yet. So we're going to work on adding in this MCP server that is going to have two tools in it for right now. It is going to have one tool to read a document, and one tool to update the contents of a document. The implementation of the server is going to be placed into the mcpserver.py file inside of the root project directory. Inside of here, I've already gone through a little bit of work to set up a basic MCP server. And then I defined a collection of documents that are going to exist only in memory. And then finally, I put together some different to-do items. So these are different tasks that you and I are going to complete inside of this file. For right now, as I just mentioned, we're going to be working only on these first two items, writing out two tools. Now we have authored tools in the past, and we saw that, well, there's a lot of syntax there. There's those big JSON schemas. But I got good news for you here. In this project, we're making use of the official MCP Python SDK. So that's what this MCP package is that we're making use inside of here. This MCP package is going to create our MCP server for us with just one line of code, like what you see right there. This SDK also makes it really easy to define tools. To define a tool, all we have to do is write out what you see on the right-hand side over here. This will make a tool named add integers with this description and two arguments that are going to be required to be passed into it. Once we write out a tool definition like this, behind the scenes, MCP is going to generate a tool JSON schema for us, which we can take and pass off to Claude. So right away, as you can see, it starts to get a lot easier to do some basic things like define tools. Now, as I mentioned, our first task is going to be to implement these two different tools. So let's go back over to our mcpserver.py file right away. And we're going to start to implement the first tool of reading a document. So the only goal here is to take in the name of some document and return the contents of it. All of our documents are already placed inside of this docs dictionary. The keys are the IDs or essentially names of a document. And the value is a document's contents. So our tool is really simple. We're going to take in one of these strings, look at the appropriate value inside this docs dictionary, and then return it. That's all we need to do. So to implement this, I'm going to find the first to-do and right underneath it, I'm going to define a new tool by writing out at mcp.tool. I'm going to give this tool a name of read doc contents and a description of read the contents of a document and return it as a string. And remember in a perfect world, we put in a really fleshed out description right here to make sure it's super clear to Claude exactly when to use this tool. But for right now, just as usual to save a little bit of time and keep you from having to type out a bunch of text here, I'm just going to leave in a very simple description. Then I will define my actual tool function. So this is the function to run whenever we decide to run this tool. I will call it read document. It's going to take in a argument of doc ID that is going to be a string, I'm going to set that to a field with a description of ID of the document to read. And then we need to make sure that we import this field class at the top. So I'm going to go up to the top and add an import from Pydantic import field. Then back down here inside of the function body, I'm going to put in my actual implementation. So the first thing I'm going to do is just make sure I handle the case in which Claude asks for a document that doesn't actually exist. So I'll say if doc ID not in docs. So in other words, if the provided document ID is not found as a key inside of this dictionary, then I'm going to raise a value error with a F string of doc with ID, doc ID not found. And then if we get past that check, I'll go ahead and return the actual document. So I'll return docs at doc ID. And that's it. That's all it takes to define a tool. So we've specified the name of the tool, its description, the argument that is expected, its type, and a description for that argument as well. All these different decorators and field types and whatnot are all going to be taken together by this Python MCP SDK, and it's going to generate a JSON schema for us. Now that we've implemented this first tool, I'm going to remove the to-do right there. and then we will implement our other tool the one to edit a document so we're going to repeat the exact same process I'll say mcp.tool I'll give it a name of edit document with a description of edit a document by replacing a string in the document's context or excuse me content with a new string then for the implementation i'll call this function edit doc actually will be consistent call edit document and then we're going to take in a couple of different arguments here first is going to be a document id and then a old string to find and then a new string to replace the old string with so let's write this all out we're going to have a doc id that will be a string with a description of ID of the document that will be edited old string will be a string with a description of the text to replace must match exactly including white space and then our new string the new text to insert in place of the old text. So our document editing here is just a very simple find and replace. That's it. Once again, inside of here, I'm going to make sure that Claude is asking for a document that actually exists. So if doc ID not in docs, raise value error, with a F string of doc with ID not found. And then if we do find the correct document, here's how we will do our edit we'll say docs at doc id is docs at doc id replace old string with the new string and that's it all right so just like that we have put together two tool implementations really really quickly i can't repeat it enough defining tools with this mcp python sdk is a lot easier than writing out the schema definition manually now that we've got both tools put together. I'm going to delete the to-do right there. Okay, so this is a good start. We have put together our MCP server, and we've implemented two tools inside of it.

(注:ASR の「mcpserver.py」は画面上の実体では mcp_server.py。「add integers」はスライド上では add_ints。)

↑ 目次へ戻る

Introduction to MCP | Lesson 06「The server inspector」(全文ナレッジ)

動画 3分52秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • 課題:MCP サーバーに機能を実装したが、動くかどうか分からない→アプリに配線する前にテストしたい
  • :MCP Python SDK を使うとブラウザ内デバッガ(MCP Inspector)が自動で付いてくる。
  • 起動手順: 1. Python 仮想環境を有効化(正確なコマンドは README 参照) 2. mcp dev mcp_server.py を実行 3. 「Proxy server listening on port 6277」「MCP Inspector is up and running at http://127.0.0.1:6274」が表示される→そのアドレスをブラウザで開く
  • Inspector の使い方(v0.10.2 時点):
  • 左ペイン:Transport Type(STDIO)/Command(uv)/Arguments(run --with mcp mcp run mcp_server.py)→ Connect ボタンでサーバー起動
  • 上部メニュー:Resources / Prompts / Tools / Ping / Sampling / Roots
  • Tools → List Tools で実装済みツール一覧(read_doc_contentsedit_document)→ツールをクリック→右パネルで引数を入れて Run Tool で手動呼び出し
  • 下部に History(initialize → tools/list → tools/call の JSON-RPC 履歴)と Server Notifications
  • 検証フロー実演read_doc_contentsdeposition.md → Success+本文表示。edit_documentThisA report に置換(大文字小文字が区別される)→ Success のみ(このツールは内容を返さない)→もう一度 read_doc_contents で「A report deposition covers …」を確認。
  • 注意:Inspector は活発に開発中で UI は変わりうる。「Tools セクションを探す」が本質。

実務ポイント(このレッスンから持ち帰るもの)

  1. mcp dev <server file> が MCP サーバー開発の基本ループ——実アプリ(クライアント)に繋ぐ前に Inspector で単体テストする。自作サーバー開発では毎回使うことになる。
  2. ポートは2つある:6277=プロキシサーバー、6274=Inspector の UI。ブラウザで開くのは案内された 6274 側のアドレス。
  3. ツールの手動実行履歴は initialize → tools/list → tools/call という MCP プロトコルの実際のメッセージ順で History に積まれる——プロトコル理解のライブ教材になる。
  4. 書き込み系ツールは戻り値がないことがある(edit_document は Success のみ)。検証は読み取り系ツールを再実行して行う(write→read-back 検証パターン)。
  5. 置換系ツールは大文字小文字・空白まで完全一致。テスト時の old_str 選びから意識する。
  6. Inspector の UI は頻繁に変わる前提でノウハウを「ボタンの位置」ではなく「機能(接続→ツール列挙→手動呼び出し)」で覚える。

本文(時系列・日本語逐語+画面差し込み)

[0:00] MCP サーバーの中に機能をいくつか組み上げましたが、それが動くのかどうかは全く分かりません。

[0:04] なので、なんらかの方法でこれをテストできると本当に良いですよね。

[0:06] 実は、この Python SDK を使うことで、ブラウザ内デバッガへのアクセスが自動的に手に入ります。

[0:13] これで、このサーバーが期待どおりに動いているかを確認できます。

[0:16] 使い方を手短にお見せしましょう。

[0:18] ターミナルに戻って、自分の Python 環境が有効化されていることを確認します。

[0:24] README ドキュメントに、その環境を有効化するために実行する正確なコマンドが詳しく書いてあることを思い出してください。

[0:31] 有効化されていることを確認できたら、mcp dev、そしてサーバーが入っているファイル名を実行します。

[0:38] 今回は mcp_server.py です。

[0:42] これを実行すると、ポート 6277 でサーバーがリッスンしていると表示され、実際にアクセスするための直接のアドレスが与えられます。

🖥 [0:31〜0:50] 画面: ターミナル(プロンプトは「(mcp) → mcp」=venv名+カレントディレクトリ名)

(mcp) → mcp  mcp dev mcp_server.py
Starting MCP inspector...
⚙️ Proxy server listening on port 6277
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀

[0:50] そのアドレスを、ブラウザの中で開きます。

[0:53] そこに行くと、このような見た目のものが表示されます。

[0:56] これが MCP インスペクターです。

🖥 [0:56] 画面: ブラウザ「MCP Inspector v0.10.2」(接続前)

MCP Inspector v0.10.2

Transport Type: [STDIO ▾]
Command:   [uv]
Arguments: [run --with mcp mcp run mcp_serv…]
> Environment Variables
> ⚙ Configuration
[▶ Connect]
● Disconnected

(中央)Connect to an MCP server to start inspecting
(下部)History: No history yet | Server Notifications: No notifications yet
(左下)テーマ: Light

[0:58] さて、すぐに理解しておいてほしい重要なことがあります。

[1:01] このインスペクターは、活発に開発が進んでいます。

[1:04] なので、あなたがこのビデオを見ている頃には、いま画面に見えているものは、私がお見せしているものとは非常に大きく違っているかもしれません。

[1:10] とはいえ、おそらく非常に似た機能は持っているはずです。

[1:14] 左側に、あなたの MCP サーバー——つまり、さっき編集したあのファイル——を起動する Connect ボタンが見えます。

[1:19] Connect をクリックすると、すぐに画面の上のほうにいくつかのものが表示されます。

[1:25] まず、上部のメニューバーに注目してほしいのです。Resources、Prompts、Tools、その他が並んでいます。

[1:29] 繰り返しますが、このビデオを見る頃には UI が変わっているかもしれません。もしこのメニューバーが見えなくても、私たちが本当に探しているのは、何らかの Tools セクションだけです。

🖥 [1:27] 画面: 接続直後のデフォルト表示は Resources タブ。中央に3パネル=「Resources([List Resources]/[Clear])」「Resource Templates([List Templates]/[Clear])」「Select a resource or template(Select a resource or template from the list to view its contents)」。左ペインのボタンは [Restart]/[Disconnect] に変わり「● Connected」、History には「1. initialize ▸」が積まれている。

[1:40] Tools をクリックしたら、List Tools をクリックします。

[1:47] すると、さっき組み上げたツールの名前が表示されます。

🖥 [1:19〜1:55] 画面: Inspector・接続後の Tools 画面

上部メニュー: Resources | Prompts | [Tools] | Ping | Sampling | Roots
左ペイン: Restart / Disconnect | ● Connected

Tools
  [List Tools]  [Clear]
  read_doc_contents   Read the contents of a document and return it as a string.
  edit_document       Edit a document by replacing a string in the documents content
                      with a new string

右パネル: read_doc_contents
  Read the contents of a document and return it as a string.
  doc_id: [Id of the document to read]
  [🡒 Run Tool]

History: 2. tools/list ▸ / 1. initialize ▸
Server Notifications: No notifications yet

[1:50] 1つをクリックすると、右側のパネルが変わります。このパネルを使って自分のツールを手動で呼び出し、期待どおりに動いているかを確認できるのです。

[2:00] これが、実際のアプリケーションに配線することなく、MCP サーバーのライブ開発を行う方法です。

[2:07] read_doc_contents ツールを使うには、ドキュメント ID を入れるだけです。

[2:13] エディタに戻って、この docs 辞書まで上がっていくと、これらのドキュメント ID の1つをコピーできます。

🖥 [2:13] 画面: VS Code mcp_server.py——docs 辞書(deposition.md の値を確認しながらコピー)。ファイル先頭は from pydantic import Fieldfrom mcp.server.fastmcp import FastMCP、その下に両ツールの完成形。

[2:21] では deposition.md を取り出します。

[2:26] それを doc_id として入れて、Run Tool をクリックします。

[2:30] すると、Run Tool の Success と、ドキュメントの内容が表示されるはずです。

🖥 [2:26〜2:38] 画面: Inspector・read_doc_contents 実行結果

read_doc_contents
doc_id: [deposition.md]
[🡒 Run Tool]

Tool Result: Success
"This deposition covers the testimony of Angela Sm…"

History: 3. tools/call ▸ / 2. tools/list ▸ / 1. initialize ▸

[2:34] まさにそれです。検証できます。あそこ(エディタ)に見えているのと、まったく同じ文字列です。

[2:39] これとまったく同じテクニックで、もう1つのツールもテストできます。

[2:43] では、edit_document ツールに切り替えます。

🖥 [2:43] 画面: Inspector・edit_document の入力フォーム

edit_document
Edit a document by replacing a string in the documents content with a new string
doc_id:  [deposition.md]
old_str: [The text to replace. Must match exactly, including whitespace](プレースホルダ)
new_str: [The new text to insert in place of the old text](プレースホルダ)
[🡒 Run Tool]

[2:47] ここでドキュメント ID と、置き換えたい古い文字列を入れます。

[2:51] 「deposition」という単語を置き換えるのはどうでしょう? 実は、もっと打ちやすい単語があります。単に「This」ではどうでしょう? そのほうが少し楽です。

[2:58] というわけで、私の old string は「This」です。

[3:01] これは大文字・小文字が区別されることを覚えておいてください。そして「A report」に置き換えます。

[3:06] ツールを実行すると、Success が返ってきます。

🖥 [3:06] 画面: Inspector・edit_document 実行結果

edit_document
doc_id:  [deposition.md]
old_str: [This]
new_str: [A report]
[🡒 Run Tool]

Tool Result: Success

History: 4. tools/call ▸ / 3. tools/call ▸ / 2. tools/list ▸ / 1. initialize ▸

[3:10] このツールは、実際にはドキュメントの内容を返さないことを思い出してください。ドキュメントを編集するだけです。

[3:16] そこで、編集が正しく行われたことを確認するために、read_doc_contents ツールに戻り、同じドキュメント ID でもう一度実行します。すると「A report deposition」うんぬん、と表示されるはずです。

🖥 [3:16〜3:28] 画面: Inspector・read_doc_contents 再実行(write→read-back 検証)

read_doc_contents
doc_id: [deposition.md]
[🡒 Run Tool]

Tool Result: Success
"A report deposition covers the testimony of Angela Smith, P.E."

History: 5. tools/call ▸ …

[3:30] はい。というわけで、見てのとおり、この MCP インスペクターを使えば、実装中の MCP サーバーを、実際のアプリケーションに配線することなく、とても簡単にデバッグできます。

[3:41] 自分自身の MCP サーバーを作り始めると、このインスペクターツールをかなり使うことになると思います。このモジュールの中でも、私たちのサーバー開発が順調に進んでいることを確認するために、もう少し使うことになるでしょう。

英語逐語(ASR全文)

We have put together some functionality inside of our MCP server, but we have no idea if it works. So it'd be really great if we could test this out somehow. It turns out that by using this Python SDK, we automatically get access to a in-browser debugger. So we can make sure that this server is working as expected. Let me show you how to use it really quickly. Back inside my terminal, I'm going to make sure that I have my Python environment activated. Remember, the readme document goes into detail on the exact command to run to make sure that you have activated that environment. Once you are sure that it is activated, we'll run mcp, dev, and then the name of the file that contains our server. In this case, it is mcpserver.py. Once I run that, I'll then be told that I have a server listening on port 6277, and I'll be given a direct address to actually access it. I'm going to open up that address inside my browser. And once you go there, you'll see something that looks like this. This is the MCP inspector. Now, right away, there's something important I want you to understand here. This inspector is in active development. So by the time you are watching this video, what you see on the screen right now might be very, very different than what I am showing. Nonetheless, it's probably still going to have some very similar functionality. On the left-hand side, you'll see a connect button that is going to start up your MCP server. so that file that we just edited. I'm going to click on connect and then right away we'll see a couple of different things on the screen up here. I first want you to notice the top menu bar up here. It lists out resources, prompts, tools, and some other stuff. Again the UI might change by the time you watch this video so if you do not see this menu bar up here all we are really looking for here is some tools section. Once I click on tools I will click on list tools and And we'll see the name of the tools that we just put together. If I click on one, the right hand panel is then going to change and I can use this panel over here to manually invoke one of my tools to make sure that it is working as expected. So this is how we can do some live development on our MCP server without actually having to wire it up to a real application. In order to use the read.contents tool, all we have to do is put in a document ID. If I go back over to my editor and go up to the docs dictionary right here, I can copy one of these document IDs. So I will take out deposition.md. I will put it in as the doc ID and then click on run tool. I should then see run tool of success with the contents of the document. That is it right there. I can verify it. It's the same exact string as what I see right there. We can use this same exact technique to test out the other tool as well. So I will change over to the edit document tool. Now I'll put in my document ID, my old string that I want to replace. How about we replace the word deposition? Actually, I have an easier word to type out. How about just this? That'll be a little bit easier. So my old string is this. Remember that is going to be capital sensitive and I'm going to replace it with a report. And if I run the tool, I'll then be given a success. Remember that tool does not actually return the document's contents. It just edits the document. So now to verify the edit was done correctly, I can go back over to the redock contents tool, run that one again with the same document ID, and I should see a report deposition and then blah, blah, blah. All right. So as you can see, this MCP inspector allows us to very easily debug a MCP server that we are implementing without actually having to wire the server up to an actual application. As you start building your own NCP servers, I expect you'll be using this inspector tool quite a bit, and we'll probably use it a little bit more inside of this module just to make sure that our server development is going along pretty well.

(注:ASR の「mcpserver.py」=画面上の実体は mcp_server.py、「read.contents」「redock contents」=read_doc_contents、「NCP servers」=MCP servers、「capital sensitive」=case sensitive の意。)

↑ 目次へ戻る

Introduction to MCP | Lesson 07「Implementing a client」(全文ナレッジ)

動画 7分26秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • 今回からクライアント側の実装に移る。サーバー(mcp_server.py)が一段落したので、プロジェクトルートの mcp_client.py を作り込む。
  • 大前提の再確認:通常のプロジェクトでは「クライアントを利用する」か「サーバーを実装する」のどちらか一方。この講座のプロジェクトだけが、仕組みの両面を見せるために両方を実装している。
  • MCPClient クラスの正体:MCP Python SDK の ClientSession(サーバーへの実際の接続)をラップする自作クラス。セッションは終了時にリソースのクリーンアップが必要で、その面倒を見るためにクラス化している(connect / cleanup / __aenter__ / __aexit__)。ClientSession を生で使わず、リソース管理を担う大きめのクラスで包むのが一般的な実務パターン。
  • クライアントの存在理由:コードベースの各所が「Claude に渡すツール一覧が欲しい」「Claude が要求したツールを実行したい」となったとき、MCP サーバーへ到達する窓口が MCP クライアント。サーバーの機能をコードベースの残りに公開する層と捉える。
  • 今回実装するのは2関数だけ
  • list_tools()result = await self.session().list_tools() して result.tools を返す。
  • call_tool(tool_name, tool_input)return await self.session().call_tool(tool_name, tool_input)。ツール名と入力引数(Claude が生成)を渡すだけ。
  • テスト方法①(単体):ファイル末尾のテストハーネス(async with MCPClient(command="uv", args=["run", "mcp_server.py"]) as _client:)に result = await _client.list_tools()print(result) を書き、uv run mcp_client.py で実行。read_doc_contentsedit_document の Tool 定義(description・inputSchema 付き)が返る。
  • テスト方法②(E2E)list_tools / call_tool を呼ぶ側のコード(core ディレクトリ)は実装済みなので、uv run main.py で CLI を起動し「What is the contents of the report.pdf document?」と質問 → Claude がツール一覧から read_doc_contents を選んで実行 → 「20m の凝縮塔(condenser tower)に関するレポート」という回答が返り、ツール呼び出しの往復が成立したことを確認。

実務ポイント(このレッスンから持ち帰るもの)

  1. ClientSession は素で使わずラップするAsyncExitStack でトランスポートとセッションを積み、__aenter__/__aexit__connect/cleanup を対にする設計は、自作 MCP クライアントのそのまま使える雛形。
  2. クライアントの API はサーバー機能の鏡list_toolscall_toollist_promptsget_promptread_resource と、MCP のプリミティブ単位でメソッドを生やすと、アプリ側(エージェントループ)が MCP を意識せず使える。
  3. SDK が JSON-RPC を全部隠すsession.list_tools() / session.call_tool() の1行が、ListToolsRequest/Result・CallToolRequest/Result のメッセージ往復に対応する。プロトコルを手書きする必要はない。
  4. 動作確認は2段階が効率的:まず if __name__ == "__main__" のテストハーネスでクライアント単体を叩き、次に CLI(エージェントループ込み)で E2E 確認。
  5. stdio 接続はクライアントがサーバーを子プロセスとして起動するcommand="uv", args=["run", "mcp_server.py"])。uv を使わない場合は command="python" にして args から "run" を外す——という注意書きがコードコメントにある。

本文(時系列・日本語逐語+画面差し込み)

[0:00] サーバーが良い状態になったので、ここで少しギアを切り替えて、MCP クライアントの作業を始めます。クライアントは、プロジェクトのルートディレクトリにある mcp_client.py ファイルの中にあります。

🖥 [0:14] 画面(VS Code:mcp_client.py 冒頭): ```python import sys import asyncio from typing import Optional, Any from contextlib import AsyncExitStack from mcp import ClientSession, StdioServerParameters, types from mcp.client.stdio import stdio_client

class MCPClient: def init( self, command: str, args: list[str], env: Optional[dict] = None, ): self._command = command self._args = args self._env = env self._session: Optional[ClientSession] = None self._exit_stack: AsyncExitStack = AsyncExitStack() ```

[0:10] さて、このファイルの中で何かをする前に、ひとつ簡単な注意をしておきます。以前お話ししたことを思い出してください。通常の典型的なプロジェクトでは、私たちはクライアントを利用しているか、サーバーを実装しているかのどちらか一方です。両方をやっているのは、いま取り組んでいるこの特定のプロジェクトだけです。繰り返しますが、パズルの両面を見てもらうためです。

🖥 [0:30] 画面(スライド「Important Note!」): 「Normally a project will implement either an MCP client or an MCP server. Our project will implement both just so we understand how they work」。図:グレーの箱「Our Server」の内側に「MCP Client」、その右に枠だけの「MCP Server」、両者を双方向矢印で接続。

[0:28] さて、このファイルの中の MCP クライアント自体は、1つのクラスで構成されています。中にはたくさんのコードがあり、さっきサーバーの中で書いたコードほどきれいには見えないことに気づくでしょう。そこで、このファイルの中で何が起きているのか、なぜこんなに大きいのかを正確に説明します。

[0:44] このファイルの中では、MCPClient クラスを作っています。このクラスは「クライアントセッション」と呼ばれるものをラップします。クライアントセッションこそが、MCP サーバーへの実際の接続です。このクライアントセッションは MCP Python SDK の一部です。繰り返しますが、このセッションが外部のサーバーへの接続を与えてくれます。

🖥 [1:21] 画面(スライド): 枠「mcp_client.py」の中に2つの箱——「MCP Client」(注記: Custom class we are authoring to make using the session easier =セッションを使いやすくするために自作しているクラス)と「Client Session」(注記: Actual connection to the server =サーバーへの実際の接続)。Client Session から右の「MCP Server」へ線が伸びる。

[1:04] セッション自体には、少しばかりリソースのクリーンアップが必要です。つまり、プログラムを終了するときや、もうサーバーは要らないと判断したときには、ちょっとしたクリーンアップ処理を通す必要があります。そして私は、そのクリーンアップコードの多くをすでに MCPClient クラスの中に書いてあります。それこそが、このクラスがそもそも存在する理由です。クリーンアップを少し楽にするため、ただそれだけです。

[1:23] そのクリーンアップコードの一部は connect 関数の中に、また少し下の cleanup、async enter、async exit の各関数にも見えます。つまり、このクライアントセッションを直接使うのではなく、こうしたリソースまわりの管理をやってくれる、より大きなクラスにラップするのが、とても一般的な慣行なのです。

🖥 [1:38] 画面(VS Code:MCPClient の connect まわり): ```python class MCPClient: def init( self, command: str, args: list[str], env: Optional[dict] = None, ): self._command = command self._args = args self._env = env self._session: Optional[ClientSession] = None self._exit_stack: AsyncExitStack = AsyncExitStack()

async def connect(self):
    server_params = StdioServerParameters(
        command=self._command,
        args=self._args,
        env=self._env,
    )

```

📄 実物コード(cli_project_COMPLETE/mcp_client.py 25-45行・96-105行)——connect とクリーンアップ一式: ```python async def connect(self): server_params = StdioServerParameters( command=self._command, args=self._args, env=self._env, ) stdio_transport = await self._exit_stack.enter_async_context( stdio_client(server_params) ) _stdio, _write = stdio_transport self._session = await self._exit_stack.enter_async_context( ClientSession(_stdio, _write) ) await self._session.initialize()

def session(self) -> ClientSession: if self._session is None: raise ConnectionError( "Client session not initialized or cache not populated. Call connect_to_server first." ) return self._session python async def cleanup(self): await self._exit_stack.aclose() self._session = None

async def aenter(self): await self.connect() return self

async def aexit(self, exc_type, exc_val, exc_tb): await self.cleanup() ```

[1:46] 次に明確にしたいのは、そもそもこのクライアントがなぜ存在するのか、です。言い換えれば、クライアントはここで実際に何をしてくれているのか? 少し前に見たこのフローを思い出してください。ここに私たちのコードがありました。そしてある時点で、Claude に送るためのツールの一覧が必要でした。さらにその後、Claude からリクエストされたツールの実行も必要でした。MCP サーバーに手を伸ばしてこのツール一覧を取得したり、ツールを実行したりする——そこで MCP クライアントを使っているのです。つまりこのクライアントは、サーバーに属する機能を、コードベースの残りの部分へ公開しているのだとイメージできます。

🖥 [1:54] 画面(スライド:シーケンス図): レーンは「User / Our Code / MCP Client / MCP Server / Claude / Github」。流れ= User「What repositories do I have?」→ Our Code「I need a list of tools to send to Claude」→ MCP Client ListToolsRequest → MCP Server、ListToolsResult が返り「Here are the tools」→ Our Code から Claude へ Query + Tools → Claude ToolUse → Our Code「Please run this tool with these args」→ MCP Client CallToolRequest → MCP Server「Request to Github」→ Github「Response」→ CallToolResult「Here's the result of running the tool」→ Our Code toolResult → Claude → 「Your repositories are...」→ User。

[2:22] コードベースの中、このプロジェクトの中、具体的には core ディレクトリの中には、このクラスを利用する、私が組み立て済みのコードがすでにたくさん入っています。つまり、ここに見える list_tools、call_tool、list_prompts、get_prompt といったさまざまな関数を呼び出す別のコードが存在するわけです。

🖥 [2:23] 画面(VS Code:mcp_client.py の未実装スタブ): ```python async def list_tools(self) -> list[types.Tool]: # TODO: Return a list of tools defined by the MCP server return []

async def call_tool( self, tool_name: str, tool_input: dict ) -> types.CallToolResult | None: # TODO: Call a particular tool and return the result return None

async def list_prompts(self) -> list[types.Prompt]: # TODO: Return a list of prompts defined by the MCP server return []

async def get_prompt(self, prompt_name, args: dict[str, str]): # TODO: Get a particular prompt defined by the MCP server return [] ```

[2:40] 今回のこの動画では、list_tools と call_tool の2つの関数の実装に集中します。さっき見たダイアグラムのとおり、この2つの関数はコードベースのいろいろな場所で使われ、Claude に提供するツールの一覧を取得し、Claude がツールの呼び出しを要求したら実際にツールを呼び出します。

🖥 [2:57] 画面(スライド:シーケンス図+赤い注釈2つ): 上の枠(ListToolsRequest/Result〜Query + Tools)に「Our CLI code is using the client to get a list of tools to pass to Claude」、下の枠(CallToolRequest/Result〜toolResult)に「Our CLI code is using the client to call a tool」。

[2:56] この2つの関数の実装は、本当にシンプルで簡単です。どうやるかお見せしましょう。まず list_tools から始めます。中の TODO を削除して、result = await self.session()——これを関数のように呼び出します——.list_tools() に置き換えます。そして result.tools を返します。以上です。これはセッション、つまり MCP サーバーへの実際の接続にアクセスし、組み込みの関数を呼び出して、そのサーバーが実装しているすべてのツールの定義(一覧)を取得します。result を受け取ったら、tools を返すだけ。それで終わりです。

🖥 [3:30] 画面(VS Code:list_tools 実装を入力中): python async def list_tools(self) -> list[types.Tool]: result = await self.session().list_to| # ← 入力途中

[3:33] 次に、そのすぐ下に call_tool を、とてもよく似たやり方で実装できます。これは return await self.session().call_tool(tool_name, tool_input) です。もう一度、セッション——つまりサーバーへの接続——にアクセスし、特定のツールを呼び出そうとします。ツールの名前が引数で渡され、あわせて Claude が提供した入力パラメータ(入力引数)も渡されます。

🖥 [4:00] 画面(VS Code:list_tools 完成、call_tool 入力中): ```python async def list_tools(self) -> list[types.Tool]: result = await self.session().list_tools() return result.tools

async def call_tool( self, tool_name: str, tool_input: dict ) -> types.CallToolResult | None: return await self.session().call_| # ← 入力途中 ```

📄 実物コード(cli_project_COMPLETE/mcp_client.py 69-76行)——完成形: ```python async def list_tools(self) -> list[types.Tool]: result = await self.session().list_tools() return result.tools

async def call_tool( self, tool_name: str, tool_input ) -> types.CallToolResult | None: return await self.session().call_tool(tool_name, tool_input) ```

[4:02] さて、この時点でこの2つの関数をさっとテストしておきたいと思います。そのために、このファイルの一番下へ移動します。そこに、ごく小さなテストハーネスを用意しておきました。ここに、このテスト用ブロックがあるのが分かります。この mcp_client.py ファイルは直接実行できます。実行すると、MCP サーバーへの接続を形成し、そのサーバーに対していくつかコマンドを実行して、何が返ってくるかを見られます。なお、あなたの手元のコードには、UV を使っていない場合に command と args をここで変更することについてのコメントが入っています。UV を使っていない場合は、必ずそのコメントを確認してください。

[4:37] この with ブロックの中に、テストコードを少し追加します。result = await _client.list_tools() と書きます。そして、返ってきた result をそのまま print します。これで、MCP サーバーのコピーが起動し、そのサーバーが定義しているすべてのツールの一覧を取得しようとし、その結果をただ表示するはずです。

🖥 [5:00] 画面(VS Code:ファイル末尾のテストハーネス): ```python

For testing

async def main(): async with MCPClient( command="uv", args=["run", "mcp_server.py"] ) as _client: result = await | # ← 入力途中

if name == "main": if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) asyncio.run(main()) ```

📄 実物コード(cli_project_COMPLETE/mcp_client.py 108-121行)——テストハーネスの完成形(コメントに uv 非使用時の変更指示): ```python

For testing

async def main(): async with MCPClient( # If using Python without UV, update command to 'python' and remove "run" from args. command="uv", args=["run", "mcp_server.py"], ) as _client: pass

if name == "main": if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) asyncio.run(main()) ```

[5:03] これをテストするために、ターミナルへ戻って uv run mcp_client.py を実行します。いつもどおり、UV を使っていない場合は python mcp_client.py とするだけです。

[5:17] よし、実行します。そして——これがツール定義の一覧です。この中に、少し前に一緒に作った read_doc_contents ツールと、edit_document ツールがあるのが見えます。それぞれに description と inputSchema も付いています。これが、最終的に Claude へ渡されることになるツール定義です。

🖥 [5:18] 画面(ターミナル):
```text
(mcp) → mcp uv run mcp_client.py
[Tool(name='read_doc_contents', description='Read the contents of a document
and return it as a string.', inputSchema={'properties': {'doc_id': {'descri
ption': 'Id of the document to read', 'title': 'Doc Id', 'type': 'string'}},
'required': ['doc_id'], 'title': 'read_documentArguments', 'type': 'object'
}), Tool(name='edit_document', description='Edit a document by replacing a s
tring in the documents content with a new string', inputSchema={'properties'
{'doc_id': {'description': 'Id of the document that will be edited', 'titl e': 'Doc Id', 'type': 'string'}, 'old_str': {'description': 'The text to rep lace. Must match exactly, including whitespace', 'title': 'Old Str', 'type': 'string'}, 'new_str': {'description': 'The new text to insert in place of t he old text', 'title': 'New Str', 'type': 'string'}}, 'required': ['doc_id', 'old_str', 'new_str'], 'title': 'edit_documentArguments', 'type': 'object'} )] ```

[5:37] 先へ進む前に、もうひとつテストしたいことがあります。思い出してください。いま実装したのは、ツールを一覧化して Claude に渡せるようにする関数と、MCP サーバーが実装するツールを呼び出してその結果を Claude に渡せるようにする関数です。list_tools と call_tool を呼び出すコードは、このプロジェクトの別の場所に、私がすでに実装してあります。この機能を追加した今——これらのツールと、特定のツールを呼び出す能力を定義した今——CLI をもう一度実行して、Claude にこれらのツールを使わせてみることができます。つまり、Claude に特定のドキュメントの中身を調べさせたり、さらにはドキュメントを編集させたりできるのです。そのやり方をお見せしましょう。

🖥 [5:57] 画面(スライド:赤い注釈付きシーケンス図・再掲): 「Our CLI code is using the client to get a list of tools to pass to Claude」「Our CLI code is using the client to call a tool」——クライアントを呼ぶ側(CLI 本体)はすでに書かれていることの説明。

[6:19] 私の MCP サーバーの中に、ID が report.pdf のドキュメントがあり、「20m の凝縮塔(20m condenser tower)」のようなテキストが入っている、ということを思い出しておいてください。

🖥 [6:22] 画面(VS Code:mcp_server.py): ```python from pydantic import Field from mcp.server.fastmcp import FastMCP

mcp = FastMCP("DocumentMCP", log_level="ERROR")

docs = { "deposition.md": "This deposition covers the testimony of Angela Smith, P.E.", "report.pdf": "The report details the state of a 20m condenser tower.", "financials.docx": "These financials outline the project's budget and expenditu...", "outlook.pdf": "This document presents the projected future performance of the ...", "plan.md": "The plan outlines the steps for the project's implementation.", "spec.txt": "These specifications define the technical requirements for the equ...", } ```

📄 実物コード(cli_project_COMPLETE/mcp_server.py 6-13行)——docs 辞書(インメモリの擬似ドキュメントDB): python docs = { "deposition.md": "This deposition covers the testimony of Angela Smith, P.E.", "report.pdf": "The report details the state of a 20m condenser tower.", "financials.docx": "These financials outline the project's budget and expenditures.", "outlook.pdf": "This document presents the projected future performance of the system.", "plan.md": "The plan outlines the steps for the project's implementation.", "spec.txt": "These specifications define the technical requirements for the equipment.", }

[6:31] ターミナルへ戻り、uv run main.py でプロジェクトを実行します。そして Claude に「What is the contents of the report.pdf document?(report.pdf ドキュメントの中身は何?)」と尋ねます。ここでは正確に report.pdf と入力するようにしてください。

[6:50] これを実行すると、リクエストと一緒に、私たちのツール一覧が送信されます。Claude は read ドキュメントツールを使うことを決め、そのドキュメントの内容を取得します。そして——はい、Claude はそのドキュメントの内容を取得できました。レポートは 20m の凝縮塔に関する何からしい、と教えてくれます。

🖥 [7:02] 画面(ターミナル:CLI での E2E テスト): ```text (mcp) → mcp uv run main.py

What is the contents of the report.pdf document? I'll check the contents of the report.pdf document for you.

Response: The report.pdf document contains information about the state of a 20m conden ser tower.

``` ([7:08] では回答行全体がハイライト表示され、ツール経由で取得した内容であることを強調)

[7:08] さて、この時点で、クライアント周りの機能を追加できました。覚えておいてください。クライアントとは、MCP サーバーの中に実装されている機能へアクセスできるようにしてくれるものです。現時点で私たちは、サーバーが作成するツールの一覧を取得すること、そして、サーバーが実装したツールを実行することができるようになりました。

英語逐語(ASR全文)

Now that our server is in a good place, we're going to shift gears a little bit and start working on our MCP client. The client can be found inside the MCP client, the .py file inside the root project directory. Now, before we do anything inside this file, I just want to give you a very quick reminder here. Remember what I told you about earlier. Usually in a typical project, we are either making use of a client or we are implementing a server. It's just in this one particular project that we are working on that we are doing both. Again, just so you can see both sides of the puzzle. Now the MCP client itself inside of this file is consisting of a single class. You'll notice there's a lot of code inside of here, and it doesn't look quite as pretty as some of the code we just wrote out inside of the server. So let me tell you exactly what's going on inside this file and exactly why it is so large. Okay, so inside this file, we are making the MCP client class. This class is going to wrap up something called a client session. The client session is the actual connection to our MCP server. This client session is a part of the MCP Python SDK. So again, this session is what gives us this connection to the outside server. The session itself requires a little bit of resource cleanup. In other words, whenever we close down our program or decide that we don't need the server anymore, we have to go through a little bit of a cleanup process. And I have already written out a lot of that cleanup code inside of the MCP client class. So that's really why this class exists at all. Just to make that cleanup a little bit easier. You can see some of that cleanup code inside the connect function and down a little bit lower at the cleanup, async enter, and async exit functions as well. So it's very common practice to not just make use of this client session directly. Instead, very common to wrap it up inside of a larger class that's going to manage some of this different resource stuff for you. The next thing I want to clarify is why this client exists at all. So in other words, what is the client really doing for us here? Well, remember this flow that we looked at a little bit ago. So we had our code right here. And at certain points in time, we needed say a list of tools to send off to Claude. And then later on after that, we also needed to run a tool that was requested by Claude. In order to reach out to our MCP server and get this list of tools or to run a tool, that's where we are making use of the MCP client. So we can imagine that this client is exposing some functionality that belongs to the server to the rest of our code base. So inside of our code base, inside this project, specifically inside of the core directory, there is a lot of code already inside there that I put together that is making use of this class. So there's some other code that's going to call some of the different functions you see inside of here, like list tools, call tool, list prompts, get prompt, and so on. For right now, in this video, we're going to focus on implementing two functions, list tools and call tool. So as you just saw in the diagram we looked at a moment ago, these two functions are going to be used in different parts of our code base to get a list of tools to provide off to Claude and eventually call a tool whenever Claude requests to call a tool. Implementing these two functions is going to be really simple and straightforward. So let me show you how we're going to do it. We'll first begin with list tools. I'm going to remove the to do inside there and replace it with result is await self.session. I'm going to call that like a function. List underscore tools. And then I will return result.tools. And that's it. So this is going to get access to our session, which is our actual connection to the MCP server. It's going to call a built-in function to get a definition or a list of all the different tools that are implemented by that server. I'm going to get back result and then just return the tools and that's it. Then we can implement call tool right here in a very similar fashion. So this will be return await self dot session, call tool, tool name, and tool input. Once again, getting access to the session, that is our connection to the server, and I'm going to attempt to call a very specific tool, the name of the tool will be passed in along with the input parameters or input arguments to it that were provided by Claude. Now at this point in time, I would like to test out these two functions really quickly. To do so, we're going to go down to the bottom of this file where I put together a very small testing harness for us. So down here, you'll notice I put together this testing block. So we can run this mcp client.py file directly. And if we do so, we're going to form a connection to our mcp server, and then we can just run some commands against it and just see what we get back. Notice that in your version of the code, there's a comment in there about changing the command and args right here in case you are not making use of UV. So if you're not using UV, make sure you take a look at that comment. Inside of this with block, I'm going to add in a little bit of testing code. So I'll say result is await underscore client list tools. And then I'm going to just print out the result that we get back. So this should start up a copy of our MCP server, then attempt to get a list of all the different tools that are defined by it. And then just print out the result to test this out. I will flip back over to my terminal and do a UV run MCP underscore client.py. And as usual, if you are not making use of UV, you'll just do a Python MCP client.py. Okay. So I'll run that. And there is our list of tool definitions. So I can see inside of here that I have the read doc contents tool, which we put together a little bit ago and our edit document tool as well. Each one has a description and a input schema as well. So this is our tool definition, which will eventually be passed off to Claude. Now, before we move on, there's one other thing I want to test. Remember, we just implemented the function that's going to allow us to list out some tools and pass them off to Claude and the function that's going to allow us to call a tool that is implemented by the MCP server and then pass the result off to Claude as well. I have already implemented the code that is going to call list tools and call tool for us somewhere else inside this project. So now that we have added in this functionality, now that we have defined these tools and the ability to call a particular tool, we can now run our CLI again and attempt to get Claude to make use of these tools. In other words, we can ask Claude to inspect the contents of some particular document and even edit a document. So let me show you how we do that. Inside of my MCP server, I just want to give you a reminder that there is a document with a ID of report.pdf and it has some text here of something like a 20 meter condenser tower. I'm going to go back over to my terminal and I'm going to run my project with a uvrun main.py. And then I'm going to ask Claude what is the contents of the report.pdf document. And make sure you put in exactly report.pdf here. And when we run this, we're sending off along with the request, our list of tools. Claude is going to decide to use the read document tool and it's going to get the contents of the document. And then we will see that, yes, Claude was able to get the contents of that document. We are told that the report is something about a 20 meter condenser tower. All right, so at this point, we have added in some functionality around our client. Remember, the client is what allows us to access some functionality that is implemented inside of the MCP server. At this point in time, we have been able to list out some tools that are created by the server and execute a tool that has been implemented by the server.

↑ 目次へ戻る

Introduction to MCP | Lesson 08「Defining resources」(全文ナレッジ)

動画 9分45秒(コース最長) / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • 今回のテーマは MCP サーバーの次の主要機能「リソース(Resources)」。題材となる新機能=@メンション——ユーザーが「@ドキュメント名」と打つと、①「@」入力時に選択可能なドキュメント一覧をオートコンプリート表示し、②送信時にそのドキュメントの中身を自動取得してプロンプトに注入する。
  • 狙い:Claude にツールを使わせて report.pdf の中身を調べさせるのではなく、ユーザーが先回りしてファイルをメンションし、コンテキストを事前注入する。プロンプトは <query>質問</query> +「The user may have referenced a document. Here is the contents of the document: <document id="report.pdf">…</document>」の形に組み立てる。
  • リソースとは:MCP サーバーがクライアントにデータを公開するための仕組み。HTTP サーバーの GET リクエストハンドラに相当。返せるデータは文字列・JSON・バイナリなど何でもよく、mime_type でクライアントに「どんなデータか」のヒントを与える。
  • 設計指針:読み取り操作1つにつきリソース1つ。今回は「ドキュメント一覧の取得」と「特定ドキュメントの中身の取得」という2つの読み取りがあるので、リソースを2つ定義する。
  • アクセスフロー:自分のコード → MCP クライアント → ReadResourceRequest(URI を含む) → MCP サーバーが URI に合致する関数を実行 → ReadResourceResult で返す。URI(例 docs://documents)はリソース定義時に決める「リソースの住所」で、ルートハンドラに相当。
  • リソースは2種類
  • ダイレクト(direct/static とも呼ぶ):URI が固定。例 @mcp.resource("docs://documents", mime_type="application/json")def list_docs() -> list[str]: return list(docs.keys())
  • テンプレート(templated):URI にパラメータ(ワイルドカード)を含む。例 @mcp.resource("docs://documents/{doc_id}", mime_type="text/plain")def fetch_doc(doc_id: str) -> strURI 中の {doc_id} は Python SDK が自動でパースし、同名のキーワード引数として関数に渡される。パラメータを増やせば、その分キーワード引数が増える。
  • 戻り値の自動シリアライズ:list_docs は JSON 文字列を自分で作らず list を返すだけ——SDK が戻り値を自動で文字列化してくれる。
  • 実務ノート:実アプリの「ドキュメント読み取り」なら、本文だけでなく ID・本文・著者名・著者 ID などを含むドキュメントレコード全体(辞書)を返すのが普通。今回は plain text の返し方を見せるために本文のみ返す。
  • Inspector での検証uv run mcp dev mcp_server.py → ポート 6274 → Connect → Resources タブ。「List Resources」にはダイレクトリソースのみ(docs://documents)が並び、テンプレートは別枠「List Templates」(fetch_doc)に出る。docs://documents の結果は text プロパティに JSON 文字列として入っており(mimeType: application/json)、デシリアライズはクライアント側の責務。fetch_doc は doc_id に report.pdf を入れて読むと text/plain のヒント付きで本文が返る=JSON パース不要のサイン。

実務ポイント(このレッスンから持ち帰るもの)

  1. 「ツールに探させる」より「リソースで事前注入」。ユーザーが対象を知っているなら、@メンション+リソース読み取りでコンテキストを先に埋め込む方が、ツール往復1回分速く、確実。(Claude Code の @ファイル名 や IDE の @メンションと同じ設計思想)
  2. リソース設計の型:読み取り操作ごとに1リソース。「一覧=ダイレクト」「個別取得=テンプレート」の2点セットは、ほぼすべての CRUD 的 MCP サーバーで再利用できる定石。
  3. mime_type は「クライアントへの契約」。application/json なら受け手がパースする、text/plain ならそのまま使う——受信側の分岐処理(Lesson 09 の read_resource)とセットで初めて機能する。
  4. URI テンプレートのパラメータ名=関数の引数名{doc_id}def fetch_doc(doc_id))。名前を揃えないと SDK が引数を渡せない。
  5. テンプレートリソースは Inspector の「Resources」一覧に出ない(Templates 側に出る)。「定義したのに一覧に見えない」と焦らないこと。
  6. 戻り値の JSON 化は SDK 任せでよいが、返ってくるのはあくまで「JSON を含む文字列」。使う側での json.loads を忘れない。

本文(時系列・日本語逐語+画面差し込み)

[0:00] この動画では、MCP サーバーの次の主要機能であるリソースに進みます。リソースを理解してもらうために、プロジェクトにもうひとつ機能を実装していきます。追加するのはこれです。ユーザーがアットマーク(@)+ドキュメント名を入力することで、ドキュメントを「メンション」できるようにしたいのです。ユーザーがそうしたら、そのドキュメントの内容を自動的に取得して、Claude に送るプロンプトへ挿入したい。

[0:17] つまり全体として、この機能には2つの側面があることになります。ユーザーがメッセージの中で @ 記号を打ったら、メンションできるすべてのドキュメントの一覧を、小さなオートコンプリートウィンドウに自動表示します。次に、ユーザーがメンションを含むメッセージを送信したら、そのドキュメントの内容を自動的に取得して、Claude に送るプロンプトへ挿入します。

🖥 [0:09] 画面(スライド「Next Feature」): 箇条書き「Users can "mention" a document by writing out "@doc_name"/Typing "@" should show a list of all the available documents/When a document is mentioned, its contents should be automatically injected into the prompt」。右にターミナルのモック:> Can you please summarize the contents of @ の下にオートコンプリート(deposition.md / design.md / financials.md / outlook.md / plan.md / spec.md、各行に「Resource」ラベル)。

[0:43] たとえば、ユーザーが「what's in the @report.pdf file(@report.pdf ファイルには何が入ってる?)」のように言ったら、私はこんなプロンプトを組み立てて Claude に送りたいのです。中にはユーザーからのクエリが入ります。さらに Claude に対して、「ユーザーはあるドキュメントを参照した可能性がある。これがそのドキュメントの内容だ」と伝えます。

🖥 [0:37] 画面(スライド:プロンプト組み立て図): ターミナル > What's in the @report.pdf file? → 「Our Code」→ 黒いプロンプトボックス → 「Claude」。プロンプトボックスの中身: ```text Answer the users query: What's in the @report.pdf file?

The user may have referenced a document. Here is the content of the document: ...condition of a 20m condenser tower... ```

[1:01] ここでのアプローチ(考え方)はこうです。report.pdf ファイルの中身が何かを把握するために、Claude が何かのツールを使いに行くことに頼らなくて済むようにする。その代わり、ユーザーが先回りしてファイルをメンションすれば、私たちが自動的に、事前にコンテキストを挿入しておくのです。

[1:17] ここでひとつ明確にしておきたいのは、私たちは実質的に2つの別々の機能の話をしている、ということです。1つ目の機能は、ユーザーが @ 記号を打つたびに、MCP サーバーから「ユーザーがメンションし得るすべてのドキュメントの一覧」をもらう必要がある、というもの。そして2つ目の側面は、ユーザーがメンションを含むメッセージを送信したら、MCP サーバーから「単一ドキュメントの内容」をもらう必要がある、というものです。

🖥 [1:20] 画面(スライド:2段の対応図): 上段「When a user types an '@'...」→ オートコンプリートのモック → 「...We need the MCP Server to give us a list of documents」。下段「When a message contains a document mention...」→ 組み立て済みプロンプトのモック → 「...We need the MCP Server to give us the contents of a single document」。

[1:41] この情報を MCP サーバーから取り出すために、私たちはリソースを使います。リソースは、MCP サーバーがある程度のデータをクライアントに公開できるようにするものです。通常、個別の読み取り操作1つごとに1つのリソースを定義します。今回の例では、ドキュメントの一覧の取得と、単一ドキュメントの内容の読み取りが必要です。ですから、おそらく2つの別々のリソースを作ることになります。1つのリソースは、オートコンプリートに入れられるように、ドキュメント名の一覧だけを返す役割。そしてもう1つ、ドキュメント ID に基づいて単一ドキュメントの内容を公開するリソースを作ることになるでしょう。

🖥 [1:44] 画面(スライド「Resources」): 箇条書き「Allow the MCP Server to expose data to the client/Similar to GET request handlers in a typical HTTP server/Can return any type of data - strings, JSON, binary, etc./We set the 'mime_type' to give the client a hint as to what data we are returning/Two types: direct and templated」。右に「MCP Server」枠と2つの Resource コード例: python @mcp.resource( "docs://documents", mime_type="application/json" ) def list_docs(): # Return a list of document names python @mcp.resource( "docs://documents/{doc_id}", mime_type="text/plain" ) def fetch_doc(doc_id: str): # Return the contents of a doc

[2:13] これらのリソースを定義すると、それらは MCP クライアント経由でアクセスされることになります。最終的に組み上げるフロー全体はこうです。ユーザーが「what's in the @」のような何かを打ち込むと——おそらくそこに何かを入れていくわけですが——その @ の文字を打った瞬間に、オートコンプリートへ入れるためのドキュメント名一覧を表示する必要があります。そこで私たちのコードは MCP クライアントに手を伸ばし、クライアントは read resource リクエストを MCP サーバーへ送ります。その read resource リクエストの中に、URI と呼ばれるものを含めます。それは要するに、読みたいリソースの住所です。この URI は、最初にリソースを組み立てるときに定義されます。つまり URI とは、そこにあるあれのことです。

[2:58] この read resource リクエストを送ると、MCP サーバーは、私たちがこの中に入れた URI を正確に見て、そこで組み立てた関数を実行し、結果を受け取って、read resource result メッセージの中に入れて私たちに送り返します。そうしたら、その中のデータを取り出してオートコンプリートに表示したり、他に必要なことに使ったりできます。

🖥 [2:17〜3:12] 画面(スライド:シーケンス図): レーン「User / Our Code / MCP Client / MCP Server」。User「What's in the @...」→ Our Code「I need a list of document names to put in the autocomplete」→ MCP Client ReadResourceRequest(ボックス内に docs://documents)→ MCP Server。折り返しは ReadResourceResultList of doc names)→「Great! I'll put these doc names into the autocomplete」。

[3:23] リソースには2つの異なる種類があります。ダイレクトテンプレートです。ダイレクトリソースは、スタティック(静的)リソースと呼ばれることもあります。ダイレクトリソースは静的な URI を持つだけです。つまり常に完全に同じもの、たとえば docs://documents のようなものです。テンプレートリソースは、URI の中に1つ以上のパラメータを持ちます。たとえば documents/ の後ろに、ある種のワイルドカードを置くことができます。そこには好きなドキュメント ID を入れられます。そして、このリソースを要求すると、URI 内のそのドキュメント ID は Python MCP SDK によって自動的にパースされ、キーワード引数として関数に渡されます。キーワード引数は、そこに書いた文字列と完全に同じ名前になります。つまり、そこの doc ID は、あそこの doc ID になるわけです。

[4:09] ご想像のとおり、MCP サーバーに対して「何を要求するか」にもう少し選択の幅・多様性・カスタマイズ性を持たせたいときにはいつでも、テンプレートリソースを使うことになります。

🖥 [3:23] 画面(スライド:Direct vs Templated 対比): 左「Direct Resource — URI doesn't contain any params.」+ python @mcp.resource( "docs://documents", # URI mime_type="application/json" ) def list_docs(): # Return a list of document names 右「Templated Resource — URI contains one or more params. The Python SDK parses these and passes them as args to your function.」+ python @mcp.resource( "docs://documents/{doc_id}", # URI mime_type="text/plain" ) def fetch_doc(doc_id: str): # Return the contents of a doc

[4:20] リソースの実装はかなり簡単です。エディタに戻って、さっそくサーバーにリソースを追加していきましょう。

[4:27] さて、エディタの中に戻ってきました。mcp_server.py ファイルを探します。少し下へスクロールして、コメントを探します——「すべてのドキュメント ID を返すリソースを書く」と「特定のドキュメントの内容を返すリソースを書く」というコメントです。

[4:42] まずこの1つ目について。私はコメントに「document IDs」と書きました。思い出してください。私たちにとって、ドキュメント ID は実質的にドキュメントの名前です。ですから私たちは、実際にはこれらの ID を返すだけです。それが名前の役割を果たします。つまり、それらをそのままオートコンプリート要素に入れられる、ということです。

[4:58] では、リソースを作りましょう。その TODO を削除して、mcp.resource を追加します。第1引数は、これにアクセスするための URI です。繰り返しますが、これはルートハンドラのようなもの(それに相当するもの)です。ここでは docs、コロン、スラッシュ、スラッシュ、documents を使います。そして mime type に application/json も追加します。

🖥 [5:00] 画面(VS Code:mcp_server.py 入力中): ```python @mcp.resource( "docs://d|" # ← URI 入力途中 )

TODO: Write a resource to return the contents of a particular doc

TODO: Write a prompt to rewrite a doc in markdown format

TODO: Write a prompt to summarize a doc

if name == "main": mcp.run(transport="stdio") ```

[5:22] リソースはどんな種類のデータでも返せます。プレーンテキストでもいいし、JSON でもいいし、バイナリデータでも、何でもいい。どんな種類のデータを返すのかについて、クライアントにヒントを与えるのは私たちの役目です。そのために、この MIME タイプを定義します。application/json という MIME タイプは、最終的にこのリソースを要求してくるクライアントに対する「構造化された JSON データを含む文字列を送り返しますよ」というヒントです。そして、そのデータをデシリアライズするのはクライアント側の仕事になります。要するに、そのデコレータの下では(戻り値を)使えるデータ構造へ変換しているわけです。

[5:56] 関数 list_docs を書き出します。文字列のリストを返すことにします。そして中では、list(docs.keys()) を返します。つまり、あの辞書からすべてのキーを取り出してリストに変換し、それを返すだけです。

🖥 [6:00] 画面(VS Code:1つ目のリソース完成): python @mcp.resource( "docs://documents", mime_type="application/json" ) def list_docs() -> list[str]: return list(docs.keys())

📄 実物コード(cli_project_COMPLETE/mcp_server.py 59-61行)——ダイレクト(直接)型リソースの完成形: python @mcp.resource("docs://documents", mime_type="application/json") def list_docs() -> list[str]: return list(docs.keys())

[6:12] ここで気づいてほしいのですが、私たちはここで明示的な JSON を返してはいません。言い換えると、実際には文字列を返していないのです。MCP Python SDK が、私たちの返したものを自動的に文字列へ変換してくれます

[6:25] よし、2つ目のリソースに取りかかりましょう。あのコメントを削除して、mcp.resource、docs://documents…… と置き換えます。ただし今回はテンプレートリソースにしたいので、ここにこのワイルドカード({doc_id})を入れます。そして今回の MIME タイプは、ちょっとした変化をつけるために、プレーンテキストを返すことにします。ドキュメントの中身そのものだけを返し、何の構造にも包まないからです。

[6:56] 参考までに言っておくと、実際のアプリケーションで「ドキュメントを読む」のようなものなら、おそらくドキュメントレコード全体を返すでしょう。つまり、ID、内容、著者名、著者 ID などを含む、なんらかの辞書です。ですがここでは例のために、ドキュメントのテキストだけを返して、プレーンテキストを普通はどう返すのかをお見せします。というわけでこのシナリオでは、私の MIME タイプは text/plain になります。そして fetch_doc を作ります。doc_id を受け取り——これは文字列です——文字列を返します。

🖥 [7:00] 画面(VS Code:2つ目のリソース入力中): python @mcp.resource( "docs://documents/{doc_id}", mime_type="|" # ← 入力途中 )

[7:27] もう一度言いますが、そこ(URI)に置いた単語が何であれ、それがあなたの関数の中にキーワード引数として現れます。もし、この中に追加のパラメータ——たとえば doc_type とか、そういうもの——を加えたら、それはこのように、追加のキーワード引数として現れるだけです。

🖥 [7:38] 画面(VS Code:追加パラメータのデモ): URI に実際にもう1つワイルドカードを打って見せる——"docs://documents/{doc_id}/{doc_typ|}"(入力途中)。直後に削除して {doc_id} のみに戻す(パラメータを足せばキーワード引数が増える、の実演)。

[7:43] それからこの中では、まず、この人が要求している ID が実際に存在することを確認します。if doc_id not in docs なら、f 文字列で「doc with ID not found」という ValueError を送出します。そのチェックを通過したら、docs[doc_id] を返します。以上です。

🖥 [7:30] 画面(VS Code:fetch_doc の定義): python @mcp.resource( "docs://documents/{doc_id}", mime_type="text/plain" ) def fetch_doc(doc_id: str) -> str: | # ← 本体はこれから入力

📄 実物コード(cli_project_COMPLETE/mcp_server.py 64-68行)——テンプレート型リソースの完成形: python @mcp.resource("docs://documents/{doc_id}", mime_type="text/plain") def fetch_doc(doc_id: str) -> str: if doc_id not in docs: raise ValueError(f"Doc with id {doc_id} not found") return docs[doc_id]

[8:06] では、この内容を、もう一度 MCP インスペクターの中でテストしてみましょう。思い出してください。ターミナルで uv run mcp dev mcp_server.py というコマンドを実行できます。これが Web サーバーを、ポート 6277——いや失礼、デフォルトでは 6274 で起動します。それをブラウザで開くようにします。

🖥 [8:18] 画面(ターミナル:Inspector 起動ログ): uv run mcp dev mcp_server.py 実行後の出力——「Starting MCP inspector.../🔧 Proxy server listening on port 6277/🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀」。※6277=プロキシサーバ、6274=Inspector 本体(UI)のポート——講師が言い直した2つの番号は両方実在する。

🖥 [8:30] 画面(ブラウザ:MCP Inspector v0.10.2 接続前): Transport Type = STDIO / Command = uv / Arguments = run --with mcp mcp run mcp_serv… /「Connect」ボタン、ステータス「Disconnected」。中央に「Connect to an MCP server to start inspecting」。

[8:28] はい、来ました。Connect をクリックします。次に Resources(リソース)を見つけます。そして、利用可能なすべてのリソースを一覧表示できるはずです。ここで、リソースを一覧表示(List Resources)すると、これは特にスタティック(ダイレクト)リソースになります。だから docs://documents だけが見えます。それから、別枠でリソーステンプレートをすべて一覧表示(List Templates)できます。そこには fetch_doc というリソーステンプレートが1つあるのが見えます。

[8:51] まず、この /documents を実行してみます。何が返ってくるか見てみましょう。これが実際のメッセージ、MCP サーバーから返される正確な構造です。text プロパティを持っていることに気づくでしょう。その中に、私たちが返しているすべてのデータが JSON 文字列としてシリアライズされて入っています。つまり繰り返しになりますが、CLI アプリケーションの側で、この text を受け取り、この JSON 文字列から使える文字列リストへデシリアライズするのは、私たちの仕事になるということです。

🖥 [9:00] 画面(MCP Inspector・接続済み): 左ペイン「Resources:List Resources/Clear/docs://documents」「Resource Templates:List Templates/Clear/fetch_doc」。右ペインのタイトル docs://documents、内容: json { contents: [ 0: { uri: "docs://documents" mimeType: "application/json" text: "[\"deposition.md\", \"report.pdf\", \"financials.docx\", \"outlook.pdf\", \"plan.md\", \"spec.txt\"]" } ] } History には 4. resources/read / 3. resources/templates/list / 2. resources/list / 1. initialize。

[9:19] それから、fetch_doc もテストできます。クリックします。doc ID を入力しなければなりません。入れましょう。report.pdf ファイルを読みたいので。そしてリソースを読み取ります(Read Resource)。すると、その特定のドキュメントの内容が見えるはずです。そして今回もまた気づいてほしいのですが、今度は text/plain が返ってきています。これは「これはプレーンテキストであり、いかなる形でも JSON としてデシリアライズしようとすべきではない」という、私へのヒントなのです。

🖥 [9:30] 画面(MCP Inspector:fetch_doc の読み取り結果): 右ペインのタイトル docs://documents/report....、内容: json { contents: [ 0: { uri: "docs://documents/report.pdf" mimeType: "text/plain" text: "The report details the state of a 20m condenser tower." } ] } History には 6. resources/read / 5. completion/complete / 4. resources/read / 3. resources/templates/list / 2. resources/list。

英語逐語(ASR全文)

In this video, we're going to move on to the next major feature inside of MCP servers, which is resources. To help you understand resources, we're going to be implementing another feature inside of our project. Here's what we're going to add in. I want to allow a user to mention a document by putting in an at symbol and then the name of a document. Whenever they do so, I want to automatically fetch the contents of that document and insert it into the prompt that we send off to Claude. So in total, there's going to be kind of two aspects to this feature. Whenever a user types out the at symbol inside of a message, we're going to automatically show a list of all the different documents that they can mention inside of a little autocomplete window. Then whenever a user submits a message with a mention inside of it, we're going to automatically get the contents of that document and insert it into the prompt that we send off to Claude. So for example, if a user says something like what's in the at report.pdf file, I would want to assemble a prompt like this and send it to Claude. So we're going to have the query inside there from the user. And then we're also going to tell Claude that the user might have referenced some document and here is the contents of the document. So the approach here or the idea here is that we will not have to rely upon Claude to go and make use of some tool to figure out what is inside of the report.pdf file. Instead, the user can just preemptively mention the file and we're going to automatically insert some context ahead of time. Now, one thing I want to clarify here is that we're kind of talking about two separate features. The first feature is that whenever a user types in the at symbol, we really need the MCP server to give us a list of all the different documents that the user can possibly mention. And then the second aspect here is that whenever a user submits a message that contains a mention, then we need the MCP server to give us the contents of a single document. To get this information out of our MCP server, we are going to be making use of resources. Resources allow our MCP server to expose some amount of data to the client. We usually define one resource for each distinct read operation. So in our example, we need to get a list of documents and read the contents of a single document. So we would probably end up making two separate resources. One resource would be responsible for returning just a list of document names so we can put them inside the autocomplete. And then we would probably make another resource that will expose the contents of a single document based upon its document ID. When we define these resources, they're going to be accessed through our MCP client. So the entire flow that we're going to eventually put together here, whenever a user types in something like what's in the at, and then presumably they're going to put in something right there. As soon as they type in that at character, we need to display a list of document names to put in the autocomplete. So our code is going to reach out to the MCP client, which in turn is going to send a read resource request off to the MCP server. Inside of that read resource request, we're going to include something called the URI. That is essentially the address of the resource we want to read. This URI gets defined whenever we put together our resource initially. So the URI is that right there. When we send off this read resource request, the MCP server is going to look at the exact URI that we put inside of here and then run the function we put together right there, take the result, and send it back to us inside of a read resource result message. We can then take the data inside there and display it inside of our autocomplete or do whatever else we need to do with it. There are two different types of resources, direct and templated. You'll also sometimes see direct resources referred to as static resources. A direct resource just has a static URI, So it's always going to be the exact same thing, such as docs colon slash slash documents. A templated resource will have one or more parameters inside of its URI. So for example, we might have document slash and then kind of a wild card right here. So we can put in any document ID we want to. And whenever we ask for this resource, that document ID right there inside the URI will be automatically parsed by the Python NCP SDK and provided as a keyword argument to our function. The keyword argument will have the exact same name of whatever string you put in right there. So doc ID right there will be doc ID right there. As you can probably guess, we'll make use of templated resources anytime that we want to allow a little bit more selection or variety or customization in what someone is asking for out of our MCP server. Implementing resources is pretty straightforward. So let's go back over to our editor and we're going to add in some resources to our server right away. All right, so back over inside my editor, I will find the mcpserver.py file. I'm then going to scroll down a little bit, and I'm going to find some comments for writing resource to return all document IDs, and writing resource to return the contents of a particular document. Now, for this first one right here, I put in the comment document IDs. Remember, for us, our document IDs are essentially the name of the document. So for us, we're really just returning these IDs. They're going to serve the purpose of the name. That means we can put them directly into that autocomplete element. All right, so to make our resource, I'm going to delete that to-do, and then I'll add in a mcp.resource. The first argument is going to be the URI for accessing this thing. Again, it's kind of equivalent to a route handler. So I will use docs, colon, slash, slash, documents, and I'm also going to add in a mime type of application, slash, JSON. A resource can return any type of data. So it can be plain text. It can be JSON. It can be binary data, anything. It's up to us to kind of give our client a hint as to what kind of data we are returning. To do so, we're going to define this MIME type. A MIME type of application slash JSON is a hint to our client, who's eventually going to ask for this resource right here, that we're going to be sending back a string that contains some structured JSON data. And so it would be up to our client to deserialize that data. We're essentially turning into some usable data structure underneath that decorator. I'll write out my function of list docs, and I'm going to return a list of strings. And then inside there, I will return list docs keys. So just take all the keys out of that dictionary and turn it into a list. And I'm going to return it. Now you'll notice that we are not returning distinct JSON here. In other words, we're not actually returning a string. the mcp python sdk is going to automatically take whatever we return and turn it into a string for us all right let's take care of our second resource so i'm going to delete that comment and then replace it with mcp resource docs colon slash slash documents and then this time i want a templated resource because i'm putting in this wild card right here and then my mime type this time around, just for a little bit of variety, I'm going to be returning plain text because it's going to be just the contents of the document and I'm not going to wrap it up in any kind of structure. Now, just so you know, in a real application, something like read a document, I would probably return an entire document record. So some kind of dictionary that contains maybe the ID, the content, the author name, the author ID and stuff like that. But just for the sake of an example, I'm going to return just the text of the document to show you how we would normally return plain text. So in this scenario, my MIME type would be text plain, and then I will make fetch doc. I'm going to take doc ID, which is going to be a string, and I'm going to return a string. Once again, whatever word you put right there, it's going to show up as a keyword argument inside of your function. If we added in some additional parameters inside of here, such as maybe doc type or something like that, it would just show up as an additional keyword argument like so. Then inside of here, I'm going to first make sure that the ID that this person is asking for actually exists. So if doc ID not in docs, I'm going to raise a value error with a F string that says doc with ID not found. And then if we get past that check, I will return docs doc ID. And that's it. Now let's try testing this stuff out inside of our MCP inspector once again. So remember at our terminal, we can run the command uvrun mcpdev mcpserver.py. That's going to start up a web server at port 6277, or excuse me, 6274 as the default. So I'm going to make sure I open that up inside my browser. Here we go. I'll click connect. I'll then find resources. And then I should be able to list out all the different resources that are available. Now when I list out resources, this is going to be specifically static or direct resources. So I'll see only doc slash documents. And then I can separately list out all my different resource templates. And so I'll see that I have one resource template of fetch doc. I'm going to first try to run the slash documents right here. We'll see what we get back. So this is the actual message, the exact structure that gets returned from our MCP server. You'll notice that it has a text property. And inside there is all the data that we are returning serialized as JSON string. So again, it would be up to us inside of our CLI application to take this text right here and deserialize it from this JSON string into a usable list of strings. Then we can also test out fetch doc. So I'll click on that. I have to enter a doc ID. So I'm going to put it in. I want to read the report.pdf file. And I'll read the resource. And now I should see the contents of that particular document. and you'll notice this time around once again i get a text plain so that's a hint to me that this is plain text and i should not attempt to deserialize it from json in any way

↑ 目次へ戻る

Introduction to MCP | Lesson 09「Accessing resources」(全文ナレッジ)

動画 4分38秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • 前回サーバーに定義した2つのリソース(docs://documents / docs://documents/{doc_id})を、クライアント側から要求できるようにする回。追加するのは MCPClient の関数1つ= read_resource(uri) だけ。呼び出し側(オートコンプリート表示やプロンプト注入を行う CLI 本体)は実装済みなので、これで @メンション機能全体が動き出す。
  • read_resource の仕事:①URI を指定して MCP サーバーへ読み取りリクエストを送る → ②返ってきた内容を mimeType に応じてパース → ③データを返す。
  • 実装手順: 1. ファイル先頭に2つの import を追加——import jsonfrom pydantic import AnyUrl(どちらも型をきれいに通すため)。 2. result = await self.session().read_resource(AnyUrl(uri))——URI 文字列を AnyUrl に包むのも型合わせのため。 3. resource = result.contents[0]——Inspector で見たとおり、レスポンスは contents のリスト。関心があるのは先頭要素だけ。 4. isinstance(resource, types.TextResourceContents) かつ resource.mimeType == "application/json" なら json.loads(resource.text) を返す——サーバーが宣言した mime_type ヒントをここで消費する。 5. それ以外は resource.text をそのまま返す(=単一ドキュメントの中身のような plain text のケース)。
  • 動作確認(E2E)uv run main.py → 「what's in the @」と打つとリソース一覧のオートコンプリートが出現。矢印キーで選択、スペースで挿入 → 「Whats in the @report.pdf document?」を送信 → ドキュメント内容がプロンプトに注入済みなので即座に回答が返る。今回 Claude はツールを一切使っていない(Lesson 07 のツール経由の読み取りとの対比)。
  • リソースの結論:リソースとは、MCP サーバーからある程度の情報を公開するための仕組み。クライアント側は「URI で要求 → mimeType で分岐してパース」という対の実装で受ける。

実務ポイント(このレッスンから持ち帰るもの)

  1. read_resource は「mimeType ディスパッチャ」として書くapplication/jsonjson.loads、それ以外 → 生テキスト。サーバー側 mime_type 宣言(Lesson 08)とクライアント側分岐は必ずセットで設計する。
  2. result.contents[0] パターン:ReadResourceResult は contents 配列で返る。単一リソース読み取りでは先頭要素だけ見ればよい——Inspector で実レスポンス構造を確認してから書くと迷わない。
  3. AnyUrl(pydantic)で URI を包むのは Python SDK の型要件。文字列を直接渡さない。
  4. ツール vs リソースの使い分けが体感できる比較:同じ「report.pdf の中身は?」でも、Lesson 07 はモデルがツール呼び出しを決断(1往復増)、Lesson 09 はアプリが事前注入(即答)。「モデルに任せる=ツール」「アプリ/ユーザーが制御する=リソース」
  5. クライアントのメソッドは汎用に、UI 機能は呼び出し側に。read_resource 自体は @メンションを知らない。一覧取得(オートコンプリート)も本文取得(プロンプト注入)も同じ関数で賄う汎用設計。

本文(時系列・日本語逐語+画面差し込み)

[0:00] 私たちは MCP サーバーの中に、2つの別々のリソースを定義しました。ですから今度は、クライアントがそれらのリソースを要求する能力を必要とします。そのために、MCP クライアントの中に関数を1つ追加します。

[0:12] 思い出してください。この MCP クライアントには、私たちが組み立てている機能が入り、それはアプリケーションの残りの部分から使われることになります。そして、そのコードは私がすでに組み立ててあります。つまり、このプロジェクトのどこか別の場所で、いまから MCP クライアントに追加しようとしているこの関数を、何かが使おうとするわけです。

🖥 [0:29] 画面(スライド:前回のシーケンス図・再掲): 「User / Our Code / MCP Client / MCP Server」のレーン。User「What's in the @...」→ Our Code「I need a list of document names to put in the autocomplete」→ MCP Client ReadResourceRequestdocs://documents)→ MCP Server → ReadResourceResultList of doc names)→「Great! I'll put these doc names into the autocomplete」。

[0:28] 始めるにあたって、もう一度 MCP クライアントのファイルを開きます。下へスクロールして、ここにある read resource を見つけます。

[0:37] ここでの私たちのゴールは、MCP サーバーへリクエストを送って特定のリソースを読み取り、返ってきた内容を MIME タイプに応じてパースし、得られたデータをそのまま返すことです。引数のひとつが URI であることに気づくでしょう。これは、サーバーから取得したいリソースの URI になります。

🖥 [0:40] 画面(VS Code:mcp_client.py の実装前スタブ): 上に未実装の list_prompts / get_prompt(各 # TODO: ...return [])が並び、その下に今回の対象—— python async def read_resource(self, uri: str) -> Any: # TODO: Read a resource, parse the contents and return it return [] (下には実装済みの cleanup / aenter / aexit が見える)

[0:56] リクエストを送るために——というより、型をきれいに揃えるためだけに——ファイルの一番上に2つの import を追加します。JSON モジュールの import を追加し、pydantic から AnyUrl を import します。

🖥 [1:01] 画面(VS Code:ファイル先頭の import ブロック・追記中): python import sys import asyncio import json # ← いま追加 from pydan| # ← AnyUrl を import 中 from typing import Optional, Any from contextlib import AsyncExitStack from mcp import ClientSession, StdioServerParameters, types from mcp.client.stdio import stdio_client (後段の isinstance チェックで使う types は既存 import に含まれている)

📄 実物コード(cli_project_COMPLETE/mcp_client.py 8-9行)——追加した import: python import json from pydantic import AnyUrl

[1:18] それから read resource 関数まで戻ります。コメントと return 文を消します。次に、await self.session()——read_resource を呼びたいので——から result を受け取ります。そしてもう一度言いますが、これは本当に型を通すためだけのものですが、入力の URI を AnyUrl に包んで渡します。

[1:36] 次に、その result から、result.contents の 0 番目を取り出します。なぜこれを追加するのか、ここではっきりさせておきたいのです。つい先ほど、インスペクターの中で、返ってくるレスポンスを見ましたよね。これが実質的にあの result 変数です。result は contents というリストを持っていて、その中に要素のリストが入っています。私たちが本当に関心を持つのは、一番最初の要素だけです。

🖥 [1:48] 画面(VS Code:read_resource 実装中): python async def read_resource(self, uri: str) -> Any: result = await self.session().read_resource(AnyUrl(uri)) resource = result.contents[0]| # ← いま書いた行

[2:01] そこで、最初の辞書を取得したい。type プロパティと MIME タイプにアクセスしたいのです。特に MIME タイプが欲しい。なぜなら、どんな種類のデータが返ってきたのかを理解する助けになるからです。もしそれが JSON なら、text を JSON としてパースして、その結果を返すようにしたい。

🖥 [2:17] 画面(VS Code+右に MCP Inspector): エディタに read_resource の書きかけ。右側のインスペクターには前回の fetch_doc 読み取り結果—— json { contents: [ 0: { uri: "docs://documents/report.pdf" mimeType: "text/plain" text: "The report details the state of a 20m condenser tower." } ] } ——が表示されたまま(「contents リストの先頭だけ見ればよい」ことの根拠として参照)。

[2:13] どうやるかお見せしましょう。if isinstance(resource, types.TextResourceContents) を追加します。その中に、if resource.mimeType == "application/json"。これが、あのヒントの使いどころです。サーバーが「JSON を返しますよ」と教えてくれていたなら、テキストの内容を必ず JSON としてパースする必要があります。その場合は、json.loads(resource.text) を返します。

[2:50] そしてそれ以外の場合——つまり、その if 文に入らず早期リターンしなかった場合——は、resource.text をそのまま返します。このシナリオでは、テキストをただのプレーンテキストとして返しているわけです。何もパースしていません。これはまさに、単一ドキュメントの中身が返ってくるケースということになります。

🖥 [3:04] 画面(VS Code:read_resource 完成形): ```python async def read_resource(self, uri: str) -> Any: result = await self.session().read_resource(AnyUrl(uri)) resource = result.contents[0]

if isinstance(resource, types.TextResourceContents):
    if resource.mimeType == "application/json":
        return json.loads(resource.text)

    return resource.text

```

📄 実物コード(cli_project_COMPLETE/mcp_client.py 86-94行)——read_resource の完成形: ```python async def read_resource(self, uri: str) -> Any: result = await self.session().read_resource(AnyUrl(uri)) resource = result.contents[0]

if isinstance(resource, types.TextResourceContents):
    if resource.mimeType == "application/json":
        return json.loads(resource.text)

    return resource.text

```

[3:08] よし。これで本当に終わりのはずです。read resource が組み上がりました。

[3:13] さて、もう一度念を押しておきます。何度も言っているのは分かっていますが、少し分かりにくいかもしれないので、あらためて。MCP クライアントの中に書いているコードは、このコードベースの他の複数の場所から使われています。つまり、このコードベースのどこか別の場所で、いま組み立てたあの関数を呼び出して、ドキュメント名の一覧を取得し、最終的にはドキュメントの中身を取得してプロンプトに入れることになります。ですからこの時点で、実質的にすべてが動くはずです。残りの仕事は、すでに済ませてあるのですから。

🖥 [3:18] 画面(ブラウザ:Google スライドでシーケンス図を再表示): ReadResourceRequest(docs://documents)→ ReadResourceResult(List of doc names)の図を示しながら、「クライアントの関数を呼ぶ側は実装済み」という説明。

[3:41] それを踏まえて、ターミナルに戻りましょう。CLI アプリケーションをもう一度テストして、このメンション機能が動くかどうかを確認します。

[3:49] はい、戻ってきました。uv run main.py を実行します。そして今度は、「what's in the @」のように打てるはずです——ほら、出ました。リソースの一覧が見えます。矢印キーでスクロールして選べます。

🖥 [3:50] 画面(ターミナル): (mcp) → mcp のプロンプトから uv run main.py を起動。「what's in the @」まで打った時点で、@ の直下にドキュメント名のオートコンプリート(deposition.md / report.pdf / financials.docx / outlook.pdf / plan.md / spec.txt)がリスト表示され、矢印キーで選択中の行がハイライトされる。

[4:02] 気に入ったリソースのところまで来たら、スペースを押すだけで、そのリソースが挿入されます。それでは——「what's in the report.pdf document」。

🖥 [4:15] 画面(ターミナル): ```text (mcp) → mcp uv run main.py

Whats in the @report.pdf document?█ ``` (@report.pdf がオートコンプリートから挿入済みの状態)

[4:11] そしてここで言えるのは、すべてが期待どおりに動いている、ということです。言い換えると、このドキュメントの中身は、プロンプトの中に入れられて Claude へ送られています。これを送信すれば、即座にレスポンスが返ってきて、report.pdf の中に何があるのかを教えてくれるはずです。

[4:25] つまり今回は、Claude はドキュメントの中身を読むためにツールを使う必要がなかったのです。

🖥 [4:24] 画面(ターミナル:実行結果): ```text (mcp) → mcp uv run main.py

Whats in the @report.pdf document?

Response: The report.pdf document contains details about the state of a 20-meter conde nser tower.

``` (回答行がハイライトされる。Lesson 07 と違い「I'll check the contents...」というツール使用の前置きが無く、即答)

[4:31] はい。これがリソースです。繰り返しますが、私たちはリソースを使って、MCP サーバーからある程度の情報を公開するのです。

英語逐語(ASR全文)

We have defined two separate resources inside of our MCP server. So now our client needs the ability to request these resources. To do so, we're going to add in a single function inside of our MCP client. And remember, this MCP client is going to have some functionality that we're putting together that is going to be used by the rest of our application. And I've already put together that code already. So somewhere else inside this project, something is going to try to make use of this function that we're about to add into the MCP client. To get started, I'm going to open up the MCP client file again. I'm going to scroll down and find read resource right here. So our goal inside of here is to read a particular resource by making a request off to our MCP server and then parse the contents that come back depending upon its MIME type and then just return whatever data we get. So you'll notice that a argument to it is the URI. This is going to be the URI of the resource that we want to fetch from the server. In order to make a request, just to get all of our types nicely, we're going to add two imports at the very top of the file. I'm going to add in an import for the JSON module, and from PyDantic, I will import any URL. Then I'll go back down to our read resource function. I'm going to clear out the comment and the return statement. Then I'll get a result from calling a wait, self session. I want to read resource. And then again, this is really just to get the types to work out. We're going to put in a, any URL with the input URI. Then I'm going to take from that result response or excuse me, result contents at zero. And I want to make this clear right here, why we are adding this in. So just a moment ago inside of our inspector, we saw the response we get back. So this is essentially that result variable. The result has a contents list and there's going to be a list of elements inside there. We really only care about the very first one. So I want to get the first dictionary. I want to access the type property and the MIME type. I want specifically the MIME type because it's going to help me understand what kind of data we got back. If it is JSON, then I want to make sure I parse the text as JSON and return that result. So let me show you how we're going to do that. I'm going to add in a, if is instance of resource types, text, resource contents. And inside there, if resource MIME type is equal to application JSON. So this is our hint in use. If the server told us that it's giving us back some JSON, we need to make sure that we parse the text content as JSON. So I will return in that case, a JSON loads of resource dot text. And then otherwise, if we don't fall into that if statement and return early, I want to just return resource dot text. So in this scenario, we'd be returning the text as just plain text. We're not parsing anything. So this would really be the case in which we get back the contents of a single document. All right. So that should really be it. We've got our read resource put together. Now, again, I want to remind you, I know I've said this several times, but I just want to remind you because I think it might be a little bit unclear. The code that we're writing inside of the MCP client is being used from several other places inside of this code base. So somewhere else in this code base, we're going to be calling that function that we just put together to get the list of document names and then eventually get the contents of a document to put into a prompt. So at this point, everything should essentially work because the rest of the work has already been done for us. So with that in mind, let's go back over to our terminal, and we're going to test out our CLI application again and see if this mention feature works. Okay, so back over here, I'll do a UV run main.py, and now I should be able to say something like, what's in the at, and there we go, I see my list of resources, and I can use the arrow key to scroll through. Once I am at a resource I like, I'll just hit space, and we'll insert that resource. So what's in the report.pdf document. And now I can tell you that everything is working as expected here. In other words, the contents of this document is being sent off to Claude inside of a prompt. So if I submit this, I should see an immediate response and it's going to tell me what is inside of report.pdf. So this time around, Claude did not have to use a tool to read the contents of the document. All right. So that is resources. Again, we make use of resources to expose some amount of information from our MCP server.

↑ 目次へ戻る

Introduction to MCP | Lesson 10「Defining prompts」(全文ナレッジ)

動画 7分44秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • prompts は MCP サーバーの第3のプリミティブ(tools・resources に続く最後の主要テーマ)。サーバー作者が事前に書き・テストし・eval(評価)まで済ませた高品質プロンプトを、クライアントアプリに配布する仕組み。
  • 題材は CLI へのスラッシュコマンド /format の実装。/ を打つとコマンド一覧がオートコンプリート表示され、format を選んでドキュメント ID(report.pdf など)を指定すると、Claude がその文書を Markdown 構文に書き直す。
  • 重要な洞察:この機能の核心は開発者のコードなしで既に実現できる。ユーザーは今でも CLI に「reformat the report.pdf file in Markdown syntax」と打てば Claude はそれなりにこなす。プロンプト機能の価値は、その「まあまあの結果」を「このシナリオ専用に作り込まれ・テスト済み・eval 済みの強いプロンプトによる、はるかに良い結果」へ引き上げること。
  • プロンプトはクライアント(CLI)のコードベースに直接埋め込むことも可能。しかしサーバー側に置けば、そのサーバーの専門領域(今回なら文書管理)に特化したプロンプトを、利用者が自分で開発せずにそのまま使える
  • 定義構文は tools / resources とほぼ同型:@mcp.prompt デコレータ + name + description(任意)。関数はメッセージのリスト(list[base.Message])を返す。これは実際の User / Assistant メッセージなので、そのまま Claude に送信できる。
  • 引数は Field(description=...) と型注釈(str)で明示(tool と同じ流儀)。import は from mcp.server.fastmcp.prompts import base
  • 実物プロンプトの設計パターン:doc_id<document_id> XML タグで f-string 補間/read ツールの利用を暗黙に誘導/edit_document ツールで編集結果の保存まで指示/「変更の説明はするな、最終版だけ返せ」。
  • 検証は MCP Inspector:Prompts タブ → List Prompts → format 選択 → doc_id 入力 → Get Prompt で、引数補間済みメッセージ(role: "user")が返ることを確認(History: prompts/listcompletion/completeprompts/get)。

実務ポイント(このレッスンから持ち帰るもの)

  1. プロンプトは「eval 済みベストプラクティスの配布チャネル」。自前 MCP サーバーには、そのドメイン特化の高品質プロンプトを同梱する(サーバー=ツール置き場だけではない)。
  2. 返すのは文字列ではなくメッセージ列list[base.Message])。会話の型(複数ターン・user/assistant の組み合わせ)ごと配布できる。
  3. f-string + XML タグ区切り<document_id>{doc_id}</document_id>)が引数補間の実物パターン。曖昧さなく値を Claude に渡せる。
  4. プロンプト本文にツール利用の指示を織り込むedit_document を使って保存せよ)ことで、単発の書き換えでなく「読み→変換→保存」のワークフロー全体を 1 プロンプトで駆動できる。
  5. リリース前に Inspector の Prompts タブで prompts/get の補間結果を目視確認してからクライアント実装へ進む(Lesson 11 へ続く流れ)。

本文(時系列・日本語逐語+画面差し込み)

[0:00] MCP サーバーの中で最後に大きく取り上げるテーマは、プロンプトです。リソースのときとまったく同じように、プロジェクトの中に小さな機能を 1 つ実装し、その機能を使ってプロンプトとは何なのかを理解していきます。さっきリソースでやったのと同じやり方です。

🖥 [0:00] 画面: スライド(アーキテクチャ図)。左に「Our Server」ボックス(中に「MCP Client」)、右に 2 つの「MCP Server」ボックス(それぞれ Tools / Resources / Prompts=Prompts だけオレンジ強調)、さらに右へ「Outside Service」。MCP Client から各サーバーへ矢印。

[0:14] これからプログラムに追加する機能について説明させてください。スラッシュコマンドのサポートを追加します。たとえば format コマンドを用意したい。どう動くかのスクリーンショットをこちらに用意しました。ユーザーが「/」を入力したら、このアプリケーションがサポートするコマンドをいくつかリスト表示します。今のところコマンドは format の 1 つだけです。つまり「/」とだけ打てば、ここに小さなオートコンプリートが出て、選択肢は format だけのはずです。

[0:42] そこで format を選択すると、その後ろにドキュメント ID を入力するよう促されます。report.pdf などの、うちのドキュメント名のどれかですね。

🖥 [0:24〜1:30] 画面: スライド「Next Feature」。箇条書き: - Users can ask Claude to 'format' a document as markdown - User will initiate the process by typing a "/" to list out possible commands - User will specify the ID of the document to format - Claude should read the document's contents, then print a markdown-formatted version

右側にターミナルのモック 3 枚(上から下へ矢印): ```

/ /format Rewrites the conten... /format doc_id deposition.md report.pdf financials.docx outlook.pdf /format report.pdf I'll help you reformat the document with markdown syntax. First, let me read the current content of the docu ment. Now I'll reformat the document using markdown syntax:

Response:

Condenser Tower Assessment Report

```

[0:52] そしてユーザーがこのコマンドを実行したときのゴールは、Claude にこのドキュメントを Markdown 構文で再フォーマットさせることです。言い換えると、今それぞれのドキュメントの中に入っている、特別な書式が何も付いていないただの文字列を対象にします。思い出してください。MCP サーバーの中では、現在のドキュメントの中身はただのプレーンテキストです。これを Claude に食わせて、どうにかして Markdown 構文で書き直させたいのです。

🖥 [1:14] 画面: エディタ mcp_server.pydocs 辞書が表示され、"deposition.md": "This deposition covers the testimony of Angela Smith, P.E.", の行がハイライト選択されている(=中身がただのプレーンテキストであることを示す)。

[1:19] 期待する出力はこんな感じです。「I'll help you reformat the document(文書の再フォーマットをお手伝いします)」のような文言が出て、Claude が次にツールを使って文書の中身を読み、最後の応答の中で、その文書の内容が Markdown 構文で書き直されてここに表示される、というものです。

[1:34] さて、この機能について指摘しておきたい面白い点があります。この機能の本当の核心——つまり本当のゴールは、ユーザーが文書を Markdown 構文に再フォーマットできるようにすることです。そしてそれは、実は私たち開発者がそれを実装するコードを一切書かなくても実現できる操作なのです。どういう意味か?

[1:52] ユーザーはすでに、私たちの CLI を起動して「reformat the report.pdf file in Markdown syntax(report.pdf を Markdown 構文で再フォーマットして)」のように言えます(※ASR では "report.pedia" と聞き取られているが、正しくは report.pdf)。

🖥 [1:46] 画面(ターミナル:手動デモの入力): ```text (mcp) → mcp uv run main.py

reformat the @report.pdf █ `` (手動指示でも Lesson 09 で作った **@メンション**を使って@report.pdf` と入力している)ユーザーは今でもこれができます。何の問題もありません。そして Claude はそれなりにうまくやってくれます。文書の中身を取ってきて Markdown に再フォーマットするのです。ここで見えているとおり、完全に問題なく動きました。

🖥 [2:00〜2:14] 画面: CLI(ターミナル uv (python3))。手動指示に対する実行結果: ```` Response: Here's the report reformatted in Markdown:

```markdown

Condenser Tower Report

The report details the state of a 20m condenser tower. ```

``` (# Condenser Tower Report` の行が選択ハイライトされている)

[2:15] では、この機能で私たちは本当のところ何をやろうとしているのでしょう? ここでの考え方はこうです。もしこれをユーザー任せにして「convert this to Markdown」のように手で打ち込ませたら、そこそこの(okay な)結果は得られるかもしれません。しかし、文書を Markdown に変換するというこの特定のシナリオのためにカスタムメイドされた、本当に強いプロンプトがあれば、はるかに良い結果が得られるかもしれないのです。

🖥 [2:16〜2:56] 画面(スライド:手打ち vs eval 済みプロンプトの対比): 左「If we left this process up to a user, here's what they'd write:」→ 黒いボックスに Convert report.pdf to markdown → 「Yes, it'd work, but the user might get a better result with some strong prompt engineering」。右「User might have more luck if they use our thoroughly-eval'd prompt instead!」の下に、作り込まれたプロンプトの実物全文: `text You are a document conversion specialist tasked with rewriting documents in Markdown format. Your goal is to take the content of a given document and convert it into well-structured Markdown, preserving the original meaning and enhancing readability. Here is the identifier of the document you need to convert: <document_id> {{doc_id}} </document_id> Instructions: 1. Retrieve the content of the document associated with the given document_id. 2. Analyze the structure and content of the document. 3. Convert the document to Markdown format, following these guidelines: - Use appropriate header levels (# for main titles, ## for subtitles, etc.) - Properly format lists (both ordered and unordered) - Use emphasis (*italic* or **bold**) where appropriate - Add links and images using Markdown syntax if present in the original document - Preserve any special formatting or structure that's important to the document's meaning Before providing the final Markdown output, in <document analysis> tags: - Identify the main sections and subsections of the document - Count the number of sections and subsections to ensure proper nesting of headers This will help ensure a thorough and well-organized conversion. After your analysis, present the converted document in Markdown format. Use markers to denote the beginning and end of the Markdown content. Example output structure: [Your analysis of the document structure and conversion plan] ```markdown

Document Title

Section 1

Content of section 1...

Section 2

Content of section 2... - List item 1 - List item 2 Link text Image description `` Please proceed with your analysis and conversion of the document. ```` (=「右側に見えているようなもの」の実体。取得→分析→変換ガイドライン→` での事前分析→出力構造例、まで指定するフルプロンプト)

[2:36] つまり、MCP サーバーの作者である私たちが腰を据えて、書いて、テストして、eval して、本当に徹底した素晴らしいプロンプト——右側に見えているようなもの——を開発する全プロセスを通しておけば、ユーザーはずっと幸せになれるでしょう。もう一度繰り返すと、ええ、ユーザーはこのワークフロー全体を自力で実行できます。でも、代わりにこちらの作り込まれたプロンプトを使えば、もっと良くなるはずです。これが MCP サーバーのプロンプト機能の本当のゴールです。

[3:02] ここでの発想は、事前に、そのサーバーが本当に得意とすることに合わせてカスタムメイドされたプロンプトのセットを、サーバーの中に定義しておけるということです。私たちの場合、このサーバーは文書の管理・読み取り・編集などが専門です。だから、非常に高品質で、eval とテストを済ませ、さまざまなシナリオで動くと分かっているプロンプトのセットを追加する、と決められるわけです。

🖥 [3:05] 画面: スライド「Prompts」。箇条書き: - Defines a set of User and Assistant messages that can be used by the client - These prompts should be high quality, well-tested, and relevant to the overall purpose of the MCP

右側「MCP Server」枠内の Prompt コード: python @mcp.prompt( name="format", description="Rewrites the contents of a document in Markdown format", ) def format_document( doc_id: str, ) -> list[base.Message]: # Return a list of messages

[3:27] そうしたら、それらのプロンプトを任意のクライアントアプリケーション——今まさに組み立てている CLI アプリのような——の中で使えるように公開できます。

[3:34] ここで 1 つ指摘しておきたいのは、このプロンプトを開発して、CLI のコードベースに直接入れてしまうことも可能だという点です。それはまったく可能です。当然できます。ただ、繰り返しになりますが、ここでの考え方は、何か特定のタスクに特化したあなたの MCP サーバーが、いくつかのプロンプトを公開しておけば、人々は事前にプロンプトを開発する心配をせずに、ただ来て使えるということです。

[3:55] MCP サーバーの中でプロンプトを定義するには、すでに組み立てたツールやリソースと非常によく似た、ちょっとした構文を書きます。prompt デコレータを使い、プロンプトに名前を付け、任意で説明も付けます。そしてクライアントがこのプロンプトを要求してきたら、メッセージのリストを送り返します。これらは実際の user・assistant メッセージなので、そのまま Claude に直接送ることができます。

[4:20] それでは、サーバーへ移動して自分たちのプロンプトを組み立ててみましょう。ここに見えているとおり、文書の内容を取ってきて、それをどうにか Markdown 形式で書き直すことに関するプロンプトです。

[4:30] エディタに戻り、MCP サーバーのファイルを開きます。少し下へ行って、文書を Markdown 形式で書き直すことについてのコメントを見つけます。その TODO を削除して、@mcp.prompt を追加します。name は format、description は「Rewrites the contents of the document in Markdown format.(文書の内容を Markdown 形式で書き直す)」です。

[4:56] 次に実際の実装を追加します。format_document です。引数として doc_id を受け取ります。そして任意で、さっきツールでやったのと同じように、ここに Field の説明も追加できます。「Id of the document to format(フォーマット対象の文書の Id)」という説明を持つ Field を任意で付けます。さらに、本当に明確になるように、str の型注釈も付けておきます。この関数からは、メッセージのリストを返します。

🖥 [5:00] 画面: エディタ mcp_server.py(パンくず: format_document > doc_id)。書きかけのコード: python @mcp.prompt( name="format", description="Rewrites the contents of the document in Markdown format." ) def format_document( doc_id=Field() ) 下方に # TODO: Write a prompt to summarize a docif __name__ == "__main__": mcp.run(transport="stdio") が見える。

[5:32] まず最初に、この base というものの import を上部に追加しておきます。既存の MCP サーバーの import のすぐ下に、from mcp.server.fastmcp.prompts import base を追加します。

🖥 [5:30〜5:50] 画面: エディタ先頭部。3 行目を入力中: ```python from pydantic import Field from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp.prompts import | ← 入力中(保存後は import base)

mcp = FastMCP("DocumentMCP", log_level="ERROR") ```

[5:51] それから下部のここに戻って、私たちの「非常によくテストされ、非常によく eval された」プロンプトを定義します。プロンプトは事前に書いておきました。このように貼り付けます。

[5:57] このプロンプトは、Claude にドキュメント ID を受け取らせるだけのものです。暗黙的には、read document ツールを使ってそのドキュメント ID の中身を取得するよう Claude に求めている形になります。そして文書を取得したら、そのまま Markdown 構文で書き直させます。最後に、書き直した後は、その更新をサーバー内に保存するために文書の編集(edit)までさせます。

🖥 [6:00] 画面: エディタ。貼り付けられたプロンプト本文(78 行目「Use the 'edit_document' tool to edit」の部分を選択ハイライト中): ```python def format_document( doc_id: str = Field(description="Id of the document to format"), ) -> list[base.Message]: prompt = f""" Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>

Add in headers, bullet points, tables, etc as necessary. Feel free to add in ex…(画面右端で見切れ)
Use the 'edit_document' tool to edit the document. After the document has been…(画面右端で見切れ)
"""

```

📄 実物コード(cli_project_COMPLETE/mcp_server.py・完成形の該当箇所全文): ```python @mcp.prompt( name="format", description="Rewrites the contents of the document in Markdown format.", ) def format_document( doc_id: str = Field(description="Id of the document to format"), ) -> list[base.Message]: prompt = f""" Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>

Add in headers, bullet points, tables, etc as necessary. Feel free to add in extra text, but don't change the meaning of the report.
Use the 'edit_document' tool to edit the document. After the document has been edited, respond with the final version of the doc. Don't explain your changes.
"""

return [base.UserMessage(prompt)]

`` (import はfrom mcp.server.fastmcp.prompts import base`。プロンプト末尾は「余計なテキストの追加は自由だがレポートの意味は変えるな/edit_document ツールで編集せよ/編集後は文書の最終版で応答せよ/変更の説明はするな」まで指示している)

[6:16] このプロンプトを定義したら、次にメッセージのリストを返します。ここで、base.UserMessage を入れたリストを return し、それに今書いたプロンプトをこのように渡します。

🖥 [6:25] 画面: エディタ。return 文まで完成: python return [base.UserMessage(prompt)]

[6:31] それではこのファイルを保存して、MCP development inspector を起動し、そのインターフェースからこのプロンプトをテストしてみましょう。ターミナルで、また同じコマンドを実行し、ブラウザでそのアドレスに移動します。

🖥 [6:41] 画面: ターミナル: (mcp) → mcp uv run mcp dev mcp_server.py (行頭の (mcp) → mcp はシェルプロンプト表示。実行コマンドは uv run mcp dev mcp_server.py)。直後の画面に: mcp uv run mcp dev mcp_server.py MCP inspector... server listening on port 6277

[6:46] サーバーに確実に接続します。それから Prompts セクションを見つけます。利用可能なプロンプトをすべて一覧表示させます。現時点ではプロンプトは 1 つ、format だけです。

[6:57] format をクリックすると、ここにドキュメント ID を入力しないといけません。今回はドキュメント ID を、そうですね、outlook.pdf にしてみましょう。それを入れて、Get Prompt します。

🖥 [7:01] 画面: MCP Inspector v0.10.2(Connected・Transport Type: STDIO・Command: uv・Arguments: run --with mcp mcp run mcp_serv…)。Prompts タブ → List Prompts の結果に format Rewrites the contents of the document in Markdown format.。右パネル「format」に doc_id 入力欄(placeholder: Enter doc_id、補完は "No results found.")と「Get Prompt」ボタン。History: 2. prompts/list 1. initialize

このときエディタに切り替えて docs 辞書の "outlook.pdf" キーを選択(コピー)してから Inspector に貼り付けている。

[7:12] すると、これが私たちのメッセージのリストです。これらは事前に組み立てられたものです。メッセージパートが 1 つあります。text パートで、そこに完全なプロンプトが丸ごと入っています。ドキュメント ID がその中に補間されているのが分かります。

🖥 [7:15] 画面: Inspector 右パネル。doc_id: outlook.pdf → Get Prompt の結果: { messages: [ 0: { role: "user" content: { ... } 2 items } ] } History: 4. prompts/get 3. completion/complete 2. prompts/list 1. initialize

[7:26] これでこのメッセージ群が手に入ったので、Claude に送ることができます。そうすれば、何かしら適切な応答が返ってくるはずです。

[7:32] 改めてもう一度。MCP サーバーの中に実装するかもしれないこれらのプロンプトの背後にある考え方の全体は、私たちが定義するプロンプトが、よくテストされ、よく eval され、1 つの特定のユースケースに本当に特化したものになる、ということです。

英語逐語(ASR全文)

The last major focus we're going to have inside of our MCP server is going to be on prompts. Once again, just like we did with resources, we're going to implement a small feature inside of our project, and we're going to use this feature to understand what prompts are all about, just like we did with resources a moment ago. Let me tell you about the feature we're going to add into our program. We're going to add in support for slash commands. So for example, I want to have a format command. I've got some screenshots over here of how it's going to work. Whenever a user types in a slash, we're going to list out some number of commands that are supported by our application. For right now, we're going to have just one command called format. So if I type in just slash, I should see a little autocomplete right here. And the only autocomplete option should be format. If I then select format, I should be prompted to add in some document ID after it. So one of our different document names like report.pdf or whatever else. Then whenever user runs this command, the goal is to get Claude to reformat this document using Markdown syntax. So in other words, take the plain string that we have without any special formatting tied to it inside of each of our documents right now. Remember inside of our MCP server, our current document content is just plain text. We want to feed this into Claude and somehow get Claude to rewrite it using Markdown syntax. So I would expect to see some output like this, something that says i'll help you reformat the document claude is then going to use a tool to read the contents of the document and then finally inside the final response i want to see the content of that document rewritten down here in markdown syntax now there's something interesting about this feature that i want to point out the real core of this feature like the real goal here is to allow a user to reformat a document into markdown syntax and that is an operation that actually doesn't require you and I, the developers to write out any code to implement. What do I mean by that? Well, a user can already launch our CLI and say something like reformat the report.pedia file in Markdown syntax. A user can already do this. No issue whatsoever. And Claude is going to do a reasonable job of it. It's going to take the contents of our document and reformat it into Markdown. And as you can see right here, it worked entirely perfectly. So what are we really doing with this feature? Well, the thought process here is that if we just left this up to users and allowed them to manually type in something like convert this to Markdown, they might get a okay result, but they might get much better result if they had a really strong prompt that is custom tailored for this particular scenario of converting a document into Markdown. So a user might be a lot happier if you and I sat down as the MCP server authors and wrote out and tested and evaled and went through the entire process of developing a really thorough, fantastic prompts, like the one you see on the right-hand side. So again, just repeat, yes, the user can execute this entire workflow on their own, but if they use this fancy prompt over here instead, well, I think they would be all the better. This is the real goal of the prompts feature inside of MCP servers. The thought here is that ahead of time, we can define a set of prompts inside of our server that are custom tailored to whatever our server is really specialized to do. In our case, our server is all about managing documents, reading documents, editing documents, and so on. So we might decide to add in a set of prompts that are very high quality that have been evaled and tested. And we know that they work in a wide variety of different scenarios. We can then expose these prompts for use inside of any client application, like the CLI app that we are putting together right now. Now, one thing I want to point out here is that we could develop this prompt and just put it directly into our CLI code base. That is totally possible. We could do that, obviously. But again, the thought here is that your MCP server that might specialize in some particular task might expose some number of prompts that people can just come and use without having to worry about developing them ahead of time. To define a prompt inside of our MCP server, we're going to write out a little bit of syntax, very similar to the tools and resources we have already put together. We will use the prompt decorator. we'll add a name to the prompt and optionally a description as well then whenever the client asks for this prompt we'll send back a list of messages these are actual user and assistant messages so we can take the messages and send them off to clod directly all right so let's go over to our server and we're going to try putting together our own prompt and just like you see right here it's going to be all about taking the contents of a document and somehow rewriting it in markdown format okay So back inside my editor, I'm going to find my MCP server file. I'm going to go down a little bit to the comment about rewriting a document in Markdown format. I'll delete that to do, and then I'll add in a MCP prompt, the name of format, and a description of rewrites the contents of the document in Markdown format. I'll then add in an actual implementation so format document I'm going to receive as an argument a doc id and then optionally we can add in a field description here as well just like we did with our tool earlier on so I can optionally add in a field with a description of id of the document to format and I'm also going to add in a type annotation of string just to make sure that's really clear as well. From this function, we are going to return a list of messages. I'm going to make sure I add in an import for this base thing at the top right away. So right underneath the existing MCP server import, I will add in from MCP server, fast MCP prompts, import base. Then back down at the bottom inside of here, we're going to define our very well tested, very well evaled prompt. I wrote a prompt out ahead of time. I'm going to paste it in like so. So this prompt is just asking Claude to take in a document ID. Implicitly, we are kind of asking Claude to fetch the document ID's contents using the read document tool. And then after getting that document, just go ahead and rewrite it with markdown syntax. And finally, after rewriting it, edit the document as well to save those updates inside of our server. now after defining this prompt we're then going to return a list of messages so down here i'm going to return a list with base user message and i'm going to feed in our prompt that we just wrote out to it like so now i'm going to save this file and then let's go start up our mcp development inspector and test out this prompt from that interface so at my terminal i'll run that same command again, and then navigate to that address inside my browser. I'll make sure I connect to my server. I'll then find the prompts section. I'm going to list out all the different prompts that are available to us. And at this point in time, we have one prompt, just format. So I'll click on format and then I have to enter in a document ID right here. Let's this time around, maybe we'll put it in a document ID of how about a outlook.pdf. So I'll put that in and then get prompt. And then here is our list of messages. So these have been put together ahead of time. I've got one message part here. So a text part with our full prompt right there. We could see that the document ID was interpolated into it. Now that we have these messages, we can send them off to Claude. And hopefully we're going to get back some appropriate kind of response. So once again, the entire idea here behind these prompts we might implement inside of our MCP server is that the prompts we are defining are going to be well tested, well evaled, really specialized to one particular use case.

↑ 目次へ戻る

Introduction to MCP | Lesson 11「Prompts in the client」(全文ナレッジ)

動画 3分01秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • クライアント側(mcp_client.py)の最後の実装は list_prompts()get_prompt() の 2 メソッド。どちらもセッションへの委譲だけで各 2〜3 行。
  • list_prompts: result = await self.session().list_prompts()return result.prompts
  • get_prompt: result = await self.session().get_prompt(prompt_name, args)return result.messages
  • args は dict で渡し、キー名はサーバー側プロンプト関数の引数名に一致させる(例:doc_id)。その値がサーバーの format_document(doc_id=...) にキーワード引数として渡り、プロンプト本文に補間される。
  • get_prompt が返す result.messages は「会話」そのもの。そのまま Claude に直接投入できる形になっている。
  • CLI での動作フロー:/ 入力 → プロンプト一覧がコマンド候補に(スラッシュコマンド名=プロンプト名)→ format 選択 + スペース → ドキュメント選択(plan.md)→ Enter → 単一の user メッセージ(作り込まれたプロンプト)が Claude に投入 → Claude が read ツールで文書内容を取得 → Markdown 版を応答。
  • 4 段の全体像(レッスン末の recap):①サーバーの目的に関連したプロンプトを書いて eval する → ②MCP サーバー内に定義する → ③クライアントはいつでもそのプロンプトを要求できる → ④要求時の引数がプロンプト関数へキーワード引数として渡り、本文に使われる。

実務ポイント(このレッスンから持ち帰るもの)

  1. クライアント実装は薄くてよい。プロンプトのロジック・品質はサーバー側に集約し、クライアントは prompts/listprompts/get の透過的なラッパに徹する。
  2. args のキー=サーバー関数の引数名という契約を崩さない({"doc_id": "plan.md"})。ここがズレると補間が失敗する。
  3. UI 統合の型list_prompts() はスラッシュコマンドのオートコンプリート源、get_prompt() の戻り値(messages)はそのまま会話履歴に差し込んで LLM 呼び出しへ——という 2 段の使い分け。
  4. 動作確認は E2E で:/format plan.md → ツール呼び出し(read_doc_contents)発生 → Markdown 出力(見出し・表・箇条書き)まで通しで見る。
  5. プロンプト機能の設計順序は「書いて eval → サーバーに定義 → クライアントから取得 → 引数補間」。この順で作ると迷わない。

本文(時系列・日本語逐語+画面差し込み)

[0:00] 最後の大きなタスクは、MCP クライアントの中に機能を実装することです。MCP サーバー内に定義されているすべてのプロンプトを一覧できるようにし、さらに、変数が補間された状態で特定のプロンプトを 1 つ取得できるようにします。

🖥 [0:00] 画面: エディタ mcp_client.py(パンくず: MCPClient > get_prompt)。実装前の状態: ```python async def list_prompts(self) -> list[types.Prompt]: # TODO: Return a list of prompts defined by the MCP server return []

async def get_prompt(self, prompt_name, args: dict[str, str]): # TODO: Get a particular prompt defined by the MCP server return [] ``` (list_prompts 側の TODO 2 行が選択ハイライトされている。下に read_resource 実装済みメソッドが見える)

[0:10] ではまず list_prompts を実装しましょう。コメントを削除して、こう置き換えます。result = await self.session().list_prompts()。そして return result.prompts。だいたいこれで終わりです。

🖥 [0:30] 画面: エディタ mcp_client.py。list_prompts 完成: python async def list_prompts(self) -> list[types.Prompt]: result = await self.session().list_prompts() return result.prompts

[0:27] 次に get_prompt です。はっきりさせておくと、個別のプロンプトを取得するとき、私たちはいくつかの引数(arguments)を渡されることになります。これらの引数は、最終的にプロンプト関数の中に現れます。たとえばこの format_document の中では、document ID を受け取ることを期待しています。

[0:44] この args 辞書の中には、document ID のキーが入っているはず、というのが前提です。そしてそれが、あちら側の該当する関数に渡されます。そうして、その値がプロンプト本文の中に補間されるわけです。

🖥 [0:52] 画面: エディタ mcp_server.py タブに切り替え(パンくず: format_document > doc_id)。Lesson 10 で作ったプロンプト定義を表示し、doc_id 引数を選択ハイライト: ```python @mcp.prompt( name="format", description="Rewrites the contents of the document in Markdown format.", ) def format_document( doc_id: str = Field(description="Id of the document to format"), ) -> list[base.Message]: prompt = f""" Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>
...
"""

```

[0:56] それでは get_prompt 関数の中で、self.session().get_prompt から result を取得します。プロンプト名(prompt name)を渡します。これは取得したいプロンプトの名前です。それから引数(arguments)を渡します。そして result.messages を return します。

[1:13] これが返ってくるメッセージ群です。これらは、Claude に直接食わせたい、ある種の会話(conversation)を形成しています。

🖥 [1:00] 画面: エディタ mcp_client.py。get_prompt 完成: python async def get_prompt(self, prompt_name, args: dict[str, str]): result = await self.session().get_prompt(prompt_name, args) return result.messages

[1:19] 以上です。クライアント側でやることはこれだけです。

[1:22] それでは、これを CLI 本体の中でテストしてみましょう。切り替えて、プロジェクトをもう一度実行します。

[1:30] ここで「/」を入力すると、この format コマンドにアクセスできるのが見えます。さて、format というのは実のところ、これから呼び出すプロンプトの名前そのものです。それを選択してスペースを押すと、今度は各ドキュメントから 1 つ選ぶよう求められます。plan.md にしましょう。Enter を押します。

🖥 [1:30〜1:47] 画面: CLI(ターミナル uv (python3)): ``` (mcp) → uv run main.py

/format plan.md deposition.md report.pdf financials.docx outlook.pdf plan.md ← 選択(白ハイライト) spec.txt `` (行頭(mcp) →` はシェルプロンプト表示)

[1:48] すると、あのプロンプト全体——実際にはあの単一の user メッセージだけ——を丸ごと取って、Claude に直接投入しています。これで Claude は、文書を Markdown 構文に再フォーマットしに行くための指示を持ったことになります。さらに、再フォーマットしてほしい文書の ID も与えられています。

[2:04] そこで Claude が最初にやるべきことは、その文書の中身を取りに行くことです。read document ツール(※ASR では "Git document tool"=実際は read_doc_contents ツール)を使ってそれを行います。そして最後に、Claude はこの文書の Markdown 版を応答として返します。ここに、Markdown 構文がたっぷり入った文書があります。

🖥 [2:00] 画面: CLI 出力: ``` (mcp) → uv run main.py

/format plan.md I'll help you reformat the document with markdown syntax. Let me first read the current content of the document to understand what I'm working with. Now I'll reformat the document with markdown syntax to enhance its presentat ion:

Response:

Project Implementation Plan

Overview

The plan outlines the steps for the project's implementation. This document serves as a roadmap for all team members to follow throughout the project li fecycle.

Implementation Phases

```

🖥 [2:19] 画面: 同出力をスクロール。Markdown の表・見出しが生成されている: ```

Phase 5: Deployment & Maintenance

  • Deployment Strategy: Plan for smooth transition to production
  • Training: Prepare users for the new system
  • Support: Establish ongoing maintenance procedures

Resource Allocation

Resource Type Allocation Timeline
Development Team 5 FTE Phases 1-5
QA Engineers 2 FTE Phases 3-4
Project Management 1 FTE All Phases
Infrastructure Cloud-based Continuous

Risk Management

```

[2:20] さて、これは問題なく動いたようなので、プロンプトについて簡単に recap して、何のためのものだったのかを確認しておきましょう。まず、自分の MCP サーバーの目的に何かしら関連性のあるプロンプトを書き、eval するところから始めます。私たちの場合は文書サーバーを作っていました。だから、文書を別のスタイルで書き直すといった機能・内容は、まあ理にかなっていると思います。

[2:42] プロンプトができあがったら、それを MCP サーバーの中に定義します。すると、クライアントはいつでもそのプロンプトを要求できます。プロンプトを要求するとき、私たちはいくつかの引数を渡します。それらはこのプロンプト関数にキーワード引数として提供され、関数はそれらのキーワード引数をプロンプト本文の中で利用できる、というわけです。

🖥 [2:30] 画面: スライド「Prompts」(Lesson 10 と同じ recap スライド): - Defines a set of User and Assistant messages that can be used by the client - These prompts should be high quality, well-tested, and relevant to the overall purpose of the MCP

右側「MCP Server」枠内 Promptpython @mcp.prompt( name="format", description="Rewrites the contents of a document in Markdown format", ) def format_document( doc_id: str, ) -> list[base.Message]: # Return a list of messages

英語逐語(ASR全文)

our last major task is to implement some functionality inside of our mcp client and allow us to list out all the different prompts that are defined inside the mcp server and also get a particular prompt with some variables interpolated into it so let's first implement list prompts i will delete the comment and replace it with a result is a weight self session list prompts and then i will return result dot prompts and that's pretty much it And then get prompt. Now, to be clear, when we get a individual prompt, we're going to be given some number of arguments. These arguments will eventually show up inside of our prompt function. So for example, inside a format document right here, we expect to receive a document ID. Inside of this arcs dictionary, the expectation is that there will be a document ID key, and that will be passed in to the appropriate function over here. And then we will get that value interpolated into the prompt itself. So inside the get prompt function, I will get a result from self session, get prompt. I'm going to pass in the prompt name. That's the name of the prompt I want to retrieve. And then I'll pass in the arguments and then I will return result dot messages. So those are the messages coming back. They form some kind of conversation that we want to feed directly into Claude. And that's it. That's all we have to do for our client. So now we can test this out inside of the CLI itself. I'll flip back over, run the project again. And now if I put in a slash right here, I'll see that I can access this format command. Now, format is really just the name of the prompt that we're going to invoke. So if I select that and then hit space, I'll then be asked to select one of the different documents. And you'll go with plan.md. I'll hit enter. And then we're taking that entire prompt, really just that single user message, and feeding it directly into CLOT. So Claude now has the instructions to go and reformat a document into Markdown syntax. And it has also been given the ID of the document that we want to reformat. So the first thing it needs to do here is go and fetch that document's contents. And it will do so by using the Git document tool. And then finally, Claude is going to respond with the Markdown version of this document. So here is the document with a bunch of Markdown syntax inside of it. All right, since it looked like this worked just fine, let's do a quick recap on prompts and make sure we understand what they are all about. We begin by writing out and evaling a prompt that has some relevancy to our MCP server's purpose. In our case, we were making a document server. So having some functionality or something about rewriting a document in a different style, I think it kind of makes sense. Once we have put our prompt together, we'll define a prompt inside the MCP server. And then our client can ask for that prompt at any point in time. When we ask for the prompt, we will put in some number of arguments that will be provided to this. Prompting function right here as keyword arguments, and then our function can make use of those keyword arguments inside the prompt itself.

↑ 目次へ戻る

Introduction to MCP | Lesson 12「MCP review」(全文ナレッジ)

動画 4分12秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03

日本語ナレッジ(要点整理)

  • コース総括。3 つのサーバープリミティブ(tools / resources / prompts)を「アプリの中の誰が実行を決め、誰が恩恵を受けるのか(=制御主体)」の軸で整理する回。
  • Tools = モデル制御(model controlled):いつツールを実行するかは Claude が単独で決める。結果を使うのも Claude。用途=Claude への機能追加。
  • Resources = アプリ制御(app controlled):アプリ内のコードが「このデータが要る」と判断してリソースを実行し、返ってきたデータを主にアプリが使う(UI 表示など)。本コースでは①オートコンプリート候補一覧の表示、②プロンプトへのコンテキスト添付(@ メンション)の 2 箇所で使った——どちらも開発者が書いたアプリ側コード。
  • Prompts = ユーザー制御(user controlled):いつ実行するかはユーザーが決める。UI 要素(ボタン・メニュー項目)のクリックや、本コースで実装したスラッシュコマンドが起動点。用途=事前定義ワークフロー。
  • 使い分けの指針:Claude に能力を足したい → tools / UI に出すなどアプリへデータを入れたい → resource / 事前定義ワークフローを作りたい → prompts。
  • claude.ai 本体が 3 プリミティブの実例になっている:
  • チャット入力欄の下の提案ボタン(例文クリックで会話開始) → ユーザー制御=事前最適化済みプロンプト → prompts で実装するもの
  • 「+」メニューの「Add from Google Drive」(文書一覧を出し、クリックで内容をコンテキストへ注入) → 何を一覧表示するかを知る必要があるのはアプリだけ=アプリ制御 → resources で実装するもの
  • 「√3 を JavaScript で計算して」と送る → JS 実行の判断は 100% モデル → tools で実装するもの
  • まとめの一行:tools はモデルに、resources はアプリに、prompts はユーザーに奉仕する。ただしこれは高レベルの指針(厳密なルールではない)。

実務ポイント(このレッスンから持ち帰るもの)

  1. MCP サーバー設計の最初の問いは「制御主体は誰か」。作りたい機能を tools / resources / prompts のどれで公開するか迷ったら、「実行の引き金を引くのはモデル?アプリ?ユーザー?」で切る。
  2. tools だけが MCP ではない。UI のデータ供給(resources)とワークフロー配布(prompts)まで含めて設計すると、クライアントアプリ全体を 1 つのサーバーで支えられる。
  3. 既存プロダクトの機能を MCP 化する際の対応表として使える:提案ボタン→prompts、ファイルピッカー/一覧→resources、モデルの自律実行機能→tools(claude.ai の実例がそのままテンプレート)。
  4. この区分は「誰の役に立つか」でサーバー API の粒度を決める発想でもある:モデル向け API は説明文の質、アプリ向けは mime_type と構造、ユーザー向けはプロンプト品質(eval)に投資する。
  5. あくまで高レベルガイドライン。境界例では厳密に分類することより、クライアント側での使われ方から逆算して選ぶ。

本文(時系列・日本語逐語+画面差し込み)

[0:00] プロジェクトはすべて完了しましたが、先に進む前に、学んできた 3 つのサーバープリミティブ——tools、resources、prompts——について簡単に recap しておきたいと思います。特に、それぞれについて面白い点を 1 つ強調したい。すなわち、アプリのどの部分がそれぞれの実行に本当に責任を持つのかという点です。言い換えると、典型的なアプリケーションにおいて、それぞれを実際に動かしているのは誰で、恩恵を受けるのは誰なのか?

🖥 [0:00] 画面: スライド「MCP Server Primitives」(3 カラム・スライド 48 枚目):

Tools(ベージュ) - Model-controlled: Claude decides when to call these. Results are used by Claude - Used for: Giving additional functionality to Claude

Resources(オレンジ) - App-controlled: Our app decides when to call these. Results are used primarily by our app. - Used for: Getting data into our app / Adding context to messages

Prompts(赤茶) - User-controlled: The user decides when to use these. - Used for: Workflows to run based on user input, like a slash command, button click, or menu option

[0:18] まず、tools はモデル制御(model controlled)だと言えます。これは、いつツールを実行するかを決める責任を、本当に Claude だけが持っているという意味です。

[0:29] resources はアプリ制御(app controlled)です。言い換えると、あなたのアプリの中で動いている何らかのコードが、「リソースが提供するデータが必要だ」と判断するのです。リソースを実行し、返ってきたデータを何らかの形で——たとえば UI でそのデータを使うといった形で——利用すると決めるのは、あなたのアプリのコードです。私たちのケースでは、リソースを取得して、そのデータを UI の中でオートコンプリート候補の一覧を出すために使いました。また、プロンプトを増強(augment)するためにもリソースを取得しました。この 2 つはどちらも、あなたと私が書いた、まさにアプリケーション側のコードでした。

[1:02] そして最後に、prompts は本当にユーザー制御(user controlled)です。プロンプトをいつ実行するかは、ユーザーが決めます。ユーザーは、ボタンやメニュー項目のような UI 要素をクリックしてプロンプトの起動を開始するかもしれませんし、スラッシュコマンドを使うかもしれません——私たちがやったのはこれですね。

[1:20] それぞれを何が制御しているのかを強調する理由は、それらの目的の感覚をつかんでもらうためです。もし Claude に能力(capabilities)を追加する必要があるなら、おそらく MCP サーバー内に tools を実装するか、MCP クライアント経由でどこかのサーバーの tools を利用することを検討すべきです。UI にコンテンツを表示するなどの目的でアプリにデータを取り込みたいなら、おそらく resource を使うべきです。そして、何らかの事前定義ワークフローを実装したいなら、おそらく prompts を検討すべきです。

[1:52] さて、これらの考え方の実例はすべて、公式の Claude インターフェース、claude.ai の中で見ることができます。これが今の私の画面です。

🖥 [1:58] 画面: ブラウザで claude.ai/new。「Back at it, Stephen」の挨拶と入力欄「How can I help you today?」。モデルは Claude 3.7 Sonnet、「Research BETA」ボタン。入力欄の下に提案ボタン群:Write / Learn / Code / Career chat / Connect apps

[2:01] メインのチャット入力欄の下に、いくつかボタンがあるのが分かります。1 つをクリックして、さらにこれらの例文の 1 つをクリックすると、すぐにチャットに入っていくのが見えます。これはユーザー制御のアクションでした。ユーザーである私が、この特定のワークフローを開始すると決めたのです。そして私は、おそらく事前に書かれ、おそらく何らかの形で最適化済みのプロンプトを利用しています。つまり、あそこにあるボタンの一覧を実装するなら、私たちはおそらく MCP サーバーの中に一連のプロンプト群を用意することになるでしょう。

🖥 [2:22] 画面: claude.ai のチャット「Comparing Your Writing Style to Famous Authors」。例文クリックで注入された事前作成プロンプト(user メッセージ): ``` Hi Claude! Could you compare my writing style to famous authors? If you need more information from me, ask me 1-2 key questions right away. If you think I should upload any documents that would help you do a better job, let me know. You can use the tools you have access to — like Google Drive, web search, etc. — if they'll help you better accomplish this task. Do not use analysis tool. Please keep your responses friendly, brief and conversational.

Please execute the task as soon as you can - an artifact would be great if it makes sense. If using an artifact, consider what kind of artifact (interactive, visual, checklist, etc.) might be most helpful for this specific task. Thanks for your help! ``` Claude の応答:「Hi there! I'd be happy to compare your writing style to famous authors. To do this effectively, I'll need to see some samples of your writing. …」(書きぶりのサンプル提供を依頼する内容が続く)

[2:32] 同様に、戻って、この小さなタブ——プラスボタン——をクリックすると、「Add from Google Drive」というボタンがあるのが分かります。クリックはしません。私の内部文書がいくつか表示されてしまうので。でも、そのボタンをクリックすると、このチャットにコンテキストとして追加できる文書の一覧が表示されます。

🖥 [2:33] 画面: claude.ai の「+」メニューが開いた状態: Search menu Upload a file Take a screenshot Add from Google Drive →(カーソルがここ) Use a project →

[2:45] そのリストに実際どの文書を描画(render)すべきかを知ること、そして 1 つクリックされるたびに、その内容をこのチャットのコンテキストへ自動注入すること——それはすべてアプリケーション側のコードです。ここに描画する文書の一覧を知る必要があるのは、もっぱらアプリケーションだけです。それはまさに UI 関連の要素です。だから、Google Drive からのあの文書一覧表示を実装するなら、私はおそらく MCP サーバーの中に resource を実装することを検討します。

[3:13] そして最後に、このチャットに「what is square root of three, use JavaScript to calculate the value(3 の平方根はいくつか。JavaScript を使って値を計算して)」のようなメッセージを入力して送信したとしましょう。私は明らかに、Claude が何らかの形で JavaScript コードを実行することを期待しています。それはおそらくツールの利用を通じて行われるでしょう。この場合、ツールを使うという判断は 100% モデル制御でした。何らかの JavaScript ツール実行を使うと決めたのは、モデルです。こういうものを MCP サーバーの中に実装するなら——ご想像のとおり——tool を提供することになるでしょう。

🖥 [3:21〜3:37] 画面(claude.ai:ツール実行の実演): 入力欄に「What is sqrt(3)? Use Javascript to calculate the value.」と打って送信。Claude は「I'll help you calculate the square root of 3 using JavaScript. Let me use the analysis tool to compute this value precisely.」と応答し、「Analyzing data」ブロック内で JavaScript を実行—— ```javascript // Calculate square root of 3 const sqrtThree = Math.sqrt(3); console.log("Square root of 3 =", sqrtThree);

// Let's also calculate it with more precision console.log("Square root of 3 with more precision =", sqrtThree.toFixed(15));

// We can also verify by squaring the result ``` 完了後は「Analyzed data / View analysis」に変わり、回答:「The square root of 3 is approximately 1.7320508075688772」+箇条書き(15 桁表示 1.732050807568877/2 乗すると 2.9999999999999996=浮動小数点精度の限界/分数近似 97/56 ≈ 1.732142857…)。チャットタイトルは「Calculating the Square Root of 3」に自動命名される。

[3:44] 以上をまとめると、これが私たちの 3 つのサーバープリミティブです。そしてそれぞれは、あなたのアプリケーション全体の中の異なる部分によって使われることを本当に意図されています。つまり、tools は概してあなたのモデルに奉仕し、resources は概してあなたのアプリに奉仕し、prompts はあなたのユーザーに奉仕します

🖥 [3:45] 画面: Google スライドに戻り、再び「MCP Server Primitives」(スライド 48)。Tools=Model-controlled: Claude / Resources=App-controlled: Our app / Prompts=User-controlled: The user の 3 カラム。

[4:02] 最後にもう一度。これらは高レベルのガイドラインです。これらに言及した唯一の理由は、あなたが何を作ろうとしているかに応じて、これらのプリミティブのそれぞれをいつ使うべきかの感覚を持ってもらうため、それだけです。

英語逐語(ASR全文)

We are all done with our project, but before we move on, I want to do a quick recap on the three server primitives that we learned about. So tools, resources, and prompts. In particular, I want to highlight something interesting about each of these, namely what part of an app is really responsible for running each. In other words, in a typical application, who is really running each of these things and who benefits from them? Well, we would say that tools are model controlled. This means that Claude alone is really responsible for deciding when to run a tool. Resources are app controlled. In other words, some code running inside of your app is going to decide that it needs some data provided by a resource. It will be your app's code that decides to execute a resource and use the return data in some way, maybe by using that data in the UI or something like that. In our case, we fetch a resource and then use that data inside the UI to provide a list of autocomplete options. We also fetch a resource to augment a prompt. Both those things were really application related code that was authored by you and I to put together. And finally, prompts are really user controlled. So a user decides when a prompt is going to run. A user might start the invocation of a prompt by clicking on some UI element, like a button or a menu option, or they might make use of a slash command, which is what we did. The reason I highlight what is controlling each of these is to give you some idea of their purpose. So if you ever need to add capabilities to Claude, you're probably going to want to look at implementing some tools inside of your MCP server or consuming some server's tools through your MCP client. If you ever want to get some data into your app for the purposes of showing content in the UI or something similar, then you probably want to use a resource. And if you ever want to implement some kind of predefined workflow, you probably want to look at prompts. Now you can see examples of all these ideas inside of the official Claude interface at Claude.ai. So here's what it currently looks like for me. You'll notice that underneath the main chat input are some buttons right here. If I click on one and then click on one of these examples, you'll see that I immediately dive into a chat. So this was a user-controlled action. I, as the user, decided to start up this particular workflow. and I'm making use of a prompt that was probably already written ahead of time and probably has been optimized in some way. So to implement that list of buttons right there, we would probably want to put together a series of different prompts inside of a MCP server. Likewise, if I go back and maybe click on this little tab right here, the plus button, you'll notice that I have a add from Google drive button. Now I'm not going to click on it because it's going to show some of my internal documents, but if I click on that button, I'm going to see some documents that I can add into this chat as some context. Knowing what documents to actually render in that list, and then whenever I click on one, automatically injecting its contents into the context of this chat, that is all application-related code. So it is solely the application that needs to know the list of documents to render here. And that's, again, specifically UI-related elements. So to implement that listing of documents from Google Drive, I would probably look at implementing a resource inside of a MCP server. And then finally, if I enter in a message to this chat of something like what is square root of three, use JavaScript to calculate the value and send it off. I'm clearly expecting Claude to somehow execute some JavaScript code, which would likely be done through the use of a tool. In this case, the decision to use a tool was 100% model controlled. It is the model that decided to use some JavaScript tool execution. To implement something like this inside of a MCP server, we'd likely want to, you guessed it, provide a tool. So in total, that's our three different server primitives. And each one is really intended to be used by a different portion of your overall application. So we've got tools, which are generally going to serve your model, resources, which are generally going to serve your app, and prompts, which are going to serve your users. And once again, these are high-level guidelines, and the only reason I mention them is to just give you a sense of when you should use each of these primitives, depending upon what you are trying to put together.

↑ 目次へ戻る