【2026】Pythonで始めるMCP入門!環境構築からホスト・クライアント・サーバー実装まで解説

生成AIが進化を続ける中、アプリやツール間の連携をスムーズかつ安全に行える仕組みとして注目されているのがMCP(Model Context Protocol)です。

本記事では「Pythonで始めるMCP入門」として、環境構築からホスト・クライアント・サーバーの実装までをわかりやすく解説します。

自然文解析と実行処理を分離できるMCPの設計思想を学び、Pythonのサンプルコードを実際に動かしながら、AIツール開発への第一歩を踏み出しましょう。

MCPとは

MCP(Model Context Protocol)は、アプリやツール同士が情報をやり取りするための共通ルールです。近年は、急速に進化する生成AIが外部の機能を安全に呼び出す仕組みとして注目を集めています。

ここではまず、MCPの基本的な概念と、なぜ今MCPがこれほどまでに注目されているのかを紐解いていきましょう。

MCPの基本概念

MCPの大きな特徴は、ホスト・サーバー・クライアントという3つの役割に分かれ、標準化された方法でやり取りできる点です。

役割説明
ホスト全体のまとめ役として、どの機能(サーバー)をどの利用者(クライアント)が使えるかを管理する
クライアント必要な機能を呼び出し、その結果を受け取る
サーバー実際の機能提供者。計算やファイル読み込みなど、実行処理を担当する

この3者は直接やり取りをするのではなく、必ずホストを介して接続されます。これによりアクセス管理や安全性が確保され、機能の追加や変更も柔軟に行える仕組みになっているのです。

なぜ今MCPが注目されているのか

近年、生成AIが業務やサービスで広く活用される中、AIが外部の情報や機能に安全かつ効率的にアクセスする仕組みが重要視されています。その中で特に注目されているのがMCPです。

MCPはアクセス方法を標準化し、安全性を確保できる仕組みとして、ClaudeやChatGPTなどの有名なAIでも採用されています。また、AIに限らず社内ツール間やローカルアプリ間の連携にも利用可能で、応用範囲が非常に広いのも魅力です。

標準化されたやりとりにより、開発者は異なる言語や環境間でも共通の方法で連携できるため、保守や機能追加も容易になります。

PythonでMCPの仕組みを体験してみよう

PythonでMCPの仕組みを体験してみよう

MCPは概念だけではイメージしづらいため、ここではPythonを使ってその仕組みを実際に体験してみましょう。ホスト・クライアント・サーバーそれぞれの実装は次項以降で紹介しますが、まずは全体の流れを押さえます。

今回の例では、「明日の10時に渋谷で待ち合わせの予定が入りました。Googleカレンダーに登録し、1時間前にリマインドしてください」という指示を、MCPがどのように処理するかを見ていきます。

ユーザーからの指示が届くと、MCPは次の流れで処理を実行します。

  1. 【H】指示文を解析し、実行すべきタスク(カレンダー登録・リマインド設定)に分解
  2. 【H】各タスクを担当クライアントに振り分け
  3. 【C】サーバーへリクエストを送信
  4. 【S】リクエストに沿って処理を実行し、ICSファイル(GoogleカレンダーやOutlookで利用できる国際標準のカレンダーファイル)を出力
  5. 【S】結果ステータスをクライアント経由でホストに返却
  6. 【H】結果をまとめ、「完了しました」とユーザーに返答

※【H】:ホスト/【C】:クライアント/【S】:サーバー

今回のサンプルでは、サーバーの処理結果としてICSファイルを出力します。ユーザーはそれをGoogleカレンダーやOutlookにインポートすることで、簡単に予定を登録できるという仕組みです。

なお、Google認証(OAuth)を組み合わせれば、登録や通知設定まで自動化することもできます。

ホスト部分の自然文解析にはOpenAIのgpt-4.1-nanoを使用します。日時や場所の抽出、あいまいな表現(「明日」「午前10時」など)の解釈が可能で、費用は処理量によりますが、gpt-4.1 nanoなら1回あたり約0.015円と非常に低コストで試せます(1ドル=150円換算)。

PythonでのMCP開発環境の準備

MCP開発環境の準備

ここでは、先ほど紹介したMCPホスト→クライアント→サーバーのサンプルコードを動かすための環境を整えていきます。PythonのインストールからAPIキー設定まで、順を追って準備していきましょう。

  1. Pythonのインストール
  2. ライブラリの導入
  3. OpenAI APIキーの設定
  4. プロジェクトの作成
  5. 動作確認

①Pythonのインストール

