電通総研 テックブログ

電通総研が運営する技術ブログ

AI初心者がMCPベースのエージェントを2日で自作し、仕組みを理解する

はじめに

最近、AWS の AI Practitioner を取得した金融IT本部 2年目の坂江 克斗です。
今回は、XI 本部の佐藤悠さんに協力していただきながら執筆した記事となります。

最近、「AI エージェント」という言葉をよく耳にするようになりましたが、正直なところ、私自身はエージェントが何をしているのか全く分かっていませんでした。
そこで今回、AI初心者の自分なりに理解するため、MCPベースのエージェントを 専用のライブラリを使わずに自作し、その仕組みを理解してみることにしました。

本記事では、AI 初心者の方向けに、以下のイメージを掴んでもらえればと思います。

  • AIエージェントが「どのような仕組み」なのか
  • MCP ホスト / MCP クライアント / MCP サーバの役割分担

学習フロー

エージェントの概要を理解し、実装するにあたり大まかに以下の流れで進めていきました。

  1. 佐藤さんからMCPやエージェントに関する概要を聞き、ソースと併せて理解を深める(1日目)
  2. MCPの公式ドキュメントを確認し、機能を絞ってエージェント(MCP ホスト / MCP クライアント / MCP サーバ / LLMとの連携)を実装する(1-2日目)

佐藤さんが執筆した MCP に関する記事も、あわせてご覧ください。

エージェントとは

ざっくりAIを考える

私自身、モデル構造や学習アルゴリズムを理解しているわけではないので、ここでは最低限持っておきたい概念レベルに絞って整理します。

AI とは、人間の知能的な振る舞いを模倣することを目的とした概念であり、現在一般ユーザーが日常的に触れている AI の多くは、LLM(Large Language Model)を中心としたシステムになっています。
LLM の内部構造を詳細に追い始めると一気に難しくなりますが、本質的には非常に多くのパラメータを持つ巨大な関数、つまり、「入力データを与えると、出力データが返ってくる」 ただそれだけの存在です。(ニューラルネットワークでは「層」という概念を用いてパラメータを調整していますが、外から見ると最終的には数式で表現できる関数として扱えます)

重要なのは、LLM 自身が自動的に外部 API を呼び出したり、何かを実行しているわけではないという点です。
ただし、その関数が持つパラメータ量と複雑さが人間の想像をはるかに超えているため、結果として「考えているように見える」出力が得られている、というわけです。

AIの弱み

AI が関数である以上、当然ながら限界も存在します。
それは、学習時(関数の各パラメータの調整時)に与えられていない情報については、単体の LLM だけでは原則として扱うことができません。
そのため開発者としては、「AI が知らない情報を、外部のデータを取得して回答に含めてほしい」と考えるようになります。
この課題を解決するために登場した仕組みが、RAG(Retrieval-Augmented Generation:検索拡張生成)です。

RAG(Retrieval-Augmented Generation)

RAG(Retrieval-Augmented Generation)は、「AIが、質問に関連する情報を参照しながら、回答を生成しているように見せる」仕組みです。
この仕組みでは、従来の Web 検索や検索エンジンの裏側でも使われてきた ベクトル検索 が利用されています。

ベクトル検索の細かい仕組みや実装は難しいですが、ざっくりとWeb検索をした際に関連サイトが取得できるイメージを持っておけば問題ありません。

大まかな実装イメージは、次のようなシンプルな流れになります。

まず、ユーザがプロンプト(質問文など)を入力すると、その内容をもとにベクトル検索を行い、あらかじめ用意しておいたデータベースの中から、プロンプトに関連する情報を取得します。(検索に適したプロンプトに変換するために、事前に LLM を利用する方式も存在します)
次に、その取得した情報をプロンプトとあわせて LLM に渡すことで、より文脈に沿った、精度の高い回答を生成できるようになります。

一方で、RAG はあくまで 「検索を拡張する仕組み」 であり、関連する情報を用いて回答を生成することはできますが、API を呼び出したり何らかの処理やタスクを実行したりすることはできません。
そこで登場したのが Function Calling です。

Function calling

Function callingは、OpenAI が発表した仕組みで、「AI が外部システムと連携し、学習範囲外のデータ取得や API 実行などの処理を行えるように見せる」ための方法です。
ただし、最初に述べたとおり、AI 自体は API を呼び出すことはできません。そのため、API を実際に呼び出す処理はアプリケーション側で実装する必要があります。
イメージとしては以下の図に示す、クライアント(アプリ)・サーバ(LLM)間での処理フローとなります。

