MCP・AIコネクタ・人間承認の仕組みを実装目線で理解する


 

MCPの具体的なイメージを

最近、ClaudeやChatGPT、Cursorなどで「MCP」という言葉をよく見かけるようになりました。

説明を読むと、

「AIと外部ツールをつなぐための共通プロトコル」

と書かれていることが多いのですが、これだけでは実際にどういうコードを書けばよいのか、なかなかイメージしにくいものです。

そこで今回は、MCPを実装レベルまで掘り下げながら、そこから派生して理解できた次の3つを整理してみます。

  1. MCPは実際にどのように動いているのか
  2. AIからGmailなどを操作するときに出る「承認ポップアップ」の正体
  3. AI判定のあとに、人間の承認を挟んで処理を実行する方法

なるべく複雑な仕様の説明ではなく、PHPなどでWebシステムを作った経験があれば理解できるレベルでまとめます。


1. MCPとは結局何なのか

まず、MCPをものすごく簡単にすると、

「AIが利用できる関数や機能を、共通ルールで公開する仕組み」

と考えると理解しやすいです。

例えば、自分のサーバーにあるファイル一覧をAIから取得したいとします。

実際に必要なPHPの処理自体は、とても単純です。

function listServerFiles(): array
{
    return scandir('/home/example/data');
}

これだけなら普通のPHPです。

MCPを使う場合、この関数を、

「AIから使えるツールです」

と公開します。

イメージとしては、

Tool名:
list_server_files

説明:
サーバー上のファイル一覧を取得します

引数:
なし

という情報をAIへ公開します。

AIはこの情報を見て、

「ユーザーがサーバーのファイル一覧を知りたいと言っているから、list_server_filesを使えばよい」

と判断します。


AIはMCPサーバーへ自然言語を送っているわけではない

ここはMCPを理解するうえでかなり重要です。

ユーザーがClaudeに、

「サーバーにはどんなファイルがありますか?」

と聞いたからといって、その文章がそのままMCPサーバーへ送られるわけではありません。

まずClaude自身が文章を理解します。

そして、

この質問には
list_server_files
というToolを使えばいい

と判断します。

そのあとMCPサーバーには、概念的には次のような構造化された命令が送られます。

{
  "method": "tools/call",
  "params": {
    "name": "list_server_files",
    "arguments": {}
  }
}

つまり、

人間の自然言語
↓
AIが意味を理解
↓
使うToolを決定
↓
Tool名と引数をMCPへ送信
↓
PHPの関数が実行される

という流れです。

MCPサーバー側で自然言語を解析しているわけではありません。


MCP SDKは何をしているのか

PHPにはMCPサーバーを作るためのSDKがあります。

このSDKの役割を理解するには、Laravelをイメージすると分かりやすいです。

Laravelでは例えば、

Route::get('/users', [UserController::class, 'index']);

と書けば、

/users
↓
UserController
↓
index()

というルーティングが行われます。

MCPでも似たことが起きています。

list_server_files
↓
FileTools
↓
listServerFiles()

という振り分けをSDKが行います。

そのため、MCP SDKはLaravelでいう単なるRouterというより、

  • ルーティング
  • 引数の検証
  • Tool一覧の生成
  • Schema生成
  • エラー処理
  • MCP形式へのレスポンス変換

などをまとめて担当するフレームワークに近い存在です。

実際、SDKを使わずに自分で作ることもできます。

例えば、

switch ($toolName) {

    case 'list_server_files':
        return listServerFiles();

    case 'read_file':
        return readFile($arguments['filename']);

}

のように自分で振り分けてもよいわけです。

ただし本格的にMCP仕様へ対応しようとすると、Tool一覧、Schema、エラー形式、通信方式などの処理が増えてきます。

その定型作業をSDKが肩代わりしてくれます。


APIとMCPは何が違うのか

ここも最初は少し混乱しやすいところです。

普通のAPIの場合、

GET /users
POST /orders
GET /reports

のようなエンドポイントがあります。

しかしAIから見ると、

「どんなAPIがあるのか」
「何のためのAPIなのか」
「どんな引数が必要なのか」

を別途知る必要があります。

そこでOpenAPIやSwaggerなどの仕様書を使います。

以前からあるGPT Actionsもこの考え方でした。

AI側へOpenAPI Schemaを登録し、