Python公式サイトからPython(3.10以上)をインストールしてください。本記事ではバージョン3.13、Windows機にて動作確認しています。

Python公式サイト

出典:Python公式サイト

インストール時には、Windowsの場合「Add Python to PATH」に必ずチェックを入れましょう。macOSやLinuxは標準でPythonが入っている場合もありますが、バージョンが古い場合は最新版をインストールしてください。

Pythonのインストール方法についてはこちらで詳しく解説していますので、併せて参考にしてください。

【2025】Pythonのインストール方法!Windows・MacのOS別に手順を解説

②ライブラリの導入

サンプルコードの動作に必要なライブラリは次の2つです。

ライブラリ名概要
openaiGPTを使った自然文解析のための公式SDK
tzdataタイムゾーン(Asia/Tokyo)の処理に必要(特にWindows環境で推奨)

これらのライブラリをインストールするには、ターミナル(またはコマンドプロンプト)で次のコマンドを実行してください。

pip install openai tzdata

これにより、自然文から予定情報を抽出し、正しいタイムゾーンで日時を扱える環境が整います。

Pythonのおすすめライブラリについては、こちらで詳しく解説しています。

Pythonのおすすめライブラリ9選!ライブラリを使うコツやポイントも紹介

③OpenAI APIキーの設定

自然言語解析でGPTを利用するには、OpenAIのAPIキーが必要です。まずOpenAIのAPIキー発行ページからキーを取得してください。

OpenAIのAPIキー発行ページ

出典:OpenAIのAPIキー発行ページ

取得後は次のコマンドを実行し、環境変数を設定します(Windowsの場合)。

setx OPENAI_API_KEY “sk-xxxxxxxx”
※”sk-xxxxxxxx”の部分に発行されたAPIキーを入れて実行

OpenAIのAPIキーを環境変数に設定

「成功」と表示されれば、設定完了です。ターミナルを再起動すると、APIキーが有効になります

④プロジェクトの作成

MCPを実装するための作業フォルダを作成します。ターミナルで任意の場所に移動し、以下のコマンドを実行してください。

mkdir mcp_project
cd mcp_project

このフォルダ内に、実際のソースコードや設定ファイルをまとめて配置していきます。

⑤動作確認

MCP実装に必要なライブラリとAPIキーが正しく設定されているか確認します。

まずライブラリの確認をしましょう。次のコマンドで、インストール済みのライブラリが表示されればOKです。

pip list
インストール済みのライブラリが表示

次に、APIキーの確認です。次のコマンドを実行し、設定したAPIキーが表示されていることを確認しましょう。使用しているプロンプトによって実行するコマンドが異なる点に注意が必要です。

(PowerShellの場合)
echo $Env:OPENAI_API_KEY
(コマンドプロンプトの場合)
echo %OPENAI_API_KEY%

どちらも問題なく確認できれば、MCP開発の準備は完了です。

MCPをPythonで実装する方法

MCPをPythonで実装する方法

ここでは、MCPの考え方を応用し、「カレンダーに予定を登録して1時間前にリマインド」する最小サンプルをご紹介します。ホスト(GPTで自然文→構造化)、クライアント(橋渡し)、サーバー(ICSファイル生成)の3段階構成を意識しながら見ていきましょう。

ホストの実装

まずはホストです。ホストはユーザーの自然文を解析し、日時・場所・リマインド分数などのパラメータをJSON(データをやり取りする際に広く使われる軽量テキストフォーマット)に整理します。

次の例では、OpenAIの「gpt-4.1-nano」を用いて「明日10時」「渋谷」「1時間前」といった曖昧な表現をAsia/Tokyo基準の具体的な日時に変換しています。

出力されるのは title・date・time・duration_minutes・location・reminder_minutes_beforeといったキーを持つ1件のJSONデータのみです。このJSONをもとに、以降の処理が進みます。

# host_gpt_mcp_demo.py
# ホスト=GPT。自然文→JSONに分解し、クライアントへ振り分け。
import os, json
from datetime import datetime
from zoneinfo import ZoneInfo
from openai import OpenAI
from clients import CalendarClient

MODEL = “gpt-4.1-nano”
TZNAME = “Asia/Tokyo”

SYSTEM_PROMPT = “””あなたはMCPホストです。ユーザーの自然文から
以下のJSONだけを出力してください(文章は出力しない)。

{
­ ­­”title”: “イベント名(省略時は’予定’)”,
­­ ”date”: “YYYY-MM-DD(’明日’等はAsia/Tokyo基準で具体日付に解決)”,
­­ ”time”: “HH:MM(24h。省略時は10:00)”,
­­ ”duration_minutes”: 60,
­­ ”location”: “文字列(省略可)”,
­­ ”reminder_minutes_before”: 60
}
“””

