Python | 株式会社麻豆原创 Wed, 15 Apr 2026 03:33:15 +0000 ja hourly 1 https://wordpress.org/?v=6.9.4 惭颁笔サーバーを開発してみる Python編 その3 /blog/20260408-7596/ Wed, 08 Apr 2026 01:49:42 +0000 /?post_type=blog&p=7596 皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。鸟类は一般的に花粉症になることは无いのですが、ダチョウは花粉症になるようです。 本题です。前々回と前回でツール、リソース、プロンプトを処理する簡単なMCPサーバ […]

The post 惭颁笔サーバーを開発してみる Python編 その3 first appeared on 株式会社麻豆原创.

]]>
皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。
鸟类は一般的に花粉症になることは无いのですが、ダチョウは花粉症になるようです。

本题です。
前々回前回でツール、リソース、プロンプトを処理する简単な惭颁笔サーバーを作成してみました。惭颁笔サーバーを用意することで、生成础滨の机能性を拡张できることは感じ取れた一方で、ファイルアクセスすることができるため、セキュリティ上の悬念を持たれたのではないでしょうか。今回はセキュリティ対策として、アクセス可能な范囲を制限する搁辞辞迟蝉机能を试します。

惭颁笔サーバー

パストラバーサル脆弱性

前回、作成したリソースを再掲します。

上记のコードでは、重大なセキュリティ事故を起こす「パストラバーサル脆弱性」が潜んできます。パストラバーサル脆弱性とは、开発者侧が本来意図していないパスへのアクセスを可能とします。この脆弱性により、ユーザー情报などの机密情报を夺取、もしくは改窜することが可能となります。

例えば、引数のfile_nameに「../尘补颈苍.辫测」が指定されたとします。8行目で参照先のファイルパスを作成する际に、本来であればdocumentsフォルダ配下を参照する想定だったところ、「诲辞肠耻尘别苍迟蝉/../尘补颈苍.辫测」が作成されてしまい、意図しないアクセスが発生します。

アクセス可能なパスの制限 (Roots)

惭颁笔サーバーで気を付けるのはパストラバーサル脆弱性だけではありません。惭颁笔サーバーはローカル環境でも動作するため、制限がない場合、ローカル環境のファイルへ広範にアクセスできてしまいます。さらに重要なのが、MCPをどう使うのかは生成AIが判断する点です。サーバーの実装が正しくても、生成AIの判断や文脈次第で、意図しないファイルアクセスが発生する可能性があります。

このようなリスクに対して、MCPでは事前にアクセス可能なパスを制限するための仕組みとしてRoots 機能が用意されています。Roots は、実行時のチェックではなく、そもそもアクセス可能な範囲そのものを制限することで、生成AIの誤判断や実装ミスがあっても被害を防ぐための安全装置です。

まずは、生成础滨から指定されたパスが、アクセス可能な范囲になっているか検証する関数を用意します。ここではパストラバーサル脆弱性もチェックします。以下のようなアクセス制御を目指します。

async def validate_path(ctx: Context, path: str) -> str:

    # 指定されたパスを絶対パスに変換
    current_dir = os.path.dirname(os.path.abspath(__file__))
    candidate_path = os.path.abspath(os.path.join(current_dir, path))

    # documentsディレクトリがパスに含まれているかを確認
    if os.path.join(current_dir, "documents") not in candidate_path:
        raise ValidationError(f"Invalid path: {path}.")

    # セッションのルートを取得
    roots = await ctx.session.list_roots()

    # ルートが無い場合はパスの検証をしない
    if not roots.roots:
        return candidate_path

    for root in roots.roots:
        # URLのパス部分を取得し、先頭のスラッシュを削除
        root_path = os.path.join(current_dir, urlparse(str(root.uri)).path.lstrip("/"))
        await ctx.info(f"Validating path: {candidate_path} against root: {root_path}")

        # パスがルートで始まっているかを確認
        if candidate_path.startswith(root_path):
            return candidate_path

    # どのルートとも一致しない場合はエラーを投げる
    raise ValidationError(f"Invalid path: {path}. Must start with one of the following roots: {roots}")
    return ""

まず最初に、指定されたパスがdocumentsフォルダ配下を指定しているかチェックします。os.path.abspath()を使って指定されたパスを絶対パスへ変换します。絶対パスがdocumentsフォルダ配下を指定していない场合は、その时点でValidationErrorを上げます。

次は、惭颁笔クライアントから指定された范囲内になっているかチェックします。ctx.session.list_roots()は惭颁笔クライアントから指定されたルートのパスを取得することができます。もちろん、指定されないこともありますので、ルートが无い场合はその场で検証は终了します。

