Additional tools
This commit is contained in:
@@ -10,29 +10,89 @@ use App\Mcp\Tools\CheckinLicenseTool;
|
||||
use App\Mcp\Tools\CheckoutAccessoryTool;
|
||||
use App\Mcp\Tools\CheckoutAssetTool;
|
||||
use App\Mcp\Tools\CheckoutComponentTool;
|
||||
use App\Mcp\Tools\CheckoutConsumableTool;
|
||||
use App\Mcp\Tools\CheckoutLicenseTool;
|
||||
use App\Mcp\Tools\CreateAccessoryTool;
|
||||
use App\Mcp\Tools\CreateAssetModelTool;
|
||||
use App\Mcp\Tools\CreateAssetTool;
|
||||
use App\Mcp\Tools\CreateCategoryTool;
|
||||
use App\Mcp\Tools\CreateCompanyTool;
|
||||
use App\Mcp\Tools\CreateComponentTool;
|
||||
use App\Mcp\Tools\CreateConsumableTool;
|
||||
use App\Mcp\Tools\CreateDepartmentTool;
|
||||
use App\Mcp\Tools\CreateDepreciationTool;
|
||||
use App\Mcp\Tools\CreateGroupTool;
|
||||
use App\Mcp\Tools\CreateLicenseTool;
|
||||
use App\Mcp\Tools\CreateLocationTool;
|
||||
use App\Mcp\Tools\CreateMaintenanceTool;
|
||||
use App\Mcp\Tools\CreateManufacturerTool;
|
||||
use App\Mcp\Tools\CreateStatusLabelTool;
|
||||
use App\Mcp\Tools\CreateSupplierTool;
|
||||
use App\Mcp\Tools\CreateUserTool;
|
||||
use App\Mcp\Tools\DeleteAccessoryTool;
|
||||
use App\Mcp\Tools\DeleteAssetModelTool;
|
||||
use App\Mcp\Tools\DeleteAssetTool;
|
||||
use App\Mcp\Tools\DeleteCategoryTool;
|
||||
use App\Mcp\Tools\DeleteCompanyTool;
|
||||
use App\Mcp\Tools\DeleteComponentTool;
|
||||
use App\Mcp\Tools\DeleteConsumableTool;
|
||||
use App\Mcp\Tools\DeleteDepartmentTool;
|
||||
use App\Mcp\Tools\DeleteDepreciationTool;
|
||||
use App\Mcp\Tools\DeleteGroupTool;
|
||||
use App\Mcp\Tools\DeleteLicenseTool;
|
||||
use App\Mcp\Tools\DeleteLocationTool;
|
||||
use App\Mcp\Tools\DeleteManufacturerTool;
|
||||
use App\Mcp\Tools\DeleteStatusLabelTool;
|
||||
use App\Mcp\Tools\DeleteSupplierTool;
|
||||
use App\Mcp\Tools\DeleteUserTool;
|
||||
use App\Mcp\Tools\GetActivityLogTool;
|
||||
use App\Mcp\Tools\GetCurrentUserTool;
|
||||
use App\Mcp\Tools\GetUserAssetsTool;
|
||||
use App\Mcp\Tools\ListAssetModelsTool;
|
||||
use App\Mcp\Tools\ListAssetsTool;
|
||||
use App\Mcp\Tools\ListCategoriesTool;
|
||||
use App\Mcp\Tools\ListCompaniesTool;
|
||||
use App\Mcp\Tools\ListConsumablesTool;
|
||||
use App\Mcp\Tools\ListDepreciationsTool;
|
||||
use App\Mcp\Tools\ListGroupsTool;
|
||||
use App\Mcp\Tools\ListLicensesTool;
|
||||
use App\Mcp\Tools\ListLocationsTool;
|
||||
use App\Mcp\Tools\ListMaintenancesTool;
|
||||
use App\Mcp\Tools\ListManufacturersTool;
|
||||
use App\Mcp\Tools\ListStatusLabelsTool;
|
||||
use App\Mcp\Tools\ListSuppliersTool;
|
||||
use App\Mcp\Tools\ListUsersTool;
|
||||
use App\Mcp\Tools\Reset2FATool;
|
||||
use App\Mcp\Tools\RestoreAssetTool;
|
||||
use App\Mcp\Tools\RestoreUserTool;
|
||||
use App\Mcp\Tools\ShowAssetModelTool;
|
||||
use App\Mcp\Tools\ShowAssetTool;
|
||||
use App\Mcp\Tools\ShowCategoryTool;
|
||||
use App\Mcp\Tools\ShowCompanyTool;
|
||||
use App\Mcp\Tools\ShowConsumableTool;
|
||||
use App\Mcp\Tools\ShowDepreciationTool;
|
||||
use App\Mcp\Tools\ShowGroupTool;
|
||||
use App\Mcp\Tools\ShowLicenseTool;
|
||||
use App\Mcp\Tools\ShowLocationTool;
|
||||
use App\Mcp\Tools\ShowManufacturerTool;
|
||||
use App\Mcp\Tools\ShowStatusLabelTool;
|
||||
use App\Mcp\Tools\ShowSupplierTool;
|
||||
use App\Mcp\Tools\ShowUserTool;
|
||||
use App\Mcp\Tools\UpdateAccessoryTool;
|
||||
use App\Mcp\Tools\UpdateAssetModelTool;
|
||||
use App\Mcp\Tools\UpdateAssetTool;
|
||||
use App\Mcp\Tools\UpdateCategoryTool;
|
||||
use App\Mcp\Tools\UpdateCompanyTool;
|
||||
use App\Mcp\Tools\UpdateComponentTool;
|
||||
use App\Mcp\Tools\UpdateConsumableTool;
|
||||
use App\Mcp\Tools\UpdateDepartmentTool;
|
||||
use App\Mcp\Tools\UpdateDepreciationTool;
|
||||
use App\Mcp\Tools\UpdateGroupTool;
|
||||
use App\Mcp\Tools\UpdateLicenseTool;
|
||||
use App\Mcp\Tools\UpdateLocationTool;
|
||||
use App\Mcp\Tools\UpdateManufacturerTool;
|
||||
use App\Mcp\Tools\UpdateStatusLabelTool;
|
||||
use App\Mcp\Tools\UpdateSupplierTool;
|
||||
use App\Mcp\Tools\UpdateUserTool;
|
||||
use Laravel\Mcp\Server;
|
||||
use Laravel\Mcp\Server\Attributes\Instructions;
|
||||
@@ -45,28 +105,51 @@ use Laravel\Mcp\Server\Attributes\Version;
|
||||
class SnipeMCPServer extends Server
|
||||
{
|
||||
protected array $tools = [
|
||||
// Assets
|
||||
ShowAssetTool::class,
|
||||
ListAssetsTool::class,
|
||||
CheckoutAssetTool::class,
|
||||
CheckinAssetTool::class,
|
||||
CreateAssetTool::class,
|
||||
UpdateAssetTool::class,
|
||||
DeleteAssetTool::class,
|
||||
RestoreAssetTool::class,
|
||||
CheckoutAssetTool::class,
|
||||
CheckinAssetTool::class,
|
||||
AuditAssetTool::class,
|
||||
|
||||
// Users
|
||||
ListUsersTool::class,
|
||||
ShowUserTool::class,
|
||||
CreateUserTool::class,
|
||||
UpdateUserTool::class,
|
||||
DeleteUserTool::class,
|
||||
RestoreUserTool::class,
|
||||
GetCurrentUserTool::class,
|
||||
GetUserAssetsTool::class,
|
||||
Reset2FATool::class,
|
||||
|
||||
// Accessories
|
||||
CreateAccessoryTool::class,
|
||||
UpdateAccessoryTool::class,
|
||||
DeleteAccessoryTool::class,
|
||||
CheckoutAccessoryTool::class,
|
||||
CheckinAccessoryTool::class,
|
||||
|
||||
// Components
|
||||
CreateComponentTool::class,
|
||||
UpdateComponentTool::class,
|
||||
DeleteComponentTool::class,
|
||||
CheckoutComponentTool::class,
|
||||
CheckinComponentTool::class,
|
||||
|
||||
// Consumables
|
||||
ListConsumablesTool::class,
|
||||
ShowConsumableTool::class,
|
||||
CreateConsumableTool::class,
|
||||
UpdateConsumableTool::class,
|
||||
DeleteConsumableTool::class,
|
||||
CheckoutConsumableTool::class,
|
||||
|
||||
// Licenses
|
||||
ListLicensesTool::class,
|
||||
ShowLicenseTool::class,
|
||||
CreateLicenseTool::class,
|
||||
@@ -74,9 +157,81 @@ class SnipeMCPServer extends Server
|
||||
DeleteLicenseTool::class,
|
||||
CheckoutLicenseTool::class,
|
||||
CheckinLicenseTool::class,
|
||||
|
||||
// Departments
|
||||
CreateDepartmentTool::class,
|
||||
UpdateDepartmentTool::class,
|
||||
DeleteDepartmentTool::class,
|
||||
|
||||
// Companies
|
||||
ListCompaniesTool::class,
|
||||
ShowCompanyTool::class,
|
||||
CreateCompanyTool::class,
|
||||
UpdateCompanyTool::class,
|
||||
DeleteCompanyTool::class,
|
||||
|
||||
// Categories
|
||||
ListCategoriesTool::class,
|
||||
ShowCategoryTool::class,
|
||||
CreateCategoryTool::class,
|
||||
UpdateCategoryTool::class,
|
||||
DeleteCategoryTool::class,
|
||||
|
||||
// Manufacturers
|
||||
ListManufacturersTool::class,
|
||||
ShowManufacturerTool::class,
|
||||
CreateManufacturerTool::class,
|
||||
UpdateManufacturerTool::class,
|
||||
DeleteManufacturerTool::class,
|
||||
|
||||
// Suppliers
|
||||
ListSuppliersTool::class,
|
||||
ShowSupplierTool::class,
|
||||
CreateSupplierTool::class,
|
||||
UpdateSupplierTool::class,
|
||||
DeleteSupplierTool::class,
|
||||
|
||||
// Status Labels
|
||||
ListStatusLabelsTool::class,
|
||||
ShowStatusLabelTool::class,
|
||||
CreateStatusLabelTool::class,
|
||||
UpdateStatusLabelTool::class,
|
||||
DeleteStatusLabelTool::class,
|
||||
|
||||
// Locations
|
||||
ListLocationsTool::class,
|
||||
ShowLocationTool::class,
|
||||
CreateLocationTool::class,
|
||||
UpdateLocationTool::class,
|
||||
DeleteLocationTool::class,
|
||||
|
||||
// Asset Models
|
||||
ListAssetModelsTool::class,
|
||||
ShowAssetModelTool::class,
|
||||
CreateAssetModelTool::class,
|
||||
UpdateAssetModelTool::class,
|
||||
DeleteAssetModelTool::class,
|
||||
|
||||
// Depreciations
|
||||
ListDepreciationsTool::class,
|
||||
ShowDepreciationTool::class,
|
||||
CreateDepreciationTool::class,
|
||||
UpdateDepreciationTool::class,
|
||||
DeleteDepreciationTool::class,
|
||||
|
||||
// Groups
|
||||
ListGroupsTool::class,
|
||||
ShowGroupTool::class,
|
||||
CreateGroupTool::class,
|
||||
UpdateGroupTool::class,
|
||||
DeleteGroupTool::class,
|
||||
|
||||
// Maintenance
|
||||
ListMaintenancesTool::class,
|
||||
CreateMaintenanceTool::class,
|
||||
|
||||
// Activity Log
|
||||
GetActivityLogTool::class,
|
||||
];
|
||||
|
||||
protected array $resources = [
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
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('checkout_consumable')]
|
||||
#[Title('Checkout Consumable')]
|
||||
#[Description('Check out a Snipe-IT consumable to a user')]
|
||||
class CheckoutConsumableTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'assigned_to' => 'required|integer',
|
||||
'note' => 'nullable|string|max:65535',
|
||||
]);
|
||||
|
||||
$consumable = $this->resolveConsumable($request);
|
||||
|
||||
if (! $consumable) {
|
||||
return Response::make(Response::error('Consumable not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('checkout', $consumable)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($consumable->numRemaining() <= 0) {
|
||||
return Response::make(Response::error('No units remaining'));
|
||||
}
|
||||
|
||||
$user = User::find($request->get('assigned_to'));
|
||||
|
||||
if (! $user) {
|
||||
return Response::make(Response::error('User not found'));
|
||||
}
|
||||
|
||||
$consumable->users()->attach($consumable->id, [
|
||||
'consumable_id' => $consumable->id,
|
||||
'created_by' => auth()->id(),
|
||||
'assigned_to' => $user->id,
|
||||
'note' => $request->get('note'),
|
||||
]);
|
||||
|
||||
event(new CheckoutableCheckedOut(
|
||||
$consumable,
|
||||
$user,
|
||||
auth()->user(),
|
||||
$request->get('note'),
|
||||
[],
|
||||
1,
|
||||
));
|
||||
|
||||
return Response::make(
|
||||
Response::text('Consumable '.$consumable->name.' checked out to '.$user->username)
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Consumable checked out successfully',
|
||||
'consumable_id' => $consumable->id,
|
||||
'consumable_name' => $consumable->name,
|
||||
'assigned_to_id' => $user->id,
|
||||
'assigned_to_username' => $user->username,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveConsumable(Request $request): ?Consumable
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Consumable::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Consumable::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the consumable to check out'),
|
||||
'name' => $schema->string()->description('Name of the consumable to check out'),
|
||||
'assigned_to' => $schema->number()->description('User ID to check out to (required)'),
|
||||
'note' => $schema->string()->description('Optional checkout note'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the checkout succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'consumable_id' => $schema->number()->description('Numeric ID of the consumable'),
|
||||
'consumable_name' => $schema->string()->description('Name of the consumable'),
|
||||
'assigned_to_id' => $schema->number()->description('ID of the user the consumable was checked out to'),
|
||||
'assigned_to_username' => $schema->string()->description('Username of the user the consumable was checked out to'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_asset_model')]
|
||||
#[Title('Create Asset Model')]
|
||||
#[Description('Create a new Snipe-IT asset model')]
|
||||
class CreateAssetModelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', AssetModel::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'category_id' => 'required|integer|exists:categories,id',
|
||||
'model_number' => 'nullable|string|max:255',
|
||||
'manufacturer_id' => 'nullable|integer|exists:manufacturers,id',
|
||||
'depreciation_id' => 'nullable|integer|exists:depreciations,id',
|
||||
'eol' => 'nullable|integer|min:0|max:240',
|
||||
'min_amt' => 'nullable|integer|min:0',
|
||||
'notes' => 'nullable|string',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'require_serial' => 'nullable|boolean',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$assetModel = new AssetModel;
|
||||
$assetModel->name = $request->get('name');
|
||||
$assetModel->category_id = $request->get('category_id');
|
||||
$assetModel->created_by = auth()->id();
|
||||
|
||||
foreach (['model_number', 'manufacturer_id', 'depreciation_id', 'eol', 'min_amt', 'notes', 'requestable', 'require_serial'] as $f) {
|
||||
if ($request->filled($f)) {
|
||||
$assetModel->{$f} = $request->get($f);
|
||||
}
|
||||
}
|
||||
|
||||
if ($assetModel->save()) {
|
||||
return Response::make(
|
||||
Response::text('Asset model '.$assetModel->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Asset model created successfully',
|
||||
'id' => $assetModel->id,
|
||||
'name' => $assetModel->name,
|
||||
'category_id' => $assetModel->category_id,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$assetModel->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Asset model name (required)'),
|
||||
'category_id' => $schema->number()->description('Category ID (required)'),
|
||||
'model_number' => $schema->string()->description('Model number'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'depreciation_id' => $schema->number()->description('Depreciation schedule ID'),
|
||||
'eol' => $schema->number()->description('End of life in months (0-240)'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity alert threshold'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'requestable' => $schema->boolean()->description('Whether the model can be requested'),
|
||||
'require_serial' => $schema->boolean()->description('Whether serial numbers are required'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the asset model was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new asset model'),
|
||||
'name' => $schema->string()->description('Name of the new asset model'),
|
||||
'category_id' => $schema->number()->description('Category ID of the new asset model'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_asset')]
|
||||
#[Title('Create Asset')]
|
||||
#[Description('Create a new Snipe-IT asset')]
|
||||
class CreateAssetTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Asset::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'model_id' => 'required|integer|exists:models,id',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'asset_tag' => 'required|string|max:255',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'serial' => 'nullable|string',
|
||||
'company_id' => 'nullable|integer',
|
||||
'location_id' => 'nullable|integer|exists:locations,id',
|
||||
'rtd_location_id' => 'nullable|integer|exists:locations,id',
|
||||
'supplier_id' => 'nullable|integer|exists:suppliers,id',
|
||||
'purchase_date' => 'nullable|date_format:Y-m-d',
|
||||
'purchase_cost' => 'nullable|numeric',
|
||||
'order_number' => 'nullable|string|max:191',
|
||||
'warranty_months' => 'nullable|integer|min:0|max:240',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'notes' => 'nullable|string|max:65535',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$asset = new Asset;
|
||||
$asset->model_id = $request->get('model_id');
|
||||
$asset->status_id = $request->get('status_id');
|
||||
$asset->asset_tag = $request->get('asset_tag');
|
||||
$asset->created_by = auth()->id();
|
||||
|
||||
foreach (['name', 'serial', 'company_id', 'location_id', 'rtd_location_id', 'supplier_id', 'purchase_date', 'purchase_cost', 'order_number', 'warranty_months', 'requestable', 'notes'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$asset->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
return Response::make(
|
||||
Response::text('Asset '.$asset->asset_tag.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Asset created successfully',
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
'name' => $asset->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$asset->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'model_id' => $schema->number()->description('Asset model ID (required)'),
|
||||
'status_id' => $schema->number()->description('Status label ID (required)'),
|
||||
'asset_tag' => $schema->string()->description('Asset tag (required)'),
|
||||
'name' => $schema->string()->description('Display name for the asset'),
|
||||
'serial' => $schema->string()->description('Serial number'),
|
||||
'company_id' => $schema->number()->description('Company ID'),
|
||||
'location_id' => $schema->number()->description('Current location ID'),
|
||||
'rtd_location_id' => $schema->number()->description('Default RTD location ID'),
|
||||
'supplier_id' => $schema->number()->description('Supplier ID'),
|
||||
'purchase_date' => $schema->string()->description('Purchase date (YYYY-MM-DD)'),
|
||||
'purchase_cost' => $schema->number()->description('Purchase cost'),
|
||||
'order_number' => $schema->string()->description('Order number'),
|
||||
'warranty_months' => $schema->number()->description('Warranty length in months (0-240)'),
|
||||
'requestable' => $schema->boolean()->description('Whether the asset is user-requestable'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the asset was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new asset'),
|
||||
'asset_tag' => $schema->string()->description('Asset tag of the new asset'),
|
||||
'name' => $schema->string()->description('Display name of the new asset'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Category;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_category')]
|
||||
#[Title('Create Category')]
|
||||
#[Description('Create a new Snipe-IT category')]
|
||||
class CreateCategoryTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Category::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'category_type' => 'required|string|in:asset,accessory,consumable,component,license',
|
||||
'checkin_email' => 'nullable|boolean',
|
||||
'require_acceptance' => 'nullable|boolean',
|
||||
'use_default_eula' => 'nullable|boolean',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$category = new Category;
|
||||
$category->name = $request->get('name');
|
||||
$category->category_type = $request->get('category_type');
|
||||
$category->created_by = auth()->id();
|
||||
|
||||
foreach (['checkin_email', 'require_acceptance', 'use_default_eula', 'notes'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$category->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($category->save()) {
|
||||
return Response::make(
|
||||
Response::text('Category '.$category->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Category created successfully',
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'category_type' => $category->category_type,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$category->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Category name (required)'),
|
||||
'category_type' => $schema->string()->description('Category type (required): asset, accessory, consumable, component, or license'),
|
||||
'checkin_email' => $schema->boolean()->description('Send checkin email when items are checked in'),
|
||||
'require_acceptance' => $schema->boolean()->description('Require user acceptance when checking out'),
|
||||
'use_default_eula' => $schema->boolean()->description('Use the default EULA'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the category was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new category'),
|
||||
'name' => $schema->string()->description('Name of the new category'),
|
||||
'category_type' => $schema->string()->description('Type of the new category'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_company')]
|
||||
#[Title('Create Company')]
|
||||
#[Description('Create a new Snipe-IT company')]
|
||||
class CreateCompanyTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Company::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'phone' => 'nullable|string',
|
||||
'fax' => 'nullable|string',
|
||||
'email' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$company = new Company;
|
||||
$company->name = $request->get('name');
|
||||
if ($request->filled('phone')) {
|
||||
$company->phone = $request->get('phone');
|
||||
}
|
||||
if ($request->filled('fax')) {
|
||||
$company->fax = $request->get('fax');
|
||||
}
|
||||
if ($request->filled('email')) {
|
||||
$company->email = $request->get('email');
|
||||
}
|
||||
if ($request->filled('notes')) {
|
||||
$company->notes = $request->get('notes');
|
||||
}
|
||||
$company->created_by = auth()->id();
|
||||
|
||||
if ($company->save()) {
|
||||
return Response::make(
|
||||
Response::text('Company '.$company->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Company created successfully',
|
||||
'id' => $company->id,
|
||||
'name' => $company->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$company->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Company name (required)'),
|
||||
'phone' => $schema->string()->description('Company phone number'),
|
||||
'fax' => $schema->string()->description('Company fax number'),
|
||||
'email' => $schema->string()->description('Company email address'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the company was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new company'),
|
||||
'name' => $schema->string()->description('Name of the new company'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Consumable;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_consumable')]
|
||||
#[Title('Create Consumable')]
|
||||
#[Description('Create a new Snipe-IT consumable')]
|
||||
class CreateConsumableTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Consumable::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'qty' => 'required|integer|min:0',
|
||||
'category_id' => 'required|integer|exists:categories,id',
|
||||
'company_id' => 'nullable|integer',
|
||||
'location_id' => 'nullable|integer|exists:locations,id',
|
||||
'manufacturer_id' => 'nullable|integer|exists:manufacturers,id',
|
||||
'supplier_id' => 'nullable|integer|exists:suppliers,id',
|
||||
'item_no' => 'nullable|string|max:255',
|
||||
'order_number' => 'nullable|string|max:255',
|
||||
'model_number' => 'nullable|string|max:255',
|
||||
'purchase_cost' => 'nullable|numeric|min:0',
|
||||
'purchase_date' => 'nullable|date_format:Y-m-d',
|
||||
'min_amt' => 'nullable|integer|min:0',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$consumable = new Consumable;
|
||||
$consumable->fill($request->only([
|
||||
'name', 'qty', 'category_id', 'company_id', 'location_id', 'manufacturer_id',
|
||||
'supplier_id', 'item_no', 'order_number', 'model_number', 'purchase_cost',
|
||||
'purchase_date', 'min_amt', 'requestable', 'notes',
|
||||
]));
|
||||
$consumable->created_by = auth()->id();
|
||||
|
||||
if ($consumable->save()) {
|
||||
return Response::make(
|
||||
Response::text('Consumable '.$consumable->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Consumable created successfully',
|
||||
'id' => $consumable->id,
|
||||
'name' => $consumable->name,
|
||||
'qty' => $consumable->qty,
|
||||
'category_id' => $consumable->category_id,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$consumable->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Consumable name (required)'),
|
||||
'qty' => $schema->number()->description('Total quantity in stock (required)'),
|
||||
'category_id' => $schema->number()->description('Category ID — must be a consumable category (required)'),
|
||||
'company_id' => $schema->number()->description('Company ID'),
|
||||
'location_id' => $schema->number()->description('Location ID'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'supplier_id' => $schema->number()->description('Supplier ID'),
|
||||
'item_no' => $schema->string()->description('Item number'),
|
||||
'order_number' => $schema->string()->description('Order number'),
|
||||
'model_number' => $schema->string()->description('Model number'),
|
||||
'purchase_cost' => $schema->number()->description('Purchase cost per unit'),
|
||||
'purchase_date' => $schema->string()->description('Purchase date (YYYY-MM-DD)'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity threshold for alerts'),
|
||||
'requestable' => $schema->boolean()->description('Whether users can request this consumable'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the consumable was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new consumable'),
|
||||
'name' => $schema->string()->description('Name of the new consumable'),
|
||||
'qty' => $schema->number()->description('Total quantity'),
|
||||
'category_id' => $schema->number()->description('Category ID'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Depreciation;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_depreciation')]
|
||||
#[Title('Create Depreciation')]
|
||||
#[Description('Create a new Snipe-IT depreciation schedule')]
|
||||
class CreateDepreciationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Depreciation::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'months' => 'required|integer|min:1|max:3600',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$depreciation = new Depreciation;
|
||||
$depreciation->name = $request->get('name');
|
||||
$depreciation->months = $request->get('months');
|
||||
|
||||
if ($depreciation->save()) {
|
||||
return Response::make(
|
||||
Response::text('Depreciation '.$depreciation->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Depreciation created successfully',
|
||||
'id' => $depreciation->id,
|
||||
'name' => $depreciation->name,
|
||||
'months' => $depreciation->months,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$depreciation->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Depreciation name (required)'),
|
||||
'months' => $schema->number()->description('Depreciation period in months (required, 1-3600)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the depreciation was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new depreciation'),
|
||||
'name' => $schema->string()->description('Name of the new depreciation'),
|
||||
'months' => $schema->number()->description('Depreciation period in months'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Group;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_group')]
|
||||
#[Title('Create Group')]
|
||||
#[Description('Create a new Snipe-IT permission group')]
|
||||
class CreateGroupTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('superadmin')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$group = new Group;
|
||||
$group->name = $request->get('name');
|
||||
if ($request->filled('notes')) {
|
||||
$group->notes = $request->get('notes');
|
||||
}
|
||||
$group->created_by = auth()->id();
|
||||
|
||||
if ($group->save()) {
|
||||
return Response::make(
|
||||
Response::text('Group '.$group->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Group created successfully',
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$group->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Group name (required, must be unique)'),
|
||||
'notes' => $schema->string()->description('Notes about the group'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the group was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new group'),
|
||||
'name' => $schema->string()->description('Name of the new group'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Location;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_location')]
|
||||
#[Title('Create Location')]
|
||||
#[Description('Create a new Snipe-IT location')]
|
||||
class CreateLocationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Location::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'address' => 'nullable|string',
|
||||
'address2' => 'nullable|string',
|
||||
'city' => 'nullable|string',
|
||||
'state' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'zip' => 'nullable|string',
|
||||
'phone' => 'nullable|string|max:255',
|
||||
'fax' => 'nullable|string|max:255',
|
||||
'currency' => 'nullable|string',
|
||||
'parent_id' => 'nullable|integer|exists:locations,id',
|
||||
'manager_id' => 'nullable|integer|exists:users,id',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$location = new Location;
|
||||
$location->name = $request->get('name');
|
||||
|
||||
foreach (['address', 'address2', 'city', 'state', 'country', 'zip', 'phone', 'fax', 'currency', 'parent_id', 'manager_id'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$location->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($location->save()) {
|
||||
return Response::make(
|
||||
Response::text('Location '.$location->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Location created successfully',
|
||||
'id' => $location->id,
|
||||
'name' => $location->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$location->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Location name (required)'),
|
||||
'address' => $schema->string()->description('Street address'),
|
||||
'address2' => $schema->string()->description('Address line 2'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Zip code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'currency' => $schema->string()->description('Currency code'),
|
||||
'parent_id' => $schema->number()->description('Parent location ID'),
|
||||
'manager_id' => $schema->number()->description('Manager user ID'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the location was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new location'),
|
||||
'name' => $schema->string()->description('Name of the new location'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Maintenance;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_maintenance')]
|
||||
#[Title('Create Maintenance')]
|
||||
#[Description('Create a new asset maintenance record')]
|
||||
class CreateMaintenanceTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('update', Asset::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'asset_id' => 'required|integer|exists:assets,id',
|
||||
'title' => 'required|string|max:255',
|
||||
'asset_maintenance_type' => 'nullable|string|max:255',
|
||||
'supplier_id' => 'nullable|integer|exists:suppliers,id',
|
||||
'is_warranty' => 'nullable|boolean',
|
||||
'cost' => 'nullable|numeric|min:0',
|
||||
'start_date' => 'nullable|date_format:Y-m-d',
|
||||
'completion_date' => 'nullable|date_format:Y-m-d',
|
||||
'notes' => 'nullable|string',
|
||||
'user_id' => 'nullable|integer|exists:users,id',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$maintenance = new Maintenance;
|
||||
$maintenance->asset_id = $request->get('asset_id');
|
||||
$maintenance->name = $request->get('title');
|
||||
$maintenance->asset_maintenance_type = $request->get('asset_maintenance_type', 'Maintenance');
|
||||
$maintenance->start_date = $request->filled('start_date') ? $request->get('start_date') : now()->format('Y-m-d');
|
||||
$maintenance->created_by = auth()->id();
|
||||
$maintenance->is_warranty = 0;
|
||||
|
||||
foreach (['supplier_id', 'is_warranty', 'cost', 'completion_date', 'notes', 'user_id'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$maintenance->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($maintenance->save()) {
|
||||
$maintenance->load('asset');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Maintenance '.$maintenance->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Maintenance created successfully',
|
||||
'id' => $maintenance->id,
|
||||
'title' => $maintenance->name,
|
||||
'asset_id' => $maintenance->asset_id,
|
||||
'asset_tag' => $maintenance->asset?->asset_tag,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$maintenance->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'asset_id' => $schema->number()->description('Asset ID the maintenance is for (required)'),
|
||||
'title' => $schema->string()->description('Maintenance title/name (required)'),
|
||||
'asset_maintenance_type' => $schema->string()->description('Type of maintenance (e.g. maintenance, repair, upgrade)'),
|
||||
'supplier_id' => $schema->number()->description('Supplier ID'),
|
||||
'is_warranty' => $schema->boolean()->description('Whether this is a warranty maintenance'),
|
||||
'cost' => $schema->number()->description('Cost of the maintenance'),
|
||||
'start_date' => $schema->string()->description('Start date (YYYY-MM-DD, defaults to today)'),
|
||||
'completion_date' => $schema->string()->description('Completion date (YYYY-MM-DD)'),
|
||||
'notes' => $schema->string()->description('Notes about the maintenance'),
|
||||
'user_id' => $schema->number()->description('Technician user ID'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the maintenance was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new maintenance record'),
|
||||
'title' => $schema->string()->description('Title of the maintenance'),
|
||||
'asset_id' => $schema->number()->description('Asset ID'),
|
||||
'asset_tag' => $schema->string()->description('Asset tag'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_manufacturer')]
|
||||
#[Title('Create Manufacturer')]
|
||||
#[Description('Create a new Snipe-IT manufacturer')]
|
||||
class CreateManufacturerTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Manufacturer::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'url' => 'nullable|string|max:255',
|
||||
'support_url' => 'nullable|string|max:255',
|
||||
'support_email' => 'nullable|email|max:191',
|
||||
'support_phone' => 'nullable|string|max:191',
|
||||
'warranty_lookup_url' => 'nullable|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->fill($request->only([
|
||||
'name', 'url', 'support_url', 'support_email', 'support_phone', 'warranty_lookup_url', 'notes',
|
||||
]));
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return Response::make(
|
||||
Response::text('Manufacturer '.$manufacturer->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Manufacturer created successfully',
|
||||
'id' => $manufacturer->id,
|
||||
'name' => $manufacturer->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$manufacturer->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Manufacturer name (required)'),
|
||||
'url' => $schema->string()->description('Manufacturer website URL'),
|
||||
'support_url' => $schema->string()->description('Support website URL'),
|
||||
'support_email' => $schema->string()->description('Support email address'),
|
||||
'support_phone' => $schema->string()->description('Support phone number'),
|
||||
'warranty_lookup_url' => $schema->string()->description('Warranty lookup URL'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the manufacturer was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new manufacturer'),
|
||||
'name' => $schema->string()->description('Name of the new manufacturer'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Statuslabel;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_status_label')]
|
||||
#[Title('Create Status Label')]
|
||||
#[Description('Create a new Snipe-IT status label')]
|
||||
class CreateStatusLabelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Statuslabel::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'type' => 'required|string|in:deployable,pending,archived,undeployable',
|
||||
'color' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
'default_label' => 'nullable|boolean',
|
||||
'show_in_nav' => 'nullable|boolean',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$statuslabel = new Statuslabel;
|
||||
$statuslabel->name = $request->get('name');
|
||||
|
||||
$statusType = Statuslabel::getStatuslabelTypesForDB($request->get('type'));
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
|
||||
if ($request->filled('color')) {
|
||||
$statuslabel->color = $request->get('color');
|
||||
}
|
||||
if ($request->filled('notes')) {
|
||||
$statuslabel->notes = $request->get('notes');
|
||||
}
|
||||
$statuslabel->default_label = $request->get('default_label', 0);
|
||||
$statuslabel->show_in_nav = $request->get('show_in_nav', 0);
|
||||
|
||||
if ($statuslabel->save()) {
|
||||
return Response::make(
|
||||
Response::text('Status label '.$statuslabel->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Status label created successfully',
|
||||
'id' => $statuslabel->id,
|
||||
'name' => $statuslabel->name,
|
||||
'type' => $statuslabel->getStatuslabelType(),
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$statuslabel->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Status label name (required)'),
|
||||
'type' => $schema->string()->description('Status label type: deployable, pending, archived, or undeployable (required)'),
|
||||
'color' => $schema->string()->description('Display color in #RRGGBB format'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'default_label' => $schema->boolean()->description('Whether this is the default label'),
|
||||
'show_in_nav' => $schema->boolean()->description('Whether to show in navigation'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the status label was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new status label'),
|
||||
'name' => $schema->string()->description('Name of the new status label'),
|
||||
'type' => $schema->string()->description('Type of the new status label'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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('create_supplier')]
|
||||
#[Title('Create Supplier')]
|
||||
#[Description('Create a new Snipe-IT supplier')]
|
||||
class CreateSupplierTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('create', Supplier::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'address' => 'nullable|string',
|
||||
'address2' => 'nullable|string',
|
||||
'city' => 'nullable|string',
|
||||
'state' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'zip' => 'nullable|string',
|
||||
'phone' => 'nullable|string',
|
||||
'fax' => 'nullable|string',
|
||||
'email' => 'nullable|email',
|
||||
'url' => 'nullable|string',
|
||||
'contact' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
$supplier = new Supplier;
|
||||
$supplier->fill($request->only([
|
||||
'name', 'address', 'address2', 'city', 'state', 'country', 'zip',
|
||||
'phone', 'fax', 'email', 'url', 'contact', 'notes',
|
||||
]));
|
||||
|
||||
if ($supplier->save()) {
|
||||
return Response::make(
|
||||
Response::text('Supplier '.$supplier->name.' created successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Supplier created successfully',
|
||||
'id' => $supplier->id,
|
||||
'name' => $supplier->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Create failed: '.$supplier->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'name' => $schema->string()->description('Supplier name (required)'),
|
||||
'address' => $schema->string()->description('Address line 1'),
|
||||
'address2' => $schema->string()->description('Address line 2'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Postal code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'email' => $schema->string()->description('Email address'),
|
||||
'url' => $schema->string()->description('Website URL'),
|
||||
'contact' => $schema->string()->description('Contact name'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the supplier was created'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the new supplier'),
|
||||
'name' => $schema->string()->description('Name of the new supplier'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
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('delete_asset_model')]
|
||||
#[Title('Delete Asset Model')]
|
||||
#[Description('Soft-delete a Snipe-IT asset model by numeric ID or name')]
|
||||
class DeleteAssetModelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$model = $this->resolveModel($request);
|
||||
|
||||
if (! $model) {
|
||||
return Response::make(Response::error('Asset model not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $model)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($model->assets()->count() > 0) {
|
||||
return Response::make(Response::error('Model has assets and cannot be deleted'));
|
||||
}
|
||||
|
||||
$name = $model->name;
|
||||
|
||||
$model->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Asset model '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Asset model deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveModel(Request $request): ?AssetModel
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return AssetModel::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return AssetModel::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the asset model to delete'),
|
||||
'name' => $schema->string()->description('Name of the asset model to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted asset model'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Category;
|
||||
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('delete_category')]
|
||||
#[Title('Delete Category')]
|
||||
#[Description('Soft-delete a Snipe-IT category. The category must have no items assigned to it.')]
|
||||
class DeleteCategoryTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$category = $this->resolveCategory($request);
|
||||
|
||||
if (! $category) {
|
||||
return Response::make(Response::error('Category not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $category)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$name = $category->name;
|
||||
|
||||
try {
|
||||
$category->delete();
|
||||
} catch (\Exception $e) {
|
||||
return Response::make(Response::error('Category cannot be deleted: '.$e->getMessage()));
|
||||
}
|
||||
|
||||
return Response::make(
|
||||
Response::text('Category '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Category deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveCategory(Request $request): ?Category
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Category::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Category::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the category to delete'),
|
||||
'name' => $schema->string()->description('Name of the category to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted category'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Company;
|
||||
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('delete_company')]
|
||||
#[Title('Delete Company')]
|
||||
#[Description('Soft-delete a Snipe-IT company by numeric ID or name')]
|
||||
class DeleteCompanyTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$company = $this->resolveCompany($request);
|
||||
|
||||
if (! $company) {
|
||||
return Response::make(Response::error('Company not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $company)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$name = $company->name;
|
||||
|
||||
$company->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Company '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Company deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveCompany(Request $request): ?Company
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Company::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Company::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the company to delete'),
|
||||
'name' => $schema->string()->description('Name of the company to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted company'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Consumable;
|
||||
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('delete_consumable')]
|
||||
#[Title('Delete Consumable')]
|
||||
#[Description('Soft-delete a Snipe-IT consumable. The consumable must have no units currently checked out.')]
|
||||
class DeleteConsumableTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$consumable = $this->resolveConsumable($request);
|
||||
|
||||
if (! $consumable) {
|
||||
return Response::make(Response::error('Consumable not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $consumable)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($consumable->users()->count() > 0) {
|
||||
return Response::make(Response::error('Consumable has items checked out and cannot be deleted'));
|
||||
}
|
||||
|
||||
$name = $consumable->name;
|
||||
|
||||
$consumable->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Consumable '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Consumable deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveConsumable(Request $request): ?Consumable
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Consumable::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Consumable::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the consumable to delete'),
|
||||
'name' => $schema->string()->description('Name of the consumable to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted consumable'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Depreciation;
|
||||
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('delete_depreciation')]
|
||||
#[Title('Delete Depreciation')]
|
||||
#[Description('Soft-delete a Snipe-IT depreciation schedule by numeric ID or name')]
|
||||
class DeleteDepreciationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$dep = $this->resolveDepreciation($request);
|
||||
|
||||
if (! $dep) {
|
||||
return Response::make(Response::error('Depreciation not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $dep)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$name = $dep->name;
|
||||
|
||||
$dep->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Depreciation '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Depreciation deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveDepreciation(Request $request): ?Depreciation
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Depreciation::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Depreciation::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the depreciation to delete'),
|
||||
'name' => $schema->string()->description('Name of the depreciation to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted depreciation'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Group;
|
||||
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('delete_group')]
|
||||
#[Title('Delete Group')]
|
||||
#[Description('Delete a Snipe-IT permission group by ID or name. The group must have no users assigned.')]
|
||||
class DeleteGroupTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('superadmin')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$group = Group::find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$group = Group::where('name', $request->get('name'))->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $group) {
|
||||
return Response::make(Response::error('Group not found'));
|
||||
}
|
||||
|
||||
$groupName = $group->name;
|
||||
|
||||
if ($group->delete()) {
|
||||
return Response::make(
|
||||
Response::text('Group '.$groupName.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Group deleted successfully',
|
||||
'name' => $groupName,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Delete failed'));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric group ID to delete'),
|
||||
'name' => $schema->string()->description('Group name to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted group'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Location;
|
||||
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('delete_location')]
|
||||
#[Title('Delete Location')]
|
||||
#[Description('Soft-delete a Snipe-IT location by numeric ID or name')]
|
||||
class DeleteLocationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$location = $this->resolveLocation($request);
|
||||
|
||||
if (! $location) {
|
||||
return Response::make(Response::error('Location not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $location)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($location->users()->count() > 0) {
|
||||
return Response::make(Response::error('Location has users assigned and cannot be deleted'));
|
||||
}
|
||||
|
||||
if ($location->children()->count() > 0) {
|
||||
return Response::make(Response::error('Location has child locations and cannot be deleted'));
|
||||
}
|
||||
|
||||
$name = $location->name;
|
||||
|
||||
$location->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Location '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Location deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveLocation(Request $request): ?Location
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Location::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Location::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the location to delete'),
|
||||
'name' => $schema->string()->description('Name of the location to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted location'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
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('delete_manufacturer')]
|
||||
#[Title('Delete Manufacturer')]
|
||||
#[Description('Soft-delete a Snipe-IT manufacturer identified by numeric ID or name')]
|
||||
class DeleteManufacturerTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$manufacturer = $this->resolveManufacturer($request);
|
||||
|
||||
if (! $manufacturer) {
|
||||
return Response::make(Response::error('Manufacturer not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $manufacturer)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$name = $manufacturer->name;
|
||||
|
||||
$manufacturer->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Manufacturer '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Manufacturer deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveManufacturer(Request $request): ?Manufacturer
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Manufacturer::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Manufacturer::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the manufacturer to delete'),
|
||||
'name' => $schema->string()->description('Name of the manufacturer to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted manufacturer'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Statuslabel;
|
||||
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('delete_status_label')]
|
||||
#[Title('Delete Status Label')]
|
||||
#[Description('Soft-delete a Snipe-IT status label identified by numeric ID or name')]
|
||||
class DeleteStatusLabelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$label = $this->resolveStatusLabel($request);
|
||||
|
||||
if (! $label) {
|
||||
return Response::make(Response::error('Status label not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $label)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($label->assets()->count() > 0) {
|
||||
return Response::make(Response::error('Status label has assets assigned and cannot be deleted'));
|
||||
}
|
||||
|
||||
$name = $label->name;
|
||||
|
||||
$label->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Status label '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Status label deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveStatusLabel(Request $request): ?Statuslabel
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Statuslabel::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Statuslabel::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the status label to delete'),
|
||||
'name' => $schema->string()->description('Name of the status label to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted status label'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Supplier;
|
||||
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('delete_supplier')]
|
||||
#[Title('Delete Supplier')]
|
||||
#[Description('Soft-delete a Snipe-IT supplier identified by numeric ID or name')]
|
||||
class DeleteSupplierTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$supplier = $this->resolveSupplier($request);
|
||||
|
||||
if (! $supplier) {
|
||||
return Response::make(Response::error('Supplier not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', $supplier)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$name = $supplier->name;
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Supplier '.$name.' deleted successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Supplier deleted successfully',
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveSupplier(Request $request): ?Supplier
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Supplier::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Supplier::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the supplier to delete'),
|
||||
'name' => $schema->string()->description('Name of the supplier to delete'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the deletion succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'name' => $schema->string()->description('Name of the deleted supplier'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
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('get_activity_log')]
|
||||
#[Title('Get Activity Log')]
|
||||
#[Description('Retrieve the Snipe-IT activity log with optional filtering by item type, item ID, user, and action type')]
|
||||
class GetActivityLogTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('activity.view')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'item_type' => 'nullable|string|max:255',
|
||||
'item_id' => 'nullable|integer',
|
||||
'user_id' => 'nullable|integer',
|
||||
'action_type' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$logs = Actionlog::with('user', 'item')->orderBy('created_at', 'desc');
|
||||
|
||||
if ($request->filled('item_type')) {
|
||||
$logs->where('item_type', $request->get('item_type'));
|
||||
}
|
||||
|
||||
if ($request->filled('item_id')) {
|
||||
$logs->where('item_id', $request->get('item_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('user_id')) {
|
||||
$logs->where('user_id', $request->get('user_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('action_type')) {
|
||||
$logs->where('action_type', $request->get('action_type'));
|
||||
}
|
||||
|
||||
$total = $logs->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $logs->skip($offset)->take($limit)->get();
|
||||
|
||||
$activityData = $results->map(fn (Actionlog $log) => [
|
||||
'id' => $log->id,
|
||||
'action_type' => $log->action_type,
|
||||
'item_type' => $log->item_type,
|
||||
'item_id' => $log->item_id,
|
||||
'user_id' => $log->user_id,
|
||||
'user' => $log->user?->username,
|
||||
'note' => $log->note,
|
||||
'created_at' => $log->created_at?->toDateTimeString(),
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} activity log entries, returning ".count($activityData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'activity' => $activityData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'item_type' => $schema->string()->description('Filter by item type (e.g. App\\Models\\Asset)'),
|
||||
'item_id' => $schema->number()->description('Filter by item ID'),
|
||||
'user_id' => $schema->number()->description('Filter by user ID'),
|
||||
'action_type' => $schema->string()->description('Filter by action type (e.g. checkout, checkin, update)'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching log entries')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'activity' => $schema->array()->description('List of activity log entries')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
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('get_current_user')]
|
||||
#[Title('Get Current User')]
|
||||
#[Description('Return information about the currently authenticated Snipe-IT user')]
|
||||
class GetCurrentUserTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! auth()->check()) {
|
||||
return Response::make(Response::error('Not authenticated'));
|
||||
}
|
||||
|
||||
$user = User::with('company', 'department', 'userloc')->find(auth()->id());
|
||||
|
||||
if (! $user) {
|
||||
return Response::make(Response::error('Not authenticated'));
|
||||
}
|
||||
|
||||
return Response::make(
|
||||
Response::text('Current user: '.$user->username)
|
||||
)->withStructuredContent([
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'email' => $user->email,
|
||||
'company' => $user->company?->name,
|
||||
'department' => $user->department?->name,
|
||||
'location' => $user->userloc?->name,
|
||||
'employee_num' => $user->employee_num,
|
||||
'title' => $user->jobtitle,
|
||||
'phone' => $user->phone,
|
||||
'activated' => (bool) $user->activated,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric user ID')->required(),
|
||||
'username' => $schema->string()->description('Username')->required(),
|
||||
'first_name' => $schema->string()->description('First name'),
|
||||
'last_name' => $schema->string()->description('Last name'),
|
||||
'email' => $schema->string()->description('Email address'),
|
||||
'company' => $schema->string()->description('Company name'),
|
||||
'department' => $schema->string()->description('Department name'),
|
||||
'location' => $schema->string()->description('Default location name'),
|
||||
'employee_num' => $schema->string()->description('Employee number'),
|
||||
'title' => $schema->string()->description('Job title'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'activated' => $schema->boolean()->description('Whether the account is activated'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
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('get_user_assets')]
|
||||
#[Title('Get User Assets')]
|
||||
#[Description('Return all assets currently checked out to a Snipe-IT user')]
|
||||
class GetUserAssetsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', User::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', Asset::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$user = User::find($request->get('id'));
|
||||
|
||||
if (! $user) {
|
||||
return Response::make(Response::error('User not found'));
|
||||
}
|
||||
|
||||
$assets = Asset::where('assigned_to', $user->id)
|
||||
->where('assigned_type', User::class)
|
||||
->with('model', 'status', 'location')
|
||||
->get();
|
||||
|
||||
$data = $assets->map(fn ($asset) => [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
'name' => $asset->name,
|
||||
'serial' => $asset->serial,
|
||||
'model' => $asset->model?->name,
|
||||
'status' => $asset->status?->name,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Found '.count($data).' assets for user '.$user->username)
|
||||
)->withStructuredContent([
|
||||
'user_id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'total' => count($data),
|
||||
'assets' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the user whose assets should be listed (required)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'user_id' => $schema->number()->description('Numeric ID of the user')->required(),
|
||||
'username' => $schema->string()->description('Username of the user')->required(),
|
||||
'total' => $schema->number()->description('Total number of assets checked out to the user')->required(),
|
||||
'assets' => $schema->array()->description('List of checked-out assets'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
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('list_asset_models')]
|
||||
#[Title('List Asset Models')]
|
||||
#[Description('Search and list Snipe-IT asset models with optional filtering and pagination')]
|
||||
class ListAssetModelsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', AssetModel::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'category_id' => 'nullable|integer',
|
||||
'manufacturer_id' => 'nullable|integer',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$models = AssetModel::with('category', 'manufacturer', 'depreciation')
|
||||
->withCount('assets as assets_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$models->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$models->where('category_id', $request->get('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$models->where('manufacturer_id', $request->get('manufacturer_id'));
|
||||
}
|
||||
|
||||
$models->orderBy('models.created_at', 'desc');
|
||||
|
||||
$total = $models->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $models->skip($offset)->take($limit)->get();
|
||||
|
||||
$modelsData = $results->map(fn (AssetModel $model) => [
|
||||
'id' => $model->id,
|
||||
'name' => $model->name,
|
||||
'model_number' => $model->model_number,
|
||||
'category_id' => $model->category_id,
|
||||
'category' => $model->category?->name,
|
||||
'manufacturer_id' => $model->manufacturer_id,
|
||||
'manufacturer' => $model->manufacturer?->name,
|
||||
'assets_count' => $model->assets_count,
|
||||
'eol' => $model->eol,
|
||||
'min_amt' => $model->min_amt,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} asset models, returning ".count($modelsData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'models' => $modelsData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across model name, model number'),
|
||||
'category_id' => $schema->number()->description('Filter by category ID'),
|
||||
'manufacturer_id' => $schema->number()->description('Filter by manufacturer ID'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching asset models')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'models' => $schema->array()->description('List of asset models')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Category;
|
||||
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('list_categories')]
|
||||
#[Title('List Categories')]
|
||||
#[Description('Search and list Snipe-IT categories with optional filtering by type and pagination')]
|
||||
class ListCategoriesTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Category::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'category_type' => 'nullable|string|in:asset,accessory,consumable,component,license',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$categories = Category::withCount(
|
||||
'showableAssets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count'
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$categories->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_type')) {
|
||||
$categories->where('category_type', $request->get('category_type'));
|
||||
}
|
||||
|
||||
$categories->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $categories->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $categories->skip($offset)->take($limit)->get();
|
||||
|
||||
$categoriesData = $results->map(fn (Category $category) => [
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'category_type' => $category->category_type,
|
||||
'assets_count' => $category->assets_count,
|
||||
'accessories_count' => $category->accessories_count,
|
||||
'consumables_count' => $category->consumables_count,
|
||||
'components_count' => $category->components_count,
|
||||
'licenses_count' => $category->licenses_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} categories, returning ".count($categoriesData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'categories' => $categoriesData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across category name, type, notes'),
|
||||
'category_type' => $schema->string()->description('Filter by type: asset, accessory, consumable, component, or license'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching categories')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'categories' => $schema->array()->description('List of categories')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Company;
|
||||
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('list_companies')]
|
||||
#[Title('List Companies')]
|
||||
#[Description('Search and list Snipe-IT companies with optional filtering and pagination')]
|
||||
class ListCompaniesTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Company::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$companies = Company::withCount([
|
||||
'assets as assets_count' => fn ($q) => $q->AssetsForShow(),
|
||||
])->withCount('licenses as licenses_count', 'users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
$companies->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $companies->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $companies->skip($offset)->take($limit)->get();
|
||||
|
||||
$companiesData = $results->map(fn (Company $company) => [
|
||||
'id' => $company->id,
|
||||
'name' => $company->name,
|
||||
'phone' => $company->phone,
|
||||
'fax' => $company->fax,
|
||||
'email' => $company->email,
|
||||
'assets_count' => $company->assets_count,
|
||||
'licenses_count' => $company->licenses_count,
|
||||
'users_count' => $company->users_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} companies, returning ".count($companiesData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'companies' => $companiesData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across company name, phone, fax, email'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching companies')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'companies' => $schema->array()->description('List of companies')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Consumable;
|
||||
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('list_consumables')]
|
||||
#[Title('List Consumables')]
|
||||
#[Description('Search and list Snipe-IT consumables with optional filtering by keyword, company, category, manufacturer, location, and pagination')]
|
||||
class ListConsumablesTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('index', Consumable::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'company_id' => 'nullable|integer',
|
||||
'category_id' => 'nullable|integer',
|
||||
'manufacturer_id' => 'nullable|integer',
|
||||
'location_id' => 'nullable|integer',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$consumables = Consumable::with('company', 'category', 'manufacturer', 'supplier', 'location')
|
||||
->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$consumables->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$consumables->where('consumables.company_id', $request->get('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$consumables->where('category_id', $request->get('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$consumables->where('manufacturer_id', $request->get('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$consumables->where('location_id', $request->get('location_id'));
|
||||
}
|
||||
|
||||
$total = $consumables->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $consumables->orderBy('consumables.created_at', 'desc')->skip($offset)->take($limit)->get();
|
||||
|
||||
$consumablesData = $results->map(fn (Consumable $consumable) => [
|
||||
'id' => $consumable->id,
|
||||
'name' => $consumable->name,
|
||||
'qty' => $consumable->qty,
|
||||
'users_count' => $consumable->users_count,
|
||||
'category' => $consumable->category?->name,
|
||||
'manufacturer' => $consumable->manufacturer?->name,
|
||||
'company' => $consumable->company?->name,
|
||||
'location' => $consumable->location?->name,
|
||||
'purchase_cost' => $consumable->purchase_cost,
|
||||
'purchase_date' => $consumable->purchase_date?->format('Y-m-d'),
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} consumables, returning ".count($consumablesData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'consumables' => $consumablesData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across consumable name and other fields'),
|
||||
'company_id' => $schema->number()->description('Filter by company ID'),
|
||||
'category_id' => $schema->number()->description('Filter by category ID'),
|
||||
'manufacturer_id' => $schema->number()->description('Filter by manufacturer ID'),
|
||||
'location_id' => $schema->number()->description('Filter by location ID'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching consumables')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Depreciation;
|
||||
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('list_depreciations')]
|
||||
#[Title('List Depreciations')]
|
||||
#[Description('Search and list Snipe-IT depreciation schedules with optional filtering and pagination')]
|
||||
class ListDepreciationsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Depreciation::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$depreciations = Depreciation::withCount('models as models_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
$depreciations->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $depreciations->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $depreciations->skip($offset)->take($limit)->get();
|
||||
|
||||
$depreciationsData = $results->map(fn (Depreciation $dep) => [
|
||||
'id' => $dep->id,
|
||||
'name' => $dep->name,
|
||||
'months' => $dep->months,
|
||||
'models_count' => $dep->models_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} depreciations, returning ".count($depreciationsData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'depreciations' => $depreciationsData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across depreciation name'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching depreciations')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'depreciations' => $schema->array()->description('List of depreciation schedules')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Group;
|
||||
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('list_groups')]
|
||||
#[Title('List Groups')]
|
||||
#[Description('List Snipe-IT permission groups with optional search and pagination')]
|
||||
class ListGroupsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('superadmin')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$groups = Group::withCount('users as users_count')
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$groups->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
$total = $groups->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $groups->skip($offset)->take($limit)->get();
|
||||
|
||||
$groupsData = $results->map(fn (Group $group) => [
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
'notes' => $group->notes,
|
||||
'users_count' => $group->users_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} groups, returning ".count($groupsData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'groups' => $groupsData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search groups by name or notes'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching groups')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'groups' => $schema->array()->description('List of groups')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Location;
|
||||
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('list_locations')]
|
||||
#[Title('List Locations')]
|
||||
#[Description('Search and list Snipe-IT locations with optional filtering and pagination')]
|
||||
class ListLocationsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Location::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'parent_id' => 'nullable|integer',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$locations = Location::with('parent')->withCount(
|
||||
'assets as assets_count',
|
||||
'users as users_count',
|
||||
'children as children_count'
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$locations->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('parent_id')) {
|
||||
$locations->where('parent_id', $request->get('parent_id'));
|
||||
}
|
||||
|
||||
$locations->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $locations->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $locations->skip($offset)->take($limit)->get();
|
||||
|
||||
$locationsData = $results->map(fn (Location $location) => [
|
||||
'id' => $location->id,
|
||||
'name' => $location->name,
|
||||
'address' => $location->address,
|
||||
'city' => $location->city,
|
||||
'state' => $location->state,
|
||||
'country' => $location->country,
|
||||
'zip' => $location->zip,
|
||||
'phone' => $location->phone,
|
||||
'parent_id' => $location->parent_id,
|
||||
'parent' => $location->parent?->name,
|
||||
'assets_count' => $location->assets_count,
|
||||
'users_count' => $location->users_count,
|
||||
'children_count' => $location->children_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} locations, returning ".count($locationsData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'locations' => $locationsData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across location name, city, state, country'),
|
||||
'parent_id' => $schema->number()->description('Filter by parent location ID'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching locations')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'locations' => $schema->array()->description('List of locations')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Maintenance;
|
||||
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('list_maintenances')]
|
||||
#[Title('List Maintenances')]
|
||||
#[Description('List asset maintenances with optional filtering by asset and pagination')]
|
||||
class ListMaintenancesTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Asset::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'asset_id' => 'nullable|integer',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$maintenances = Maintenance::with('asset', 'supplier');
|
||||
|
||||
if ($request->filled('asset_id')) {
|
||||
$maintenances->where('asset_id', $request->get('asset_id'));
|
||||
}
|
||||
|
||||
$maintenances->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $maintenances->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $maintenances->skip($offset)->take($limit)->get();
|
||||
|
||||
$maintenancesData = $results->map(fn (Maintenance $maintenance) => [
|
||||
'id' => $maintenance->id,
|
||||
'title' => $maintenance->name,
|
||||
'asset_id' => $maintenance->asset_id,
|
||||
'asset_tag' => $maintenance->asset?->asset_tag,
|
||||
'is_warranty' => (bool) $maintenance->is_warranty,
|
||||
'cost' => $maintenance->cost,
|
||||
'start_date' => $maintenance->start_date,
|
||||
'completion_date' => $maintenance->completion_date,
|
||||
'supplier' => $maintenance->supplier?->name,
|
||||
'notes' => $maintenance->notes,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} maintenances, returning ".count($maintenancesData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'maintenances' => $maintenancesData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'asset_id' => $schema->number()->description('Filter by asset ID'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching maintenances')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'maintenances' => $schema->array()->description('List of maintenances')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
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('list_manufacturers')]
|
||||
#[Title('List Manufacturers')]
|
||||
#[Description('Search and list Snipe-IT manufacturers with optional filtering and pagination')]
|
||||
class ListManufacturersTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Manufacturer::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$manufacturers = Manufacturer::withCount(
|
||||
'assets as assets_count',
|
||||
'licenses as licenses_count',
|
||||
'accessories as accessories_count',
|
||||
'components as components_count'
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$manufacturers->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
$manufacturers->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $manufacturers->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $manufacturers->skip($offset)->take($limit)->get();
|
||||
|
||||
$manufacturersData = $results->map(fn (Manufacturer $manufacturer) => [
|
||||
'id' => $manufacturer->id,
|
||||
'name' => $manufacturer->name,
|
||||
'url' => $manufacturer->url,
|
||||
'support_url' => $manufacturer->support_url,
|
||||
'support_email' => $manufacturer->support_email,
|
||||
'support_phone' => $manufacturer->support_phone,
|
||||
'assets_count' => $manufacturer->assets_count,
|
||||
'licenses_count' => $manufacturer->licenses_count,
|
||||
'accessories_count' => $manufacturer->accessories_count,
|
||||
'components_count' => $manufacturer->components_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} manufacturers, returning ".count($manufacturersData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'manufacturers' => $manufacturersData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across manufacturer name and notes'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching manufacturers')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'manufacturers' => $schema->array()->description('List of manufacturers')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Statuslabel;
|
||||
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('list_status_labels')]
|
||||
#[Title('List Status Labels')]
|
||||
#[Description('Search and list Snipe-IT status labels with optional filtering and pagination')]
|
||||
class ListStatusLabelsTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Statuslabel::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'status_type' => 'nullable|string|in:deployable,pending,archived,undeployable',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$labels = Statuslabel::withCount('assets as assets_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$labels->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('status_type')) {
|
||||
$type = $request->get('status_type');
|
||||
if ($type === 'deployable') {
|
||||
$labels->Deployable();
|
||||
} elseif ($type === 'pending') {
|
||||
$labels->Pending();
|
||||
} elseif ($type === 'archived') {
|
||||
$labels->Archived();
|
||||
} elseif ($type === 'undeployable') {
|
||||
$labels->Undeployable();
|
||||
}
|
||||
}
|
||||
|
||||
$total = $labels->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $labels->skip($offset)->take($limit)->get();
|
||||
|
||||
$labelsData = $results->map(fn (Statuslabel $label) => [
|
||||
'id' => $label->id,
|
||||
'name' => $label->name,
|
||||
'type' => $label->getStatuslabelType(),
|
||||
'color' => $label->color,
|
||||
'assets_count' => $label->assets_count,
|
||||
'deployable' => $label->deployable,
|
||||
'pending' => $label->pending,
|
||||
'archived' => $label->archived,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} status labels, returning ".count($labelsData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'status_labels' => $labelsData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across status label name and notes'),
|
||||
'status_type' => $schema->string()->description('Filter by type: deployable, pending, archived, undeployable'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching status labels')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'status_labels' => $schema->array()->description('List of status labels')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Supplier;
|
||||
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('list_suppliers')]
|
||||
#[Title('List Suppliers')]
|
||||
#[Description('Search and list Snipe-IT suppliers with optional filtering and pagination')]
|
||||
class ListSuppliersTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('view', Supplier::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'search' => 'nullable|string|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:500',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$suppliers = Supplier::withCount(
|
||||
'assets as assets_count',
|
||||
'licenses as licenses_count'
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers->TextSearch($request->get('search'));
|
||||
}
|
||||
|
||||
$suppliers->orderBy('created_at', 'desc');
|
||||
|
||||
$total = $suppliers->count();
|
||||
$limit = $request->filled('limit') ? (int) $request->get('limit') : 25;
|
||||
$offset = $request->filled('offset') ? (int) $request->get('offset') : 0;
|
||||
|
||||
$results = $suppliers->skip($offset)->take($limit)->get();
|
||||
|
||||
$suppliersData = $results->map(fn (Supplier $supplier) => [
|
||||
'id' => $supplier->id,
|
||||
'name' => $supplier->name,
|
||||
'address' => $supplier->address,
|
||||
'city' => $supplier->city,
|
||||
'state' => $supplier->state,
|
||||
'country' => $supplier->country,
|
||||
'phone' => $supplier->phone,
|
||||
'email' => $supplier->email,
|
||||
'url' => $supplier->url,
|
||||
'assets_count' => $supplier->assets_count,
|
||||
'licenses_count' => $supplier->licenses_count,
|
||||
])->values()->all();
|
||||
|
||||
return Response::make(
|
||||
Response::text("Found {$total} suppliers, returning ".count($suppliersData))
|
||||
)->withStructuredContent([
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
'suppliers' => $suppliersData,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'search' => $schema->string()->description('Keyword to search across supplier fields'),
|
||||
'limit' => $schema->number()->description('Number of results to return (default: 25, max: 500)'),
|
||||
'offset' => $schema->number()->description('Number of results to skip for pagination (default: 0)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'total' => $schema->number()->description('Total number of matching suppliers')->required(),
|
||||
'offset' => $schema->number()->description('Current pagination offset')->required(),
|
||||
'limit' => $schema->number()->description('Results per page')->required(),
|
||||
'suppliers' => $schema->array()->description('List of suppliers')->required(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\User;
|
||||
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('reset_2fa')]
|
||||
#[Title('Reset Two-Factor Authentication')]
|
||||
#[Description('Reset two-factor authentication for a Snipe-IT user')]
|
||||
class Reset2FATool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('update', User::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$user = User::find($request->get('id'));
|
||||
|
||||
if (! $user) {
|
||||
return Response::make(Response::error('User not found'));
|
||||
}
|
||||
|
||||
$user->two_factor_secret = null;
|
||||
$user->two_factor_enrolled = 0;
|
||||
$user->two_factor_optin = 0;
|
||||
$user->save();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Two-factor authentication reset for '.$user->username)
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Two-factor authentication reset successfully',
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the user whose 2FA should be reset (required)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the reset succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the user'),
|
||||
'username' => $schema->string()->description('Username of the user'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Asset;
|
||||
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('restore_asset')]
|
||||
#[Title('Restore Asset')]
|
||||
#[Description('Restore a soft-deleted Snipe-IT asset')]
|
||||
class RestoreAssetTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$asset = Asset::withTrashed()->find($request->get('id'));
|
||||
|
||||
if (! $asset) {
|
||||
return Response::make(Response::error('Asset not found'));
|
||||
}
|
||||
|
||||
if (! $asset->deleted_at) {
|
||||
return Response::make(Response::error('Asset is not deleted'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', Asset::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$asset->restore();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Asset '.$asset->asset_tag.' restored successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Asset restored successfully',
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the asset to restore (required)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the restore succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the restored asset'),
|
||||
'asset_tag' => $schema->string()->description('Asset tag of the restored asset'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\User;
|
||||
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('restore_user')]
|
||||
#[Title('Restore User')]
|
||||
#[Description('Restore a soft-deleted Snipe-IT user')]
|
||||
class RestoreUserTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('delete', User::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$user = User::withTrashed()->find($request->get('id'));
|
||||
|
||||
if (! $user) {
|
||||
return Response::make(Response::error('User not found'));
|
||||
}
|
||||
|
||||
if (! $user->deleted_at) {
|
||||
return Response::make(Response::error('User is not deleted'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('delete', User::class)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$user->restore();
|
||||
|
||||
return Response::make(
|
||||
Response::text('User '.$user->username.' restored successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'User restored successfully',
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the user to restore (required)'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'success' => $schema->boolean()->description('True if the restore succeeded'),
|
||||
'message' => $schema->string()->description('Human-readable result message')->required(),
|
||||
'id' => $schema->number()->description('Numeric ID of the restored user'),
|
||||
'username' => $schema->string()->description('Username of the restored user'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
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('show_asset_model')]
|
||||
#[Title('Show Asset Model Details')]
|
||||
#[Description('Look up a single Snipe-IT asset model by numeric ID or name and return its full details')]
|
||||
class ShowAssetModelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$model = $this->resolveModel($request);
|
||||
|
||||
if ($model === false) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $model) {
|
||||
return Response::make(Response::error('Asset model not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $model)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$model->loadCount('assets as assets_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Asset model '.$model->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $model->id,
|
||||
'name' => $model->name,
|
||||
'model_number' => $model->model_number,
|
||||
'category_id' => $model->category_id,
|
||||
'category' => $model->category?->name,
|
||||
'manufacturer_id' => $model->manufacturer_id,
|
||||
'manufacturer' => $model->manufacturer?->name,
|
||||
'depreciation_id' => $model->depreciation_id,
|
||||
'depreciation' => $model->depreciation?->name,
|
||||
'assets_count' => $model->assets_count,
|
||||
'eol' => $model->eol,
|
||||
'min_amt' => $model->min_amt,
|
||||
'notes' => $model->notes,
|
||||
'created_at' => $model->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $model->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveModel(Request $request): AssetModel|false|null
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return AssetModel::with('category', 'manufacturer', 'depreciation')->find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return AssetModel::with('category', 'manufacturer', 'depreciation')->where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the asset model to look up'),
|
||||
'name' => $schema->string()->description('Name of the asset model to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric asset model ID'),
|
||||
'name' => $schema->string()->description('Asset model name'),
|
||||
'model_number' => $schema->string()->description('Model number'),
|
||||
'category_id' => $schema->number()->description('Category ID'),
|
||||
'category' => $schema->string()->description('Category name'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'manufacturer' => $schema->string()->description('Manufacturer name'),
|
||||
'depreciation_id' => $schema->number()->description('Depreciation schedule ID'),
|
||||
'depreciation' => $schema->string()->description('Depreciation schedule name'),
|
||||
'assets_count' => $schema->number()->description('Number of assets using this model'),
|
||||
'eol' => $schema->number()->description('End of life in months'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity alert threshold'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Category;
|
||||
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('show_category')]
|
||||
#[Title('Show Category Details')]
|
||||
#[Description('Look up a single Snipe-IT category by numeric ID or name and return its full details')]
|
||||
class ShowCategoryTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$withCounts = [
|
||||
'showableAssets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count',
|
||||
];
|
||||
|
||||
$category = null;
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$category = Category::withCount($withCounts)->find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$category = Category::withCount($withCounts)->where('name', $request->get('name'))->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $category) {
|
||||
return Response::make(Response::error('Category not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $category)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
return Response::make(
|
||||
Response::text('Category '.$category->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'category_type' => $category->category_type,
|
||||
'assets_count' => $category->assets_count,
|
||||
'accessories_count' => $category->accessories_count,
|
||||
'consumables_count' => $category->consumables_count,
|
||||
'components_count' => $category->components_count,
|
||||
'licenses_count' => $category->licenses_count,
|
||||
'notes' => $category->notes,
|
||||
'created_at' => $category->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $category->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the category to look up'),
|
||||
'name' => $schema->string()->description('Name of the category to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric category ID'),
|
||||
'name' => $schema->string()->description('Category name'),
|
||||
'category_type' => $schema->string()->description('Category type: asset, accessory, consumable, component, or license'),
|
||||
'assets_count' => $schema->number()->description('Number of assets in this category'),
|
||||
'accessories_count' => $schema->number()->description('Number of accessories in this category'),
|
||||
'consumables_count' => $schema->number()->description('Number of consumables in this category'),
|
||||
'components_count' => $schema->number()->description('Number of components in this category'),
|
||||
'licenses_count' => $schema->number()->description('Number of licenses in this category'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Company;
|
||||
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('show_company')]
|
||||
#[Title('Show Company Details')]
|
||||
#[Description('Look up a single Snipe-IT company by numeric ID or name and return its full details')]
|
||||
class ShowCompanyTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$company = null;
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$company = Company::withCount([
|
||||
'assets as assets_count' => fn ($q) => $q->AssetsForShow(),
|
||||
])->withCount('users as users_count')->find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$company = Company::withCount([
|
||||
'assets as assets_count' => fn ($q) => $q->AssetsForShow(),
|
||||
])->withCount('users as users_count')->where('name', $request->get('name'))->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $company) {
|
||||
return Response::make(Response::error('Company not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $company)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
return Response::make(
|
||||
Response::text('Company '.$company->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $company->id,
|
||||
'name' => $company->name,
|
||||
'phone' => $company->phone,
|
||||
'fax' => $company->fax,
|
||||
'email' => $company->email,
|
||||
'assets_count' => $company->assets_count,
|
||||
'users_count' => $company->users_count,
|
||||
'notes' => $company->notes,
|
||||
'created_at' => $company->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $company->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the company to look up'),
|
||||
'name' => $schema->string()->description('Name of the company to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric company ID'),
|
||||
'name' => $schema->string()->description('Company name'),
|
||||
'phone' => $schema->string()->description('Company phone number'),
|
||||
'fax' => $schema->string()->description('Company fax number'),
|
||||
'email' => $schema->string()->description('Company email address'),
|
||||
'assets_count' => $schema->number()->description('Number of assets belonging to this company'),
|
||||
'users_count' => $schema->number()->description('Number of users belonging to this company'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Consumable;
|
||||
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('show_consumable')]
|
||||
#[Title('Show Consumable Details')]
|
||||
#[Description('Look up a single Snipe-IT consumable by numeric ID or name and return its full details')]
|
||||
class ShowConsumableTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$consumable = null;
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$consumable = Consumable::with('company', 'category', 'manufacturer', 'supplier', 'location')->find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$consumable = Consumable::with('company', 'category', 'manufacturer', 'supplier', 'location')->where('name', $request->get('name'))->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Either id or name is required'));
|
||||
}
|
||||
|
||||
if (! $consumable) {
|
||||
return Response::make(Response::error('Consumable not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $consumable)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$usersCount = $consumable->users()->count();
|
||||
|
||||
return Response::make(
|
||||
Response::text('Consumable '.$consumable->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $consumable->id,
|
||||
'name' => $consumable->name,
|
||||
'qty' => $consumable->qty,
|
||||
'users_count' => $usersCount,
|
||||
'min_amt' => $consumable->min_amt,
|
||||
'category_id' => $consumable->category_id,
|
||||
'category' => $consumable->category?->name,
|
||||
'manufacturer_id' => $consumable->manufacturer_id,
|
||||
'manufacturer' => $consumable->manufacturer?->name,
|
||||
'company_id' => $consumable->company_id,
|
||||
'company' => $consumable->company?->name,
|
||||
'location_id' => $consumable->location_id,
|
||||
'location' => $consumable->location?->name,
|
||||
'purchase_cost' => $consumable->purchase_cost,
|
||||
'purchase_date' => $consumable->purchase_date?->format('Y-m-d'),
|
||||
'order_number' => $consumable->order_number,
|
||||
'model_number' => $consumable->model_number,
|
||||
'notes' => $consumable->notes,
|
||||
'created_at' => $consumable->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $consumable->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the consumable to look up'),
|
||||
'name' => $schema->string()->description('Name of the consumable to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric consumable ID'),
|
||||
'name' => $schema->string()->description('Consumable name'),
|
||||
'qty' => $schema->number()->description('Total quantity in stock'),
|
||||
'users_count' => $schema->number()->description('Number of units checked out'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity alert threshold'),
|
||||
'category_id' => $schema->number()->description('Category ID'),
|
||||
'category' => $schema->string()->description('Category name'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'manufacturer' => $schema->string()->description('Manufacturer name'),
|
||||
'company_id' => $schema->number()->description('Company ID'),
|
||||
'company' => $schema->string()->description('Company name'),
|
||||
'location_id' => $schema->number()->description('Location ID'),
|
||||
'location' => $schema->string()->description('Location name'),
|
||||
'purchase_cost' => $schema->string()->description('Purchase cost'),
|
||||
'purchase_date' => $schema->string()->description('Purchase date (YYYY-MM-DD)'),
|
||||
'order_number' => $schema->string()->description('Order number'),
|
||||
'model_number' => $schema->string()->description('Model number'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last updated timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Depreciation;
|
||||
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('show_depreciation')]
|
||||
#[Title('Show Depreciation Details')]
|
||||
#[Description('Look up a single Snipe-IT depreciation schedule by numeric ID or name and return its full details')]
|
||||
class ShowDepreciationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$depreciation = $this->resolveDepreciation($request);
|
||||
|
||||
if ($depreciation === false) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $depreciation) {
|
||||
return Response::make(Response::error('Depreciation not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $depreciation)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$depreciation->loadCount('models as models_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Depreciation '.$depreciation->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $depreciation->id,
|
||||
'name' => $depreciation->name,
|
||||
'months' => $depreciation->months,
|
||||
'models_count' => $depreciation->models_count,
|
||||
'created_at' => $depreciation->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $depreciation->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveDepreciation(Request $request): Depreciation|false|null
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Depreciation::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Depreciation::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the depreciation to look up'),
|
||||
'name' => $schema->string()->description('Name of the depreciation to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric depreciation ID'),
|
||||
'name' => $schema->string()->description('Depreciation name'),
|
||||
'months' => $schema->number()->description('Depreciation period in months'),
|
||||
'models_count' => $schema->number()->description('Number of asset models using this depreciation'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Group;
|
||||
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('show_group')]
|
||||
#[Title('Show Group')]
|
||||
#[Description('Look up a single Snipe-IT permission group by numeric ID or name')]
|
||||
class ShowGroupTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('superadmin')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$group = Group::withCount('users as users_count')->find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$group = Group::withCount('users as users_count')
|
||||
->where('name', $request->get('name'))
|
||||
->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $group) {
|
||||
return Response::make(Response::error('Group not found'));
|
||||
}
|
||||
|
||||
return Response::make(
|
||||
Response::text('Group '.$group->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
'notes' => $group->notes,
|
||||
'permissions' => $group->decodePermissions(),
|
||||
'users_count' => $group->users_count,
|
||||
]);
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric group ID'),
|
||||
'name' => $schema->string()->description('Group name to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric group ID')->required(),
|
||||
'name' => $schema->string()->description('Group name')->required(),
|
||||
'notes' => $schema->string()->description('Notes about the group'),
|
||||
'permissions' => $schema->object()->description('Decoded permissions array'),
|
||||
'users_count' => $schema->number()->description('Number of users in this group'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Location;
|
||||
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('show_location')]
|
||||
#[Title('Show Location Details')]
|
||||
#[Description('Look up a single Snipe-IT location by numeric ID or name and return its full details')]
|
||||
class ShowLocationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$location = $this->resolveLocation($request);
|
||||
|
||||
if ($location === false) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $location) {
|
||||
return Response::make(Response::error('Location not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $location)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$location->loadCount('assets as assets_count', 'users as users_count', 'children as children_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Location '.$location->name.' found')
|
||||
)->withStructuredContent([
|
||||
'id' => $location->id,
|
||||
'name' => $location->name,
|
||||
'address' => $location->address,
|
||||
'address2' => $location->address2,
|
||||
'city' => $location->city,
|
||||
'state' => $location->state,
|
||||
'country' => $location->country,
|
||||
'zip' => $location->zip,
|
||||
'phone' => $location->phone,
|
||||
'fax' => $location->fax,
|
||||
'parent_id' => $location->parent_id,
|
||||
'parent' => $location->parent?->name,
|
||||
'assets_count' => $location->assets_count,
|
||||
'users_count' => $location->users_count,
|
||||
'children_count' => $location->children_count,
|
||||
'created_at' => $location->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $location->updated_at?->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveLocation(Request $request): Location|false|null
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Location::with('parent')->find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Location::with('parent')->where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the location to look up'),
|
||||
'name' => $schema->string()->description('Name of the location to look up'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric location ID'),
|
||||
'name' => $schema->string()->description('Location name'),
|
||||
'address' => $schema->string()->description('Street address'),
|
||||
'address2' => $schema->string()->description('Address line 2'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Zip code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'parent_id' => $schema->number()->description('Parent location ID'),
|
||||
'parent' => $schema->string()->description('Parent location name'),
|
||||
'assets_count' => $schema->number()->description('Number of assets at this location'),
|
||||
'users_count' => $schema->number()->description('Number of users at this location'),
|
||||
'children_count' => $schema->number()->description('Number of child locations'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
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('show_manufacturer')]
|
||||
#[Title('Show Manufacturer')]
|
||||
#[Description('Show details of a Snipe-IT manufacturer identified by numeric ID or name')]
|
||||
class ShowManufacturerTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$manufacturer = $this->resolveManufacturer($request);
|
||||
|
||||
if (! $manufacturer) {
|
||||
if (! $request->filled('id') && ! $request->filled('name')) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Manufacturer not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $manufacturer)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$manufacturer->loadCount('assets as assets_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Manufacturer: '.$manufacturer->name)
|
||||
)->withStructuredContent([
|
||||
'id' => $manufacturer->id,
|
||||
'name' => $manufacturer->name,
|
||||
'url' => $manufacturer->url,
|
||||
'support_url' => $manufacturer->support_url,
|
||||
'support_email' => $manufacturer->support_email,
|
||||
'support_phone' => $manufacturer->support_phone,
|
||||
'warranty_lookup_url' => $manufacturer->warranty_lookup_url,
|
||||
'assets_count' => $manufacturer->assets_count,
|
||||
'notes' => $manufacturer->notes,
|
||||
'created_at' => $manufacturer->created_at?->toISOString(),
|
||||
'updated_at' => $manufacturer->updated_at?->toISOString(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveManufacturer(Request $request): ?Manufacturer
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Manufacturer::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Manufacturer::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the manufacturer to show'),
|
||||
'name' => $schema->string()->description('Name of the manufacturer to show'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the manufacturer'),
|
||||
'name' => $schema->string()->description('Manufacturer name')->required(),
|
||||
'url' => $schema->string()->description('Manufacturer website URL'),
|
||||
'support_url' => $schema->string()->description('Support website URL'),
|
||||
'support_email' => $schema->string()->description('Support email address'),
|
||||
'support_phone' => $schema->string()->description('Support phone number'),
|
||||
'warranty_lookup_url' => $schema->string()->description('Warranty lookup URL'),
|
||||
'assets_count' => $schema->number()->description('Number of assets from this manufacturer'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Statuslabel;
|
||||
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('show_status_label')]
|
||||
#[Title('Show Status Label')]
|
||||
#[Description('Show details of a Snipe-IT status label identified by numeric ID or name')]
|
||||
class ShowStatusLabelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$label = $this->resolveStatusLabel($request);
|
||||
|
||||
if (! $label) {
|
||||
if (! $request->filled('id') && ! $request->filled('name')) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Status label not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $label)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$label->loadCount('assets as assets_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Status label: '.$label->name)
|
||||
)->withStructuredContent([
|
||||
'id' => $label->id,
|
||||
'name' => $label->name,
|
||||
'type' => $label->getStatuslabelType(),
|
||||
'color' => $label->color,
|
||||
'deployable' => $label->deployable,
|
||||
'pending' => $label->pending,
|
||||
'archived' => $label->archived,
|
||||
'assets_count' => $label->assets_count,
|
||||
'default_label' => $label->default_label,
|
||||
'show_in_nav' => $label->show_in_nav,
|
||||
'notes' => $label->notes,
|
||||
'created_at' => $label->created_at?->toISOString(),
|
||||
'updated_at' => $label->updated_at?->toISOString(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveStatusLabel(Request $request): ?Statuslabel
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Statuslabel::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Statuslabel::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the status label to show'),
|
||||
'name' => $schema->string()->description('Name of the status label to show'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the status label'),
|
||||
'name' => $schema->string()->description('Status label name')->required(),
|
||||
'type' => $schema->string()->description('Status label type (deployable, pending, archived, undeployable)'),
|
||||
'color' => $schema->string()->description('Display color'),
|
||||
'deployable' => $schema->boolean()->description('Whether status is deployable'),
|
||||
'pending' => $schema->boolean()->description('Whether status is pending'),
|
||||
'archived' => $schema->boolean()->description('Whether status is archived'),
|
||||
'assets_count' => $schema->number()->description('Number of assets with this status'),
|
||||
'default_label' => $schema->boolean()->description('Whether this is the default label'),
|
||||
'show_in_nav' => $schema->boolean()->description('Whether to show in navigation'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Supplier;
|
||||
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('show_supplier')]
|
||||
#[Title('Show Supplier')]
|
||||
#[Description('Show details of a Snipe-IT supplier identified by numeric ID or name')]
|
||||
class ShowSupplierTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$supplier = $this->resolveSupplier($request);
|
||||
|
||||
if (! $supplier) {
|
||||
if (! $request->filled('id') && ! $request->filled('name')) {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Supplier not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('view', $supplier)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$supplier->loadCount('assets as assets_count', 'licenses as licenses_count');
|
||||
|
||||
return Response::make(
|
||||
Response::text('Supplier: '.$supplier->name)
|
||||
)->withStructuredContent([
|
||||
'id' => $supplier->id,
|
||||
'name' => $supplier->name,
|
||||
'address' => $supplier->address,
|
||||
'address2' => $supplier->address2,
|
||||
'city' => $supplier->city,
|
||||
'state' => $supplier->state,
|
||||
'country' => $supplier->country,
|
||||
'zip' => $supplier->zip,
|
||||
'phone' => $supplier->phone,
|
||||
'fax' => $supplier->fax,
|
||||
'email' => $supplier->email,
|
||||
'url' => $supplier->url,
|
||||
'contact' => $supplier->contact,
|
||||
'notes' => $supplier->notes,
|
||||
'assets_count' => $supplier->assets_count,
|
||||
'licenses_count' => $supplier->licenses_count,
|
||||
'created_at' => $supplier->created_at?->toISOString(),
|
||||
'updated_at' => $supplier->updated_at?->toISOString(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveSupplier(Request $request): ?Supplier
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Supplier::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Supplier::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the supplier to show'),
|
||||
'name' => $schema->string()->description('Name of the supplier to show'),
|
||||
];
|
||||
}
|
||||
|
||||
public function outputSchema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID of the supplier'),
|
||||
'name' => $schema->string()->description('Supplier name')->required(),
|
||||
'address' => $schema->string()->description('Address line 1'),
|
||||
'address2' => $schema->string()->description('Address line 2'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Postal code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'email' => $schema->string()->description('Email address'),
|
||||
'url' => $schema->string()->description('Website URL'),
|
||||
'contact' => $schema->string()->description('Contact name'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'assets_count' => $schema->number()->description('Number of assets from this supplier'),
|
||||
'licenses_count' => $schema->number()->description('Number of licenses from this supplier'),
|
||||
'created_at' => $schema->string()->description('Creation timestamp'),
|
||||
'updated_at' => $schema->string()->description('Last update timestamp'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
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_asset_model')]
|
||||
#[Title('Update Asset Model')]
|
||||
#[Description('Update fields on a Snipe-IT asset model identified by numeric ID or name')]
|
||||
class UpdateAssetModelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'category_id' => 'nullable|integer|exists:categories,id',
|
||||
'manufacturer_id' => 'nullable|integer|exists:manufacturers,id',
|
||||
'depreciation_id' => 'nullable|integer|exists:depreciations,id',
|
||||
'model_number' => 'nullable|string|max:255',
|
||||
'eol' => 'nullable|integer|min:0|max:240',
|
||||
'min_amt' => 'nullable|integer|min:0',
|
||||
'notes' => 'nullable|string',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'require_serial' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$model = $this->resolveModel($request);
|
||||
|
||||
if (! $model) {
|
||||
return Response::make(Response::error('Asset model not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $model)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$model->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
foreach (['category_id', 'manufacturer_id', 'depreciation_id', 'model_number', 'eol', 'min_amt', 'notes', 'requestable', 'require_serial'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$model->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($model->save()) {
|
||||
return Response::make(
|
||||
Response::text('Asset model '.$model->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Asset model updated successfully',
|
||||
'id' => $model->id,
|
||||
'name' => $model->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$model->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveModel(Request $request): ?AssetModel
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return AssetModel::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return AssetModel::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the asset model'),
|
||||
'name' => $schema->string()->description('Name to identify the asset model'),
|
||||
'new_name' => $schema->string()->description('New name (renames the asset model)'),
|
||||
'category_id' => $schema->number()->description('Category ID'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'depreciation_id' => $schema->number()->description('Depreciation schedule ID'),
|
||||
'model_number' => $schema->string()->description('Model number'),
|
||||
'eol' => $schema->number()->description('End of life in months (0-240)'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity alert threshold'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'requestable' => $schema->boolean()->description('Whether the model can be requested'),
|
||||
'require_serial' => $schema->boolean()->description('Whether serial numbers are required'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the asset model'),
|
||||
'name' => $schema->string()->description('Name of the asset model'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Category;
|
||||
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_category')]
|
||||
#[Title('Update Category')]
|
||||
#[Description('Update fields on a Snipe-IT category identified by numeric ID or name')]
|
||||
class UpdateCategoryTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'category_type' => 'nullable|string|in:asset,accessory,consumable,component,license',
|
||||
'notes' => 'nullable|string',
|
||||
'checkin_email' => 'nullable|boolean',
|
||||
'require_acceptance' => 'nullable|boolean',
|
||||
'use_default_eula' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$category = $this->resolveCategory($request);
|
||||
|
||||
if (! $category) {
|
||||
return Response::make(Response::error('Category not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $category)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$category->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
foreach (['category_type', 'notes', 'checkin_email', 'require_acceptance', 'use_default_eula'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$category->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($category->save()) {
|
||||
return Response::make(
|
||||
Response::text('Category '.$category->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Category updated successfully',
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'category_type' => $category->category_type,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$category->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveCategory(Request $request): ?Category
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Category::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Category::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the category'),
|
||||
'name' => $schema->string()->description('Name to identify the category'),
|
||||
'new_name' => $schema->string()->description('New name (renames the category)'),
|
||||
'category_type' => $schema->string()->description('Category type: asset, accessory, consumable, component, or license'),
|
||||
'checkin_email' => $schema->boolean()->description('Send checkin email when items are checked in'),
|
||||
'require_acceptance' => $schema->boolean()->description('Require user acceptance when checking out'),
|
||||
'use_default_eula' => $schema->boolean()->description('Use the default EULA'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the category'),
|
||||
'name' => $schema->string()->description('Name of the category'),
|
||||
'category_type' => $schema->string()->description('Type of the category'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Company;
|
||||
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_company')]
|
||||
#[Title('Update Company')]
|
||||
#[Description('Update fields on a Snipe-IT company identified by numeric ID or name')]
|
||||
class UpdateCompanyTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'phone' => 'nullable|string',
|
||||
'fax' => 'nullable|string',
|
||||
'email' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$company = $this->resolveCompany($request);
|
||||
|
||||
if (! $company) {
|
||||
return Response::make(Response::error('Company not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $company)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$company->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
foreach (['phone', 'fax', 'email', 'notes'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$company->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($company->save()) {
|
||||
return Response::make(
|
||||
Response::text('Company '.$company->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Company updated successfully',
|
||||
'id' => $company->id,
|
||||
'name' => $company->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$company->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveCompany(Request $request): ?Company
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Company::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Company::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the company'),
|
||||
'name' => $schema->string()->description('Name to identify the company'),
|
||||
'new_name' => $schema->string()->description('New name (renames the company)'),
|
||||
'phone' => $schema->string()->description('Company phone number'),
|
||||
'fax' => $schema->string()->description('Company fax number'),
|
||||
'email' => $schema->string()->description('Company email address'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the company'),
|
||||
'name' => $schema->string()->description('Name of the company'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Consumable;
|
||||
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_consumable')]
|
||||
#[Title('Update Consumable')]
|
||||
#[Description('Update fields on a Snipe-IT consumable identified by numeric ID or name')]
|
||||
class UpdateConsumableTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'qty' => 'nullable|integer|min:0',
|
||||
'category_id' => 'nullable|integer|exists:categories,id',
|
||||
'company_id' => 'nullable|integer',
|
||||
'location_id' => 'nullable|integer|exists:locations,id',
|
||||
'manufacturer_id' => 'nullable|integer|exists:manufacturers,id',
|
||||
'supplier_id' => 'nullable|integer',
|
||||
'purchase_cost' => 'nullable|numeric|min:0',
|
||||
'purchase_date' => 'nullable|date_format:Y-m-d',
|
||||
'min_amt' => 'nullable|integer|min:0',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$consumable = $this->resolveConsumable($request);
|
||||
|
||||
if (! $consumable) {
|
||||
return Response::make(Response::error('Consumable not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $consumable)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
$updatable = [
|
||||
'qty', 'category_id', 'company_id', 'location_id', 'manufacturer_id',
|
||||
'supplier_id', 'purchase_cost', 'purchase_date', 'min_amt', 'notes',
|
||||
];
|
||||
|
||||
foreach ($updatable as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$consumable->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$consumable->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
if ($consumable->save()) {
|
||||
return Response::make(
|
||||
Response::text('Consumable '.$consumable->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Consumable updated successfully',
|
||||
'id' => $consumable->id,
|
||||
'name' => $consumable->name,
|
||||
'qty' => $consumable->qty,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$consumable->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveConsumable(Request $request): ?Consumable
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Consumable::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Consumable::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the consumable'),
|
||||
'name' => $schema->string()->description('Name to identify the consumable'),
|
||||
'new_name' => $schema->string()->description('New name (renames the consumable)'),
|
||||
'qty' => $schema->number()->description('Total quantity in stock'),
|
||||
'category_id' => $schema->number()->description('Category ID'),
|
||||
'company_id' => $schema->number()->description('Company ID'),
|
||||
'location_id' => $schema->number()->description('Location ID'),
|
||||
'manufacturer_id' => $schema->number()->description('Manufacturer ID'),
|
||||
'supplier_id' => $schema->number()->description('Supplier ID'),
|
||||
'purchase_cost' => $schema->number()->description('Purchase cost per unit'),
|
||||
'purchase_date' => $schema->string()->description('Purchase date (YYYY-MM-DD)'),
|
||||
'min_amt' => $schema->number()->description('Minimum quantity alert threshold'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the consumable'),
|
||||
'name' => $schema->string()->description('Name of the consumable'),
|
||||
'qty' => $schema->number()->description('Total quantity'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Depreciation;
|
||||
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_depreciation')]
|
||||
#[Title('Update Depreciation')]
|
||||
#[Description('Update fields on a Snipe-IT depreciation schedule identified by numeric ID or name')]
|
||||
class UpdateDepreciationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'months' => 'nullable|integer|min:1|max:3600',
|
||||
]);
|
||||
|
||||
$dep = $this->resolveDepreciation($request);
|
||||
|
||||
if (! $dep) {
|
||||
return Response::make(Response::error('Depreciation not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $dep)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$dep->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
if ($request->filled('months')) {
|
||||
$dep->months = $request->get('months');
|
||||
}
|
||||
|
||||
if ($dep->save()) {
|
||||
return Response::make(
|
||||
Response::text('Depreciation '.$dep->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Depreciation updated successfully',
|
||||
'id' => $dep->id,
|
||||
'name' => $dep->name,
|
||||
'months' => $dep->months,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$dep->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveDepreciation(Request $request): ?Depreciation
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Depreciation::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Depreciation::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the depreciation'),
|
||||
'name' => $schema->string()->description('Name to identify the depreciation'),
|
||||
'new_name' => $schema->string()->description('New name (renames the depreciation)'),
|
||||
'months' => $schema->number()->description('Depreciation period in months (1-3600)'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the depreciation'),
|
||||
'name' => $schema->string()->description('Name of the depreciation'),
|
||||
'months' => $schema->number()->description('Depreciation period in months'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Group;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
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_group')]
|
||||
#[Title('Update Group')]
|
||||
#[Description('Update an existing Snipe-IT permission group by ID or name')]
|
||||
class UpdateGroupTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
if (! Gate::allows('superadmin')) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return Response::make(Response::error($e->validator->errors()->first()));
|
||||
}
|
||||
|
||||
if ($request->filled('id')) {
|
||||
$group = Group::find($request->get('id'));
|
||||
} elseif ($request->filled('name')) {
|
||||
$group = Group::where('name', $request->get('name'))->first();
|
||||
} else {
|
||||
return Response::make(Response::error('Please provide an id or name'));
|
||||
}
|
||||
|
||||
if (! $group) {
|
||||
return Response::make(Response::error('Group not found'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$group->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$group->notes = $request->get('notes');
|
||||
}
|
||||
|
||||
if ($group->save()) {
|
||||
return Response::make(
|
||||
Response::text('Group '.$group->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Group updated successfully',
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$group->getErrors()->first()));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric group ID to update'),
|
||||
'name' => $schema->string()->description('Group name to look up for updating'),
|
||||
'new_name' => $schema->string()->description('New name to rename the group to'),
|
||||
'notes' => $schema->string()->description('Updated notes for the group'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the updated group'),
|
||||
'name' => $schema->string()->description('Name of the updated group'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Location;
|
||||
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_location')]
|
||||
#[Title('Update Location')]
|
||||
#[Description('Update fields on a Snipe-IT location identified by numeric ID or name')]
|
||||
class UpdateLocationTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'address' => 'nullable|string',
|
||||
'city' => 'nullable|string',
|
||||
'state' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'zip' => 'nullable|string',
|
||||
'phone' => 'nullable|string',
|
||||
'fax' => 'nullable|string',
|
||||
'currency' => 'nullable|string',
|
||||
'parent_id' => 'nullable|integer|exists:locations,id',
|
||||
'manager_id' => 'nullable|integer|exists:users,id',
|
||||
]);
|
||||
|
||||
$location = $this->resolveLocation($request);
|
||||
|
||||
if (! $location) {
|
||||
return Response::make(Response::error('Location not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $location)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$location->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
foreach (['address', 'city', 'state', 'country', 'zip', 'phone', 'fax', 'currency', 'parent_id', 'manager_id'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$location->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($location->save()) {
|
||||
return Response::make(
|
||||
Response::text('Location '.$location->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Location updated successfully',
|
||||
'id' => $location->id,
|
||||
'name' => $location->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$location->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveLocation(Request $request): ?Location
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Location::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Location::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the location'),
|
||||
'name' => $schema->string()->description('Name to identify the location'),
|
||||
'new_name' => $schema->string()->description('New name (renames the location)'),
|
||||
'address' => $schema->string()->description('Street address'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Zip code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'currency' => $schema->string()->description('Currency code'),
|
||||
'parent_id' => $schema->number()->description('Parent location ID'),
|
||||
'manager_id' => $schema->number()->description('Manager user ID'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the location'),
|
||||
'name' => $schema->string()->description('Name of the location'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
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_manufacturer')]
|
||||
#[Title('Update Manufacturer')]
|
||||
#[Description('Update fields on a Snipe-IT manufacturer identified by numeric ID or name')]
|
||||
class UpdateManufacturerTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'url' => 'nullable|string|max:255',
|
||||
'support_url' => 'nullable|string|max:255',
|
||||
'support_email' => 'nullable|email|max:191',
|
||||
'support_phone' => 'nullable|string|max:191',
|
||||
'warranty_lookup_url' => 'nullable|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$manufacturer = $this->resolveManufacturer($request);
|
||||
|
||||
if (! $manufacturer) {
|
||||
return Response::make(Response::error('Manufacturer not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $manufacturer)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$manufacturer->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
$updatable = ['url', 'support_url', 'support_email', 'support_phone', 'warranty_lookup_url', 'notes'];
|
||||
|
||||
foreach ($updatable as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$manufacturer->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return Response::make(
|
||||
Response::text('Manufacturer '.$manufacturer->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Manufacturer updated successfully',
|
||||
'id' => $manufacturer->id,
|
||||
'name' => $manufacturer->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$manufacturer->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveManufacturer(Request $request): ?Manufacturer
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Manufacturer::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Manufacturer::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the manufacturer'),
|
||||
'name' => $schema->string()->description('Name to identify the manufacturer'),
|
||||
'new_name' => $schema->string()->description('New name (renames the manufacturer)'),
|
||||
'url' => $schema->string()->description('Manufacturer website URL'),
|
||||
'support_url' => $schema->string()->description('Support website URL'),
|
||||
'support_email' => $schema->string()->description('Support email address'),
|
||||
'support_phone' => $schema->string()->description('Support phone number'),
|
||||
'warranty_lookup_url' => $schema->string()->description('Warranty lookup URL'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the manufacturer'),
|
||||
'name' => $schema->string()->description('Name of the manufacturer'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Statuslabel;
|
||||
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_status_label')]
|
||||
#[Title('Update Status Label')]
|
||||
#[Description('Update fields on a Snipe-IT status label identified by numeric ID or name')]
|
||||
class UpdateStatusLabelTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'type' => 'nullable|string|in:deployable,pending,archived,undeployable',
|
||||
'color' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
'default_label' => 'nullable|boolean',
|
||||
'show_in_nav' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$label = $this->resolveStatusLabel($request);
|
||||
|
||||
if (! $label) {
|
||||
return Response::make(Response::error('Status label not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $label)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$label->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
if ($request->filled('type')) {
|
||||
$statusType = Statuslabel::getStatuslabelTypesForDB($request->get('type'));
|
||||
$label->deployable = $statusType['deployable'];
|
||||
$label->pending = $statusType['pending'];
|
||||
$label->archived = $statusType['archived'];
|
||||
}
|
||||
|
||||
if ($request->filled('color')) {
|
||||
$label->color = $request->get('color');
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$label->notes = $request->get('notes');
|
||||
}
|
||||
|
||||
if ($request->has('default_label')) {
|
||||
$label->default_label = $request->get('default_label');
|
||||
}
|
||||
|
||||
if ($request->has('show_in_nav')) {
|
||||
$label->show_in_nav = $request->get('show_in_nav');
|
||||
}
|
||||
|
||||
if ($label->save()) {
|
||||
return Response::make(
|
||||
Response::text('Status label '.$label->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Status label updated successfully',
|
||||
'id' => $label->id,
|
||||
'name' => $label->name,
|
||||
'type' => $label->getStatuslabelType(),
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$label->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveStatusLabel(Request $request): ?Statuslabel
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Statuslabel::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Statuslabel::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the status label'),
|
||||
'name' => $schema->string()->description('Name to identify the status label'),
|
||||
'new_name' => $schema->string()->description('New name (renames the status label)'),
|
||||
'type' => $schema->string()->description('New type: deployable, pending, archived, or undeployable'),
|
||||
'color' => $schema->string()->description('Display color in #RRGGBB format'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
'default_label' => $schema->boolean()->description('Whether this is the default label'),
|
||||
'show_in_nav' => $schema->boolean()->description('Whether to show in navigation'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the status label'),
|
||||
'name' => $schema->string()->description('Name of the status label'),
|
||||
'type' => $schema->string()->description('Type of the status label'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Tools;
|
||||
|
||||
use App\Models\Supplier;
|
||||
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_supplier')]
|
||||
#[Title('Update Supplier')]
|
||||
#[Description('Update fields on a Snipe-IT supplier identified by numeric ID or name')]
|
||||
class UpdateSupplierTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): ResponseFactory
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'nullable|string|max:255',
|
||||
'new_name' => 'nullable|string|max:255',
|
||||
'address' => 'nullable|string',
|
||||
'address2' => 'nullable|string',
|
||||
'city' => 'nullable|string',
|
||||
'state' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'zip' => 'nullable|string',
|
||||
'phone' => 'nullable|string',
|
||||
'fax' => 'nullable|string',
|
||||
'email' => 'nullable|email',
|
||||
'url' => 'nullable|string',
|
||||
'contact' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$supplier = $this->resolveSupplier($request);
|
||||
|
||||
if (! $supplier) {
|
||||
return Response::make(Response::error('Supplier not found'));
|
||||
}
|
||||
|
||||
if (! Gate::allows('update', $supplier)) {
|
||||
return Response::make(Response::error('Unauthorized'));
|
||||
}
|
||||
|
||||
if ($request->filled('new_name')) {
|
||||
$supplier->name = $request->get('new_name');
|
||||
}
|
||||
|
||||
$updatable = ['address', 'address2', 'city', 'state', 'country', 'zip', 'phone', 'fax', 'email', 'url', 'contact', 'notes'];
|
||||
|
||||
foreach ($updatable as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$supplier->{$field} = $request->get($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($supplier->save()) {
|
||||
return Response::make(
|
||||
Response::text('Supplier '.$supplier->name.' updated successfully')
|
||||
)->withStructuredContent([
|
||||
'success' => true,
|
||||
'message' => 'Supplier updated successfully',
|
||||
'id' => $supplier->id,
|
||||
'name' => $supplier->name,
|
||||
]);
|
||||
}
|
||||
|
||||
return Response::make(Response::error('Update failed: '.$supplier->getErrors()->first()));
|
||||
}
|
||||
|
||||
private function resolveSupplier(Request $request): ?Supplier
|
||||
{
|
||||
if ($request->filled('id')) {
|
||||
return Supplier::find($request->get('id'));
|
||||
}
|
||||
if ($request->filled('name')) {
|
||||
return Supplier::where('name', $request->get('name'))->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'id' => $schema->number()->description('Numeric ID to identify the supplier'),
|
||||
'name' => $schema->string()->description('Name to identify the supplier'),
|
||||
'new_name' => $schema->string()->description('New name (renames the supplier)'),
|
||||
'address' => $schema->string()->description('Address line 1'),
|
||||
'address2' => $schema->string()->description('Address line 2'),
|
||||
'city' => $schema->string()->description('City'),
|
||||
'state' => $schema->string()->description('State'),
|
||||
'country' => $schema->string()->description('Country'),
|
||||
'zip' => $schema->string()->description('Postal code'),
|
||||
'phone' => $schema->string()->description('Phone number'),
|
||||
'fax' => $schema->string()->description('Fax number'),
|
||||
'email' => $schema->string()->description('Email address'),
|
||||
'url' => $schema->string()->description('Website URL'),
|
||||
'contact' => $schema->string()->description('Contact name'),
|
||||
'notes' => $schema->string()->description('Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
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(),
|
||||
'id' => $schema->number()->description('Numeric ID of the supplier'),
|
||||
'name' => $schema->string()->description('Name of the supplier'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -180,16 +180,26 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['assets.view.requestable' => '1']);
|
||||
}
|
||||
|
||||
public function deleteAssetModels()
|
||||
{
|
||||
return $this->appendPermission(['models.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewAssetModels()
|
||||
{
|
||||
return $this->appendPermission(['models.view' => '1']);
|
||||
}
|
||||
|
||||
public function createAssetModels()
|
||||
{
|
||||
return $this->appendPermission(['models.create' => '1']);
|
||||
}
|
||||
|
||||
public function editAssetModels()
|
||||
{
|
||||
return $this->appendPermission(['models.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteAssetModels()
|
||||
{
|
||||
return $this->appendPermission(['models.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewAccessories()
|
||||
{
|
||||
return $this->appendPermission(['accessories.view' => '1']);
|
||||
@@ -370,11 +380,41 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['users.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewCategories()
|
||||
{
|
||||
return $this->appendPermission(['categories.view' => '1']);
|
||||
}
|
||||
|
||||
public function createCategories()
|
||||
{
|
||||
return $this->appendPermission(['categories.create' => '1']);
|
||||
}
|
||||
|
||||
public function editCategories()
|
||||
{
|
||||
return $this->appendPermission(['categories.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteCategories()
|
||||
{
|
||||
return $this->appendPermission(['categories.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewLocations()
|
||||
{
|
||||
return $this->appendPermission(['locations.view' => '1']);
|
||||
}
|
||||
|
||||
public function createLocations()
|
||||
{
|
||||
return $this->appendPermission(['locations.create' => '1']);
|
||||
}
|
||||
|
||||
public function editLocations()
|
||||
{
|
||||
return $this->appendPermission(['locations.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteLocations()
|
||||
{
|
||||
return $this->appendPermission(['locations.delete' => '1']);
|
||||
@@ -415,11 +455,41 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['customfields.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewDepreciations()
|
||||
{
|
||||
return $this->appendPermission(['depreciations.view' => '1']);
|
||||
}
|
||||
|
||||
public function createDepreciations()
|
||||
{
|
||||
return $this->appendPermission(['depreciations.create' => '1']);
|
||||
}
|
||||
|
||||
public function editDepreciations()
|
||||
{
|
||||
return $this->appendPermission(['depreciations.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteDepreciations()
|
||||
{
|
||||
return $this->appendPermission(['depreciations.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewManufacturers()
|
||||
{
|
||||
return $this->appendPermission(['manufacturers.view' => '1']);
|
||||
}
|
||||
|
||||
public function createManufacturers()
|
||||
{
|
||||
return $this->appendPermission(['manufacturers.create' => '1']);
|
||||
}
|
||||
|
||||
public function editManufacturers()
|
||||
{
|
||||
return $this->appendPermission(['manufacturers.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteManufacturers()
|
||||
{
|
||||
return $this->appendPermission(['manufacturers.delete' => '1']);
|
||||
@@ -435,11 +505,41 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['kits.view' => '1']);
|
||||
}
|
||||
|
||||
public function viewStatusLabels()
|
||||
{
|
||||
return $this->appendPermission(['statuslabels.view' => '1']);
|
||||
}
|
||||
|
||||
public function createStatusLabels()
|
||||
{
|
||||
return $this->appendPermission(['statuslabels.create' => '1']);
|
||||
}
|
||||
|
||||
public function editStatusLabels()
|
||||
{
|
||||
return $this->appendPermission(['statuslabels.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteStatusLabels()
|
||||
{
|
||||
return $this->appendPermission(['statuslabels.delete' => '1']);
|
||||
}
|
||||
|
||||
public function viewSuppliers()
|
||||
{
|
||||
return $this->appendPermission(['suppliers.view' => '1']);
|
||||
}
|
||||
|
||||
public function createSuppliers()
|
||||
{
|
||||
return $this->appendPermission(['suppliers.create' => '1']);
|
||||
}
|
||||
|
||||
public function editSuppliers()
|
||||
{
|
||||
return $this->appendPermission(['suppliers.edit' => '1']);
|
||||
}
|
||||
|
||||
public function deleteSuppliers()
|
||||
{
|
||||
return $this->appendPermission(['suppliers.delete' => '1']);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Tests\Feature\Mcp;
|
||||
|
||||
use App\Mcp\Tools\CreateAssetModelTool;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\User;
|
||||
use Laravel\Mcp\Request;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Tests\Feature\Mcp;
|
||||
|
||||
use App\Mcp\Tools\CreateAssetTool;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\User;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Tests\Feature\Mcp;
|
||||
|
||||
use App\Mcp\Tools\CreateDepreciationTool;
|
||||
use App\Models\Depreciation;
|
||||
use App\Models\User;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\ResponseFactory;
|
||||
|
||||
@@ -23,7 +23,7 @@ class CreateGroupToolTest extends TestCase
|
||||
|
||||
public function test_creates_group()
|
||||
{
|
||||
$name = 'Test MCP Group ' . uniqid();
|
||||
$name = 'Test MCP Group '.uniqid();
|
||||
|
||||
$this->handle(['name' => $name]);
|
||||
|
||||
@@ -32,7 +32,7 @@ class CreateGroupToolTest extends TestCase
|
||||
|
||||
public function test_response_includes_id_and_name()
|
||||
{
|
||||
$name = 'Test MCP Group ' . uniqid();
|
||||
$name = 'Test MCP Group '.uniqid();
|
||||
|
||||
$content = $this->handle(['name' => $name])->getStructuredContent();
|
||||
|
||||
@@ -51,7 +51,7 @@ class CreateGroupToolTest extends TestCase
|
||||
{
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$name = 'Unauthorized Group ' . uniqid();
|
||||
$name = 'Unauthorized Group '.uniqid();
|
||||
|
||||
$this->handle(['name' => $name]);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class UpdateGroupToolTest extends TestCase
|
||||
public function test_updates_group_by_id()
|
||||
{
|
||||
$group = Group::factory()->create();
|
||||
$newNotes = 'Updated notes ' . uniqid();
|
||||
$newNotes = 'Updated notes '.uniqid();
|
||||
|
||||
$this->handle(['id' => $group->id, 'notes' => $newNotes]);
|
||||
|
||||
@@ -35,7 +35,7 @@ class UpdateGroupToolTest extends TestCase
|
||||
public function test_renames_via_new_name()
|
||||
{
|
||||
$group = Group::factory()->create();
|
||||
$newName = 'Renamed Group ' . uniqid();
|
||||
$newName = 'Renamed Group '.uniqid();
|
||||
|
||||
$this->handle(['id' => $group->id, 'new_name' => $newName]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user