初めのLLMへのリクエストにおいて、LLMに使用すべきツール(機能)を選択させ、その機能をクライアント側で実行したのちに、結果とともに再度LLMに回答をもらう流れとなります。仕組み自体は、「API 呼び出しをうまく抽象化しているだけ」とも言えますが、この発想を形にした点が非常に面白いと感じました。

一方で、Function callingには弱点もあります。それは、ツール呼び出しの実装がクライアント側に閉じてしまうという点です。つまり、実装が各アプリごとに依存する課題を抱えています。
この課題を解決するために登場したのが MCP(Model Context Protocol) です。

Model Context Protocol(MCP

Model Context Protocol(MCP) は Claude を開発している Anthropic 社が定義したプロトコルです。
Function calling と同様に、「AI が外部システムと連携して、処理・回答しているように見せる」という点は同じですが、ツール呼び出しの方法そのものを標準化したことが大きな特徴です。
アーキテクチャは以下のようになります。先ほどのクライアントとなるアプリが、MCPホストとMCPサーバに分割された形になります。

Function Callingに比べて少しリソースが増えました。
全体の処理フローは以下のようになり、MCPサーバがツールの実行を抽象化していることが分かります。

この構成の強みは、MCP ホスト / MCPクライアント / MCP サーバという役割を標準化したことにあります。

例えば、サービス提供者が MCP サーバを用意しておけば、利用者はその実装の詳細を意識せず、他の MCP サーバと同じ感覚で利用できます。

HTTP が存在しなかった世界では、サービスごとに独自のプロトコルAPI を実装する必要がありました。実際には、HTTP という共通プロトコルがあるからこそ、私たちは意識せずに様々な Web サービスを利用できています。
MCP も、それと同じ文脈でサービスの提供者・使用者の目線で考えると分かりやすいかと思います。

AIエージェント

Function Calling や MCP を用いることで、LLM が本来単体では実行できないタスクについても、外部サービス(ツール)による処理の実行や、外部サービスにより取得したデータを文脈として取り込みながら回答を生成できるようになります。
このように、外部のサービスやデータと連携した処理を基に振る舞う仕組み全体を、一般に AI エージェント と呼びます。

ただし、実態としてはWeb アプリケーションとしてのクライアント・サーバ構成で動いているという点を理解しておくことが重要です。

エージェントの作成(MCPベース)

前提

ということで、実際にMCPをベースにしたエージェントを実装してみます。
本記事の目的は、MCPの推奨構成をそのまま再現することではなく、各コンポーネントがどんな順序で、どんなデータをやり取りしているかを理解することです。
そのため、公式ドキュメントで扱われているいくつかの要素を省略し、以下の方針で実装を行いました。

  • 目標
    • エージェントに「(アメリカの都市名)の直近の天気」を必要とする質問を尋ねると、実際の天気予報 API を基にした回答を返してくれること
  • 実装方針
    • MCPのバージョン
    • 機能面
      • MCP サーバ
        • Prompts / Resources は省略して、Tools のみを実装し、外部 API(天気予報 API)の呼び出しを主に実装
        • ローカルでHTTPサーバとして起動
      • MCP クライアント
        • Roots / Sampling / Elicitation は省略し、MCP サーバに対する Tools の呼び出しを主に実装
        • MCPホストにクラスとして内包される
      • MCPホスト
        • UIとしてCLIでのstdin / stdoutを使用(ユーザとの入出力はコマンドラインベース)
        • ローカルで起動
    • 通信面
      • HTTP ベースでの実装
      • SSEやセッション管理(stateful)を省略し、ステートレスに実装
      • 認証は実装しない
    • 使用技術
      • LLM:Claude
      • MCPホスト / MCPクライアント /MCP サーバー:JavaScript(TypeScript)での実装

実装コードはGitHubで公開しています。詳細が気になる方は mcp-sample を参照してください(mcp系のライブラリを使用せず自作したこともあり、設計・実装ともに粗い点はご容赦ください)。

実装方針

MCP の公式ドキュメントをただ読むだけで、そのままコードに落とし込むのは難しく感じたため、今回は以下の手順で実装をします。

  1. 先に発生するすべてのHTTP通信を書き出し
  2. 各通信それぞれの HTTPヘッダ と ボディ(JSON-RPC) を整理
  3. 通信内容から逆算して実装。

通信内容の整理

JSON-RPC

MCPでは JSON-RPC 2.0 がメッセージ形式として採用されています。
以下が JSON-RPC の基本的な形式です。

  • リクエスト
{
    "jsonrpc": "2.0",
    "id": "number",
    "method": "string",
    "params": { object }
}
  • レスポンス
{
    "jsonrpc": "2.0",
    "id": "number",
    "result": { object }
}
  • レスポンス(エラー)
{
    "jsonrpc": "2.0",
    "id": "number",
    "error": {
        "code": "number", // -32768 ~ -32000 are reserved, 
        "message": "string",
        "data": "primitive or structed value"
    }
}

今回はこの JSON-RPC を HTTP リクエストのボディに乗せて送受信する形で実装します。
つまり、HTTP はあくまで通信経路として利用し、実際の処理内容やメソッドの表現は JSON-RPC によって行う構成になります。

TCPの上でHTTPが成り立つように、HTTPのボディの上でJSON-RPC形式によるやり取りを行うイメージです

MCP

本来のMCPの通信では、公式ドキュメントのシーケンス図に記載されているとおり、Server-Sent Events(SSE)やセッション管理を含む構成が想定されています。
しかし、今回の構成では理解を優先するため、SSE やセッション管理は割愛し、公式ドキュメントの構成を簡略化した以下の形で MCP によるやり取りを行います。
今回はセッションを持たないステートレスな HTTP 通信として実装しているため、通信の終了フェーズを明示的に実装する必要はありません。

今回想定するMCPリクエスト・レスポンス

通信の全体像が把握できたところで、公式ドキュメントに記載のライフサイクル章トランスポート章を参考にし、各フェーズにおいてどのようなデータが送受信されるのかを整理します。

initialize
  • リクエスト
    • ヘッダ
Content-Type: application/json
Accept: application/json
- ボディ
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": {
      "name": "ExampleClient",
      "version": "1.0.0",
      "description": "An example MCP client application"
    }
  }
}
  • レスポンス
    • ステータス: 200
    • ボディ
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "tools": {
        "listChanged": false
      }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "version": "1.0.0",
      "description": "An example MCP server providing tools and resources"
    }
  }
}
notifications/initialized
  • リクエスト
    • ヘッダ
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25
- ボディ
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}
  • レスポンス
    • ステータス: 202
    • ボディ:空
