MCPとは?自分だけの「毎朝ニュースを読むMCP」を作ってみる

に公開

MCPとは?自分だけの「毎朝ニュースを読むMCP」を作ってみる

はじめに

最近はAIの進化が本当に速くて、「MCP」という言葉を耳にすることも増えてきました。私も同じで、最初は何のことかさっぱりでしたが、しばらく使っているうちに少しずつ慣れてきました。

ただ、これまではずっと誰かが作った既製のMCPを使うばかり。そこでふと、「自分で一からMCPを作るのって、難しいのかな?」という疑問が湧いてきました。気になったので、実際に手を動かして調べてみることにしました。

でも、何のMCPを作ろう? そもそもどんなことにMCPが必要なんだろう? それが次の問題でした。あれこれ考えているうちに思い出したのが、仕事を始める前にニュースをざっと見る習慣があること。でも情報があふれていて、ひと通り目を通すだけでもけっこう時間を取られます。そこで思いつきました——「いっそ自分専用のニュース読み上げMCPを作れば、時間の節約になるんじゃない?」

というわけで、この記事ではその過程を記録していきます。MCPを一から自作する方法をシェアしつつ、MCPがまだよくわからない方にも雰囲気が伝われば嬉しいです。


1. MCPとは?

MCPは Model Context Protocol(モデル・コンテキスト・プロトコル)の略で、Claude や Cursor のようなAIアプリが、外部のツールやデータを使えるようにするための接続規格です。

いちばんイメージしやすいのは USB-C の差し込み口だと思います。

昔は機器ごとに専用のケーブルが必要でした。スマホはこれ、カメラはあれ、外付けドライブはまた別物……。でもUSB-Cが登場してからは、ひとつの規格にまとまって、ひとつの差し込み口でいろんな機器がつながるようになりました。

MCPは同じことを、今度はAIに対してやってくれます。これまでは、AIにあるツールを使わせたければ、そのツール専用のつなぎ方をいちいち書く必要がありました。MCPなら、どのツールも「共通の言葉」で話すので、差し込めばすぐAIが使えます。

MCPはAIにいろいろ持たせられますが、今回必要なのは ツール(tool) だけ——AIが呼び出して特定の仕事をさせる関数のことです。ここでの仕事は、ニュースを取ってくることです。

MCPの概念図
画像出典: modelcontextprotocol.info「Understanding MCP Protocol」

仕組みはどうなっている?

MCPを使うとき、実は3つの部分が連携しています。

  • ホスト(Host) は、あなたが開いて質問を打ち込むアプリ。たとえば Claude for Desktop です。直接やり取りするのはこれだけ。
  • サーバー(Server) は、ツールを持っていて、具体的な仕事(データ取得、API呼び出し、ファイル操作……)をこなす部分。これがあなたが自分で書くところです。
  • クライアント(Client) は、ホストの中にすでに入っている仲介役。配達員のようなもので、AIからの依頼をサーバーに渡し、結果を持ち帰ってくれます。これは用意済みなので、書く必要はありません。

質問をすると、だいたいこんな流れで進みます。

  1. あなたがホスト(Claude)に質問を打ち込む。
  2. Claudeが手持ちのツールを見て、依頼に合うツールを見つける。
  3. Claudeがクライアント経由でサーバーを呼ぶ。
  4. サーバーがツールを動かし、仕事を終えて結果を返す。
  5. Claudeが結果を受け取り、あなたへの答えとしてまとめ直す。

ひとつ面白いのは、「どんなときにどのツールを呼ぶか」をClaudeに教え込む必要がないこと。ツールの説明さえはっきり書いておけば——何をして、いつ使うのか——呼ぶタイミングはClaudeが自分で判断してくれます。

どんなときにMCPを作るべき?

作り始める前に、ひとつ立ち止まって考えたい問いがあります。いまやろうとしていることに、本当にMCPは必要でしょうか? なんでもかんでも必要なわけではありません。

確かめ方は簡単で、「これはAIが自分でできること?」と自問するだけです。すでにうまくできることなら、MCPはいりません。文章を訳す、記事を要約する、概念を説明する、メールの下書きを書く——こういうのはClaudeがすでに得意なので、わざわざMCPで作り直すのは骨折り損です。

MCPが活きるのは、AIに足りない部分を補うときだけ。AIがいちばん苦手なのは「いまこの瞬間の情報」です。Claudeは今日の天気が晴れか雨かも、今朝のニュースも、いまの金価格も知りません。学習した知識が過去のある時点で止まっているからです。それに長期の記憶もありません。会話が新しくなるたびにゼロからのスタートなので、何かを覚えておいてほしいならMCPに保管役を任せる必要があります。さらに、メールを送る、パソコンの中のファイルを読むといった「AIの外で手を動かす」作業も、MCPが橋渡ししてくれないと届きません。

ひとことで言えば——AIが自分でできることはAIに任せ、MCPは足りないところだけに使う、ということです。


2. このニュースMCPは何をするのか