def parse_with_gpt(natural_text: str) -> dict:
­ ­­ client = OpenAI(api_key=os.getenv(“OPENAI_API_KEY”))
­ ­­ now_tokyo = datetime.now(ZoneInfo(TZNAME)).strftime(“%Y-%m-%d %H:%M”)
­ ­­ sys = SYSTEM_PROMPT + f”\n現在日時(Asia/Tokyo)は {now_tokyo} です。”

­ ­­ resp = client.chat.completions.create(
­ ­­ ­ ­­ model=MODEL,
­ ­­ ­ ­­ response_format={“type”: “json_object”},
­ ­­ ­ ­­ messages=[{“role”: “system”, “content”: sys},
­ ­­ ­ ­­ ­ ­­ {“role”: “user”, “content”: natural_text}],
­ ­­ ­ ­­ temperature=0.0,
­ ­­ )
­ ­­ data = json.loads(resp.choices[0].message.content)
­ ­­ data.setdefault(“title”, “予定”)
­ ­­ data.setdefault(“time”, “10:00”)
­ ­­ data.setdefault(“duration_minutes”, 60)
­ ­­ data.setdefault(“reminder_minutes_before”, 60)
­ ­­ data.setdefault(“location”, “”)
­ ­­ return data

def main():
­ ­­ user_text = input(“自然文で入力(例:明日10時に渋谷で待ち合わせを登録して、1時間前に通知して): “).strip()
­ ­­ if not user_text:
­ ­­ ­ ­­ user_text = “明日10時に渋谷で待ち合わせを登録して、1時間前に通知して”

­ ­­ print(“\n[HOST] GPTで解析中…”)
­ ­­ evt = parse_with_gpt(user_text)
­ ­­ print(” 解析結果:”, evt)

­ ­­ print(“[CLIENT] サーバへ登録…”)
­ ­­ cli = CalendarClient(TZNAME)
­ ­­ created = cli.create_event(
­ ­­ ­ ­­ title=evt[“title”],
­ ­­ ­ ­­ date_str=evt[“date”],
­ ­­ ­ ­­ time_str=evt[“time”],
­ ­­ ­ ­­ duration_minutes=int(evt[“duration_minutes”]),
­ ­­ ­ ­­ location=evt.get(“location”, “”)
­ ­­ )
­ ­­ print(” 作成:”, created)

­ ­­ if int(evt.get(“reminder_minutes_before”, 0)) > 0:
­ ­­ ­ ­­ print(“[CLIENT] リマインド設定…”)
­ ­­ ­ ­­ r = cli.add_reminder(created[“id”], int(evt[“reminder_minutes_before”]))
­ ­­ ­ ­­ print(” ics:”, r[“ics_path”])
­ ­­ ­ ­­ print(“\n完了。Googleカレンダー/OutlookにこのICSをインポートしてください。”)
­ ­­ else:
­ ­­ ­ ­­ print(“\n完了。リマインドなしのイベントを作成しました。”)

if __name__ == “__main__”:
­ ­­ main()

ホストはタスク分解とパラメータ抽出のみに専念し、実際の処理は行いません。もしエラーが起きた場合は、簡易パーサ(最低限の処理だけを行う解析プログラム)に切り替えることで、APIのクォータ不足やネットワーク障害があってもデモを止めずに動作できます。

クライアントの実装

次はクライアントです。クライアントはホストから受け取ったJSONをサーバーに渡す「仲介役」を担います。

具体的には、日付・時刻をISO 8601形式に組み立て、イベント作成→リマインド設定の順にサーバーのメソッドを呼び出します。依存関係のある処理は順序を守ることが重要です。

# clients.py
# クライアント=担当者。サーバのAPI(メソッド)を呼ぶだけ。
from __future__ import annotations
from datetime import datetime, timedelta, time
from zoneinfo import ZoneInfo
from server import CalendarServer

class CalendarClient:
­ ­­ def __init__(self, tzname: str = “Asia/Tokyo”):
­ ­­ ­ ­­ self.server = CalendarServer(tzname=tzname)
­ ­­ ­ ­­ self.tz = ZoneInfo(tzname)