tools/list
  • リクエスト
    • ヘッダ
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25
- ボディ
{
  "jsonrpc": "2.0",
          "id": 2,
  "method": "tools/list",
  "params": {}
}
  • レスポンス
    • ステータス: 200
    • ボディ
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "latitude": { "type": "number" },
            "longitude": { "type": "number" }
          },
          "required": [ "latitude", "longitude" ]
        }
      }
    ]
  }
}
tools/call
  • リクエスト
    • ヘッダ
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25
- ボディ
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "longitude": "-94.0",
      "latitude": "40.71"
    }
  }
}
  • レスポンス
    • ステータス: 200
    • ボディ
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
      }
    ]
  }
}

このようにデータの流れをイメージできたため、あとはこの流れ通りに動作するよう MCPホスト / MCPクライアント / MCPサーバ を実装していきました。
その結果が、先ほど紹介したリポジトリの状態となります。

実装を進める中で、特に詰まった点は以下の2点です。

  • LLMがJSONを返却する際、どうしてもコードブロックを含めてしまう点
    • システムプロンプトによる制御では完全に回避できなかったため、文字列を整形する処理を実装し対処。
  • 「直接天気を調べて」とは指示せず、「天気をもとにアドバイスしてほしい」といった依頼をした場合に、LLMがツールを選択せず、「その情報を調べてもよいですか?」と確認してくるケースがあった点
    • 「回答に必要な処理が存在する場合は、必ずツール呼び出しのみを返すこと」を明示したプロンプトで対処。

検証

本章では、前章で整理した通信設計が、実際の実装でもその通りに動作しているかを確認します。
初めにローカル環境でMCPサーバーを起動します。

PS C:\Users\katsu\Documents\github\mcp-sample\server> npm run start

