動画 7分03秒 / 講師: Stephen Grider(コース講師) / 文字起こし: mlx_whisper large-v3-turbo / 2026-07-03
日本語ナレッジ(要点整理)
- このレッスンのゴール:CLI チャットボット用 MCP サーバーに最初の2ツールを実装する。
read_doc_contents=ドキュメントを読む(doc_id → docs 辞書の値を返す)
edit_document=ドキュメントを編集する(old_str→new_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 は「空白含め完全一致」が必要。
実務ポイント(このレッスンから持ち帰るもの)
- ツールは「デコレータ+型ヒント+Field」で宣言的に定義——JSON スキーマは SDK 生成に任せる。これが MCP サーバー開発の標準形。
- description はツール選択の精度を決める。実務では「いつ使うか」まで書き込む(講座の簡素版をそのまま真似ない)。引数側にも
Field(description=...) を必ず付ける。
- 入力バリデーション→
ValueError 送出のパターンをすべてのツールに敷く。エラーメッセージに識別子(doc_id)を含めると Claude 側が自己修正しやすい。
edit_document の old_str「Must match exactly, including whitespace」は Claude Code の Edit ツールと同じ設計思想——完全一致置換 API の説明文の書き方としてそのまま使える。
- 実物完成形は
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。)