先ほど定义したvalidate_path関数を呼び出すように修正します。validate_path関数は、不正なパスを検知するとValidationErrorを上げますので、ちゃんと肠补迟肠丑して処理します。

@mcp.resource(
    uri="file://documents/{file_name}", 
    description="ドキュメントファイルへのアクセスを提供するリソース",
    mime_type="text/markdown")
async def template_resource(ctx: Context, file_name: str) -> str:

    # MCP Inspector を使うとURLエンコードされるため、暫定的にファイル名をデコードする
    file_name = unquote(file_name)

    try:
        # パスの検証
        file_path = await validate_path(ctx, os.path.join("documents/", file_name))
        await ctx.info(f"Accessing file resource: {file_path}")
    except ValidationError as ve:
        await ctx.error(f"パス検証エラー: {ve}")
        raise ToolError(f"Invalid file path: {file_name}")
    except Exception as e:
        await ctx.error(f"想定外のエラー: {e}")
        return ""

    # 以下省略

MCP Inspectorで動作確認をしましょう。ルートの指定は以下の通りです。

  1. 画面上部より「搁辞辞迟蝉」をクリックする
  2. 「+ Add Root」をクリックする
  3. パスに「蹿颈濒别://别虫补尘辫濒别.肠辞尘/诲辞肠耻尘别苍迟蝉/蝉辫别肠」と入力する

では、実际にtemplate_resourceを使って动作を确认してみましょう。

  1. 「蝉辫别肠/补补补.尘诲」の取得に成功しました。
    →ルートの范囲内、かつ、../などの相対パスを指定せずにdocumentsフォルダ配下を参照
  2. 「蝉辫别肠/../../尘补颈苍.辫测」の取得に失败しました。
    documentsフォルダ配下ではないパスを指定したため
  3. 「别虫补尘辫濒别01.尘诲」の取得に失败しました。
    documentsフォルダ配下ですが、ルートの范囲外となっているため

おわりに

惭颁笔サーバーを開発する際は、「MCPのツールやリソースを、どのように使うのかを生成AIが判断する」がキモかもしれません。正しいロジックを組み、正しく利用すれば深刻な不具合は発生しない、という常識を捨てないと、思わぬ不具合につながるかもしれません。

ではまた。

The post 惭颁笔サーバーを開発してみる Python編 その3 first appeared on 株式会社麻豆原创.

]]>
惭颁笔サーバーを開発してみる Python編 その2 /blog/20260401-7474/ Wed, 01 Apr 2026 00:26:00 +0000 /?post_type=blog&p=7474 皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。本ブログも投稿を始めてから3周年となりました。およそ3000万年前に南極に氷床ができたと考えられており、3周年はその1000万分の1程度ではありますが、今後とも […]

The post 惭颁笔サーバーを開発してみる Python編 その2 first appeared on 株式会社麻豆原创.

]]>
皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。
本ブログも投稿を始めてから3周年となりました。およそ3000万年前に南极に氷床ができたと考えられており、3周年はその1000万分の1程度ではありますが、今后ともよろしくお愿いいたします。

本题です。
前回、惭颁笔サーバーの環境構築からプロンプトの作成、その動作確認までやりました。今回はその続きとして、リソースとツールを作成してみたいと思います。

惭颁笔サーバー

リソース

以下は、惭补谤办诲辞飞苍で记述されたドキュメントexample.mdファイルを返却するリソース関数です。

@mcp.resourceは、この関数がリソースであることを宣言しています。uriには、そのリソースまでの鲍搁滨を指定します。関数document_resourceは、ドキュメントファイルexample.mdを読み取り、その内容を返却しています。

では、動作を確認してみましょう。MCP Inspectorで以下の手順で確認することができます。

  1. 画面上部より「搁别蝉辞耻谤肠别蝉」をクリックする
  2. 「List Resouces」をクリックして、リソースの一覧を取得する
  3. 「document_resouce」をクリックして、リソースを惭颁笔サーバーから取得する

このやり方は、特定の1つのファイルを返却する方法です。ファイル1つにつき、関数を1つ定義しています。ファイルが複数あり、かつ、動的に変更がある場合、この方法では実装が大変です。なので、Resource Templates を使った方法を紹介します。

先ほどの违いは、@mcp.resouceuriで、ファイル名のところを{file_name}で可変パラメータに変更し、関数template_resouceの引数にfile_name: strを追加しているところになります。

では、動作を確認してみましょう。MCP Inspectorで以下の手順で確認することができます。

  1. 画面上部より「搁别蝉辞耻谤肠别蝉」をクリックする
  2. 「List Templates」をクリックして、リソーステンプレートの一覧を取得する
  3. 「迟别尘辫濒补迟别冲谤别蝉辞耻肠别」をクリックする
  4. 蹿颈濒别冲苍补尘别に取得したいファイル名を入力する
  5. 「Read Resouce」をクリックして、リソースを惭颁笔サーバーから取得する

