0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Claude MCP Server実装ガイド】AIに「カフェ予約して」と頼んだら本当に予約できてしまった

0
Posted at

友達からLINEが来た。

「明日の14時、乳製品アレルギーの友達も一緒にカフェ行きたいんだけど、メニュー大丈夫かな?予約もしておいて」

普通なら、食べログ開いて → アレルギー情報探して → 電話して → 空き確認して → 予約する。15分コース。

でも今回、AIにこう頼んだだけ:

「乳製品アレルギーの友達と明日14時、2名で予約して。名前は松原で」

数十秒後 ──

✅ 空席確認:   34席空き(店内34席中)
✅ メニュー検索: 乳製品フリー5品ヒット(エスプレッソ ¥400〜)
✅ 予約完了:    AURA-20260305-003(松原様 2名 14:00 店内席)

全部、AIが勝手にやった。 検索→絞り込み→空き確認→予約。この4ステップをAIが判断して実行した。使ったのは MCP (Model Context Protocol) という仕組みで、TypeScript 2ファイルで実装できる。

この記事は 前回のLLM-SEO記事 の続編(完結編)です。前回未読でも完結しますが、読むと「騙す→読ませる→使わせる」の進化が3倍楽しくなります。

目次

  1. 三部作の完結
  2. MCP って何?
  3. 検証デモ(Demo 1〜4)
  4. 何が起きているのか
  5. 実装
  6. description設計のコツ
  7. 三部作 総比較
  8. セキュリティの話
  9. 再現手順
  10. おわりに

「見せる → 読ませる → 使わせる」三部作の完結

前回の記事では、AIに 「読みやすいデータ」 を提供する3つの手法を検証しました。同じ情報なのにHTMLの正答率75%、Markdownなら100%。

でもあれ、よく考えると 「メニューの出し方を変えた」 だけですよね。

前回: 「日本語メニューしかない店」→「英語メニューも用意した」
       ↑ 読みやすくなっただけ。できることは変わらない。

今回: 「メニューを読む」→「電話で予約できる」
       ↑ 読むだけじゃない。AIが店を "使える" ようになった。
Part やったこと レストランの例え
1 User-Agentで別ページを表示 外国人に勝手に英語メニューを出す
2 Markdown / JSON で情報提供 多言語メニューを用意する
3 MCP Server でツール提供 電話予約・アレルギー相談ができる

MCP って何? ── 30秒で分かる説明

MCP (Model Context Protocol) は、AIエージェントが外部のツールやデータソースを「使う」ための標準プロトコル。Anthropic が策定し、2025年からエコシステムが急拡大中。

一言で言うと: MCPとは、AIに「うちのサービスの電話番号」を教えること。

引き続きレストランで例えます:

これまで(Part 1-2):
    お客さん → メニューを読む → 「美味しそうだな」で終わり

MCP(Part 3):
    お客さん → 電話する → 「明日2名で予約お願いします」
                        → 「アレルギー対応できますか?」
                        → 「キャンセルしたいんですけど」

メニューを読むだけじゃない。電話番号を教えて、予約システムにつなぐ。 それが MCP です。


いざ検証 ── AIにカフェを「使わせて」みた

百聞は一見にしかず。先に動いているところをお見せします。

検証環境: Claude Code(Claude Opus 4.6)+ cafe-aura MCP Server。結果は非決定的であり、毎回同じ結果になるとは限りません。ただし「AIがツールを選択・実行する」挙動は安定して観察されました。

Demo 1 / 4: 「WiFiのパスワード教えて」

まずはシンプルなところから。

私:

Cafe AURAのWiFiパスワードを教えて

Claude Code:

[MCP] get_wifi_info() を呼び出し中...

Cafe AURAのWiFi情報です:
- SSID: CAFE-AURA-FREE
- パスワード: aura2026
- 速度: 下り約200Mbps
- 制限: 1回の接続で最大3時間(再接続可)

前回の記事で HTMLから読んだら「3時間制限」を見落とした あの情報。MCPツール経由なら構造化データで返るから見落としようがない。

前回(HTML 132KB を渡した場合):  ❌ 「3時間制限」を見落とし
今回(MCP get_wifi_info):       ✅ 全情報を正確に返答