> mcp-server@1.0.0 start
> node dist/index.js

MCP server listening on http://127.0.0.1:8080/mcp

次に、同じくローカル環境でMCPホストを起動します(MCPホストはプロセス内部でMCPクライアントクラスのインスタンスを生成・管理しています)。
起動後はユーザーの入力を促すメッセージが出るので、依頼文(ユーザプロンプト)を入力します。

PS C:\Users\katsu\Documents\github\mcp-sample\host> npm run start

> mcp-host@1.0.0 start
> node --env-file=.env dist/index.js

##################################################################
------------ initialize request ------------
Content-Type: application/json
Accept: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": {
      "name": "weather",
      "version": "0.1.0",
      "description": "Weather MCP Client with Stateless and Streamable HTTP and non SSE"
    }
  }
}

------------ initialize response ------------
status: 200

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "tools": {
        "listChanged": false
      }
    },
    "serverInfo": {
      "name": "weather",
      "version": "0.1.0",
      "description": "Sample MCP Server with Stateless and Streamable HTTP and non SSE"
    }
  }
}
##################################################################
------------ notifications/initialized request ------------
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

------------ notifications/initialized response (no body) ------------
status: 202

##################################################################
------------ tools/list request ------------
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}

------------ tools/list response ------------
status: 200

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "description": "Get weather forecast for US coordinates using NWS API.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "latitude": {
              "type": "number"
            },
            "longitude": {
              "type": "number"
            }
          },
          "required": [
            "latitude",
            "longitude"
          ]
        }
      }
    ]
  }
}
##################################################################
# 質問してみましょう
明日夜のニューヨークの天気を基に、服装のアドバイスをしてください。今は半袖短パンを想定しています。

##################################################################
------------ LLM request ------------
Content-Type: application/json
x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
anthropic-version: 2023-06-01

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1000,
  "system": "\nYou are a tool-routing assistant.\n\nABSOLUTE RULES:\n- You MUST respond in Japanese only.\n- Output MUST be a single valid JSON object.\n- Do NOT use Markdown.\n- Do NOT use code fences or triple backticks.\n- The first character MUST be \"{\" and the last character MUST be \"}\".\n- Do NOT include any text outside the JSON object.\n",
  "messages": [
    {
      "role": "user",
      "content": "\nYou will be given:\n1) A list of available tools (JSON)\n2) A user request\n\nDecide whether a tool is needed.\n\nRules:\n- Use a tool only if it materially improves correctness or usefulness.\n- If multiple tools could work, pick exactly ONE.\n- Never invent tools or parameters.\n- If required parameters are missing, ask ONE concise follow-up question.\n- If answering accurately to user request requires external or real-world data, you MUST select the appropriate tool and make a tool_call.\n\nOutput format (JSON only):\n\nTool call:\n{\n  \"type\": \"tool_call\",\n  \"tool\": \"<tool_name>\",\n  \"arguments\": { },\n  \"reason\": \"<short reason>\"\n}\n\nDirect answer:\n{\n  \"type\": \"direct_answer\",\n  \"answer\": \"<answer>\",\n  \"reason\": \"<short reason>\"\n}\n\nTools (JSON):\n[\n  {\n    \"name\": \"get_weather\",\n    \"description\": \"Get weather forecast for US coordinates using NWS API.\",\n    \"inputSchema\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"latitude\": {\n          \"type\": \"number\"\n        },\n        \"longitude\": {\n          \"type\": \"number\"\n        }\n      },\n      \"required\": [\n        \"latitude\",\n        \"longitude\"\n      ]\n    }\n  }\n]\n\nUser request:\n明日夜のニューヨークの天気を基に、服装のアドバイスをしてください。今は半袖短パンを想定しています。\n"
    }
  ]
}

------------ LLM response ------------
status : 200

{
  "model": "claude-sonnet-4-5-20250929",
  "id": "msg_0144BrLAY9HUWMcHmgopBxpp",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "```json\n{\n  \"type\": \"tool_call\",\n  \"tool\": \"get_weather\",\n  \"arguments\": {\n    \"latitude\": 40.7128,\n    \"longitude\": -74.0060\n  },\n  \"reason\": \"ニューヨークの明日夜の天気予報を取得して、適切な服装アドバイスを提供するため\"\n}\n```"
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 481,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 0,
      "ephemeral_1h_input_tokens": 0
    },
    "output_tokens": 104,
    "service_tier": "standard"
  }
}
##################################################################
------------ tools/call request ------------
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2025-11-25

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "latitude": 40.7128,
      "longitude": -74.006
    }
  }
}