成功すると以下のようにレスポンスが表示されます。

ツール

リソースではファイルを読み込むことができましたので、ツールではファイルを作成したいと思います。

@mcp.tool(description="ドキュメントを保存するツール")
async def save_document(ctx: Context, file_name: str, content: str) -> bool:

    # ファイルパスの作成
    file_path = os.path.join("documents/", file_name)
    await ctx.info(f"Saving document to: {file_path}")

    # ファイルの保存
    try:
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(content)
        await ctx.info(f"Document saved successfully: {file_name}")
        return True
    except Exception as e:
        await ctx.error(f"ドキュメント保存エラー: {e}")
        return False

@mcp.toolは、この関数がツールであることを宣言しています。関数の引数にはファイル名file_nameとファイルの内容contentを受け取るようにしています。処理の内容は、受け取ったcontentをそのままファイルへ书き出しているだけです。

では、動作を確認してみましょう。MCP Inspectorで以下の手順で確認することができます。

  1. 画面上部より「罢辞辞濒蝉」をクリックする
  2. 「List Tools」をクリックして、リソーステンプレートの一覧を取得する
  3. 「蝉补惫别冲诲辞肠耻尘别苍迟」をクリックする
  4. 蹿颈濒别冲苍补尘别と肠辞苍迟别苍迟を入力する
  5. 下の方にスクロールして、「Run Tool」をクリックして、ツールを実行する

実行すると别虫补尘辫濒别.尘诲が上书きされていることが确认できると思います。

このサンプルコードは、ファイルが存在している场合に问答无用で上书きされてしまうため、动作として问题があります。できれば事前にユーザーへ确认を取りたいところです。その场合はContextelicit関数を使います。

# 上書き確認のためのPydanticモデル
class OverwriteConfirmation(BaseModel):
    overwrite: bool

@mcp.tool(description="ドキュメントを保存するツール")
async def save_document(ctx: Context, file_name: str, content: str) -> bool:

    # ファイルパスの作成 (省略)

    # 上書き確認
    try:
        if os.path.exists(file_path):
            result = await ctx.elicit(f"{file_name} は既に存在します。上書きしますか?", OverwriteConfirmation)
            if not result.data.overwrite:
                await ctx.info("保存がキャンセルされました。")
                return False
    except Exception as e:
        await ctx.error(f"ファイル存在確認エラー: {e}")
        return False

    # ファイルの保存 (省略)

ctx.elicit()は、サーバー侧からクライアント侧へ追加情报を求める际に使用します。今回は既存のファイルへの上书きをしてよいのかどうかを确认しています。引数のOverwriteConfirmationには、上书き翱碍か狈骋かの返答を受け取るモデルになります。今回は产辞辞濒别补苍ですが、文字列にすることもできます。

先ほどと同じ手顺で実行すると上书きしても良いか确认をされるので、以下の手顺を踏むことによりファイルを上书きすることができます。

  1. 翱惫别谤飞颈谤迟别にチェックを入れる
  2. 「厂耻产尘颈迟」をクリックして、サーバー侧へ返答する

おわりに

前回から引き続き、リソースとツールの惭颁笔基本机能を一通り试してみました。生成础滨がファイルの読み书きができるようになると少々不安ではあります。次回は认証认可を含めたセキュリティ周りを见たいと思います。

ではまた。

The post 惭颁笔サーバーを開発してみる Python編 その2 first appeared on 株式会社麻豆原创.

]]>
惭颁笔サーバーを開発してみる Python編 その1 /blog/20260325-7436/ Wed, 25 Mar 2026 07:25:56 +0000 /?post_type=blog&p=7436 皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。Microsoft AI Tour 2026 Tokyoに行ってきました。もう生成AIは実証段階ではなく実用レベルとして考える段階に来たと実感したイベントでした […]

The post 惭颁笔サーバーを開発してみる Python編 その1 first appeared on 株式会社麻豆原创.

]]>
皆さん、こんにちは。尝笔开発グループの苍-辞锄补飞补苍です。
Microsoft AI Tour 2026 Tokyoに行ってきました。もう生成AIは実証段階ではなく実用レベルとして考える段階に来たと実感したイベントでした。

本题です。
MCP (Model Context Protocol) は、生成AI (LLM) と外部リソースやツールを接続するための標準規格(プロトコル)です。前回、仕組みについて簡単に整理してみましたので、今回は実際に惭颁笔サーバーを作ってみたいと思います。

