Add QBCore tools for player and shared data information

- Implement GetQBCorePlayers tool to retrieve player data structure, methods, events, and examples.
- Implement GetQBCoreSharedData tool to retrieve shared data types including items, jobs, gangs, vehicles, weapons, starter items, and utilities.
- Create corresponding Blade views for displaying player and shared data information.
- Add tests for GetQBCoreConfig, GetQBCorePlayers, and GetQBCoreSharedData tools to ensure correct functionality and response handling.
This commit is contained in:
Adrian Mejias committed 2026-03-01 14:51:22 -06:00
1 parent 2f8ebe2940
commit b4d3b3582c
14 files changed
+1157 -6

No files matched your search

+13 -1
View File
@@ -14,7 +14,7 @@ The FiveM MCP Server enables seamless integration with AI coding assistants (Cla
### Live Site
A live instance is available at: **https://fivem-mcp.kingsoflossantos.com/fivem**
A live instance is available at: **https://fivem-mcp.kingsoflossantos.com**
This site includes interactive documentation, tool descriptions, and integration guides.
@@ -44,6 +44,18 @@ npm run build
### Running the Server
**Live AI Assistants (Claude/Client):**
```json
{
"servers": {
"fivem": {
"url": "https://fivem-mcp.kingsoflossantos.com/fivem",
"type": "http"
}
}
}
```
**Local Testing:**
```bash
php artisan mcp:inspector fivem
+6
View File
@@ -24,6 +24,9 @@ use App\Mcp\Tools\FiveM\Server\GetEventServerReference;
use App\Mcp\Tools\FiveM\Server\GetNativeServerFunction;
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\GetQBCoreSharedData;
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerEventReference;
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerFunction;
use Laravel\Mcp\Server;
@@ -60,6 +63,9 @@ class FiveMServer extends Server
GetQBCoreClientFunction::class,
GetQBCoreServerEventReference::class,
GetQBCoreClientEventReference::class,
GetQBCoreSharedData::class,
GetQBCoreConfig::class,
GetQBCorePlayers::class,
GetCOXEventReference::class,
GetCOXFunction::class,
GetInventoryServerFunction::class,
@@ -113,6 +113,101 @@ class GetQBCoreClientFunction extends Tool
'lua_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoad')\nAddEventHandler('QBCore:Client:OnPlayerLoad', function()\n local playerData = QBCore.Functions.GetPlayerData()\n print('Player loaded: ' .. playerData.charinfo.firstname)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoad');\nAddEventHandler('QBCore:Client:OnPlayerLoad', function() {\n const playerData = QBCore.Functions.GetPlayerData();\n console.log(`Player loaded: \${playerData.charinfo.firstname}`);\n});",
],
'TriggerServerEvent' => [
'namespace' => 'FiveM Core',
'description' => 'Trigger a server-side event from the client (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'Event name to trigger on server'],
['name' => '...', 'type' => 'any', 'description' => 'Arguments to pass to server event'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "TriggerServerEvent('myResource:serverEvent', 'arg1', 'arg2')\n-- Received on server with:\n-- RegisterNetEvent('myResource:serverEvent')\n-- AddEventHandler('myResource:serverEvent', function(arg1, arg2) end)",
'js_example' => "TriggerServerEvent('myResource:serverEvent', 'arg1', 'arg2');\n// Received on server with:\n// on('myResource:serverEvent', (arg1, arg2) => {})",
],
'Citizen.Wait' => [
'namespace' => 'FiveM Core',
'description' => 'Wait/sleep for a specified duration in milliseconds (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'ms', 'type' => 'number', 'description' => 'Duration in milliseconds'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "print('Before wait')\nCitizen.Wait(5000) -- Wait 5 seconds\nprint('After wait')",
'js_example' => "console.log('Before wait');\nawait Citizen.Wait(5000); // Wait 5 seconds\nconsole.log('After wait');",
],
'Citizen.CreateThread' => [
'namespace' => 'FiveM Core',
'description' => 'Create an async thread (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'callback', 'type' => 'function', 'description' => 'Function to execute as thread'],
],
'returns' => ['type' => 'number', 'description' => 'Thread ID'],
'lua_example' => "Citizen.CreateThread(function()\n while true do\n Citizen.Wait(1000) -- Repeat every second\n print('This runs repeatedly')\n end\nend)",
'js_example' => "Citizen.CreateThread(async () => {\n while (true) {\n await Citizen.Wait(1000); // Repeat every second\n console.log('This runs repeatedly');\n }\n});",
],
'PlayerPedId' => [
'namespace' => 'FiveM Core',
'description' => 'Get the local player ped entity (client-side only)',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'number', 'description' => 'Player ped entity ID'],
'lua_example' => "local playerPed = PlayerPedId()\nprint('My ped ID: ' .. playerPed)",
'js_example' => "const playerPed = PlayerPedId();\nconsole.log(`My ped ID: \${playerPed}`);",
],
'GetEntityCoords' => [
'namespace' => 'FiveM Core',
'description' => 'Get the coordinates of an entity (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'entity', 'type' => 'number', 'description' => 'Entity ID'],
['name' => 'alive', 'type' => 'boolean|nil', 'description' => 'Whether to get alive coords (default true)'],
],
'returns' => ['type' => 'vector3', 'description' => 'X, Y, Z coordinates'],
'lua_example' => "local playerPed = PlayerPedId()\nlocal playerCoords = GetEntityCoords(playerPed)\nprint('Player at: ' .. playerCoords.x .. ', ' .. playerCoords.y .. ', ' .. playerCoords.z)",
'js_example' => "const playerPed = PlayerPedId();\nconst playerCoords = GetEntityCoords(playerPed);\nconsole.log(`Player at: \${playerCoords.x}, \${playerCoords.y}, \${playerCoords.z}`);",
],
'SetEntityCoords' => [
'namespace' => 'FiveM Core',
'description' => 'Set the coordinates of an entity (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'entity', 'type' => 'number', 'description' => 'Entity ID'],
['name' => 'x', 'type' => 'number', 'description' => 'X coordinate'],
['name' => 'y', 'type' => 'number', 'description' => 'Y coordinate'],
['name' => 'z', 'type' => 'number', 'description' => 'Z coordinate'],
['name' => 'alive', 'type' => 'boolean|nil', 'description' => 'Whether alive (default true)'],
['name' => 'animate', 'type' => 'boolean|nil', 'description' => 'Whether to animate movement (default false)'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local playerPed = PlayerPedId()\nSetEntityCoords(playerPed, 100.0, 200.0, 50.0, false, false, false, false)\nprint('Teleported!')",
'js_example' => "const playerPed = PlayerPedId();\nSetEntityCoords(playerPed, 100.0, 200.0, 50.0, false, false, false, false);\nconsole.log('Teleported!');",
],
'QBCore.Functions.Notify' => [
'namespace' => 'UI',
'description' => 'Send a notification to the local player on client (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'text', 'type' => 'string', 'description' => 'Notification text'],
['name' => 'type', 'type' => 'string', 'description' => 'Notification type: error, success, primary, warning'],
['name' => 'duration', 'type' => 'number|nil', 'description' => 'Duration in milliseconds (default 5000)'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "QBCore.Functions.Notify('Welcome to the server!', 'success', 5000)",
'js_example' => "QBCore.Functions.Notify('Welcome to the server!', 'success', 5000);",
],
'QBCore.Functions.GetResourceMeta' => [
'namespace' => 'Core',
'description' => 'Get a resource metadata value (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'key', 'type' => 'string', 'description' => 'Metadata key to retrieve'],
],
'returns' => ['type' => 'any', 'description' => 'The metadata value or nil'],
'lua_example' => "local version = QBCore.Functions.GetResourceMeta('version')\nif version then\n print('QBCore Version: ' .. version)\nend",
'js_example' => "const version = QBCore.Functions.GetResourceMeta('version');\nif (version) {\n console.log(`QBCore Version: \${version}`);\n}",
],
];
$nameLower = strtolower($name);
@@ -147,7 +242,7 @@ class GetQBCoreClientFunction extends Tool
return [
'function_name' => $schema
->string()
->description('The name of the QBCore client function to look up (e.g., "QBCore.Functions.GetPlayerData", "QBCore.Commands.Add")')
->description('The name of the QBCore client function to look up (e.g., "QBCore.Functions.GetPlayerData", "QBCore.Commands.Add", "TriggerServerEvent", "Citizen.Wait", "PlayerPedId")')
->required(),
'language' => $schema
->string()
+159
View File
@@ -0,0 +1,159 @@
<?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('Look up QBCore configuration options and settings. Returns information about core config, player defaults, server settings, and framework options.')]
class GetQBCoreConfig extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$configType = $request->get('config_type');
$language = $request->get('language', 'lua');
$config = $this->findConfigType($configType);
if (! $config) {
return Response::text(sprintf("QBCore config '%s' not found. Available types: Player, Framework, Database, Callbacks, Database, Features", $configType));
}
$output = $this->formatConfigInfo($config, $language);
return Response::text($output);
}
/**
* Find a QBCore config type.
*/
protected function findConfigType(string $name): ?array
{
$configs = [
'Player' => [
'category' => 'Player',
'description' => 'Default player configuration including character generation and initial data setup.',
'location' => 'qb-core/config.lua',
'config_options' => [
['name' => 'PlayerDefaults', 'type' => 'table', 'description' => 'Default values for new player characters (money, metadata, inventory)'],
['name' => 'Metadata', 'type' => 'table', 'description' => 'Default metadata values like hunger, thirst, stress, temperature'],
['name' => 'Money', 'type' => 'table', 'description' => 'Default starting money (cash, bank, crypto)'],
['name' => 'Licenses', 'type' => 'table', 'description' => 'Default licenses a player starts with'],
],
'lua_example' => "local QBConfig = exports['qb-core']:GetCoreObject().Config\nif QBConfig.PlayerDefaults then\n print('Default metadata: ' .. json.encode(QBConfig.PlayerDefaults.metadata))\nend",
'js_example' => "const QBConfig = exports['qb-core']:GetCoreObject().Config;\nif (QBConfig.PlayerDefaults) {\n console.log('Default metadata: ' + JSON.stringify(QBConfig.PlayerDefaults.metadata));\n}",
],
'Framework' => [
'category' => 'Framework',
'description' => 'Core framework settings including server name, version, and general options.',
'location' => 'qb-core/config.lua',
'config_options' => [
['name' => 'ServerName', 'type' => 'string', 'description' => 'Name of your QBCore server'],
['name' => 'Version', 'type' => 'string', 'description' => 'QBCore framework version'],
['name' => 'Timezone', 'type' => 'string', 'description' => 'Server timezone (e.g., "America/New_York")'],
['name' => 'Debug', 'type' => 'boolean', 'description' => 'Enable debug logging'],
],
'lua_example' => "local QBConfig = exports['qb-core']:GetCoreObject().Config\nprint('Server Name: ' .. QBConfig.ServerName)\nprint('Version: ' .. QBConfig.Version)\nprint('Debug Mode: ' .. tostring(QBConfig.Debug))",
'js_example' => "const QBConfig = exports['qb-core']:GetCoreObject().Config;\nconsole.log(`Server Name: \${QBConfig.ServerName}`);\nconsole.log(`Version: \${QBConfig.Version}`);\nconsole.log(`Debug Mode: \${QBConfig.Debug}`);",
],
'Database' => [
'category' => 'Database',
'description' => 'Database configuration and connection settings.',
'location' => 'qb-core/config.lua',
'config_options' => [
['name' => 'Backend', 'type' => 'string', 'description' => 'Database backend (ox_mysql, mysql-async, oxmysql)'],
['name' => 'Host', 'type' => 'string', 'description' => 'Database server address'],
['name' => 'Port', 'type' => 'number', 'description' => 'Database server port'],
['name' => 'Username', 'type' => 'string', 'description' => 'Database username'],
['name' => 'Password', 'type' => 'string', 'description' => 'Database password (set in .env or server.cfg)'],
['name' => 'Database', 'type' => 'string', 'description' => 'Database name'],
],
'lua_example' => "-- Database configuration is typically in environment variables\n-- Access through resource config if exposed\nprint('Database configuration is set in server.cfg or environment variables')",
'js_example' => "// Database configuration is typically in environment variables\n// Access through resource config if exposed\nconsole.log('Database configuration is set in server.cfg or environment variables');",
],
'Callbacks' => [
'category' => 'Callbacks',
'description' => 'Server-side callbacks that resources can register to respond to client requests.',
'location' => 'qb-core/server/callbacks.lua',
'config_options' => [
['name' => 'RegisterCallback', 'type' => 'function', 'description' => 'Register a server callback that client scripts can call'],
['name' => 'TriggerCallback', 'type' => 'function', 'description' => 'Trigger a callback from client to server with response'],
],
'lua_example' => "-- Server-side callback registration\nQBCore.Functions.CreateCallback('myResource:getData', function(source, cb, data)\n -- Process data from client\n cb({success = true, message = 'Data received'})\nend)\n\n-- Client-side callback usage\nQBCore.Functions.TriggerCallback('myResource:getData', function(result)\n print('Server response: ' .. result.message)\nend, myData)",
'js_example' => "// Server-side callback registration\nQBCore.Functions.CreateCallback('myResource:getData', (source, cb, data) => {\n // Process data from client\n cb({success: true, message: 'Data received'});\n});\n\n// Client-side callback usage\nQBCore.Functions.TriggerCallback('myResource:getData', (result) => {\n console.log(`Server response: \${result.message}`);\n}, myData);",
],
'Features' => [
'category' => 'Features',
'description' => 'Framework feature flags and optional feature configurations.',
'location' => 'qb-core/config.lua',
'config_options' => [
['name' => 'Encryption', 'type' => 'boolean', 'description' => 'Enable data encryption'],
['name' => 'Logging', 'type' => 'boolean', 'description' => 'Enable comprehensive logging'],
['name' => 'PerformanceMonitor', 'type' => 'boolean', 'description' => 'Monitor resource performance'],
['name' => 'AntiCheat', 'type' => 'boolean', 'description' => 'Enable anti-cheat systems'],
],
'lua_example' => "local QBConfig = exports['qb-core']:GetCoreObject().Config\nif QBConfig.Encryption then\n print('Data encryption is enabled')\nend\nif QBConfig.AntiCheat then\n print('Anti-cheat is enabled')\nend",
'js_example' => "const QBConfig = exports['qb-core']:GetCoreObject().Config;\nif (QBConfig.Encryption) {\n console.log('Data encryption is enabled');\n}\nif (QBConfig.AntiCheat) {\n console.log('Anti-cheat is enabled');\n}",
],
'RepTypes' => [
'category' => 'RepTypes',
'description' => 'Reputation types available for player progression. Defined in config for use with AddRep/RemoveRep.',
'location' => 'qb-core/config.lua',
'config_options' => [
['name' => 'Examples', 'type' => 'table', 'description' => 'Common rep types: selling, heist, cartheft, etc.'],
['name' => 'Custom', 'type' => 'string', 'description' => 'Add custom rep types in config for your resources'],
],
'lua_example' => "-- Add rep to player (types defined in config)\nPlayer.Functions.AddRep('selling', 10)\nPlayer.Functions.AddRep('heist', 5)\nPlayer.Functions.AddRep('cartheft', 15)\n\n-- Get current rep\nlocal sellingRep = Player.Functions.GetRep('selling')\nprint('Selling Rep: ' .. sellingRep)",
'js_example' => "// Add rep to player (types defined in config)\nPlayer.Functions.AddRep('selling', 10);\nPlayer.Functions.AddRep('heist', 5);\nPlayer.Functions.AddRep('cartheft', 15);\n\n// Get current rep\nconst sellingRep = Player.Functions.GetRep('selling');\nconsole.log(`Selling Rep: \${sellingRep}`);",
],
];
$nameLower = strtolower($name);
foreach ($configs as $configName => $configData) {
if (strtolower($configName) === $nameLower) {
return $configData;
}
}
return null;
}
/**
* Format config info.
*/
protected function formatConfigInfo(array $config, string $language): string
{
return view('mcp.qbcore.qbcore-config', [
'config' => $config,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'config_type' => $schema
->string()
->description('The type of QBCore configuration to look up (Player, Framework, Database, Callbacks, Features, RepTypes)')
->enum(['Player', 'Framework', 'Database', 'Callbacks', 'Features', 'RepTypes'])
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
+170
View File
@@ -0,0 +1,170 @@
<?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('Look up information about the QBCore.Players table structure and how to access player data. Describes player object properties and methods.')]
class GetQBCorePlayers extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$infoType = $request->get('info_type');
$language = $request->get('language', 'lua');
$info = $this->findPlayerInfo($infoType);
if (! $info) {
return Response::text(sprintf("QBCore player info '%s' not found. Available types: Structure, PlayerData, Methods, Events, Examples", $infoType));
}
$output = $this->formatPlayerInfo($info, $language);
return Response::text($output);
}
/**
* Find QBCore player information.
*/
protected function findPlayerInfo(string $name): ?array
{
$playerInfo = [
'Structure' => [
'type' => 'Structure',
'title' => 'QBCore.Players Table Structure',
'description' => 'The QBCore.Players table on the server contains all currently connected players indexed by their source (net ID).',
'properties' => [
['name' => 'QBCore.Players[source]', 'type' => 'table', 'description' => 'Player object for given source ID'],
['name' => 'PlayerData', 'type' => 'table', 'description' => 'All player data (citizenid, job, gang, money, items, etc.)'],
['name' => 'Functions', 'type' => 'table', 'description' => 'Methods available on player object (AddItem, RemoveItem, etc.)'],
['name' => 'Metadata', 'type' => 'table', 'description' => 'Player metadata (hunger, thirst, stress, etc.)'],
['name' => 'Inventory', 'type' => 'table', 'description' => 'Current inventory items and slots'],
],
'lua_example' => "-- Access a player by source (server-side)\nlocal Player = QBCore.Players[source]\nif Player then\n print('Player found: ' .. Player.PlayerData.charinfo.firstname)\nend\n\n-- Better to use the function instead:\nlocal Player = QBCore.Functions.GetPlayer(source)\nif Player then\n print('Player: ' .. Player.Functions.GetName())\nend",
'js_example' => "// Access a player by source (server-side)\nconst Player = QBCore.Players[global.source];\nif (Player) {\n console.log(`Player found: \${Player.PlayerData.charinfo.firstname}`);\n}\n\n// Better to use the function instead:\nconst Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n console.log(`Player: \${Player.Functions.GetName()}`);\n}",
],
'PlayerData' => [
'type' => 'PlayerData',
'title' => 'Player Data Properties',
'description' => 'Complete structure of the PlayerData table containing all player information.',
'properties' => [
['name' => 'source', 'type' => 'number', 'description' => 'Player net ID (used to identify player)'],
['name' => 'citizenid', 'type' => 'string', 'description' => 'Unique character identifier'],
['name' => 'cid', 'type' => 'number', 'description' => 'Character ID in database'],
['name' => 'charinfo', 'type' => 'table', 'description' => 'Character information (firstname, lastname, gender, birthdate, etc.)'],
['name' => 'job', 'type' => 'table', 'description' => 'Job information (name, label, grade, payment, onduty)'],
['name' => 'gang', 'type' => 'table', 'description' => 'Gang information (name, label, grade, isboss)'],
['name' => 'money', 'type' => 'table', 'description' => 'Money table (cash, bank accounts)'],
['name' => 'metadata', 'type' => 'table', 'description' => 'Player metadata (hunger, thirst, stress, armor, health)'],
['name' => 'items', 'type' => 'table', 'description' => 'Inventory items'],
['name' => 'position', 'type' => 'vector3', 'description' => 'Player position coordinates'],
['name' => 'licenses', 'type' => 'table', 'description' => 'Licenses owned by player'],
],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local pd = Player.PlayerData\n print('Name: ' .. pd.charinfo.firstname .. ' ' .. pd.charinfo.lastname)\n print('Citizen ID: ' .. pd.citizenid)\n print('Job: ' .. pd.job.name .. ' (Grade: ' .. pd.job.grade.level .. ')')\n print('Cash: $' .. pd.money.cash .. ' | Bank: $' .. pd.money.bank)\n print('Hunger: ' .. pd.metadata.hunger)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const pd = Player.PlayerData;\n console.log(`Name: \${pd.charinfo.firstname} \${pd.charinfo.lastname}`);\n console.log(`Citizen ID: \${pd.citizenid}`);\n console.log(`Job: \${pd.job.name} (Grade: \${pd.job.grade.level})`);\n console.log(`Cash: \\\$\${pd.money.cash} | Bank: \\\$\${pd.money.bank}`);\n console.log(`Hunger: \${pd.metadata.hunger}`);\n}",
],
'Methods' => [
'type' => 'Methods',
'title' => 'Player Object Methods',
'description' => 'Functions available on the Player object for manipulating player data.',
'methods' => [
['name' => 'AddItem(item, amount)', 'description' => 'Add item to player inventory'],
['name' => 'RemoveItem(item, amount)', 'description' => 'Remove item from player inventory'],
['name' => 'HasItem(item, amount)', 'description' => 'Check if player has item'],
['name' => 'GetItemByName(item)', 'description' => 'Get item data by name'],
['name' => 'AddMoney(type, amount, reason)', 'description' => 'Add money to player account'],
['name' => 'RemoveMoney(type, amount, reason)', 'description' => 'Remove money from player account'],
['name' => 'SetMoney(type, amount, reason)', 'description' => 'Set money to exact amount'],
['name' => 'GetMoney(type)', 'description' => 'Get player money amount'],
['name' => 'SetJob(job, grade)', 'description' => 'Set player job and grade'],
['name' => 'GetJob()', 'description' => 'Get player job data'],
['name' => 'SetJobDuty(onDuty)', 'description' => 'Set player on/off duty'],
['name' => 'SetGang(gang, grade)', 'description' => 'Set player gang and grade'],
['name' => 'SetPlayerData(key, value)', 'description' => 'Set player data by key'],
['name' => 'SetMetaData(key, value)', 'description' => 'Set player metadata by key'],
['name' => 'GetMetaData(key)', 'description' => 'Get player metadata by key'],
['name' => 'AddRep(type, amount)', 'description' => 'Add reputation points'],
['name' => 'RemoveRep(type, amount)', 'description' => 'Remove reputation points'],
['name' => 'GetRep(type)', 'description' => 'Get reputation value'],
['name' => 'Notify(text, type, duration)', 'description' => 'Send notification to player'],
['name' => 'Save()', 'description' => 'Save player data to database'],
['name' => 'UpdatePlayerData()', 'description' => 'Sync player data to client'],
['name' => 'GetName()', 'description' => 'Get full player name'],
['name' => 'Logout()', 'description' => 'Force player to logout'],
],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n -- Manage inventory\n Player.Functions.AddItem('water', 5)\n Player.Functions.RemoveItem('water', 2)\n \n -- Manage money\n Player.Functions.AddMoney('cash', 1000, 'Salary')\n local cash = Player.Functions.GetMoney('cash')\n \n -- Set job\n Player.Functions.SetJob('police', 2)\n Player.Functions.SetJobDuty(true)\n \n -- Notify player\n Player.Functions.Notify('Welcome to the server!', 'success')\n \n -- Save changes\n Player.Functions.Save()\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n // Manage inventory\n Player.Functions.AddItem('water', 5);\n Player.Functions.RemoveItem('water', 2);\n \n // Manage money\n Player.Functions.AddMoney('cash', 1000, 'Salary');\n const cash = Player.Functions.GetMoney('cash');\n \n // Set job\n Player.Functions.SetJob('police', 2);\n Player.Functions.SetJobDuty(true);\n \n // Notify player\n Player.Functions.Notify('Welcome to the server!', 'success');\n \n // Save changes\n Player.Functions.Save();\n}",
],
'Events' => [
'type' => 'Events',
'title' => 'Player-Related Events',
'description' => 'Events triggered when player data changes or player actions occur.',
'events' => [
['name' => 'QBCore:Player:SetPlayerData', 'description' => 'Triggered when player data is updated'],
['name' => 'QBCore:Server:OnJobUpdate', 'description' => 'Triggered when player job is changed'],
['name' => 'QBCore:Client:OnJobUpdate', 'description' => 'Client-side job update event'],
['name' => 'QBCore:Player:PlayerLoaded', 'description' => 'Triggered when player finishes loading'],
['name' => 'QBCore:Player:PlayerLoggedOut', 'description' => 'Triggered when player logs out'],
],
'lua_example' => "-- Server-side: Listen for job updates\nRegisterNetEvent('QBCore:Server:OnJobUpdate', function(source)\n local Player = QBCore.Functions.GetPlayer(source)\n if Player then\n print(Player.Functions.GetName() .. ' job updated to ' .. Player.PlayerData.job.name)\n end\nend)\n\n-- Client-side: Listen for player load\nRegisterNetEvent('QBCore:Player:SetPlayerData')\nAddEventHandler('QBCore:Player:SetPlayerData', function()\n local playerData = QBCore.Functions.GetPlayerData()\n print('Player data updated!')\nend)",
'js_example' => "// Server-side: Listen for job updates\non('QBCore:Server:OnJobUpdate', (source) => {\n const Player = QBCore.Functions.GetPlayer(source);\n if (Player) {\n console.log(`\${Player.Functions.GetName()} job updated to \${Player.PlayerData.job.name}`);\n }\n});\n\n// Client-side: Listen for player load\non('QBCore:Player:SetPlayerData', () => {\n const playerData = QBCore.Functions.GetPlayerData();\n console.log('Player data updated!');\n});",
],
'Examples' => [
'type' => 'Examples',
'title' => 'Common Player Usage Examples',
'description' => 'Real-world examples of working with player objects in QBCore.',
'lua_example' => "-- Example 1: Check if player has items before proceeding\nlocal Player = QBCore.Functions.GetPlayer(source)\nif Player then\n if Player.Functions.HasItem('police_badge', 1) then\n print('Player is a police officer')\n end\nend\n\n-- Example 2: Transfer money between players\nlocal Player1 = QBCore.Functions.GetPlayer(source)\nlocal Player2 = QBCore.Functions.GetPlayerByCitizenId(citizenid)\nif Player1 and Player2 then\n local amount = 500\n if Player1.Functions.RemoveMoney('cash', amount) then\n Player2.Functions.AddMoney('cash', amount, 'Player Transfer')\n Player1.Functions.Notify('Gave \$' .. amount .. ' to ' .. Player2.Functions.GetName(), 'success')\n Player2.Functions.Notify('Received \$' .. amount .. ' from ' .. Player1.Functions.GetName(), 'success')\n end\nend\n\n-- Example 3: Job paycheck system\nfor i = 1, #QBCore.Functions.GetPlayers() do\n local Player = QBCore.Functions.GetPlayer(QBCore.Functions.GetPlayers()[i])\n if Player and Player.PlayerData.job.onduty then\n local payment = Player.PlayerData.job.payment\n Player.Functions.AddMoney('bank', payment, 'Salary')\n Player.Functions.Notify('Received \$' .. payment .. ' salary', 'success')\n end\nend",
'js_example' => "// Example 1: Check if player has items before proceeding\nconst Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n if (Player.Functions.HasItem('police_badge', 1)) {\n console.log('Player is a police officer');\n }\n}\n\n// Example 2: Transfer money between players\nconst Player1 = QBCore.Functions.GetPlayer(global.source);\nconst Player2 = QBCore.Functions.GetPlayerByCitizenId(citizenid);\nif (Player1 && Player2) {\n const amount = 500;\n if (Player1.Functions.RemoveMoney('cash', amount)) {\n Player2.Functions.AddMoney('cash', amount, 'Player Transfer');\n Player1.Functions.Notify(`Gave \\\$\${amount} to \${Player2.Functions.GetName()}`, 'success');\n Player2.Functions.Notify(`Received \\\$\${amount} from \${Player1.Functions.GetName()}`, 'success');\n }\n}\n\n// Example 3: Job paycheck system\nconst players = QBCore.Functions.GetPlayers();\nfor (const playerId of players) {\n const Player = QBCore.Functions.GetPlayer(playerId);\n if (Player && Player.PlayerData.job.onduty) {\n const payment = Player.PlayerData.job.payment;\n Player.Functions.AddMoney('bank', payment, 'Salary');\n Player.Functions.Notify(`Received \\\$\${payment} salary`, 'success');\n }\n}",
],
];
$nameLower = strtolower($name);
foreach ($playerInfo as $infoName => $infoData) {
if (strtolower($infoName) === $nameLower) {
return $infoData;
}
}
return null;
}
/**
* Format player info.
*/
protected function formatPlayerInfo(array $info, string $language): string
{
return view('mcp.qbcore.qbcore-players', [
'info' => $info,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'info_type' => $schema
->string()
->description('The type of QBCore player information to look up (Structure, PlayerData, Methods, Events, Examples)')
->enum(['Structure', 'PlayerData', 'Methods', 'Events', 'Examples'])
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,183 @@
<?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('Look up QBCore shared data structure by name. Returns information about items, jobs, gangs, vehicles, weapons, and utility functions.')]
class GetQBCoreSharedData extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$dataType = $request->get('data_type');
$language = $request->get('language', 'lua');
$data = $this->findSharedData($dataType);
if (! $data) {
return Response::text(sprintf("QBCore shared data '%s' not found. Available types: Items, Jobs, Gangs, Vehicles, Weapons, StarterItems, Utilities", $dataType));
}
$output = $this->formatSharedInfo($data, $language);
return Response::text($output);
}
/**
* Find a QBCore shared data type.
*/
protected function findSharedData(string $name): ?array
{
$sharedData = [
'Items' => [
'type' => 'Items',
'description' => 'Table of all items available on the server. Each item has properties like label, weight, type, image, useable, etc.',
'location' => 'qb-core/shared/items.lua',
'properties' => [
['name' => 'name', 'type' => 'string', 'description' => 'Internal item identifier'],
['name' => 'label', 'type' => 'string', 'description' => 'Display name shown to players'],
['name' => 'weight', 'type' => 'number', 'description' => 'Item weight (affects inventory limit)'],
['name' => 'type', 'type' => 'string', 'description' => 'Item type (item, weapon, component, etc.)'],
['name' => 'image', 'type' => 'string', 'description' => 'Image filename for inventory display'],
['name' => 'unique', 'type' => 'boolean', 'description' => 'If true, item cannot be stacked'],
['name' => 'useable', 'type' => 'boolean', 'description' => 'If true, item can be used/consumed'],
['name' => 'shouldClose', 'type' => 'boolean', 'description' => 'If true, inventory closes when using item'],
['name' => 'description', 'type' => 'string', 'description' => 'Item description shown in inventory'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal item = QBCore.Shared.Items['water_bottle']\nif item then\n print('Item: ' .. item.label .. ' (Weight: ' .. item.weight .. ')')\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst item = QBCore.Shared.Items['water_bottle'];\nif (item) {\n console.log(`Item: \${item.label} (Weight: \${item.weight})`);\n}",
],
'Jobs' => [
'type' => 'Jobs',
'description' => 'Table of all jobs available on the server. Each job has grades (ranks) and payment amounts.',
'location' => 'qb-core/shared/jobs.lua',
'properties' => [
['name' => 'label', 'type' => 'string', 'description' => 'Job display name'],
['name' => 'type', 'type' => 'string', 'description' => 'Job type (leo, illegal, casino, etc.)'],
['name' => 'defaultDuty', 'type' => 'boolean', 'description' => 'If true, player starts on duty'],
['name' => 'offDutyPay', 'type' => 'boolean', 'description' => 'If true, player gets paid while off duty'],
['name' => 'grades', 'type' => 'table', 'description' => 'Array of job grades with name and payment'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal job = QBCore.Shared.Jobs['police']\nif job then\n print('Job: ' .. job.label)\n for i = 1, #job.grades do\n local grade = job.grades[i]\n print(' Grade: ' .. grade.name .. ' (Payment: $' .. grade.payment .. ')')\n end\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst job = QBCore.Shared.Jobs['police'];\nif (job) {\n console.log(`Job: \${job.label}`);\n job.grades.forEach((grade, i) => {\n console.log(` Grade: \${grade.name} (Payment: \\\$\${grade.payment})`);\n });\n}",
],
'Gangs' => [
'type' => 'Gangs',
'description' => 'Table of all gangs available on the server. Similar structure to jobs with labels and grades.',
'location' => 'qb-core/shared/gangs.lua',
'properties' => [
['name' => 'label', 'type' => 'string', 'description' => 'Gang display name'],
['name' => 'grades', 'type' => 'table', 'description' => 'Array of gang grades/ranks'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal gang = QBCore.Shared.Gangs['lostmc']\nif gang then\n print('Gang: ' .. gang.label)\n for i = 1, #gang.grades do\n print(' Rank: ' .. gang.grades[i].name)\n end\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst gang = QBCore.Shared.Gangs['lostmc'];\nif (gang) {\n console.log(`Gang: \${gang.label}`);\n gang.grades.forEach((grade) => {\n console.log(` Rank: \${grade.name}`);\n });\n}",
],
'Vehicles' => [
'type' => 'Vehicles',
'description' => 'Table of all vehicle models available on the server. Indexed by model name for quick lookup.',
'location' => 'qb-core/shared/vehicles.lua',
'properties' => [
['name' => 'model', 'type' => 'string', 'description' => 'Vehicle model spawn code'],
['name' => 'name', 'type' => 'string', 'description' => 'Vehicle display name'],
['name' => 'brand', 'type' => 'string', 'description' => 'Vehicle manufacturer/brand'],
['name' => 'price', 'type' => 'number', 'description' => 'Vehicle resale price'],
['name' => 'category', 'type' => 'string', 'description' => 'Vehicle category (compacts, sedans, etc.)'],
['name' => 'type', 'type' => 'string', 'description' => 'Vehicle type (automobile, bike, helicopter, etc.)'],
['name' => 'shop', 'type' => 'string|table', 'description' => 'Shop(s) where vehicle can be purchased'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal vehicle = QBCore.Shared.Vehicles['adder']\nif vehicle then\n print('Vehicle: ' .. vehicle.name .. ' by ' .. vehicle.brand)\n print('Price: $' .. vehicle.price)\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst vehicle = QBCore.Shared.Vehicles['adder'];\nif (vehicle) {\n console.log(`Vehicle: \${vehicle.name} by \${vehicle.brand}`);\n console.log(`Price: \\\$\${vehicle.price}`);\n}",
],
'Weapons' => [
'type' => 'Weapons',
'description' => 'Table of all weapons indexed by weapon hash. Used for retrieving weapon information.',
'location' => 'qb-core/shared/weapons.lua',
'properties' => [
['name' => 'name', 'type' => 'string', 'description' => 'Weapon model/spawn name'],
['name' => 'label', 'type' => 'string', 'description' => 'Weapon display name'],
['name' => 'weapontype', 'type' => 'string', 'description' => 'Weapon type (Pistol, Rifle, Shotgun, etc.)'],
['name' => 'ammotype', 'type' => 'string', 'description' => 'Ammo type used by weapon'],
['name' => 'damageReason', 'type' => 'string', 'description' => 'Death notification message for kills'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal weapon = QBCore.Shared.Weapons[`weapon_pistol`]\nif weapon then\n print('Weapon: ' .. weapon.label .. ' (' .. weapon.weapontype .. ')')\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst weapon = QBCore.Shared.Weapons['weapon_pistol'];\nif (weapon) {\n console.log(`Weapon: \${weapon.label} (\${weapon.weapontype})`);\n}",
],
'StarterItems' => [
'type' => 'StarterItems',
'description' => 'Table of items that new players receive when first joining. Defines item name and amount.',
'location' => 'qb-core/shared/main.lua',
'properties' => [
['name' => 'item_name', 'type' => 'string', 'description' => 'Item to give to new players'],
['name' => 'amount', 'type' => 'number', 'description' => 'Amount of item to give'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nfor item, amount in pairs(QBCore.Shared.StarterItems) do\n local itemInfo = QBCore.Shared.Items[item]\n if itemInfo then\n print('New players get ' .. amount .. 'x ' .. itemInfo.label)\n end\nend",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nfor (const [item, amount] of Object.entries(QBCore.Shared.StarterItems)) {\n const itemInfo = QBCore.Shared.Items[item];\n if (itemInfo) {\n console.log(`New players get \${amount}x \${itemInfo.label}`);\n }\n}",
],
'Utilities' => [
'type' => 'Utilities',
'description' => 'Utility functions available in QBShared for common operations like string manipulation and vehicle handling.',
'location' => 'qb-core/shared/main.lua',
'methods' => [
['name' => 'RandomStr(length)', 'description' => 'Generate random string of specified length'],
['name' => 'RandomInt(length)', 'description' => 'Generate random numeric string of specified length'],
['name' => 'SplitStr(str, delimiter)', 'description' => 'Split string by delimiter into table'],
['name' => 'Trim(value)', 'description' => 'Remove leading/trailing whitespace'],
['name' => 'Round(number, decimals)', 'description' => 'Round number to specified decimal places'],
['name' => 'ChangeVehicleExtra(vehicle, extra, enable)', 'description' => 'Toggle vehicle extra on/off'],
['name' => 'SetDefaultVehicleExtras(vehicle, config)', 'description' => 'Set default vehicle extras from config table'],
],
'lua_example' => "local QBCore = exports['qb-core']:GetCoreObject({'Shared'})\nlocal randomId = QBCore.Shared.RandomStr(8)\nlocal trimmed = QBCore.Shared.Trim(' hello world ')\nlocal rounded = QBCore.Shared.Round(3.14159, 2) -- Returns 3.14\nprint('Random ID: ' .. randomId)\nprint('Trimmed: ' .. trimmed)\nprint('Rounded: ' .. rounded)",
'js_example' => "const QBCore = exports['qb-core']:GetCoreObject(['Shared']);\nconst randomId = QBCore.Shared.RandomStr(8);\nconst trimmed = QBCore.Shared.Trim(' hello world ');\nconst rounded = QBCore.Shared.Round(3.14159, 2); // Returns 3.14\nconsole.log(`Random ID: \${randomId}`);\nconsole.log(`Trimmed: \${trimmed}`);\nconsole.log(`Rounded: \${rounded}`);",
],
];
$nameLower = strtolower($name);
foreach ($sharedData as $typeName => $typeData) {
if (strtolower($typeName) === $nameLower) {
return $typeData;
}
}
return null;
}
/**
* Format shared data info.
*/
protected function formatSharedInfo(array $data, string $language): string
{
return view('mcp.qbcore.qbcore-shared-data', [
'data' => $data,
'language' => $language,
])->render();
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
'data_type' => $schema
->string()
->description('The type of QBCore shared data to look up (Items, Jobs, Gangs, Vehicles, Weapons, StarterItems, Utilities)')
->enum(['Items', 'Jobs', 'Gangs', 'Vehicles', 'Weapons', 'StarterItems', 'Utilities'])
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -178,6 +178,193 @@ class GetQBCoreServerFunction extends Tool
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.DeleteVehicle('QBTEST')\n TriggerClientEvent('QBCore:Notify', source, 'Vehicle removed from garage')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.DeleteVehicle('QBTEST');\n TriggerClientEvent('QBCore:Notify', global.source, 'Vehicle removed from garage');\n}",
],
'SetGang' => [
'namespace' => 'Player',
'description' => 'Set a player\'s gang and grade (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'gangName', 'type' => 'string', 'description' => 'The gang name'],
['name' => 'grade', 'type' => 'number', 'description' => 'The gang grade/rank level'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetGang('lostmc', 1)\n TriggerClientEvent('QBCore:Notify', source, 'You joined the gang', 'success')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetGang('lostmc', 1);\n TriggerClientEvent('QBCore:Notify', global.source, 'You joined the gang', 'success');\n}",
],
'Notify' => [
'namespace' => 'Player',
'description' => 'Send a notification to a player (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'text', 'type' => 'string', 'description' => 'Notification text'],
['name' => 'type', 'type' => 'string', 'description' => 'Notification type: error, success, primary, warning'],
['name' => 'duration', 'type' => 'number|nil', 'description' => 'Duration in milliseconds (default 5000)'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.Notify('Hello!', 'success', 5000)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.Notify('Hello!', 'success', 5000);\n}",
],
'HasItem' => [
'namespace' => 'Player',
'description' => 'Check if a player has an item (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string|table', 'description' => 'Item name or array of item names'],
['name' => 'amount', 'type' => 'number', 'description' => 'Amount required (default 1)'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if player has the item(s)'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n if Player.Functions.HasItem('water_bottle', 1) then\n print('Player has water')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n if (Player.Functions.HasItem('water_bottle', 1)) {\n console.log('Player has water');\n }\n}",
],
'GetName' => [
'namespace' => 'Player',
'description' => 'Get a player\'s full character name (server-side)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'string', 'description' => 'Full name (first lastname)'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local fullName = Player.Functions.GetName()\n print('Player name: ' .. fullName)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const fullName = Player.Functions.GetName();\n console.log(`Player name: \${fullName}`);\n}",
],
'SetJobDuty' => [
'namespace' => 'Player',
'description' => 'Set a player on/off duty for their job (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'onDuty', 'type' => 'boolean', 'description' => 'True to set on duty, false to set off duty'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetJobDuty(true)\n Player.Functions.Notify('You are now on duty', 'success')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetJobDuty(true);\n Player.Functions.Notify('You are now on duty', 'success');\n}",
],
'SetPlayerData' => [
'namespace' => 'Player',
'description' => 'Set a player data value by key (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'key', 'type' => 'string', 'description' => 'The player data key to set'],
['name' => 'value', 'type' => 'any', 'description' => 'The value to set'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetPlayerData('position', vector3(-425.3, 1123.6, 325.0))\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetPlayerData('position', [-425.3, 1123.6, 325.0]);\n}",
],
'SetMetaData' => [
'namespace' => 'Player',
'description' => 'Set a player metadata value by key (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'key', 'type' => 'string', 'description' => 'The metadata key to set'],
['name' => 'value', 'type' => 'any', 'description' => 'The value to set'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetMetaData('hunger', 80)\n Player.Functions.SetMetaData('thirst', 70)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetMetaData('hunger', 80);\n Player.Functions.SetMetaData('thirst', 70);\n}",
],
'GetMetaData' => [
'namespace' => 'Player',
'description' => 'Get a player metadata value by key (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'key', 'type' => 'string', 'description' => 'The metadata key to retrieve'],
],
'returns' => ['type' => 'any', 'description' => 'The metadata value'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local hunger = Player.Functions.GetMetaData('hunger')\n print('Hunger Level: ' .. hunger)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const hunger = Player.Functions.GetMetaData('hunger');\n console.log(`Hunger Level: \${hunger}`);\n}",
],
'AddRep' => [
'namespace' => 'Player',
'description' => 'Add reputation points to a player (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'repType', 'type' => 'string', 'description' => 'Type of reputation to add (e.g., selling, heist)'],
['name' => 'amount', 'type' => 'number', 'description' => 'Amount of reputation to add'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.AddRep('selling', 10)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.AddRep('selling', 10);\n}",
],
'RemoveRep' => [
'namespace' => 'Player',
'description' => 'Remove reputation points from a player (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'repType', 'type' => 'string', 'description' => 'Type of reputation to remove'],
['name' => 'amount', 'type' => 'number', 'description' => 'Amount of reputation to remove'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.RemoveRep('selling', 5)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.RemoveRep('selling', 5);\n}",
],
'GetRep' => [
'namespace' => 'Player',
'description' => 'Get a player\'s reputation value (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'repType', 'type' => 'string', 'description' => 'Type of reputation to retrieve'],
],
'returns' => ['type' => 'number', 'description' => 'Reputation value'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local sellingRep = Player.Functions.GetRep('selling')\n print('Selling Rep: ' .. sellingRep)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const sellingRep = Player.Functions.GetRep('selling');\n console.log(`Selling Rep: \${sellingRep}`);\n}",
],
'SetMoney' => [
'namespace' => 'Player',
'description' => 'Set a player\'s money to a specific amount (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
['name' => 'amount', 'type' => 'number', 'description' => 'Amount to set'],
['name' => 'reason', 'type' => 'string|nil', 'description' => 'Optional reason for logging'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetMoney('cash', 5000, 'Admin Command')\n Player.Functions.Notify('Your cash has been set', 'info')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetMoney('cash', 5000, 'Admin Command');\n Player.Functions.Notify('Your cash has been set', 'info');\n}",
],
'Save' => [
'namespace' => 'Player',
'description' => 'Save a player\'s data to the database (server-side)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.Save()\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.Save();\n}",
],
'Logout' => [
'namespace' => 'Player',
'description' => 'Force a player to logout to the character selection (server-side)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.Logout()\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.Logout();\n}",
],
'UpdatePlayerData' => [
'namespace' => 'Player',
'description' => 'Update the player data on client and server (server-side)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.UpdatePlayerData()\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.UpdatePlayerData();\n}",
],
'GetPlayerBophone' => [
'namespace' => 'Core',
'description' => 'Get a player by their phone number (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'phoneNumber', 'type' => 'string', 'description' => 'The phone number to search for'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object or nil'],
'lua_example' => "local Player = QBCore.Functions.GetPlayerByPhone('555-0001')\nif Player then\n print('Found player: ' .. Player.Functions.GetName())\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayerByPhone('555-0001');\nif (Player) {\n console.log(`Found player: \${Player.Functions.GetName()}`);\n}",
],
'GetPlayers' => [
'namespace' => 'Core',
'description' => 'Get table of all online player net IDs (server-side only)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Array of all connected player net IDs'],
'lua_example' => "local players = QBCore.Functions.GetPlayers()\nfor i = 1, #players do\n local Player = QBCore.Functions.GetPlayer(players[i])\n if Player then\n print('Player: ' .. Player.Functions.GetName())\n end\nend",
'js_example' => "const players = QBCore.Functions.GetPlayers();\nfor (const playerId of players) {\n const Player = QBCore.Functions.GetPlayer(playerId);\n if (Player) {\n console.log(`Player: \${Player.Functions.GetName()}`);\n }\n}",
],
];
$nameLower = strtolower($name);
@@ -212,7 +399,7 @@ class GetQBCoreServerFunction extends Tool
return [
'function_name' => $schema
->string()
->description('The name of the QBCore server function to look up (e.g., "QBCore.Functions.GetPlayer", "AddMoney", "SetJob")')
->description('The name of the QBCore server function to look up (e.g., "QBCore.Functions.GetPlayer", "AddMoney", "SetJob", "SetGang", "Notify", "HasItem", "SetMetaData", "GetRep")')
->required(),
'language' => $schema
->string()
+10 -3
View File
@@ -171,11 +171,18 @@
<h2 class="text-3xl font-extrabold text-gray-900 mb-8">Resources</h2>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div class="bg-white shadow rounded-lg p-6">
<h3 class="text-lg font-medium text-gray-900 mb-4">Testing</h3>
<h3 class="text-lg font-medium text-gray-900 mb-4">MCP Server Configuration</h3>
<p class="text-sm text-gray-500 mb-4">
Test the MCP server locally using the inspector:
To connect to the FiveM MCP server, use the following configuration in your MCP client:
</p>
<pre class="bg-gray-50 rounded p-4 text-sm overflow-x-auto"><code>php artisan mcp:inspector fivem</code></pre>
<pre class="bg-gray-50 rounded p-4 text-sm overflow-x-auto"><code>{
"servers": {
"fivem": {
"url": "{{ route('fivem') }}",
"type": "http"
}
}
}</code></pre>
</div>
<div class="bg-white shadow rounded-lg p-6">
<h3 class="text-lg font-medium text-gray-900 mb-4">External Links</h3>
@@ -0,0 +1,28 @@
## {{ $config['category'] }} Configuration
**Location:** `{{ $config['location'] }}`
{{ $config['description'] }}
### Configuration Options
| Option | Type | Description |
|--------|------|-------------|
@foreach($config['config_options'] as $option)
| `{{ $option['name'] }}` | {{ $option['type'] }} | {{ $option['description'] }} |
@endforeach
@if($language === 'lua')
### Lua Example
```lua
{{ $config['lua_example'] }}
```
@else
### JavaScript Example
```javascript
{{ $config['js_example'] }}
```
@endif
### Learn More
- [QBCore Configuration Guide](https://docs.qbcore.org/qbcore-documentation/qb-core/configuration)
- [QBCore Repository](https://github.com/qbcore-framework/qb-core)
@@ -0,0 +1,46 @@
## {{ $info['title'] }}
{{ $info['description'] }}
@if(isset($info['properties']))
### Properties
| Property | Type | Description |
|----------|------|-------------|
@foreach($info['properties'] as $prop)
| `{{ $prop['name'] }}` | {{ $prop['type'] }} | {{ $prop['description'] }} |
@endforeach
@endif
@if(isset($info['methods']))
### Available Methods
| Method | Description |
|--------|-------------|
@foreach($info['methods'] as $method)
| `{{ $method['name'] }}` | {{ $method['description'] }} |
@endforeach
@endif
@if(isset($info['events']))
### Events
| Event | Description |
|-------|-------------|
@foreach($info['events'] as $event)
| `{{ $event['name'] }}` | {{ $event['description'] }} |
@endforeach
@endif
@if($language === 'lua')
### Lua Example
```lua
{{ $info['lua_example'] }}
```
@else
### JavaScript Example
```javascript
{{ $info['js_example'] }}
```
@endif
### Learn More
- [QBCore Player Data Documentation](https://docs.qbcore.org/qbcore-documentation/qb-core/player-data)
- [QBCore Repository](https://github.com/qbcore-framework/qb-core)
@@ -0,0 +1,35 @@
## {{ $data['type'] }} - QBCore Shared Data
**Location:** `{{ $data['location'] }}`
{{ $data['description'] }}
### Properties
@if(isset($data['properties']))
| Property | Type | Description |
|----------|------|-------------|
@foreach($data['properties'] as $prop)
| `{{ $prop['name'] }}` | {{ $prop['type'] }} | {{ $prop['description'] }} |
@endforeach
@endif
@if(isset($data['methods']))
### Available Methods
@foreach($data['methods'] as $method)
- **{{ $method['name'] }}** - {{ $method['description'] }}
@endforeach
@endif
### Lua Example
```lua
{{ $data['lua_example'] }}
```
### JavaScript Example
```javascript
{{ $data['js_example'] }}
```
### Learn More
- [QBCore Shared Documentation](https://docs.qbcore.org/qbcore-documentation/qb-core/shared)
- [QBCore Repository](https://github.com/qbcore-framework/qb-core)
+72
View File
@@ -0,0 +1,72 @@
<?php
use App\Mcp\Tools\QBCore\GetQBCoreConfig;
use Laravel\Mcp\Request;
test('GetQBCoreConfig returns Player config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'Player', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Player');
expect((string) $response->content())->toContain('PlayerDefaults');
});
test('GetQBCoreConfig returns Framework config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'Framework', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Framework');
expect((string) $response->content())->toContain('Version');
});
test('GetQBCoreConfig returns Database config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'Database', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Database');
expect((string) $response->content())->toContain('Backend');
});
test('GetQBCoreConfig returns Callbacks config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'Callbacks', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Callbacks');
expect((string) $response->content())->toContain('JavaScript Example');
});
test('GetQBCoreConfig returns Features config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'Features', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Features');
});
test('GetQBCoreConfig returns RepTypes config', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'RepTypes', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('RepTypes');
expect((string) $response->content())->toContain('selling');
});
test('GetQBCoreConfig returns not found for invalid type', function () {
$tool = new GetQBCoreConfig;
$request = new Request(['config_type' => 'InvalidType', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
@@ -0,0 +1,66 @@
<?php
use App\Mcp\Tools\QBCore\GetQBCorePlayers;
use Laravel\Mcp\Request;
test('GetQBCorePlayers returns Structure info', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'Structure', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Structure');
expect((string) $response->content())->toContain('PlayerData');
expect((string) $response->content())->toContain('Functions');
});
test('GetQBCorePlayers returns PlayerData info', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'PlayerData', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('PlayerData');
expect((string) $response->content())->toContain('citizenid');
expect((string) $response->content())->toContain('charinfo');
});
test('GetQBCorePlayers returns Methods info', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'Methods', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Methods');
expect((string) $response->content())->toContain('AddItem');
expect((string) $response->content())->toContain('SetJob');
});
test('GetQBCorePlayers returns Events info', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'Events', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Events');
expect((string) $response->content())->toContain('JavaScript Example');
});
test('GetQBCorePlayers returns Examples info', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'Examples', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Examples');
expect((string) $response->content())->toContain('HasItem');
});
test('GetQBCorePlayers returns not found for invalid type', function () {
$tool = new GetQBCorePlayers;
$request = new Request(['info_type' => 'InvalidType', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
@@ -0,0 +1,85 @@
<?php
use App\Mcp\Tools\QBCore\GetQBCoreSharedData;
use Laravel\Mcp\Request;
test('GetQBCoreSharedData returns Items data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Items', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Items');
expect((string) $response->content())->toContain('label');
expect((string) $response->content())->toContain('weight');
});
test('GetQBCoreSharedData returns Jobs data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Jobs', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Jobs');
expect((string) $response->content())->toContain('grades');
expect((string) $response->content())->toContain('Lua Example');
});
test('GetQBCoreSharedData returns Gangs data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Gangs', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Gangs');
});
test('GetQBCoreSharedData returns Vehicles data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Vehicles', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Vehicles');
expect((string) $response->content())->toContain('model');
expect((string) $response->content())->toContain('price');
});
test('GetQBCoreSharedData returns Weapons data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Weapons', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Weapons');
expect((string) $response->content())->toContain('JavaScript Example');
});
test('GetQBCoreSharedData returns StarterItems data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'StarterItems', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('StarterItems');
});
test('GetQBCoreSharedData returns Utilities data', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'Utilities', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Utilities');
expect((string) $response->content())->toContain('RandomStr');
expect((string) $response->content())->toContain('Trim');
});
test('GetQBCoreSharedData returns not found for invalid type', function () {
$tool = new GetQBCoreSharedData;
$request = new Request(['data_type' => 'InvalidType', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});