6 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
Adrian Mejias b4d3b3582c 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.
2026-03-01 14:51:22 -06:00
Adrian Mejias 2f8ebe2940 feat: Update COX and QBCore tools with new event and function references, enhance documentation views, and improve boilerplate generation 2026-03-01 14:41:31 -06:00
Adrian Mejias 6a14dff647 docs: Update README.md to enhance project overview, installation instructions, and tool descriptions 2026-03-01 14:34:12 -06:00
Adrian Mejias 4a4d9822e7 Add documentation links, refactor tools, and implement new tests
- Added links to QBCore and COX MySQL documentation in the main docs index.
- Refactored GenerateManifest and GenerateResourceBoilerplate tools to use the correct namespaces.
- Updated tests for GenerateManifest and GenerateResourceBoilerplate tools to remove unnecessary parentheses.
- Refactored COX event and function tools to use the correct namespaces and updated related tests.
- Introduced new tests for GetEventClientReference and GetEventServerReference tools.
- Added tests for inventory client and server functions, ensuring they handle various scenarios.
- Created tests for native client and server functions, including case insensitivity and example support.
- Implemented QBCore client and server event/function reference tests, ensuring proper functionality and error handling.
- Removed outdated tests for GetEventReference and GetQBCoreEventReference tools.
- Updated SearchFiveMDocs tool to use the correct namespace and improved test coverage.
2026-03-01 14:30:24 -06:00
84 changed files with 6153 additions and 1105 deletions

No files matched your search

