公開日 2026-04-26

PHPでMCPサーバー入門(公式PHP SDK + STDIO)

公式PHP SDKを使って最小のMCPサーバーを作り、STDIOとInspectorで動作確認する。

目次

  1. 1. ゴールと前提
  2. 2. 新規デモ環境を作る
  3. 3. 公式 PHP SDK を入れる
  4. 4. 最小 Tool と server.php を作る
  5. コードのポイント
  6. 5. STDIO server として起動する
  7. 6. Inspector で Tool を確認する
  8. 7. 詰まりどころと次の一歩

公式 PHP SDK を使って最小の MCP サーバーを作り、STDIO で動かします。
Laravel や HTTP transport へ広げる前に、まず Tool 1本 を動かして Inspector で確認するところまで進めます。

PHP で MCP サーバーを始める最短ルートは次です。

  • Windows 11 + WSL2 + Docker でデモ環境を作る
  • composer require mcp/sdk で公式 SDK を入れる
  • #[McpTool] を付けた Tool を 1 本だけ作る
  • server.phpStdioTransport を起動する
  • Inspector から、その server.php をコマンドとして呼ぶ

執筆時点の公式 README では、PHP SDK は official SDK として案内されています。
一方で active development、初回メジャー前は experimental とも明記されています。
そのため今回は、まず server 側の最小構成だけに絞ります。

この記事で扱わないもの:

  • Streamable HTTP transport
  • ResourcePrompt
  • Laravel / Symfony / Slim 連携
  • 本番配備やリモート公開
  • クライアント比較
  • Claude Desktop 設定

1. ゴールと前提

到達する状態は次のとおりです。

  • 公式 PHP SDK で最小の MCP server を作れる
  • hello Tool 1 本を STDIO で起動できる
  • Inspector で Tool を確認できる

環境前提は次で固定します。

  • Windows 11
  • WSL2(Ubuntu)
  • VS Code(Remote - WSL)
  • Docker Desktop
  • Node.js(Windows 側、Inspector を使う章だけ)

この前提にしている理由は、Windows 側に PHPComposer を直接入れなくても進められるからです。
実際の PHP 実行と composer require は、すべてコンテナ内で行います。 ただし 6章の Inspector だけは Windows 側の PowerShell で npx を使うため、Node.js を前提にします。

2. 新規デモ環境を作る

WSL 側のターミナルで、デモ用ディレクトリを作ります。

mkdir -p ~/projects/php-mcp-server-intro-demo
cd ~/projects/php-mcp-server-intro-demo
mkdir -p docker/php src
code .

compose.yml を作成します。

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    working_dir: /workspace
    volumes:
      - ./:/workspace
    command: ["sleep", "infinity"]

docker/php/Dockerfile を作成します。

FROM php:8.5-cli

RUN apt-get update \
    && apt-get install -y --no-install-recommends git unzip \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /workspace

今回は検証時に動作確認できた php:8.5-cli を使います。 SDK 自体は php ^8.1 を要求します。執筆時点の要件は composer show mcp/sdk でも確認できます。

composer.json は最小状態で始めます。

{
  "name": "example/php-mcp-server-intro-demo",
  "type": "project",
  "require": {},
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}

起動して、コンテナ内の PHP と Composer を確認します。

docker compose up -d --build
docker compose exec app php -v
docker compose exec app composer --version
docker compose exec app php -m | grep fileinfo
コンテナ内の PHP と Composer、fileinfo の確認結果

執筆時点の確認例:

PHP 8.5.3 (cli)
Composer version 2.9.5

ここで fileinfo も出れば、SDK が要求する ext-fileinfo も満たしています。

ここでコンテナが起動すれば、以後の composer require もそのまま進められます。
Dockerfile を修正したときは、docker compose up -d --build を再実行してください。

3. 公式 PHP SDK を入れる

次に mcp/sdk を導入します。

docker compose exec app composer require mcp/sdk
docker compose exec app composer show mcp/sdk --latest
mcp/sdk の導入結果とバージョン確認

執筆時点の確認例:

versions : * v0.4.0
released : 2026-02-23
requires
php ^8.1
ext-fileinfo *

