Upgrade to Pro — share decks privately, control downloads, hide ads and more …

たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server wit...

たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP

2025/06/28 開催の「PHP Conference Japan 2025」(https://phpcon.php.gr.jp/2025/ )の LT 資料です。

詳細:https://fortee.jp/phpcon-2025/proposal/d85fb877-e734-43a4-a529-9aea783eeea5

Avatar for Shohei Okada

Shohei Okada

June 28, 2025
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. 外界とのやりとりはアプリケーションの役割 LLM LLM アプリケー ション 今日の東京の天気は? ツールとして 天気情報システムが使えます。 ユーザーが 「今日の東京の天気は?」

    と聞いています。 天気情報システムに 2025 年 6 月 28 日の東京の天気を問い合わせま す。 天気情報 システム 2025-06-28, 東京 晴れ
  2. 外界とのやりとりはアプリケーションの役割 LLM LLM アプリケー ション 今日の東京の天気は? ツールとして 天気情報システムが使えます。 ユーザーが 「今日の東京の天気は?」

    と聞いています。 天気情報システムに 2025 年 6 月 28 日の東京の天気を問い合わせま す。 天気情報 システム 2025-06-28, 東京 晴れ
  3. MCP で連携する場合 各 LLM アプリケーションと MCP の関係 MCP MCP Client

    MCP Client MCP Client MCP Server MCP Server : : Claude Desktop Copilot Chat Gemini CLI 1 種類のサーバで複数種のクライアントに対応できる
  4. PSR cf.) PHP における FW と PSR の関係 Logger DI

    Container : 1 種類の実装を複数の FW から利用できる :
  5. • 外部のシステム等とやりとりをするために 必ず MCP が必要、というわけではない • あくまで 1 企業が提唱したプロトコルに過ぎない •

    連携のために各 LLM アプリケーションが独自で 仕組みを実装したってよい 知っておくとよいこと
  6. Composer 未使用 = Web アプリケーション FW や  MCP SDK などを一切使わない

     素 PHP のみ 実装したものがこちら <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } 約 100 行
  7. MCP サーバ(Streamable HTTP Transport)の仕様 • 受け取るのは /mcp への POST のみ

    • やりとりするデータは JSON RPC 2.0 に準拠 最小限のMCP サーバを実装するために • 1 つの tool(日の出時刻)だけを提供 • 4 種類の処理を実装するだけで OK • 入力値の検証やエラーハンドリング等は行わない 実装説明
  8. MCP サーバ(Streamable HTTP Transport)の仕様 • 受け取るのは /mcp への POST のみ

    • やりとりするデータは JSON RPC 2.0 に準拠 最小限のMCP サーバを実装するために • 1 つの tool(日の出時刻)だけを提供 • 4 種類の処理を実装するだけで OK • 入力値の検証やエラーハンドリング等は行わない 実装説明
  9. <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data =

    match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } 実装説明 - リクエストハンドリング HTTPメソッドの検証などはしない。 なぜなら最小限だから。 ※真似する場合は自己責任で
  10. MCP サーバ(Streamable HTTP Transport)の仕様 • 受け取るのは /mcp への POST のみ

    • やりとりするデータは JSON RPC 2.0 に準拠 最小限のMCP サーバを実装するために • 1 つの tool(日の出時刻)だけを提供 • 4 種類の処理を実装するだけで OK • 入力値の検証やエラーハンドリング等は行わない 実装説明
  11. JSON-RPC 2.0 { jsonrpc: "2.0"; id: string | number; method:

    string; params?: { [key: string]: unknown; }; } { jsonrpc: "2.0"; id: string | number; result?: { [key: string]: unknown; } error?: { code: number; message: string; data?: unknown; } } Request Response Notifications は今回使わないので省略
  12. <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data =

    match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } 実装説明 - JSON RPC リクエストのパース $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); json_decode は必ず成功するものとする。 なぜなら最小限だから。
  13. 実装説明 - JSON RPC レスポンス <?php $requestBody = file_get_contents('php://input'); $parsedBody

    = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); $parsedBody には必ず id が存在するものとする。 なぜなら最小限だから。
  14. MCP サーバ(Streamable HTTP Transport)の仕様 • 受け取るのは /mcp への POST のみ

    • やりとりするデータは JSON RPC 2.0 に準拠 最小限のMCP サーバを実装するために • 1 つの tool(日の出時刻)だけを提供 • 4 種類の処理を実装するだけで OK • 入力値の検証やエラーハンドリング等は行わない 実装説明
  15. 実装説明 <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data

    = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry
  16. 実装説明 <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data

    = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry 固定値を返すだけ
  17. 実装説明 - initialize <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody,

    true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry
  18. 実装説明 - initialize function getInitialInfo(): array { return [ 'protocolVersion'

    => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; }
  19. 実装説明 - initialize function getInitialInfo(): array { return [ 'protocolVersion'

    => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } tool のみを提供
  20. 実装説明 - initialize function getInitialInfo(): array { return [ 'protocolVersion'

    => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } 変更を通知しない
  21. 実装説明 - notifications/initialized <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody,

    true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry クライアントの初期化が成功したら送られてくる
  22. 実装説明 - tools/list <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody,

    true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry 利用可能なツールの情報を提供
  23. function listTools(): array { return [ 'tools' => [ [

    'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], // (中略) ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } 実装説明 - tools/list
  24. function listTools(): array { return [ 'tools' => [ [

    'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], // (中略) ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } 実装説明 - tools/list
  25. 実装説明 - tools/call <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody,

    true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry
  26. 実装説明 - tools/call <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody,

    true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; $parsedBody は常に正しい形式であるとする。 なぜなら最小限(ry 今回 tool は 1種類だけ なので決め打ち
  27. 実装説明 - tools/call - get_sunrise_time <?php $requestBody = file_get_contents('php://input'); $parsedBody

    = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。 ', ]], 'isError' => true, ]; }
  28. 実装説明 - tools/call - get_sunrise_time <?php $requestBody = file_get_contents('php://input'); $parsedBody

    = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。 '; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。 '; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ];
  29. Composer 未使用 = Web アプリケーション FW や  MCP SDK などを一切使わない

     素 PHP のみ 実装説明、以上 <?php $requestBody = file_get_contents('php://input'); $parsedBody = json_decode($requestBody, true); $data = match ($parsedBody['method']) { 'initialize' => getInitialInfo(), 'notifications/initialized' => http_response_code(202) && exit(), 'tools/list' => listTools(), 'tools/call' => getSunriseTime( (float)$parsedBody['params']['arguments']['latitude'], (float)$parsedBody['params']['arguments']['longitude'], $parsedBody['params']['arguments']['date'] ?? null, ), }; header('Content-Type: application/json'); echo json_encode([ 'jsonrpc' => '2.0', 'id' => $parsedBody['id'], 'result' => $data, ]); exit(); function getInitialInfo(): array { return [ 'protocolVersion' => '2025-03-26', 'capabilities' => [ 'tools' => [ 'listChanged' => false, ], ], 'serverInfo' => [ 'name' => 'date sun info server', 'version' => '1.0.0', ], ]; } function listTools(): array { return [ 'tools' => [ [ 'name' => 'get_sunrise_time', 'description' => '与えらた地点の緯度経度と日付から、その地点の日の出時刻をUTCで取得します。', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'latitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の北緯。南緯は負の値で表します。', ], 'longitude' => [ 'type' => 'number', 'description' => '日の出時刻を知りたい地点の東経。西経は負の値で表します。', ], 'date' => [ 'type' => 'string', 'description' => '日の出時刻を知りたい日付。YYYY-MM-DD形式で指定します。デフォルトは今日の日付です。', ], ], 'required' => ['latitude', 'longitude'], ], ], ], ]; } function getSunriseTime(float $latitude, float $longitude, ?string $date = null): array { $timestamp = $date ? strtotime($date) : time(); if ($timestamp === false) { return [ 'content' => [[ 'type' => 'text', 'text' => '日付の形式が不正です。', ]], 'isError' => true, ]; } $sunriseTime = date_sun_info($timestamp, $latitude, $longitude)['sunrise']; if ($sunriseTime === false) { $text = '1日中日が昇りません(極夜)。'; } else if ($sunriseTime === true) { $text = '1日中日が昇ったままです(白夜)。'; } else { $text = '日の出時刻は' . date('H:i', $sunriseTime) . '(UTC)です。'; } return [ 'content' => [[ 'type' => 'text', 'text' => $text, ]], 'isError' => false, ]; } 約 100 行