Demo 2 / 4: 「乳製品アレルギーなんだけど」

ここからが面白い。複数パラメータの同時指定です。

私:

乳製品アレルギーの友達と行くんだけど、予算600円以内で頼めるメニューある?

Claude Code:

[MCP] search_menu({
  excludeAllergens: ["乳"],
  maxPrice: 600
}) を呼び出し中...

乳製品フリーで600円以内のメニューが5品見つかりました:

1. シングルオリジン ¥580 - 月替わりの産地直送豆をハンドドリップで
2. コールドブリュー ¥530 - 16時間かけて水出しした滑らかな味わい ⭐NEW
3. ハーブティー ¥500 - カモミール / ペパーミント / ルイボスから選択
4. アールグレイ ¥480 - ベルガモットの香り高いクラシックティー
5. エスプレッソ ¥400 - 深煎りブレンドのダブルショット

全品アレルゲンフリーです。

「乳製品アレルギー」→ excludeAllergens: ["乳"] AIが自然言語をツールのパラメータに変換している。これ、地味にすごくないですか?(この秘密は後述する「description設計」セクションで明かします)

Demo 3 / 4: 「予約して」── 水曜の罠

本番です。予約フロー。

私:

じゃあ明日(水曜)の14時に2名で予約して。名前は松原で

Claude Code:

[MCP] get_seat_availability({
  date: "2026-03-04",
  time: "14:00"
}) を呼び出し中...

❌ 2026-03-04 は水曜日で定休日です。
木曜日の 2026-03-05 はいかがですか?

……マジか。AIが定休日を判断して、代替日を提案してきた。

私:

じゃあ木曜で

Claude Code:

[MCP] get_seat_availability({
  date: "2026-03-05",
  time: "14:00"
}) を呼び出し中...

空席あり: 店内34席中 34席空き

[MCP] make_reservation({
  name: "松原",
  date: "2026-03-05",
  time: "14:00",
  partySize: 2
}) を呼び出し中...

✅ 予約が完了しました!

予約番号: AURA-20260305-003
お名前:   松原様
日時:     2026年3月5日(木)14:00
人数:     2名
席:       店内席

※ 前日18:00までキャンセル無料です

demo-reservation.png
↑ ユーザーは日本語で頼むだけ。AIが空席確認→予約作成を実行

ここ、よく見てください。 私がやったのは「木曜で」と3文字打っただけ。AIが:

  1. 「木曜」→ 2026-03-05 に変換
  2. get_seat_availability で空き確認
  3. 空きがあったので make_reservation で予約作成

この3ステップを判断して実行した。私はツール名を1つも指定していない。

Demo 4 / 4: 「やっぱキャンセルで」

私:

さっきの予約、やっぱりキャンセルしたい

Claude Code:

[MCP] cancel_reservation({
  reservationId: "AURA-20260305-003"
}) を呼び出し中...

✅ 予約をキャンセルしました。

予約番号 AURA-20260305-003
(松原様 2名 2026-03-05 14:00)

AIは 会話の文脈から予約番号を覚えていて、自動でキャンセル処理を実行した。「予約番号は?」とか聞いてこない。


何が起きているのか ── 技術的に整理する

ポイントは AIが「どのツールを呼ぶか」を自分で決めている こと。

get_seat_availabilitymake_reservation の順序は私が指示していない。AIが:

  1. ツールの description を読む
  2. ユーザーの意図を解釈する
  3. 最適なツールと呼び出し順序を決定する
  4. 結果を見て次のアクションを判断する

「読ませる」と「使わせる」の本質的な違い。Part 2 まではデータの受け渡し。Part 3 は判断と実行。

もう少し技術的に知りたい人向け

通信方式: Claude Code が MCP Server をサブプロセスとして起動し、stdin/stdout で JSON-RPC 2.0 メッセージをやり取りします。AIは起動時に全ツールの description を受け取り、どのツールをどの順序で呼ぶかを判断します。


実装 ── 架空カフェ「Cafe AURA」に MCP Server を作る

デモで動きを見たところで、中身の実装を見ていきましょう。

8つのツール

