Add a tool to update your own profile
This commit is contained in:
@@ -93,6 +93,7 @@ use App\Mcp\Tools\UpdateLocationTool;
|
||||
use App\Mcp\Tools\UpdateManufacturerTool;
|
||||
use App\Mcp\Tools\UpdateStatusLabelTool;
|
||||
use App\Mcp\Tools\UpdateSupplierTool;
|
||||
use App\Mcp\Tools\UpdateProfileTool;
|
||||
use App\Mcp\Tools\UpdateUserTool;
|
||||
use Laravel\Mcp\Server;
|
||||
use Laravel\Mcp\Server\Attributes\Instructions;
|
||||
@@ -124,6 +125,7 @@ class SnipeMCPServer extends Server
|
||||
DeleteUserTool::class,
|
||||
RestoreUserTool::class,
|
||||
GetCurrentUserTool::class,
|
||||
UpdateProfileTool::class,
|
||||
GetUserAssetsTool::class,
|
||||
Reset2FATool::class,
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\ResponseFactory;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Attributes\Name;
|
||||
use Laravel\Mcp\Server\Attributes\Title;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
|
||||
#[Name('update_profile')]
|
||||
#[Title('Update Profile')]
|
||||
#[Description('Update the authenticated user\'s own profile information')]
|
||||
class UpdateProfileTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'first_name' => 'nullable|string|max:255',
|
||||
'last_name' => 'nullable|string|max:255',
|
||||
'phone' => 'nullable|string|max:35',
|
||||
'website' => 'nullable|url|max:255',
|
||||
'gravatar' => 'nullable|string|max:255',
|
||||
'locale' => 'nullable|string|max:10',
|
||||
'two_factor_optin'=> 'nullable|boolean',
|
||||
'location_id' => 'nullable|integer|exists:locations,id',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
if (Gate::allows('self.profile') && ! config('app.lock_passwords')) {
|
||||
foreach (['first_name', 'last_name', 'phone', 'website', 'gravatar'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$user->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('locale')) {
|
||||
$user->locale = $request->get('locale');
|
||||
}
|
||||
|
||||
if (
|
||||
$request->filled('two_factor_optin') &&
|
||||
Gate::allows('self.two_factor') &&
|
||||
Setting::getSettings()->two_factor_enabled == '1' &&
|
||||
! config('app.lock_passwords')
|
||||
) {
|
||||
$user->two_factor_optin = $request->get('two_factor_optin');
|
||||
}
|
||||
|
||||
if ($request->filled('location_id') && Gate::allows('self.edit_location') && ! config('app.lock_passwords')) {
|
||||
$user->location_id = $request->get('location_id');
|
||||
}
|
||||
|
||||
if ($user->save()) {
|
||||
return Response::make(
|
||||
Response::text('Profile updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Profile updated successfully',
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'phone' => $user->phone,
|
||||
'website' => $user->website,
|
||||
'locale' => $user->locale,
|
||||
'location_id'=> $user->location_id,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$user->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'first_name' => $schema->string()->description('First name'),
|
||||
'last_name' => $schema->string()->description('Last name'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'website' => $schema->string()->description('Personal website URL'),
|
||||
'gravatar' => $schema->string()->description('Gravatar email or hash'),
|
||||
'locale' => $schema->string()->description('Locale/language code (e.g. en-US)'),
|
||||
'two_factor_optin' => $schema->boolean()->description('Opt in to two-factor authentication (requires self.two_factor permission and 2FA enabled in settings)'),
|
||||
'location_id' => $schema->number()->description('Default location ID (requires self.edit_location permission)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the update succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'first_name' => $schema->string()->description('Updated first name'),
|
||||
'last_name' => $schema->string()->description('Updated last name'),
|
||||
'phone' => $schema->string()->description('Updated phone number'),
|
||||
'website' => $schema->string()->description('Updated website URL'),
|
||||
'locale' => $schema->string()->description('Updated locale'),
|
||||
'location_id' => $schema->number()->description('Updated location ID'),
|
||||
];
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
* list of urls, explode that out into an array to whitelist just those urls.
|
||||
*/
|
||||
$allowed_origins = env('CORS_ALLOWED_ORIGINS') !== null ?
|
||||
explode(',', env('CORS_ALLOWED_ORIGINS')) : [];
|
||||
explode(',', env('CORS_ALLOWED_ORIGINS')) : ['*'];
|
||||
|
||||
/**
|
||||
* Original Laravel CORS package config file modifications end here
|
||||
@@ -41,6 +41,6 @@ return [
|
||||
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
||||
'exposed_headers' => [],
|
||||
'max_age' => 0,
|
||||
'paths' => ['api/*', 'sanctum/csrf-cookie'],
|
||||
'paths' => ['api/*', 'sanctum/csrf-cookie', '.well-known/*', 'oauth/*', 'mcp/*'],
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Mcp;
|
||||
|
||||
use App\Mcp\Tools\UpdateProfileTool;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\ResponseFactory;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UpdateProfileToolTest extends TestCase
|
||||
{
|
||||
private User $user;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->user = User::factory()->create();
|
||||
$this->actingAs($this->user);
|
||||
}
|
||||
|
||||
private function handle(array $args): ResponseFactory
|
||||
{
|
||||
return (new UpdateProfileTool)->handle(new Request($args));
|
||||
}
|
||||
|
||||
public function test_updates_profile_fields()
|
||||
{
|
||||
$content = $this->handle([
|
||||
'first_name' => 'Updated',
|
||||
'last_name' => 'Name',
|
||||
'phone' => '555-1234',
|
||||
])->getStructuredContent();
|
||||
|
||||
$this->assertTrue($content['success']);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $this->user->id,
|
||||
'first_name' => 'Updated',
|
||||
'last_name' => 'Name',
|
||||
'phone' => '555-1234',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_updates_locale()
|
||||
{
|
||||
$content = $this->handle(['locale' => 'fr'])->getStructuredContent();
|
||||
|
||||
$this->assertTrue($content['success']);
|
||||
$this->assertDatabaseHas('users', ['id' => $this->user->id, 'locale' => 'fr']);
|
||||
}
|
||||
|
||||
public function test_updates_location_with_permission()
|
||||
{
|
||||
$this->actingAs(User::factory()->canEditOwnLocation()->create());
|
||||
$location = Location::factory()->create();
|
||||
|
||||
$content = $this->handle(['location_id' => $location->id])->getStructuredContent();
|
||||
|
||||
$this->assertTrue($content['success']);
|
||||
$this->assertEquals($location->id, $content['location_id']);
|
||||
}
|
||||
|
||||
public function test_does_not_update_location_without_permission()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
|
||||
$originalLocation = $this->user->location_id;
|
||||
|
||||
$this->handle(['location_id' => $location->id]);
|
||||
|
||||
$this->assertDatabaseHas('users', ['id' => $this->user->id, 'location_id' => $originalLocation]);
|
||||
}
|
||||
|
||||
public function test_only_updates_provided_fields()
|
||||
{
|
||||
$originalLastName = $this->user->last_name;
|
||||
|
||||
$this->handle(['first_name' => 'Changed']);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $this->user->id,
|
||||
'first_name' => 'Changed',
|
||||
'last_name' => $originalLastName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user