今回の一時ディレクトリ検証では、2026-03-07 時点で composer require mcp/sdk 実行時に v0.4.0 が解決されました。
ただし、ここは時点依存です。記事内の版番号は「執筆時点の確認結果」として読んでください。

autoload の更新は GreetingElements.php を追加したあとに実行したいので、4章の末尾でまとめて実行します。

オフライン環境では --latest が使いにくいので、その場合は次で十分です。

docker compose exec app composer show mcp/sdk

4. 最小 Tool と server.php を作る

ここでは hello という Tool を 1 本だけ作ります。
src/GreetingElements.php を作成してください。

<?php

declare(strict_types=1);

namespace App;

use Mcp\Capability\Attribute\McpTool;

final class GreetingElements
{
    #[McpTool]
    public function hello(string $name): string
    {
        return "Hello, {$name}!";
    }
}

#[McpTool] を付けると、この method が Tool として discovery 対象になります。
今回は名前を明示せず、method 名の hello をそのまま Tool 名として使います。

続いて、プロジェクトのルートディレクトリに server.php を作成します。

<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;

$server = Server::builder()
    ->setServerInfo('Greeting Server', '0.1.0')
    ->setDiscovery(__DIR__, ['src'])
    ->build();

$status = $server->run(new StdioTransport());

exit($status);

server.php で押さえるのは次の 3 つです。

  • setServerInfo(...) で server 名と版を渡す
  • setDiscovery(__DIR__, ['src'])src/ をスキャンする
  • StdioTransportSTDIO server として起動する

ここで一つ重要なのが、STDOUTechovar_dump を出さないことです。
公式 transports.md でも、STDIO transport では STDOUT は JSON-RPC 用なので使わないよう明記されています。
デバッグしたいときは STDERR を使ってください。

fwrite(STDERR, "debug\n");

最後に、念のため autoload を再生成します。 特に composer.json を変更した場合や、autoload 周りの切り分けをしたいときに有効です。

docker compose exec app composer dump-autoload

コードのポイント

#[McpTool] で Tool を宣言する

    #[McpTool]
    public function hello(string $name): string
    {
        return "Hello, {$name}!";
    }

#[McpTool] を付けるだけで、この method が Tool として discovery 対象になる。引数の型ヒント(string $name)は Inspector や client が表示するスキーマとして使われる。名前を省略すると method 名(hello)がそのまま Tool 名になる。

setDiscovery でスキャン対象を指定し、StdioTransport で起動する

$server = Server::builder()
    ->setServerInfo('Greeting Server', '0.1.0')
    ->setDiscovery(__DIR__, ['src'])
    ->build();

$status = $server->run(new StdioTransport());

setDiscovery(__DIR__, ['src'])src/ 配下を再帰スキャンして #[McpTool] の付いた method を収集する。run(new StdioTransport()) が STDIO server としての待受エントリポイントになる。STDOUT に出力が混入すると JSON-RPC が壊れるため、デバッグは fwrite(STDERR, ...) に限定する。

5. STDIO server として起動する

単体で起動します。

docker compose exec -T app php /workspace/server.php
STDIO server を単体起動した待受状態

ここで -T を付けているのは、STDIO 連携に余計な疑似 TTY を入れないためです。
このコマンドを実行すると、server は待受に入り、目立った出力なしで止まって見えるはずです。
それで正常です。

止めるときは Ctrl+C です。

もしここで即座に終了したり、予期しない出力が出るなら、次を見直してください。

  • composer require mcp/sdk を実行したか
  • composer dump-autoload を実行したか
  • server.phpGreetingElements.php に構文エラーがないか
  • echo var_dump print_r を入れていないか

6. Inspector で Tool を確認する

次は公式 Inspector で確認します。
ここからのコマンドは Windows 側の PowerShell で実行してください。 Inspector は npx で起動するので、この章だけは Windows 側に Node.js が必要です。

Inspector から見ると、接続の流れは次のようになります。入口は wsl.exe ですが、最終的にはコンテナ内の php /workspace/server.phpSTDIO で JSON-RPC を返します。

