From bf875e152cde4fa01b31de4cc857d7a68a3cbd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Tue, 11 Aug 2026 13:56:31 +0200 Subject: [PATCH] fix(mcp): pass the space-scoped query so phpstan can type the resolved model A class-string parameter meant the forSpace() scope was called on Builder, which phpstan cannot resolve. Callers now build the scoped query themselves, keeping the concrete model type all the way to the return. --- app/Mcp/Tools/WriteTool.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/Mcp/Tools/WriteTool.php b/app/Mcp/Tools/WriteTool.php index 5e432d31..03bdec63 100644 --- a/app/Mcp/Tools/WriteTool.php +++ b/app/Mcp/Tools/WriteTool.php @@ -9,6 +9,7 @@ use App\Models\Label; use App\Models\Space; use App\Models\Transaction; use App\Models\User; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; @@ -46,21 +47,22 @@ abstract class WriteTool extends McpTool abstract protected function write(Request $request, User $user): Response; /** - * Resolve a model of the given class inside the space, failing with a message - * that tells the agent which tool lists the valid ids. + * Resolve the record the request points at, failing with a message that tells + * the agent which tool lists the valid ids. Callers pass the space-scoped + * query so each keeps its own model type. * * @template TModel of Model * - * @param class-string $model - * @param string $noun how the model is named in the failure message + * @param Builder $query + * @param string $noun how the record is named in the failure message * @param string $hint appended to the failure message * @return TModel */ - protected function modelInSpace(Request $request, Space $space, string $model, string $key, string $noun, string $hint = ''): Model + protected function modelInSpace(Request $request, Space $space, Builder $query, string $key, string $noun, string $hint = ''): Model { $id = $request->string($key)->toString(); - $found = $model::query()->forSpace($space)->whereKey($id)->first(); + $found = $query->whereKey($id)->first(); if ($found === null) { throw ValidationException::withMessages([ @@ -103,7 +105,7 @@ abstract class WriteTool extends McpTool */ protected function accountInSpace(Request $request, Space $space, string $key = 'account_id'): Account { - return $this->modelInSpace($request, $space, Account::class, $key, 'account', 'Call list_accounts to see valid ids.'); + return $this->modelInSpace($request, $space, Account::query()->forSpace($space), $key, 'account', 'Call list_accounts to see valid ids.'); } /** @@ -126,22 +128,22 @@ abstract class WriteTool extends McpTool protected function transactionInSpace(Request $request, Space $space, string $key = 'transaction_id'): Transaction { - return $this->modelInSpace($request, $space, Transaction::class, $key, 'transaction', 'Call search_transactions to find ids.'); + return $this->modelInSpace($request, $space, Transaction::query()->forSpace($space), $key, 'transaction', 'Call search_transactions to find ids.'); } protected function categoryInSpace(Request $request, Space $space, string $key = 'category_id'): Category { - return $this->modelInSpace($request, $space, Category::class, $key, 'category', 'Call list_categories to see valid ids.'); + return $this->modelInSpace($request, $space, Category::query()->forSpace($space), $key, 'category', 'Call list_categories to see valid ids.'); } protected function labelInSpace(Request $request, Space $space, string $key = 'label_id'): Label { - return $this->modelInSpace($request, $space, Label::class, $key, 'label', 'Call list_labels to see valid ids.'); + return $this->modelInSpace($request, $space, Label::query()->forSpace($space), $key, 'label', 'Call list_labels to see valid ids.'); } protected function ruleInSpace(Request $request, Space $space, string $key = 'automation_rule_id'): AutomationRule { - return $this->modelInSpace($request, $space, AutomationRule::class, $key, 'automation rule'); + return $this->modelInSpace($request, $space, AutomationRule::query()->forSpace($space), $key, 'automation rule'); } /**