Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/rules/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ paths:
# Mcp

## Compare MCP JSON-RPC methods with App\Enums\Mcp\Method
Never compare an MCP method as a string literal ('tools/call', 'tools/list', 'initialize', ...). Use `App\Enums\Mcp\Method`, comparing the raw value strictly: `$request->json('method') !== Method::ToolsCall->value` (`LoadMcpWorkspace`). Never `$request->enum('method', Method::class)`: the body is client input, and a non-scalar `method` makes `tryFrom()` throw a TypeError, which turns laravel/mcp's JSON-RPC -32600 into a 500. laravel/mcp keeps its method names in the protected `Server::$methods` array, so there is no package constant. A method the app starts reading gets a case in the enum. `ChallengeChatGptToolCalls`, `ChatGptMcpClient` and `TryPostServer` still use strings; moving them is issue #438.
Never compare an MCP method as a string literal ('tools/call', 'tools/list', 'initialize', ...). Use `App\Enums\Mcp\Method`, comparing the raw value strictly: `$request->json('method') !== Method::ToolsCall->value` (`LoadMcpWorkspace`). Never `$request->enum('method', Method::class)`: the body is client input, and a non-scalar `method` makes `tryFrom()` throw a TypeError, which turns laravel/mcp's JSON-RPC -32600 into a 500. laravel/mcp keeps its method names in the protected `Server::$methods` array, so there is no package constant. A method the app starts reading gets a case in the enum.
3 changes: 2 additions & 1 deletion app/Http/Middleware/Mcp/ChallengeChatGptToolCalls.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Http\Middleware\Mcp;

use App\Enums\Mcp\Method;
use App\Support\Mcp\ChatGptMcpClient;
use Closure;
use Illuminate\Http\Request;
Expand Down Expand Up @@ -32,7 +33,7 @@ public function handle(Request $request, Closure $next): Response

$call = rescue(fn (): JsonRpcRequest => JsonRpcRequest::from($request->json()->all()), report: false);

if ($call?->method !== 'tools/call') {
if ($call?->method !== Method::ToolsCall->value) {
return $response;
}

Expand Down
3 changes: 2 additions & 1 deletion app/Mcp/Servers/TryPostServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Mcp\Servers;

use App\Enums\Mcp\Method;
use App\Mcp\Methods\ListToolsWithSecuritySchemes;
use App\Mcp\Tools\Analytics\GetAnalyticsPublicationTool;
use App\Mcp\Tools\Analytics\GetAnalyticsReportTool;
Expand Down Expand Up @@ -206,6 +207,6 @@ class TryPostServer extends Server

protected function boot(): void
{
$this->addMethod('tools/list', ListToolsWithSecuritySchemes::class);
$this->addMethod(Method::ToolsList->value, ListToolsWithSecuritySchemes::class);
}
}
8 changes: 7 additions & 1 deletion app/Support/Mcp/ChatGptMcpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Support\Mcp;

use App\Actions\Mcp\ListChatGptEgressRanges;
use App\Enums\Mcp\Method;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\IpUtils;

Expand All @@ -14,7 +15,12 @@ class ChatGptMcpClient
* JSON-RPC methods ChatGPT may call without a token: connecting and
* reading the tool list. A tool call always needs a token.
*/
private const DISCOVERY_METHODS = ['initialize', 'notifications/initialized', 'ping', 'tools/list'];
private const DISCOVERY_METHODS = [
Method::Initialize->value,
Method::NotificationsInitialized->value,
Method::Ping->value,
Method::ToolsList->value,
];

/**
* Passport blanks the Authorization header when it refuses a token, so
Expand Down
Loading