コードを書く前に、まず動き方をはっきりイメージしておきましょう。

アイデアはシンプルです。Claudeに get_latest_news というツールを足してあげます。「今朝のニュースある?」のように聞くと、Claudeがこのツールを呼びます。ツールは各ニュースサイトを回って、最新記事のリスト——見出し、数行の要約、リンク——を集め、Claudeに渡します。あとはClaudeの仕事です。トピックごとにまとめ、短くして、読みやすく書き直してくれます。

ここでツールの役目は、ニュースを取ってくるだけ。翻訳も要約もしません。さきほど書いたとおり、翻訳や要約はClaudeがとても得意なので、安心して任せられます。ツールはClaudeにできない部分、つまり「今日の新しいニュースを取ってくる」ことだけを担えば十分です。

では、ニュースはどこから? 私は RSS を使いました。古い規格ですが、大手の報道サイトはたいてい今も提供しています(NHK、Yahoo!ニュース、ITmedia、さらにBBCにもあります)。RSSなら登録もAPIキーも不要。サイトの .rss リンクを取ってくるだけで、もうニュースのリストが手に入るので、とても手軽です。


3. 準備:技術スタックをざっと確認

手を動かす前に、今回使う道具と、それぞれを選んだ理由をまとめておきます。全体像をつかむために、ざっと眺めるだけでOKです(暗記は不要)。

  • Python(3.10以上) — メインの言語。MCPのSDKがPython向けに用意されているからです。
  • uv — プロジェクト作成とライブラリ管理のツール。速くて軽いので pip の代わりに使います。
  • mcp[cli] — MCPの公式SDK。数行でサーバーが書けます。
  • httpx — HTTPリクエスト用のライブラリ。ニュースサイトから中身を取ってくるのに使います。
  • feedparser — RSSを読み取って解析するためのライブラリ。
  • RSS — 今回の「ニュースの仕入れ先」。APIキーなしでニュースを取得できる仕組みです。
  • Claude for Desktop — 実際にMCPを差し込んで使う場所。

まとめると、データの流れはこうです。feedparserRSS からニュースを読み(取得は httpx)、mcpツール にまとめ、それを Claude for Desktop が呼び出す。全体は Python の上で動き、uv で組み立てます。


4. 実際に作ってみる

ステップ1. uvのインストール

システムに入れる必要があるのは uv だけです(macOS / Linux)。

curl -LsSf https://astral.sh/uv/install.sh | sh

インストール後、ターミナルを開き直すのを忘れずに(exec $SHELL -l というコマンドでも、シェルの設定を読み込み直せます)。

ステップ2. プロジェクト作成

uv init news-reader-mcp
cd news-reader-mcp
uv venv
source .venv/bin/activate
uv add "mcp[cli]" httpx feedparser
touch server.py

上の uv add が、技術スタックで挙げた3つのライブラリを入れるところです。

ステップ3. コードを書く

server.py を開いて、下のコードを書きます。追いやすいようにコメントも付けてあります。

import html
import re
from datetime import datetime, timedelta, timezone

import feedparser
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("news-reader")

JST = timezone(timedelta(hours=9))  # 日本標準時

# 購読するニュースサイト一覧(RSSのURL)
FEEDS = [
    {"name": "NHK", "url": "https://www3.nhk.or.jp/rss/news/cat0.xml"},
    {"name": "Yahoo!ニュース", "url": "https://news.yahoo.co.jp/rss/topics/top-picks.xml"},
]


def clean(raw: str) -> str:
    """HTMLタグを取り除いて読みやすいテキストにする(本文は省略しない)。"""
    text = re.sub(r"<[^>]+>", "", raw or "")
    text = html.unescape(text)
    return re.sub(r"\s+", " ", text).strip()


def published_jst(entry) -> str:
    """公開日時を日本時間(JST)の読みやすい形式で返す。取れなければ空文字。"""
    t = entry.get("published_parsed") or entry.get("updated_parsed")
    if not t:
        return ""
    # feedparser の時刻は UTC なので JST に変換する
    return datetime(*t[:6], tzinfo=timezone.utc).astimezone(JST).strftime("%Y-%m-%d %H:%M")


@mcp.tool()
async def get_latest_news(limit: int = 10) -> str:
    """各ニュースサイトから最新記事を取得する(生データを返す。ツール側では要約しない)。

    取得した記事をもとに、日本語で読みやすいニュースまとめを作成してユーザーに伝えること。
    出力は次のレイアウトに従う。

      ## (見出し)
      **媒体**:媒体名 | **公開**:YYYY-MM-DD HH:MM(JST)

      内容のまとめ(要点を省かず2〜3文。誰が・何を・どうしたかが分かるように)

      🔗 元記事:(リンク)

    ・記事ごとに上記をまとめ、記事と記事の間は空行で区切って見やすくする。
    ・「内容のまとめ」は元の主旨を落とさないこと。リンクは必ず元記事のURLを載せる。

    Args:
        limit: 取得する記事の最大件数。
    """
    items = []
    async with httpx.AsyncClient(follow_redirects=True) as client:
        for feed in FEEDS:
            try:
                r = await client.get(feed["url"], timeout=20.0)
                data = feedparser.parse(r.content)
            except Exception:
                continue  # 1つのサイトが失敗しても無視し、全体を止めないようにする
            for e in data.entries[:limit]:
                items.append(
                    f"媒体: {feed['name']}\n"
                    f"見出し: {e.get('title', '')}\n"
                    f"公開: {published_jst(e) or '不明'}\n"
                    f"内容: {clean(e.get('summary', '')) or '(本文なし)'}\n"
                    f"元記事URL: {e.get('link', '')}"
                )
    if not items:
        return "ニュースを取得できませんでした。しばらくしてからもう一度お試しください。"
    return "\n\n---\n\n".join(items[:limit])


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

