PHP SDK
Официальный PHP SDK для MCP, пакет mcp/sdk 0.8.1 - сервер отвечает обеим эпохам с одного адреса, клиент по умолчанию говорит на 2025-11-25, SDK ещё не достиг версии 1.0
сверено с выпуском 0.8.1 4 октября 2026
Что это
Заголовок раздела «Что это»По README выпуска это официальный PHP SDK для MCP, не привязанный к какой-либо программной платформе: серверы и клиенты, транспорты stdio и HTTP, обе эпохи протокола.
README называет и тех, кто ведёт проект: это совместная работа The PHP Foundation и проекта Symfony. Проект следует стандартам кода Symfony и его обещанию обратной совместимости.
В списке официальных SDK у PHP третий уровень (Tier 3). По правилам уровней третий уровень - это экспериментальные, частично реализованные или специализированные SDK.
Выпуск и установка
Заголовок раздела «Выпуск и установка»Проверенный выпуск - 0.8.1 от 29 августа 2026: выпуск на GitHub, пакет mcp/sdk на Packagist. Помеченных предварительных выпусков нет.
По руководству по установке нужны PHP 8.1 или новее и расширение fileinfo.
composer require mcp/sdkЭтого хватает для сервера и клиента на stdio. Остальное ставится по задаче:
| Что нужно | Что доставить |
|---|---|
искать #[McpTool] и другие атрибуты в каталоге | symfony/finder |
| сервер по HTTP | любую реализацию PSR-17, например nyholm/psr7 |
| отдать ответ из отдельной точки входа HTTP | laminas/laminas-httphandlerrunner |
| клиент по HTTP | любой клиент PSR-18, например symfony/http-client |
| проверять токены доступа JWT | firebase/php-jwt |
Какие редакции понимает выпуск
Заголовок раздела «Какие редакции понимает выпуск»Список записан в ProtocolVersion.php: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28. Граница эпох задана константой FIRST_MODERN_VERSION, она равна 2026-07-28.
По журналу изменений редакция 2026-07-28 появилась в выпуске 0.8.0.
Для вас это значит следующее. Сервер на этом выпуске обслуживает и клиента Modern, и клиента Legacy. Клиент на этом выпуске умеет обе эпохи, но не выбирает между ними сам: эпоху задаёте вы.
Что клиент отправляет по умолчанию
Заголовок раздела «Что клиент отправляет по умолчанию»По документации клиента клиент по умолчанию предлагает в рукопожатии V2025_11_25. Редакцию 2026-07-28 нужно выбрать явно.
$client = Client::builder() ->setClientInfo('my-client', '1.0.0') ->setProtocolVersion(ProtocolVersion::V2026_07_28) ->setCapabilities(new ClientCapabilities(elicitation: true)) ->addRequestHandler($myElicitationHandler) ->build();
$client->connect(new HttpTransport('https://example.com/mcp'));
$client->callTool('greet', []);Пример взят из документации выпуска 0.8.1 и здесь не запускался.
Что при этом меняется, описано в документации о версиях:
connect()не отправляетinitialize. Клиент запрашиваетserver/discoverтолько ради сведений о сервере. Если сервер на этот запрос не ответил, соединение всё равно считается рабочим.- Если ответ
server/discoverсодержитsupportedVersionsи выбранной редакции там нет, клиент переходит на редакцию Modern из списка сервера или отказывается от соединения. - Перехода на
initializeпри неудаче нет. В коде клиента отказserver/discoverзаписывается в журнал, и работа продолжается в эпохе Modern.
В старой эпохе выбранная версия - это предложение. Клиент принимает встречное предложение сервера, если знает такую редакцию. Иначе рукопожатие завершается ошибкой ConnectionException.
Что принимает сервер
Заголовок раздела «Что принимает сервер»По описанию работы с двумя эпохами Server::builder()->build() собирает сервер с обработчиком для каждой эпохи, а StreamableHttpTransport решает по каждому запросу, какой из них ответит. Один адрес отвечает и на initialize, и на server/discover.
Решение принимается по телу запроса:
| Что в запросе | Куда он попадает |
|---|---|
params._meta называет редакцию Modern | эпоха Modern |
params._meta называет редакцию рукопожатия | эпоха Legacy |
| такого поля нет | эпоха Legacy, включая initialize |
запрос GET или DELETE | эпоха Legacy |
Заголовок MCP-Protocol-Version решения не принимает. Он сверяется с телом. Запрос, в котором заголовок противоречит _meta, отклоняется с кодом -32020. Неподдерживаемая версия отклоняется с кодом -32022 и списком поддерживаемых.
Рукопожатие не договаривается о версии Modern. Клиенту, который просит в initialize версию 2026-07-28, сервер предложит последнюю версию рукопожатия.
Оставить одну эпоху можно так:
withoutModernEra()- сервер только старой эпохи;buildStateless()вместе соStatelessHttpTransport- адрес только эпохи Modern;setModernVersions()- сузить список редакций Modern.
Минимальный сервер
Заголовок раздела «Минимальный сервер»use Mcp\Capability\Attribute\McpResource;use Mcp\Capability\Attribute\McpTool;use Mcp\Server;use Mcp\Server\Transport\StdioTransport;
class Calculator{ /** * Adds two numbers. */ #[McpTool] public function add(int $a, int $b): int { return $a + $b; }
#[McpResource(uri: 'config://calculator/settings')] public function settings(): array { return ['precision' => 2]; }}
exit(Server::builder() ->setServerInfo('Calculator', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build() ->run(new StdioTransport()));Пример взят из документации выпуска 0.8.1 и здесь не запускался.
Метод setDiscovery() ищет атрибуты в каталоге, для этого нужен пакет symfony/finder.
Минимальный клиент
Заголовок раздела «Минимальный клиент»use Mcp\Client;use Mcp\Client\Transport\StdioTransport;
$client = Client::builder() ->setClientInfo('My Application', '1.0.0') ->build();
$client->connect(new StdioTransport(command: 'php', args: ['/path/to/server.php']));
$tools = $client->listTools();$result = $client->callTool('add', ['a' => 5, 'b' => 3]);
$client->disconnect();Пример взят из документации выпуска 0.8.1 и здесь не запускался.
Этот клиент не задаёт редакцию, поэтому начинает с initialize и предлагает 2025-11-25.
Расширения
Заголовок раздела «Расширения»Tasks. В выпуске не реализовано. В плане развития пункт «Implement the Tasks extension» не отмечен как выполненный.
MCP Apps. Есть типы и средства для сервера. По документации расширений сервер включает расширение вызовом enableExtension(new McpApps()), описывает ресурс с адресом ui:// и связывает с ним инструмент. Клиент может объявить расширение через Client\Builder::enableExtension(). Показ интерфейса в изолированной рамке - задача программы-хоста, готового средства для этого документация выпуска не описывает. Поэтому в блоке совместимости стоит «частично».
Устаревшие функции в выпуске
Заголовок раздела «Устаревшие функции в выпуске»| Функция | Как с ней обходится выпуск 0.8.1 |
|---|---|
| Roots, Sampling, Logging | Помечены устаревшими и выдают предупреждение, но работают. Журнал изменений называет самый ранний срок удаления - 28 июля 2027. На редакции 2026-07-28 вызовы sample() и listRoots() бросают LogicException |
initialize и сеансы | Работают для клиентов старой эпохи. Документация о сеансах: сеансы относятся к эпохе рукопожатия, редакция 2026-07-28 их убрала |
| Динамическая регистрация клиента (RFC 7591) | На стороне сервера есть ClientRegistrationMiddleware |
HTTP+SSE из 2024-11-05 | В выпуске не реализован. Документ об уровне SDK относит его к пробелам, которые нужно либо реализовать, либо явно назвать намеренно пропущенными |
Авторизация OAuth в SDK не завершена. В плане развития пункты «Implement OAuth2 authentication for server» и «Implement OAuth2 authentication for client» не отмечены как выполненные. Документ об уровне SDK сообщает о результатах проверки соответствия: сервер - 100%, клиент - 20%, почти все провалы клиента относятся к OAuth. Этот документ написан до выпуска 0.8.x: последней версией в нём названа 0.7.1.
Выпуск и ветка main
Заголовок раздела «Выпуск и ветка main»На 4 октября 2026 ветка main опережает метку v0.8.1 на 13 изменений. Список редакций в ProtocolVersion.php там тот же. В журнале изменений на main есть раздел 0.9.0, который ещё не выпущен. В нём, например, удалён один из аргументов атрибута #[CompletionProvider] и добавлен метод HttpTransport::getSessionId().
Что неизвестно
Заголовок раздела «Что неизвестно»- Когда выйдет версия 1.0: план развития перечисляет цели, но сроков не называет.
- Текущие результаты проверки соответствия для выпуска 0.8.1: опубликованные в документации цифры относятся к более раннему состоянию.
- Как сервер на stdio выбирает эпоху: документация выпуска описывает разбор запросов только для
StreamableHttpTransport.
Что читать дальше
Заголовок раздела «Что читать дальше»- Все SDK - сравнение официальных SDK по эпохам.
- Две эпохи на одном сервере - как обслуживать старых и новых клиентов.
- Ядро без состояния - поля
_meta, которые несёт каждый запрос Modern. - Устаревшие функции - сроки для Roots, Sampling и Logging.