From 6cec2a728f6aed155a5bb4518e9241fbe3fa4d17 Mon Sep 17 00:00:00 2001 From: CodeWithKyrian <48791154+CodeWithKyrian@users.noreply.github.com> Date: Sun, 13 Jul 2025 00:06:01 +0000 Subject: [PATCH 1/3] Update CHANGELOG --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e28f5..3b690bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to `php-mcp/laravel` will be documented in this file. +## v3.1.0 - 2025-07-13 + +### What's Changed + +* Add PHPUnit 12 support and fix test compatibility issues by @tkaratug in https://github.com/php-mcp/laravel/pull/26 +* feat: Add Closure Handler Support and Custom Input Schema for Tools by @CodeWithKyrian in https://github.com/php-mcp/laravel/pull/27 +* Fix: correct argument order in response()->stream for legacy SSE mode by @sergioalborada in https://github.com/php-mcp/laravel/pull/31 +* Fix ServeCommand output interfering with stdio JSON-RPC communication by @CodeWithKyrian in https://github.com/php-mcp/laravel/pull/34 + +### New Contributors + +* @tkaratug made their first contribution in https://github.com/php-mcp/laravel/pull/26 +* @sergioalborada made their first contribution in https://github.com/php-mcp/laravel/pull/31 + +**Full Changelog**: https://github.com/php-mcp/laravel/compare/3.0.0...3.1.0 + ## v3.0.0 - 2025-06-26 ### Major Changes @@ -35,6 +51,7 @@ All notable changes to `php-mcp/laravel` will be documented in this file. composer require php-mcp/laravel:^3.0 php artisan vendor:publish --provider="PhpMcp\Laravel\McpServiceProvider" + ``` **Full Changelog**: https://github.com/php-mcp/laravel/compare/2.1.1...3.0.0 @@ -146,6 +163,7 @@ This release marks a **major overhaul**, bringing it into full alignment with `p + ``` * **`mcp:serve` for HTTP:** The `--transport=http` option for `mcp:serve` now launches a *dedicated* ReactPHP-based server process. For serving MCP via your main Laravel application routes, ensure the `http_integrated` transport is enabled in `config/mcp.php` and your web server is configured appropriately. * **Event Handling:** If you were directly listening to internal events from the previous version, these may have changed. Rely on the documented Laravel events (`ToolsListChanged`, etc.). @@ -212,6 +230,7 @@ php artisan vendor:publish --provider="PhpMcp\Laravel\Server\McpServiceProvider" + ``` ## Getting Started From 840aa668be8ea858e78f038abb2a563981f04f8c Mon Sep 17 00:00:00 2001 From: Kyrian Obikwelu Date: Mon, 14 Jul 2025 21:27:14 +0100 Subject: [PATCH 2/3] feat: add stateless mode support for HTTP transports Adds stateless configuration option for both dedicated and integrated HTTP transports. Requires server package 3.3+ for stateless StreamableHttpServerTransport support. --- composer.json | 2 +- config/mcp.php | 2 + samples/basic/composer.lock | 228 ++++++++++-------- samples/basic/config/mcp.php | 2 + src/Commands/ServeCommand.php | 2 + .../StreamableTransportController.php | 3 +- .../StreamableHttpServerTransport.php | 57 +++-- 7 files changed, 171 insertions(+), 125 deletions(-) diff --git a/composer.json b/composer.json index ef52894..47d8ecd 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "require": { "php": "^8.1", "laravel/framework": "^9.46 || ^10.34 || ^11.29 || ^12.0", - "php-mcp/server": "^3.2" + "php-mcp/server": "^3.3" }, "require-dev": { "laravel/pint": "^1.13", diff --git a/config/mcp.php b/config/mcp.php index 3fa876c..5822ddf 100644 --- a/config/mcp.php +++ b/config/mcp.php @@ -87,6 +87,7 @@ 'host' => env('MCP_HTTP_DEDICATED_HOST', '127.0.0.1'), 'port' => (int) env('MCP_HTTP_DEDICATED_PORT', 8090), 'path_prefix' => env('MCP_HTTP_DEDICATED_PATH_PREFIX', 'mcp'), + 'stateless' => (bool) env('MCP_HTTP_DEDICATED_STATELESS', false), 'ssl_context_options' => [], 'enable_json_response' => (bool) env('MCP_HTTP_DEDICATED_JSON_RESPONSE', true), 'event_store' => null, // FQCN or null @@ -96,6 +97,7 @@ 'enabled' => (bool) env('MCP_HTTP_INTEGRATED_ENABLED', true), 'legacy' => (bool) env('MCP_HTTP_INTEGRATED_LEGACY', false), 'route_prefix' => env('MCP_HTTP_INTEGRATED_ROUTE_PREFIX', 'mcp'), + 'stateless' => (bool) env('MCP_HTTP_INTEGRATED_STATELESS', false), 'middleware' => ['api'], 'domain' => env('MCP_HTTP_INTEGRATED_DOMAIN'), 'sse_poll_interval' => (int) env('MCP_HTTP_INTEGRATED_SSE_POLL_SECONDS', 1), diff --git a/samples/basic/composer.lock b/samples/basic/composer.lock index 094bb16..ab604f2 100644 --- a/samples/basic/composer.lock +++ b/samples/basic/composer.lock @@ -1207,16 +1207,16 @@ }, { "name": "laravel/framework", - "version": "v12.19.3", + "version": "v12.20.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262" + "reference": "1b9a00f8caf5503c92aa436279172beae1a484ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/4e6ec689ef704bb4bd282f29d9dd658dfb4fb262", - "reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262", + "url": "https://api.github.com/repos/laravel/framework/zipball/1b9a00f8caf5503c92aa436279172beae1a484ff", + "reference": "1b9a00f8caf5503c92aa436279172beae1a484ff", "shasum": "" }, "require": { @@ -1418,20 +1418,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-18T12:56:23+00:00" + "time": "2025-07-08T15:02:21+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.5", + "version": "v0.3.6", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1" + "reference": "86a8b692e8661d0fb308cec64f3d176821323077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1", + "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077", "shasum": "" }, "require": { @@ -1475,9 +1475,9 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.5" + "source": "https://github.com/laravel/prompts/tree/v0.3.6" }, - "time": "2025-02-11T13:34:40+00:00" + "time": "2025-07-07T14:17:42+00:00" }, { "name": "laravel/serializable-closure", @@ -2854,12 +2854,12 @@ "dist": { "type": "path", "url": "../..", - "reference": "571d03d87225587b1799d6f58880b4d805cdbaef" + "reference": "6cec2a728f6aed155a5bb4518e9241fbe3fa4d17" }, "require": { "laravel/framework": "^9.46 || ^10.34 || ^11.29 || ^12.0", "php": "^8.1", - "php-mcp/server": "^3.1" + "php-mcp/server": "^3.3" }, "require-dev": { "laravel/pint": "^1.13", @@ -2868,7 +2868,7 @@ "orchestra/testbench": "^8.0 || ^9.0", "pestphp/pest": "^2.0", "pestphp/pest-plugin-laravel": "^2.0", - "phpunit/phpunit": "^10.0 || ^11.0" + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0" }, "type": "library", "extra": { @@ -2909,11 +2909,18 @@ "role": "Developer" } ], - "description": "The official Laravel integration for the PHP MCP Server package.", + "description": "Laravel SDK for building Model Context Protocol (MCP) servers - Seamlessly integrate MCP tools, resources, and prompts into Laravel applications", "homepage": "https://github.com/php-mcp/laravel", "keywords": [ "ai", "laravel", + "laravel mcp", + "laravel mcp prompts", + "laravel mcp resources", + "laravel mcp sdk", + "laravel mcp server", + "laravel mcp tools", + "laravel model context protocol", "llm", "mcp", "model-context-protocol", @@ -2965,16 +2972,16 @@ }, { "name": "php-mcp/server", - "version": "3.1.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/php-mcp/server.git", - "reference": "caa5686076a4707239a0af902f97722bc9689a89" + "reference": "37b40d5e91f0600442677ddd226e5a22d5661ee1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mcp/server/zipball/caa5686076a4707239a0af902f97722bc9689a89", - "reference": "caa5686076a4707239a0af902f97722bc9689a89", + "url": "https://api.github.com/repos/php-mcp/server/zipball/37b40d5e91f0600442677ddd226e5a22d5661ee1", + "reference": "37b40d5e91f0600442677ddd226e5a22d5661ee1", "shasum": "" }, "require": { @@ -3001,7 +3008,7 @@ "symfony/var-dumper": "^6.4.11|^7.1.5" }, "suggest": { - "react/http": "Required for using the ReactPHP HTTP transport handler (^1.11 recommended)." + "ext-pcntl": "For signal handling support when using StdioServerTransport with StreamSelectLoop" }, "type": "library", "autoload": { @@ -3019,20 +3026,25 @@ "email": "koshnawaza@gmail.com" } ], - "description": "Core PHP implementation for the Model Context Protocol (MCP) server", + "description": "PHP SDK for building Model Context Protocol (MCP) servers - Create MCP tools, resources, and prompts", "keywords": [ "Model Context Protocol", - "ai", - "llm", "mcp", "php", + "php mcp", + "php mcp prompts", + "php mcp resources", + "php mcp sdk", + "php mcp server", + "php mcp tools", + "php model context protocol", "server" ], "support": { "issues": "https://github.com/php-mcp/server/issues", - "source": "https://github.com/php-mcp/server/tree/3.1.0" + "source": "https://github.com/php-mcp/server/tree/3.3.0" }, - "time": "2025-06-25T22:55:35+00:00" + "time": "2025-07-12T22:19:39+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -3286,16 +3298,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", "shasum": "" }, "require": { @@ -3327,9 +3339,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" }, - "time": "2025-02-19T13:28:12+00:00" + "time": "2025-07-13T07:04:09+00:00" }, { "name": "psr/clock", @@ -3944,21 +3956,20 @@ }, { "name": "ramsey/uuid", - "version": "4.8.1", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", - "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -4017,9 +4028,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.8.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "time": "2025-06-01T06:28:46+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "react/cache", @@ -4639,16 +4650,16 @@ }, { "name": "symfony/console", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", "shasum": "" }, "require": { @@ -4713,7 +4724,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.0" + "source": "https://github.com/symfony/console/tree/v7.3.1" }, "funding": [ { @@ -4729,7 +4740,7 @@ "type": "tidelift" } ], - "time": "2025-05-24T10:34:04+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/css-selector", @@ -4865,16 +4876,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83" + "reference": "35b55b166f6752d6aaf21aa042fc5ed280fce235" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/35b55b166f6752d6aaf21aa042fc5ed280fce235", + "reference": "35b55b166f6752d6aaf21aa042fc5ed280fce235", "shasum": "" }, "require": { @@ -4922,7 +4933,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.0" + "source": "https://github.com/symfony/error-handler/tree/v7.3.1" }, "funding": [ { @@ -4938,7 +4949,7 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-06-13T07:48:40+00:00" }, { "name": "symfony/event-dispatcher", @@ -5162,16 +5173,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4236baf01609667d53b20371486228231eb135fd" + "reference": "23dd60256610c86a3414575b70c596e5deff6ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd", - "reference": "4236baf01609667d53b20371486228231eb135fd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/23dd60256610c86a3414575b70c596e5deff6ed9", + "reference": "23dd60256610c86a3414575b70c596e5deff6ed9", "shasum": "" }, "require": { @@ -5221,7 +5232,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.1" }, "funding": [ { @@ -5237,20 +5248,20 @@ "type": "tidelift" } ], - "time": "2025-05-12T14:48:23+00:00" + "time": "2025-06-23T15:07:14+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f" + "reference": "1644879a66e4aa29c36fe33dfa6c54b450ce1831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ac7b8e163e8c83dce3abcc055a502d4486051a9f", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1644879a66e4aa29c36fe33dfa6c54b450ce1831", + "reference": "1644879a66e4aa29c36fe33dfa6c54b450ce1831", "shasum": "" }, "require": { @@ -5335,7 +5346,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.1" }, "funding": [ { @@ -5351,20 +5362,20 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:47:32+00:00" + "time": "2025-06-28T08:24:55+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c" + "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/0f375bbbde96ae8c78e4aa3e63aabd486e33364c", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c", + "url": "https://api.github.com/repos/symfony/mailer/zipball/b5db5105b290bdbea5ab27b89c69effcf1cb3368", + "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368", "shasum": "" }, "require": { @@ -5415,7 +5426,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.0" + "source": "https://github.com/symfony/mailer/tree/v7.3.1" }, "funding": [ { @@ -5431,7 +5442,7 @@ "type": "tidelift" } ], - "time": "2025-04-04T09:51:09+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/mime", @@ -6468,16 +6479,16 @@ }, { "name": "symfony/translation", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667" + "reference": "241d5ac4910d256660238a7ecf250deba4c73063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/4aba29076a29a3aa667e09b791e5f868973a8667", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667", + "url": "https://api.github.com/repos/symfony/translation/zipball/241d5ac4910d256660238a7ecf250deba4c73063", + "reference": "241d5ac4910d256660238a7ecf250deba4c73063", "shasum": "" }, "require": { @@ -6544,7 +6555,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.0" + "source": "https://github.com/symfony/translation/tree/v7.3.1" }, "funding": [ { @@ -6560,7 +6571,7 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/translation-contracts", @@ -6642,16 +6653,16 @@ }, { "name": "symfony/uid", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/7beeb2b885cd584cd01e126c5777206ae4c3c6a3", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { @@ -6696,7 +6707,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.0" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -6712,20 +6723,20 @@ "type": "tidelift" } ], - "time": "2025-05-24T14:28:13+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e" + "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/548f6760c54197b1084e1e5c71f6d9d523f2f78e", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", + "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", "shasum": "" }, "require": { @@ -6780,7 +6791,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.1" }, "funding": [ { @@ -6796,7 +6807,7 @@ "type": "tidelift" } ], - "time": "2025-04-27T18:39:23+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -7551,16 +7562,16 @@ }, { "name": "laravel/pint", - "version": "v1.22.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "941d1927c5ca420c22710e98420287169c7bcaf7" + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7", - "reference": "941d1927c5ca420c22710e98420287169c7bcaf7", + "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", "shasum": "" }, "require": { @@ -7571,10 +7582,10 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.75.0", - "illuminate/view": "^11.44.7", - "larastan/larastan": "^3.4.0", - "laravel-zero/framework": "^11.36.1", + "friendsofphp/php-cs-fixer": "^3.82.2", + "illuminate/view": "^11.45.1", + "larastan/larastan": "^3.5.0", + "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", "pestphp/pest": "^2.36.0" @@ -7584,6 +7595,9 @@ ], "type": "project", "autoload": { + "files": [ + "overrides/Runner/Parallel/ProcessFactory.php" + ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -7613,7 +7627,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-05-08T08:38:12+00:00" + "time": "2025-07-10T18:09:32+00:00" }, { "name": "laravel/sail", @@ -7763,16 +7777,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", "shasum": "" }, "require": { @@ -7811,7 +7825,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" }, "funding": [ { @@ -7819,7 +7833,7 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-07-05T12:25:42+00:00" }, { "name": "nunomaduro/collision", @@ -9864,16 +9878,16 @@ }, { "name": "symfony/yaml", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2" + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/cea40a48279d58dc3efee8112634cb90141156c2", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb", + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb", "shasum": "" }, "require": { @@ -9916,7 +9930,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com/", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.0" + "source": "https://github.com/symfony/yaml/tree/v7.3.1" }, "funding": [ { @@ -9932,7 +9946,7 @@ "type": "tidelift" } ], - "time": "2025-04-04T10:10:33+00:00" + "time": "2025-06-03T06:57:57+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/samples/basic/config/mcp.php b/samples/basic/config/mcp.php index 24ffc6f..e950e70 100644 --- a/samples/basic/config/mcp.php +++ b/samples/basic/config/mcp.php @@ -84,6 +84,7 @@ 'host' => env('MCP_HTTP_DEDICATED_HOST', '127.0.0.1'), 'port' => (int) env('MCP_HTTP_DEDICATED_PORT', 8090), 'path_prefix' => env('MCP_HTTP_DEDICATED_PATH_PREFIX', 'mcp'), + 'stateless' => (bool) env('MCP_HTTP_DEDICATED_STATELESS', false), 'ssl_context_options' => [], 'enable_json_response' => (bool) env('MCP_HTTP_DEDICATED_JSON_RESPONSE', true), 'event_store' => null, // FQCN or null @@ -93,6 +94,7 @@ 'enabled' => (bool) env('MCP_HTTP_INTEGRATED_ENABLED', true), 'legacy' => (bool) env('MCP_HTTP_INTEGRATED_LEGACY', false), 'route_prefix' => env('MCP_HTTP_INTEGRATED_ROUTE_PREFIX', 'mcp'), + 'stateless' => (bool) env('MCP_HTTP_INTEGRATED_STATELESS', true), 'middleware' => explode(',', env('MCP_HTTP_INTEGRATED_MIDDLEWARE', 'api')), 'domain' => env('MCP_HTTP_INTEGRATED_DOMAIN'), 'sse_poll_interval' => (int) env('MCP_HTTP_INTEGRATED_SSE_POLL_SECONDS', 1), diff --git a/src/Commands/ServeCommand.php b/src/Commands/ServeCommand.php index da1dbab..b84bc28 100644 --- a/src/Commands/ServeCommand.php +++ b/src/Commands/ServeCommand.php @@ -151,6 +151,7 @@ private function handleStreamableHttpTransport(Server $server, string $host, int { $enableJsonResponse = config('mcp.transports.http_dedicated.enable_json_response', true); $eventStore = $this->createEventStore(); + $stateless = config('mcp.transports.http_dedicated.stateless', false); $this->info("Starting MCP server on http://{$host}:{$port}"); $this->line(" - Transport: Streamable HTTP"); @@ -163,6 +164,7 @@ private function handleStreamableHttpTransport(Server $server, string $host, int mcpPath: $pathPrefix, sslContext: $sslContextOptions, enableJsonResponse: $enableJsonResponse, + stateless: $stateless, eventStore: $eventStore ); diff --git a/src/Http/Controllers/StreamableTransportController.php b/src/Http/Controllers/StreamableTransportController.php index 000f70f..becc601 100644 --- a/src/Http/Controllers/StreamableTransportController.php +++ b/src/Http/Controllers/StreamableTransportController.php @@ -19,8 +19,9 @@ public function __construct(Server $server) { $eventStore = $this->createEventStore(); $sessionManager = $server->getSessionManager(); + $stateless = config('mcp.transports.http_integrated.stateless', false); - $this->transport = new StreamableHttpServerTransport($sessionManager, $eventStore); + $this->transport = new StreamableHttpServerTransport($sessionManager, $eventStore, $stateless); $server->listen($this->transport, false); } diff --git a/src/Transports/StreamableHttpServerTransport.php b/src/Transports/StreamableHttpServerTransport.php index c583453..f07a32d 100644 --- a/src/Transports/StreamableHttpServerTransport.php +++ b/src/Transports/StreamableHttpServerTransport.php @@ -29,7 +29,8 @@ class StreamableHttpServerTransport implements ServerTransportInterface public function __construct( protected SessionManager $sessionManager, - protected ?EventStoreInterface $eventStore = null + protected ?EventStoreInterface $eventStore = null, + protected bool $stateless = false ) {} protected function generateId(): string @@ -104,27 +105,33 @@ public function handlePostRequest(Request $request): Response $isInitializeRequest = ($message instanceof JsonRpcRequest && $message->method === 'initialize'); $sessionId = null; - if ($isInitializeRequest) { - if ($request->hasHeader('Mcp-Session-Id')) { - Log::warning('Client sent Mcp-Session-Id with InitializeRequest. Ignoring.', ['clientSentId' => $request->header('Mcp-Session-Id')]); - $error = Error::forInvalidRequest('Invalid request: Session already initialized. Mcp-Session-Id header not allowed with InitializeRequest.', $message->getId()); - return response()->json($error, 400, $this->getCorsHeaders()); - } - + if ($this->stateless) { $sessionId = $this->generateId(); $this->emit('client_connected', [$sessionId]); } else { - $sessionId = $request->header('Mcp-Session-Id'); + if ($isInitializeRequest) { + if ($request->hasHeader('Mcp-Session-Id')) { + Log::warning('Client sent Mcp-Session-Id with InitializeRequest. Ignoring.', ['clientSentId' => $request->header('Mcp-Session-Id')]); + $error = Error::forInvalidRequest('Invalid request: Session already initialized. Mcp-Session-Id header not allowed with InitializeRequest.', $message->getId()); + return response()->json($error, 400, $this->getCorsHeaders()); + } + + $sessionId = $this->generateId(); + $this->emit('client_connected', [$sessionId]); + } else { + $sessionId = $request->header('Mcp-Session-Id'); - if (empty($sessionId)) { - Log::warning('POST request without Mcp-Session-Id'); - $error = Error::forInvalidRequest('Mcp-Session-Id header required for POST requests', $message->getId()); - return response()->json($error, 400, $this->getCorsHeaders()); + if (empty($sessionId)) { + Log::warning('POST request without Mcp-Session-Id'); + $error = Error::forInvalidRequest('Mcp-Session-Id header required for POST requests', $message->getId()); + return response()->json($error, 400, $this->getCorsHeaders()); + } } } $context = [ 'is_initialize_request' => $isInitializeRequest, + 'stateless' => $this->stateless, ]; $nRequests = match (true) { @@ -171,10 +178,14 @@ protected function handleJsonResponse(Message $message, string $sessionId, array ...$this->getCorsHeaders() ]; - if ($context['is_initialize_request'] ?? false) { + if ($context['is_initialize_request'] ?? false && !$this->stateless) { $headers['Mcp-Session-Id'] = $sessionId; } + if ($this->stateless) { + $this->emit('client_disconnected', [$sessionId, 'Stateless request completed']); + } + return response()->make($data, 200, $headers); } catch (Throwable $e) { Log::error('JSON response mode error', ['exception' => $e]); @@ -195,7 +206,7 @@ protected function handleSseResponse(Message $message, string $sessionId, int $n 'X-Accel-Buffering' => 'no', ], $this->getCorsHeaders()); - if ($context['is_initialize_request'] ?? false) { + if ($context['is_initialize_request'] ?? false && !$this->stateless) { $headers['Mcp-Session-Id'] = $sessionId; } @@ -210,6 +221,10 @@ protected function handleSseResponse(Message $message, string $sessionId, int $n $messages = $this->dequeueMessagesForContext($sessionId, 'post_sse', $streamId); $this->sendSseEvent($messages[0]['data'], $messages[0]['id']); + + if ($this->stateless) { + $this->emit('client_disconnected', [$sessionId, 'Stateless request completed']); + } }, headers: $headers); } @@ -218,6 +233,12 @@ protected function handleSseResponse(Message $message, string $sessionId, int $n */ public function handleGetRequest(Request $request): StreamedResponse|Response { + if ($this->stateless) { + return new Response('SSE not available in stateless mode', 405, [ + 'Allow' => 'POST, DELETE, OPTIONS' + ]); + } + $acceptHeader = $request->header('Accept'); if (!str_contains($acceptHeader, 'text/event-stream')) { $error = Error::forInvalidRequest("Not Acceptable: Client must accept text/event-stream for GET requests."); @@ -274,6 +295,10 @@ public function handleGetRequest(Request $request): StreamedResponse|Response */ public function handleDeleteRequest(Request $request): Response { + if ($this->stateless) { + return response()->noContent(headers: $this->getCorsHeaders()); + } + $sessionId = $request->header('Mcp-Session-Id'); if (empty($sessionId)) { Log::warning("DELETE request without Mcp-Session-Id."); @@ -285,7 +310,7 @@ public function handleDeleteRequest(Request $request): Response $this->emit('client_disconnected', [$sessionId, 'Session terminated by DELETE request']); - return response()->noContent(204, $this->getCorsHeaders()); + return response()->noContent(headers: $this->getCorsHeaders()); } /** From c84322f31e1ee22fbbbfb81e9a99e050923abc8a Mon Sep 17 00:00:00 2001 From: Kyrian Obikwelu Date: Tue, 19 Aug 2025 17:16:18 +0100 Subject: [PATCH 3/3] chore: use a more explicit install command in docs to force latest version [skip ci] --- README.md | 2 +- samples/basic/app/Models/User.php | 3 +- samples/basic/composer.json | 1 + samples/basic/composer.lock | 68 +++++++++++++- samples/basic/config/mcp.php | 2 +- samples/basic/config/sanctum.php | 84 ++++++++++++++++++ ...17_create_personal_access_tokens_table.php | 33 +++++++ samples/basic/dump.rdb | Bin 0 -> 1151 bytes samples/basic/routes/api.php | 8 ++ samples/basic/routes/mcp.php | 5 ++ 10 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 samples/basic/config/sanctum.php create mode 100644 samples/basic/database/migrations/2025_08_08_121817_create_personal_access_tokens_table.php create mode 100644 samples/basic/dump.rdb create mode 100644 samples/basic/routes/api.php diff --git a/README.md b/README.md index 50353ad..739cfeb 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ This package supports the **2025-03-26** version of the Model Context Protocol. Install the package via Composer: ```bash -composer require php-mcp/laravel +composer require php-mcp/laravel:^3.0 -W ``` Publish the configuration file: diff --git a/samples/basic/app/Models/User.php b/samples/basic/app/Models/User.php index 749c7b7..a6ab88e 100644 --- a/samples/basic/app/Models/User.php +++ b/samples/basic/app/Models/User.php @@ -6,11 +6,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable; + use HasFactory, Notifiable, HasApiTokens; /** * The attributes that are mass assignable. diff --git a/samples/basic/composer.json b/samples/basic/composer.json index 23dae59..36f722c 100644 --- a/samples/basic/composer.json +++ b/samples/basic/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", + "laravel/sanctum": "^4.0", "laravel/tinker": "^2.10.1", "php-mcp/laravel": "dev-main" }, diff --git a/samples/basic/composer.lock b/samples/basic/composer.lock index ab604f2..9e1170e 100644 --- a/samples/basic/composer.lock +++ b/samples/basic/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26187052f3195fd4e3e90385de4d0446", + "content-hash": "56f664a818eb3d1df9d4165ed6c99428", "packages": [ { "name": "brick/math", @@ -1479,6 +1479,70 @@ }, "time": "2025-07-07T14:17:42+00:00" }, + { + "name": "laravel/sanctum", + "version": "v4.2.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/fd6df4f79f48a72992e8d29a9c0ee25422a0d677", + "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "php": "^8.2", + "symfony/console": "^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2025-07-09T19:45:24+00:00" + }, { "name": "laravel/serializable-closure", "version": "v2.0.4", @@ -2854,7 +2918,7 @@ "dist": { "type": "path", "url": "../..", - "reference": "6cec2a728f6aed155a5bb4518e9241fbe3fa4d17" + "reference": "840aa668be8ea858e78f038abb2a563981f04f8c" }, "require": { "laravel/framework": "^9.46 || ^10.34 || ^11.29 || ^12.0", diff --git a/samples/basic/config/mcp.php b/samples/basic/config/mcp.php index e950e70..f8e9fc9 100644 --- a/samples/basic/config/mcp.php +++ b/samples/basic/config/mcp.php @@ -95,7 +95,7 @@ 'legacy' => (bool) env('MCP_HTTP_INTEGRATED_LEGACY', false), 'route_prefix' => env('MCP_HTTP_INTEGRATED_ROUTE_PREFIX', 'mcp'), 'stateless' => (bool) env('MCP_HTTP_INTEGRATED_STATELESS', true), - 'middleware' => explode(',', env('MCP_HTTP_INTEGRATED_MIDDLEWARE', 'api')), + 'middleware' => ['api', 'auth:sanctum'], 'domain' => env('MCP_HTTP_INTEGRATED_DOMAIN'), 'sse_poll_interval' => (int) env('MCP_HTTP_INTEGRATED_SSE_POLL_SECONDS', 1), 'cors_origin' => env('MCP_HTTP_INTEGRATED_CORS_ORIGIN', '*'), diff --git a/samples/basic/config/sanctum.php b/samples/basic/config/sanctum.php new file mode 100644 index 0000000..44527d6 --- /dev/null +++ b/samples/basic/config/sanctum.php @@ -0,0 +1,84 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort(), + // Sanctum::currentRequestHost(), + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/samples/basic/database/migrations/2025_08_08_121817_create_personal_access_tokens_table.php b/samples/basic/database/migrations/2025_08_08_121817_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/samples/basic/database/migrations/2025_08_08_121817_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->text('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable()->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/samples/basic/dump.rdb b/samples/basic/dump.rdb new file mode 100644 index 0000000000000000000000000000000000000000..772f46d0bac1a76929be44396028fa48b5265e1e GIT binary patch literal 1151 zcma)+&1(}u7>8%`u}P~HTNM?p8MhWhvzk;9yP`~}#kQanD|qlDne0xQp}RA2W;Xqz zAo#7|xgIPQPgbZP)N@FW-bC=)zd>(lv2L4Gq}t+j*!MT@`^@w1_1??Qt)MTtCQDB`c^FwH5V9_0!x;u@?6waZHbR)!f&8pccoxVCJzT74Q=* z>*3b@f|RLERkp|lo#4#!uvA3IWD)=n0PWm)qDi2ZyhW9{I5l z+!0+}$&7w}Kdl87oqxq~Mim#o$AL~0AjqPC}(mp)0cU9c+$vY7c-j}mVtf6jih07#z+8&Gq@+R zjt%Y&-}8x&I&_B=u{ zV{i89SMk_Tkrq%A-Hny7AWZc;@Iv@(`b;;v6cR+DcGR39U8@pbnBt54Uyg5 z>#olIf85E2xBzaBbp!pglHrmpa+SrZ`t03IaizVrZtL!|o4>DB`941jB99xKpEN+nUj(3bG`20rI#}VgKR|Zlz{YlwWIel-4UCq-Vq__E V$e50;lr=fE`t;^%d6B=({Q|h9pi}?= literal 0 HcmV?d00001 diff --git a/samples/basic/routes/api.php b/samples/basic/routes/api.php new file mode 100644 index 0000000..ccc387f --- /dev/null +++ b/samples/basic/routes/api.php @@ -0,0 +1,8 @@ +user(); +})->middleware('auth:sanctum'); diff --git a/samples/basic/routes/mcp.php b/samples/basic/routes/mcp.php index 3d8d775..fc88880 100644 --- a/samples/basic/routes/mcp.php +++ b/samples/basic/routes/mcp.php @@ -5,6 +5,7 @@ use App\Mcp\GetArticleContent; use App\Mcp\GetAppVersion; use PhpMcp\Laravel\Facades\Mcp; +use Illuminate\Support\Facades\Auth; Mcp::tool('welcome_message', GenerateWelcomeMessage::class); @@ -12,6 +13,10 @@ ->name('laravel_app_version') ->mimeType('text/plain'); +Mcp::tool('get_me', function () { + return Auth::user(); +}); + Mcp::resourceTemplate('content://articles/{articleId}', GetArticleContent::class) ->name('article_content') ->mimeType('application/json');