­ ­­ def create_event(self, title: str, date_str: str, time_str: str,
­ ­­ ­ ­­ ­ ­­ ­ ­­ ­ ­­ duration_minutes: int = 60, location: str = “”) -> dict:
­ ­­ ­ ­­ # 文字列→絶対時刻(ISO)へ
­ ­­ ­ ­­ start_date = datetime.strptime(date_str, “%Y-%m-%d”).date()
­ ­­ ­ ­­ hh, mm = map(int, time_str.split(“:”))
­ ­­ ­ ­­ start_dt = datetime.combine(start_date, time(hh, mm), self.tz)
­ ­­ ­ ­­ end_dt = start_dt + timedelta(minutes=int(duration_minutes))
­ ­­ ­ ­­ return self.server.create_event(
­ ­­ ­ ­­ ­ ­­ title=title,
­ ­­ ­ ­­ ­ ­­ start_iso=start_dt.isoformat(),
­ ­­ ­ ­­ ­ ­­ end_iso=end_dt.isoformat(),
­ ­­ ­ ­­ ­ ­­ location=location
­ ­­ ­ ­­ )

­ ­­ def add_reminder(self, event_id: str, minutes_before: int = 60) -> dict:
­ ­­ ­ ­­ return self.server.add_reminder(event_id, minutes_before)

クライアントはビジネスロジックを極力薄く保ち、ファイル出力やフォーマット生成などの重い処理はサーバー側に委譲しています。こうすることで、将来的にサーバーの処理を差し替える場合も柔軟に対応できます。

サーバーの実装

最後はサーバーです。サーバーはクライアントから受け取ったパラメータを使い、ICS(iCalendar)ファイルを生成します。

# server.py
# サーバ=実処理担当。イベントを保存し、ICSを生成(VALARM対応)。
from __future__ import annotations
import uuid, json, os
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

class CalendarServer:
­ ­­ def __init__(self, tzname: str = “Asia/Tokyo”, base_dir: str = “data”):
­ ­­ ­ ­­ self.tzname = tzname
­ ­­ ­ ­­ self.tz = ZoneInfo(tzname)
­ ­­ ­ ­­ self.base = base_dir
­ ­­ ­ ­­ os.makedirs(self.base, exist_ok=True)
­ ­­ ­ ­­ os.makedirs(os.path.join(self.base, “events”), exist_ok=True)

­ ­­ def _event_path(self, event_id: str) -> str:
­ ­­ ­ ­­ return os.path.join(self.base, “events”, f”{event_id}.json”)

­ ­­ def _ics_path(self, event_id: str) -> str:
­ ­­ ­ ­­ return os.path.join(self.base, “events”, f”{event_id}.ics”)

­ ­­ def create_event(self, title: str, start_iso: str, end_iso: str, location: str = “”) -> dict:
­ ­­ ­ ­­ event_id = f”evt_{uuid.uuid4().hex[:8]}”
­ ­­ ­ ­­ record = {
­ ­­ ­ ­­ ­ ­­ ”id”: event_id,
­ ­­ ­ ­­ ­ ­­ ”title”: title or “予定”,
­ ­­ ­ ­­ ­ ­­ ”start”: start_iso,
­ ­­ ­ ­­ ­ ­­ ”end”: end_iso,
­ ­­ ­ ­­ ­ ­­ ”location”: location or “”,
­ ­­ ­ ­­ ­ ­­ ”reminder_minutes_before”: 0
­ ­­ ­ ­­ }
­ ­­ ­ ­­ with open(self._event_path(event_id), “w”, encoding=”utf-8″) as f:
­ ­­ ­ ­­ ­ ­­ json.dump(record, f, ensure_ascii=False, indent=2)
­ ­­ ­ ­­ return record

­ ­­ def add_reminder(self, event_id: str, minutes_before: int) -> dict:
­ ­­ ­ ­­ # 既存イベントを更新
­ ­­ ­ ­­ with open(self._event_path(event_id), “r”, encoding=”utf-8″) as f:
­ ­­ ­ ­­ ­ ­­ rec = json.load(f)
­ ­­ ­ ­­ rec[“reminder_minutes_before”] = int(minutes_before)

­ ­­ ­ ­­ # ICSを生成
­ ­­ ­ ­­ ics = self._build_ics(rec)
­ ­­ ­ ­­ with open(self._ics_path(event_id), “w”, encoding=”utf-8″) as f:
­ ­­ ­ ­­ ­ ­­ f.write(ics)

­ ­­ ­ ­­ with open(self._event_path(event_id), “w”, encoding=”utf-8″) as f:
­ ­­ ­ ­­ ­ ­­ json.dump(rec, f, ensure_ascii=False, indent=2)

­ ­­ ­ ­­ return {“event_id”: event_id, “ics_path”: self._ics_path(event_id)}