------------ tools/call response ------------
status: 200

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Forecast for 40.7128, -74.006:\n\nTonight:\nTemperature: 13°F\nWind: 14 to 22 mph N\nMostly Cloudy\n---\nSunday:\nTemperature: 25°F\nWind: 17 to 22 mph N\nMostly Cloudy\n---\nSunday Night:\nTemperature: 16°F\nWind: 14 to 17 mph NW\nMostly Cloudy\n---\nMonday:\nTemperature: 32°F\nWind: 14 mph NW\nSunny\n---\nMonday Night:\nTemperature: 16°F\nWind: 8 to 12 mph NW\nMostly Clear\n---\nTuesday:\nTemperature: 31°F\nWind: 9 mph W\nSunny\n---\nTuesday Night:\nTemperature: 22°F\nWind: 6 to 10 mph W\nPartly Cloudy\n---\nWednesday:\nTemperature: 30°F\nWind: 9 mph NW\nMostly Sunny\n---\nWednesday Night:\nTemperature: 17°F\nWind: 10 mph NW\nMostly Clear\n---\nThursday:\nTemperature: 29°F\nWind: 10 mph NW\nSunny\n---\nThursday Night:\nTemperature: 15°F\nWind: 10 mph NW\nPartly Cloudy\n---\nFriday:\nTemperature: 30°F\nWind: 8 to 12 mph W\nPartly Sunny then Slight Chance Light Snow\n---\nFriday Night:\nTemperature: 18°F\nWind: 12 to 15 mph W\nChance Light Snow\n---\nSaturday:\nTemperature: 22°F\nWind: 16 to 21 mph NW\nChance Light Snow then Mostly Sunny\n---"
      }
    ]
  }
}

##################################################################
------------ LLM request ------------
Content-Type: application/json
x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
anthropic-version: 2023-06-01

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1000,
  "system": "\nYou are a Japanese assistant.\n\nABSOLUTE RULES:\n- You MUST respond in Japanese only.\n",
  "messages": [
    {
      "role": "user",
      "content": "\nYou will be given:\n1) A list of API result\n2) A user request\n\nAnswer user request with API result.\nDo not mention that you are referring to the API results.\n\nAPI Result (JSON):\n{\n  \"content\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Forecast for 40.7128, -74.006:\\n\\nTonight:\\nTemperature: 13°F\\nWind: 14 to 22 mph N\\nMostly Cloudy\\n---\\nSunday:\\nTemperature: 25°F\\nWind: 17 to 22 mph N\\nMostly Cloudy\\n---\\nSunday Night:\\nTemperature: 16°F\\nWind: 14 to 17 mph NW\\nMostly Cloudy\\n---\\nMonday:\\nTemperature: 32°F\\nWind: 14 mph NW\\nSunny\\n---\\nMonday Night:\\nTemperature: 16°F\\nWind: 8 to 12 mph NW\\nMostly Clear\\n---\\nTuesday:\\nTemperature: 31°F\\nWind: 9 mph W\\nSunny\\n---\\nTuesday Night:\\nTemperature: 22°F\\nWind: 6 to 10 mph W\\nPartly Cloudy\\n---\\nWednesday:\\nTemperature: 30°F\\nWind: 9 mph NW\\nMostly Sunny\\n---\\nWednesday Night:\\nTemperature: 17°F\\nWind: 10 mph NW\\nMostly Clear\\n---\\nThursday:\\nTemperature: 29°F\\nWind: 10 mph NW\\nSunny\\n---\\nThursday Night:\\nTemperature: 15°F\\nWind: 10 mph NW\\nPartly Cloudy\\n---\\nFriday:\\nTemperature: 30°F\\nWind: 8 to 12 mph W\\nPartly Sunny then Slight Chance Light Snow\\n---\\nFriday Night:\\nTemperature: 18°F\\nWind: 12 to 15 mph W\\nChance Light Snow\\n---\\nSaturday:\\nTemperature: 22°F\\nWind: 16 to 21 mph NW\\nChance Light Snow then Mostly Sunny\\n---\"\n    }\n  ]\n}\n\nUser request:\n明日夜のニューヨークの天気を基に、服装のアドバイスをして ください。今は半袖短パンを想定しています。\n"
    }
  ]
}