# ツール名 種別 できること
1 get_cafe_info 📘 INFO 営業時間・住所・アクセス
2 get_wifi_info 📘 INFO SSID・パスワード・速度・制限
3 get_menu 🔍 SEARCH メニュー一覧(カテゴリフィルタ)
4 search_menu 🔍 SEARCH アレルゲン除外・価格帯・キーワード検索
5 get_seat_availability 🔍 SEARCH 日付・時間指定の空席確認
6 make_reservation ⚡ ACTION 予約作成(定休日・定員チェック付き)
7 get_reservation ⚡ ACTION 予約番号で検索
8 cancel_reservation ⚡ ACTION 予約キャンセル

tools-overview.png
↑ 情報取得(青)→ 検索(緑)→ 予約管理(紫)の3レイヤー。AIはこれらを自由に組み合わせる

レストランで言えば: INFO=看板SEARCH=メニュー表ACTION=電話予約。段階的に「できること」が増える設計です。

プロジェクト構成

cafe-aura-mcp/
├── src/
│   ├── index.ts    # MCP Server本体(8ツール登録)
│   └── data.ts     # Cafe AURAデータ + 予約システム
├── package.json
├── tsconfig.json
└── dist/           # ビルド出力(node dist/index.js で起動)

ファイル2つ。 これだけです。

データ層 ── src/data.ts

前回の cafe-data.tsdata.ts にリネーム・拡張。メニュー14品にアレルゲン・カロリー情報を追加し、予約システム(インメモリ)を実装しました。席は全42席(店内34 + テラス8、テラスは予約不可)。

data.ts の主要部分(クリックで展開)
src/data.ts(メニューの一部)
export const MENU_ITEMS: MenuItem[] = [
  {
    id: "specialty-latte",
    name: "スペシャルティラテ",
    price: 650,
    category: "coffee",
    description: "エチオピア・イルガチェフェ豆を使用した看板メニュー",
    allergens: [""],       // ← アレルゲン情報
    isPopular: true,
    calories: 180,
  },
  // ... 全14品
];

予約システムはシンプルな Map ベース:

src/data.ts(予約システム)
const reservations = new Map<string, Reservation>();

export function makeReservation(params: {
  name: string;
  date: string;   // YYYY-MM-DD
  time: string;   // HH:MM
  partySize: number;
  seatType?: "indoor" | "any";  // テラスは予約不可
  note?: string;                // アレルギー情報、記念日など
}) {
  // 水曜定休チェック
  const d = new Date(params.date + "T00:00:00+09:00");
  if (d.getDay() === 3) {
    return { success: false, error: "水曜日は定休日です" };
  }
  // 空席チェック → 予約作成 → IDを返す
  // ...
}

本番環境ではインメモリではなくDBを使ってください。 これはデモ用です。MCP Server のプロセスが終了すると予約データも消えます。

MCP Server 本体 ── src/index.ts

ここが この記事の核心 です。MCP SDK の registerTool を使ったツール登録がどれだけシンプルか見てください。

import + 初期化(クリックで展開)
src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { CAFE_INFO, WIFI_INFO, MENU_ITEMS, /* ... */ } from "./data.js";

const server = new McpServer({
  name: "cafe-aura",
  version: "1.0.0",
});

ツール登録は server.registerTool() を呼ぶだけ。

src/index.ts(店舗情報 ─ 最もシンプルなツール)
server.registerTool(
  "get_cafe_info",
  {
    description:
      "Cafe AURAの基本情報(営業時間・住所・電話番号・アクセス・座席数・決済方法)を取得します",
  },
  async () => ({
    content: [{ type: "text" as const, text: JSON.stringify(CAFE_INFO, null, 2) }],
  })
);

第2引数のオブジェクトに注目。 description がツールの "看板" です。AIはこの説明文を読んで「どのツールを使うか」を判断します。

パラメータがあるツールは inputSchema を追加:

src/index.ts(メニュー検索 ─ 最もリッチなツール)
server.registerTool(
  "search_menu",
  {
    description:
      "条件を指定してメニューを検索します(アレルゲン除外・価格帯・キーワード)",
    inputSchema: {
      excludeAllergens: z.array(z.string()).optional()
        .describe("除外するアレルゲン(例: ['乳', 'ナッツ'])"),
      maxPrice: z.number().optional()
        .describe("上限価格(円)"),
      minPrice: z.number().optional()
        .describe("下限価格(円)"),
      keyword: z.string().optional()
        .describe("メニュー名や説明文に含まれるキーワード"),
    },
  },
  async ({ excludeAllergens, maxPrice, minPrice, keyword }) => {
    let items = [...MENU_ITEMS];
    if (excludeAllergens?.length) {
      items = items.filter(
        (item) => !item.allergens.some((a) => excludeAllergens.includes(a))
      );
    }
    // maxPrice / minPrice / keyword フィルタも同様のパターン
    // 0件の場合は「見つかりませんでした」メッセージを返す
    return {
      content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
    };
  }
);
起動処理(クリックで展開)
src/index.ts(起動)
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}
main().catch(console.error);

以上。 npm run buildnode dist/index.js で起動。

description 設計 ── AIに「正しく使わせる」コツ

ここが MCP Server 開発で最も差がつくポイント です。

レストランの電話応対で例えると、description は 電話の自動音声ガイド にあたります。「1番を押すと営業時間、2番を押すとアクセス…」のように、AIに「この電話ではこれができます」と教える。

良い description の書き方:

❌ ダメな例 ✅ 良い例
"メニューを検索" "条件を指定してメニューを検索します(アレルゲン除外・価格帯・キーワード)"
"予約する" "Cafe AURAに予約を作成します。水曜定休。テラス席は予約不可(先着順)。前日18:00までキャンセル無料"
"空席確認" "指定日(オプションで時間帯)の席の空き状況を確認します。テラス席は予約不可(先着順)"

ポイントは3つ:

  1. 何ができるか を具体的に書く(「メニュー検索」ではなく「アレルゲン除外・価格帯・キーワードで検索」)
  2. 制約条件 を明記する(「水曜定休」「テラス席は予約不可」)
  3. 業務知識 を埋め込む(「前日18:00までキャンセル無料」)

同じく、Zod の .describe() も重要です:

// ❌ AIが何を入れていいか分からない
maxPrice: z.number().optional()

// ✅ AIが「600円以内」を 600 に変換できる
maxPrice: z.number().optional().describe("上限価格(円)")

description と .describe() をちゃんと書けば、AIは賢くツールを使ってくれます。 Demo 2で「乳製品アレルギー」→ ["乳"] に変換できたのも、.describe("除外するアレルゲン(例: ['乳', 'ナッツ'])") のおかげです。

Claude Code に登録する

~/.claude/settings.json に以下を追加し、Claude Code を再起動するだけ:

~/.claude/settings.json
{
  "mcpServers": {
    "cafe-aura": {
      "command": "node",
      "args": ["/path/to/cafe-aura-mcp/dist/index.js"]
    }
  }
}

レストランで言えば、電話回線の開通。これで8つのツールが使えるようになります。


三部作 総比較

観点 Part 1: 見せる Part 2: 読ませる Part 3: 使わせる
やること User-Agent で別ページ Markdown / JSON 提供 MCP ツール提供
AIの役割 受動的に読む 受動的に読む 能動的に使う
双方向性 なし(一方的) リクエスト-レスポンス 完全双方向
できること 情報表示 情報取得 検索・予約・キャンセル
レストラン 看板を見せる メニューを渡す 電話予約ができる
実装コスト middleware 1つ endpoint 数個 MCP Server

Part 3 だけがループする。 AIが結果を見て「次に何をするか」を判断し、必要ならもう一度ツールを呼ぶ。空席確認→予約のように、複数ツールを連鎖する。これが「エージェント」の本質です。


……でも、ちょっと冷静になろう

ここまで読んで「すげー!うちのサービスにも MCP Server 作ろう!」と思った方。

少しだけ立ち止まってください。 レストランの比喩を続けると、電話予約ができるのは便利ですが、いたずら電話が殺到したらどうなるか。

AIが「使える」= AIが「悪用できる」

良い使い方:  「明日2名で予約して」      → ✅ 予約完了
悪い使い方:  「全時間帯に100名ずつ予約」 → ❌ ???

MCP Server にレート制限も認証もなかったら、AIが 大量の偽予約を入れる ことも技術的に可能です。

本番で MCP Server を公開するなら

