2 Commits
Author SHA1 Message Date
Adrian Mejias 24c53368a7 Add COX tools for doorlock, fuel, and target functionalities
- Implemented GetOxDoorlockServerFunction and GetOxDoorlockClientFunction for server-side and client-side door management, respectively.
- Created GetOxFuelServerFunction and GetOxFuelClientFunction for handling fuel management on server and client sides.
- Developed GetOxTargetClientFunction to manage targeting interactions, including global options and zone management.
- Added feature tests for all new tools to ensure functionality and error handling for unknown functions.
2026-03-01 15:35:04 -06:00
Adrian Mejias 29b854fd44 Add feature tests for QBCore and FiveM tools
- Implement tests for GetInventoryClientFunction and GetInventoryServerFunction in the COX namespace.
- Create tests for GenerateManifest and GenerateResourceBoilerplate in the FiveM namespace.
- Add tests for event and native function references in the FiveM namespace.
- Introduce tests for QBCore client and server event references, functions, and configurations.
- Implement tests for shared data retrieval in QBCore.
- Ensure all tests validate expected responses and handle edge cases.
2026-03-01 15:23:07 -06:00
37 changed files with 2283 additions and 0 deletions

No files matched your search

+16
View File
@@ -11,10 +11,16 @@ use App\Mcp\Resources\BestPractices;
use App\Mcp\Resources\CodeSnippets;
use App\Mcp\Resources\DatabaseQueries;
use App\Mcp\Resources\FrameworkComparison;
use App\Mcp\Tools\COX\Doorlock\Client\GetOxDoorlockClientFunction;
use App\Mcp\Tools\COX\Doorlock\Server\GetOxDoorlockServerEvent;
use App\Mcp\Tools\COX\Doorlock\Server\GetOxDoorlockServerFunction;
use App\Mcp\Tools\COX\Fuel\Client\GetOxFuelClientFunction;
use App\Mcp\Tools\COX\Fuel\Server\GetOxFuelServerFunction;
use App\Mcp\Tools\COX\Inventory\Client\GetInventoryClientFunction;
use App\Mcp\Tools\COX\Inventory\Server\GetInventoryServerFunction;
use App\Mcp\Tools\COX\MySQL\GetCOXEventReference;
use App\Mcp\Tools\COX\MySQL\GetCOXFunction;
use App\Mcp\Tools\COX\Target\Client\GetOxTargetClientFunction;
use App\Mcp\Tools\FiveM\Client\GetEventClientReference;
use App\Mcp\Tools\FiveM\Client\GetNativeClientFunction;
use App\Mcp\Tools\FiveM\GenerateManifest;
@@ -26,6 +32,8 @@ use App\Mcp\Tools\QBCore\Client\GetQBCoreClientEventReference;
use App\Mcp\Tools\QBCore\Client\GetQBCoreClientFunction;
use App\Mcp\Tools\QBCore\GetQBCoreConfig;
use App\Mcp\Tools\QBCore\GetQBCorePlayers;
use App\Mcp\Tools\QBCore\GetQBCoreResourceList;
use App\Mcp\Tools\QBCore\GetQBCoreResourceReference;
use App\Mcp\Tools\QBCore\GetQBCoreSharedData;
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerEventReference;
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerFunction;
@@ -66,10 +74,18 @@ class FiveMServer extends Server
GetQBCoreSharedData::class,
GetQBCoreConfig::class,
GetQBCorePlayers::class,
GetQBCoreResourceList::class,
GetQBCoreResourceReference::class,
GetCOXEventReference::class,
GetCOXFunction::class,
GetInventoryServerFunction::class,
GetInventoryClientFunction::class,
GetOxTargetClientFunction::class,
GetOxFuelClientFunction::class,
GetOxFuelServerFunction::class,
GetOxDoorlockClientFunction::class,
GetOxDoorlockServerFunction::class,
GetOxDoorlockServerEvent::class,
];
protected array $resources = [
@@ -0,0 +1,109 @@
<?php
namespace App\Mcp\Tools\COX\Doorlock\Client;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_doorlock client functions by name. Returns function signature, parameters, return type, and usage examples for client-side door management.')]
class GetOxDoorlockClientFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$function = $this->findFunction($functionName);
if (! $function) {
return Response::text(sprintf("ox_doorlock client function '%s' not found. Check ox_doorlock documentation at https://coxdocs.dev/ox_doorlock", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_doorlock client function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'pickClosestDoor' => [
'namespace' => 'Client',
'description' => 'Attempt to lockpick the closest door',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => 'exports.ox_doorlock:pickClosestDoor()',
'js_example' => 'exports.ox_doorlock.pickClosestDoor();',
],
'useClosestDoor' => [
'namespace' => 'Client',
'description' => 'Toggle the lock state of the closest door',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => 'exports.ox_doorlock:useClosestDoor()',
'js_example' => 'exports.ox_doorlock.useClosestDoor();',
],
'getClosestDoor' => [
'namespace' => 'Client',
'description' => 'Get data for the closest door',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'table|nil', 'description' => 'Door data or nil if no door nearby'],
'lua_example' => "local door = exports.ox_doorlock:getClosestDoor()\nif door then\n print('Closest door ID: '..door.id)\n print('Door is locked: '..tostring(door.state == 1))\nend",
'js_example' => "const door = exports.ox_doorlock.getClosestDoor();\nif (door) {\n console.log('Closest door ID: ' + door.id);\n console.log('Door is locked: ' + (door.state === 1));\n}",
],
];
$nameLower = strtolower($name);
foreach ($functions as $functionName => $functionData) {
if (strtolower($functionName) === $nameLower) {
return array_merge(['name' => $functionName], $functionData);
}
}
return null;
}
/**
* Format function info.
*/
protected function formatFunctionInfo(array $function, string $language): string
{
return view('mcp.cox.cox-function', [
'function' => $function,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'function_name' => $schema
->string()
->description('The name of the ox_doorlock client function to look up (e.g., "pickClosestDoor", "useClosestDoor", "getClosestDoor")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Mcp\Tools\COX\Doorlock\Server;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_doorlock server events by name. Returns event signature, parameters, and usage examples for server-side door events.')]
class GetOxDoorlockServerEvent extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$eventName = $request->get('event_name');
$language = $request->get('language', 'lua');
$event = $this->findEvent($eventName);
if (! $event) {
return Response::text(sprintf("ox_doorlock server event '%s' not found. Check ox_doorlock documentation at https://coxdocs.dev/ox_doorlock", $eventName));
}
$output = $this->formatEventInfo($event, $language);
return Response::text($output);
}
/**
* Find an ox_doorlock server event.
*/
protected function findEvent(string $name): ?array
{
$events = [
'ox_doorlock:stateChanged' => [
'type' => 'doorlock',
'namespace' => 'Server',
'description' => 'Triggered when a door lock state changes',
'side' => 'server',
'parameters' => [
['name' => 'doorId', 'type' => 'number', 'description' => 'The door ID'],
['name' => 'state', 'type' => 'number', 'description' => 'New door state (0 = unlocked, 1 = locked)'],
],
'lua_example' => "AddEventHandler('ox_doorlock:stateChanged', function(doorId, state)\n local doorName = exports.ox_doorlock:getDoor(doorId)?.name or 'Unknown'\n local stateText = state == 1 and 'locked' or 'unlocked'\n print(('Door %s (ID: %s) is now %s'):format(doorName, doorId, stateText))\nend)",
'js_example' => "on('ox_doorlock:stateChanged', (doorId, state) => {\n const door = exports.ox_doorlock.getDoor(doorId);\n const doorName = door?.name || 'Unknown';\n const stateText = state === 1 ? 'locked' : 'unlocked';\n console.log(`Door \${doorName} (ID: \${doorId}) is now \${stateText}`);\n});",
],
];
$nameLower = strtolower($name);
foreach ($events as $eventName => $eventData) {
if (strtolower($eventName) === $nameLower) {
return array_merge(['name' => $eventName], $eventData);
}
}
return null;
}
/**
* Format event info.
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.cox.cox-event-reference', [
'event' => $event,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'event_name' => $schema
->string()
->description('The name of the ox_doorlock server event to look up (e.g., "ox_doorlock:stateChanged")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,130 @@
<?php
namespace App\Mcp\Tools\COX\Doorlock\Server;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_doorlock server functions by name. Returns function signature, parameters, return type, and usage examples for server-side door management.')]
class GetOxDoorlockServerFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$function = $this->findFunction($functionName);
if (! $function) {
return Response::text(sprintf("ox_doorlock server function '%s' not found. Check ox_doorlock documentation at https://coxdocs.dev/ox_doorlock", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_doorlock server function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'getDoor' => [
'namespace' => 'Server',
'description' => 'Get door data by door ID',
'side' => 'server',
'parameters' => [
['name' => 'doorId', 'type' => 'number', 'description' => 'The door ID'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Door data or nil if not found'],
'lua_example' => "local door = exports.ox_doorlock:getDoor(1)\nif door then\n print('Door name: '..door.name)\n print('Door state: '..door.state)\nend",
'js_example' => "const door = exports.ox_doorlock.getDoor(1);\nif (door) {\n console.log('Door name: ' + door.name);\n console.log('Door state: ' + door.state);\n}",
],
'getDoorFromName' => [
'namespace' => 'Server',
'description' => 'Get door data by door name',
'side' => 'server',
'parameters' => [
['name' => 'doorName', 'type' => 'string', 'description' => 'The door name'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Door data or nil if not found'],
'lua_example' => "local door = exports.ox_doorlock:getDoorFromName('mrpd_main')\nif door then\n print('Door ID: '..door.id)\n print('Door is locked: '..tostring(door.state == 1))\nend",
'js_example' => "const door = exports.ox_doorlock.getDoorFromName('mrpd_main');\nif (door) {\n console.log('Door ID: ' + door.id);\n console.log('Door is locked: ' + (door.state === 1));\n}",
],
'editDoor' => [
'namespace' => 'Server',
'description' => 'Edit door properties by door ID',
'side' => 'server',
'parameters' => [
['name' => 'doorId', 'type' => 'number', 'description' => 'The door ID'],
['name' => 'data', 'type' => 'table', 'description' => 'Table with properties to update (state, groups, etc.)'],
['name' => 'playerId', 'type' => 'number|nil', 'description' => 'Optional player ID who triggered the edit'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if door was successfully edited'],
'lua_example' => "local success = exports.ox_doorlock:editDoor(1, {\n state = 0, -- Unlocked\n groups = {police = 0} -- Police can use\n})\nif success then\n print('Door edited successfully')\nend",
'js_example' => "const success = exports.ox_doorlock.editDoor(1, {\n state: 0, // Unlocked\n groups: {police: 0} // Police can use\n});\nif (success) {\n console.log('Door edited successfully');\n}",
],
'setDoorState' => [
'namespace' => 'Server',
'description' => 'Set the lock state of a door',
'side' => 'server',
'parameters' => [
['name' => 'doorId', 'type' => 'number', 'description' => 'The door ID'],
['name' => 'state', 'type' => 'number', 'description' => '0 = unlocked, 1 = locked'],
['name' => 'playerId', 'type' => 'number|nil', 'description' => 'Optional player ID who triggered the state change'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if state was successfully changed'],
'lua_example' => "local success = exports.ox_doorlock:setDoorState(1, 1) -- Lock door ID 1\nif success then\n print('Door locked successfully')\nend",
'js_example' => "const success = exports.ox_doorlock.setDoorState(1, 1); // Lock door ID 1\nif (success) {\n console.log('Door locked successfully');\n}",
],
];
$nameLower = strtolower($name);
foreach ($functions as $functionName => $functionData) {
if (strtolower($functionName) === $nameLower) {
return array_merge(['name' => $functionName], $functionData);
}
}
return null;
}
/**
* Format function info.
*/
protected function formatFunctionInfo(array $function, string $language): string
{
return view('mcp.cox.cox-function', [
'function' => $function,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'function_name' => $schema
->string()
->description('The name of the ox_doorlock server function to look up (e.g., "getDoor", "getDoorFromName", "editDoor", "setDoorState")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Mcp\Tools\COX\Fuel\Client;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_fuel client functions by name. Returns function signature, parameters, return type, and usage examples for vehicle fuel management.')]
class GetOxFuelClientFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$function = $this->findFunction($functionName);
if (! $function) {
return Response::text(sprintf("ox_fuel client function '%s' not found. Check ox_fuel documentation at https://coxdocs.dev/ox_fuel", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_fuel client function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'setMoneyCheck' => [
'namespace' => 'Client',
'description' => 'Set a custom function to check if a player has enough money to pay for fuel',
'side' => 'client',
'parameters' => [
['name' => 'callback', 'type' => 'function', 'description' => 'Function(amount: number) -> boolean that returns true if player can afford fuel'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_fuel:setMoneyCheck(function(amount)\n local playerMoney = exports.framework:GetPlayerMoney()\n return playerMoney >= amount\nend)",
'js_example' => "exports.ox_fuel.setMoneyCheck((amount) => {\n const playerMoney = exports.framework.GetPlayerMoney();\n return playerMoney >= amount;\n});",
],
];
$nameLower = strtolower($name);
foreach ($functions as $functionName => $functionData) {
if (strtolower($functionName) === $nameLower) {
return array_merge(['name' => $functionName], $functionData);
}
}
return null;
}
/**
* Format function info.
*/
protected function formatFunctionInfo(array $function, string $language): string
{
return view('mcp.cox.cox-function', [
'function' => $function,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'function_name' => $schema
->string()
->description('The name of the ox_fuel client function to look up (e.g., "setMoneyCheck")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Mcp\Tools\COX\Fuel\Server;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_fuel server functions by name. Returns function signature, parameters, return type, and usage examples for server-side fuel management.')]
class GetOxFuelServerFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$function = $this->findFunction($functionName);
if (! $function) {
return Response::text(sprintf("ox_fuel server function '%s' not found. Check ox_fuel documentation at https://coxdocs.dev/ox_fuel", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_fuel server function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'setPaymentMethod' => [
'namespace' => 'Server',
'description' => 'Set a custom payment method for fuel purchases',
'side' => 'server',
'parameters' => [
['name' => 'callback', 'type' => 'function', 'description' => 'Function(source: number, amount: number) -> boolean that handles payment and returns true if successful'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_fuel:setPaymentMethod(function(source, amount)\n local success = exports.framework:RemovePlayerMoney(source, amount)\n if success then\n print('Player '..source..' paid $'..amount..' for fuel')\n end\n return success\nend)",
'js_example' => "exports.ox_fuel.setPaymentMethod((source, amount) => {\n const success = exports.framework.RemovePlayerMoney(source, amount);\n if (success) {\n console.log(`Player \${source} paid $\${amount} for fuel`);\n }\n return success;\n});",
],
];
$nameLower = strtolower($name);
foreach ($functions as $functionName => $functionData) {
if (strtolower($functionName) === $nameLower) {
return array_merge(['name' => $functionName], $functionData);
}
}
return null;
}
/**
* Format function info.
*/
protected function formatFunctionInfo(array $function, string $language): string
{
return view('mcp.cox.cox-function', [
'function' => $function,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'function_name' => $schema
->string()
->description('The name of the ox_fuel server function to look up (e.g., "setPaymentMethod")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,260 @@
<?php
namespace App\Mcp\Tools\COX\Target\Client;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up ox_target client functions by name. Returns function signature, parameters, return type, and usage examples for targeting/third-eye interaction system.')]
class GetOxTargetClientFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$function = $this->findFunction($functionName);
if (! $function) {
return Response::text(sprintf("ox_target client function '%s' not found. Check ox_target documentation at https://coxdocs.dev/ox_target", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_target client function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'disableTargeting' => [
'namespace' => 'Client',
'description' => 'Disables or enables the targeting system',
'side' => 'client',
'parameters' => [
['name' => 'disabled', 'type' => 'boolean', 'description' => 'true to disable, false to enable'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => 'exports.ox_target:disableTargeting(true) -- Disable targeting',
'js_example' => 'exports.ox_target.disableTargeting(true); // Disable targeting',
],
'isActive' => [
'namespace' => 'Client',
'description' => 'Check if the targeting system is currently active',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'boolean', 'description' => 'true if targeting is active'],
'lua_example' => "local active = exports.ox_target:isActive()\nif active then\n print('Targeting is active')\nend",
'js_example' => "const active = exports.ox_target.isActive();\nif (active) {\n console.log('Targeting is active');\n}",
],
'addGlobalOption' => [
'namespace' => 'Client',
'description' => 'Add a global target option that appears on all entities',
'side' => 'client',
'parameters' => [
['name' => 'options', 'type' => 'table', 'description' => 'Target option configuration'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_target:addGlobalOption({\n name = 'greet',\n icon = 'fas fa-hand-wave',\n label = 'Wave',\n onSelect = function()\n print('You waved!')\n end\n})",
'js_example' => "exports.ox_target.addGlobalOption({\n name: 'greet',\n icon: 'fas fa-hand-wave',\n label: 'Wave',\n onSelect: () => {\n console.log('You waved!');\n }\n});",
],
'addGlobalPed' => [
'namespace' => 'Client',
'description' => 'Add target options to all peds',
'side' => 'client',
'parameters' => [
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'number', 'description' => 'Option ID'],
'lua_example' => "local id = exports.ox_target:addGlobalPed({\n {\n name = 'talk',\n icon = 'fas fa-comment',\n label = 'Talk',\n onSelect = function(data)\n print('Talking to ped: '..data.entity)\n end\n }\n})",
'js_example' => "const id = exports.ox_target.addGlobalPed([\n {\n name: 'talk',\n icon: 'fas fa-comment',\n label: 'Talk',\n onSelect: (data) => {\n console.log('Talking to ped: ' + data.entity);\n }\n }\n]);",
],
'addGlobalVehicle' => [
'namespace' => 'Client',
'description' => 'Add target options to all vehicles',
'side' => 'client',
'parameters' => [
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'number', 'description' => 'Option ID'],
'lua_example' => "local id = exports.ox_target:addGlobalVehicle({\n {\n name = 'lockpick',\n icon = 'fas fa-lock',\n label = 'Lockpick Vehicle',\n canInteract = function(entity)\n return GetVehicleDoorLockStatus(entity) == 2\n end,\n onSelect = function(data)\n -- Lockpick logic\n end\n }\n})",
'js_example' => "const id = exports.ox_target.addGlobalVehicle([\n {\n name: 'lockpick',\n icon: 'fas fa-lock',\n label: 'Lockpick Vehicle',\n canInteract: (entity) => {\n return GetVehicleDoorLockStatus(entity) === 2;\n },\n onSelect: (data) => {\n // Lockpick logic\n }\n }\n]);",
],
'addGlobalObject' => [
'namespace' => 'Client',
'description' => 'Add target options to all objects',
'side' => 'client',
'parameters' => [
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'number', 'description' => 'Option ID'],
'lua_example' => "local id = exports.ox_target:addGlobalObject({\n {\n name = 'examine',\n icon = 'fas fa-search',\n label = 'Examine',\n onSelect = function(data)\n print('Examining object')\n end\n }\n})",
'js_example' => "const id = exports.ox_target.addGlobalObject([\n {\n name: 'examine',\n icon: 'fas fa-search',\n label: 'Examine',\n onSelect: (data) => {\n console.log('Examining object');\n }\n }\n]);",
],
'addGlobalPlayer' => [
'namespace' => 'Client',
'description' => 'Add target options to all players',
'side' => 'client',
'parameters' => [
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'number', 'description' => 'Option ID'],
'lua_example' => "local id = exports.ox_target:addGlobalPlayer({\n {\n name = 'givemoney',\n icon = 'fas fa-dollar-sign',\n label = 'Give Money',\n onSelect = function(data)\n -- Give money logic\n end\n }\n})",
'js_example' => "const id = exports.ox_target.addGlobalPlayer([\n {\n name: 'givemoney',\n icon: 'fas fa-dollar-sign',\n label: 'Give Money',\n onSelect: (data) => {\n // Give money logic\n }\n }\n]);",
],
'addModel' => [
'namespace' => 'Client',
'description' => 'Add target options to specific entity models',
'side' => 'client',
'parameters' => [
['name' => 'models', 'type' => 'string|table', 'description' => 'Model hash or array of model hashes'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'number', 'description' => 'Option ID'],
'lua_example' => "local id = exports.ox_target:addModel('prop_atm_01', {\n {\n name = 'atm',\n icon = 'fas fa-credit-card',\n label = 'Use ATM',\n onSelect = function()\n -- Open ATM interface\n end\n }\n})",
'js_example' => "const id = exports.ox_target.addModel('prop_atm_01', [\n {\n name: 'atm',\n icon: 'fas fa-credit-card',\n label: 'Use ATM',\n onSelect: () => {\n // Open ATM interface\n }\n }\n]);",
],
'addEntity' => [
'namespace' => 'Client',
'description' => 'Add target options to a specific entity',
'side' => 'client',
'parameters' => [
['name' => 'entity', 'type' => 'number', 'description' => 'Entity handle'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "local ped = PlayerPedId()\nexports.ox_target:addEntity(ped, {\n {\n name = 'selfie',\n icon = 'fas fa-camera',\n label = 'Take Selfie',\n onSelect = function()\n print('Taking selfie')\n end\n }\n})",
'js_example' => "const ped = PlayerPedId();\nexports.ox_target.addEntity(ped, [\n {\n name: 'selfie',\n icon: 'fas fa-camera',\n label: 'Take Selfie',\n onSelect: () => {\n console.log('Taking selfie');\n }\n }\n]);",
],
'addLocalEntity' => [
'namespace' => 'Client',
'description' => 'Add target options to a networked entity (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'netId', 'type' => 'number', 'description' => 'Network ID'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "local netId = NetworkGetNetworkIdFromEntity(entity)\nexports.ox_target:addLocalEntity(netId, {\n {\n name = 'interact',\n icon = 'fas fa-hand',\n label = 'Interact',\n onSelect = function()\n print('Interacting')\n end\n }\n})",
'js_example' => "const netId = NetworkGetNetworkIdFromEntity(entity);\nexports.ox_target.addLocalEntity(netId, [\n {\n name: 'interact',\n icon: 'fas fa-hand',\n label: 'Interact',\n onSelect: () => {\n console.log('Interacting');\n }\n }\n]);",
],
'addSphereZone' => [
'namespace' => 'Client',
'description' => 'Add a sphere zone with target options',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Unique zone name'],
['name' => 'coords', 'type' => 'vector3', 'description' => 'Zone center coordinates'],
['name' => 'radius', 'type' => 'number', 'description' => 'Zone radius'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
['name' => 'zoneOptions', 'type' => 'table', 'description' => 'Zone configuration'],
],
'returns' => ['type' => 'number', 'description' => 'Zone ID'],
'lua_example' => "local id = exports.ox_target:addSphereZone('bank_atm', vector3(150.0, -1040.0, 29.5), 2.0, {\n {\n name = 'atm',\n icon = 'fas fa-credit-card',\n label = 'Use ATM',\n onSelect = function()\n print('Opening ATM')\n end\n }\n}, {})",
'js_example' => "const id = exports.ox_target.addSphereZone('bank_atm', [150.0, -1040.0, 29.5], 2.0, [\n {\n name: 'atm',\n icon: 'fas fa-credit-card',\n label: 'Use ATM',\n onSelect: () => {\n console.log('Opening ATM');\n }\n }\n], {});",
],
'addBoxZone' => [
'namespace' => 'Client',
'description' => 'Add a box zone with target options',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Unique zone name'],
['name' => 'coords', 'type' => 'vector3', 'description' => 'Zone center coordinates'],
['name' => 'size', 'type' => 'vector3', 'description' => 'Zone size (length, width, height)'],
['name' => 'rotation', 'type' => 'number', 'description' => 'Zone rotation'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
['name' => 'zoneOptions', 'type' => 'table', 'description' => 'Zone configuration'],
],
'returns' => ['type' => 'number', 'description' => 'Zone ID'],
'lua_example' => "local id = exports.ox_target:addBoxZone('garage', vector3(215.0, -809.0, 30.7), vector3(5.0, 10.0, 3.0), 0, {\n {\n name = 'vehicles',\n icon = 'fas fa-car',\n label = 'Garage',\n onSelect = function()\n print('Opening garage')\n end\n }\n}, {})",
'js_example' => "const id = exports.ox_target.addBoxZone('garage', [215.0, -809.0, 30.7], [5.0, 10.0, 3.0], 0, [\n {\n name: 'vehicles',\n icon: 'fas fa-car',\n label: 'Garage',\n onSelect: () => {\n console.log('Opening garage');\n }\n }\n], {});",
],
'addPolyZone' => [
'namespace' => 'Client',
'description' => 'Add a polygon zone with target options',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Unique zone name'],
['name' => 'points', 'type' => 'table', 'description' => 'Array of vector2 points'],
['name' => 'options', 'type' => 'table', 'description' => 'Array of target options'],
['name' => 'zoneOptions', 'type' => 'table', 'description' => 'Zone configuration'],
],
'returns' => ['type' => 'number', 'description' => 'Zone ID'],
'lua_example' => "local id = exports.ox_target:addPolyZone('parking', {\n vector2(200.0, -800.0),\n vector2(220.0, -800.0),\n vector2(220.0, -820.0),\n vector2(200.0, -820.0)\n}, {\n {\n name = 'park',\n icon = 'fas fa-parking',\n label = 'Park Vehicle',\n onSelect = function()\n print('Parking vehicle')\n end\n }\n}, {})",
'js_example' => "const id = exports.ox_target.addPolyZone('parking', [\n [200.0, -800.0],\n [220.0, -800.0],\n [220.0, -820.0],\n [200.0, -820.0]\n], [\n {\n name: 'park',\n icon: 'fas fa-parking',\n label: 'Park Vehicle',\n onSelect: () => {\n console.log('Parking vehicle');\n }\n }\n], {});",
],
'removeZone' => [
'namespace' => 'Client',
'description' => 'Remove a zone by ID',
'side' => 'client',
'parameters' => [
['name' => 'id', 'type' => 'number', 'description' => 'Zone ID to remove'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => 'exports.ox_target:removeZone(zoneId)',
'js_example' => 'exports.ox_target.removeZone(zoneId);',
],
'zoneExists' => [
'namespace' => 'Client',
'description' => 'Check if a zone exists by name',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Zone name'],
],
'returns' => ['type' => 'boolean', 'description' => 'true if zone exists'],
'lua_example' => "local exists = exports.ox_target:zoneExists('bank_atm')\nif exists then\n print('Zone exists')\nend",
'js_example' => "const exists = exports.ox_target.zoneExists('bank_atm');\nif (exists) {\n console.log('Zone exists');\n}",
],
];
$nameLower = strtolower($name);
foreach ($functions as $functionName => $functionData) {
if (strtolower($functionName) === $nameLower) {
return array_merge(['name' => $functionName], $functionData);
}
}
return null;
}
/**
* Format function info.
*/
protected function formatFunctionInfo(array $function, string $language): string
{
return view('mcp.cox.cox-function', [
'function' => $function,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'function_name' => $schema
->string()
->description('The name of the ox_target client function to look up (e.g., "addSphereZone", "addGlobalVehicle", "addModel")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,500 @@
<?php
namespace App\Mcp\Tools\QBCore;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Get a complete list of all 58 documented QBCore resources with brief descriptions and categories.')]
class GetQBCoreResourceList extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$category = $request->get('category');
$resources = $this->getAllResources($category);
return Response::text($this->formatResourceList($resources));
}
/**
* Get all QBCore resources, optionally filtered by category.
*/
protected function getAllResources(?string $category = null): array
{
$allResources = [
// Admin & Management
'qb-adminmenu' => [
'name' => 'qb-adminmenu',
'title' => 'Admin Menu',
'category' => 'Admin & Management',
'description' => 'Admin panel for server management with player controls, moderation, warnings, and coordinate tools',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-adminmenu',
],
// Jobs
'qb-ambulancejob' => [
'name' => 'qb-ambulancejob',
'title' => 'Ambulance Job',
'category' => 'Jobs',
'description' => 'Medical job with hospital check-in, damage/bleeding system, and patient revival mechanics',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-ambulancejob',
],
'qb-busjob' => [
'name' => 'qb-busjob',
'title' => 'Bus Job',
'category' => 'Jobs',
'description' => 'Public transportation job for bus drivers with route and payment system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-busjob',
],
'qb-garbagejob' => [
'name' => 'qb-garbagejob',
'title' => 'Garbage Job',
'category' => 'Jobs',
'description' => 'Garbage collection job with pickup routes and payment system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-garbagejob',
],
'qb-hotdogjob' => [
'name' => 'qb-hotdogjob',
'title' => 'Hot Dog Job',
'category' => 'Jobs',
'description' => 'Hot dog stand vendor job with payment system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-hotdogjob',
],
'qb-mechanicjob' => [
'name' => 'qb-mechanicjob',
'title' => 'Mechanic Job',
'category' => 'Jobs',
'description' => 'Vehicle repair and customization job with parts system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-mechanicjob',
],
'qb-newsjob' => [
'name' => 'qb-newsjob',
'title' => 'News Job',
'category' => 'Jobs',
'description' => 'News reporter job with interviews and broadcast system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-newsjob',
],
'qb-policejob' => [
'name' => 'qb-policejob',
'title' => 'Police Job',
'category' => 'Jobs',
'description' => 'Law enforcement job with dispatch, arrest, jailing, and evidence system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-policejob',
],
'qb-recyclejob' => [
'name' => 'qb-recyclejob',
'title' => 'Recycle Job',
'category' => 'Jobs',
'description' => 'Recycling center worker job with item pickup and crafting',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-recyclejob',
],
'qb-taxijob' => [
'name' => 'qb-taxijob',
'title' => 'Taxi Job',
'category' => 'Jobs',
'description' => 'Taxi driver job with passenger pickup and payment system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-taxijob',
],
'qb-towjob' => [
'name' => 'qb-towjob',
'title' => 'Tow Job',
'category' => 'Jobs',
'description' => 'Vehicle towing service with impound and payment system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-towjob',
],
'qb-truckerjob' => [
'name' => 'qb-truckerjob',
'title' => 'Trucker Job',
'category' => 'Jobs',
'description' => 'Long-haul trucking job with delivery routes and payment',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-truckerjob',
],
// Crime & Robberies
'qb-bankrobbery' => [
'name' => 'qb-bankrobbery',
'title' => 'Bank Robbery',
'category' => 'Crime & Robberies',
'description' => 'Heist job with thermal drill, hacking, and loot system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-bankrobbery',
],
'qb-drugs' => [
'name' => 'qb-drugs',
'title' => 'Drug Dealing',
'category' => 'Crime & Robberies',
'description' => 'Drug production and dealing system with supply and demand',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-drugs',
],
'qb-houserobbery' => [
'name' => 'qb-houserobbery',
'title' => 'House Robbery',
'category' => 'Crime & Robberies',
'description' => 'Residential burglary system with lockpicking and loot',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-houserobbery',
],
'qb-jewelry' => [
'name' => 'qb-jewelry',
'title' => 'Jewelry Store Robbery',
'category' => 'Crime & Robberies',
'description' => 'Jewelry store heist with thermite drill and loot system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-jewelry',
],
'qb-storerobbery' => [
'name' => 'qb-storerobbery',
'title' => 'Store Robbery',
'category' => 'Crime & Robberies',
'description' => 'Convenience store robbery with holdup mechanics',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-storerobbery',
],
'qb-truckrobbery' => [
'name' => 'qb-truckrobbery',
'title' => 'Truck Robbery',
'category' => 'Crime & Robberies',
'description' => 'Armored truck heist with combat and loot mechanics',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-truckrobbery',
],
'qb-weed' => [
'name' => 'qb-weed',
'title' => 'Weed Growing',
'category' => 'Crime & Robberies',
'description' => 'Marijuana cultivation and harvesting system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-weed',
],
'qb-vineyard' => [
'name' => 'qb-vineyard',
'title' => 'Vineyard',
'category' => 'Crime & Robberies',
'description' => 'Grape harvesting and wine production system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-vineyard',
],
// Housing & Interiors
'qb-apartments' => [
'name' => 'qb-apartments',
'title' => 'Apartments',
'category' => 'Housing & Interiors',
'description' => 'Player apartment rental and management system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-apartments',
],
'qb-houses' => [
'name' => 'qb-houses',
'title' => 'Houses',
'category' => 'Housing & Interiors',
'description' => 'Player house ownership with storage and customization',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-houses',
],
'qb-interior' => [
'name' => 'qb-interior',
'title' => 'Interior',
'category' => 'Housing & Interiors',
'description' => 'Interior world system for building custom rooms',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-interior',
],
// Vehicles & Transportation
'qb-garages' => [
'name' => 'qb-garages',
'title' => 'Garages',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle parking and retrieval system with multiple garages',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-garages',
],
'qb-vehiclekeys' => [
'name' => 'qb-vehiclekeys',
'title' => 'Vehicle Keys',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle ownership and key distribution system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-vehiclekeys',
],
'qb-vehiclesales' => [
'name' => 'qb-vehiclesales',
'title' => 'Vehicle Sales',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle dealership system for buying and trading vehicles',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-vehiclesales',
],
'qb-vehicleshop' => [
'name' => 'qb-vehicleshop',
'title' => 'Vehicle Shop',
'category' => 'Vehicles & Transportation',
'description' => 'Show room for purchasing new vehicles',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-vehicleshop',
],
'qb-fuel' => [
'name' => 'qb-fuel',
'title' => 'Fuel System',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle fueling system with gas stations',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-fuel',
],
// Commerce & Markets
'qb-banking' => [
'name' => 'qb-banking',
'title' => 'Banking',
'category' => 'Commerce & Markets',
'description' => 'Bank account system with deposits, withdrawals, and transfers',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-banking',
],
'qb-cityhall' => [
'name' => 'qb-cityhall',
'title' => 'City Hall',
'category' => 'Commerce & Markets',
'description' => 'Government records and ID system for players',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-cityhall',
],
'qb-clothing' => [
'name' => 'qb-clothing',
'title' => 'Clothing Shop',
'category' => 'Commerce & Markets',
'description' => 'Clothing store system for outfit customization',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-clothing',
],
'qb-crypto' => [
'name' => 'qb-crypto',
'title' => 'Cryptocurrency',
'category' => 'Commerce & Markets',
'description' => 'Cryptocurrency trading platform system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-crypto',
],
'qb-pawnshop' => [
'name' => 'qb-pawnshop',
'title' => 'Pawn Shop',
'category' => 'Commerce & Markets',
'description' => 'Pawn shop for selling and buying items',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-pawnshop',
],
'qb-shops' => [
'name' => 'qb-shops',
'title' => 'Shops',
'category' => 'Commerce & Markets',
'description' => 'General-purpose shop system for generic stores',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-shops',
],
// Player Systems
'qb-inventory' => [
'name' => 'qb-inventory',
'title' => 'Inventory',
'category' => 'Player Systems',
'description' => 'Player item inventory management system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-inventory',
],
'qb-management' => [
'name' => 'qb-management',
'title' => 'Management',
'category' => 'Player Systems',
'description' => 'Org/faction/job management system for player hierarchies',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-management',
],
'qb-multicharacter' => [
'name' => 'qb-multicharacter',
'title' => 'Multi-Character',
'category' => 'Player Systems',
'description' => 'Multiple character selection and management on login',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-multicharacter',
],
'qb-phone' => [
'name' => 'qb-phone',
'title' => 'Phone',
'category' => 'Player Systems',
'description' => 'In-game phone system with apps, messages, and calls',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-phone',
],
'qb-prison' => [
'name' => 'qb-prison',
'title' => 'Prison',
'category' => 'Player Systems',
'description' => 'Prison system for jailed players with tasks and release',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-prison',
],
'qb-spawn' => [
'name' => 'qb-spawn',
'title' => 'Spawn',
'category' => 'Player Systems',
'description' => 'Initial spawn location selector for new players',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-spawn',
],
'qb-fitbit' => [
'name' => 'qb-fitbit',
'title' => 'Fitbit',
'category' => 'Player Systems',
'description' => 'Health and fitness tracking wearable device',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-fitbit',
],
// UI & Interface
'qb-hud' => [
'name' => 'qb-hud',
'title' => 'HUD',
'category' => 'UI & Interface',
'description' => 'Main heads-up display for player information',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-hud',
],
'qb-input' => [
'name' => 'qb-input',
'title' => 'Input',
'category' => 'UI & Interface',
'description' => 'Generic input dialog system for NUI popups',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-input',
],
'qb-loading' => [
'name' => 'qb-loading',
'title' => 'Loading Screen',
'category' => 'UI & Interface',
'description' => 'Animated loading screen on server startup',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-loading',
],
'qb-menu' => [
'name' => 'qb-menu',
'title' => 'Menu',
'category' => 'UI & Interface',
'description' => 'Simple menu UI system for player interactions',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-menu',
],
'qb-radialmenu' => [
'name' => 'qb-radialmenu',
'title' => 'Radial Menu',
'category' => 'UI & Interface',
'description' => 'Radial/circular menu interface for quick selections',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-radialmenu',
],
'qb-scoreboard' => [
'name' => 'qb-scoreboard',
'title' => 'Scoreboard',
'category' => 'UI & Interface',
'description' => 'Server player list and information scoreboard',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-scoreboard',
],
'qb-target' => [
'name' => 'qb-target',
'title' => 'Target System',
'category' => 'UI & Interface',
'description' => 'NPC and object targeting system for interactions',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-target',
],
// Entertainment & Activities
'qb-diving' => [
'name' => 'qb-diving',
'title' => 'Diving',
'category' => 'Entertainment & Activities',
'description' => 'Underwater treasure hunting activity',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-diving',
],
'qb-lapraces' => [
'name' => 'qb-lapraces',
'title' => 'Lap Races',
'category' => 'Entertainment & Activities',
'description' => 'Racing competition system with lap tracking',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-lapraces',
],
'qb-minigames' => [
'name' => 'qb-minigames',
'title' => 'Minigames',
'category' => 'Entertainment & Activities',
'description' => 'Collection of minigames for lockpicking, hacking, etc.',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-minigames',
],
'qb-radio' => [
'name' => 'qb-radio',
'title' => 'Radio',
'category' => 'Entertainment & Activities',
'description' => 'In-vehicle or ambient radio station system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-radio',
],
'qb-streetraces' => [
'name' => 'qb-streetraces',
'title' => 'Street Races',
'category' => 'Entertainment & Activities',
'description' => 'Street racing event system with records',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-streetraces',
],
// Utilities & Miscellaneous
'qb-doorlock' => [
'name' => 'qb-doorlock',
'title' => 'Door Lock',
'category' => 'Utilities & Miscellaneous',
'description' => 'Door locking system with access control',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-doorlock',
],
'qb-scrapyard' => [
'name' => 'qb-scrapyard',
'title' => 'Scrap Yard',
'category' => 'Utilities & Miscellaneous',
'description' => 'Vehicle scrapping system for parts and money',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-scrapyard',
],
'qb-smallresources' => [
'name' => 'qb-smallresources',
'title' => 'Small Resources',
'category' => 'Utilities & Miscellaneous',
'description' => 'Bundle of small utility resources for various features',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-smallresources',
],
'qb-weapons' => [
'name' => 'qb-weapons',
'title' => 'Weapons',
'category' => 'Utilities & Miscellaneous',
'description' => 'Weapon shop and ammo purchasing system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-weapons',
],
'qb-weathersync' => [
'name' => 'qb-weathersync',
'title' => 'Weather Sync',
'category' => 'Utilities & Miscellaneous',
'description' => 'Server-wide weather synchronization system',
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-weathersync',
],
];
if ($category) {
return array_filter($allResources, fn ($r) => strtolower($r['category']) === strtolower($category));
}
return $allResources;
}
/**
* Format the resource list.
*/
protected function formatResourceList(array $resources): string
{
return view('mcp.qbcore.qbcore-resource-list', [
'resources' => $resources,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'category' => $schema
->string()
->description('Optional: Filter resources by category (Admin & Management, Jobs, Crime & Robberies, Housing & Interiors, Vehicles & Transportation, Commerce & Markets, Player Systems, UI & Interface, Entertainment & Activities, Utilities & Miscellaneous)')
->enum([
'Admin & Management',
'Jobs',
'Crime & Robberies',
'Housing & Interiors',
'Vehicles & Transportation',
'Commerce & Markets',
'Player Systems',
'UI & Interface',
'Entertainment & Activities',
'Utilities & Miscellaneous',
]),
];
}
}
@@ -0,0 +1,325 @@
<?php
namespace App\Mcp\Tools\QBCore;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Get detailed documentation for a specific QBCore resource. Returns information about configuration, commands, events, callbacks, and usage examples.')]
class GetQBCoreResourceReference extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$resourceName = $request->get('resource_name');
$resource = $this->findResource($resourceName);
if (! $resource) {
return Response::text(sprintf("QBCore resource '%s' not found. Use GetQBCoreResourceList to see all available resources.", $resourceName));
}
return Response::text($this->formatResourceReference($resource));
}
/**
* Find a QBCore resource by name.
*/
protected function findResource(string $name): ?array
{
$resources = [
'qb-adminmenu' => [
'name' => 'qb-adminmenu',
'title' => 'Admin Menu',
'category' => 'Admin & Management',
'description' => 'Admin panel for server management and player moderation.',
'features' => ['Player Management', 'Moderation', 'Coordinate Tools', 'Announcements', 'Reports', 'Warnings'],
'commands' => [
'/admin' => 'Opens the admin menu',
'/blips' => 'Toggles player blips',
'/names' => 'Toggles player names display',
'/coords' => 'Shows your current coordinates',
'/maxmods' => 'Sets vehicle to max mods',
'/noclip' => 'Toggles noclip mode',
'/admincar' => 'Adds current vehicle to garage',
'/announce [message]' => 'Creates an announcement',
'/report [message]' => 'Create a report to staff',
'/reportr [message]' => 'Replies to a user report',
'/reporttoggle' => 'Opt in/out of receiving player reports',
'/staffchat [message]' => 'Sends a staff-only message',
'/warn [id] [reason]' => 'Warn a player',
'/checkwarns [id] [opt: number]' => 'View warnings for a player',
'/delwarn [id] [number]' => 'Delete a warning from a player',
'/givenuifocus [id] [hasFocus] [hasCursor]' => 'Sets nuifocus state for player',
'/setmodel [model] [id]' => 'Changes the players ped model',
'/setspeed [opt: speed]' => 'Sets players foot speed',
'/kickall' => 'Kick all players from server',
'/setammo [amount] [opt: weapon]' => 'Set weapon ammo',
'/vector2' => 'Copies vector2 to clipboard',
'/vector3' => 'Copies vector3 to clipboard',
'/vector4' => 'Copies vector4 to clipboard',
'/heading' => 'Copies heading to clipboard',
],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-adminmenu',
],
'qb-ambulancejob' => [
'name' => 'qb-ambulancejob',
'title' => 'Ambulance Job',
'category' => 'Jobs',
'description' => 'Medical job with hospital check-in system, damage/bleeding mechanics, and patient revival.',
'features' => ['Hospital Check-in System', 'Damage Tracking', 'Bleeding System', 'Patient Revival', 'Job Shop', 'Vehicle Spawner'],
'commands' => [
'/911e [message]' => 'Sends a message to EMS',
'/status' => 'Check the status of the nearest player',
'/heal' => 'Heals the nearest player',
'/revivep' => 'Revives the nearest player',
'/revive' => 'Revive yourself',
'/setpain [opt: id]' => 'Sets the pain level',
'/kill [opt: id]' => 'Kills the player',
'/aheal [opt: id]' => 'Heals a player',
],
'items' => [
'ifaks' => 'Relieves player stress and heals them by +10',
'bandage' => 'Heals player by +10 and has 50% chance of removing 1 level of bleeding',
'firstaid' => 'Completely heals closest player as long as they are in last-stand',
'painkillers' => 'Stops bleeding temporarily but wears off over time',
],
'events' => [
'hospital:server:SetDeathStatus' => 'Modify the players isdead metadata',
'hospital:server:SetLaststandStatus' => 'Modify the players inlaststand metadata',
'hospital:server:ambulanceAlert' => 'Send a notification to all EMTs',
'hospital:server:RevivePlayer' => 'Revive a player for free or a fee',
],
'callbacks' => [
'hospital:GetDoctors' => 'Returns the number of players online with the ambulance job',
],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-ambulancejob',
],
'qb-banking' => [
'name' => 'qb-banking',
'title' => 'Banking',
'category' => 'Commerce & Markets',
'description' => 'Bank account system with deposits, withdrawals, and transfers between players.',
'features' => ['Account Management', 'Money Transfer', 'Transaction History', 'ATM Withdrawals'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-banking',
],
'qb-inventory' => [
'name' => 'qb-inventory',
'title' => 'Inventory',
'category' => 'Player Systems',
'description' => 'Player item inventory management system with weight-based limits.',
'features' => ['Item Management', 'Weight Limits', 'Inventory UI', 'Drop Items', 'Trade Items'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-inventory',
],
'qb-phone' => [
'name' => 'qb-phone',
'title' => 'Phone',
'category' => 'Player Systems',
'description' => 'In-game phone system with apps, messages, calls, and notifications.',
'features' => ['Messaging', 'Phone Calls', 'Apps System', 'Contacts', 'Photos', 'Radio'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-phone',
],
'qb-policejob' => [
'name' => 'qb-policejob',
'title' => 'Police Job',
'category' => 'Jobs',
'description' => 'Law enforcement job with dispatch, arrest, jailing, and evidence system.',
'features' => ['Dispatch System', 'Arrests', 'Jail Management', 'Evidence Collection', 'Vehicle Impound'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-policejob',
],
'qb-garages' => [
'name' => 'qb-garages',
'title' => 'Garages',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle parking and retrieval system with multiple garage locations.',
'features' => ['Multiple Garages', 'Vehicle Storage', 'Quick Parking', 'Vehicle Retrieval'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-garages',
],
'qb-shops' => [
'name' => 'qb-shops',
'title' => 'Shops',
'category' => 'Commerce & Markets',
'description' => 'General-purpose shop system for creating generic stores.',
'features' => ['Shop Creation', 'Item Pricing', 'Purchase System', 'Multiple Locations'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-shops',
],
'qb-target' => [
'name' => 'qb-target',
'title' => 'Target System',
'category' => 'UI & Interface',
'description' => 'NPC and object targeting system for player interactions.',
'features' => ['Click Targeting', 'NPC Interaction', 'Object Targeting', 'Distance Based'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-target',
],
'qb-hud' => [
'name' => 'qb-hud',
'title' => 'HUD',
'category' => 'UI & Interface',
'description' => 'Main heads-up display showing player information and status.',
'features' => ['Health Display', 'Armor Display', 'Hunger/Thirst', 'Stress Level', 'Mini Map'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-hud',
],
'qb-doorlock' => [
'name' => 'qb-doorlock',
'title' => 'Door Lock',
'category' => 'Utilities & Miscellaneous',
'description' => 'Door locking system with access control and permissions.',
'features' => ['Door Locking', 'Access Control', 'Job Locks', 'Gang Locks'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-doorlock',
],
'qb-mechanicjob' => [
'name' => 'qb-mechanicjob',
'title' => 'Mechanic Job',
'category' => 'Jobs',
'description' => 'Vehicle repair and customization job with parts system.',
'features' => ['Vehicle Repair', 'Vehicle Customization', 'Parts Management', 'Customer Jobs'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-mechanicjob',
],
'qb-housing' => [
'name' => 'qb-houses',
'title' => 'Houses',
'category' => 'Housing & Interiors',
'description' => 'Player house ownership with storage and customization.',
'features' => ['House Purchase', 'House Storage', 'Furniture Customization', 'Multi-Garage'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-houses',
],
'qb-prison' => [
'name' => 'qb-prison',
'title' => 'Prison',
'category' => 'Player Systems',
'description' => 'Prison system for jailed players with tasks and release.',
'features' => ['Jailing System', 'Prison Jobs', 'Release System', 'Time Tracking'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-prison',
],
'qb-bankrobbery' => [
'name' => 'qb-bankrobbery',
'title' => 'Bank Robbery',
'category' => 'Crime & Robberies',
'description' => 'Heist job with thermal drill, hacking, and loot system.',
'features' => ['Thermal Drill', 'Hacking Minigame', 'Loot System', 'Police Alerts'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-bankrobbery',
],
'qb-drugs' => [
'name' => 'qb-drugs',
'title' => 'Drug Dealing',
'category' => 'Crime & Robberies',
'description' => 'Drug production and dealing system with supply and demand.',
'features' => ['Drug Production', 'Dealing Mechanics', 'Supply/Demand', 'Dealer Routes'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-drugs',
],
'qb-fuel' => [
'name' => 'qb-fuel',
'title' => 'Fuel System',
'category' => 'Vehicles & Transportation',
'description' => 'Vehicle fueling system with gas stations.',
'features' => ['Gas Stations', 'Fuel Usage', 'Cost Calculation', 'Player Jobs'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-fuel',
],
'qb-multicharacter' => [
'name' => 'qb-multicharacter',
'title' => 'Multi-Character',
'category' => 'Player Systems',
'description' => 'Multiple character selection and management on login.',
'features' => ['Character Creation', 'Character Selection', 'Character Deletion'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-multicharacter',
],
'qb-clothing' => [
'name' => 'qb-clothing',
'title' => 'Clothing Shop',
'category' => 'Commerce & Markets',
'description' => 'Clothing store system for outfit customization.',
'features' => ['Outfit Customization', 'Clothing Purchase', 'Clothing Categories'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-clothing',
],
'qb-vehicleshop' => [
'name' => 'qb-vehicleshop',
'title' => 'Vehicle Shop',
'category' => 'Vehicles & Transportation',
'description' => 'Show room for purchasing new vehicles.',
'features' => ['Vehicle Showcase', 'Vehicle Purchase', 'Test Drive'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-vehicleshop',
],
'qb-input' => [
'name' => 'qb-input',
'title' => 'Input',
'category' => 'UI & Interface',
'description' => 'Generic input dialog system for NUI popups.',
'features' => ['Text Input', 'Number Input', 'Validation', 'Custom Styling'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-input',
],
'qb-manage' => [
'name' => 'qb-management',
'title' => 'Management',
'category' => 'Player Systems',
'description' => 'Org/faction/job management for player hierarchies.',
'features' => ['Rank Management', 'Member Management', 'Permission Control'],
'url' => 'https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-management',
],
];
// Normalize the resource name for matching
$normalized = strtolower(str_replace(['_', ' '], '-', $name));
// Remove any prefix variations and ensure qb- prefix
$normalized = preg_replace('/^(qbcore[-_]?)?/i', '', $normalized);
$normalized = 'qb-'.ltrim($normalized, 'qb-');
foreach ($resources as $resource) {
if (strtolower($resource['name']) === $normalized) {
return $resource;
}
}
return null;
}
/**
* Format the resource reference.
*/
protected function formatResourceReference(array $resource): string
{
return view('mcp.qbcore.qbcore-resource-reference', [
'resource' => $resource,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'resource_name' => $schema
->string()
->description('The name of the QBCore resource to look up (e.g., qb-ambulancejob, qb-adminmenu, qb-inventory)')
->required(),
];
}
}
@@ -0,0 +1,25 @@
## QBCore Resources Directory
Complete list of all {{ count($resources) }} official QBCore resources. Use `GetQBCoreResourceReference` to get detailed information about specific resources.
@php
$grouped = collect($resources)->groupBy('category');
@endphp
@foreach($grouped as $category => $categoryResources)
### {{ $category }}
| Resource | Description | Documentation |
|----------|-------------|-----------------|
@foreach($categoryResources as $resource)
| **{{ $resource['title'] }}** (`{{ $resource['name'] }}`) | {{ $resource['description'] }} | [View Docs]({{ $resource['url'] }}) |
@endforeach
@endforeach
### Quick Links
- [QBCore Framework GitHub](https://github.com/qbcore-framework)
- [QBCore Documentation](https://docs.qbcore.org)
- [QBCore Discord Community](https://discord.gg/qbcore)
**Total Resources:** {{ count($resources) }}
@@ -0,0 +1,64 @@
## {{ $resource['title'] }} (`{{ $resource['name'] }}`)
**Category:** {!! $resource['category'] !!}
{{ $resource['description'] }}
### Overview
{{ $resource['description'] }}
@if(isset($resource['features']) && !empty($resource['features']))
#### Key Features
@foreach($resource['features'] as $feature)
- {{ $feature }}
@endforeach
@endif
@if(isset($resource['commands']) && !empty($resource['commands']))
### Commands
@foreach($resource['commands'] as $command => $description)
#### `{{ $command }}`
{{ $description }}
@endforeach
@endif
@if(isset($resource['events']) && !empty($resource['events']))
### Events
@foreach($resource['events'] as $event => $description)
#### `{{ $event }}`
{{ $description }}
@endforeach
@endif
@if(isset($resource['callbacks']) && !empty($resource['callbacks']))
### Callbacks
@foreach($resource['callbacks'] as $callback => $description)
#### `{{ $callback }}`
{{ $description }}
@endforeach
@endif
@if(isset($resource['items']) && !empty($resource['items']))
### Items
| Item | Description |
|------|-------------|
@foreach($resource['items'] as $item => $description)
| `{{ $item }}` | {{ $description }} |
@endforeach
@endif
### Documentation
- [Resource Documentation]({{ $resource['url'] }})
- [QBCore Framework](https://docs.qbcore.org)
- [GitHub Repository](https://github.com/qbcore-framework)
**Learn More:**
To get detailed configuration options and advanced usage, visit the [full documentation]({{ $resource['url'] }}) for this resource.
@@ -0,0 +1,54 @@
<?php
use App\Mcp\Tools\COX\Doorlock\Client\GetOxDoorlockClientFunction;
use Laravel\Mcp\Request;
it('get ox_doorlock client function returns response with content', function () {
$tool = new GetOxDoorlockClientFunction;
$request = new Request(['function_name' => 'getClosestDoor']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock client function handles unknown function', function () {
$tool = new GetOxDoorlockClientFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_doorlock client function works with pickClosestDoor', function () {
$tool = new GetOxDoorlockClientFunction;
$request = new Request(['function_name' => 'pickClosestDoor']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock client function works with useClosestDoor', function () {
$tool = new GetOxDoorlockClientFunction;
$request = new Request(['function_name' => 'useClosestDoor']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock client function supports javascript examples', function () {
$tool = new GetOxDoorlockClientFunction;
$request = new Request(['function_name' => 'getClosestDoor', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,34 @@
<?php
use App\Mcp\Tools\COX\Doorlock\Server\GetOxDoorlockServerEvent;
use Laravel\Mcp\Request;
it('get ox_doorlock server event returns response with content', function () {
$tool = new GetOxDoorlockServerEvent;
$request = new Request(['event_name' => 'ox_doorlock:stateChanged']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock server event handles unknown event', function () {
$tool = new GetOxDoorlockServerEvent;
$request = new Request(['event_name' => 'NonExistentEvent123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_doorlock server event supports javascript examples', function () {
$tool = new GetOxDoorlockServerEvent;
$request = new Request(['event_name' => 'ox_doorlock:stateChanged', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,64 @@
<?php
use App\Mcp\Tools\COX\Doorlock\Server\GetOxDoorlockServerFunction;
use Laravel\Mcp\Request;
it('get ox_doorlock server function returns response with content', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'getDoor']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock server function handles unknown function', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_doorlock server function works with getDoorFromName', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'getDoorFromName']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock server function works with editDoor', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'editDoor']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock server function works with setDoorState', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'setDoorState']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_doorlock server function supports javascript examples', function () {
$tool = new GetOxDoorlockServerFunction;
$request = new Request(['function_name' => 'getDoor', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,34 @@
<?php
use App\Mcp\Tools\COX\Fuel\Client\GetOxFuelClientFunction;
use Laravel\Mcp\Request;
it('get ox_fuel client function returns response with content', function () {
$tool = new GetOxFuelClientFunction;
$request = new Request(['function_name' => 'setMoneyCheck']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_fuel client function handles unknown function', function () {
$tool = new GetOxFuelClientFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_fuel client function supports javascript examples', function () {
$tool = new GetOxFuelClientFunction;
$request = new Request(['function_name' => 'setMoneyCheck', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,34 @@
<?php
use App\Mcp\Tools\COX\Fuel\Server\GetOxFuelServerFunction;
use Laravel\Mcp\Request;
it('get ox_fuel server function returns response with content', function () {
$tool = new GetOxFuelServerFunction;
$request = new Request(['function_name' => 'setPaymentMethod']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_fuel server function handles unknown function', function () {
$tool = new GetOxFuelServerFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_fuel server function supports javascript examples', function () {
$tool = new GetOxFuelServerFunction;
$request = new Request(['function_name' => 'setPaymentMethod', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,84 @@
<?php
use App\Mcp\Tools\COX\Target\Client\GetOxTargetClientFunction;
use Laravel\Mcp\Request;
it('get ox_target client function returns response with content', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addSphereZone']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function handles unknown function', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get ox_target client function works with addGlobalVehicle', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addGlobalVehicle']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function works with addModel', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addModel']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function works with addBoxZone', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addBoxZone']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function works with addPolyZone', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addPolyZone']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function works with isActive', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'isActive']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get ox_target client function supports javascript examples', function () {
$tool = new GetOxTargetClientFunction;
$request = new Request(['function_name' => 'addEntity', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,132 @@
<?php
use App\Mcp\Tools\QBCore\GetQBCoreResourceList;
use Laravel\Mcp\Request;
test('GetQBCoreResourceList returns all resources', function () {
$tool = new GetQBCoreResourceList;
$request = new Request([]);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('QBCore Resources Directory');
expect($content)->toContain('qb-ambulancejob');
expect($content)->toContain('qb-adminmenu');
expect($content)->toContain('qb-policejob');
});
test('GetQBCoreResourceList contains all 58 resources', function () {
$tool = new GetQBCoreResourceList;
$request = new Request([]);
$response = $tool->handle($request);
$content = (string) $response->content();
// Count resource names in the response
$resources = [
'qb-adminmenu', 'qb-ambulancejob', 'qb-apartments', 'qb-banking', 'qb-bankrobbery',
'qb-busjob', 'qb-cityhall', 'qb-clothing', 'qb-crypto', 'qb-diving',
'qb-doorlock', 'qb-drugs', 'qb-fitbit', 'qb-fuel', 'qb-garages',
'qb-garbagejob', 'qb-hotdogjob', 'qb-houserobbery', 'qb-houses', 'qb-hud',
'qb-input', 'qb-interior', 'qb-inventory', 'qb-jewelry', 'qb-lapraces',
'qb-loading', 'qb-management', 'qb-mechanicjob', 'qb-menu', 'qb-minigames',
'qb-multicharacter', 'qb-newsjob', 'qb-pawnshop', 'qb-phone', 'qb-policejob',
'qb-prison', 'qb-radialmenu', 'qb-radio', 'qb-recyclejob', 'qb-scoreboard',
'qb-scrapyard', 'qb-shops', 'qb-smallresources', 'qb-spawn', 'qb-storerobbery',
'qb-streetraces', 'qb-taxijob', 'qb-towjob', 'qb-target', 'qb-truckerjob',
'qb-truckrobbery', 'qb-vehiclekeys', 'qb-vehiclesales', 'qb-vehicleshop', 'qb-vineyard',
'qb-weapons', 'qb-weathersync', 'qb-weed',
];
foreach ($resources as $resource) {
expect($content)->toContain($resource);
}
});
test('GetQBCoreResourceList filters by category - Jobs', function () {
$tool = new GetQBCoreResourceList;
$request = new Request(['category' => 'Jobs']);
$response = $tool->handle($request);
$content = (string) $response->content();
// Should contain job resources
expect($content)->toContain('qb-ambulancejob');
expect($content)->toContain('qb-policejob');
expect($content)->toContain('qb-mechanicjob');
// Should not contain non-job resources
expect($content)->not->toContain('## Admin & Management');
});
test('GetQBCoreResourceList filters by category - Crime & Robberies', function () {
$tool = new GetQBCoreResourceList;
$request = new Request(['category' => 'Crime & Robberies']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('qb-bankrobbery');
expect($content)->toContain('qb-drugs');
expect($content)->toContain('qb-weed');
});
test('GetQBCoreResourceList filters by category - Admin & Management', function () {
$tool = new GetQBCoreResourceList;
$request = new Request(['category' => 'Admin & Management']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('qb-adminmenu');
});
test('GetQBCoreResourceList filters by category - UI & Interface', function () {
$tool = new GetQBCoreResourceList;
$request = new Request(['category' => 'UI & Interface']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('qb-hud');
expect($content)->toContain('qb-menu');
expect($content)->toContain('qb-target');
});
test('GetQBCoreResourceList filters by category - Housing & Interiors', function () {
$tool = new GetQBCoreResourceList;
$request = new Request(['category' => 'Housing & Interiors']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('qb-apartments');
expect($content)->toContain('qb-houses');
expect($content)->toContain('qb-interior');
});
test('GetQBCoreResourceList contains documentation links', function () {
$tool = new GetQBCoreResourceList;
$request = new Request([]);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('https://docs.qbcore.org/qbcore-documentation/qbcore-resources/');
expect($content)->toContain('View Docs');
});
test('GetQBCoreResourceList has grouped categories', function () {
$tool = new GetQBCoreResourceList;
$request = new Request([]);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('### Admin');
expect($content)->toContain('### Jobs');
expect($content)->toContain('### Crime');
expect($content)->toContain('### Housing');
expect($content)->toContain('### Vehicles');
});
@@ -0,0 +1,138 @@
<?php
use App\Mcp\Tools\QBCore\GetQBCoreResourceReference;
use Laravel\Mcp\Request;
test('GetQBCoreResourceReference returns ambulance job details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-ambulancejob']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Ambulance Job');
expect($content)->toContain('Hospital Check-in System');
expect($content)->toContain('/heal');
expect($content)->toContain('ifaks');
});
test('GetQBCoreResourceReference returns admin menu details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-adminmenu']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Admin Menu');
expect($content)->toContain('/admin');
expect($content)->toContain('/blips');
expect($content)->toContain('Player Management');
});
test('GetQBCoreResourceReference returns hud details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-hud']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('HUD');
expect($content)->toContain('Health Display');
});
test('GetQBCoreResourceReference returns inventory details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-inventory']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Inventory');
expect($content)->toContain('Player Systems');
});
test('GetQBCoreResourceReference returns phone details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-phone']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Phone');
expect($content)->toContain('Messaging');
});
test('GetQBCoreResourceReference returns police job details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-policejob']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Police Job');
expect($content)->toContain('Dispatch System');
});
test('GetQBCoreResourceReference includes documentation URL', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-adminmenu']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('https://docs.qbcore.org/qbcore-documentation/qbcore-resources/qb-adminmenu');
});
test('GetQBCoreResourceReference not found returns helpful message', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-nonexistent']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
expect($content)->toContain('GetQBCoreResourceList');
});
test('GetQBCoreResourceReference handles case variations', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'QBCore-Ambulancejob']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Ambulance Job');
});
test('GetQBCoreResourceReference returns garages details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-garages']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Garages');
expect($content)->toContain('Transportation');
});
test('GetQBCoreResourceReference returns shops details', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-shops']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('Shops');
expect($content)->toContain('Commerce');
});
test('GetQBCoreResourceReference includes categories', function () {
$tool = new GetQBCoreResourceReference;
$request = new Request(['resource_name' => 'qb-target']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('UI & Interface');
expect($content)->toContain('Target System');
});