feat: Add COX MySQL tools and resources for event references and function lookups

- Implemented DatabaseQueries resource for FiveM database query examples.
- Created GetCOXEventReference tool to retrieve information about COX MySQL events.
- Developed GetCOXFunction tool to look up COX MySQL functions and methods.
- Added views for displaying event lists, event references, and function details.
- Included comprehensive CRUD operation examples for database interactions in FiveM.
- Established tests for GetCOXEventReference and GetCOXFunction tools to ensure functionality.
This commit is contained in:
Adrian Mejias committed 2026-03-01 01:20:59 -06:00
1 parent 18765b1013
commit a2591c2209
12 files changed
+1012 -5

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Resource;
#[Description('FiveM database query examples and common patterns for COX MySQL')]
class DatabaseQueries extends Resource
{
public function uri(): string
{
return 'fivem://guides/database-queries';
}
public function handle(Request $request): Response
{
return Response::text(view('mcp.resources.database-queries')->render());
}
}
+10
View File
@@ -9,11 +9,16 @@ use App\Mcp\Prompts\DebugIssues;
use App\Mcp\Prompts\OptimizePerformance;
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 Laravel\Mcp\Server;
use Laravel\Mcp\Server\Attributes\Instructions;
@@ -43,12 +48,17 @@ class FiveMServer extends Server
GenerateManifest::class,
GetEventReference::class,
GenerateResourceBoilerplate::class,
GetQBCoreEventReference::class,
GetQBCoreFunction::class,
GetCOXEventReference::class,
GetCOXFunction::class,
];
protected array $resources = [
CodeSnippets::class,
BestPractices::class,
FrameworkComparison::class,
DatabaseQueries::class,
];
protected array $prompts = [
+213
View File
@@ -0,0 +1,213 @@
<?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 COX MySQL events and callbacks.')]
class GetCOXEventReference 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->getCOXEventsDatabase();
foreach ($events as $event) {
if (strtolower($event['name']) === strtolower($eventName)) {
return Response::text($this->formatEventInfo($event, $language));
}
}
return Response::text(sprintf("COX event '%s' not found in the database.", $eventName));
}
/**
* List events by type.
*/
protected function listEvents(string $type, string $language): Response
{
$events = $this->getCOXEventsDatabase();
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.cox-event-list', [
'eventsByType' => $eventsByType,
])->render()
);
}
/**
* Format event information.
*/
protected function formatEventInfo(array $event, string $language): string
{
return view('mcp.cox-event-reference', [
'event' => $event,
'language' => $language,
])->render();
}
/**
* Get COX events database.
*/
protected function getCOXEventsDatabase(): array
{
return [
// Query Events
[
'name' => 'coxMySQL:queryCompleted',
'type' => 'query',
'description' => 'Triggered when a query completes (success or failure)',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'The SQL query that was executed'],
['name' => 'result', 'type' => 'table|boolean', 'description' => 'Query result or false if failed'],
],
'lua_example' => "RegisterNetEvent('coxMySQL:queryCompleted', function(sql, result)\n if result then\n print('Query succeeded')\n else\n print('Query failed: ' .. sql)\n end\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:queryCompleted', (sql, result) => {\n if (result) {\n console.log('Query succeeded');\n } else {\n console.log('Query failed: ' + sql);\n }\n});",
],
[
'name' => 'coxMySQL:insertCompleted',
'type' => 'query',
'description' => 'Triggered when an insert query completes',
'parameters' => [
['name' => 'insertId', 'type' => 'int', 'description' => 'ID of the inserted row'],
['name' => 'affectedRows', 'type' => 'int', 'description' => 'Number of rows affected'],
],
'lua_example' => "RegisterNetEvent('coxMySQL:insertCompleted', function(insertId, affectedRows)\n print('Inserted ID: ' .. insertId .. ', Rows affected: ' .. affectedRows)\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:insertCompleted', (insertId, affectedRows) => {\n console.log(`Inserted ID: \${insertId}, Rows affected: \${affectedRows}`);\n});",
],
// Connection Events
[
'name' => 'coxMySQL:connected',
'type' => 'connection',
'description' => 'Triggered when the database connection is established',
'parameters' => [],
'lua_example' => "RegisterNetEvent('coxMySQL:connected', function()\n print('Database connected successfully!')\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:connected', () => {\n console.log('Database connected successfully!');\n});",
],
[
'name' => 'coxMySQL:disconnected',
'type' => 'connection',
'description' => 'Triggered when the database connection closes',
'parameters' => [],
'lua_example' => "RegisterNetEvent('coxMySQL:disconnected', function()\n print('Database disconnected')\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:disconnected', () => {\n console.log('Database disconnected');\n});",
],
[
'name' => 'coxMySQL:connectionError',
'type' => 'connection',
'description' => 'Triggered when a database connection error occurs',
'parameters' => [
['name' => 'error', 'type' => 'string', 'description' => 'Error message'],
],
'lua_example' => "RegisterNetEvent('coxMySQL:connectionError', function(error)\n print('Connection error: ' .. error)\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:connectionError', (error) => {\n console.log('Connection error: ' + error);\n});",
],
// Transaction Events
[
'name' => 'coxMySQL:transactionBegun',
'type' => 'transaction',
'description' => 'Triggered when a database transaction begins',
'parameters' => [],
'lua_example' => "RegisterNetEvent('coxMySQL:transactionBegun', function()\n print('Transaction started')\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:transactionBegun', () => {\n console.log('Transaction started');\n});",
],
[
'name' => 'coxMySQL:transactionCommitted',
'type' => 'transaction',
'description' => 'Triggered when a transaction is successfully committed',
'parameters' => [],
'lua_example' => "RegisterNetEvent('coxMySQL:transactionCommitted', function()\n print('Transaction committed successfully')\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:transactionCommitted', () => {\n console.log('Transaction committed successfully');\n});",
],
[
'name' => 'coxMySQL:transactionRolledBack',
'type' => 'transaction',
'description' => 'Triggered when a transaction is rolled back',
'parameters' => [],
'lua_example' => "RegisterNetEvent('coxMySQL:transactionRolledBack', function()\n print('Transaction rolled back')\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:transactionRolledBack', () => {\n console.log('Transaction rolled back');\n});",
],
// Error Events
[
'name' => 'coxMySQL:queryError',
'type' => 'error',
'description' => 'Triggered when a query execution fails',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'The SQL that failed'],
['name' => 'errorMessage', 'type' => 'string', 'description' => 'MySQL error message'],
],
'lua_example' => "RegisterNetEvent('coxMySQL:queryError', function(sql, errorMessage)\n print('Query error: ' .. errorMessage)\n print('Failed query: ' .. sql)\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:queryError', (sql, errorMessage) => {\n console.log('Query error: ' + errorMessage);\n console.log('Failed query: ' + sql);\n});",
],
[
'name' => 'coxMySQL:slowQuery',
'type' => 'error',
'description' => 'Triggered when a query exceeds the configured timeout',
'parameters' => [
['name' => 'sql', 'type' => 'string', 'description' => 'The slow SQL query'],
['name' => 'duration', 'type' => 'float', 'description' => 'Query duration in milliseconds'],
],
'lua_example' => "RegisterNetEvent('coxMySQL:slowQuery', function(sql, duration)\n print('Slow query (' .. duration .. 'ms): ' .. sql)\nend)",
'js_example' => "RegisterNetEvent('coxMySQL:slowQuery', (sql, duration) => {\n console.log(`Slow query (\${duration}ms): \${sql}`);\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 COX event name to look up (optional, leave empty to list all events)'),
'event_type' => $schema
->string()
->enum(['all', 'query', 'connection', 'transaction', 'error'])
->description('Filter events by type')
->default('all'),
'language' => $schema
->string()
->enum(['lua', 'js'])
->description('The scripting language for code examples')
->default('lua'),
];
}
}
+230
View File
@@ -0,0 +1,230 @@
<?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'),
];
}
}
+30
View File
@@ -191,6 +191,36 @@ class SearchFiveMDocs extends Tool
'description' => 'Database transaction handling with COX MySQL library',
'keywords' => ['cox', 'mysql', 'transaction', 'commit', 'rollback'],
],
[
'title' => 'COX Docs - Prepared Statements',
'url' => 'https://coxdocs.dev/prepared-statements',
'description' => 'Prepared statements for secure SQL queries and SQL injection prevention',
'keywords' => ['cox', 'mysql', 'prepared', 'statement', 'security', 'injection'],
],
[
'title' => 'COX Docs - Connection Pooling',
'url' => 'https://coxdocs.dev/connection-pooling',
'description' => 'Connection pooling and management for optimal database performance',
'keywords' => ['cox', 'mysql', 'connection', 'pool', 'pooling', 'performance'],
],
[
'title' => 'COX Docs - Common Patterns',
'url' => 'https://coxdocs.dev/patterns',
'description' => 'Common database patterns (CRUD operations, async queries, error handling)',
'keywords' => ['cox', 'mysql', 'pattern', 'crud', 'create', 'read', 'update', 'delete'],
],
[
'title' => 'COX Docs - Debugging & Performance',
'url' => 'https://coxdocs.dev/debugging',
'description' => 'Debugging MySQL queries, performance optimization, and troubleshooting',
'keywords' => ['cox', 'mysql', 'debug', 'performance', 'optimize', 'slow', 'error'],
],
[
'title' => 'COX Docs - Best Practices',
'url' => 'https://coxdocs.dev/best-practices',
'description' => 'Database best practices and conventions for FiveM development',
'keywords' => ['cox', 'mysql', 'best', 'practice', 'convention', 'standard'],
],
],
];
+60 -5
View File
@@ -322,6 +322,59 @@
<h4 class="font-semibold text-gray-900 mb-2">Example:</h4>
<pre class="bg-gray-100 rounded p-3 text-sm"><code>How do I use the AddMoney function in QBCore?</code></pre>
</div>
<!-- Tool 8: GetCOXEventReference -->
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
<h3 class="text-xl font-semibold text-gray-900 mb-3">8. GetCOXEventReference</h3>
<p class="text-gray-700 mb-4">Get information about COX MySQL events and callbacks.</p>
<h4 class="font-semibold text-gray-900 mb-2">Parameters:</h4>
<ul class="list-disc list-inside text-gray-700 mb-4 ml-4">
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">event_name</code> (optional): Specific event name to
look up</li>
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">event_type</code> (optional): Filter by type (all,
query, connection, transaction, error)</li>
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">language</code> (optional): Code example language (lua
or js)</li>
</ul>
<h4 class="font-semibold text-gray-900 mb-2">Examples:</h4>
<pre class="bg-gray-100 rounded p-3 text-sm mb-2"><code>What are the COX MySQL connection events?</code></pre>
<pre class="bg-gray-100 rounded p-3 text-sm mb-2"><code>Show me all COX transaction events</code></pre>
<pre class="bg-gray-100 rounded p-3 text-sm"><code>How do I handle query errors in COX?</code></pre>
</div>
<!-- Tool 9: GetCOXFunction -->
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
<h3 class="text-xl font-semibold text-gray-900 mb-3">9. GetCOXFunction</h3>
<p class="text-gray-700 mb-4">Look up COX MySQL functions and methods.</p>
<h4 class="font-semibold text-gray-900 mb-2">Parameters:</h4>
<ul class="list-disc list-inside text-gray-700 mb-4 ml-4">
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">function_name</code> (required): COX function name
(e.g., "MySQL.query", "MySQL.insert")</li>
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">language</code> (optional): Code example language (lua
or js)</li>
</ul>
<h4 class="font-semibold text-gray-900 mb-2">Common COX Methods:</h4>
<ul class="list-disc list-inside text-gray-700 mb-4 ml-4">
<li>Query: <code class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.query</code>, <code
class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.single</code>, <code
class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.scalar</code></li>
<li>Modify: <code class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.insert</code>, <code
class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.update</code></li>
<li>Transactions: <code class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.transaction.begin</code>, <code
class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.transaction.commit</code></li>
<li>Connection: <code class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.ready</code>, <code
class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.getStatus</code></li>
<li>Security: <code class="bg-gray-100 px-2 py-1 rounded text-sm">MySQL.prepare</code> (prepared statements)
</li>
</ul>
<h4 class="font-semibold text-gray-900 mb-2">Example:</h4>
<pre
class="bg-gray-100 rounded p-3 text-sm"><code>How do I query the database safely with prepared statements?</code></pre>
</div>
</section>
<!-- Available Resources -->
@@ -345,12 +398,14 @@
<p class="text-gray-700 mt-2">Covers performance optimization, security best practices, code organization, resource management, testing, and common pitfalls.</p>
</div>
<!-- Resource 3: Framework Comparison -->
<!-- Resource 4: Database Queries -->
<div class="mb-8 border-l-4 border-green-500 pl-6">
<h3 class="text-xl font-semibold text-gray-900 mb-3">3. Framework Comparison</h3>
<p class="text-gray-700 mb-4">Comparison of FiveM frameworks: Standalone, ESX, and QBCore.</p>
<p class="text-gray-600 text-sm">URI: <code class="bg-gray-100 px-2 py-1 rounded text-sm">fivem://guides/framework-comparison</code></p>
<p class="text-gray-700 mt-2">Complete comparison with pros/cons, code examples, common functions, and migration guides.</p>
<h3 class="text-xl font-semibold text-gray-900 mb-3">4. Database Queries</h3>
<p class="text-gray-700 mb-4">FiveM database query examples and common patterns for COX MySQL.</p>
<p class="text-gray-600 text-sm">URI: <code
class="bg-gray-100 px-2 py-1 rounded text-sm">fivem://guides/database-queries</code></p>
<p class="text-gray-700 mt-2">Covers CRUD operations, prepared statements, transactions, async queries, error handling,
performance tips, and common database patterns (SELECT, INSERT, UPDATE, DELETE).</p>
</div>
</section>
@@ -0,0 +1,10 @@
# COX MySQL Events
@foreach($eventsByType as $type => $events)
## {{ ucfirst($type) }} Events
@foreach($events as $event)
- **{{ $event['name'] }}** - {{ $event['description'] }}
@endforeach
@endforeach
@@ -0,0 +1,29 @@
# COX Event Reference
## {{ $event['name'] }}
**Type:** {{ $event['type'] }}
### Description
{{ $event['description'] }}
### Parameters
@if(count($event['parameters']) > 0)
@foreach($event['parameters'] as $param)
- **{{ $param['name'] }}** ({{ $param['type'] }}): {{ $param['description'] }}
@endforeach
@else
This event takes no parameters.
@endif
### Code Examples
**Lua:**
```lua
{{ $event['lua_example'] }}
```
**JavaScript:**
```javascript
{{ $event['js_example'] }}
```
@@ -0,0 +1,48 @@
# COX Function: {{ $function['name'] }}
**Namespace:** {{ $function['namespace'] }}
**Side:** {{ ucfirst($function['side']) }}
### Description
{{ $function['description'] }}
### Parameters
@if(count($function['parameters']) > 0)
@foreach($function['parameters'] as $param)
- **{{ $param['name'] }}** ({{ $param['type'] }}): {{ $param['description'] }}
@endforeach
@else
This function takes no parameters.
@endif
### Returns
**Type:** {{ $function['returns']['type'] }}
{{ $function['returns']['description'] }}
### Code Examples
@if($language === 'js')
**JavaScript:**
```javascript
{{ $function['js_example'] }}
```
@else
**Lua:**
```lua
{{ $function['lua_example'] }}
```
@endif
### Alternative Example
@if($language === 'js')
**Lua (alternative):**
```lua
{{ $function['lua_example'] }}
```
@else
**JavaScript (alternative):**
```javascript
{{ $function['js_example'] }}
```
@endif
@@ -0,0 +1,252 @@
# FiveM Database Query Examples
Common database patterns and examples for COX MySQL operations in FiveM development.
## CRUD Operations
### Create (INSERT)
**Lua Example:**
```lua
MySQL.insert('users', {
username = 'player_name',
money = 1000,
bank = 5000,
created_at = os.date('%Y-%m-%d %H:%M:%S')
}, function(insertId)
print('User created with ID: ' .. insertId)
end)
```
**JavaScript Example:**
```javascript
MySQL.insert('users', {
username: 'player_name',
money: 1000,
bank: 5000,
created_at: new Date().toISOString()
}, (insertId) => {
console.log('User created with ID: ' + insertId);
});
```
### Read (SELECT)
**Get Multiple Rows:**
```lua
MySQL.query('SELECT * FROM users WHERE banned = ?', { 0 }, function(results)
if results then
for i = 1, #results do
print(results[i].username .. ' - $' .. results[i].money)
end
end
end)
```
**Get Single Row:**
```lua
MySQL.single('SELECT * FROM users WHERE id = ?', { playerId }, function(user)
if user then
print('Player: ' .. user.username)
end
end)
```
**Get Single Value:**
```lua
MySQL.scalar('SELECT COUNT(*) as count FROM users', {}, function(count)
print('Total users: ' .. (count or 0))
end)
```
### Update (UPDATE)
**Update Single Record:**
```lua
MySQL.update('users', {
money = 500,
updated_at = os.date('%Y-%m-%d %H:%M:%S')
}, { id = playerId }, function(affectedRows)
print('Updated ' .. affectedRows .. ' user(s)')
end)
```
**Update Multiple Records:**
```lua
MySQL.update('users', { banned = 1 }, { last_login = '<', '2024-01-01' }, function(updated)
print('Banned ' .. updated .. ' inactive users')
end)
```
### Delete (DELETE)
**Lua Example:**
```lua
MySQL.query('DELETE FROM users WHERE banned = ? AND last_login < DATE_SUB(NOW(), INTERVAL 90 DAY)', { 1 }, function(result)
if result then
print('Cleanup completed')
end
end)
```
## Prepared Statements (Security)
Always use prepared statements to prevent SQL injection:
**Secure Query:**
```lua
-- Using ? placeholders for parameters
MySQL.query('SELECT * FROM users WHERE username = ? AND password_hash = ?',
{ inputUsername, passwordHash },
function(result)
-- Safe from SQL injection
end)
```
**Avoid This (Vulnerable):**
```lua
-- DO NOT DO THIS - Vulnerable to SQL injection
local query = 'SELECT * FROM users WHERE username = \'' .. inputUsername .. '\''
MySQL.query(query, {}, callback) -- BAD!
```
## Transactions
Transactions ensure atomicity - either all changes succeed or none:
**Money Transfer Example:**
```lua
MySQL.transaction.begin()
-- Deduct from source account
MySQL.query('UPDATE users SET money = money - ? WHERE id = ?', { amount, sourceId })
-- Add to destination account
MySQL.query('UPDATE users SET money = money + ? WHERE id = ?', { amount, destId })
-- Commit or rollback based on results
MySQL.transaction.commit(function(success)
if success then
print('Transfer success')
else
MySQL.transaction.rollback()
print('Transfer failed and rolled back')
end
end)
```
## Async Operations
Using async queries for better code organization:
**Lua Example:**
```lua
CreateThread(function()
-- Query executes asynchronously
local users = MySQL.async.query('SELECT id FROM users WHERE active = ?', { 1 })
if users then
print('Found ' .. #users .. ' active users')
end
end)
```
## Error Handling
Always handle errors gracefully:
**Try-Catch Pattern:**
```lua
RegisterNetEvent('myResource:registerUser', function()
local username = 'newuser'
local email = 'user@example.com'
MySQL.insert('users', { username = username, email = email }, function(insertId)
if insertId then
TriggerClientEvent('notify', source, 'Account created!', 'success')
else
TriggerClientEvent('notify', source, 'Account creation failed', 'error')
end
end)
end)
```
## Connection Checks
Always verify database is ready before executing queries:
```lua
if MySQL.ready then
MySQL.query('SELECT 1', {}, function(result)
print('Database is available')
end)
else
print('Waiting for database connection...')
end
```
## Common Queries
### Get Player Data
```lua
MySQL.single('SELECT * FROM users WHERE id = ?', { playerId }, function(user)
if user then
print('User: ' .. user.username .. ', Money: ' .. user.money)
end
end)
```
### Update Player Money
```lua
MySQL.update('users', { money = newAmount }, { id = playerId }, callback)
```
### Check User Exists
```lua
MySQL.scalar('SELECT COUNT(*) FROM users WHERE username = ?', { username }, function(exists)
if exists > 0 then
print('User already exists')
end
end)
```
### Get All Active Players
```lua
MySQL.query('SELECT id, username FROM users WHERE active = ? ORDER BY username ASC', { 1 }, callback)
```
### Batch Insert
```lua
for i = 1, 10 do
MySQL.insert('logs', { action = 'test', timestamp = os.time() })
end
```
## Performance Tips
1. **Use WHERE clauses** to limit result sets
2. **Use LIMIT** for pagination: `SELECT * FROM users LIMIT 10 OFFSET 20`
3. **Use indexes** on frequently queried columns (id, username, etc.)
4. **Use transactions** for related operations
5. **Use prepared statements** always
6. **Cache results** when data doesn't change frequently
7. **Avoid N+1 queries** - fetch related data in one query
8. **Use EXPLAIN** to analyze query performance
## Debugging
Monitor slow queries and errors:
```lua
RegisterNetEvent('coxMySQL:queryError', function(sql, error)
print('Query error: ' .. error)
print('Failed query: ' .. sql)
-- Log to file or monitoring service
end)
RegisterNetEvent('coxMySQL:slowQuery', function(sql, duration)
if duration > 500 then
print('SLOW QUERY (' .. duration .. 'ms): ' .. sql)
end
end)
```
@@ -0,0 +1,54 @@
<?php
use App\Mcp\Tools\GetCOXEventReference;
use Laravel\Mcp\Request;
it('get cox event reference returns response with content', function () {
$tool = new GetCOXEventReference;
$request = new Request(['event_name' => 'coxMySQL:connected']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox event reference handles unknown event', function () {
$tool = new GetCOXEventReference;
$request = new Request(['event_name' => 'NonExistentCOXEvent123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found') || expect($content)->toContain('COX event');
});
it('get cox event reference filters by type', function () {
$tool = new GetCOXEventReference;
$request = new Request(['event_type' => 'connection']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox event reference lists all events', function () {
$tool = new GetCOXEventReference;
$request = new Request(['event_type' => 'all']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox event reference supports query events', function () {
$tool = new GetCOXEventReference;
$request = new Request(['event_name' => 'coxMySQL:queryCompleted']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
+54
View File
@@ -0,0 +1,54 @@
<?php
use App\Mcp\Tools\GetCOXFunction;
use Laravel\Mcp\Request;
it('get cox function returns response with content', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'MySQL.query']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox function handles unknown function', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'NonExistentFunction123']);
$response = $tool->handle($request);
$content = (string) $response->content();
expect($content)->toContain('not found') || expect($content)->toContain('COX');
});
it('get cox function works with insert method', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'MySQL.insert']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox function supports javascript examples', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'MySQL.query', 'language' => 'js']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});
it('get cox function works with transaction methods', function () {
$tool = new GetCOXFunction;
$request = new Request(['function_name' => 'MySQL.transaction.begin']);
$response = $tool->handle($request);
expect($response)->not()->toBeNull();
expect($response->content())->not()->toBeNull();
});