------------ LLM response ------------
status : 200

{
  "model": "claude-sonnet-4-5-20250929",
  "id": "msg_011ykwGSo5qRzEHTQpD9UV9G",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "明日夜のニューヨークの天気は以下の通りです:\n\n**日曜日の夜**\n- 気温:16°F(約-9°C)\n- 風:14〜17 mph 北西 の風\n- 天候:ほぼ曇り\n\n**服装のアドバイス:**\n\n半袖短パンは**絶対に避けてください**。この気温では命に関わる危険性があり ます。\n\n以下の装備が必要です:\n\n1. **厚手の冬用コート**(ダウンジャケットやウールコートなど)\n2. **重ね着**(長袖のイン ナー、セーター、フリースなど複数枚)\n3. **厚手の長ズボン**(ジーンズやウールパンツに加えて、可能であればレギンスなどの下着も)\n4. **防寒アクセサリー**:\n   - 帽子(耳まで覆えるもの)\n   - 手袋(厚手のもの)\n   - マフラー\n   - 厚手の靴下\n5. ** 防風性のある上着**(風が強いため)\n\n氷点下の極寒の天候ですので、肌の露出は最小限に抑え、しっかりと防寒対策をしてお出かけく ださい。"
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 713,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 0,
      "ephemeral_1h_input_tokens": 0
    },
    "output_tokens": 382,
    "service_tier": "standard"
  }
}
##################################################################
明日夜のニューヨークの天気は以下の通りです:

**日曜日の夜**
- 気温:16°F(約-9°C)
- 風:14〜17 mph 北西の風
- 天候:ほぼ曇り

**服装のアドバイス:**

半袖短パンは**絶対に避けてください**。この気温では命に関わる危険性があります。

以下の装備が必要です:

1. **厚手の冬用コート**(ダウンジャケットやウールコートなど)
2. **重ね着**(長袖のインナー、セーター、フリースなど複数枚)
3. **厚手の長ズボン**(ジーンズやウールパンツに加えて、可能であればレギンスなどの下着も)
4. **防寒アクセサリー**:
   - 帽子(耳まで覆えるもの)
   - 手袋(厚手のもの)
   - マフラー
   - 厚手の靴下
5. **防風性のある上着**(風が強いため)

氷点下の極寒の天候ですので、肌の露出は最小限に抑え、しっかりと防寒対策をしてお出かけください。

上記の結果から、想定通りの通信が発生し、回答も外部サービスを使用した結果が含まれていることが確認できました。

まとめ

外部のサービスやデータを用いて、あたかもLLM自身が処理を行ってタスクを実施し、回答しているように見せる仕組み全体を、AIエージェントと呼びます。
その中で、外部サービス(ツール)呼び出しの形式を標準化したものがFunction CallingやMCPです。

特にMCPで定義されたコンポーネントの役割は以下のように分類できます。

実体がWebアプリと同じようなクライアント・サーバ構成である以上、MCP サーバの配置場所や実行させる処理内容によっては、セキュリティや監査の扱いが非常に重要になります。例えば、ローカルで実行した MCP サーバが意図せず内部ファイルに干渉してしまうケースや、外部に公開した MCP サーバがセキュリティ攻撃にさらされる可能性などが考えられます。
これは、これまでの Web サービスと本質的には同じであるため、適切なアーキテクチャ設計とセキュリティポリシーの策定が、今後ますます定式化されていく領域だと感じています。

おわりに

佐藤さんの助けもあり、これまで実態がつかめていなかった「AI エージェント」という概念を掴みつつ、実装しながら理解を深められたのはとても面白い体験でした。
このエージェントをさらに抽象化した仕組みとして Strands が登場しているので、次はそちらの実装を検証してみたいと思います。
今後も抽象化のレイヤーはどんどん高まっていくはずなので、流れに取り残されないためにも、AI 関連技術の基礎知識を学びつつ、使用者側としても継続的に研鑽していきます。

私たちは一緒に働いてくれる仲間を募集しています!

電通総研 キャリア採用サイト 電通総研 新卒採用サイト

執筆:@sakae.katsuto
レビュー:@yamashita.tsuyoshi
Shodoで執筆されました