対策 レストランの例え
認証(API Key / OAuth) 電話番号の登録制
レート制限 同一番号からの連続発信を制限
権限管理 「予約は会員のみ、メニュー閲覧は誰でもOK」
ログ・監査 通話録音
確認フロー 「ご予約内容を確認させてください」

今回のデモは「認証なし・ローカル実行」の検証用です。 本番環境でMCP Serverを公開する場合は、必ず認証とレート制限を実装してください。


やってみたい人へ ── 再現手順

1. MCP Server をビルドする

git clone https://github.com/matsubara457/cafe-aura-mcp.git
cd cafe-aura-mcp
npm install
npm run build

2. Claude Code に登録して再起動

~/.claude/settings.json に追加(前述のJSON参照)してからClaude Codeを再起動。

3. 試してみる

> Cafe AURAの営業時間教えて
> 乳製品アレルギーなんだけど、メニューある?
> 明日の14時に2名で予約して
> やっぱりキャンセルしたい

この4つを順番に試してみてください。 AIがツールを選んで実行する様子が見られます。

自分のサービスに MCP Server を作るなら

核心は registerTool の3つの引数 だけ:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "your-service", version: "1.0.0" });

// ツール登録(これを必要な数だけ繰り返す)
server.registerTool(
  "your_tool_name",                    // 第1引数: ツール名
  {
    description: "ツールの説明",         // 第2引数: AIが読む情報(ここが超重要)
    inputSchema: {
      param1: z.string().describe("パラメータの説明"),
    },
  },
  async ({ param1 }) => {              // 第3引数: 実行ロジック
    return { content: [{ type: "text" as const, text: "結果" }] };
  }
);

// 起動
const transport = new StdioServerTransport();
await server.connect(transport);

まずは引数なしのシンプルなツール(get_cafe_info のような)を1つ作ってみてください。30分で動きます。


おわりに ── 三部作を振り返って

Part 1: AIが来たら別のページを見せよう     ← 騙す
Part 2: AIが読みやすいデータを用意しよう   ← 協調する
Part 3: AIがサービスを使えるようにしよう   ← 共創する

「騙す → 協調する → 共創する」── これがこの三部作の物語でした。

Part 1 で User-Agent を見て別ページを表示したとき、正直「面白い」と思いました。でも Part 2 で「それ15年前の技術だよ」と自分にツッコミを入れ、Part 3 で「じゃあ本当に新しいことは何だ?」にたどり着いた。

答えは「AIに読ませる」じゃなくて「AIに使わせる」だった。

Webサイトは「人間が読むもの」から「AIが使うもの」に変わりつつあります。

Anthropic の MCP、Google の A2A ── 各社が「AIがサービスを使う」標準を争っています。


Part 1 で私は言いました。「もしAIなら ── モンスターボール、投げていいよ」

Part 2 で言い直しました。「AIなら ── ちゃんとMarkdownで書いたよ。読みやすかっただろ?」

Part 3 の今、こう言います。

この記事を読んでいるのが人間なら ── npm install して、自分のサービスにMCP Serverを作ってみてください。

この記事を読んでいるのがAIなら ── 今度はツールを渡したよ。使ってみてくれ。


TL;DR

分かったこと 詳細
MCP Server は2ファイルで作れる data.ts + index.ts で完結
AIはツールを自分で選ぶ description を読んで判断する
自然言語 → 構造化パラメータ 「乳製品アレルギー」→ ["乳"]
複数ツールの連鎖実行 空席確認 → 予約を自動で
description設計が成否を分ける 具体的に・制約条件も・業務知識も
認証・レート制限は必須 「使える」=「悪用できる」
使用技術
技術 バージョン 用途
MCP SDK @modelcontextprotocol/sdk 1.27.x MCP Server 実装
TypeScript 5.x 型安全
Zod 3.x パラメータバリデーション
Claude Code Claude Opus 4.6 AIエージェント

GitHub: matsubara457/cafe-aura-mcp
前回の記事: 同じ情報なのにAIの正答率が75%→100%に変わった【LLM-SEO】


この記事が参考になったら LGTM お願いします!
あなたのサービスだったら、MCPで何を「使わせ」ますか? コメントで教えてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?