+243 -12
View File
@@ -1,21 +1,252 @@
# FiveM MCP
# FiveM MCP Server
This repository contains a custom implementation of a Modular Command Protocol (MCP) server for FiveM, built using Laravel. The MCP server provides an interface for handling various commands and events related to FiveM development, allowing developers to easily access information about native functions, events, and more.
A comprehensive Model Context Protocol (MCP) server for FiveM (GTA5) development, built with Laravel. This server provides AI assistants with powerful tools and resources to help develop FiveM resources more efficiently.
## VSCode Extension
## Overview
A custom VSCode extension is included in this repository, which allows developers to interact with the MCP server directly from their code editor. The extension provides features such as command execution, event handling, and more.
The FiveM MCP Server enables seamless integration with AI coding assistants (Claude, ChatGPT, etc.) to provide:
- **Native function lookups** with client/server separation
- **Event documentation** for all frameworks
- **Framework support** for Standalone, ESX, QBCore, and COX
- **Resource generation** with customizable boilerplate
- **Documentation search** across FiveM, QBCore, and COX MySQL
- **Code snippets and best practices** for common patterns
`.vscode/settings.json`
### Live Site
A live instance is available at: **https://fivem-mcp.kingsoflossantos.com**
This site includes interactive documentation, tool descriptions, and integration guides.
## Quick Start
### Installation
```bash
# Clone the repository
git clone https://github.com/yourusername/fivem-mcp.git
cd fivem-mcp
# Install dependencies
composer install
npm install
# Copy environment file and generate key
cp .env.example .env
php artisan key:generate
# Run database migrations
php artisan migrate
# Build frontend assets
npm run build
```
### Running the Server
**Live AI Assistants (Claude/Client):**
```json
{
"servers": {
"fivem": {
"url": "https://fivem-mcp.kingsoflossantos.com/fivem",
"type": "http"
}
},
"inputs": []
"servers": {
"fivem": {
"url": "https://fivem-mcp.kingsoflossantos.com/fivem",
"type": "http"
}
}
}
```
**Local Testing:**
```bash
php artisan mcp:inspector fivem
```
**Start the MCP Server:**
```bash
php artisan mcp:start fivem
```
**For AI Assistants (Claude/Cline):**
Configure in your AI client settings:
```json
{
"servers": {
"fivem": {
"command": "php",
"args": ["/path/to/fivem-mcp/artisan", "mcp:start", "fivem"]
}
}
}
```
## Tools
The MCP server includes 15 powerful tools organized by framework and execution side:
### FiveM Core (5 tools)
- **SearchFiveMDocs** - Search FiveM, QBCore, and COX documentation
- **GetNativeClientFunction** - Look up client-side native functions (10 natives)
- **GetNativeServerFunction** - Look up server-side native functions (12 natives)
- **GetEventClientReference** - Client-side event reference (5 events)
- **GetEventServerReference** - Server-side event reference (4+ events)
### QBCore Framework (4 tools)
- **GetQBCoreServerFunction** - QBCore server functions (13+ functions)
- **GetQBCoreClientFunction** - QBCore client functions (6 functions)
- **GetQBCoreServerEventReference** - QBCore server events (6 events)
- **GetQBCoreClientEventReference** - QBCore client events (6 events)
### COX MySQL (2 tools)
- **GetCOXFunction** - OxMySQL database functions (8 functions)
- **GetCOXEventReference** - COX-specific events
### ox_inventory (2 tools)
- **GetInventoryServerFunction** - Server-side inventory management (21 functions)
- **GetInventoryClientFunction** - Client-side inventory functions (8 functions)
### Resource Generation (2 tools)
- **GenerateManifest** - Generate fxmanifest.lua with framework support
- **GenerateResourceBoilerplate** - Complete resource scaffolding
## Project Structure
```
app/Mcp/
├── Tools/
│ ├── COX/
│ │ ├── MySQL/
│ │ │ ├── GetCOXFunction.php
│ │ │ └── GetCOXEventReference.php
│ │ └── Inventory/
│ │ ├── Server/GetInventoryServerFunction.php
│ │ └── Client/GetInventoryClientFunction.php
│ ├── FiveM/
│ │ ├── Client/
│ │ │ ├── GetNativeClientFunction.php
│ │ │ └── GetEventClientReference.php
│ │ ├── Server/
│ │ │ ├── GetNativeServerFunction.php
│ │ │ └── GetEventServerReference.php
│ │ ├── GenerateManifest.php
│ │ ├── GenerateResourceBoilerplate.php
│ │ └── SearchFiveMDocs.php
│ └── QBCore/
│ ├── Server/
│ │ ├── GetQBCoreServerFunction.php
│ │ └── GetQBCoreServerEventReference.php
│ └── Client/
│ ├── GetQBCoreClientFunction.php
│ └── GetQBCoreClientEventReference.php
├── Resources/
│ ├── CodeSnippets.php
│ ├── BestPractices.php
│ ├── FrameworkComparison.php
│ └── DatabaseQueries.php
├── Prompts/
│ ├── CreateNewResource.php
│ ├── DebugIssues.php
│ ├── OptimizePerformance.php
│ ├── ConvertLanguage.php
│ └── AddFrameworkIntegration.php
└── Servers/
└── FiveMServer.php
```
## Resources
MCP Resources provide read-only reference documentation:
1. **Code Snippets** - Common FiveM patterns in Lua and JavaScript
2. **Best Practices** - Performance optimization and security guidelines
3. **Framework Comparison** - Standalone vs ESX vs QBCore differences
4. **Database Queries** - COX MySQL examples and patterns
## Prompts
Interactive prompts to guide AI assistants:
1. **Create New Resource** - Scaffold a complete FiveM resource
2. **Debug Issues** - Analyze and fix FiveM errors
3. **Optimize Performance** - Improve resource efficiency
4. **Convert Language** - Translate between Lua and JavaScript
5. **Add Framework Integration** - Migrate between frameworks
## Testing
Run the test suite to ensure everything works:
```bash
# Run all tests
php artisan test
# Run specific test file
php artisan test tests/Feature/GetNativeClientFunctionToolTest.php
# Run with coverage report
php artisan test --coverage
```
**Current Test Status:** 82 tests passing ✅
## Code Quality
This project follows PSR-12 coding standards and uses Laravel Pint for automatic formatting:
```bash
# Format all code
vendor/bin/pint
# Check formatting without fixing
vendor/bin/pint --test
```
## Documentation
- [FiveM Documentation](https://docs.fivem.net/)
- [FiveM Natives Reference](https://docs.fivem.net/natives/)
- [QBCore Documentation](https://docs.qbcore.org/)
- [COX MySQL Documentation](https://coxdocs.dev/)
- [Laravel MCP Documentation](https://laravel.com/docs/mcp)
- [Model Context Protocol](https://modelcontextprotocol.io/)
## Architecture Notes
### Client/Server Separation
All tools are organized by **execution side** (Client or Server) to ensure AI assistants provide appropriate code examples. For example:
- `GetNativeClientFunction` only contains client-side natives
- `GetNativeServerFunction` only contains server-side natives
- Events are similarly separated to avoid confusion
### Framework Organization
Tools are grouped by framework (FiveM, QBCore, COX) to make discovery intuitive and keep documentation focused.
### Response Format
All tool responses use consistent formatting with:
- Function/event signatures
- Parameter descriptions
- Return types
- Lua and JavaScript code examples
- Complete usage patterns
## Contributing
To add new tools or improve existing ones:
1. Create a new tool class in `app/Mcp/Tools/{Framework}/{Side}/`
2. Implement the `Tool` interface
3. Add corresponding test file in `tests/Feature/`
4. Register in `app/Mcp/Servers/FiveMServer.php`
5. Run tests: `php artisan test`
6. Format code: `vendor/bin/pint`
7. Submit PR with description
## License
MIT License - See LICENSE file for details
## Support
For issues or questions:
- Check the [documentation site](local)
- Review [FiveM docs](https://docs.fivem.net/)
- Open an issue on GitHub
+9 -9
View File
@@ -29,15 +29,15 @@ class AddFrameworkIntegration extends Prompt
$parts[] = sprintf(
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s",
"Please:",
"1. Update the fxmanifest.lua with framework dependencies",
"2. Replace custom player systems with framework functions",
"3. Convert money/inventory operations to framework equivalents",
"4. Update event handlers to use framework events",
"5. Add framework-specific features if beneficial",
"6. Maintain backward compatibility where possible",
"7. Add comments explaining the integration changes",
"Use the GetEventReference tool to find relevant framework events."
'Please:',
'1. Update the fxmanifest.lua with framework dependencies',
'2. Replace custom player systems with framework functions',
'3. Convert money/inventory operations to framework equivalents',
'4. Update event handlers to use framework events',
'5. Add framework-specific features if beneficial',
'6. Maintain backward compatibility where possible',
'7. Add comments explaining the integration changes',
'Use the GetEventReference tool to find relevant framework events.'
);
return Response::prompt(implode('', $parts));
+13 -13
View File
@@ -25,19 +25,19 @@ class ConvertLanguage extends Prompt
$parts[] = sprintf(
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n%s\n%s\n%s\n%s\n%s",
"Please:",
"1. Convert the code while maintaining functionality",
"2. Use idiomatic patterns for the target language",
"3. Update native function calls to match language syntax",
"4. Adjust event handlers and callbacks",
"5. Explain major differences in the conversion",
"6. Ensure FiveM compatibility in the target language",
"Key differences to consider:",
"- Array/table indexing (0 vs 1-based)",
"- String formatting",
"- Event registration syntax",
"- Async patterns",
"- Native return values"
'Please:',
'1. Convert the code while maintaining functionality',
'2. Use idiomatic patterns for the target language',
'3. Update native function calls to match language syntax',
'4. Adjust event handlers and callbacks',
'5. Explain major differences in the conversion',
'6. Ensure FiveM compatibility in the target language',
'Key differences to consider:',
'- Array/table indexing (0 vs 1-based)',
'- String formatting',
'- Event registration syntax',
'- Async patterns',
'- Native return values'
);
return Response::prompt(implode('', $parts));
+11 -11
View File
@@ -18,17 +18,17 @@ class CreateNewResource extends Prompt
$language = $request->get('language', 'lua');
return Response::prompt(sprintf(
"Create a new FiveM resource called '%s' with the following requirements:\n\n" .
"Framework: %s\n" .
"Language: %s\n\n" .
"Please:\n" .
"1. Generate a complete fxmanifest.lua\n" .
"2. Create a config file with common settings\n" .
"3. Generate boilerplate client and server files\n" .
"4. Include proper error handling and validation\n" .
"5. Follow FiveM best practices\n" .
"6. Add helpful comments explaining the code structure\n\n" .
"Use the available tools to generate the manifest and boilerplate code.",
"Create a new FiveM resource called '%s' with the following requirements:\n\n".
"Framework: %s\n".
"Language: %s\n\n".
"Please:\n".
"1. Generate a complete fxmanifest.lua\n".
"2. Create a config file with common settings\n".
"3. Generate boilerplate client and server files\n".
"4. Include proper error handling and validation\n".
"5. Follow FiveM best practices\n".
"6. Add helpful comments explaining the code structure\n\n".
'Use the available tools to generate the manifest and boilerplate code.',
$resourceName,
$framework,
$language
+7 -7
View File
@@ -28,13 +28,13 @@ class DebugIssues extends Prompt
$parts[] = sprintf(
"%s\n%s\n%s\n%s\n%s\n%s\n\n%s",
"Please analyze this issue and:",
"1. Identify the likely cause",
"2. Suggest fixes with code examples",
"3. Explain common mistakes that lead to this issue",
"4. Provide preventive measures",
"5. Reference FiveM best practices if applicable",
"Use the SearchFiveMDocs and GetNativeFunction tools to find relevant information."
'Please analyze this issue and:',
'1. Identify the likely cause',
'2. Suggest fixes with code examples',
'3. Explain common mistakes that lead to this issue',
'4. Provide preventive measures',
'5. Reference FiveM best practices if applicable',
'Use the SearchFiveMDocs and GetNativeFunction tools to find relevant information.'
);
return Response::prompt(implode('', $parts));
+12 -12
View File
@@ -25,18 +25,18 @@ class OptimizePerformance extends Prompt
$parts[] = sprintf(
"Script Type: %s\n\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n%s\n%s\n%s\n%s\n%s",
$scriptType,
"Please analyze the code and:",
"1. Identify performance issues (unnecessary loops, missing waits, etc.)",
"2. Suggest optimizations with code examples",
"3. Explain why each optimization improves performance",
"4. Provide the optimized version of the code",
"5. Reference FiveM best practices for performance",
"Focus on:",
"- Proper Citizen.Wait() usage",
"- Distance checks before expensive operations",
"- Caching frequently accessed values",
"- Reducing server-client communication",
"- Efficient event handling"
'Please analyze the code and:',
'1. Identify performance issues (unnecessary loops, missing waits, etc.)',
'2. Suggest optimizations with code examples',
'3. Explain why each optimization improves performance',
'4. Provide the optimized version of the code',
'5. Reference FiveM best practices for performance',
'Focus on:',
'- Proper Citizen.Wait() usage',
'- Distance checks before expensive operations',
'- Caching frequently accessed values',
'- Reducing server-client communication',
'- Efficient event handling'
);
return Response::prompt(implode('', $parts));
+47 -13
View File
@@ -11,15 +11,32 @@ use App\Mcp\Resources\BestPractices;
use App\Mcp\Resources\CodeSnippets;
use App\Mcp\Resources\DatabaseQueries;
use App\Mcp\Resources\FrameworkComparison;
use App\Mcp\Tools\GenerateManifest;
use App\Mcp\Tools\GenerateResourceBoilerplate;
use App\Mcp\Tools\GetCOXEventReference;
use App\Mcp\Tools\GetCOXFunction;
use App\Mcp\Tools\GetEventReference;
use App\Mcp\Tools\GetNativeFunction;
use App\Mcp\Tools\GetQBCoreEventReference;
use App\Mcp\Tools\GetQBCoreFunction;
use App\Mcp\Tools\SearchFiveMDocs;
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;
use App\Mcp\Tools\FiveM\GenerateResourceBoilerplate;
use App\Mcp\Tools\FiveM\SearchFiveMDocs;
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\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;
use Laravel\Mcp\Server;
use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
@@ -44,14 +61,31 @@ class FiveMServer extends Server
{
protected array $tools = [
SearchFiveMDocs::class,
GetNativeFunction::class,
GenerateManifest::class,
GetEventReference::class,
GenerateResourceBoilerplate::class,
GetQBCoreEventReference::class,
GetQBCoreFunction::class,
GetNativeClientFunction::class,
GetNativeServerFunction::class,
GetEventClientReference::class,
GetEventServerReference::class,
GetQBCoreServerFunction::class,
GetQBCoreClientFunction::class,
GetQBCoreServerEventReference::class,
GetQBCoreClientEventReference::class,
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,178 @@
<?php
namespace App\Mcp\Tools\COX\Inventory\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_inventory client functions by name. Returns function signature, parameters, return type, and usage examples for client-side inventory management.')]
class GetInventoryClientFunction 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_inventory client function '%s' not found. Check ox_inventory documentation at https://coxdocs.dev/ox_inventory", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_inventory client function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'openInventory' => [
'namespace' => 'Client',
'description' => 'Opens an inventory using the passed data',
'side' => 'client',
'parameters' => [
['name' => 'invType', 'type' => 'string', 'description' => "Type: 'player', 'shop', 'stash', 'crafting', 'container', 'drop', 'glovebox', 'trunk', 'dumpster'"],
['name' => 'data', 'type' => 'number|string|table', 'description' => 'Inventory data (playerId, stash name, etc.)'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_inventory:openInventory('player', 3)",
'js_example' => "exports.ox_inventory.openInventory('player', 3);",
],
'SearchPlayer' => [
'namespace' => 'Client',
'description' => 'Searches the player inventory for an item. Can return slots or count.',
'side' => 'client',
'parameters' => [
['name' => 'search', 'type' => 'string', 'description' => "'slots' or 'count'"],
['name' => 'item', 'type' => 'table|string', 'description' => 'Item name or array of items'],
['name' => 'metadata', 'type' => 'table|string|nil', 'description' => 'Metadata filter'],
],
'returns' => ['type' => 'table|number', 'description' => 'Slots or count depending on search type'],
'lua_example' => "local count = exports.ox_inventory:SearchPlayer('count', 'water')\nprint('You have '..count..' water')",
'js_example' => "const count = exports.ox_inventory.SearchPlayer('count', 'water');\nconsole.log(`You have \${count} water`);",
],
'GetCurrentWeapon' => [
'namespace' => 'Client',
'description' => 'Get data for the currently equipped weapon',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'table|nil', 'description' => 'Weapon data or nil if no weapon equipped'],
'lua_example' => "local weapon = exports.ox_inventory:GetCurrentWeapon()\nif weapon then\n print('Current weapon: '..weapon.name)\nend",
'js_example' => "const weapon = exports.ox_inventory.GetCurrentWeapon();\nif (weapon) {\n console.log('Current weapon: ' + weapon.name);\n}",
],
'closeInventory' => [
'namespace' => 'Client',
'description' => 'Closes the currently open inventory',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => 'exports.ox_inventory:closeInventory()',
'js_example' => 'exports.ox_inventory.closeInventory();',
],
'useItem' => [
'namespace' => 'Client',
'description' => 'Uses an item in the inventory. Triggers server-side item use callback.',
'side' => 'client',
'parameters' => [
['name' => 'item', 'type' => 'table', 'description' => 'Item data with slot and name'],
['name' => 'cb', 'type' => 'function|nil', 'description' => 'Callback function after use'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "local item = {slot = 1, name = 'water'}\nexports.ox_inventory:useItem(item)",
'js_example' => "const item = {slot: 1, name: 'water'};\nexports.ox_inventory.useItem(item);",
],
'displayMetadata' => [
'namespace' => 'Client',
'description' => 'Shows metadata information for an item',
'side' => 'client',
'parameters' => [
['name' => 'metadata', 'type' => 'table', 'description' => 'Metadata table to display'],
],
'returns' => ['type' => 'table', 'description' => 'Formatted metadata for display'],
'lua_example' => "local formatted = exports.ox_inventory:displayMetadata({serial = 'ABC123', owner = 'John'})",
'js_example' => "const formatted = exports.ox_inventory.displayMetadata({serial: 'ABC123', owner: 'John'});",
],
'Items' => [
'namespace' => 'Client',
'description' => 'Register client-side usable items with callbacks',
'side' => 'client',
'parameters' => [
['name' => 'itemName', 'type' => 'string', 'description' => 'Name of the item to register'],
['name' => 'callback', 'type' => 'function', 'description' => 'Function(data, slot) called when item is used'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_inventory:Items('bandage', function(data, slot)\n -- Apply bandage healing logic\n print('Used bandage from slot '..slot)\nend)",
'js_example' => "exports.ox_inventory.Items('bandage', (data, slot) => {\n // Apply bandage healing logic\n console.log(`Used bandage from slot \${slot}`);\n});",
],
'getCurrentWeight' => [
'namespace' => 'Client',
'description' => 'Returns the current total weight of the player inventory',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'number', 'description' => 'Current weight in grams'],
'lua_example' => "local weight = exports.ox_inventory:getCurrentWeight()\nprint('Current inventory weight: '..weight..'g')",
'js_example' => "const weight = exports.ox_inventory.getCurrentWeight();\nconsole.log(`Current inventory weight: \${weight}g`);",
],
'getPlayerInventory' => [
'namespace' => 'Client',
'description' => 'Returns the full player inventory data',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Complete inventory data'],
'lua_example' => "local inventory = exports.ox_inventory:getPlayerInventory()\nprint(json.encode(inventory, {indent=true}))",
'js_example' => "const inventory = exports.ox_inventory.getPlayerInventory();\nconsole.log(JSON.stringify(inventory, null, 2));",
],
];
$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_inventory client function to look up (e.g., "openInventory", "SearchPlayer", "useItem", "closeInventory")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,337 @@
<?php
namespace App\Mcp\Tools\COX\Inventory\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_inventory server functions by name. Returns function signature, parameters, return type, and usage examples for server-side inventory management.')]
class GetInventoryServerFunction 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_inventory server function '%s' not found. Check ox_inventory documentation at https://coxdocs.dev/ox_inventory", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find an ox_inventory server function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'AddItem' => [
'namespace' => 'Server',
'description' => 'Adds an item into the specified inventory. Should be used alongside CanCarryItem.',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory unique id, playerId, or table with id and owner'],
['name' => 'item', 'type' => 'string', 'description' => 'Name of the item to add'],
['name' => 'count', 'type' => 'number', 'description' => 'Number of items to add'],
['name' => 'metadata', 'type' => 'table|string|nil', 'description' => 'Unique data to attach to item. String creates table with "type" field'],
['name' => 'slot', 'type' => 'number|nil', 'description' => 'Specific slot to add item to'],
['name' => 'cb', 'type' => 'function|nil', 'description' => 'Callback function(success, response)'],
],
'returns' => ['type' => 'boolean, string|table', 'description' => 'success and response (or via callback)'],
'lua_example' => "local success, response = exports.ox_inventory:AddItem('gloveVGH283', 'bread', 4)\nif not success then\n return print(response) -- 'invalid_item', 'invalid_inventory', 'inventory_full'\nend\nprint(json.encode(response, {indent=true}))",
'js_example' => "const [success, response] = exports.ox_inventory.AddItem('gloveVGH283', 'bread', 4);\nif (!success) {\n return console.log(response);\n}\nconsole.log(JSON.stringify(response, null, 2));",
],
'RemoveItem' => [
'namespace' => 'Server',
'description' => 'Removes the specified item from the specified inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory unique id, playerId, or table'],
['name' => 'item', 'type' => 'string', 'description' => 'Name of the item to remove'],
['name' => 'count', 'type' => 'number', 'description' => 'Number of items to remove'],
['name' => 'metadata', 'type' => 'table|string|nil', 'description' => 'Only remove items with matching metadata'],
['name' => 'slot', 'type' => 'number|nil', 'description' => 'Specific slot to remove from'],
['name' => 'ignoreTotal', 'type' => 'boolean|nil', 'description' => 'Removes as many as possible up to count'],
['name' => 'strict', 'type' => 'boolean|nil', 'description' => 'Exact match on metadata (default true)'],
],
'returns' => ['type' => 'boolean, string', 'description' => 'success and optional error message'],
'lua_example' => "local success = exports.ox_inventory:RemoveItem('gloveVGH283', 'water', 2)\nif not success then\n print('Failed to remove item')\nend",
'js_example' => "const success = exports.ox_inventory.RemoveItem('gloveVGH283', 'water', 2);\nif (!success) {\n console.log('Failed to remove item');\n}",
],
'GetItem' => [
'namespace' => 'Server',
'description' => 'Returns generic item data from the specified inventory with total count',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'item', 'type' => 'table|string', 'description' => 'Item name or array of item names'],
['name' => 'metadata', 'type' => 'any|nil', 'description' => 'Only count items matching metadata'],
['name' => 'returnsCount', 'type' => 'boolean|nil', 'description' => 'If true, returns count only'],
],
'returns' => ['type' => 'table|number', 'description' => 'Item data with count, or just count if returnsCount=true'],
'lua_example' => "local item = ox_inventory:GetItem(source, 'water', nil, false)\nprint(json.encode(item, {indent=true}))",
'js_example' => "const item = ox_inventory.GetItem(source, 'water', null, false);\nconsole.log(JSON.stringify(item, null, 2));",
],
'CanCarryItem' => [
'namespace' => 'Server',
'description' => 'Returns true or false depending if inventory can carry the specified item. Checks weight and slots.',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'item', 'type' => 'table|string', 'description' => 'Item name or array of items'],
['name' => 'count', 'type' => 'number', 'description' => 'Number of items'],
['name' => 'metadata', 'type' => 'table|string|nil', 'description' => 'Item metadata'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if can carry, false otherwise'],
'lua_example' => "if exports.ox_inventory:CanCarryItem(source, 'water', 3) then\n -- Do stuff if can carry\nelse\n -- Do stuff if can't carry\nend",
'js_example' => "if (exports.ox_inventory.CanCarryItem(source, 'water', 3)) {\n // Do stuff if can carry\n} else {\n // Do stuff if can't carry\n}",
],
'GetInventory' => [
'namespace' => 'Server',
'description' => 'Returns the inventory associated with the ID and owner. Returns null if not found.',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'number|table', 'description' => 'Inventory identifier'],
['name' => 'owner', 'type' => 'string|boolean|nil', 'description' => 'Owner identifier'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Inventory data or nil'],
'lua_example' => "local inventory = exports.ox_inventory:GetInventory('example_stash', false)\nprint(json.encode(inventory, {indent = true}))",
'js_example' => "const inventory = exports.ox_inventory.GetInventory('example_stash', false);\nconsole.log(JSON.stringify(inventory, null, 2));",
],
'RegisterStash' => [
'namespace' => 'Server',
'description' => 'Creates a new custom stash. Must be called before player can open the stash.',
'side' => 'server',
'parameters' => [
['name' => 'id', 'type' => 'string|number', 'description' => 'Stash identifier for database'],
['name' => 'label', 'type' => 'string', 'description' => 'Display name when inventory is open'],
['name' => 'slots', 'type' => 'number', 'description' => 'Number of slots'],
['name' => 'maxWeight', 'type' => 'number', 'description' => 'Maximum weight in grams'],
['name' => 'owner', 'type' => 'string|boolean|nil', 'description' => 'Owner: string=specific, true=per-player, nil=shared'],
['name' => 'groups', 'type' => 'table|nil', 'description' => 'Table of jobs with minimum grades'],
['name' => 'coords', 'type' => 'vector3|table|nil', 'description' => 'Coordinates for proximity check'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_inventory:RegisterStash('policestash', 'Police Storage', 50, 100000, false, {['police'] = 0})",
'js_example' => "exports.ox_inventory.RegisterStash('policestash', 'Police Storage', 50, 100000, false, {police: 0});",
],
'Search' => [
'namespace' => 'Server',
'description' => 'Searches an inventory for a specified item. Can return slots or count.',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'search', 'type' => 'string', 'description' => "'slots' or 'count'"],
['name' => 'item', 'type' => 'table|string', 'description' => 'Item name or array of names'],
['name' => 'metadata', 'type' => 'table|string|nil', 'description' => 'Item metadata filter'],
],
'returns' => ['type' => 'table|number', 'description' => 'Slots data or count depending on search type'],
'lua_example' => "local slots = exports.ox_inventory:Search(source, 'slots', 'water')\nfor k, v in pairs(slots) do\n print(v.slot..' contains '..v.count..' water')\nend",
'js_example' => "const slots = exports.ox_inventory.Search(source, 'slots', 'water');\nfor (const [k, v] of Object.entries(slots)) {\n console.log(`\${v.slot} contains \${v.count} water`);\n}",
],
'SetItem' => [
'namespace' => 'Server',
'description' => 'Sets data for a specific item slot in an inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'slot', 'type' => 'number', 'description' => 'Slot number to modify'],
['name' => 'item', 'type' => 'table', 'description' => 'Item data to set'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "exports.ox_inventory:SetItem(source, 1, {name = 'water', count = 5})",
'js_example' => "exports.ox_inventory.SetItem(source, 1, {name: 'water', count: 5});",
],
'ClearInventory' => [
'namespace' => 'Server',
'description' => 'Removes all items from the specified inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'filterItem', 'type' => 'string|nil', 'description' => 'Optional item name to keep'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "exports.ox_inventory:ClearInventory(source) -- Clear all items\nexports.ox_inventory:ClearInventory(source, 'id_card') -- Clear all except id_card",
'js_example' => "exports.ox_inventory.ClearInventory(source); // Clear all items\nexports.ox_inventory.ClearInventory(source, 'id_card'); // Clear all except id_card",
],
'ConfiscateInventory' => [
'namespace' => 'Server',
'description' => 'Temporarily removes all items from inventory and stores them for later return',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local success = exports.ox_inventory:ConfiscateInventory(source)\nif success then\n print('Inventory confiscated')\nend",
'js_example' => "const success = exports.ox_inventory.ConfiscateInventory(source);\nif (success) {\n console.log('Inventory confiscated');\n}",
],
'ReturnInventory' => [
'namespace' => 'Server',
'description' => 'Returns previously confiscated items to the inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local success = exports.ox_inventory:ReturnInventory(source)\nif success then\n print('Inventory returned')\nend",
'js_example' => "const success = exports.ox_inventory.ReturnInventory(source);\nif (success) {\n console.log('Inventory returned');\n}",
],
'SetDurability' => [
'namespace' => 'Server',
'description' => 'Sets the durability value for an item in a specific slot',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'slot', 'type' => 'number', 'description' => 'Slot number'],
['name' => 'durability', 'type' => 'number', 'description' => 'Durability value (0-100)'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => 'exports.ox_inventory:SetDurability(source, 1, 50) -- Set to 50% durability',
'js_example' => 'exports.ox_inventory.SetDurability(source, 1, 50); // Set to 50% durability',
],
'SetMetadata' => [
'namespace' => 'Server',
'description' => 'Updates metadata for an item in a specific slot',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'slot', 'type' => 'number', 'description' => 'Slot number'],
['name' => 'metadata', 'type' => 'table', 'description' => 'New metadata table'],
],
'returns' => ['type' => 'boolean, table', 'description' => 'Success status and updated item data'],
'lua_example' => "local success, item = exports.ox_inventory:SetMetadata(source, 1, {serial = 'ABC123', registered = 'John Doe'})",
'js_example' => "const [success, item] = exports.ox_inventory.SetMetadata(source, 1, {serial: 'ABC123', registered: 'John Doe'});",
],
'SwapSlots' => [
'namespace' => 'Server',
'description' => 'Swaps items between two slots in an inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'fromSlot', 'type' => 'number', 'description' => 'Source slot number'],
['name' => 'toSlot', 'type' => 'number', 'description' => 'Destination slot number'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => 'exports.ox_inventory:SwapSlots(source, 1, 5) -- Swap items in slots 1 and 5',
'js_example' => 'exports.ox_inventory.SwapSlots(source, 1, 5); // Swap items in slots 1 and 5',
],
'SetMaxWeight' => [
'namespace' => 'Server',
'description' => 'Sets the maximum weight capacity for an inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'maxWeight', 'type' => 'number', 'description' => 'Maximum weight in grams'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => 'exports.ox_inventory:SetMaxWeight(source, 50000) -- Set max weight to 50kg',
'js_example' => 'exports.ox_inventory.SetMaxWeight(source, 50000); // Set max weight to 50kg',
],
'GetItemCount' => [
'namespace' => 'Server',
'description' => 'Returns the total count of a specific item in an inventory',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'item', 'type' => 'string', 'description' => 'Item name'],
['name' => 'metadata', 'type' => 'table|nil', 'description' => 'Optional metadata filter'],
],
'returns' => ['type' => 'number', 'description' => 'Total count of the item'],
'lua_example' => "local count = exports.ox_inventory:GetItemCount(source, 'water')\nprint('Player has '..count..' water')",
'js_example' => "const count = exports.ox_inventory.GetItemCount(source, 'water');\nconsole.log(`Player has \${count} water`);",
],
'GetSlotWeight' => [
'namespace' => 'Server',
'description' => 'Returns the weight of an item in a specific slot',
'side' => 'server',
'parameters' => [
['name' => 'inv', 'type' => 'table|string|number', 'description' => 'Inventory identifier'],
['name' => 'slot', 'type' => 'number', 'description' => 'Slot number'],
],
'returns' => ['type' => 'number', 'description' => 'Weight in grams'],
'lua_example' => "local weight = exports.ox_inventory:GetSlotWeight(source, 1)\nprint('Slot 1 weight: '..weight..'g')",
'js_example' => "const weight = exports.ox_inventory.GetSlotWeight(source, 1);\nconsole.log(`Slot 1 weight: \${weight}g`);",
],
'CustomDrop' => [
'namespace' => 'Server',
'description' => 'Creates a custom drop inventory at specific coordinates',
'side' => 'server',
'parameters' => [
['name' => 'prefix', 'type' => 'string', 'description' => 'Prefix for drop ID'],
['name' => 'items', 'type' => 'table', 'description' => 'Array of items to add'],
['name' => 'coords', 'type' => 'vector3', 'description' => 'Drop coordinates'],
['name' => 'slots', 'type' => 'number|nil', 'description' => 'Number of slots (default config)'],
['name' => 'maxWeight', 'type' => 'number|nil', 'description' => 'Max weight (default config)'],
['name' => 'model', 'type' => 'string|number|nil', 'description' => 'Model hash for drop'],
],
'returns' => ['type' => 'string', 'description' => 'Drop inventory ID'],
'lua_example' => "local dropId = exports.ox_inventory:CustomDrop('evidence', {{name='phone', count=1}}, vector3(100.0, 200.0, 30.0))",
'js_example' => "const dropId = exports.ox_inventory.CustomDrop('evidence', [{name: 'phone', count: 1}], [100.0, 200.0, 30.0]);",
],
'ConvertItems' => [
'namespace' => 'Server',
'description' => 'Converts old items to new items with metadata (for migration)',
'side' => 'server',
'parameters' => [
['name' => 'playerId', 'type' => 'number', 'description' => 'Player server ID'],
['name' => 'items', 'type' => 'table', 'description' => 'Array of items to convert'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local items = {{name='old_phone', count=1, info={}}}\nexports.ox_inventory:ConvertItems(source, items)",
'js_example' => "const items = [{name: 'old_phone', count: 1, info: {}}];\nexports.ox_inventory.ConvertItems(source, items);",
],
];
$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_inventory server function to look up (e.g., "AddItem", "RemoveItem", "GetItem", "RegisterStash", "SetDurability", "SetMetadata")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\COX\MySQL;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -60,7 +60,7 @@ class GetCOXEventReference extends Tool
}
return Response::text(
view('mcp.cox-event-list', [
view('mcp.cox.cox-event-list', [
'eventsByType' => $eventsByType,
])->render()
);
@@ -71,7 +71,7 @@ class GetCOXEventReference extends Tool
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.cox-event-reference', [
return view('mcp.cox.cox-event-reference', [
'event' => $event,
'language' => $language,
])->render();
+191
View File
@@ -0,0 +1,191 @@
<?php
namespace App\Mcp\Tools\COX\MySQL;
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 COX MySQL functions and methods by name. Returns function signature, parameters, return type, and usage examples.')]
class GetCOXFunction 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("COX function '%s' not found. Check COX documentation at https://coxdocs.dev/", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find a COX function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
// Query Methods
'MySQL.query' => [
'namespace' => 'Query',
'description' => 'Execute a MySQL query with optional parameters. When selecting data, returns all matching rows; otherwise returns data like insertId, affectedRows, etc.',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query with ? placeholders for prepared statements'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Array of parameters to bind to placeholders'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback function that receives the query result'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Query result (rows for SELECT, or metadata for INSERT/UPDATE/DELETE)'],
'lua_example' => "MySQL.query('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', {\n identifier\n}, function(response)\n if response then\n for i = 1, #response do\n local row = response[i]\n print(row.firstname, row.lastname)\n end\n end\nend)",
'js_example' => "MySQL.query('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', [\n identifier\n], (response) => {\n if (response) {\n response.forEach(row => {\n console.log(row.firstname, row.lastname);\n });\n }\n});",
],
'MySQL.insert' => [
'namespace' => 'Query',
'description' => 'Insert a row into the database and get the insert ID',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'INSERT SQL query with ? placeholders'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Array of parameters to bind to placeholders'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback function that receives the insert ID'],
],
'returns' => ['type' => 'number', 'description' => 'Insert ID (in callback mode) or awaitable insert ID (in promise mode)'],
'lua_example' => "MySQL.insert('INSERT INTO `users` (identifier, firstname, lastname) VALUES (?, ?, ?)', {\n identifier, firstName, lastName\n}, function(id)\n print('Inserted with ID: ' .. id)\nend)",
'js_example' => "MySQL.insert('INSERT INTO `users` (identifier, firstname, lastname) VALUES (?, ?, ?)', [\n identifier, firstName, lastName\n], (id) => {\n console.log('Inserted with ID: ' + id);\n});",
],
'MySQL.update' => [
'namespace' => 'Query',
'description' => 'Update rows in the database and get the number of affected rows',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'UPDATE SQL query with ? placeholders'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Array of parameters to bind to placeholders'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback function that receives affected rows count'],
],
'returns' => ['type' => 'number', 'description' => 'Number of affected rows (in callback mode) or awaitable affected rows (in promise mode)'],
'lua_example' => "MySQL.update('UPDATE users SET firstname = ? WHERE identifier = ?', {\n newName, identifier\n}, function(affectedRows)\n print('Updated ' .. affectedRows .. ' rows')\nend)",
'js_example' => "MySQL.update('UPDATE users SET firstname = ? WHERE identifier = ?', [\n newName, identifier\n], (affectedRows) => {\n console.log(`Updated \${affectedRows} rows`);\n});",
],
'MySQL.scalar' => [
'namespace' => 'Query',
'description' => 'Returns the first column for a single row',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query with ? placeholders'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Query parameters'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback that receives the scalar value'],
],
'returns' => ['type' => 'any', 'description' => 'Single value from the first column of the first row'],
'lua_example' => "MySQL.scalar('SELECT `firstname` FROM `users` WHERE `identifier` = ? LIMIT 1', {\n identifier\n}, function(firstName)\n print(firstName)\nend)",
'js_example' => "MySQL.scalar('SELECT `firstname` FROM `users` WHERE `identifier` = ? LIMIT 1', [\n identifier\n], (firstName) => {\n console.log(firstName);\n});",
],
'MySQL.single' => [
'namespace' => 'Query',
'description' => 'Returns all selected columns for a single row',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query with ? placeholders'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Query parameters'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback that receives the row data'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Single row object or nil if no results'],
'lua_example' => "MySQL.single('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ? LIMIT 1', {\n identifier\n}, function(row)\n if not row then return end\n print(row.firstname, row.lastname)\nend)",
'js_example' => "MySQL.single('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ? LIMIT 1', [\n identifier\n], (row) => {\n if (!row) return;\n console.log(row.firstname, row.lastname);\n});",
],
// Transaction Methods
'MySQL.transaction' => [
'namespace' => 'Transaction',
'description' => 'Execute multiple queries as a transaction. If one fails, none are committed. Returns boolean indicating success.',
'side' => 'server',
'parameters' => [
['name' => 'queries', 'type' => 'table', 'description' => 'Array of query objects with query and values/parameters'],
['name' => 'values', 'type' => 'table|function|nil', 'description' => 'Shared parameters for all queries (optional) or callback function'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback that receives boolean success result'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if transaction succeeded, false otherwise'],
'lua_example' => "-- Specific format (unique params per query)\nlocal queries = {\n { query = 'INSERT INTO `test` (id) VALUES (?)', values = { 1 } },\n { query = 'INSERT INTO `test` (id, name) VALUES (?, ?)', values = { 2, 'bob' } },\n}\nMySQL.transaction(queries, function(success)\n print(success)\nend)",
'js_example' => "// Specific format (unique params per query)\nconst queries = [\n { query: 'INSERT INTO `test` (id) VALUES (?)', values: [1] },\n { query: 'INSERT INTO `test` (id, name) VALUES (?, ?)', values: [2, 'bob'] },\n];\nMySQL.transaction(queries, (success) => {\n console.log(success);\n});",
],
// Prepared Statements
'MySQL.prepare' => [
'namespace' => 'Prepared',
'description' => 'Execute frequently called queries faster. Accepts multiple sets of parameters. Only supports ? value placeholders. Return format matches query/single/scalar based on result shape.',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL with ? value placeholders (no ?? column or named placeholders)'],
['name' => 'params', 'type' => 'table', 'description' => 'Parameters to bind'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback that receives results'],
],
'returns' => ['type' => 'any', 'description' => 'Returns column, row, or array of rows depending on SELECT result shape'],
'lua_example' => "MySQL.prepare('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', {\n identifier\n}, function(response)\n print(json.encode(response, { indent = true, sort_keys = true }))\nend)",
'js_example' => "MySQL.prepare('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', [\n identifier\n], (response) => {\n console.log(JSON.stringify(response, null, 2));\n});",
],
'MySQL.rawExecute' => [
'namespace' => 'Prepared',
'description' => 'Execute frequently called queries faster. Unlike prepare, SELECT always returns array of rows. Only supports ? value placeholders. Date and TINYINT/BIT types are not converted.',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL with ? value placeholders only'],
['name' => 'params', 'type' => 'table', 'description' => 'Parameters to bind'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback that receives results'],
],
'returns' => ['type' => 'table', 'description' => 'Always returns array of rows for SELECT'],
'lua_example' => "MySQL.rawExecute('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', {\n identifier\n}, function(response)\n print(json.encode(response, { indent = true, sort_keys = true }))\nend)",
'js_example' => "MySQL.rawExecute('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', [\n identifier\n], (response) => {\n console.log(JSON.stringify(response, null, 2));\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 COX function to look up (e.g., "MySQL.query", "MySQL.insert", "MySQL.transaction.begin")')
->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'),
];
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\FiveM\Client;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -8,8 +8,8 @@ use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Get information about FiveM events including built-in and common framework events (ESX, QBCore).')]
class GetEventReference extends Tool
#[Description('Get information about FiveM client-side events including framework events (ESX, QBCore).')]
class GetEventClientReference extends Tool
{
/**
* Handle the tool request.
@@ -40,7 +40,7 @@ class GetEventReference extends Tool
}
}
return Response::text(sprintf("Event '%s' not found in the database.", $eventName));
return Response::text(sprintf("Client event '%s' not found in the database.", $eventName));
}
/**
@@ -60,7 +60,7 @@ class GetEventReference extends Tool
}
return Response::text(
view('mcp.event-list', [
view('mcp.fivem.event-list', [
'eventsByType' => $eventsByType,
])->render()
);
@@ -71,70 +71,44 @@ class GetEventReference extends Tool
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.event-reference', [
return view('mcp.fivem.event-reference', [
'event' => $event,
'language' => $language,
])->render();
}
/**
* Get events database.
* Get client events database.
*/
protected function getEventsDatabase(): array
{
return [
// Core FiveM Events
// Core FiveM Client Events
[
'name' => 'onResourceStart',
'type' => 'core',
'side' => 'both',
'description' => 'Triggered when a resource starts',
'side' => 'client',
'description' => 'Triggered when a resource starts (client-side)',
'parameters' => [
['name' => 'resourceName', 'type' => 'string', 'description' => 'The name of the resource that started'],
],
'lua_example' => "AddEventHandler('onResourceStart', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource started!')\n end\nend)",
'js_example' => "on('onResourceStart', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource started!');\n }\n});",
'lua_example' => "AddEventHandler('onResourceStart', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource started on client!')\n end\nend)",
'js_example' => "on('onResourceStart', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource started on client!');\n }\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/onResourceStart/',
],
[
'name' => 'onResourceStop',
'type' => 'core',
'side' => 'both',
'description' => 'Triggered when a resource stops',
'side' => 'client',
'description' => 'Triggered when a resource stops (client-side)',
'parameters' => [
['name' => 'resourceName', 'type' => 'string', 'description' => 'The name of the resource that stopped'],
],
'lua_example' => "AddEventHandler('onResourceStop', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource stopping!')\n end\nend)",
'js_example' => "on('onResourceStop', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource stopping!');\n }\n});",
'lua_example' => "AddEventHandler('onResourceStop', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource stopped on client!')\n end\nend)",
'js_example' => "on('onResourceStop', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource stopped on client!');\n }\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/onResourceStop/',
],
[
'name' => 'playerConnecting',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a player is connecting to the server',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Player name'],
['name' => 'setKickReason', 'type' => 'function', 'description' => 'Function to deny connection'],
['name' => 'deferrals', 'type' => 'object', 'description' => 'Deferrals object for async connection'],
],
'lua_example' => "AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)\n local source = source\n deferrals.defer()\n -- Check player\n deferrals.done()\nend)",
'js_example' => "on('playerConnecting', (name, setKickReason, deferrals) => {\n const source = global.source;\n deferrals.defer();\n // Check player\n deferrals.done();\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/playerConnecting/',
],
[
'name' => 'playerDropped',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a player disconnects from the server',
'parameters' => [
['name' => 'reason', 'type' => 'string', 'description' => 'Disconnect reason'],
],
'lua_example' => "AddEventHandler('playerDropped', function(reason)\n local source = source\n print('Player ' .. GetPlayerName(source) .. ' dropped: ' .. reason)\nend)",
'js_example' => "on('playerDropped', (reason) => {\n const source = global.source;\n console.log(`Player \${GetPlayerName(source)} dropped: \${reason}`);\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/playerDropped/',
],
// ESX Events
// ESX Client Events
[
'name' => 'esx:playerLoaded',
'type' => 'esx',
@@ -157,7 +131,7 @@ class GetEventReference extends Tool
'lua_example' => "RegisterNetEvent('esx:setJob', function(job)\n ESX.PlayerData.job = job\n print('Job changed to: ' .. job.name)\nend)",
'js_example' => "RegisterNetEvent('esx:setJob', (job) => {\n ESX.PlayerData.job = job;\n console.log(`Job changed to: \${job.name}`);\n});",
],
// QBCore Events
// QBCore Client Events
[
'name' => 'QBCore:Client:OnPlayerLoaded',
'type' => 'qbcore',
@@ -191,7 +165,7 @@ class GetEventReference extends Tool
return [
'event_name' => $schema
->string()
->description('Specific event name to look up (optional, leave empty to list all events)'),
->description('Specific client event name to look up (optional, leave empty to list all events)'),
'event_type' => $schema
->string()
->enum(['all', 'core', 'esx', 'qbcore'])
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\FiveM\Client;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -8,8 +8,8 @@ use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Look up GTA5/FiveM native functions by name. Returns function signature, parameters, return type, and usage examples.')]
class GetNativeFunction extends Tool
#[Description('Look up GTA5/FiveM client-side native functions by name. Returns function signature, parameters, return type, and usage examples.')]
class GetNativeClientFunction extends Tool
{
/**
* Handle the tool request.
@@ -22,7 +22,7 @@ class GetNativeFunction extends Tool
$native = $this->findNative($functionName);
if (! $native) {
return Response::text(sprintf("Native function '%s' not found. Try searching at https://docs.fivem.net/natives/", $functionName));
return Response::text(sprintf("Client native function '%s' not found. Try searching at https://docs.fivem.net/natives/", $functionName));
}
$output = $this->formatNativeInfo($native, $language);
@@ -31,16 +31,17 @@ class GetNativeFunction extends Tool
}
/**
* Find a native function.
* Find a client native function.
*/
protected function findNative(string $name): ?array
{
// Common FiveM/GTA5 natives database
// Client-side only FiveM natives
$natives = [
// Player/Ped natives
'GetPlayerPed' => [
'namespace' => 'PLAYER',
'description' => 'Gets the local or specified player\'s ped handle',
'side' => 'client',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player ID, or -1 for local player'],
],
@@ -48,9 +49,19 @@ class GetNativeFunction extends Tool
'lua_example' => 'local playerPed = GetPlayerPed(-1)',
'js_example' => 'const playerPed = GetPlayerPed(-1);',
],
'PlayerPedId' => [
'namespace' => 'PLAYER',
'description' => 'Returns the ped ID of the local player',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'int', 'description' => 'The local player ped ID'],
'lua_example' => 'local playerPed = PlayerPedId()',
'js_example' => 'const playerPed = PlayerPedId();',
],
'GetPlayerName' => [
'namespace' => 'PLAYER',
'description' => 'Returns the name of a player',
'description' => 'Returns the name of a player (client-side lookup)',
'side' => 'client',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player ID'],
],
@@ -58,9 +69,19 @@ class GetNativeFunction extends Tool
'lua_example' => 'local name = GetPlayerName(PlayerId())',
'js_example' => 'const name = GetPlayerName(PlayerId());',
],
'PlayerId' => [
'namespace' => 'PLAYER',
'description' => 'Gets the local player ID on client',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'int', 'description' => 'The local player ID'],
'lua_example' => 'local playerId = PlayerId()',
'js_example' => 'const playerId = PlayerId();',
],
'SetEntityCoords' => [
'namespace' => 'ENTITY',
'description' => 'Sets the coordinates of an entity',
'description' => 'Sets the coordinates of an entity (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'entity', 'type' => 'int', 'description' => 'The entity handle'],
['name' => 'x', 'type' => 'float', 'description' => 'X coordinate'],
@@ -78,6 +99,7 @@ class GetNativeFunction extends Tool
'GetEntityCoords' => [
'namespace' => 'ENTITY',
'description' => 'Gets the coordinates of an entity',
'side' => 'client',
'parameters' => [
['name' => 'entity', 'type' => 'int', 'description' => 'The entity handle'],
['name' => 'alive', 'type' => 'boolean', 'description' => 'Whether to get alive coordinates only'],
@@ -86,19 +108,21 @@ class GetNativeFunction extends Tool
'lua_example' => 'local coords = GetEntityCoords(PlayerPedId(), true)',
'js_example' => 'const coords = GetEntityCoords(PlayerPedId(), true);',
],
'RegisterNetEvent' => [
'namespace' => 'CFX',
'description' => 'Registers a network event handler (FiveM-specific)',
'DeleteEntity' => [
'namespace' => 'ENTITY',
'description' => 'Deletes an entity (network delete for networked entities)',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'entity', 'type' => 'int', 'description' => 'The entity handle to delete'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "RegisterNetEvent('myResource:eventName', function(param1, param2)\n -- Handle event\nend)",
'js_example' => "RegisterNetEvent('myResource:eventName', (param1, param2) => {\n // Handle event\n});",
'lua_example' => 'DeleteEntity(ped)',
'js_example' => 'DeleteEntity(ped);',
],
'TriggerServerEvent' => [
'namespace' => 'CFX',
'description' => 'Triggers an event on the server from client',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
@@ -107,21 +131,21 @@ class GetNativeFunction extends Tool
'lua_example' => "TriggerServerEvent('myResource:serverEvent', arg1, arg2)",
'js_example' => "TriggerServerEvent('myResource:serverEvent', arg1, arg2);",
],
'TriggerClientEvent' => [
'RegisterNetEvent' => [
'namespace' => 'CFX',
'description' => 'Triggers an event on a client from server',
'description' => 'Registers a network event handler (client-side)',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'playerId', 'type' => 'int|string', 'description' => 'The player server ID or -1 for all'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerClientEvent('myResource:clientEvent', playerId, arg1, arg2)",
'js_example' => "TriggerClientEvent('myResource:clientEvent', playerId, arg1, arg2);",
'lua_example' => "RegisterNetEvent('myResource:clientEvent')\nAddEventHandler('myResource:clientEvent', function(param1, param2)\n -- Handle event\nend)",
'js_example' => "RegisterNetEvent('myResource:clientEvent');\nAddEventHandler('myResource:clientEvent', (param1, param2) => {\n // Handle event\n});",
],
'AddEventHandler' => [
'namespace' => 'CFX',
'description' => 'Adds an event handler for local events',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'callback', 'type' => 'function', 'description' => 'The handler function'],
@@ -130,25 +154,26 @@ class GetNativeFunction extends Tool
'lua_example' => "AddEventHandler('onResourceStart', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource started!')\n end\nend)",
'js_example' => "AddEventHandler('onResourceStart', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource started!');\n }\n});",
],
'ESX.GetPlayerFromId' => [
'namespace' => 'ESX',
'description' => 'Get ESX player object from server ID (server-side)',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'table', 'description' => 'ESX player object'],
'lua_example' => 'local xPlayer = ESX.GetPlayerFromId(source)',
'js_example' => 'const xPlayer = ESX.GetPlayerFromId(source);',
'GetCurrentResourceName' => [
'namespace' => 'CFX',
'description' => 'Returns the name of the currently executing resource',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'string', 'description' => 'The resource name'],
'lua_example' => "local resName = GetCurrentResourceName()\nprint('Current resource: ' .. resName)",
'js_example' => "const resName = GetCurrentResourceName();\nconsole.log('Current resource: ' + resName);",
],
'QBCore.Functions.GetPlayer' => [
'namespace' => 'QBCore',
'description' => 'Get QBCore player object from server ID (server-side)',
'TriggerEvent' => [
'namespace' => 'CFX',
'description' => 'Triggers a local event on the current client',
'side' => 'client',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object'],
'lua_example' => 'local Player = QBCore.Functions.GetPlayer(source)',
'js_example' => 'const Player = QBCore.Functions.GetPlayer(source);',
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerEvent('myEvent', 'arg1', 'arg2')",
'js_example' => "TriggerEvent('myEvent', 'arg1', 'arg2');",
],
];
@@ -168,7 +193,7 @@ class GetNativeFunction extends Tool
*/
protected function formatNativeInfo(array $native, string $language): string
{
return view('mcp.native-function', [
return view('mcp.fivem.native-function', [
'native' => $native,
'language' => $language,
])->render();
@@ -184,7 +209,7 @@ class GetNativeFunction extends Tool
return [
'function_name' => $schema
->string()
->description('The name of the native function to look up (e.g., "GetPlayerPed", "SetEntityCoords")')
->description('The name of the client-side native function to look up (e.g., "PlayerPedId", "SetEntityCoords", "TriggerServerEvent")')
->required(),
'language' => $schema
->string()
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\FiveM;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\FiveM;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -29,7 +29,7 @@ class GenerateResourceBoilerplate extends Tool
]);
return Response::text(
view('mcp.resource-boilerplate', [
view('mcp.shared.resource-boilerplate', [
'resourceName' => $resourceName,
'files' => $files,
])->render()
@@ -1,6 +1,6 @@
<?php
namespace App\Mcp\Tools;
namespace App\Mcp\Tools\FiveM;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@@ -26,7 +26,7 @@ class SearchFiveMDocs extends Tool
}
return Response::text(
view('mcp.search-results', [
view('mcp.shared.search-results', [
'query' => $query,
'results' => $results,
])->render()
@@ -0,0 +1,187 @@
<?php
namespace App\Mcp\Tools\FiveM\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('Get information about FiveM server-side events including framework events (ESX, QBCore).')]
class GetEventServerReference extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$eventName = $request->get('event_name');
$eventType = $request->get('event_type', 'all');
$language = $request->get('language', 'lua');
if ($eventName) {
return $this->getEventInfo($eventName, $language);
}
return $this->listEvents($eventType, $language);
}
/**
* Get specific event information.
*/
protected function getEventInfo(string $eventName, string $language): Response
{
$events = $this->getEventsDatabase();
foreach ($events as $event) {
if (strtolower($event['name']) === strtolower($eventName)) {
return Response::text($this->formatEventInfo($event, $language));
}
}
return Response::text(sprintf("Server event '%s' not found in the database.", $eventName));
}
/**
* List events by type.
*/
protected function listEvents(string $type, string $language): Response
{
$events = $this->getEventsDatabase();
if ($type !== 'all') {
$events = array_filter($events, fn ($e) => $e['type'] === $type);
}
$eventsByType = [];
foreach ($events as $event) {
$eventsByType[$event['type']][] = $event;
}
return Response::text(
view('mcp.fivem.event-list', [
'eventsByType' => $eventsByType,
])->render()
);
}
/**
* Format event information.
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.fivem.event-reference', [
'event' => $event,
'language' => $language,
])->render();
}
/**
* Get server events database.
*/
protected function getEventsDatabase(): array
{
return [
// Core FiveM Server Events
[
'name' => 'onResourceStart',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a resource starts (server-side)',
'parameters' => [
['name' => 'resourceName', 'type' => 'string', 'description' => 'The name of the resource that started'],
],
'lua_example' => "AddEventHandler('onResourceStart', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource started on server!')\n end\nend)",
'js_example' => "on('onResourceStart', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource started on server!');\n }\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/onResourceStart/',
],
[
'name' => 'onResourceStop',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a resource stops (server-side)',
'parameters' => [
['name' => 'resourceName', 'type' => 'string', 'description' => 'The name of the resource that stopped'],
],
'lua_example' => "AddEventHandler('onResourceStop', function(resourceName)\n if resourceName == GetCurrentResourceName() then\n print('Resource stopped on server!')\n end\nend)",
'js_example' => "on('onResourceStop', (resourceName) => {\n if (resourceName === GetCurrentResourceName()) {\n console.log('Resource stopped on server!');\n }\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/onResourceStop/',
],
[
'name' => 'playerConnecting',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a player is connecting to the server',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Player name'],
['name' => 'setKickReason', 'type' => 'function', 'description' => 'Function to deny connection'],
['name' => 'deferrals', 'type' => 'object', 'description' => 'Deferrals object for async connection'],
],
'lua_example' => "AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)\n local source = source\n deferrals.defer()\n -- Check player\n deferrals.done()\nend)",
'js_example' => "on('playerConnecting', (name, setKickReason, deferrals) => {\n const source = global.source;\n deferrals.defer();\n // Check player\n deferrals.done();\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/playerConnecting/',
],
[
'name' => 'playerDropped',
'type' => 'core',
'side' => 'server',
'description' => 'Triggered when a player disconnects from the server',
'parameters' => [
['name' => 'reason', 'type' => 'string', 'description' => 'Disconnect reason'],
],
'lua_example' => "AddEventHandler('playerDropped', function(reason)\n local source = source\n print('Player ' .. GetPlayerName(source) .. ' dropped: ' .. reason)\nend)",
'js_example' => "on('playerDropped', (reason) => {\n const source = global.source;\n console.log(`Player \${GetPlayerName(source)} dropped: \${reason}`);\n});",
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/playerDropped/',
],
// ESX Server Events
[
'name' => 'esx:playerLoaded',
'type' => 'esx',
'side' => 'server',
'description' => 'Triggered on server when ESX player loads',
'parameters' => [
['name' => 'xPlayer', 'type' => 'table', 'description' => 'ESX player object'],
],
'lua_example' => "AddEventHandler('esx:playerLoaded', function(source, xPlayer)\n -- Server-side player loaded\nend)",
'js_example' => "on('esx:playerLoaded', (source, xPlayer) => {\n // Server-side player loaded\n});",
],
// QBCore Server Events
[
'name' => 'QBCore:Server:PlayerLoaded',
'type' => 'qbcore',
'side' => 'server',
'description' => 'Triggered on server when QBCore player loads',
'parameters' => [
['name' => 'Player', 'type' => 'table', 'description' => 'QBCore player object'],
],
'lua_example' => "AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)\n -- Server-side player loaded\nend)",
'js_example' => "on('QBCore:Server:PlayerLoaded', (Player) => {\n // Server-side player loaded\n});",
],
];
}
/**
* 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('Specific server event name to look up (optional, leave empty to list all events)'),
'event_type' => $schema
->string()
->enum(['all', 'core', 'esx', 'qbcore'])
->description('Filter events by type')
->default('all'),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,210 @@
<?php
namespace App\Mcp\Tools\FiveM\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 GTA5/FiveM server-side native functions by name. Returns function signature, parameters, return type, and usage examples.')]
class GetNativeServerFunction extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$functionName = $request->get('function_name');
$language = $request->get('language', 'lua');
$native = $this->findNative($functionName);
if (! $native) {
return Response::text(sprintf("Server native function '%s' not found. Try searching at https://docs.fivem.net/natives/", $functionName));
}
$output = $this->formatNativeInfo($native, $language);
return Response::text($output);
}
/**
* Find a server native function.
*/
protected function findNative(string $name): ?array
{
// Server-side only FiveM natives
$natives = [
'TriggerClientEvent' => [
'namespace' => 'CFX',
'description' => 'Triggers an event on a client from server',
'side' => 'server',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'playerId', 'type' => 'int|string', 'description' => 'The player server ID or -1 for all'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerClientEvent('myResource:clientEvent', playerId, arg1, arg2)",
'js_example' => "TriggerClientEvent('myResource:clientEvent', playerId, arg1, arg2);",
],
'TriggerClientEventReliable' => [
'namespace' => 'CFX',
'description' => 'Triggers an event on a client with guaranteed delivery',
'side' => 'server',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'playerId', 'type' => 'int|string', 'description' => 'The player server ID'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerClientEventReliable('myResource:clientEvent', playerId, arg1, arg2)",
'js_example' => "TriggerClientEventReliable('myResource:clientEvent', playerId, arg1, arg2);",
],
'GetPlayerName' => [
'namespace' => 'PLAYER',
'description' => 'Returns the name of a player (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'string', 'description' => 'The player name'],
'lua_example' => 'local name = GetPlayerName(source)',
'js_example' => 'const name = GetPlayerName(global.source);',
],
'GetPlayerIdentifier' => [
'namespace' => 'PLAYER',
'description' => 'Gets a specific identifier for a player',
'side' => 'server',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'type', 'type' => 'int', 'description' => 'Identifier type (0 = license, 1 = steam, etc.)'],
],
'returns' => ['type' => 'string', 'description' => 'The player identifier'],
'lua_example' => "local license = GetPlayerIdentifier(source, 0)\nlocal steam = GetPlayerIdentifier(source, 1)",
'js_example' => "const license = GetPlayerIdentifier(global.source, 0);\nconst steam = GetPlayerIdentifier(global.source, 1);",
],
'GetPlayerPing' => [
'namespace' => 'PLAYER',
'description' => 'Gets the ping of a player in milliseconds',
'side' => 'server',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'int', 'description' => 'Player ping in ms'],
'lua_example' => 'local ping = GetPlayerPing(source)',
'js_example' => 'const ping = GetPlayerPing(global.source);',
],
'DropPlayer' => [
'namespace' => 'PLAYER',
'description' => 'Disconnects a player from the server',
'side' => 'server',
'parameters' => [
['name' => 'playerId', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'reason', 'type' => 'string', 'description' => 'Reason for dropping the player'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "DropPlayer(source, 'You were kicked by admin')",
'js_example' => "DropPlayer(global.source, 'You were kicked by admin');",
],
'GetPlayers' => [
'namespace' => 'PLAYER',
'description' => 'Gets a list of all player server IDs on the server',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Array of player IDs'],
'lua_example' => "local players = GetPlayers()\nfor _, playerId in ipairs(players) do\n print('Player: ' .. playerId)\nend",
'js_example' => "const players = GetPlayers();\nfor (const playerId of players) {\n console.log('Player: ' + playerId);\n}",
],
'AddEventHandler' => [
'namespace' => 'CFX',
'description' => 'Adds a server-side event handler',
'side' => 'server',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => 'callback', 'type' => 'function', 'description' => 'The handler function'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)\n -- Handle player connecting\nend)",
'js_example' => "AddEventHandler('playerConnecting', (name, setKickReason, deferrals) => {\n // Handle player connecting\n});",
],
'TriggerEvent' => [
'namespace' => 'CFX',
'description' => 'Triggers a server-side event',
'side' => 'server',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
['name' => '...args', 'type' => 'any', 'description' => 'Event arguments'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerEvent('myEvent', arg1, arg2)",
'js_example' => "TriggerEvent('myEvent', arg1, arg2);",
],
'ESX.GetPlayerFromId' => [
'namespace' => 'ESX',
'description' => 'Get ESX player object from server ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'table', 'description' => 'ESX player object'],
'lua_example' => 'local xPlayer = ESX.GetPlayerFromId(source)',
'js_example' => 'const xPlayer = ESX.GetPlayerFromId(global.source);',
],
'QBCore.Functions.GetPlayer' => [
'namespace' => 'QBCore',
'description' => 'Get QBCore player object from server ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object'],
'lua_example' => 'local Player = QBCore.Functions.GetPlayer(source)',
'js_example' => 'const Player = QBCore.Functions.GetPlayer(global.source);',
],
];
$nameLower = strtolower($name);
foreach ($natives as $nativeName => $nativeData) {
if (strtolower($nativeName) === $nameLower) {
return array_merge(['name' => $nativeName], $nativeData);
}
}
return null;
}
/**
* Format native function info.
*/
protected function formatNativeInfo(array $native, string $language): string
{
return view('mcp.fivem.native-function', [
'native' => $native,
'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 server-side native function to look up (e.g., "TriggerClientEvent", "GetPlayers", "DropPlayer")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
-230
View File
@@ -1,230 +0,0 @@
<?php
namespace App\Mcp\Tools;
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 COX MySQL functions and methods by name. Returns function signature, parameters, return type, and usage examples.')]
class GetCOXFunction 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("COX function '%s' not found. Check COX documentation at https://coxdocs.dev/", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find a COX function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
// Query Methods
'MySQL.query' => [
'namespace' => 'Query',
'description' => 'Execute a MySQL query with optional parameters (prepared statement)',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query with ? placeholders for prepared statements'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Array of parameters to bind to placeholders'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if query succeeded, false otherwise'],
'lua_example' => "MySQL.query('SELECT * FROM users WHERE id = ?', { 1 }, function(result)\n if result then\n for i = 1, #result do\n print(result[i].name)\n end\n end\nend)",
'js_example' => "MySQL.query('SELECT * FROM users WHERE id = ?', [1], (result) => {\n if (result) {\n result.forEach(row => console.log(row.name));\n }\n});",
],
'MySQL.insert' => [
'namespace' => 'Query',
'description' => 'Insert a row into the database and get the insert ID',
'side' => 'server',
'parameters' => [
['name' => 'tableName', 'type' => 'string', 'description' => 'Name of the table'],
['name' => 'data', 'type' => 'table', 'description' => 'Table with column names as keys and values'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback function'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "MySQL.insert('users', {\n username = 'player',\n money = 500\n}, function(id)\n print('Inserted with ID: ' .. id)\nend)",
'js_example' => "MySQL.insert('users', {\n username: 'player',\n money: 500\n}, (id) => {\n console.log('Inserted with ID: ' + id);\n});",
],
'MySQL.update' => [
'namespace' => 'Query',
'description' => 'Update rows in the database where a condition matches',
'side' => 'server',
'parameters' => [
['name' => 'tableName', 'type' => 'string', 'description' => 'Name of the table'],
['name' => 'data', 'type' => 'table', 'description' => 'Column names and new values'],
['name' => 'conditions', 'type' => 'string|table', 'description' => 'WHERE clause or conditions table'],
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback function'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "MySQL.update('users', {\n money = 1000\n}, { id = 5 }, function(affectedRows)\n print('Updated ' .. affectedRows .. ' rows')\nend)",
'js_example' => "MySQL.update('users', {\n money: 1000\n}, { id: 5 }, (affectedRows) => {\n console.log(`Updated \${affectedRows} rows`);\n});",
],
'MySQL.scalar' => [
'namespace' => 'Query',
'description' => 'Get a single scalar value from the database (single result)',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Query parameters'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback with the scalar value'],
],
'returns' => ['type' => 'void', 'description' => 'Callback receives the value'],
'lua_example' => "MySQL.scalar('SELECT COUNT(*) as count FROM users', {}, function(count)\n print('Total users: ' .. (count or 0))\nend)",
'js_example' => "MySQL.scalar('SELECT COUNT(*) as count FROM users', [], (count) => {\n console.log('Total users: ' + (count || 0));\n});",
],
'MySQL.single' => [
'namespace' => 'Query',
'description' => 'Get a single row from the database',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Query parameters'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback with the row data'],
],
'returns' => ['type' => 'void', 'description' => 'Callback receives a single row or nil'],
'lua_example' => "MySQL.single('SELECT * FROM users WHERE id = ?', { 1 }, function(user)\n if user then\n print('User: ' .. user.username)\n end\nend)",
'js_example' => "MySQL.single('SELECT * FROM users WHERE id = ?', [1], (user) => {\n if (user) {\n console.log('User: ' + user.username);\n }\n});",
],
// Transaction Methods
'MySQL.transaction.begin' => [
'namespace' => 'Transaction',
'description' => 'Begin a new database transaction',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "MySQL.transaction.begin()\nMySQL.update('users', { money = money - 500 }, { id = 1 })\nMySQL.update('users', { money = money + 500 }, { id = 2 })\nMySQL.transaction.commit()",
'js_example' => "MySQL.transaction.begin();\nMySQL.update('users', { money: 'money - 500' }, { id: 1 });\nMySQL.update('users', { money: 'money + 500' }, { id: 2 });\nMySQL.transaction.commit();",
],
'MySQL.transaction.commit' => [
'namespace' => 'Transaction',
'description' => 'Commit the current transaction',
'side' => 'server',
'parameters' => [
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback after commit'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "MySQL.transaction.commit(function(success)\n if success then\n print('Transaction committed successfully')\n else\n print('Transaction commit failed')\n end\nend)",
'js_example' => "MySQL.transaction.commit((success) => {\n if (success) {\n console.log('Transaction committed successfully');\n } else {\n console.log('Transaction commit failed');\n }\n});",
],
'MySQL.transaction.rollback' => [
'namespace' => 'Transaction',
'description' => 'Rollback the current transaction',
'side' => 'server',
'parameters' => [
['name' => 'callback', 'type' => 'function|nil', 'description' => 'Optional callback after rollback'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "if error then\n MySQL.transaction.rollback(function(success)\n print('Transaction rolled back')\n end)\nend",
'js_example' => "if (error) {\n MySQL.transaction.rollback((success) => {\n console.log('Transaction rolled back');\n });\n}",
],
// Connection Methods
'MySQL.ready' => [
'namespace' => 'Connection',
'description' => 'Check if the database connection is ready',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'boolean', 'description' => 'True if connection is ready'],
'lua_example' => "if MySQL.ready then\n MySQL.query('SELECT 1', function(result)\n print('Database is connected!')\n end)\nend",
'js_example' => "if (MySQL.ready) {\n MySQL.query('SELECT 1', (result) => {\n console.log('Database is connected!');\n });\n}",
],
'MySQL.getStatus' => [
'namespace' => 'Connection',
'description' => 'Get the current MySQL connection status',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'string', 'description' => 'Status string (connected, connecting, disconnected, etc.)'],
'lua_example' => "local status = MySQL.getStatus()\nprint('Connection status: ' .. status)",
'js_example' => "const status = MySQL.getStatus();\nconsole.log('Connection status: ' + status);",
],
// Prepared Statements
'MySQL.prepare' => [
'namespace' => 'Prepared',
'description' => 'Prepare a query for execution with bound parameters (prevents SQL injection)',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL with ? placeholders'],
['name' => 'params', 'type' => 'table', 'description' => 'Parameters to bind'],
['name' => 'callback', 'type' => 'function', 'description' => 'Callback with results'],
],
'returns' => ['type' => 'void', 'description' => 'Callback receives prepared statement result'],
'lua_example' => "MySQL.prepare('INSERT INTO logs (user_id, action) VALUES (?, ?)', { 5, 'joined' }, function(success)\n if success then\n print('Log inserted')\n end\nend)",
'js_example' => "MySQL.prepare('INSERT INTO logs (user_id, action) VALUES (?, ?)', [5, 'joined'], (success) => {\n if (success) {\n console.log('Log inserted');\n }\n});",
],
// Async Methods
'MySQL.async.query' => [
'namespace' => 'Async',
'description' => 'Execute an async query (returns promise-like structure)',
'side' => 'server',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'SQL query'],
['name' => 'params', 'type' => 'table|nil', 'description' => 'Query parameters'],
],
'returns' => ['type' => 'table', 'description' => 'Awaitable result table'],
'lua_example' => "local result = MySQL.async.query('SELECT * FROM users WHERE banned = ?', { 0 })\nprint('Query completed')",
'js_example' => "const result = MySQL.async.query('SELECT * FROM users WHERE banned = ?', [0]);\nconsole.log('Query completed');",
],
];
$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-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 COX function to look up (e.g., "MySQL.query", "MySQL.insert", "MySQL.transaction.begin")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
-277
View File
@@ -1,277 +0,0 @@
<?php
namespace App\Mcp\Tools;
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 information about QBCore framework events including player, job, inventory, and vehicle events.')]
class GetQBCoreEventReference extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$eventName = $request->get('event_name');
$eventCategory = $request->get('event_category', 'all');
$language = $request->get('language', 'lua');
if ($eventName) {
return $this->getEventInfo($eventName, $language);
}
return $this->listEvents($eventCategory, $language);
}
/**
* Get specific event information.
*/
protected function getEventInfo(string $eventName, string $language): Response
{
$events = $this->getQBCoreEventsDatabase();
foreach ($events as $event) {
if (strtolower($event['name']) === strtolower($eventName)) {
return Response::text($this->formatEventInfo($event, $language));
}
}
return Response::text(sprintf("QBCore event '%s' not found in the database.", $eventName));
}
/**
* List events by category.
*/
protected function listEvents(string $category, string $language): Response
{
$events = $this->getQBCoreEventsDatabase();
if ($category !== 'all') {
$events = array_filter($events, fn ($e) => $e['category'] === $category);
}
$eventsByCategory = [];
foreach ($events as $event) {
$eventsByCategory[$event['category']][] = $event;
}
return Response::text(
view('mcp.qbcore-event-list', [
'eventsByCategory' => $eventsByCategory,
])->render()
);
}
/**
* Format event information.
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.qbcore-event-reference', [
'event' => $event,
'language' => $language,
])->render();
}
/**
* Get QBCore events database.
*/
protected function getQBCoreEventsDatabase(): array
{
return [
// Player Events
[
'name' => 'QBCore:Client:OnPlayerLoaded',
'category' => 'player',
'side' => 'client',
'description' => 'Triggered when a player\'s data is loaded on the client.',
'parameters' => [],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()\n local PlayerData = QBCore.Functions.GetPlayerData()\n print('Player loaded: ' .. PlayerData.charinfo.firstname)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoaded', () => {\n const PlayerData = QBCore.Functions.GetPlayerData();\n console.log('Player loaded: ' + PlayerData.charinfo.firstname);\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/player',
],
[
'name' => 'QBCore:Client:OnPlayerUnload',
'category' => 'player',
'side' => 'client',
'description' => 'Triggered when a player\'s data is unloaded (before character switch or logout).',
'parameters' => [],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()\n print('Player unloaded')\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnPlayerUnload', () => {\n console.log('Player unloaded');\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/player',
],
[
'name' => 'QBCore:Server:OnPlayerLoaded',
'category' => 'player',
'side' => 'server',
'description' => 'Triggered on the server when a player\'s data is successfully loaded.',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'lua_example' => "RegisterNetEvent('QBCore:Server:OnPlayerLoaded', function()\n local Player = QBCore.Functions.GetPlayer(source)\n if Player then\n print('Server: Player loaded - ' .. Player.PlayerData.charinfo.firstname)\n end\nend)",
'js_example' => "RegisterNetEvent('QBCore:Server:OnPlayerLoaded', () => {\n const Player = QBCore.Functions.GetPlayer(global.source);\n if (Player) {\n console.log('Server: Player loaded - ' + Player.PlayerData.charinfo.firstname);\n }\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/player',
],
// Job Events
[
'name' => 'QBCore:Client:OnJobUpdate',
'category' => 'job',
'side' => 'client',
'description' => 'Triggered when a player\'s job is updated.',
'parameters' => [
['name' => 'job', 'type' => 'table', 'description' => 'The job data table'],
],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate', function(job)\n PlayerData.job = job\n print('Job changed to: ' .. job.name .. ' - Grade: ' .. job.grade.name)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate', (job) => {\n PlayerData.job = job;\n console.log(`Job changed to: \${job.name} - Grade: \${job.grade.name}`);\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/jobs',
],
[
'name' => 'QBCore:Server:SetJob',
'category' => 'job',
'side' => 'server',
'description' => 'Set a player\'s job on the server (requires permission).',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'job', 'type' => 'string', 'description' => 'The job name'],
['name' => 'grade', 'type' => 'int', 'description' => 'The job grade level'],
],
'lua_example' => "TriggerServerEvent('QBCore:Server:SetJob', source, 'police', 2)",
'js_example' => "TriggerServerEvent('QBCore:Server:SetJob', global.source, 'police', 2);",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/jobs',
],
// Inventory Events
[
'name' => 'QBCore:Client:OnItemAdd',
'category' => 'inventory',
'side' => 'client',
'description' => 'Triggered when an item is added to the player inventory.',
'parameters' => [
['name' => 'itemName', 'type' => 'string', 'description' => 'The item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'The amount added'],
],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnItemAdd', function(itemName, amount)\n print('Added ' .. amount .. 'x ' .. itemName)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnItemAdd', (itemName, amount) => {\n console.log(`Added \${amount}x \${itemName}`);\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/items',
],
[
'name' => 'QBCore:Client:OnItemRemove',
'category' => 'inventory',
'side' => 'client',
'description' => 'Triggered when an item is removed from the player inventory.',
'parameters' => [
['name' => 'itemName', 'type' => 'string', 'description' => 'The item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'The amount removed'],
],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnItemRemove', function(itemName, amount)\n print('Removed ' .. amount .. 'x ' .. itemName)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnItemRemove', (itemName, amount) => {\n console.log(`Removed \${amount}x \${itemName}`);\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/items',
],
[
'name' => 'QBCore:Server:AddItem',
'category' => 'inventory',
'side' => 'server',
'description' => 'Add an item to a player\'s inventory.',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'item', 'type' => 'string', 'description' => 'The item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'The amount to add'],
['name' => 'slot', 'type' => 'int', 'description' => 'Optional inventory slot'],
['name' => 'info', 'type' => 'table', 'description' => 'Optional item info'],
],
'lua_example' => "exports['qb-core']:GetPlayer(source):AddItem('water', 1)",
'js_example' => "exports['qb-core']:GetPlayer(global.source).AddItem('water', 1);",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/items',
],
// Vehicle Events
[
'name' => 'QBCore:Server:AddVehicle',
'category' => 'vehicle',
'side' => 'server',
'description' => 'Add a vehicle to a player\'s garage.',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'modelName', 'type' => 'string', 'description' => 'The vehicle model name'],
['name' => 'plate', 'type' => 'string', 'description' => 'The vehicle license plate'],
],
'lua_example' => "TriggerServerEvent('QBCore:Server:AddVehicle', 'adder', 'QBTEST123')",
'js_example' => "TriggerServerEvent('QBCore:Server:AddVehicle', 'adder', 'QBTEST123');",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/vehicles',
],
[
'name' => 'QBCore:Server:DeleteVehicle',
'category' => 'vehicle',
'side' => 'server',
'description' => 'Remove a vehicle from a player\'s garage.',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'plate', 'type' => 'string', 'description' => 'The vehicle license plate'],
],
'lua_example' => "TriggerServerEvent('QBCore:Server:DeleteVehicle', 'QBTEST123')",
'js_example' => "TriggerServerEvent('QBCore:Server:DeleteVehicle', 'QBTEST123');",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/vehicles',
],
// Money Events
[
'name' => 'QBCore:Client:OnMoneyChange',
'category' => 'money',
'side' => 'client',
'description' => 'Triggered when a player\'s money amount changes.',
'parameters' => [
['name' => 'type', 'type' => 'string', 'description' => 'Money type (cash, bank, etc.)'],
['name' => 'amount', 'type' => 'int', 'description' => 'The new amount'],
['name' => 'difference', 'type' => 'int', 'description' => 'The amount changed'],
],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnMoneyChange', function(type, amount, difference)\n print('Money changed: ' .. type .. ' = ' .. amount)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnMoneyChange', (type, amount, difference) => {\n console.log(`Money changed: \${type} = \${amount}`);\n});",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/player',
],
[
'name' => 'QBCore:Server:AddMoney',
'category' => 'money',
'side' => 'server',
'description' => 'Add money to a player\'s account.',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
['name' => 'moneyType', 'type' => 'string', 'description' => 'Money type (cash, bank)'],
['name' => 'amount', 'type' => 'int', 'description' => 'The amount to add'],
['name' => 'reason', 'type' => 'string', 'description' => 'Optional reason for log'],
],
'lua_example' => "exports['qb-core']:AddMoney(source, 'cash', 500, 'Job Payment')",
'js_example' => "exports['qb-core']:AddMoney(global.source, 'cash', 500, 'Job Payment');",
'documentation' => 'https://docs.qbcore.org/qbcore-documentation/core-concepts/player',
],
];
}
/**
* 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('Specific QBCore event name to look up (optional, leave empty to list all events)'),
'event_category' => $schema
->string()
->enum(['all', 'player', 'job', 'inventory', 'vehicle', 'money'])
->description('Filter events by category')
->default('all'),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
-270
View File
@@ -1,270 +0,0 @@
<?php
namespace App\Mcp\Tools;
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 functions and exports by name. Returns function signature, parameters, return type, and usage examples.')]
class GetQBCoreFunction 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("QBCore function '%s' not found. Check QBCore documentation at https://docs.qbcore.org/", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find a QBCore function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
// Player Functions
'QBCore.Functions.GetPlayer' => [
'namespace' => 'Core',
'description' => 'Get a QBCore player object from server ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object or nil'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n print('Player: ' .. Player.PlayerData.charinfo.firstname)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n console.log('Player: ' + Player.PlayerData.charinfo.firstname);\n}",
],
'QBCore.Functions.GetPlayerByCitizenId' => [
'namespace' => 'Core',
'description' => 'Get a player object by their citizen ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'citizenId', 'type' => 'string', 'description' => 'The player citizen ID'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object or nil'],
'lua_example' => "local Player = QBCore.Functions.GetPlayerByCitizenId('ABC12345')\nif Player then\n TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, 'Hello!')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayerByCitizenId('ABC12345');\nif (Player) {\n TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, 'Hello!');\n}",
],
'QBCore.Functions.GetPlayerData' => [
'namespace' => 'Core',
'description' => 'Get the current player data (client-side only)',
'side' => 'client',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Current player data table'],
'lua_example' => "local PlayerData = QBCore.Functions.GetPlayerData()\nprint('Job: ' .. PlayerData.job.name)",
'js_example' => "const PlayerData = QBCore.Functions.GetPlayerData();\nconsole.log('Job: ' + PlayerData.job.name);",
],
// Item Functions
'AddItem' => [
'namespace' => 'Player',
'description' => 'Add an item to a player\'s inventory',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to add (default 1)'],
['name' => 'slot', 'type' => 'int|nil', 'description' => 'Optional specific slot'],
['name' => 'info', 'type' => 'table|nil', 'description' => 'Optional item metadata/info'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local success = Player.Functions.AddItem('water', 1)\n if success then\n TriggerClientEvent('QBCore:Notify', source, 'Item added')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const success = Player.Functions.AddItem('water', 1);\n if (success) {\n TriggerClientEvent('QBCore:Notify', global.source, 'Item added');\n }\n}",
],
'RemoveItem' => [
'namespace' => 'Player',
'description' => 'Remove an item from a player\'s inventory',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to remove (default 1)'],
['name' => 'slot', 'type' => 'int|nil', 'description' => 'Optional specific slot'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local success = Player.Functions.RemoveItem('water', 1)\n if success then\n TriggerClientEvent('QBCore:Notify', source, 'Item removed')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const success = Player.Functions.RemoveItem('water', 1);\n if (success) {\n TriggerClientEvent('QBCore:Notify', global.source, 'Item removed');\n }\n}",
],
'GetItemByName' => [
'namespace' => 'Player',
'description' => 'Get item data from a player\'s inventory',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name to search for'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Item data table or nil if not found'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local item = Player.Functions.GetItemByName('water')\n if item then\n print('Found ' .. item.amount .. 'x ' .. item.name)\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const item = Player.Functions.GetItemByName('water');\n if (item) {\n console.log(`Found \${item.amount}x \${item.name}`);\n }\n}",
],
// Money Functions
'AddMoney' => [
'namespace' => 'Player',
'description' => 'Add money to a player\'s account',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to add'],
['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.AddMoney('cash', 500, 'Job Payment')\n TriggerClientEvent('QBCore:Notify', source, 'You earned $500')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.AddMoney('cash', 500, 'Job Payment');\n TriggerClientEvent('QBCore:Notify', global.source, 'You earned $500');\n}",
],
'RemoveMoney' => [
'namespace' => 'Player',
'description' => 'Remove money from a player\'s account',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to remove'],
['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.RemoveMoney('cash', 500, 'Shop Purchase')\n TriggerClientEvent('QBCore:Notify', source, 'Payment successful')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.RemoveMoney('cash', 500, 'Shop Purchase');\n TriggerClientEvent('QBCore:Notify', global.source, 'Payment successful');\n}",
],
'GetMoney' => [
'namespace' => 'Player',
'description' => 'Get a player\'s money amount',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
],
'returns' => ['type' => 'int', 'description' => 'Money amount'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local cashAmount = Player.Functions.GetMoney('cash')\n print('Cash: $' .. cashAmount)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const cashAmount = Player.Functions.GetMoney('cash');\n console.log('Cash: $' + cashAmount);\n}",
],
// Job Functions
'SetJob' => [
'namespace' => 'Player',
'description' => 'Set a player\'s job',
'side' => 'server',
'parameters' => [
['name' => 'jobName', 'type' => 'string', 'description' => 'The job name'],
['name' => 'grade', 'type' => 'int', 'description' => 'The job grade (usually 0 for basic)'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetJob('police', 0)\n TriggerClientEvent('QBCore:Notify', source, 'You are now a police officer', 'success')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetJob('police', 0);\n TriggerClientEvent('QBCore:Notify', global.source, 'You are now a police officer', 'success');\n}",
],
'GetJob' => [
'namespace' => 'Player',
'description' => 'Get a player\'s job information',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Job data table with name, label, grade, etc.'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local job = Player.Functions.GetJob()\n print('Job: ' .. job.name .. ' (' .. job.grade.name .. ')')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const job = Player.Functions.GetJob();\n console.log(`Job: \${job.name} (\${job.grade.name})`);\n}",
],
// Vehicle Functions
'AddVehicle' => [
'namespace' => 'Player',
'description' => 'Add a vehicle to a player\'s garage',
'side' => 'server',
'parameters' => [
['name' => 'modelName', 'type' => 'string', 'description' => 'Vehicle model name (e.g., "adder")'],
['name' => 'plate', 'type' => 'string', 'description' => 'License plate (usually unique identifier)'],
['name' => 'properties', 'type' => 'table|nil', 'description' => 'Optional vehicle properties/customization'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.AddVehicle('adder', 'QBTEST')\n TriggerClientEvent('QBCore:Notify', source, 'Vehicle added to garage')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.AddVehicle('adder', 'QBTEST');\n TriggerClientEvent('QBCore:Notify', global.source, 'Vehicle added to garage');\n}",
],
'DeleteVehicle' => [
'namespace' => 'Player',
'description' => 'Remove a vehicle from a player\'s garage',
'side' => 'server',
'parameters' => [
['name' => 'plate', 'type' => 'string', 'description' => 'License plate of vehicle to remove'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'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}",
],
// Utility Functions
'Notify' => [
'namespace' => 'Utility',
'description' => 'Send a notification to a player',
'side' => 'client',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'Player server ID'],
['name' => 'message', 'type' => 'string', 'description' => 'Notification message'],
['name' => 'type', 'type' => 'string', 'description' => 'Type: "success", "error", "info" (default: info)'],
['name' => 'length', 'type' => 'int|nil', 'description' => 'Duration in milliseconds'],
],
'returns' => ['type' => 'void', 'description' => 'No return value'],
'lua_example' => "TriggerClientEvent('QBCore:Notify', source, 'Hello Player!', 'success', 5000)",
'js_example' => "TriggerClientEvent('QBCore:Notify', global.source, 'Hello Player!', 'success', 5000);",
],
'HasItem' => [
'namespace' => 'Player',
'description' => 'Check if a player has an item',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name to check'],
['name' => 'amount', 'type' => 'int|nil', 'description' => 'Minimum amount required'],
],
'returns' => ['type' => 'boolean', 'description' => 'True if player has item'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n if Player.Functions.GetItemByName('water') then\n print('Has water')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n if (Player.Functions.GetItemByName('water')) {\n console.log('Has water');\n }\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.qbcore-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 QBCore function to look up (e.g., "QBCore.Functions.GetPlayer", "AddMoney")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
@@ -0,0 +1,143 @@
<?php
namespace App\Mcp\Tools\QBCore\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('QBCore event reference for client-side events. Includes built-in core events and framework-specific events for client scripts.')]
class GetQBCoreClientEventReference extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$eventName = $request->get('event_name', '');
$events = $this->getClientEvents();
if ($eventName) {
$event = collect($events)->firstWhere('name', $eventName);
if ($event) {
return Response::text($this->formatEvent($event));
}
return Response::text("Client-side event '$eventName' not found.");
}
return Response::text($this->formatEventList($events, 'QBCore Client Events'));
}
/**
* Get all client-side events.
*/
protected function getClientEvents(): array
{
return [
[
'name' => 'QBCore:Client:OnPlayerLoad',
'side' => 'client',
'description' => 'Fired when the player loads on the client (character selected)',
'parameters' => ['PlayerData' => 'The player data table'],
'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});",
],
[
'name' => 'QBCore:Client:OnPlayerUnload',
'side' => 'client',
'description' => 'Fired when the player unloads (leaving character/logging out)',
'parameters' => [],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnPlayerUnload')\nAddEventHandler('QBCore:Client:OnPlayerUnload', function()\n print('Player unloaded')\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnPlayerUnload');\nAddEventHandler('QBCore:Client:OnPlayerUnload', function() {\n console.log('Player unloaded');\n});",
],
[
'name' => 'QBCore:Client:OnJobUpdate',
'side' => 'client',
'description' => 'Fired when the player\'s job is updated locally',
'parameters' => ['JobInfo' => 'The job table with name, grade, etc'],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate')\nAddEventHandler('QBCore:Client:OnJobUpdate', function(JobInfo)\n print('Job updated: ' .. JobInfo.name)\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate');\nAddEventHandler('QBCore:Client:OnJobUpdate', function(JobInfo) {\n console.log(`Job updated: \${JobInfo.name}`);\n});",
],
[
'name' => 'QBCore:Client:OnMoneyChange',
'side' => 'client',
'description' => 'Fired when the player\'s money amount changes',
'parameters' => ['MoneyType' => 'Type of money changed', 'Amount' => 'New amount', 'ChangedAmount' => 'Amount changed'],
'lua_example' => "AddEventHandler('QBCore:Client:OnMoneyChange', function(MoneyType, Amount, ChangedAmount)\n print('Money changed: ' .. ChangedAmount .. ' (' .. MoneyType .. ')')\nend)",
'js_example' => "AddEventHandler('QBCore:Client:OnMoneyChange', function(MoneyType, Amount, ChangedAmount) {\n console.log(`Money changed: \${ChangedAmount} (\${MoneyType})`);\n});",
],
[
'name' => 'QBCore:Client:OnItemUpdate',
'side' => 'client',
'description' => 'Fired when an item in the player\'s inventory updates',
'parameters' => ['inventory' => 'Updated inventory table'],
'lua_example' => "RegisterNetEvent('QBCore:Client:OnItemUpdate')\nAddEventHandler('QBCore:Client:OnItemUpdate', function(inventory)\n print('Inventory updated')\nend)",
'js_example' => "RegisterNetEvent('QBCore:Client:OnItemUpdate');\nAddEventHandler('QBCore:Client:OnItemUpdate', function(inventory) {\n console.log('Inventory updated');\n});",
],
[
'name' => 'QBCore:Notify',
'side' => 'client',
'description' => 'Send a notification to the player client',
'parameters' => ['message' => 'Notification message', 'type' => 'Type (success, error, info)', 'duration' => 'Duration in milliseconds'],
'lua_example' => "TriggerEvent('QBCore:Notify', 'Hello World!', 'success')\n-- Server trigger:\n-- TriggerClientEvent('QBCore:Notify', source, 'Hello World!', 'success')",
'js_example' => "TriggerEvent('QBCore:Notify', 'Hello World!', 'success');\n// Server trigger:\n// TriggerClientEvent('QBCore:Notify', global.source, 'Hello World!', 'success');",
],
];
}
/**
* Format a single event.
*/
protected function formatEvent(array $event): string
{
return view('mcp.qbcore.qbcore-event-reference', [
'event' => $event,
])->render();
}
/**
* Format event list.
*/
protected function formatEventList(array $events, string $title): string
{
$list = sprintf("# %s\n\n", $title);
foreach ($events as $event) {
$list .= sprintf("- **%s**: %s\n", $event['name'], $event['description']);
}
return $list;
}
/**
* Format parameters list.
*/
protected function formatParameters(array $parameters): string
{
$formatted = '';
foreach ($parameters as $name => $description) {
$formatted .= sprintf("- `%s`: %s\n", $name, $description);
}
return $formatted;
}
/**
* 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('Specific event name to look up, or leave empty to list all client events')
->default(''),
];
}
}
@@ -0,0 +1,254 @@
<?php
namespace App\Mcp\Tools\QBCore\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 QBCore client-side functions by name. Returns function signature, parameters, return type, and usage examples.')]
class GetQBCoreClientFunction 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("QBCore client function '%s' not found. Check QBCore documentation at https://docs.qbcore.org/", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find a QBCore client function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'QBCore.Functions.GetPlayerData' => [
'namespace' => 'Core',
'description' => 'Get the local player data (client-side only). Contains all player information.',
'side' => 'client',
'parameters' => [],
'returns' => [
'type' => 'table',
'description' => 'Player data table with keys: source, charinfo, job, gang, money, items, inventory, licenses, etc.',
],
'lua_example' => "local playerData = QBCore.Functions.GetPlayerData()\nif playerData then\n print('Name: ' .. playerData.charinfo.firstname .. ' ' .. playerData.charinfo.lastname)\n print('Job: ' .. playerData.job.name)\nend",
'js_example' => "const playerData = QBCore.Functions.GetPlayerData();\nif (playerData) {\n console.log(`Name: \${playerData.charinfo.firstname} \${playerData.charinfo.lastname}`);\n console.log(`Job: \${playerData.job.name}`);\n}",
],
'QBCore.Functions.TriggerCallback' => [
'namespace' => 'Core',
'description' => 'Call a server-side callback from the client and wait for response (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'The callback name'],
['name' => 'function', 'type' => 'function', 'description' => 'Callback function to execute with result'],
['name' => '...', 'type' => 'any', 'description' => 'Arguments to pass to server callback'],
],
'returns' => ['type' => 'void', 'description' => 'Result passed to callback function'],
'lua_example' => "QBCore.Functions.TriggerCallback('myResource:getPlayerInfo', function(result)\n if result then\n print('Got result: ' .. result.name)\n end\nend, playerId)",
'js_example' => "QBCore.Functions.TriggerCallback('myResource:getPlayerInfo', function(result) {\n if (result) {\n console.log(`Got result: \${result.name}`);\n }\n}, playerId);",
],
'QBCore.Commands.Add' => [
'namespace' => 'Client',
'description' => 'Register a client-side command (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'name', 'type' => 'string', 'description' => 'Command name (without slash)'],
['name' => 'help', 'type' => 'string', 'description' => 'Help text for command'],
['name' => 'callback', 'type' => 'function', 'description' => 'Function to execute when command runs'],
['name' => 'restricted', 'type' => 'boolean|nil', 'description' => 'Whether command requires admin'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "QBCore.Commands.Add('testcmd', 'Test Command', {}, false, function(args)\n print('Command executed!')\nend)",
'js_example' => "QBCore.Commands.Add('testcmd', 'Test Command', {}, false, function(args) {\n console.log('Command executed!');\n});",
],
'QBCore.UI.DrawText3D' => [
'namespace' => 'UI',
'description' => 'Draw 3D text in the game world (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'x', 'type' => 'number', 'description' => 'World X coordinate'],
['name' => 'y', 'type' => 'number', 'description' => 'World Y coordinate'],
['name' => 'z', 'type' => 'number', 'description' => 'World Z coordinate'],
['name' => 'text', 'type' => 'string', 'description' => 'Text to display'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "QBCore.UI.DrawText3D(100.0, 200.0, 50.0, 'Press [E] to interact')",
'js_example' => "QBCore.UI.DrawText3D(100.0, 200.0, 50.0, 'Press [E] to interact');",
],
'TriggerEvent' => [
'namespace' => 'FiveM Core',
'description' => 'Trigger a local client-side event (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'The event name to trigger'],
['name' => '...', 'type' => 'any', 'description' => 'Arguments to pass to event listeners'],
],
'returns' => ['type' => 'void', 'description' => ''],
'lua_example' => "TriggerEvent('playerLoaded', playerData)\n-- Listen with:\n-- RegisterNetEvent('playerLoaded', function(playerData)\n-- print('Player loaded!')\n-- end)",
'js_example' => "TriggerEvent('playerLoaded', playerData);\n// Listen with:\n// on('playerLoaded', (playerData) => {\n// console.log('Player loaded!');\n// });",
],
'RegisterNetEvent' => [
'namespace' => 'FiveM Core',
'description' => 'Register an event listener from the server (client-side only)',
'side' => 'client',
'parameters' => [
['name' => 'eventName', 'type' => 'string', 'description' => 'Event name to listen for'],
['name' => 'callback', 'type' => 'function', 'description' => 'Function to call when event fires'],
],
'returns' => ['type' => 'void', 'description' => ''],
'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);
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.qbcore.qbcore-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 QBCore client function to look up (e.g., "QBCore.Functions.GetPlayerData", "QBCore.Commands.Add", "TriggerServerEvent", "Citizen.Wait", "PlayerPedId")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
+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,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,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'),
];
}
}
@@ -0,0 +1,143 @@
<?php
namespace App\Mcp\Tools\QBCore\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('QBCore event reference for server-side events. Includes built-in core events and framework-specific events for server scripts.')]
class GetQBCoreServerEventReference extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$eventName = $request->get('event_name', '');
$events = $this->getServerEvents();
if ($eventName) {
$event = collect($events)->firstWhere('name', $eventName);
if ($event) {
return Response::text($this->formatEvent($event));
}
return Response::text("Server-side event '$eventName' not found.");
}
return Response::text($this->formatEventList($events, 'QBCore Server Events'));
}
/**
* Get all server-side events.
*/
protected function getServerEvents(): array
{
return [
[
'name' => 'QBCore:Server:PlayerLoaded',
'side' => 'server',
'description' => 'Fired when a player fully loads on the server',
'parameters' => ['source' => 'Player server ID'],
'lua_example' => "AddEventHandler('QBCore:Server:PlayerLoaded', function(source)\n print('Player ' .. source .. ' loaded')\nend)",
'js_example' => "AddEventHandler('QBCore:Server:PlayerLoaded', function(source) {\n console.log('Player ' + source + ' loaded');\n});",
],
[
'name' => 'QBCore:Server:playerDropped',
'side' => 'server',
'description' => 'Fired when a player disconnects from the server',
'parameters' => ['source' => 'Player server ID', 'reason' => 'Disconnect reason string'],
'lua_example' => "AddEventHandler('QBCore:Server:playerDropped', function(source, reason)\n print('Player ' .. source .. ' dropped: ' .. reason)\nend)",
'js_example' => "AddEventHandler('QBCore:Server:playerDropped', function(source, reason) {\n console.log('Player ' + source + ' dropped: ' + reason);\n});",
],
[
'name' => 'QBCore:Server:PlayerDataChanged',
'side' => 'server',
'description' => 'Fired when a player\'s data changes (job, money, items, etc)',
'parameters' => ['source' => 'Player server ID', 'dataName' => 'Name of changed data'],
'lua_example' => "AddEventHandler('QBCore:Server:PlayerDataChanged', function(source, dataName)\n if dataName == 'money' then\n print('Player money changed')\n end\nend)",
'js_example' => "AddEventHandler('QBCore:Server:PlayerDataChanged', function(source, dataName) {\n if (dataName === 'money') {\n console.log('Player money changed');\n }\n});",
],
[
'name' => 'QBCore:Server:OnJobUpdate',
'side' => 'server',
'description' => 'Fired when a player\'s job is updated',
'parameters' => ['source' => 'Player server ID', 'job' => 'New job table'],
'lua_example' => "AddEventHandler('QBCore:Server:OnJobUpdate', function(source, job)\n print('Player job: ' .. job.name)\nend)",
'js_example' => "AddEventHandler('QBCore:Server:OnJobUpdate', function(source, job) {\n console.log('Player job: ' + job.name);\n});",
],
[
'name' => 'QBCore:Server:SyncPlayerData',
'side' => 'server',
'description' => 'Sync player data across server (triggered internally)',
'parameters' => ['source' => 'Player server ID'],
'lua_example' => "TriggerEvent('QBCore:Server:SyncPlayerData', source)",
'js_example' => "TriggerEvent('QBCore:Server:SyncPlayerData', global.source);",
],
[
'name' => 'QBCore:Server:SavePlayer',
'side' => 'server',
'description' => 'Save a player\'s data to database',
'parameters' => ['source' => 'Player server ID'],
'lua_example' => "TriggerEvent('QBCore:Server:SavePlayer', source)",
'js_example' => "TriggerEvent('QBCore:Server:SavePlayer', global.source);",
],
];
}
/**
* Format a single event.
*/
protected function formatEvent(array $event): string
{
return view('mcp.qbcore.qbcore-event-reference', [
'event' => $event,
])->render();
}
/**
* Format event list.
*/
protected function formatEventList(array $events, string $title): string
{
$list = sprintf("# %s\n\n", $title);
foreach ($events as $event) {
$list .= sprintf("- **%s**: %s\n", $event['name'], $event['description']);
}
return $list;
}
/**
* Format parameters list.
*/
protected function formatParameters(array $parameters): string
{
$formatted = '';
foreach ($parameters as $name => $description) {
$formatted .= sprintf("- `%s`: %s\n", $name, $description);
}
return $formatted;
}
/**
* 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('Specific event name to look up, or leave empty to list all server events')
->default(''),
];
}
}
@@ -0,0 +1,411 @@
<?php
namespace App\Mcp\Tools\QBCore\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 QBCore server-side functions and exports by name. Returns function signature, parameters, return type, and usage examples.')]
class GetQBCoreServerFunction 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("QBCore server function '%s' not found. Check QBCore documentation at https://docs.qbcore.org/", $functionName));
}
$output = $this->formatFunctionInfo($function, $language);
return Response::text($output);
}
/**
* Find a QBCore server function.
*/
protected function findFunction(string $name): ?array
{
$functions = [
'QBCore.Functions.GetPlayer' => [
'namespace' => 'Core',
'description' => 'Get a QBCore player object from server ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'source', 'type' => 'int', 'description' => 'The player server ID'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object or nil'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n print('Player: ' .. Player.PlayerData.charinfo.firstname)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n console.log('Player: ' + Player.PlayerData.charinfo.firstname);\n}",
],
'QBCore.Functions.GetPlayerByCitizenId' => [
'namespace' => 'Core',
'description' => 'Get a player object by their citizen ID (server-side only)',
'side' => 'server',
'parameters' => [
['name' => 'citizenId', 'type' => 'string', 'description' => 'The player citizen ID'],
],
'returns' => ['type' => 'table', 'description' => 'QBCore player object or nil'],
'lua_example' => "local Player = QBCore.Functions.GetPlayerByCitizenId('ABC12345')\nif Player then\n TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, 'Hello!')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayerByCitizenId('ABC12345');\nif (Player) {\n TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, 'Hello!');\n}",
],
'AddItem' => [
'namespace' => 'Player',
'description' => 'Add an item to a player\'s inventory (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to add (default 1)'],
['name' => 'slot', 'type' => 'int|nil', 'description' => 'Optional specific slot'],
['name' => 'info', 'type' => 'table|nil', 'description' => 'Optional item metadata/info'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local success = Player.Functions.AddItem('water', 1)\n if success then\n TriggerClientEvent('QBCore:Notify', source, 'Item added')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const success = Player.Functions.AddItem('water', 1);\n if (success) {\n TriggerClientEvent('QBCore:Notify', global.source, 'Item added');\n }\n}",
],
'RemoveItem' => [
'namespace' => 'Player',
'description' => 'Remove an item from a player\'s inventory (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to remove (default 1)'],
['name' => 'slot', 'type' => 'int|nil', 'description' => 'Optional specific slot'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local success = Player.Functions.RemoveItem('water', 1)\n if success then\n TriggerClientEvent('QBCore:Notify', source, 'Item removed')\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const success = Player.Functions.RemoveItem('water', 1);\n if (success) {\n TriggerClientEvent('QBCore:Notify', global.source, 'Item removed');\n }\n}",
],
'GetItemByName' => [
'namespace' => 'Player',
'description' => 'Get item data from a player\'s inventory (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'item', 'type' => 'string', 'description' => 'Item name to search for'],
],
'returns' => ['type' => 'table|nil', 'description' => 'Item data table or nil if not found'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local item = Player.Functions.GetItemByName('water')\n if item then\n print('Found ' .. item.amount .. 'x ' .. item.name)\n end\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const item = Player.Functions.GetItemByName('water');\n if (item) {\n console.log(`Found \${item.amount}x \${item.name}`);\n }\n}",
],
'AddMoney' => [
'namespace' => 'Player',
'description' => 'Add money to a player\'s account (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to add'],
['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.AddMoney('cash', 500, 'Job Payment')\n TriggerClientEvent('QBCore:Notify', source, 'You earned \$500')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.AddMoney('cash', 500, 'Job Payment');\n TriggerClientEvent('QBCore:Notify', global.source, 'You earned \$500');\n}",
],
'RemoveMoney' => [
'namespace' => 'Player',
'description' => 'Remove money from a player\'s account (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
['name' => 'amount', 'type' => 'int', 'description' => 'Amount to remove'],
['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.RemoveMoney('cash', 500, 'Shop Purchase')\n TriggerClientEvent('QBCore:Notify', source, 'Payment successful')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.RemoveMoney('cash', 500, 'Shop Purchase');\n TriggerClientEvent('QBCore:Notify', global.source, 'Payment successful');\n}",
],
'GetMoney' => [
'namespace' => 'Player',
'description' => 'Get a player\'s money amount (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'moneyType', 'type' => 'string', 'description' => 'Type: "cash", "bank", or custom'],
],
'returns' => ['type' => 'int', 'description' => 'Money amount'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local cashAmount = Player.Functions.GetMoney('cash')\n print('Cash: \$' .. cashAmount)\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const cashAmount = Player.Functions.GetMoney('cash');\n console.log('Cash: \$' + cashAmount);\n}",
],
'SetJob' => [
'namespace' => 'Player',
'description' => 'Set a player\'s job (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'jobName', 'type' => 'string', 'description' => 'The job name'],
['name' => 'grade', 'type' => 'int', 'description' => 'The job grade (usually 0 for basic)'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.SetJob('police', 0)\n TriggerClientEvent('QBCore:Notify', source, 'You are now a police officer', 'success')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.SetJob('police', 0);\n TriggerClientEvent('QBCore:Notify', global.source, 'You are now a police officer', 'success');\n}",
],
'GetJob' => [
'namespace' => 'Player',
'description' => 'Get a player\'s job information (server-side)',
'side' => 'server',
'parameters' => [],
'returns' => ['type' => 'table', 'description' => 'Job data table with name, label, grade, etc.'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n local job = Player.Functions.GetJob()\n print('Job: ' .. job.name .. ' (' .. job.grade.name .. ')')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n const job = Player.Functions.GetJob();\n console.log(`Job: \${job.name} (\${job.grade.name})`);\n}",
],
'AddVehicle' => [
'namespace' => 'Player',
'description' => 'Add a vehicle to a player\'s garage (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'modelName', 'type' => 'string', 'description' => 'Vehicle model name (e.g., "adder")'],
['name' => 'plate', 'type' => 'string', 'description' => 'License plate (usually unique identifier)'],
['name' => 'properties', 'type' => 'table|nil', 'description' => 'Optional vehicle properties/customization'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'lua_example' => "local Player = QBCore.Functions.GetPlayer(source)\nif Player then\n Player.Functions.AddVehicle('adder', 'QBTEST')\n TriggerClientEvent('QBCore:Notify', source, 'Vehicle added to garage')\nend",
'js_example' => "const Player = QBCore.Functions.GetPlayer(global.source);\nif (Player) {\n Player.Functions.AddVehicle('adder', 'QBTEST');\n TriggerClientEvent('QBCore:Notify', global.source, 'Vehicle added to garage');\n}",
],
'DeleteVehicle' => [
'namespace' => 'Player',
'description' => 'Remove a vehicle from a player\'s garage (server-side)',
'side' => 'server',
'parameters' => [
['name' => 'plate', 'type' => 'string', 'description' => 'License plate of vehicle to remove'],
],
'returns' => ['type' => 'boolean', 'description' => 'Success status'],
'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);
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.qbcore.qbcore-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 QBCore server function to look up (e.g., "QBCore.Functions.GetPlayer", "AddMoney", "SetJob", "SetGang", "Notify", "HasItem", "SetMetaData", "GetRep")')
->required(),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
+16 -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>
@@ -186,6 +193,12 @@
<li>
<a href="https://docs.fivem.net/natives/" class="text-indigo-600 hover:text-indigo-500">FiveM Natives Reference</a>
</li>
<li>
<a href="https://docs.qbcore.org/" class="text-indigo-600 hover:text-indigo-500">QBCore Documentation</a>
</li>
<li>
<a href="https://coxdocs.dev/" class="text-indigo-600 hover:text-indigo-500">COX MySQL Documentation</a>
</li>
<li>
<a href="https://laravel.com/docs/mcp" class="text-indigo-600 hover:text-indigo-500">Laravel MCP Documentation</a>
</li>
@@ -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)
@@ -2,7 +2,6 @@
## {{ $event['name'] }}
**Category:** {{ $event['category'] }}
**Side:** {{ ucfirst($event['side']) }}
### Description
@@ -10,8 +9,8 @@
### Parameters
@if(count($event['parameters']) > 0)
@foreach($event['parameters'] as $param)
- **{{ $param['name'] }}** ({{ $param['type'] }}): {{ $param['description'] }}
@foreach($event['parameters'] as $name => $description)
- `{{ $name }}`: {{ $description }}
@endforeach
@else
This event takes no parameters.
@@ -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,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,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)
@@ -1,6 +1,6 @@
<?php
use App\Mcp\Tools\GetCOXEventReference;
use App\Mcp\Tools\COX\MySQL\GetCOXEventReference;
use Laravel\Mcp\Request;
it('get cox event reference returns response with content', function () {
@@ -1,6 +1,6 @@
<?php
use App\Mcp\Tools\GetCOXFunction;
use App\Mcp\Tools\COX\MySQL\GetCOXFunction;
use Laravel\Mcp\Request;
it('get cox function returns response with content', function () {
@@ -45,7 +45,7 @@ it('get cox function supports javascript examples', function () {
it('get cox function works with transaction methods', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'MySQL.transaction.begin']);
$request = new Request(['function_name' => 'MySQL.transaction']);
$response = $tool->handle($request);
@@ -0,0 +1,64 @@
<?php
use App\Mcp\Tools\COX\Inventory\Client\GetInventoryClientFunction;
use Laravel\Mcp\Request;
it('get inventory client function returns response with content', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'openInventory']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory client function handles unknown function', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get inventory client function works with SearchPlayer', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'SearchPlayer']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory client function works with useItem', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'useItem']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory client function supports javascript examples', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'GetCurrentWeapon', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory client function works with getCurrentWeight', function () {
$tool = new GetInventoryClientFunction;
$request = new Request(['function_name' => 'getCurrentWeight']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -0,0 +1,64 @@
<?php
use App\Mcp\Tools\COX\Inventory\Server\GetInventoryServerFunction;
use Laravel\Mcp\Request;
it('get inventory server function returns response with content', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'AddItem']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory server function handles unknown function', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
it('get inventory server function works with RemoveItem', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'RemoveItem']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory server function works with RegisterStash', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'RegisterStash']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory server function supports javascript examples', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'GetItem', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get inventory server function works with SetDurability', function () {
$tool = new GetInventoryServerFunction;
$request = new Request(['function_name' => 'SetDurability']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -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();
});
@@ -1,10 +1,10 @@
<?php
use App\Mcp\Tools\GenerateManifest;
use App\Mcp\Tools\FiveM\GenerateManifest;
use Laravel\Mcp\Request;
it('generate manifest returns response with content', function () {
$tool = new GenerateManifest();
$tool = new GenerateManifest;
$request = new Request(['resource_name' => 'test-resource']);
$response = $tool->handle($request);
@@ -14,7 +14,7 @@ it('generate manifest returns response with content', function () {
});
it('generate manifest includes required fields', function () {
$tool = new GenerateManifest();
$tool = new GenerateManifest;
$request = new Request(['resource_name' => 'my-test-resource']);
$response = $tool->handle($request);
@@ -1,10 +1,10 @@
<?php
use App\Mcp\Tools\GenerateResourceBoilerplate;
use App\Mcp\Tools\FiveM\GenerateResourceBoilerplate;
use Laravel\Mcp\Request;
it('generate resource boilerplate returns response with content', function () {
$tool = new GenerateResourceBoilerplate();
$tool = new GenerateResourceBoilerplate;
$request = new Request(['resource_name' => 'test-resource']);
$response = $tool->handle($request);
@@ -14,7 +14,7 @@ it('generate resource boilerplate returns response with content', function () {
});
it('generate resource boilerplate includes resource name', function () {
$tool = new GenerateResourceBoilerplate();
$tool = new GenerateResourceBoilerplate;
$request = new Request(['resource_name' => 'my-boilerplate']);
$response = $tool->handle($request);
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\FiveM\Client\GetEventClientReference;
use Laravel\Mcp\Request;
it('returns client event reference', function () {
$tool = new GetEventClientReference;
$request = new Request(['event_name' => 'onResourceStart']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('onResourceStart');
});
it('returns not found for invalid event', function () {
$tool = new GetEventClientReference;
$request = new Request(['event_name' => 'InvalidEvent']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('lists all client events when empty event name', function () {
$tool = new GetEventClientReference;
$request = new Request(['event_name' => '']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('handles case insensitive event names', function () {
$tool = new GetEventClientReference;
$request = new Request(['event_name' => 'onresourcestart']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('onResourceStart');
});
it('contains lua examples', function () {
$tool = new GetEventClientReference;
$request = new Request(['event_name' => 'onResourceStart']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\FiveM\Server\GetEventServerReference;
use Laravel\Mcp\Request;
it('returns server event reference', function () {
$tool = new GetEventServerReference;
$request = new Request(['event_name' => 'playerConnecting']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('playerConnecting');
});
it('returns not found for invalid server event', function () {
$tool = new GetEventServerReference;
$request = new Request(['event_name' => 'InvalidServerEvent']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('lists all server events when empty event name', function () {
$tool = new GetEventServerReference;
$request = new Request(['event_name' => '']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('handles case insensitive server event names', function () {
$tool = new GetEventServerReference;
$request = new Request(['event_name' => 'playerdropped']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('playerDropped');
});
it('contains examples for server events', function () {
$tool = new GetEventServerReference;
$request = new Request(['event_name' => 'playerConnecting']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\FiveM\Client\GetNativeClientFunction;
use Laravel\Mcp\Request;
it('returns client native function info', function () {
$tool = new GetNativeClientFunction;
$request = new Request(['function_name' => 'PlayerPedId', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('PlayerPedId');
});
it('returns not found for invalid function', function () {
$tool = new GetNativeClientFunction;
$request = new Request(['function_name' => 'InvalidFunction', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('supports javascript examples', function () {
$tool = new GetNativeClientFunction;
$request = new Request(['function_name' => 'GetEntityCoords', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('js');
});
it('handles case insensitive function names', function () {
$tool = new GetNativeClientFunction;
$request = new Request(['function_name' => 'playerpedid', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('PlayerPedId');
});
it('lists all client functions when no name provided', function () {
$tool = new GetNativeClientFunction;
$request = new Request(['function_name' => '', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\FiveM\Server\GetNativeServerFunction;
use Laravel\Mcp\Request;
it('returns server native function info', function () {
$tool = new GetNativeServerFunction;
$request = new Request(['function_name' => 'TriggerClientEvent', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('TriggerClientEvent');
});
it('returns not found for invalid function', function () {
$tool = new GetNativeServerFunction;
$request = new Request(['function_name' => 'InvalidServerFunction', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('supports javascript examples for server natives', function () {
$tool = new GetNativeServerFunction;
$request = new Request(['function_name' => 'GetPlayerName', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('handles case insensitive server function names', function () {
$tool = new GetNativeServerFunction;
$request = new Request(['function_name' => 'getplayername', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('GetPlayerName');
});
it('lists all server functions when no name provided', function () {
$tool = new GetNativeServerFunction;
$request = new Request(['function_name' => '', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -1,10 +1,10 @@
<?php
use App\Mcp\Tools\SearchFiveMDocs;
use App\Mcp\Tools\FiveM\SearchFiveMDocs;
use Laravel\Mcp\Request;
it('search fivem docs returns response with content', function () {
$tool = new SearchFiveMDocs();
$tool = new SearchFiveMDocs;
$request = new Request(['query' => 'networking']);
$response = $tool->handle($request);
@@ -14,7 +14,7 @@ it('search fivem docs returns response with content', function () {
});
it('search fivem docs handles empty results', function () {
$tool = new SearchFiveMDocs();
$tool = new SearchFiveMDocs;
$request = new Request(['query' => 'nonexistent_topic_xyz']);
$response = $tool->handle($request);
@@ -1,24 +0,0 @@
<?php
use App\Mcp\Tools\GetEventReference;
use Laravel\Mcp\Request;
it('get event reference returns response with content', function () {
$tool = new GetEventReference();
$request = new Request(['event_name' => 'playerSpawned']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get event reference handles unknown event', function () {
$tool = new GetEventReference();
$request = new Request(['event_name' => 'NonExistentEvent123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found') || expect($content)->toContain('Event');
});
@@ -1,34 +0,0 @@
<?php
use App\Mcp\Tools\GetNativeFunction;
use Laravel\Mcp\Request;
it('get native function returns response with content', function () {
$tool = new GetNativeFunction();
$request = new Request(['function_name' => 'GetPlayerPed']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get native function finds known function', function () {
$tool = new GetNativeFunction();
$request = new Request(['function_name' => 'GetPlayerPed']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('GetPlayerPed') || expect($content)->toContain('player');
});
it('get native function handles unknown function', function () {
$tool = new GetNativeFunction();
$request = new Request(['function_name' => 'UnknownFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found');
});
@@ -1,34 +0,0 @@
<?php
use App\Mcp\Tools\GetQBCoreEventReference;
use Laravel\Mcp\Request;
it('get qbcore event reference returns response with content', function () {
$tool = new GetQBCoreEventReference();
$request = new Request(['event_name' => 'QBCore:Client:OnPlayerLoaded']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get qbcore event reference handles unknown event', function () {
$tool = new GetQBCoreEventReference();
$request = new Request(['event_name' => 'NonExistentQBCoreEvent123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found') || expect($content)->toContain('QBCore event');
});
it('get qbcore event reference filters by category', function () {
$tool = new GetQBCoreEventReference();
$request = new Request(['event_category' => 'player']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
@@ -1,44 +0,0 @@
<?php
use App\Mcp\Tools\GetQBCoreFunction;
use Laravel\Mcp\Request;
it('get qbcore function returns response with content', function () {
$tool = new GetQBCoreFunction();
$request = new Request(['function_name' => 'QBCore.Functions.GetPlayer']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get qbcore function handles unknown function', function () {
$tool = new GetQBCoreFunction();
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found') || expect($content)->toContain('QBCore');
});
it('get qbcore function works with shorthand names', function () {
$tool = new GetQBCoreFunction();
$request = new Request(['function_name' => 'AddMoney']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get qbcore function supports javascript examples', function () {
$tool = new GetQBCoreFunction();
$request = new Request(['function_name' => 'AddMoney', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
+6 -6
View File
@@ -4,7 +4,7 @@ use App\Mcp\Resources\CodeSnippets;
use Laravel\Mcp\Request;
it('code snippets resource returns response with content', function () {
$resource = new CodeSnippets();
$resource = new CodeSnippets;
$request = new Request([]);
$response = $resource->handle($request);
@@ -14,7 +14,7 @@ it('code snippets resource returns response with content', function () {
});
it('code snippets response contains content', function () {
$resource = new CodeSnippets();
$resource = new CodeSnippets;
$request = new Request([]);
$response = $resource->handle($request);
@@ -24,7 +24,7 @@ it('code snippets response contains content', function () {
});
it('best practices resource returns response with content', function () {
$resource = new \App\Mcp\Resources\BestPractices();
$resource = new \App\Mcp\Resources\BestPractices;
$request = new Request([]);
$response = $resource->handle($request);
@@ -34,7 +34,7 @@ it('best practices resource returns response with content', function () {
});
it('best practices response contains content', function () {
$resource = new \App\Mcp\Resources\BestPractices();
$resource = new \App\Mcp\Resources\BestPractices;
$request = new Request([]);
$response = $resource->handle($request);
@@ -44,7 +44,7 @@ it('best practices response contains content', function () {
});
it('framework comparison resource returns response with content', function () {
$resource = new \App\Mcp\Resources\FrameworkComparison();
$resource = new \App\Mcp\Resources\FrameworkComparison;
$request = new Request([]);
$response = $resource->handle($request);
@@ -54,7 +54,7 @@ it('framework comparison resource returns response with content', function () {
});
it('framework comparison response contains content', function () {
$resource = new \App\Mcp\Resources\FrameworkComparison();
$resource = new \App\Mcp\Resources\FrameworkComparison;
$request = new Request([]);
$response = $resource->handle($request);
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\QBCore\Client\GetQBCoreClientEventReference;
use Laravel\Mcp\Request;
it('returns qbcore client event reference', function () {
$tool = new GetQBCoreClientEventReference;
$request = new Request(['event_name' => 'QBCore:Client:OnPlayerLoad']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('OnPlayerLoad');
});
it('returns not found for invalid client event', function () {
$tool = new GetQBCoreClientEventReference;
$request = new Request(['event_name' => 'InvalidClientEvent']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('lists all client events when empty event name', function () {
$tool = new GetQBCoreClientEventReference;
$request = new Request(['event_name' => '']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('searches by client event short names', function () {
$tool = new GetQBCoreClientEventReference;
$request = new Request(['event_name' => 'QBCore:Client:OnPlayerLoad']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('OnPlayerLoad');
});
it('includes notification event', function () {
$tool = new GetQBCoreClientEventReference;
$request = new Request(['event_name' => 'QBCore:Notify']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('Notify');
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\QBCore\Client\GetQBCoreClientFunction;
use Laravel\Mcp\Request;
it('returns qbcore client function info', function () {
$tool = new GetQBCoreClientFunction;
$request = new Request(['function_name' => 'QBCore.Functions.GetPlayerData', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('GetPlayerData');
});
it('returns not found for invalid client function', function () {
$tool = new GetQBCoreClientFunction;
$request = new Request(['function_name' => 'InvalidClientFunction', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('supports javascript examples for client functions', function () {
$tool = new GetQBCoreClientFunction;
$request = new Request(['function_name' => 'QBCore.Commands.Add', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('handles case insensitive client function names', function () {
$tool = new GetQBCoreClientFunction;
$request = new Request(['function_name' => 'qbcore.functions.getplayerdata', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('GetPlayerData');
});
it('includes command registration function', function () {
$tool = new GetQBCoreClientFunction;
$request = new Request(['function_name' => 'QBCore.Commands.Add', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -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,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');
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerEventReference;
use Laravel\Mcp\Request;
it('returns qbcore server event reference', function () {
$tool = new GetQBCoreServerEventReference;
$request = new Request(['event_name' => 'QBCore:Server:PlayerLoaded']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('PlayerLoaded');
});
it('returns not found for invalid server event', function () {
$tool = new GetQBCoreServerEventReference;
$request = new Request(['event_name' => 'InvalidServerEvent']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('lists all server events when empty event name', function () {
$tool = new GetQBCoreServerEventReference;
$request = new Request(['event_name' => '']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('searches by server event short names', function () {
$tool = new GetQBCoreServerEventReference;
$request = new Request(['event_name' => 'QBCore:Server:PlayerLoaded']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('PlayerLoaded');
});
it('contains examples for server events', function () {
$tool = new GetQBCoreServerEventReference;
$request = new Request(['event_name' => 'QBCore:Server:playerDropped']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
@@ -0,0 +1,49 @@
<?php
use App\Mcp\Tools\QBCore\Server\GetQBCoreServerFunction;
use Laravel\Mcp\Request;
it('returns qbcore server function info', function () {
$tool = new GetQBCoreServerFunction;
$request = new Request(['function_name' => 'QBCore.Functions.GetPlayer', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('GetPlayer');
});
it('returns not found for invalid server function', function () {
$tool = new GetQBCoreServerFunction;
$request = new Request(['function_name' => 'InvalidQBFunction', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('not found');
});
it('supports javascript examples for server functions', function () {
$tool = new GetQBCoreServerFunction;
$request = new Request(['function_name' => 'AddMoney', 'language' => 'js']);
$response = $tool->handle($request);
expect((string) $response->content())->not()->toBeEmpty();
});
it('handles case insensitive server function names', function () {
$tool = new GetQBCoreServerFunction;
$request = new Request(['function_name' => 'addmoney', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('AddMoney');
});
it('includes player data structure', function () {
$tool = new GetQBCoreServerFunction;
$request = new Request(['function_name' => 'GetMoney', 'language' => 'lua']);
$response = $tool->handle($request);
expect((string) $response->content())->toContain('GetMoney');
});
@@ -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');
});