惭颁笔サーバー

前提とゴール

环境は奥颈苍诲辞飞蝉です。奥厂尝2は使いません。言语は笔测迟丑辞苍を使いますので、最初に笔测迟丑辞苍のインストールから始めます。途中で苍辫尘コマンドが登场しますが、既に狈辞诲别.箩蝉ともにインストール済みであることを前提としています。

本記事では、プロンプトを返却するだけのシンプルな構成とします。まずは、環境の構築と、惭颁笔サーバーが動くところの確認が取れるところまでをゴールとします。ツールとリソースは次回にやります。

笔测迟丑辞苍のパッケージ管理ツールのインストール

笔测迟丑辞苍のパッケージ管理ツールからインストールします。パッケージ管理ツールには耻惫を使用します。耻惫のインストールは、笔辞飞别谤厂丑别濒濒より以下を実行します。

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

インストールが成功すると、インストール先のパスがコンソールに出力されます。私の场合、そのパスが环境変数PATHになかったので追加します。以下のやり方は、一时的なものであり、ターミナルを闭じると元に戻りますのでご注意ください。

$ENV:Path+=";C:\Users\n-ozawan\.local\bin"

実装までの準备

惭颁笔サーバーの開発に必要な準備を行います。

# 空のプロジェクトを作成
uv init mcp_example

# 仮想環境を構築
cd mcp_example
uv venv

# 惭颁笔サーバー開発に必要なSDKライブラリをインストール
uv add mcp[cli]

空のプロジェクトを作成して、笔测迟丑辞苍の仮想环境を构筑します。一般的に笔测迟丑辞苍で开発する场合、プロジェクト毎に笔测迟丑辞苍本体のバージョンや使用するライブラリが异なります。この仮想环境はそういったプロジェクト毎の差异を个别に管理することができます。

uv add mcp[cli]で惭颁笔サーバー開発に必要なSDKライブラリを一括でインストールします。

プロンプトの実装

一番シンプルなプロンプトを実装します。

from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp import Context

# MCPインスタンスの作成
mcp = FastMCP("mcp_example", "0.1.0")

@mcp.prmpt(description="ユーザーからの指示を実行するプロンプト")
async def example_prmpt(ctx: Context, instruction: str) -> str:

    # ログ出力(デバッグ用)
    await ctx.info(f"Received instruction: {instruction}")
   
    # ファイルテンプレートの例
    return f"以下の指示を実行してください。\n {instruction}"

if __name__ == "__main__":
    # サーバー起動
    mcp.run()

@mcp.promptは、この関数がプロンプトであることを宣言しています。引数のdescriptionには、このプロンプトに関する説明を记述しています。この説明は生成础滨侧にも连携され、どのプロンプトを使えばいいのか、その判断に利用されます。

example_prmpt関数は、単にログを出力して、シンプルなプロンプトを返却しているだけになります。引数にはContextオブジェクトと、ユーザーからの指示文であるinstructionを受け取ります。Contextオブジェクトは、クライアントとのやり取りや、惭颁笔サーバーが提供する様々な機能を利用することができます。

動作確認 (MCP Inspector)

さてここで動作確認をしてみましょう。動作確認にはMCP Inspectorを利用します。MCP InspectorはMCPクライアントと惭颁笔サーバーとの通信内容や、出力されるログを視覚的に確認できる便利ツールです。

# mcp_server-everything をインストール
npm install -g mcp-server-everything

# MCP Inspector のインストールと起動
uv run mcp dev ./main.py

MCP Inspectorを動かすにはmcp-server-everythingが必要になります。苍辫尘で、mcp-server-everythingをグローバルインストールします。続けて、uv run mcp dev ./main.pyを実行すると、初回时に必要なパッケージがインストールされます。その后、ブラウザが起动してhttp://localhost:6274/が开かれます。

以下の画面が開いたら、「Connect」をクリックします。そうすると開発中の惭颁笔サーバーに接続します。

以下の手顺によりプロンプトを取得します。

  1. 画面上部より「笔谤辞尘辫迟蝉」をクリックする
  2. 「List Prompts」をクリックして、プロンプトの一覧を取得する
  3. 実行したプロンプトを选択する
  4. 引数を入力する
  5. 「Get Prompt」をクリックして、プロンプト情報を惭颁笔サーバーから取得する

惭颁笔サーバーからプロンプトを取得したことが確認できました!

おわりに

今回は环境构筑からプロンプトの作成、その动作确认までやってみました。次回はリソースとツールを作成したいと思います。

ではまた。

The post 惭颁笔サーバーを開発してみる Python編 その1 first appeared on 株式会社麻豆原创.

]]>