sequenceDiagram
    participant Inspector
    participant WSL as wsl.exe
    participant Compose as Docker Compose
    participant Server as MCP server
    participant Tool as hello Tool

    Inspector->>WSL: launch command
    WSL->>Compose: docker compose exec -T app php /workspace/server.php
    Compose->>Server: start STDIO server
    Inspector->>Server: initialize and list tools
    Server-->>Inspector: server info and hello tool
    Inspector->>Server: call hello with name=Taro
    Server->>Tool: invoke GreetingElements::hello
    Tool-->>Server: Hello, Taro!
    Server-->>Inspector: tool result

この図のどこかでパスやコマンドが崩れると、接続自体は始まっても Tool が見えなかったり、起動直後に落ちたりします。6 章ではその流れを崩さない形でコマンドをそのまま通します。

前提を確認します。

node --version
npm --version
wsl -l -v
wsl.exe -d Ubuntu -- whoami

Ubuntu は自分の distro 名に置き換えてください。 whoami の結果が、このあとの /home/<your-user>/... に入る WSL ユーザー名です。

# <your-user> は自身の WSL ユーザー名に置換してください
npx -y @modelcontextprotocol/inspector wsl.exe -d Ubuntu -- docker compose --project-directory /home/<your-user>/projects/php-mcp-server-intro-demo -f /home/<your-user>/projects/php-mcp-server-intro-demo/compose.yml exec -T app php /workspace/server.php

この 1 行で、Inspector の起動と接続先の指定をまとめて行えます。

ここでは bash -lc を使わず、wsl.exe から docker compose --project-directory ... を直接呼びます。 これなら Arguments 欄でも区切りが崩れにくく、Inspector 側の接続確認に集中できます。

見落としやすいのは、--project-directory-f の両方で WSL 側の絶対パスを指定することです。 ここも C:\... ではなく、/home/<your-user>/... の形にします。

もし Inspector を npx -y @modelcontextprotocol/inspector だけで起動して、あとから UI で接続先を入れたい場合は、左側を次のように設定します。

  • Transport Type: STDIO
  • Command: wsl.exe
  • Arguments: -d Ubuntu -- docker compose --project-directory /home/<your-user>/projects/php-mcp-server-intro-demo -f /home/<your-user>/projects/php-mcp-server-intro-demo/compose.yml exec -T app php /workspace/server.php

起動できたら、Inspector 側で hello Tool が見えるはずです。 nameTaro を入れて実行すると、戻り値は次のようになります。

Hello, Taro!

ここまで確認できれば、最初の 1 本としては十分です。

Inspector で Connect を押す前の設定画面 Inspector の Tools タブで List Tools を実行する画面 Inspector に hello Tool が表示された状態 Inspector で hello Tool に name を入力する画面 Inspector で hello Tool の実行結果を確認する画面

7. 詰まりどころと次の一歩

詰まりやすい点と対処は次のとおりです。

症状まず見る場所
Mcp\\... が見つからないdocker compose exec app composer require mcp/sdkcomposer dump-autoload
server が起動直後に落ちるphp -l server.phpphp -l src/GreetingElements.php
Inspector で接続できないwsl.exe -d Ubuntu -- docker compose --project-directory /home/<your-user>/projects/php-mcp-server-intro-demo -f /home/<your-user>/projects/php-mcp-server-intro-demo/compose.yml exec -T app php /workspace/server.php を単体で実行
protocol error のような挙動になるSTDOUTechovar_dump を出していないか
パスが見つからない--project-directory-f/home/<your-user>/projects/... が WSL 側の絶対パスになっているか

ここまでできれば、PHP で MCP server を始める最初の 1 本は完了です。
次に広げるなら、ResourcePrompt を足す、HTTP transport に進む、別の MCP client へつなぐ、といった順で広げると把握しやすいです。

シリーズ 1/2

このシリーズ

PHPでMCPサーバーを作る

  1. 1. PHPでMCPサーバー入門(公式PHP SDK + STDIO) 現在の記事
  2. 2. PHPでMCPサーバー実践編(複数Tool + 入力スキーマ + ログ出力)