「このAPIにはこのエンドポイントがある」

と教えておきます。

一方MCPでは、サーバー自身が、

「私はこのToolを提供しています」

という一覧を返します。

つまり簡単に言えば、

GPT Actions
=
AI側にAPIの取扱説明書を登録する

MCP
=
サーバー側が取扱説明書を持ち、
AIが接続したときに取得する

という違いがあります。

さらにMCPでは、

Tool一覧を取得する
Toolを呼ぶ
結果を返す

というところまで共通ルールになっています。

そのため、Claude、ChatGPT、Cursorなどが同じMCPサーバーへ接続しやすくなります。


APIをMCPへ自動変換することもできる

既存サービスにREST APIとOpenAPI仕様書があるなら、それをMCP Toolへ変換することもできます。

例えばOpenAPIに、

operationId:
getCustomer

description:
顧客情報を取得する

parameter:
id

とあれば、

MCP Tool:
get_customer(id)

へ機械的に変換できます。

つまり、

OpenAPI
↓
変換プログラム
↓
MCP Tool
↓
AI

という仕組みです。

ただし注意点があります。

APIを全部そのままMCPへ変換すると、

getUser
updateUser
deleteUser
refundPayment
deleteAccount

など、AIに使わせたくない操作まで公開されてしまう可能性があります。

そのため実際には、

OpenAPI
↓
自動変換候補
↓
人間が公開範囲を選択
↓
許可されたToolだけMCPへ公開

という設計が安全です。

読み取り系だけ公開し、削除や送信などは非公開にする、といった制御ができます。


2. Gmail送信時などに出る「承認ポップアップ」は何なのか

ChatGPTやClaudeなどからGmailを操作すると、

「このメールを送信しますか?」

といったシステム上のポップアップが出ることがあります。

ここで、

  • 今回だけ許可
  • 常に許可
  • 拒否

などを選べる場合があります。

このポップアップは、AIが文章として表示しているものではありません。

AIと外部サービスの間にある、システム側の「承認ゲート」です。

全体の流れはこうなります。

ユーザー
↓
「このメールを送って」

AI
↓
send_email を使うと判断

Tool Callを作成
↓
承認ゲート

「本当に実行していいですか?」
↓
ユーザーが承認
↓
Gmail API / Connector / MCP
↓
メール送信

重要なのは、AIは、

「メールを送るべきだ」

というTool Callを作るところまでだということです。

そのTool Callを実際に実行するかどうかは、AIの外側にあるシステムが決めています。


承認後にAIはもう一度判断しない

ここも重要です。

例えばAIが、

send_email(
    to = "user@example.com",
    subject = "資料について",
    body = "本日はありがとうございました"
)

というTool Callを作ったとします。

この時点ではまだ実行せず、

pending_approval

のような状態になっています。

ユーザーが「承認」を押したら、そのTool Callそのものが実行されます。

つまり、

AI
↓
Tool Call作成
↓
停止
↓
ユーザー承認
↓
そのTool Callを実行

です。

承認したあと、

「ユーザーが承認したようなので、もう一度AIにメール内容を考えさせよう」

とはしません。

もし再度AIに判断させると、件名や本文が微妙に変わる可能性があるからです。

承認した内容と実際に実行する内容を同じにするためにも、この構造は合理的です。


「常に許可」もAIの記憶ではない

「今後この操作を常に許可する」

という設定も、AIが覚えているというより、

send_email
→ always_allow

のような権限設定をシステム側が保存していると考えると分かりやすいです。

次回同じTool Callが発生したとき、

Tool Call
↓
Permissionチェック
↓
always_allow
↓
そのまま実行

となります。

つまり役割を分けると、

AI
=
何をしたいか決める

Permission System
=
実行していいか決める

Connector / MCP / API
=
実際に処理する

となります。


3. AI判定のあとに人間の承認を挟む方法

この仕組みは自分のWebサービスにも応用できます。

例えば、ユーザーがニュース記事を投稿するサービスがあるとします。

現在、

ユーザー投稿
↓
AI審査
↓
OK
↓
そのまま公開

となっているとします。

ここへ、

「AIはOKと言ったけれど、本当に公開しますか?」

という人間の承認を追加したい場合です。

設計としては、

ユーザー投稿
↓
AI審査
↓
OK
↓
承認待ち
↓
人間が確認
↓
承認
↓
公開

