Skip to content
1 change: 1 addition & 0 deletions src/Eager/Related.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public function resolveField(Repository $repository): EagerField
{
return $this
->field
->forMcp($repository->isForMcp())
->columns($this->getColumns())
->resolve($repository);
}
Expand Down
19 changes: 18 additions & 1 deletion src/Eager/RelatedCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Binaryk\LaravelRestify\Fields\MorphToMany;
use Binaryk\LaravelRestify\Filters\SortableFilter;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\MCP\Concerns\HasMcpTools;
use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Support\Collection;

Expand Down Expand Up @@ -97,6 +98,21 @@ public function forIndex(RestifyRequest $request, Repository $repository): self
});
}

public function forMcp(RestifyRequest $request, Repository $repository): self
{
return $this->filter(function (Related $related) {
// If there's an EagerField, check its repository class
if ($related->field && $related->field->repositoryClass) {
return in_array(HasMcpTools::class, class_uses_recursive($related->field->repositoryClass), true);
}

// For string relationships (without EagerField), we need to find the repository
// This happens when relationships are defined as static::$related = ['user']
// We'll allow these through and let the serialization handle the filtering
return true;
});
}

public function inRequest(RestifyRequest $request, Repository $repository): self
{
return $this->filter(function (mixed $repositoryRelatedField, $repositoryRelatedKey) use (
Expand Down Expand Up @@ -167,7 +183,8 @@ public function forRequest(RestifyRequest $request, Repository $repository): sel
->authorized($request)
->inRequest($request, $repository)
->when($request->isShowRequest(), fn (self $collection) => $collection->forShow($request, $repository))
->when($request->isIndexRequest(), fn (self $collection) => $collection->forIndex($request, $repository));
->when($request->isIndexRequest(), fn (self $collection) => $collection->forIndex($request, $repository))
->when($repository->isForMcp(), fn (self $collection) => $collection->forIndex($request, $repository));
}

public function unserialized(RestifyRequest $request, Repository $repository)
Expand Down
13 changes: 10 additions & 3 deletions src/Fields/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Fields\Concerns\Attachable;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\MCP\Requests\McpRequest;
use Binaryk\LaravelRestify\Repositories\PivotsCollection;
use Binaryk\LaravelRestify\Repositories\Repository;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;

class BelongsToMany extends EagerField
{
Expand Down Expand Up @@ -51,8 +51,15 @@ public function resolve($repository, $attribute = null)

$this->value = $paginator->map(function ($item) {
try {
return $this->repositoryClass::resolveWith($item)
->allowToShow(app(Request::class))
/**
* @var Repository $repositoryFromClass
*/
$repositoryFromClass = $this->repositoryClass::resolveWith($item);

return $repositoryFromClass
->allowToShow(
$this->isForMcp() ? app(McpRequest::class) : app(RestifyRequest::class)
)
->withPivots(
PivotsCollection::make($this->pivotFields)
->map(fn (Field $field) => clone $field)
Expand Down
23 changes: 22 additions & 1 deletion src/Fields/EagerField.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Binaryk\LaravelRestify\Filters\RelatedQuery;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\MCP\Requests\McpRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Restify;
use Binaryk\LaravelRestify\Traits\HasColumns;
Expand All @@ -30,6 +31,8 @@ class EagerField extends Field

private RelatedQuery $relatedQuery;

public bool $forMcp = false;

public function __construct($attribute, ?string $parentRepository = null)
{
parent::__construct(attribute: $attribute);
Expand Down Expand Up @@ -85,7 +88,7 @@ public function resolve($repository, $attribute = null)
$serializableRepository = $this->repositoryClass::resolveWith($relatedModel);

$this->value = $serializableRepository
->allowToShow($repository->request ?? app(Request::class))
->allowToShow($this->isForMcp() ? app(McpRequest::class) : app(RestifyRequest::class))
->columns()
->eager($this);
} catch (AuthorizationException) {
Expand Down Expand Up @@ -160,4 +163,22 @@ public function qualifySortable(RestifyRequest $request): ?string

return $table.'.attributes.'.$this->sortableColumn;
}

public function forMcp(bool|callable $forMcp = false): self
{
if (is_callable($forMcp)) {
$this->forMcp = $forMcp();

return $this;
}

$this->forMcp = $forMcp;

return $this;
}

public function isForMcp(): bool
{
return $this->forMcp;
}
}
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpActionTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function actionTool(Action $action, array $arguments, McpActionRequest $a

public static function actionToolSchema(Action $action, ToolInputSchema $schema, McpActionRequest $mcpRequest): void
{
$modelName = class_basename(static::$model);
$modelName = class_basename(static::guessModelClassName());

// Add action-specific validation rules
$actionRules = $action->rules();
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpDestroyTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function deleteTool(array $arguments, McpRequest $request): array
public static function destroyToolSchema(ToolInputSchema $schema): void
{
$key = static::uriKey();
$modelName = class_basename(static::$model);
$modelName = class_basename(static::guessModelClassName());

$schema->string('id')
->description("The ID of the $modelName to delete")
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpGetterTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function getterTool(Getter $getter, array $arguments, McpGetterRequest $g

public static function getterToolSchema(Getter $getter, ToolInputSchema $schema, McpGetterRequest $mcpRequest): void
{
$modelName = class_basename(static::$model);
$modelName = class_basename(static::guessModelClassName());

// Add getter-specific validation rules if the getter has a rules method
if (method_exists($getter, 'rules')) {
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpShowTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function showTool(array $arguments, McpRequest $request): array

public static function showToolSchema(ToolInputSchema $schema): void
{
$modelName = class_basename(static::$model);
$modelName = class_basename(static::guessModelClassName());

$schema->string('id')
->description("The ID of the $modelName to retrieve")
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpStoreTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function storeTool(array $arguments, McpRequest $request): array

public static function storeToolSchema(ToolInputSchema $schema): void
{
$repository = static::resolveWith(app(static::$model));
$repository = static::resolveWith(static::newModel());

$repository->collectFields($request = app(McpRequest::class))
->forStore($request, $repository)
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Concerns/McpUpdateTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function updateTool(array $arguments, McpRequest $request): array
public static function updateToolSchema(ToolInputSchema $schema): void
{
$key = static::uriKey();
$modelName = class_basename(static::$model);
$modelName = class_basename(static::guessModelClassName());

$schema->string('id')
->description("The ID of the $modelName to update")
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/ActionTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function description(): string
{
$repositoryUriKey = $this->repository->uriKey();
$actionName = $this->action->name();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

if ($this->action->isStandalone()) {
return "Execute {$actionName} action (standalone - no models required) in the {$repositoryUriKey} repository.";
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/DeleteTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function name(): string
public function description(): string
{
$uriKey = $this->repository->uriKey();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Delete an existing {$modelName} record by ID from the {$uriKey} repository.";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/GetterTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function description(): string
{
$repositoryUriKey = $this->repository->uriKey();
$getterName = $this->getter->name();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

// Check if it's primarily a show getter or index getter
$mcpRequest = app(McpGetterRequest::class);
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/IndexTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function name(): string
public function description(): string
{
$uriKey = $this->repository->uriKey();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Retrieve a paginated list of {$modelName} records from the {$uriKey} repository with filtering, sorting, and search capabilities.";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/ProfileTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function name(): string

public function description(): string
{
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Get the current authenticated user profile including {$modelName} and relationship information.";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/ShowTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function name(): string
public function description(): string
{
$uriKey = $this->repository->uriKey();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Retrieve a single {$modelName} record by ID from the {$uriKey} repository with optional relationship loading.";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/StoreTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function name(): string
public function description(): string
{
$uriKey = $this->repository->uriKey();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Create a new {$modelName} record in the {$uriKey} repository with the provided data.";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MCP/Tools/Operations/UpdateTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function name(): string
public function description(): string
{
$uriKey = $this->repository->uriKey();
$modelName = class_basename($this->repository::$model);
$modelName = class_basename($this->repository::guessModelClassName());

return "Update an existing {$modelName} record by ID in the {$uriKey} repository with the provided data.";
}
Expand Down
25 changes: 21 additions & 4 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Binaryk\LaravelRestify\Actions\Action;
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Eager\Related;
use Binaryk\LaravelRestify\Eager\RelatedCollection;
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
use Binaryk\LaravelRestify\Fields\BelongsToMany;
use Binaryk\LaravelRestify\Fields\EagerField;
Expand Down Expand Up @@ -107,7 +108,7 @@ class Repository implements JsonSerializable, RestifySearchable
*/
public Model $resource;

// public RestifyRequest $request;
public bool $forMcp = false;

/**
* The list of relations available for the show or index.
Expand Down Expand Up @@ -558,6 +559,8 @@ public function resolveIndexPivots(RestifyRequest $request): array
*/
public function resolveRelationships($request): array
{
$this->forMcp($request instanceof McpRequest);

if (! $request->related()->hasRelated()) {
return [];
}
Expand All @@ -566,6 +569,7 @@ public function resolveRelationships($request): array
->forRequest($request, $this)
->mapIntoRelated($request, $this)
->unserialized($request, $this)
->when($this->isForMcp(), fn (RelatedCollection $collection) => $collection->forMcp($request, $this))
->map(fn (Related $related) => $related->resolve($request, $this)->getValue())
->map(function (mixed $items) {
if ($items instanceof Collection) {
Expand Down Expand Up @@ -608,7 +612,6 @@ public function resolveIndexRelationships($request)
public function indexAsArray(RestifyRequest $request): array
{
// Preserve the request instance for the entire flow
// $this->request = $request;

// Check if the model was set under the repository
throw_if(
Expand All @@ -627,7 +630,6 @@ public function indexAsArray(RestifyRequest $request): array
$items = $this->indexCollection($request, $paginator->getCollection())->map(function ($value) {
$repository = static::resolveWith($value);
// Ensure each resolved repository maintains the original request
// $repository->request = $request;

return $repository;
})->filter(function (self $repository) use ($request) {
Expand Down Expand Up @@ -1151,7 +1153,9 @@ protected function getId(RestifyRequest $request): ?string
public function jsonSerialize()
{
return $this->serializeForShow(
$this->request ?? app(RestifyRequest::class)
$this->isForMcp()
? app(McpRequest::class)
: app(RestifyRequest::class)
);
}

Expand Down Expand Up @@ -1209,6 +1213,7 @@ public function eager(?EagerField $field = null): Repository
return $this;
}

$this->forMcp($field->isForMcp());
$this->eagerState = $field->queryKeyThatRendered();
$this->columns($field->getColumns());

Expand Down Expand Up @@ -1283,4 +1288,16 @@ protected function ensureResourceExists(): self

return $this;
}

public function forMcp($forMcp = true): self
{
$this->forMcp = $forMcp;

return $this;
}

public function isForMcp(): bool
{
return $this->forMcp;
}
}
Loading
Loading