注目したいのは2か所です。

  • @mcp.tool() の一行が、普通のPython関数を、Claudeから呼べるツールに変えてくれます。
  • """..."""(docstring)はただのコメントではありません。Claudeがツールの役割を理解するために読む部分であり、ここに「結果をどう見せるか」まで書けます。今回は、見出し・媒体・公開日時・内容のまとめ・元記事リンクをセットにし、内容は要点を落とさない、というレイアウトを指定しています。そのためClaudeはこの形式に沿って、読みやすい日本語のニュースまとめを返してくれます。docstringを丁寧に書くほど、出力は意図どおりになります。

ステップ4. 動かしてみる

uv run server.py

エラーも出ず、何も表示されなければOKです——サーバーが動いていて、接続を待っている状態です。

ステップ5. Claude に登録する

使っているアプリによって、登録のしかたが変わります。Claude Desktop なら設定ファイルを編集し、Claude Code なら専用コマンドで追加します。自分が使っているほうだけでOKです。

方法A:Claude Desktop に登録する(設定ファイルを編集)

設定ファイルを開きます。

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %AppData%\Claude\claude_desktop_config.json

そして次のように追加します(パスはお使いの環境に合わせて、ステップ2で作った news-reader-mcp フォルダを指すようにしてください)。

{
  "mcpServers": {
    "news-reader": {
      "command": "uv",
      "args": [
        "--directory",
        "/news-reader-mcp への絶対パス",
        "run",
        "server.py"
      ]
    }
  }
}

--directory の次の行には、ステップ2で作ったフォルダの絶対パスを書いてください。

  • macOS なら /Users/ユーザー名/news-reader-mcp のような形式で、フォルダ内で pwd を実行すると正確なパスが分かります。
  • Windows では C:/Users/ユーザー名/news-reader-mcp のようにスラッシュで書くのが簡単です(JSONではバックスラッシュ \ 1つだとエラーになるので、使う場合は C:\\Users\\ユーザー名\\news-reader-mcp と2つ重ねてください)。

保存したら、Claude for Desktop を一度完全に終了して開き直します(ウィンドウを閉じるだけでは足りません。macOSなら Cmd+Q)。立ち上げたあと、チャット入力欄のツール(コネクタ)メニューに news-reader が出ていれば、ちゃんとつながっています。

方法B:Claude Code に登録する(CLIコマンド)

Claude Code を使っているなら、専用のCLIコマンド一発で登録できます。

claude mcp add news-reader -- uv --directory /path/to/news-reader-mcp run server.py

-- より後ろが、サーバーを起動するためのコマンドです(ここでは uv 経由で server.py を実行しています)。/path/to/news-reader-mcp の部分は、ステップ2で作ったフォルダの絶対パスに置き換えてください。

登録できたかは、ターミナルで claude mcp list を実行し、一覧に news-reader が出ていれば確認できます。

ステップ6. 話しかけてみる

Claudeを開き直して、こう聞いてみます。

今朝のニュース、ある? トピックごとにまとめて教えて。

するとClaudeは get_latest_news ツールを呼び、ニュースを取ってきて、docstringで指示したとおり、すっきりした記事まとめに仕立ててくれます。実際に返ってきた結果がこちらです。

追いたいサイトが増えたら、FEEDS リストに一行足して再起動するだけです。


まとめ

これで、実際に動くニュース読み上げMCPが完成しました。振り返って、覚えておきたいポイントはこのあたりです。

  • MCPはAIにとっての USB-C のようなもの。外部のツールを使えるようにする共通規格です。
  • 設計のコツは「役割分担」。ツールはClaudeにできないこと(今日のニュースを取ってくる)だけを担当し、要約・まとめ方・見せ方はdocstring経由でClaudeに任せればOKです。
  • ツールの正体は、@mcp.tool() を付けて、わかりやすいdocstringを書いたPython関数にすぎません。

この記事が、MCPの作り方をイメージする助けになれば嬉しいです。これを土台に、いつかもっと複雑で、あなたの仕事にぴったり合うMCPを作ってみてください。


参考資料

この記事を書くにあたって参考にした、MCPの公式情報源です。

Sun* Developers

Discussion