にします。


AIのOKを「公開命令」にしない

ここが一番重要です。

例えば現在、

$result = checkArticleWithAI($article);

if ($result === 'OK') {
    publishArticle($article);
}

となっているなら、

AIがOKを返した瞬間に公開されています。

これを、

$result = checkArticleWithAI($article);

if ($result === 'OK') {
    updateStatus(
        $article['id'],
        'pending_approval'
    );
}

とします。

つまり、

AI OK
≠
公開

AI OK
=
人間の承認へ進める

という考え方です。


DBには記事を先に保存しておく

例えば記事テーブルに、

id
title
body
status

があるとします。

投稿された時点では、

status = draft

または、

status = ai_reviewing

として保存します。

AI審査に合格したら、

status = pending_approval

へ変更します。

そして画面に、

AI審査に合格しました。

この記事を公開しますか?

[キャンセル]
[公開する]

というモーダルを表示します。

「公開する」を押したら、

POST /article/approve.php

などへ記事IDを送ります。

PHP側では、

$article = getArticle($articleId);

if ($article['status'] !== 'pending_approval') {
    exit('承認できない記事です');
}

publishArticle($articleId);

updateStatus(
    $articleId,
    'published'
);

とします。

これだけで、

AI判断
↓
承認待ち
↓
人間の判断
↓
実行

という構造が作れます。


状態管理をするとシステムが分かりやすくなる

例えば記事の状態を、

submitted
↓
ai_reviewing
↓
├─ ai_rejected
└─ pending_approval
       ↓
       ├─ human_rejected
       └─ published

と定義しておくと非常に分かりやすくなります。

AIが判断する部分と、人間が判断する部分と、実際に公開する部分が分離されるからです。

この考え方は記事投稿だけではありません。

例えば、

  • メール送信
  • データ削除
  • 設定変更
  • 返金
  • 注文
  • 契約変更
  • 外部公開

などにもそのまま応用できます。


DBを使わずに承認する方法もある

今回の記事では詳細は割愛しますが、DBに「承認待ち」を保存しない方式もあります。

例えば、

AI審査
↓
記事本文をブラウザ側に一時保持
↓
署名付きトークンを発行
↓
ユーザーが承認
↓
記事本文+トークンをPOST
↓
サーバーがトークンを検証
↓
公開

という方法です。

ブラウザ側でデータを保持する方法には、

  • JavaScript変数
  • hidden要素
  • sessionStorage
  • localStorage

などがあります。

ただしブラウザ側の情報はユーザーが変更できるため、改ざん防止が必要になります。

そのため署名付きトークンや記事本文のハッシュなどを使います。

一方、すでに記事用DBが存在するシステムなら、

status = pending_approval

を使う方が実装も管理も圧倒的に簡単です。


まとめ

今回MCPについて調べていく中で、AI連携の仕組みは意外と既存のWeb開発の考え方に近いことが分かりました。

MCPは難解なAI専用技術というより、

「AIが利用できる機能を、共通形式で公開する仕組み」

と捉えると理解しやすくなります。

例えば、

AI
↓
Toolを選ぶ
↓
MCP
↓
PHP関数 / API / DB

という構造です。

MCP SDKは、LaravelのRouterやController Dispatcherのように、Tool名から実際の処理へ振り分ける役割を持っています。

またAIがGmailなどを操作するときに表示される承認ポップアップは、AIそのものではなく、

AI
↓
Tool Call
↓
Permission Gate
↓
外部サービス

という別レイヤーで実装されています。

さらに同じ考え方を自分のシステムへ持ち込めば、

AI判定
↓
承認待ち
↓
人間承認
↓
実行

という安全なAI自動化を作れます。

個人的には、この「AIに判断させる部分」と「現実の処理を実行する部分」を分離する考え方が、今後AIをシステムへ組み込む上でかなり重要になると感じました。

AIに全部を任せるのではなく、

AI
=
判断・提案

システム
=
権限・状態管理

人間
=
必要な場面で最終承認

という役割分担です。

MCPを理解すると、単にAIへ外部機能を追加するだけでなく、

「AIを既存システムの中にどう安全に組み込むか」

という設計そのものが見えやすくなります。

WEBプログム、WEBデザインなどの制作については、以下を御覧ください。

WEBプログム、WEBデザインなどの制作