­ ­­ def _build_ics(self, rec: dict) -> str:
­ ­­ ­ ­­ uid = f”{uuid.uuid4()}@mcp-demo.local”
­ ­­ ­ ­­ dtstamp_utc = datetime.now(timezone.utc).strftime(“%Y%m%dT%H%M%SZ”)

­ ­­ ­ ­­ # ISO8601文字列をパース(タイムゾーン維持)
­ ­­ ­ ­­ start_dt = datetime.fromisoformat(rec[“start”])
­ ­­ ­ ­­ end_dt = datetime.fromisoformat(rec[“end”])

­ ­­ ­ ­­ dtstart_local = start_dt.strftime(“%Y%m%dT%H%M%S”)
­ ­­ ­ ­­ dtend_local = end_dt.strftime(“%Y%m%dT%H%M%S”)

­ ­­ ­ ­­ alarm_block = “”
­ ­­ ­ ­­ m = int(rec.get(“reminder_minutes_before”, 0))
­ ­­ ­ ­­ if m > 0:
­ ­­ ­ ­­ ­ ­­ alarm_block = f”””BEGIN:VALARM
TRIGGER:-PT{m}M
ACTION:DISPLAY
DESCRIPTION:{m}分前リマインド
END:VALARM
“””

­ ­­ ­ ­­ return f”””BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//MCP Demo//Calendar//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:{uid}
DTSTAMP:{dtstamp_utc}
DTSTART;TZID={self.tzname}:{dtstart_local}
DTEND;TZID={self.tzname}:{dtend_local}
SUMMARY:{rec.get(“title”,”予定”)}
LOCATION:{rec.get(“location”,””)}
{alarm_block}END:VEVENT
END:VCALENDAR
“””

イベント本体(VEVENT)に加え、1時間前通知を表す「VALARM」を設定しているのがポイントです。このコードによってICSファイルが生成され、「data/events」フォルダに保存されます。

Pythonで開発したMCPの動作確認

一連の準備が整ったら、次のコマンドでサンプルコードを実行してみましょう。

python host_gpt_mcp_demo.py

実行するとプロンプトが表示されるので、「明日の正午、横浜駅で友達と待ち合わせをしました。カレンダーに登録して、1時間前にリマインドしてください」のように、自然文で指示を入力してください。

MCPの動作確認

指示を入力すると、data/eventsフォルダにICSファイルが自動生成されます。

data/eventsフォルダにICSファイルが自動生成

このICSファイルをGoogleカレンダーやOutlookにインポートすると、開始1時間前に通知が届く予定が登録されます。「明日」「正午」のような曖昧な日時指定でも、Asia/Tokyo 時間帯の具体的な日付と時刻に変換されていることが確認できるでしょう。

Googleカレンダー

将来的に完全自動化したい場合は、ICS生成部分をGoogle Calendar API呼び出しに置き換えることで、手動インポートなしで直接カレンダー登録が可能になります。

MCPを学ぶ前に!Python基礎セミナー講習

Python基礎セミナー講習

MCPの仕組みをしっかり理解し、自由に活用するためには、まずPythonの基礎力が欠かせません。Python基礎セミナー講習」では、初心者でも体系的に学べるカリキュラムをご用意しています。

基礎文法からライブラリの活用、Webスクレイピングやエラー解決、データ可視化、AIの概要、Excel処理の自動化、さらに画像処理の基礎まで、幅広いスキルを一度に習得できるのが魅力です。これからMCPやAI開発に挑戦したい方は、ぜひ受講をご検討ください。

セミナー名Python基礎セミナー講習
運営元GETT Proskill(ゲット プロスキル)
価格(税込)27,500円〜
開催期間2日間
受講形式対面(東京・名古屋・大阪)・ライブウェビナー・eラーニング

Python基礎セミナー講習はこちら

PythonでMCPを学びAI開発を始めよう

本記事では、MCPの基本概念と役割分担(ホスト・クライアント・サーバー)、環境構築の手順、プロジェクト作成の流れ、そして「カレンダーに予定を登録して1時間前にリマインド」するPythonサンプルコードをご紹介しました。

MCPの設計を取り入れることで、自然文の解析から実行処理までを明確に分離でき、機能追加やAPI切り替えも容易になります。まずは記事内のサンプルを動かし、MCPによるAIツール開発の流れを実際に体験してみましょう。

小さな成功体験を積み重ねれば、より高度なアプリケーションやAIツールへの応用もスムーズに進められるはずです。

最新情報をチェックしよう!