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

symfony/mcp-bundleで、既存アプリケーションもお手軽にMCPサーバー化

 symfony/mcp-bundleで、既存アプリケーションもお手軽にMCPサーバー化

PHPカンファレンス香川2025でのLT資料です
https://fortee.jp/phpconkagawa-2025/proposal/984674a8-deb8-48e5-aa02-285dd423c50b

Avatar for hideki kinjyo

hideki kinjyo PRO

November 23, 2025
Tweet

More Decks by hideki kinjyo

Other Decks in Programming

Transcript

  1. 自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏

    : @o0h_ • 好きなFWはCakePHP • アイコンは美味しい鮭親子丼の写真です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm 
  2. 「Content」リソース用のController #[Route('/content')] class ContentController extends AbstractController { public function __construct(

    private ContentService $contentService, ) { コンストラクタで 「サービス」を注入 ※ 依存解決はFWの標準的なモノ
  3. list #[Route('/', name: 'content_index', methods:['GET'])] public function index(): Response {

    $contents = $this ->contentService ->listLatest(); return $this->render( 'content/index.html.twig', ['contents' => $contents] ); }
  4. list #[Route('/', name: 'content_index', methods:['GET'])] public function index(): Response {

    $contents = $this ->contentService ->listLatest(); return $this->render( 'content/index.html.twig', ['contents' => $contents] ); } $contents = $this ->contentService ->listLatest(); 「サービス」に生やした機能に 処理は丸投げ
  5. list #[Route('/', name: 'content_index', methods:['GET'])] public function index(): Response {

    $contents = $this ->contentService ->listLatest(); return $this->render( 'content/index.html.twig', ['contents' => $contents] ); } ['contents' => $contents] 「サービス」から受け取った オブジェクトを Viewにそのまま引き渡す
  6. add #[Route('/new', name:'content_create', methods:['POST'])] public function create( #[MapRequestPayload] ContentInput $input,

    ): Response { $this->contentService->create($input); $this->addFlash( 'success', 'Content created successfully', ); return $this->redirectToRoute('content_index'); }
  7. add #[Route('/new', name:'content_create', methods:['POST'])] public function create( #[MapRequestPayload] ContentInput $input,

    ): Response { $this->contentService->create($input); $this->addFlash( 'success', 'Content created successfully', ); return $this->redirectToRoute('content_index'); } #[MapRequestPayload] ContentInput $input, FWの機能で リクエスト内容が オブジェクトに自動変換されて メソッドに入ってくる
  8. add #[Route('/new', name:'content_create', methods:['POST'])] public function create( #[MapRequestPayload] ContentInput $input,

    ): Response { $this->contentService->create($input); $this->addFlash( 'success', 'Content created successfully', ); return $this->redirectToRoute('content_index'); } $this->contentService->create($input); $inputを そのまま「サービス」に丸投げ
  9. 「Content Tools」用のクラス final class ContentTools { public function __construct( private

    ContentService $contentService, private ValidatorInterface $validator, ) { コンストラクタで 必要な依存を注入 ※ 依存解決はFWの標準的なモノ
  10. list ★中身はま〜〜〜〜ったく同じと言えるレベル! public function listContents(): array { return $this->contentService->listLatest(); public

    function index(): Response { $contents = $this->contentService->listLatest(); return $this->render( Controller Tool用クラス
  11. list #[McpTool( name: 'list_contents', description: 'ίϯςϯπҰཡΛ࠷৽ॱͰऔಘ͠·͢', )] public function listContents():

    array { return $this->contentService->listLatest(); } #[McpTool( name: 'list_contents', description: 'ίϯςϯπҰཡΛ࠷৽ॱͰऔಘ͠·͢', )] Attributesが重要
  12. add #[McpTool( name: 'create_content', description: '৽͍͠ίϯςϯπΛ࡞੒͠·͢', )] public function createContent(

    string $title, string $body, string $status, ?string $publishedAt = null, ): Content {
  13. add #[McpTool( name: 'create_content', description: '৽͍͠ίϯςϯπΛ࡞੒͠·͢', )] public function createContent(

    string $title, string $body, string $status, ?string $publishedAt = null, ): Content { string $title, string $body, string $status, ?string $publishedAt = null, メソッドの引数を自動的に解釈して MCPサーバーとして 喋ってくれる ※PHPDocがあればそれも
  14. add // public function createContent { $input = new ContentInput(

    $title, $body, $status, $publishedAt ? new \DateTimeImmutable($publishedAt) : null, ); $this->validateInput($input); return $this->contentService- >create($input); } 中身は 何の変哲も無いコード
  15. mcp/sdk (modelcontextprotocol/php-sdk) • PHP FoundationやSymfonyチームも噛んでいる • 詳しい話はThe PHP Foundationのブログに •

    https://thephp.foundation/blog/2025/09/05/php-mcp-sdk/ • mcp-bundleも公式SDKの公開にあわせて乗り換えた経緯 • [MCP Bundle] Adopt official MCP SDK · Issue #526 · symfony/ai https://github.com/symfony/ai/issues/526 • PSR-11(Container I/F)を採用していて便利
  16. MCPαʔόʔͷ࢓૊Έ ௨৴ํࣜ • stdio (ඪ४ೖग़ྗ) • SSE (Server-Sent Events) ඞਢػೳ

    • initialize - ઀ଓཱ֬ • tools/list - πʔϧҰཡ • tools/call - πʔϧ࣮ߦ جຊతͳྲྀΕ AIΞϓϦ → MCPαʔόʔ: tools/list Ͱπʔϧऔಘ AIΞϓϦ → MCPαʔόʔ: tools/call Ͱπʔϧ࣮ߦ MCPαʔόʔ → AIΞϓϦ: ࣮ߦ݁ՌΛฦ٫ MCP Server ͷҙٛͱ࣮૷ཁ݅
  17. ࣮૷ཁ݅ͱ·ͱΊ ࠷খݶͷ࣮૷ • αʔόʔͷىಈॲཧ (stdio ·ͨ͸ SSE) • initialize ϋϯυϥʔ

    • tools/list ϋϯυϥʔ • tools/call ϋϯυϥʔ MCPαʔόʔ = AI͕֎෦πʔϧɾσʔλʹΞΫηε͢ΔͨΊͷඪ४తͳڮ౉͠໾ MCP Server ͷҙٛͱ࣮૷ཁ݅