Add FiveM resources and templates for documentation and code snippets
- Introduced a new code snippets resource for common FiveM patterns in Lua and JavaScript. - Added a framework comparison document outlining Standalone, ESX, and QBCore features. - Created search results view for FiveM documentation queries. - Developed client and server template scripts for ESX and QBCore frameworks. - Implemented NUI templates including HTML, CSS, and JavaScript for user interfaces. - Established routes for documentation and console commands. - Added tests for various features including documentation routes and tool functionalities. - Configured Vite for asset management with Laravel. - Updated .gitignore files for storage and framework directories.
This commit is contained in:
112 files changed
+51106
No files matched your search
@@ -0,0 +1,155 @@
|
||||
---
|
||||
name: mcp-development
|
||||
description: "Develops MCP servers, tools, resources, and prompts. Activates when creating MCP tools, resources, or prompts; setting up AI integrations; debugging MCP connections; working with routes/ai.php; or when the user mentions MCP, Model Context Protocol, AI tools, AI server, or building tools for AI assistants."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# MCP Development
|
||||
|
||||
## Documentation First
|
||||
|
||||
**CRITICAL**: Always use `search-docs` BEFORE writing MCP code. The documentation is version-specific, comprehensive, and always up-to-date.
|
||||
|
||||
<!-- Search MCP Documentation -->
|
||||
```bash
|
||||
|
||||
# Example searches
|
||||
|
||||
search-docs(['mcp tools', 'mcp resources', 'mcp validation'])
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Artisan Commands
|
||||
|
||||
<!-- Create MCP Primitives -->
|
||||
```bash
|
||||
{{ $assist->artisanCommand('make:mcp-tool ToolName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-resource ResourceName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-prompt PromptName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-server ServerName') }}
|
||||
```
|
||||
|
||||
### Basic Tool Implementation
|
||||
|
||||
<!-- Tool Example -->
|
||||
```php
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
|
||||
class MyTool extends Tool
|
||||
{
|
||||
protected string $description = 'Tool description for LLM';
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'param' => $schema->string()->required(),
|
||||
];
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return Response::text($request->get('param'));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Basic Resource Implementation
|
||||
|
||||
<!-- Resource Example -->
|
||||
```php
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Resource;
|
||||
|
||||
class MyResource extends Resource
|
||||
{
|
||||
protected string $description = 'Resource description';
|
||||
protected string $uri = 'file://path/to/resource';
|
||||
protected string $mimeType = 'text/markdown';
|
||||
|
||||
public function handle(): Response
|
||||
{
|
||||
return Response::text($content);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response Methods
|
||||
|
||||
<!-- Available Responses -->
|
||||
```php
|
||||
Response::text('Text content');
|
||||
Response::error('Error message');
|
||||
Response::structured(['key' => 'value']);
|
||||
```
|
||||
|
||||
## Testing MCP Primitives
|
||||
|
||||
Test tools, resources, and prompts directly on their server:
|
||||
|
||||
<!-- Test MCP Primitives -->
|
||||
```php
|
||||
// Test a tool
|
||||
$response = MyServer::tool(MyTool::class, ['param' => 'value']);
|
||||
$response->assertOk()->assertSee('Expected text');
|
||||
|
||||
// Test as authenticated user
|
||||
$response = MyServer::actingAs($user)->tool(MyTool::class, [...]);
|
||||
|
||||
// Available assertions
|
||||
$response->assertOk();
|
||||
$response->assertSee('text');
|
||||
$response->assertHasErrors();
|
||||
$response->assertHasNoErrors();
|
||||
$response->assertName('tool-name');
|
||||
$response->assertSentNotification('event/type', ['data' => 'value']);
|
||||
```
|
||||
|
||||
### MCP Inspector
|
||||
|
||||
Test interactively using the inspector:
|
||||
|
||||
<!-- Launch MCP Inspector -->
|
||||
```bash
|
||||
{{ $assist->artisanCommand('mcp:inspector mcp/my-server') }} # Web server
|
||||
|
||||
{{ $assist->artisanCommand('mcp:inspector my-server') }} # Local server
|
||||
|
||||
```
|
||||
|
||||
## Available Features
|
||||
|
||||
The following features exist—**use `search-docs` for implementation details**:
|
||||
|
||||
- **Tools**: `schema()`, validation, annotations (`#[IsReadOnly]`, `#[IsDestructive]`, etc.)
|
||||
- **Resources**: URI templates (`HasUriTemplate`), Dynamic resources
|
||||
- **Prompts**: Arguments, multi-message responses
|
||||
- **All primitives**: Dependency injection, `shouldRegister()`, validation
|
||||
- **Responses**: Text, error, structured, streaming, metadata
|
||||
- **Server registration**: Web routes, local routes, OAuth
|
||||
|
||||
## Critical Imports
|
||||
|
||||
<!-- Correct Imports -->
|
||||
```php
|
||||
use Laravel\Mcp\Request; // NOT Laravel\Mcp\Server\Request
|
||||
use Laravel\Mcp\Response; // NOT Laravel\Mcp\Server\Response
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Resource;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Not using `search-docs` before implementation**
|
||||
- Wrong imports: `Laravel\Mcp\Server\Request` (wrong) vs `Laravel\Mcp\Request` (correct)
|
||||
- Forgetting `schema()` method for tools with parameters
|
||||
- Missing required properties: `$description`, `$uri`, `$mimeType`
|
||||
- Wrong response pattern: `new Response()` instead of `Response::text()`
|
||||
- Running `mcp:start` command locally (hangs waiting for stdin)
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
name: pest-testing
|
||||
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pest Testing 4
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating new tests (unit, feature, or browser)
|
||||
- Modifying existing tests
|
||||
- Debugging test failures
|
||||
- Working with browser testing or smoke testing
|
||||
- Writing architecture tests or visual regression tests
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pest 4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Tests
|
||||
|
||||
All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
|
||||
- Browser tests: `tests/Browser/` directory.
|
||||
- Do NOT remove tests without approval - these are core application code.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
<!-- Basic Pest Test Example -->
|
||||
```php
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
|
||||
- Run all tests: `php artisan test --compact`.
|
||||
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
|
||||
|
||||
## Assertions
|
||||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
<!-- Pest Response Assertion -->
|
||||
```php
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
```
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
| `assertSuccessful()` | `assertStatus(200)` |
|
||||
| `assertNotFound()` | `assertStatus(404)` |
|
||||
| `assertForbidden()` | `assertStatus(403)` |
|
||||
|
||||
## Mocking
|
||||
|
||||
Import mock function before use: `use function Pest\Laravel\mock;`
|
||||
|
||||
## Datasets
|
||||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
<!-- Pest Dataset Example -->
|
||||
```php
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
```
|
||||
|
||||
## Pest 4 Features
|
||||
|
||||
| Feature | Purpose |
|
||||
|---------|---------|
|
||||
| Browser Testing | Full integration tests in real browsers |
|
||||
| Smoke Testing | Validate multiple pages quickly |
|
||||
| Visual Regression | Compare screenshots for visual changes |
|
||||
| Test Sharding | Parallel CI runs |
|
||||
| Architecture Testing | Enforce code conventions |
|
||||
|
||||
### Browser Test Example
|
||||
|
||||
Browser tests run in real browsers for full integration testing:
|
||||
|
||||
- Browser tests live in `tests/Browser/`.
|
||||
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
|
||||
- Use `RefreshDatabase` for clean state per test.
|
||||
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
|
||||
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
|
||||
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging.
|
||||
|
||||
<!-- Pest Browser Test Example -->
|
||||
```php
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in');
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavaScriptErrors()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!');
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
```
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Quickly validate multiple pages have no JavaScript errors:
|
||||
|
||||
<!-- Pest Smoke Testing Example -->
|
||||
```php
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
|
||||
```
|
||||
|
||||
### Visual Regression Testing
|
||||
|
||||
Capture and compare screenshots to detect visual changes.
|
||||
|
||||
### Test Sharding
|
||||
|
||||
Split tests across parallel processes for faster CI runs.
|
||||
|
||||
### Architecture Testing
|
||||
|
||||
Pest 4 includes architecture testing (from Pest 3):
|
||||
|
||||
<!-- Architecture Test Example -->
|
||||
```php
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not importing `use function Pest\Laravel\mock;` before using mock
|
||||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Forgetting `assertNoJavaScriptErrors()` in browser tests
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
name: tailwindcss-development
|
||||
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Tailwind CSS Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Adding styles to components or pages
|
||||
- Working with responsive design
|
||||
- Implementing dark mode
|
||||
- Extracting repeated patterns into components
|
||||
- Debugging spacing or layout issues
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
|
||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||
|
||||
## Tailwind CSS v4 Specifics
|
||||
|
||||
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||
- `corePlugins` is not supported in Tailwind v4.
|
||||
|
||||
### CSS-First Configuration
|
||||
|
||||
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||
|
||||
<!-- CSS-First Config -->
|
||||
```css
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
```
|
||||
|
||||
### Import Syntax
|
||||
|
||||
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||
|
||||
<!-- v4 Import Syntax -->
|
||||
```diff
|
||||
- @tailwind base;
|
||||
- @tailwind components;
|
||||
- @tailwind utilities;
|
||||
+ @import "tailwindcss";
|
||||
```
|
||||
|
||||
### Replaced Utilities
|
||||
|
||||
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
|
||||
|
||||
| Deprecated | Replacement |
|
||||
|------------|-------------|
|
||||
| bg-opacity-* | bg-black/* |
|
||||
| text-opacity-* | text-black/* |
|
||||
| border-opacity-* | border-black/* |
|
||||
| divide-opacity-* | divide-black/* |
|
||||
| ring-opacity-* | ring-black/* |
|
||||
| placeholder-opacity-* | placeholder-black/* |
|
||||
| flex-shrink-* | shrink-* |
|
||||
| flex-grow-* | grow-* |
|
||||
| overflow-ellipsis | text-ellipsis |
|
||||
| decoration-slice | box-decoration-slice |
|
||||
| decoration-clone | box-decoration-clone |
|
||||
|
||||
## Spacing
|
||||
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
|
||||
- Using `@tailwind` directives instead of `@import "tailwindcss"`
|
||||
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
|
||||
- Using margins for spacing between siblings instead of gap utilities
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[compose.yaml]
|
||||
indent_size = 4
|
||||
@@ -0,0 +1,65 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
@@ -0,0 +1,11 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
applyTo: "**"
|
||||
description: Aikido MCP Security Rules
|
||||
---
|
||||
|
||||
# Project security best practices
|
||||
|
||||
- Always run **aikido_full_scan** on generated, added and modified first-party code, unless the prompt explicitly says not to.
|
||||
- Always provide the full file content to the scanner.
|
||||
- If the scan identifies any security issues in newly generated or modified code, fix the issues using the scan result and remediation provided by **Aikido**.
|
||||
- After applying fixes, run aikido_full_scan again to verify that the issues were successfully resolved, and no new issues were introduced.
|
||||
- Repeat the fix-and-rescan cycle until the code passes with zero remaining or newly introduced security issues.
|
||||
- If the **Aikido MCP server** required to run scans is not installed or not started successfully, inform the user with a message directing them to install it using the official Aikido MCP setup guide at:
|
||||
**https://help.aikido.dev/ide-plugins/aikido-mcp**.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpactor.json
|
||||
.phpunit.result.cache
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.phpunit.cache
|
||||
/.vscode
|
||||
/.zed
|
||||
/auth.json
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/vendor
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"laravel-boost": {
|
||||
"command": "php",
|
||||
"args": [
|
||||
"artisan",
|
||||
"boost:mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+2334
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,249 @@
|
||||
<laravel-boost-guidelines>
|
||||
=== foundation rules ===
|
||||
|
||||
# Laravel Boost Guidelines
|
||||
|
||||
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
|
||||
|
||||
## Foundational Context
|
||||
|
||||
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
|
||||
|
||||
- php - 8.4.18
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/boost (BOOST) - v2
|
||||
- laravel/pail (PAIL) - v1
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
- pestphp/pest (PEST) - v4
|
||||
- phpunit/phpunit (PHPUNIT) - v12
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
## Skills Activation
|
||||
|
||||
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
|
||||
|
||||
- `mcp-development` — Develops MCP servers, tools, resources, and prompts. Activates when creating MCP tools, resources, or prompts; setting up AI integrations; debugging MCP connections; working with routes/ai.php; or when the user mentions MCP, Model Context Protocol, AI tools, AI server, or building tools for AI assistants.
|
||||
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
|
||||
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
|
||||
|
||||
## Conventions
|
||||
|
||||
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
|
||||
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
|
||||
- Check for existing components to reuse before writing a new one.
|
||||
|
||||
## Verification Scripts
|
||||
|
||||
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
|
||||
|
||||
## Application Structure & Architecture
|
||||
|
||||
- Stick to existing directory structure; don't create new base folders without approval.
|
||||
- Do not change the application's dependencies without approval.
|
||||
|
||||
## Frontend Bundling
|
||||
|
||||
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
|
||||
|
||||
## Documentation Files
|
||||
|
||||
- You must only create documentation files if explicitly requested by the user.
|
||||
|
||||
## Replies
|
||||
|
||||
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
|
||||
|
||||
=== boost rules ===
|
||||
|
||||
# Laravel Boost
|
||||
|
||||
- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
|
||||
|
||||
## Artisan
|
||||
|
||||
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters.
|
||||
|
||||
## URLs
|
||||
|
||||
- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port.
|
||||
|
||||
## Tinker / Debugging
|
||||
|
||||
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
|
||||
- Use the `database-query` tool when you only need to read from the database.
|
||||
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
|
||||
|
||||
## Reading Browser Logs With the `browser-logs` Tool
|
||||
|
||||
- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
|
||||
- Only recent browser logs will be useful - ignore old logs.
|
||||
|
||||
## Searching Documentation (Critically Important)
|
||||
|
||||
- Boost comes with a powerful `search-docs` tool you should use before trying other approaches when working with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
|
||||
- Search the documentation before making code changes to ensure we are taking the correct approach.
|
||||
- Use multiple, broad, simple, topic-based queries at once. For example: `['rate limiting', 'routing rate limiting', 'routing']`. The most relevant results will be returned first.
|
||||
- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
|
||||
|
||||
### Available Search Syntax
|
||||
|
||||
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'.
|
||||
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit".
|
||||
3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order.
|
||||
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit".
|
||||
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms.
|
||||
|
||||
=== php rules ===
|
||||
|
||||
# PHP
|
||||
|
||||
- Always use curly braces for control structures, even for single-line bodies.
|
||||
|
||||
## Constructors
|
||||
|
||||
- Use PHP 8 constructor property promotion in `__construct()`.
|
||||
- `public function __construct(public GitHub $github) { }`
|
||||
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
|
||||
|
||||
## Type Declarations
|
||||
|
||||
- Always use explicit return type declarations for methods and functions.
|
||||
- Use appropriate PHP type hints for method parameters.
|
||||
|
||||
<!-- Explicit Return Types and Method Params -->
|
||||
```php
|
||||
protected function isAccessible(User $user, ?string $path = null): bool
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
|
||||
|
||||
## Comments
|
||||
|
||||
- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless the logic is exceptionally complex.
|
||||
|
||||
## PHPDoc Blocks
|
||||
|
||||
- Add useful array shape type definitions when appropriate.
|
||||
|
||||
=== laravel/core rules ===
|
||||
|
||||
# Do Things the Laravel Way
|
||||
|
||||
- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
|
||||
- If you're creating a generic PHP class, use `php artisan make:class`.
|
||||
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
|
||||
|
||||
## Database
|
||||
|
||||
- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
|
||||
- Use Eloquent models and relationships before suggesting raw database queries.
|
||||
- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
|
||||
- Generate code that prevents N+1 query problems by using eager loading.
|
||||
- Use Laravel's query builder for very complex database operations.
|
||||
|
||||
### Model Creation
|
||||
|
||||
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`.
|
||||
|
||||
### APIs & Eloquent Resources
|
||||
|
||||
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
|
||||
|
||||
## Controllers & Validation
|
||||
|
||||
- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
|
||||
- Check sibling Form Requests to see if the application uses array or string based validation rules.
|
||||
|
||||
## Authentication & Authorization
|
||||
|
||||
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
|
||||
|
||||
## URL Generation
|
||||
|
||||
- When generating links to other pages, prefer named routes and the `route()` function.
|
||||
|
||||
## Queues
|
||||
|
||||
- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
|
||||
|
||||
## Configuration
|
||||
|
||||
- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
|
||||
|
||||
## Testing
|
||||
|
||||
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
|
||||
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
|
||||
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
|
||||
|
||||
## Vite Error
|
||||
|
||||
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
|
||||
|
||||
=== laravel/v12 rules ===
|
||||
|
||||
# Laravel 12
|
||||
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
|
||||
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
|
||||
|
||||
## Laravel 12 Structure
|
||||
|
||||
- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
|
||||
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
|
||||
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
|
||||
- `bootstrap/providers.php` contains application specific service providers.
|
||||
- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
|
||||
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.
|
||||
|
||||
## Database
|
||||
|
||||
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
|
||||
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
|
||||
|
||||
### Models
|
||||
|
||||
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
|
||||
|
||||
=== mcp/core rules ===
|
||||
|
||||
# Laravel MCP
|
||||
|
||||
- Laravel MCP allows you to rapidly build MCP servers for your Laravel applications.
|
||||
- IMPORTANT: laravel/mcp is very new. Always use the `search-docs` tool for authoritative documentation on writing and testing Laravel MCP servers, tools, resources, and prompts.
|
||||
- IMPORTANT: Activate `mcp-development` every time you're working with an MCP-related task.
|
||||
|
||||
=== pint/core rules ===
|
||||
|
||||
# Laravel Pint Code Formatter
|
||||
|
||||
- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.
|
||||
|
||||
=== pest/core rules ===
|
||||
|
||||
## Pest
|
||||
|
||||
- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
|
||||
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
|
||||
- Do NOT delete tests without approval.
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples.
|
||||
- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task.
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
# Tailwind CSS
|
||||
|
||||
- Always use existing Tailwind conventions; check project patterns before adding new ones.
|
||||
- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data.
|
||||
- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task.
|
||||
|
||||
</laravel-boost-guidelines>
|
||||
@@ -0,0 +1,249 @@
|
||||
<laravel-boost-guidelines>
|
||||
=== foundation rules ===
|
||||
|
||||
# Laravel Boost Guidelines
|
||||
|
||||
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
|
||||
|
||||
## Foundational Context
|
||||
|
||||
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
|
||||
|
||||
- php - 8.4.18
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/boost (BOOST) - v2
|
||||
- laravel/pail (PAIL) - v1
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
- pestphp/pest (PEST) - v4
|
||||
- phpunit/phpunit (PHPUNIT) - v12
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
## Skills Activation
|
||||
|
||||
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
|
||||
|
||||
- `mcp-development` — Develops MCP servers, tools, resources, and prompts. Activates when creating MCP tools, resources, or prompts; setting up AI integrations; debugging MCP connections; working with routes/ai.php; or when the user mentions MCP, Model Context Protocol, AI tools, AI server, or building tools for AI assistants.
|
||||
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
|
||||
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
|
||||
|
||||
## Conventions
|
||||
|
||||
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
|
||||
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
|
||||
- Check for existing components to reuse before writing a new one.
|
||||
|
||||
## Verification Scripts
|
||||
|
||||
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
|
||||
|
||||
## Application Structure & Architecture
|
||||
|
||||
- Stick to existing directory structure; don't create new base folders without approval.
|
||||
- Do not change the application's dependencies without approval.
|
||||
|
||||
## Frontend Bundling
|
||||
|
||||
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
|
||||
|
||||
## Documentation Files
|
||||
|
||||
- You must only create documentation files if explicitly requested by the user.
|
||||
|
||||
## Replies
|
||||
|
||||
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
|
||||
|
||||
=== boost rules ===
|
||||
|
||||
# Laravel Boost
|
||||
|
||||
- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
|
||||
|
||||
## Artisan
|
||||
|
||||
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters.
|
||||
|
||||
## URLs
|
||||
|
||||
- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port.
|
||||
|
||||
## Tinker / Debugging
|
||||
|
||||
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
|
||||
- Use the `database-query` tool when you only need to read from the database.
|
||||
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
|
||||
|
||||
## Reading Browser Logs With the `browser-logs` Tool
|
||||
|
||||
- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
|
||||
- Only recent browser logs will be useful - ignore old logs.
|
||||
|
||||
## Searching Documentation (Critically Important)
|
||||
|
||||
- Boost comes with a powerful `search-docs` tool you should use before trying other approaches when working with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
|
||||
- Search the documentation before making code changes to ensure we are taking the correct approach.
|
||||
- Use multiple, broad, simple, topic-based queries at once. For example: `['rate limiting', 'routing rate limiting', 'routing']`. The most relevant results will be returned first.
|
||||
- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
|
||||
|
||||
### Available Search Syntax
|
||||
|
||||
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'.
|
||||
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit".
|
||||
3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order.
|
||||
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit".
|
||||
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms.
|
||||
|
||||
=== php rules ===
|
||||
|
||||
# PHP
|
||||
|
||||
- Always use curly braces for control structures, even for single-line bodies.
|
||||
|
||||
## Constructors
|
||||
|
||||
- Use PHP 8 constructor property promotion in `__construct()`.
|
||||
- `public function __construct(public GitHub $github) { }`
|
||||
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
|
||||
|
||||
## Type Declarations
|
||||
|
||||
- Always use explicit return type declarations for methods and functions.
|
||||
- Use appropriate PHP type hints for method parameters.
|
||||
|
||||
<!-- Explicit Return Types and Method Params -->
|
||||
```php
|
||||
protected function isAccessible(User $user, ?string $path = null): bool
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
|
||||
|
||||
## Comments
|
||||
|
||||
- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless the logic is exceptionally complex.
|
||||
|
||||
## PHPDoc Blocks
|
||||
|
||||
- Add useful array shape type definitions when appropriate.
|
||||
|
||||
=== laravel/core rules ===
|
||||
|
||||
# Do Things the Laravel Way
|
||||
|
||||
- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
|
||||
- If you're creating a generic PHP class, use `php artisan make:class`.
|
||||
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
|
||||
|
||||
## Database
|
||||
|
||||
- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
|
||||
- Use Eloquent models and relationships before suggesting raw database queries.
|
||||
- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
|
||||
- Generate code that prevents N+1 query problems by using eager loading.
|
||||
- Use Laravel's query builder for very complex database operations.
|
||||
|
||||
### Model Creation
|
||||
|
||||
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`.
|
||||
|
||||
### APIs & Eloquent Resources
|
||||
|
||||
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
|
||||
|
||||
## Controllers & Validation
|
||||
|
||||
- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
|
||||
- Check sibling Form Requests to see if the application uses array or string based validation rules.
|
||||
|
||||
## Authentication & Authorization
|
||||
|
||||
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
|
||||
|
||||
## URL Generation
|
||||
|
||||
- When generating links to other pages, prefer named routes and the `route()` function.
|
||||
|
||||
## Queues
|
||||
|
||||
- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
|
||||
|
||||
## Configuration
|
||||
|
||||
- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
|
||||
|
||||
## Testing
|
||||
|
||||
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
|
||||
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
|
||||
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
|
||||
|
||||
## Vite Error
|
||||
|
||||
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
|
||||
|
||||
=== laravel/v12 rules ===
|
||||
|
||||
# Laravel 12
|
||||
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
|
||||
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
|
||||
|
||||
## Laravel 12 Structure
|
||||
|
||||
- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
|
||||
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
|
||||
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
|
||||
- `bootstrap/providers.php` contains application specific service providers.
|
||||
- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
|
||||
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.
|
||||
|
||||
## Database
|
||||
|
||||
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
|
||||
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
|
||||
|
||||
### Models
|
||||
|
||||
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
|
||||
|
||||
=== mcp/core rules ===
|
||||
|
||||
# Laravel MCP
|
||||
|
||||
- Laravel MCP allows you to rapidly build MCP servers for your Laravel applications.
|
||||
- IMPORTANT: laravel/mcp is very new. Always use the `search-docs` tool for authoritative documentation on writing and testing Laravel MCP servers, tools, resources, and prompts.
|
||||
- IMPORTANT: Activate `mcp-development` every time you're working with an MCP-related task.
|
||||
|
||||
=== pint/core rules ===
|
||||
|
||||
# Laravel Pint Code Formatter
|
||||
|
||||
- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.
|
||||
|
||||
=== pest/core rules ===
|
||||
|
||||
## Pest
|
||||
|
||||
- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
|
||||
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
|
||||
- Do NOT delete tests without approval.
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples.
|
||||
- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task.
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
# Tailwind CSS
|
||||
|
||||
- Always use existing Tailwind conventions; check project patterns before adding new ones.
|
||||
- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data.
|
||||
- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task.
|
||||
|
||||
</laravel-boost-guidelines>
|
||||
@@ -0,0 +1,3 @@
|
||||
# FiveM MCP
|
||||
|
||||
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.
|
||||
+28519
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\View\View;
|
||||
|
||||
class DocsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the documentation index page.
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return view('docs.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the quick start guide.
|
||||
*/
|
||||
public function quickstart(): View
|
||||
{
|
||||
return view('docs.quickstart');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the full documentation.
|
||||
*/
|
||||
public function documentation(): View
|
||||
{
|
||||
return view('docs.documentation');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Prompts;
|
||||
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
|
||||
#[Description('Add ESX or QBCore framework integration to existing resource')]
|
||||
class AddFrameworkIntegration extends Prompt
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$code = $request->get('code', '');
|
||||
$currentFramework = $request->get('current_framework', 'standalone');
|
||||
$targetFramework = $request->get('target_framework', 'esx');
|
||||
|
||||
$parts = [sprintf(
|
||||
"Convert this FiveM resource from %s to %s:\n\n",
|
||||
$currentFramework,
|
||||
$targetFramework
|
||||
)];
|
||||
|
||||
if ($code) {
|
||||
$parts[] = sprintf("Current Code:\n```\n%s\n```\n\n", $code);
|
||||
}
|
||||
|
||||
$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."
|
||||
);
|
||||
|
||||
return Response::prompt(implode('', $parts));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'code' => $schema->string()
|
||||
->description('Current resource code')
|
||||
->default(''),
|
||||
'current_framework' => $schema->enum(['standalone', 'esx', 'qbcore'])
|
||||
->description('Current framework')
|
||||
->default('standalone'),
|
||||
'target_framework' => $schema->enum(['esx', 'qbcore'])
|
||||
->description('Target framework')
|
||||
->default('esx'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Prompts;
|
||||
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
|
||||
#[Description('Convert code between Lua and JavaScript')]
|
||||
class ConvertLanguage extends Prompt
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$code = $request->get('code', '');
|
||||
$from = $request->get('from', 'lua');
|
||||
$to = $request->get('to', 'js');
|
||||
|
||||
$parts = [sprintf("Convert this FiveM code from %s to %s:\n\n", $from, $to)];
|
||||
|
||||
if ($code) {
|
||||
$parts[] = sprintf("```%s\n%s\n```\n\n", $from, $code);
|
||||
}
|
||||
|
||||
$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"
|
||||
);
|
||||
|
||||
return Response::prompt(implode('', $parts));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'code' => $schema->string()
|
||||
->description('Code to convert')
|
||||
->required(),
|
||||
'from' => $schema->enum(['lua', 'js'])
|
||||
->description('Source language')
|
||||
->default('lua'),
|
||||
'to' => $schema->enum(['lua', 'js'])
|
||||
->description('Target language')
|
||||
->default('js'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Prompts;
|
||||
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
|
||||
#[Description('Create a new FiveM resource with framework and language selection')]
|
||||
class CreateNewResource extends Prompt
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$resourceName = $request->get('resource_name', 'my-resource');
|
||||
$framework = $request->get('framework', 'standalone');
|
||||
$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.",
|
||||
$resourceName,
|
||||
$framework,
|
||||
$language
|
||||
));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'resource_name' => $schema->string()
|
||||
->description('Name of the resource to create')
|
||||
->default('my-resource'),
|
||||
'framework' => $schema->enum(['standalone', 'esx', 'qbcore'])
|
||||
->description('Framework to use')
|
||||
->default('standalone'),
|
||||
'language' => $schema->enum(['lua', 'js'])
|
||||
->description('Scripting language')
|
||||
->default('lua'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Prompts;
|
||||
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
|
||||
#[Description('Debug common FiveM issues and errors')]
|
||||
class DebugIssues extends Prompt
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$issue = $request->get('issue', '');
|
||||
$errorMessage = $request->get('error_message', '');
|
||||
|
||||
$parts = ["Help me debug a FiveM issue:\n\n"];
|
||||
|
||||
if ($issue) {
|
||||
$parts[] = sprintf("Issue Description: %s\n\n", $issue);
|
||||
}
|
||||
|
||||
if ($errorMessage) {
|
||||
$parts[] = sprintf("Error Message: %s\n\n", $errorMessage);
|
||||
}
|
||||
|
||||
$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."
|
||||
);
|
||||
|
||||
return Response::prompt(implode('', $parts));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'issue' => $schema->string()
|
||||
->description('Description of the issue or problem')
|
||||
->default(''),
|
||||
'error_message' => $schema->string()
|
||||
->description('Error message from console or logs')
|
||||
->default(''),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Prompts;
|
||||
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Attributes\Description;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
|
||||
#[Description('Optimize FiveM script performance')]
|
||||
class OptimizePerformance extends Prompt
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$code = $request->get('code', '');
|
||||
$scriptType = $request->get('script_type', 'client');
|
||||
|
||||
$parts = ["Review and optimize this FiveM script for better performance:\n\n"];
|
||||
|
||||
if ($code) {
|
||||
$parts[] = sprintf("```\n%s\n```\n\n", $code);
|
||||
}
|
||||
|
||||
$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"
|
||||
);
|
||||
|
||||
return Response::prompt(implode('', $parts));
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'code' => $schema->string()
|
||||
->description('The code to optimize')
|
||||
->default(''),
|
||||
'script_type' => $schema->enum(['client', 'server', 'shared'])
|
||||
->description('Type of script')
|
||||
->default('client'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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 development best practices and optimization tips')]
|
||||
class BestPractices extends Resource
|
||||
{
|
||||
public function uri(): string
|
||||
{
|
||||
return 'fivem://guides/best-practices';
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return Response::text(view('mcp.resources.best-practices')->render());
|
||||
}
|
||||
}
|
||||
@@ -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('Common FiveM code patterns and snippets for quick reference')]
|
||||
class CodeSnippets extends Resource
|
||||
{
|
||||
public function uri(): string
|
||||
{
|
||||
return 'fivem://snippets/common';
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return Response::text(view('mcp.resources.code-snippets')->render());
|
||||
}
|
||||
}
|
||||
@@ -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('Comparison of FiveM frameworks: Standalone, ESX, and QBCore')]
|
||||
class FrameworkComparison extends Resource
|
||||
{
|
||||
public function uri(): string
|
||||
{
|
||||
return 'fivem://guides/framework-comparison';
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return Response::text(view('mcp.resources.framework-comparison')->render());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mcp\Servers;
|
||||
|
||||
use App\Mcp\Prompts\AddFrameworkIntegration;
|
||||
use App\Mcp\Prompts\ConvertLanguage;
|
||||
use App\Mcp\Prompts\CreateNewResource;
|
||||
use App\Mcp\Prompts\DebugIssues;
|
||||
use App\Mcp\Prompts\OptimizePerformance;
|
||||
use App\Mcp\Resources\BestPractices;
|
||||
use App\Mcp\Resources\CodeSnippets;
|
||||
use App\Mcp\Resources\FrameworkComparison;
|
||||
use App\Mcp\Tools\GenerateManifest;
|
||||
use App\Mcp\Tools\GenerateResourceBoilerplate;
|
||||
use App\Mcp\Tools\GetEventReference;
|
||||
use App\Mcp\Tools\GetNativeFunction;
|
||||
use App\Mcp\Tools\SearchFiveMDocs;
|
||||
use Laravel\Mcp\Server;
|
||||
use Laravel\Mcp\Server\Attributes\Instructions;
|
||||
use Laravel\Mcp\Server\Attributes\Name;
|
||||
use Laravel\Mcp\Server\Attributes\Version;
|
||||
|
||||
#[Name('FiveM Development Server')]
|
||||
#[Version('1.0.0')]
|
||||
#[Instructions(<<<'INSTRUCTIONS'
|
||||
This MCP server provides comprehensive tools for FiveM (GTA5) script development.
|
||||
|
||||
Available features:
|
||||
- Search FiveM documentation and native functions
|
||||
- Generate resource manifests (fxmanifest.lua)
|
||||
- Look up game natives for client, server, and shared scripts
|
||||
- Get code examples and best practices
|
||||
- Access event documentation
|
||||
- Generate boilerplate code for resources
|
||||
|
||||
Use these tools to accelerate your FiveM development workflow.
|
||||
INSTRUCTIONS)]
|
||||
class FiveMServer extends Server
|
||||
{
|
||||
protected array $tools = [
|
||||
SearchFiveMDocs::class,
|
||||
GetNativeFunction::class,
|
||||
GenerateManifest::class,
|
||||
GetEventReference::class,
|
||||
GenerateResourceBoilerplate::class,
|
||||
];
|
||||
|
||||
protected array $resources = [
|
||||
CodeSnippets::class,
|
||||
BestPractices::class,
|
||||
FrameworkComparison::class,
|
||||
];
|
||||
|
||||
protected array $prompts = [
|
||||
CreateNewResource::class,
|
||||
DebugIssues::class,
|
||||
OptimizePerformance::class,
|
||||
ConvertLanguage::class,
|
||||
AddFrameworkIntegration::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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('Generate a fxmanifest.lua file for a FiveM resource with the specified configuration options.')]
|
||||
class GenerateManifest extends Tool
|
||||
{
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$resourceName = $request->get('resource_name');
|
||||
$author = $request->get('author', 'Unknown');
|
||||
$description = $request->get('description', 'A FiveM resource');
|
||||
$version = $request->get('version', '1.0.0');
|
||||
$scriptType = $request->get('script_type', 'lua');
|
||||
$includeClient = $request->get('include_client', true);
|
||||
$includeServer = $request->get('include_server', true);
|
||||
$framework = $request->get('framework', 'standalone');
|
||||
|
||||
return Response::text(
|
||||
view('mcp.templates.fxmanifest', [
|
||||
'resourceName' => $resourceName,
|
||||
'author' => $author,
|
||||
'description' => $description,
|
||||
'version' => $version,
|
||||
'scriptExt' => $scriptType === 'js' ? 'js' : 'lua',
|
||||
'includeClient' => $includeClient,
|
||||
'includeServer' => $includeServer,
|
||||
'framework' => $framework,
|
||||
])->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 FiveM resource')
|
||||
->required(),
|
||||
'author' => $schema
|
||||
->string()
|
||||
->description('The author name')
|
||||
->default('Unknown'),
|
||||
'description' => $schema
|
||||
->string()
|
||||
->description('Resource description')
|
||||
->default('A FiveM resource'),
|
||||
'version' => $schema
|
||||
->string()
|
||||
->description('Resource version (e.g., "1.0.0")')
|
||||
->default('1.0.0'),
|
||||
'script_type' => $schema
|
||||
->string()
|
||||
->enum(['lua', 'js'])
|
||||
->description('Scripting language (Lua or JavaScript)')
|
||||
->default('lua'),
|
||||
'include_client' => $schema
|
||||
->boolean()
|
||||
->description('Include client-side scripts')
|
||||
->default(true),
|
||||
'include_server' => $schema
|
||||
->boolean()
|
||||
->description('Include server-side scripts')
|
||||
->default(true),
|
||||
'framework' => $schema
|
||||
->string()
|
||||
->enum(['standalone', 'esx', 'qbcore'])
|
||||
->description('Framework type (standalone, ESX, or QBCore)')
|
||||
->default('standalone'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?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('Generate boilerplate code for a FiveM resource including client, server, config, and manifest files.')]
|
||||
class GenerateResourceBoilerplate extends Tool
|
||||
{
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$resourceName = $request->get('resource_name');
|
||||
$framework = $request->get('framework', 'standalone');
|
||||
$scriptType = $request->get('script_type', 'lua');
|
||||
$includeNUI = $request->get('include_nui', false);
|
||||
|
||||
$files = $this->generateBoilerplate([
|
||||
'resource_name' => $resourceName,
|
||||
'framework' => $framework,
|
||||
'script_type' => $scriptType,
|
||||
'include_nui' => $includeNUI,
|
||||
]);
|
||||
|
||||
return Response::text(
|
||||
view('mcp.resource-boilerplate', [
|
||||
'resourceName' => $resourceName,
|
||||
'files' => $files,
|
||||
])->render()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate boilerplate files.
|
||||
*/
|
||||
protected function generateBoilerplate(array $config): array
|
||||
{
|
||||
$files = [];
|
||||
$ext = $config['script_type'] === 'js' ? 'js' : 'lua';
|
||||
|
||||
// Config file
|
||||
$files[sprintf('config.%s', $ext)] = $this->generateConfig($config);
|
||||
|
||||
// Client file
|
||||
$files[sprintf('client/main.%s', $ext)] = $this->generateClient($config);
|
||||
|
||||
// Server file
|
||||
$files[sprintf('server/main.%s', $ext)] = $this->generateServer($config);
|
||||
|
||||
// NUI files if requested
|
||||
if ($config['include_nui']) {
|
||||
$files['html/index.html'] = $this->generateNUIHTML();
|
||||
$files['html/style.css'] = $this->generateNUICSS();
|
||||
$files[sprintf('html/script.%s', $ext)] = $this->generateNUIScript($config);
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate config file.
|
||||
*/
|
||||
protected function generateConfig(array $config): string
|
||||
{
|
||||
$template = $config['script_type'] === 'js' ? 'config-js' : 'config-lua';
|
||||
|
||||
return view(sprintf('mcp.templates.%s', $template))->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate client file.
|
||||
*/
|
||||
protected function generateClient(array $config): string
|
||||
{
|
||||
$template = $config['script_type'] === 'js' ? 'client-js' : 'client-lua';
|
||||
|
||||
return view(sprintf('mcp.templates.%s', $template), [
|
||||
'framework' => $config['framework'],
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate server file.
|
||||
*/
|
||||
protected function generateServer(array $config): string
|
||||
{
|
||||
$template = $config['script_type'] === 'js' ? 'server-js' : 'server-lua';
|
||||
|
||||
return view(sprintf('mcp.templates.%s', $template), [
|
||||
'framework' => $config['framework'],
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate NUI HTML.
|
||||
*/
|
||||
protected function generateNUIHTML(): string
|
||||
{
|
||||
return view('mcp.templates.nui-html')->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate NUI CSS.
|
||||
*/
|
||||
protected function generateNUICSS(): string
|
||||
{
|
||||
return view('mcp.templates.nui-css')->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate NUI script.
|
||||
*/
|
||||
protected function generateNUIScript(array $config): string
|
||||
{
|
||||
$template = $config['script_type'] === 'js' ? 'nui-script-js' : 'nui-script-lua';
|
||||
|
||||
return view(sprintf('mcp.templates.%s', $template))->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 resource to generate boilerplate for')
|
||||
->required(),
|
||||
'framework' => $schema
|
||||
->string()
|
||||
->enum(['standalone', 'esx', 'qbcore'])
|
||||
->description('Framework type (standalone, ESX, or QBCore)')
|
||||
->default('standalone'),
|
||||
'script_type' => $schema
|
||||
->string()
|
||||
->enum(['lua', 'js'])
|
||||
->description('Scripting language (Lua or JavaScript)')
|
||||
->default('lua'),
|
||||
'include_nui' => $schema
|
||||
->boolean()
|
||||
->description('Include NUI (UI) files')
|
||||
->default(false),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?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 FiveM events including built-in and common framework events (ESX, QBCore).')]
|
||||
class GetEventReference 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("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.event-list', [
|
||||
'eventsByType' => $eventsByType,
|
||||
])->render()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format event information.
|
||||
*/
|
||||
protected function formatEventInfo(array $event, string $language): string
|
||||
{
|
||||
return view('mcp.event-reference', [
|
||||
'event' => $event,
|
||||
'language' => $language,
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events database.
|
||||
*/
|
||||
protected function getEventsDatabase(): array
|
||||
{
|
||||
return [
|
||||
// Core FiveM Events
|
||||
[
|
||||
'name' => 'onResourceStart',
|
||||
'type' => 'core',
|
||||
'side' => 'both',
|
||||
'description' => 'Triggered when a resource starts',
|
||||
'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});",
|
||||
'documentation' => 'https://docs.fivem.net/docs/scripting-reference/events/list/onResourceStart/',
|
||||
],
|
||||
[
|
||||
'name' => 'onResourceStop',
|
||||
'type' => 'core',
|
||||
'side' => 'both',
|
||||
'description' => 'Triggered when a resource stops',
|
||||
'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});",
|
||||
'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
|
||||
[
|
||||
'name' => 'esx:playerLoaded',
|
||||
'type' => 'esx',
|
||||
'side' => 'client',
|
||||
'description' => 'Triggered when ESX player data is loaded on client',
|
||||
'parameters' => [
|
||||
['name' => 'xPlayer', 'type' => 'table', 'description' => 'ESX player object'],
|
||||
],
|
||||
'lua_example' => "RegisterNetEvent('esx:playerLoaded', function(xPlayer)\n ESX.PlayerData = xPlayer\n -- Player is loaded\nend)",
|
||||
'js_example' => "RegisterNetEvent('esx:playerLoaded', (xPlayer) => {\n ESX.PlayerData = xPlayer;\n // Player is loaded\n});",
|
||||
],
|
||||
[
|
||||
'name' => 'esx:setJob',
|
||||
'type' => 'esx',
|
||||
'side' => 'client',
|
||||
'description' => 'Triggered when player job changes',
|
||||
'parameters' => [
|
||||
['name' => 'job', 'type' => 'table', 'description' => 'New job object'],
|
||||
],
|
||||
'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
|
||||
[
|
||||
'name' => 'QBCore:Client:OnPlayerLoaded',
|
||||
'type' => 'qbcore',
|
||||
'side' => 'client',
|
||||
'description' => 'Triggered when QBCore player data is loaded on client',
|
||||
'parameters' => [],
|
||||
'lua_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()\n local PlayerData = QBCore.Functions.GetPlayerData()\n -- Player is loaded\nend)",
|
||||
'js_example' => "RegisterNetEvent('QBCore:Client:OnPlayerLoaded', () => {\n const PlayerData = QBCore.Functions.GetPlayerData();\n // Player is loaded\n});",
|
||||
],
|
||||
[
|
||||
'name' => 'QBCore:Client:OnJobUpdate',
|
||||
'type' => 'qbcore',
|
||||
'side' => 'client',
|
||||
'description' => 'Triggered when player job changes in QBCore',
|
||||
'parameters' => [
|
||||
['name' => 'job', 'type' => 'table', 'description' => 'New job object'],
|
||||
],
|
||||
'lua_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate', function(job)\n PlayerData.job = job\n print('Job changed to: ' .. job.name)\nend)",
|
||||
'js_example' => "RegisterNetEvent('QBCore:Client:OnJobUpdate', (job) => {\n PlayerData.job = job;\n console.log(`Job changed to: \${job.name}`);\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 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,196 @@
|
||||
<?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 GTA5/FiveM native functions by name. Returns function signature, parameters, return type, and usage examples.')]
|
||||
class GetNativeFunction 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("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 native function.
|
||||
*/
|
||||
protected function findNative(string $name): ?array
|
||||
{
|
||||
// Common FiveM/GTA5 natives database
|
||||
$natives = [
|
||||
// Player/Ped natives
|
||||
'GetPlayerPed' => [
|
||||
'namespace' => 'PLAYER',
|
||||
'description' => 'Gets the local or specified player\'s ped handle',
|
||||
'parameters' => [
|
||||
['name' => 'playerId', 'type' => 'int', 'description' => 'The player ID, or -1 for local player'],
|
||||
],
|
||||
'returns' => ['type' => 'int', 'description' => 'The player ped handle'],
|
||||
'lua_example' => 'local playerPed = GetPlayerPed(-1)',
|
||||
'js_example' => 'const playerPed = GetPlayerPed(-1);',
|
||||
],
|
||||
'GetPlayerName' => [
|
||||
'namespace' => 'PLAYER',
|
||||
'description' => 'Returns the name of a player',
|
||||
'parameters' => [
|
||||
['name' => 'playerId', 'type' => 'int', 'description' => 'The player ID'],
|
||||
],
|
||||
'returns' => ['type' => 'string', 'description' => 'The player name'],
|
||||
'lua_example' => 'local name = GetPlayerName(PlayerId())',
|
||||
'js_example' => 'const name = GetPlayerName(PlayerId());',
|
||||
],
|
||||
'SetEntityCoords' => [
|
||||
'namespace' => 'ENTITY',
|
||||
'description' => 'Sets the coordinates of an entity',
|
||||
'parameters' => [
|
||||
['name' => 'entity', 'type' => 'int', 'description' => 'The entity handle'],
|
||||
['name' => 'x', 'type' => 'float', 'description' => 'X coordinate'],
|
||||
['name' => 'y', 'type' => 'float', 'description' => 'Y coordinate'],
|
||||
['name' => 'z', 'type' => 'float', 'description' => 'Z coordinate'],
|
||||
['name' => 'alive', 'type' => 'boolean', 'description' => 'Keep entity alive during teleport'],
|
||||
['name' => 'deadFlag', 'type' => 'boolean', 'description' => 'Teleport even if dead'],
|
||||
['name' => 'ragdollFlag', 'type' => 'boolean', 'description' => 'Teleport even in ragdoll'],
|
||||
['name' => 'clearArea', 'type' => 'boolean', 'description' => 'Clear area at destination'],
|
||||
],
|
||||
'returns' => ['type' => 'void', 'description' => 'No return value'],
|
||||
'lua_example' => 'SetEntityCoords(PlayerPedId(), 0.0, 0.0, 72.0, true, false, false, true)',
|
||||
'js_example' => 'SetEntityCoords(PlayerPedId(), 0.0, 0.0, 72.0, true, false, false, true);',
|
||||
],
|
||||
'GetEntityCoords' => [
|
||||
'namespace' => 'ENTITY',
|
||||
'description' => 'Gets the coordinates of an entity',
|
||||
'parameters' => [
|
||||
['name' => 'entity', 'type' => 'int', 'description' => 'The entity handle'],
|
||||
['name' => 'alive', 'type' => 'boolean', 'description' => 'Whether to get alive coordinates only'],
|
||||
],
|
||||
'returns' => ['type' => 'vector3', 'description' => 'The entity coordinates'],
|
||||
'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)',
|
||||
'parameters' => [
|
||||
['name' => 'eventName', 'type' => 'string', 'description' => 'The name of the event'],
|
||||
],
|
||||
'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});",
|
||||
],
|
||||
'TriggerServerEvent' => [
|
||||
'namespace' => 'CFX',
|
||||
'description' => 'Triggers an event on the server from client',
|
||||
'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' => "TriggerServerEvent('myResource:serverEvent', arg1, arg2)",
|
||||
'js_example' => "TriggerServerEvent('myResource:serverEvent', arg1, arg2);",
|
||||
],
|
||||
'TriggerClientEvent' => [
|
||||
'namespace' => 'CFX',
|
||||
'description' => 'Triggers an event on a client from 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);",
|
||||
],
|
||||
'AddEventHandler' => [
|
||||
'namespace' => 'CFX',
|
||||
'description' => 'Adds an event handler for local events',
|
||||
'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('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);',
|
||||
],
|
||||
'QBCore.Functions.GetPlayer' => [
|
||||
'namespace' => 'QBCore',
|
||||
'description' => 'Get QBCore player object from server ID (server-side)',
|
||||
'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(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.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 native function to look up (e.g., "GetPlayerPed", "SetEntityCoords")')
|
||||
->required(),
|
||||
'language' => $schema
|
||||
->string()
|
||||
->enum(['lua', 'js'])
|
||||
->description('The scripting language for code examples')
|
||||
->default('lua'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?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('Search FiveM documentation for specific topics, concepts, or features. Returns relevant documentation URLs and summaries.')]
|
||||
class SearchFiveMDocs extends Tool
|
||||
{
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$query = $request->get('query');
|
||||
$category = $request->get('category', 'all');
|
||||
|
||||
$results = $this->searchDocs($query, $category);
|
||||
|
||||
if (empty($results)) {
|
||||
return Response::text(sprintf('No documentation found for query: %s', $query));
|
||||
}
|
||||
|
||||
return Response::text(
|
||||
view('mcp.search-results', [
|
||||
'query' => $query,
|
||||
'results' => $results,
|
||||
])->render()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the FiveM documentation.
|
||||
*/
|
||||
protected function searchDocs(string $query, string $category): array
|
||||
{
|
||||
$baseUrl = 'https://docs.fivem.net/docs/';
|
||||
|
||||
// Common documentation pages organized by category
|
||||
$docs = [
|
||||
'scripting' => [
|
||||
[
|
||||
'title' => 'Scripting Introduction',
|
||||
'url' => sprintf('%sscripting-manual/introduction/', $baseUrl),
|
||||
'description' => 'Learn the basics of FiveM scripting with Lua and JavaScript',
|
||||
'keywords' => ['script', 'introduction', 'basics', 'getting started', 'lua', 'javascript'],
|
||||
],
|
||||
[
|
||||
'title' => 'Client Functions',
|
||||
'url' => sprintf('%sscripting-reference/client-functions/', $baseUrl),
|
||||
'description' => 'Client-side native functions for game interaction',
|
||||
'keywords' => ['client', 'native', 'function', 'game', 'player'],
|
||||
],
|
||||
[
|
||||
'title' => 'Server Functions',
|
||||
'url' => sprintf('%sscripting-reference/server-functions/', $baseUrl),
|
||||
'description' => 'Server-side functions for player management and game logic',
|
||||
'keywords' => ['server', 'function', 'player', 'management'],
|
||||
],
|
||||
[
|
||||
'title' => 'Resource Manifest',
|
||||
'url' => sprintf('%sscripting-reference/resource-manifest/resource-manifest/', $baseUrl),
|
||||
'description' => 'Configure your resource with fxmanifest.lua',
|
||||
'keywords' => ['manifest', 'fxmanifest', 'resource', 'config', 'configuration'],
|
||||
],
|
||||
],
|
||||
'natives' => [
|
||||
[
|
||||
'title' => 'Native Reference',
|
||||
'url' => sprintf('%snatives/', $baseUrl),
|
||||
'description' => 'Complete reference of GTA5 native functions',
|
||||
'keywords' => ['native', 'reference', 'gta', 'function'],
|
||||
],
|
||||
[
|
||||
'title' => 'CFX Natives',
|
||||
'url' => sprintf('%snatives/?n_CFX', $baseUrl),
|
||||
'description' => 'FiveM-specific native functions (CFX namespace)',
|
||||
'keywords' => ['cfx', 'native', 'fivem', 'custom'],
|
||||
],
|
||||
],
|
||||
'networking' => [
|
||||
[
|
||||
'title' => 'Network Events',
|
||||
'url' => sprintf('%sscripting-manual/networking/events/', $baseUrl),
|
||||
'description' => 'Client-server communication using events',
|
||||
'keywords' => ['event', 'network', 'trigger', 'client', 'server', 'communication'],
|
||||
],
|
||||
[
|
||||
'title' => 'State Bags',
|
||||
'url' => sprintf('%sscripting-manual/networking/state-bags/', $baseUrl),
|
||||
'description' => 'Synchronized state management across clients and server',
|
||||
'keywords' => ['state', 'bag', 'sync', 'data', 'shared'],
|
||||
],
|
||||
],
|
||||
'resources' => [
|
||||
[
|
||||
'title' => 'Creating Resources',
|
||||
'url' => sprintf('%sscripting-manual/introduction/creating-your-first-script/', $baseUrl),
|
||||
'description' => 'Step-by-step guide to creating your first FiveM resource',
|
||||
'keywords' => ['create', 'resource', 'first', 'tutorial', 'guide'],
|
||||
],
|
||||
[
|
||||
'title' => 'Resource Structure',
|
||||
'url' => sprintf('%sscripting-manual/introduction/about-resources/', $baseUrl),
|
||||
'description' => 'Understanding FiveM resource folder structure and organization',
|
||||
'keywords' => ['structure', 'folder', 'organization', 'resource', 'files'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$results = [];
|
||||
$searchIn = $category === 'all' ? array_merge(...array_values($docs)) : ($docs[$category] ?? []);
|
||||
$queryLower = strtolower($query);
|
||||
|
||||
foreach ($searchIn as $doc) {
|
||||
$matchScore = 0;
|
||||
|
||||
// Check title
|
||||
if (str_contains(strtolower($doc['title']), $queryLower)) {
|
||||
$matchScore += 10;
|
||||
}
|
||||
|
||||
// Check keywords
|
||||
foreach ($doc['keywords'] as $keyword) {
|
||||
if (str_contains(strtolower($keyword), $queryLower) || str_contains($queryLower, strtolower($keyword))) {
|
||||
$matchScore += 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Check description
|
||||
if (str_contains(strtolower($doc['description']), $queryLower)) {
|
||||
$matchScore += 3;
|
||||
}
|
||||
|
||||
if ($matchScore > 0) {
|
||||
$results[] = array_merge($doc, ['score' => $matchScore]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by score descending
|
||||
usort($results, fn ($a, $b) => $b['score'] <=> $a['score']);
|
||||
|
||||
return array_slice($results, 0, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool's input schema.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\JsonSchema\JsonSchema>
|
||||
*/
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'query' => $schema
|
||||
->string()
|
||||
->description('The search query for FiveM documentation (e.g., "events", "natives", "manifest")')
|
||||
->required(),
|
||||
'category' => $schema
|
||||
->string()
|
||||
->enum(['all', 'scripting', 'natives', 'networking', 'resources'])
|
||||
->description('Filter results by documentation category')
|
||||
->default('all'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
$status = $app->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"agents": [
|
||||
"claude_code",
|
||||
"gemini"
|
||||
],
|
||||
"guidelines": true,
|
||||
"herd_mcp": false,
|
||||
"mcp": true,
|
||||
"nightwatch_mcp": false,
|
||||
"sail": false,
|
||||
"skills": [
|
||||
"mcp-development",
|
||||
"pest-testing",
|
||||
"tailwindcss-development"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
})->create();
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
];
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"framework"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/mcp": "^0.5.9",
|
||||
"laravel/tinker": "^2.10.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-ide-helper": "^3.6",
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/boost": "^2.0",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
"laravel/sail": "^1.41",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"pestphp/pest": "^4.4",
|
||||
"pestphp/pest-plugin-laravel": "^4.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"setup": [
|
||||
"composer install",
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
|
||||
"@php artisan key:generate",
|
||||
"@php artisan migrate --force",
|
||||
"npm install",
|
||||
"npm run build"
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
|
||||
],
|
||||
"test": [
|
||||
"@php artisan config:clear --ansi",
|
||||
"@php artisan test"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
|
||||
"@php artisan boost:update --ansi",
|
||||
"@php artisan ide-helper:generate",
|
||||
"@php artisan ide-helper:meta"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi",
|
||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
||||
"@php artisan migrate --graceful --ansi"
|
||||
],
|
||||
"pre-package-uninstall": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
Generated
+9855
File diff suppressed because it is too large.
Load diff
+126
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application, which will be used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| other UI elements where an application name needs to be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default authentication "guard" and password
|
||||
| reset "broker" for your application. You may change these values
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTH_GUARD', 'web'),
|
||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| which utilizes session storage plus the Eloquent user provider.
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| providers to represent the model / table. These providers may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options specify the behavior of Laravel's password
|
||||
| reset functionality, including the table utilized for token storage
|
||||
| and the user provider that is invoked to actually retrieve users.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the number of seconds before a password confirmation
|
||||
| window expires and users are asked to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||
|
||||
];
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane",
|
||||
| "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'stores' => [
|
||||
'database',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
|
||||
|
||||
];
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
'transaction_mode' => 'DEFERRED',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
'driver' => 'mariadb',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => env('DB_SSLMODE', 'prefer'),
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application for file storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure as many filesystem disks as necessary, and you
|
||||
| may even configure multiple disks for the same driver. Examples for
|
||||
| most supported storage drivers are configured here for reference.
|
||||
|
|
||||
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'handler_with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send all email
|
||||
| messages unless another mailer is explicitly specified when sending
|
||||
| the message. All additional mailers can be configured within the
|
||||
| "mailers" array. Examples of each type of mailer are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||
| when delivering an email. You may specify which one you're using for
|
||||
| your mailers below. You may also add additional mailers if needed.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "resend", "log", "array",
|
||||
| "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'transport' => 'resend',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all emails sent by your application to be sent from
|
||||
| the same address. Here you may specify a name and address that is
|
||||
| used globally for all emails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue supports a variety of backends via a single, unified
|
||||
| API, giving you convenient access to each backend using identical
|
||||
| syntax for each. The default queue connection is defined below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection options for every queue backend
|
||||
| used by your application. An example configuration is provided for
|
||||
| each backend supported by Laravel. You're also free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
|
||||
| "deferred", "background", "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||
'queue' => env('DB_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'deferred' => [
|
||||
'driver' => 'deferred',
|
||||
],
|
||||
|
||||
'background' => [
|
||||
'driver' => 'background',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'connections' => [
|
||||
'database',
|
||||
'deferred',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Batching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options configure the database and table that store job
|
||||
| batching information. These options can be updated to any database
|
||||
| connection and table which has been defined by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'batching' => [
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'job_batches',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control how and where failed jobs are stored. Laravel ships with
|
||||
| support for storing failed jobs in a simple file or in a database.
|
||||
|
|
||||
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'postmark' => [
|
||||
'key' => env('POSTMARK_API_KEY'),
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'key' => env('RESEND_API_KEY'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'notifications' => [
|
||||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines the default session driver that is utilized for
|
||||
| incoming requests. Laravel supports a variety of storage options to
|
||||
| persist session data. Database storage is a great default choice.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "memcached",
|
||||
| "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to expire immediately when the browser is closed then you may
|
||||
| indicate that via the expire_on_close configuration option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => (int) env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it's stored. All encryption is performed
|
||||
| automatically by Laravel and you may use the session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => env('SESSION_ENCRYPT', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the "file" session driver, the session files are placed
|
||||
| on disk. The default storage location is defined here; however, you
|
||||
| are free to provide another location where they should be stored.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table to
|
||||
| be used to store sessions. Of course, a sensible default is defined
|
||||
| for you; however, you're welcome to change this to another table.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => env('SESSION_TABLE', 'sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using one of the framework's cache driven session backends, you may
|
||||
| define the cache store which should be used to store the session data
|
||||
| between requests. This must match one of your defined cache stores.
|
||||
|
|
||||
| Affects: "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the session cookie that is created by
|
||||
| the framework. Typically, you should not need to change this value
|
||||
| since doing so does not grant a meaningful security improvement.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug((string) env('APP_NAME', 'laravel')).'-session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application, but you're free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('SESSION_PATH', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the domain and subdomains the session cookie is
|
||||
| available to. By default, the cookie will be available to the root
|
||||
| domain without subdomains. Typically, this shouldn't be changed.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. It's unlikely you should disable this option.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" to permit secure cross-site requests.
|
||||
|
|
||||
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||
|
||||
];
|
||||
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration')->index();
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Generated
+3532
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://www.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"axios": "^1.11.0",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^2.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"vite": "^7.0.7"
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory>tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="BROADCAST_CONNECTION" value="null"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
<env name="NIGHTWATCH_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,25 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews -Indexes
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Handle X-XSRF-Token Header
|
||||
RewriteCond %{HTTP:x-xsrf-token} .
|
||||
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
|
||||
|
||||
# Redirect Trailing Slashes If Not A Folder...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
||||
Whitespace-only changes.
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->handleRequest(Request::capture());
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,11 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import './bootstrap';
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
@@ -0,0 +1,478 @@
|
||||
@extends('docs.layout')
|
||||
|
||||
@section('title', 'Full Documentation - FiveM MCP Server')
|
||||
|
||||
@section('content')
|
||||
<style>
|
||||
.tab-button {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="max-w-5xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-white shadow rounded-lg p-8">
|
||||
<h1 class="text-3xl font-extrabold text-gray-900 mb-6">FiveM Development MCP Server</h1>
|
||||
|
||||
<p class="text-lg text-gray-700 mb-8">
|
||||
A comprehensive Model Context Protocol (MCP) server for FiveM (GTA5) development, built with Laravel MCP.
|
||||
This server provides AI assistants with powerful tools to help you develop FiveM resources more efficiently.
|
||||
</p>
|
||||
|
||||
<!-- Installation -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Installation & Connection</h2>
|
||||
<p class="text-gray-700 mb-6">Choose how you want to connect to the FiveM MCP server:</p>
|
||||
|
||||
<!-- Tab Buttons -->
|
||||
<div class="border-b border-gray-200 mb-6">
|
||||
<nav class="-mb-px flex space-x-8">
|
||||
<button
|
||||
data-tab="remote"
|
||||
class="tab-button active border-b-2 border-indigo-500 py-4 px-1 text-center text-sm font-medium text-indigo-600"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<span class="mr-2">🌐</span>
|
||||
Remote Server
|
||||
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">
|
||||
Recommended
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
data-tab="local"
|
||||
class="tab-button border-b-2 border-transparent py-4 px-1 text-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<span class="mr-2">💻</span>
|
||||
Local Installation
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content Container -->
|
||||
<div class="tab-content-container">
|
||||
<!-- Remote Server Option -->
|
||||
<div data-tab-content="remote" class="tab-content active">
|
||||
<div class="bg-indigo-50 border-l-4 border-indigo-500 p-6">
|
||||
<p class="text-gray-700 mb-4">
|
||||
<strong>Easy setup!</strong> Connect to a hosted MCP server. This is the easiest option and works from anywhere without any local setup.
|
||||
</p>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Claude Desktop Configuration:</h4>
|
||||
<p class="text-gray-700 mb-3">Edit your configuration file:</p>
|
||||
<ul class="list-disc list-inside text-gray-700 mb-3 ml-4">
|
||||
<li><strong>macOS:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">~/Library/Application Support/Claude/claude_desktop_config.json</code></li>
|
||||
<li><strong>Windows:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">%APPDATA%\Claude\claude_desktop_config.json</code></li>
|
||||
</ul>
|
||||
|
||||
<p class="text-gray-700 mb-3">Add this configuration (replace with your actual server URL):</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcpServers": {
|
||||
"fivem": {
|
||||
"url": "https://your-domain.com/mcp"
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">VSCode Configuration (Cline Extension):</h4>
|
||||
<p class="text-gray-700 mb-3">If using VSCode with the Cline extension, edit your settings:</p>
|
||||
<ul class="list-disc list-inside text-gray-700 mb-3 ml-4">
|
||||
<li>Open VSCode Settings (JSON)</li>
|
||||
<li>Add to your global or workspace <code class="bg-gray-100 px-2 py-1 rounded text-sm">settings.json</code></li>
|
||||
</ul>
|
||||
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"url": "https://your-domain.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Local Installation Option -->
|
||||
<div data-tab-content="local" class="tab-content hidden">
|
||||
<div class="bg-gray-50 border-l-4 border-gray-400 p-6">
|
||||
<p class="text-gray-700 mb-4">
|
||||
<strong>Advanced setup.</strong> Run the MCP server on your own machine. Requires PHP and Laravel setup.
|
||||
</p>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Claude Desktop Configuration:</h4>
|
||||
<p class="text-gray-700 mb-3">Edit your configuration file and add:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto mb-4"><code>{
|
||||
"mcpServers": {
|
||||
"fivem": {
|
||||
"command": "/path/to/php",
|
||||
"args": [
|
||||
"/path/to/fivem-mcp/artisan",
|
||||
"mcp:start",
|
||||
"fivem"
|
||||
]
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Command Line Usage:</h4>
|
||||
<p class="text-gray-700 mb-3">For any MCP-compatible client:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>php artisan mcp:start fivem</code></pre>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2 mt-6">VSCode Configuration (Cline Extension):</h4>
|
||||
<p class="text-gray-700 mb-3">If using VSCode with the Cline extension, add to <code class="bg-gray-100 px-2 py-1 rounded text-sm">settings.json</code>:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"command": "php",
|
||||
"args": ["artisan", "mcp:start", "fivem"],
|
||||
"cwd": "/path/to/fivem-mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-3 mt-4"><strong>Or use the full PHP path:</strong></p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"command": "/path/to/php",
|
||||
"args": ["/path/to/fivem-mcp/artisan", "mcp:start", "fivem"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-50 border-l-4 border-blue-500 p-4 mt-6">
|
||||
<p class="text-sm text-blue-800">
|
||||
<strong>💡 Tip:</strong> After configuring Claude Desktop, completely quit and restart the application for changes to take effect.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Available Tools -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-6">Available Tools</h2>
|
||||
|
||||
<!-- Tool 1: SearchFiveMDocs -->
|
||||
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">1. SearchFiveMDocs</h3>
|
||||
<p class="text-gray-700 mb-4">Search FiveM documentation for specific topics.</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">query</code> (required): Search query (e.g., "events", "natives", "manifest")</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">category</code> (optional): Filter by category (all, scripting, natives, networking, resources)</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example:</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Search FiveM docs for "network events"</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Tool 2: GetNativeFunction -->
|
||||
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">2. GetNativeFunction</h3>
|
||||
<p class="text-gray-700 mb-4">Look up GTA5/FiveM native functions.</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): Native function name (e.g., "GetPlayerPed", "SetEntityCoords")</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 Native Examples:</h4>
|
||||
<ul class="list-disc list-inside text-gray-700 mb-4 ml-4">
|
||||
<li>Common natives: <code class="bg-gray-100 px-2 py-1 rounded text-sm">GetPlayerPed</code>, <code class="bg-gray-100 px-2 py-1 rounded text-sm">SetEntityCoords</code>, <code class="bg-gray-100 px-2 py-1 rounded text-sm">GetEntityCoords</code></li>
|
||||
<li>CFX natives: <code class="bg-gray-100 px-2 py-1 rounded text-sm">RegisterNetEvent</code>, <code class="bg-gray-100 px-2 py-1 rounded text-sm">TriggerServerEvent</code>, <code class="bg-gray-100 px-2 py-1 rounded text-sm">TriggerClientEvent</code></li>
|
||||
<li>ESX: <code class="bg-gray-100 px-2 py-1 rounded text-sm">ESX.GetPlayerFromId</code></li>
|
||||
<li>QBCore: <code class="bg-gray-100 px-2 py-1 rounded text-sm">QBCore.Functions.GetPlayer</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Tool 3: GenerateManifest -->
|
||||
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">3. GenerateManifest</h3>
|
||||
<p class="text-gray-700 mb-4">Generate a fxmanifest.lua file.</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">resource_name</code> (required): Resource name</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">author</code> (optional): Author name</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">description</code> (optional): Resource description</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">version</code> (optional): Version number (default: "1.0.0")</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">script_type</code> (optional): lua or js (default: lua)</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">include_client</code> (optional): Include client scripts (default: true)</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">include_server</code> (optional): Include server scripts (default: true)</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">framework</code> (optional): standalone, esx, or qbcore (default: standalone)</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example:</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Generate a manifest for an ESX resource called "my-shop"</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Tool 4: GetEventReference -->
|
||||
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">4. GetEventReference</h3>
|
||||
<p class="text-gray-700 mb-4">Get information about FiveM events.</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, core, esx, qbcore)</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>Show me all core FiveM events</code></pre>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm mb-2"><code>Look up the playerConnecting event</code></pre>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>List all ESX events</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Tool 5: GenerateResourceBoilerplate -->
|
||||
<div class="mb-8 border-l-4 border-indigo-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">5. GenerateResourceBoilerplate</h3>
|
||||
<p class="text-gray-700 mb-4">Generate complete resource boilerplate code.</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">resource_name</code> (required): Resource name</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">framework</code> (optional): standalone, esx, or qbcore (default: standalone)</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">script_type</code> (optional): lua or js (default: lua)</li>
|
||||
<li><code class="bg-gray-100 px-2 py-1 rounded text-sm">include_nui</code> (optional): Include NUI files (default: false)</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example:</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Generate boilerplate for a QBCore resource with NUI called "admin-panel"</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Available Resources -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-6">Available Resources</h2>
|
||||
<p class="text-gray-700 mb-6">MCP Resources provide read-only reference documentation that AI assistants can access:</p>
|
||||
|
||||
<!-- Resource 1: Code Snippets -->
|
||||
<div class="mb-8 border-l-4 border-green-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">1. Code Snippets</h3>
|
||||
<p class="text-gray-700 mb-4">Common FiveM code patterns and snippets for quick reference.</p>
|
||||
<p class="text-gray-600 text-sm">URI: <code class="bg-gray-100 px-2 py-1 rounded text-sm">fivem://snippets/common</code></p>
|
||||
<p class="text-gray-700 mt-2">Includes patterns for distance checks, event handlers, threads with cleanup, database queries, and NUI callbacks in both Lua and JavaScript.</p>
|
||||
</div>
|
||||
|
||||
<!-- Resource 2: Best Practices -->
|
||||
<div class="mb-8 border-l-4 border-green-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">2. Best Practices</h3>
|
||||
<p class="text-gray-700 mb-4">FiveM development best practices and optimization tips.</p>
|
||||
<p class="text-gray-600 text-sm">URI: <code class="bg-gray-100 px-2 py-1 rounded text-sm">fivem://guides/best-practices</code></p>
|
||||
<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 -->
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Available Prompts -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-6">Available Prompts</h2>
|
||||
<p class="text-gray-700 mb-6">MCP Prompts are pre-built prompt templates for common tasks:</p>
|
||||
|
||||
<!-- Prompt 1: Create New Resource -->
|
||||
<div class="mb-8 border-l-4 border-purple-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">1. Create New Resource</h3>
|
||||
<p class="text-gray-700 mb-4">Create a new FiveM resource with framework and language selection.</p>
|
||||
<p class="text-gray-600 text-sm">Parameters: resource_name, framework (standalone/esx/qbcore), language (lua/js)</p>
|
||||
</div>
|
||||
|
||||
<!-- Prompt 2: Debug Issues -->
|
||||
<div class="mb-8 border-l-4 border-purple-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">2. Debug Issues</h3>
|
||||
<p class="text-gray-700 mb-4">Debug common FiveM issues and errors with detailed analysis.</p>
|
||||
<p class="text-gray-600 text-sm">Parameters: issue (description), error_message (from console)</p>
|
||||
</div>
|
||||
|
||||
<!-- Prompt 3: Optimize Performance -->
|
||||
<div class="mb-8 border-l-4 border-purple-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">3. Optimize Performance</h3>
|
||||
<p class="text-gray-700 mb-4">Optimize FiveM script performance with detailed suggestions.</p>
|
||||
<p class="text-gray-600 text-sm">Parameters: code (script to optimize), script_type (client/server/shared)</p>
|
||||
</div>
|
||||
|
||||
<!-- Prompt 4: Convert Language -->
|
||||
<div class="mb-8 border-l-4 border-purple-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">4. Convert Language</h3>
|
||||
<p class="text-gray-700 mb-4">Convert code between Lua and JavaScript with proper syntax.</p>
|
||||
<p class="text-gray-600 text-sm">Parameters: code, from (lua/js), to (lua/js)</p>
|
||||
</div>
|
||||
|
||||
<!-- Prompt 5: Add Framework Integration -->
|
||||
<div class="mb-8 border-l-4 border-purple-500 pl-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">5. Add Framework Integration</h3>
|
||||
<p class="text-gray-700 mb-4">Add ESX or QBCore framework integration to existing resource.</p>
|
||||
<p class="text-gray-600 text-sm">Parameters: code (current resource), current_framework, target_framework (esx/qbcore)</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Testing -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Testing the Server</h2>
|
||||
<p class="text-gray-700 mb-4">You can test the MCP server locally using the inspector:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>php artisan mcp:inspector fivem</code></pre>
|
||||
<p class="text-gray-600 text-sm mt-2">This will open the MCP Inspector in your browser where you can test all the tools interactively.</p>
|
||||
</section>
|
||||
|
||||
<!-- Development -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Development</h2>
|
||||
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Adding New Tools</h3>
|
||||
<p class="text-gray-700 mb-4">1. Create a new tool class in <code class="bg-gray-100 px-2 py-1 rounded text-sm">app/Mcp/Tools/</code>:</p>
|
||||
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto mb-4"><code><?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('Your tool description')]
|
||||
class YourTool extends Tool
|
||||
{
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
// Tool logic here
|
||||
return Response::text('Result');
|
||||
}
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'param' => $schema->string()->required(),
|
||||
];
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-4">2. Register the tool in <code class="bg-gray-100 px-2 py-1 rounded text-sm">app/Mcp/Servers/FiveMServer.php</code>:</p>
|
||||
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto mb-4"><code>protected array $tools = [
|
||||
// ... existing tools
|
||||
\App\Mcp\Tools\YourTool::class,
|
||||
];</code></pre>
|
||||
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3 mt-6">Code Formatting</h3>
|
||||
<p class="text-gray-700 mb-4">Format code with Laravel Pint:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>vendor/bin/pint</code></pre>
|
||||
</section>
|
||||
|
||||
<!-- Frameworks & Languages -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Supported Frameworks</h2>
|
||||
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li><strong>Standalone:</strong> Basic FiveM resources without framework dependencies</li>
|
||||
<li><strong>ESX:</strong> ESX Framework (es_extended)</li>
|
||||
<li><strong>QBCore:</strong> QBCore Framework (qb-core)</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4 mt-6">Scripting Languages</h2>
|
||||
<p class="text-gray-700">Both Lua and JavaScript are fully supported across all tools and generators.</p>
|
||||
</section>
|
||||
|
||||
<!-- Resources -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Resources</h2>
|
||||
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li><a href="https://docs.fivem.net/docs/" class="text-indigo-600 hover:text-indigo-500">FiveM Documentation</a></li>
|
||||
<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://laravel.com/docs/mcp" class="text-indigo-600 hover:text-indigo-500">Laravel MCP Documentation</a></li>
|
||||
<li><a href="https://modelcontextprotocol.io/" class="text-indigo-600 hover:text-indigo-500">Model Context Protocol</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Contributing -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Contributing</h2>
|
||||
<p class="text-gray-700 mb-4">Contributions are welcome! Areas for expansion:</p>
|
||||
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li>More native functions in the database</li>
|
||||
<li>Additional framework support (vRP, etc.)</li>
|
||||
<li>More event types</li>
|
||||
<li>Advanced code generators</li>
|
||||
<li>Database query builders</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- License -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">License</h2>
|
||||
<p class="text-gray-700">MIT License</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tabButtons = document.querySelectorAll('.tab-button');
|
||||
const tabContents = document.querySelectorAll('.tab-content');
|
||||
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetTab = this.dataset.tab;
|
||||
|
||||
// Remove active class from all buttons
|
||||
tabButtons.forEach(btn => {
|
||||
btn.classList.remove('active', 'border-indigo-500', 'text-indigo-600');
|
||||
btn.classList.add('border-transparent', 'text-gray-500');
|
||||
});
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.remove('border-transparent', 'text-gray-500');
|
||||
this.classList.add('active', 'border-indigo-500', 'text-indigo-600');
|
||||
|
||||
// Hide all tab contents
|
||||
tabContents.forEach(content => {
|
||||
content.classList.add('hidden');
|
||||
content.classList.remove('active');
|
||||
});
|
||||
|
||||
// Show target tab content
|
||||
const targetContent = document.querySelector(`[data-tab-content="${targetTab}"]`);
|
||||
if (targetContent) {
|
||||
targetContent.classList.remove('hidden');
|
||||
targetContent.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,200 @@
|
||||
@extends('docs.layout')
|
||||
|
||||
@section('title', 'FiveM MCP Server')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<!-- Hero Section -->
|
||||
<div class="text-center mb-12">
|
||||
<h1 class="text-4xl font-extrabold text-gray-900 sm:text-5xl sm:tracking-tight lg:text-6xl">
|
||||
FiveM Development MCP Server
|
||||
</h1>
|
||||
<p class="mt-5 max-w-xl mx-auto text-xl text-gray-500">
|
||||
A comprehensive Model Context Protocol server for FiveM development with 5 tools, 3 resources, and 5 prompts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Features Grid -->
|
||||
<div class="mt-12">
|
||||
<h2 class="text-3xl font-extrabold text-gray-900 mb-8">Features</h2>
|
||||
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<!-- Documentation Search -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Documentation Search</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Search FiveM documentation by topic and category. Get direct links to relevant documentation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Native Functions -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Native Functions</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Look up GTA5/FiveM native functions with signatures, parameters, and code examples.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manifest Generator -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Manifest Generator</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Generate fxmanifest.lua files automatically for any framework and scripting language.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Event Reference -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Event Reference</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Complete database of FiveM events including core, ESX, and QBCore events with examples.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Boilerplate Generator -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Boilerplate Generator</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Generate complete resource structures with client, server, config, and optional NUI files.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Framework Support -->
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 bg-indigo-500 rounded-md p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<h3 class="text-lg font-medium text-gray-900">Framework Support</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-500">
|
||||
Full support for Standalone, ESX, and QBCore frameworks in both Lua and JavaScript.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="mt-16 bg-indigo-700 rounded-lg shadow-xl overflow-hidden">
|
||||
<div class="px-6 py-12 sm:px-12">
|
||||
<div class="text-center">
|
||||
<h2 class="text-3xl font-extrabold text-white">Ready to get started?</h2>
|
||||
<p class="mt-4 text-lg text-indigo-100">
|
||||
Set up the MCP server in just 5 minutes with our quick start guide.
|
||||
</p>
|
||||
<div class="mt-8 flex justify-center space-x-4">
|
||||
<a href="{{ route('docs.quickstart') }}" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-indigo-700 bg-white hover:bg-indigo-50">
|
||||
Quick Start Guide
|
||||
</a>
|
||||
<a href="{{ route('docs.documentation') }}" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-500 hover:bg-indigo-600">
|
||||
Full Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resources -->
|
||||
<div class="mt-16">
|
||||
<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>
|
||||
<p class="text-sm text-gray-500 mb-4">
|
||||
Test the MCP server locally using the inspector:
|
||||
</p>
|
||||
<pre class="bg-gray-50 rounded p-4 text-sm overflow-x-auto"><code>php artisan mcp:inspector fivem</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>
|
||||
<ul class="space-y-2 text-sm">
|
||||
<li>
|
||||
<a href="https://docs.fivem.net/docs/" class="text-indigo-600 hover:text-indigo-500">FiveM Documentation</a>
|
||||
</li>
|
||||
<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://laravel.com/docs/mcp" class="text-indigo-600 hover:text-indigo-500">Laravel MCP Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://modelcontextprotocol.io/" class="text-indigo-600 hover:text-indigo-500">Model Context Protocol</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@yield('title', 'FiveM MCP Server Documentation')</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
<style>
|
||||
.code-block-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.copy-button {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
background-color: rgba(55, 65, 81, 0.8);
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease-in-out;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.code-block-wrapper:hover .copy-button {
|
||||
opacity: 1;
|
||||
}
|
||||
.copy-button:hover {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
}
|
||||
.copy-button.copied {
|
||||
background-color: rgba(16, 185, 129, 0.9);
|
||||
border-color: rgba(16, 185, 129, 1);
|
||||
}
|
||||
.copy-button svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="h-full bg-gray-50">
|
||||
<div class="min-h-full">
|
||||
<!-- Navigation -->
|
||||
<nav class="bg-white shadow-sm">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
<h1 class="text-xl font-bold text-gray-900">FiveM MCP Server</h1>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
<a href="{{ route('docs.index') }}" class="@if(request()->routeIs('docs.index')) border-indigo-500 text-gray-900 @else border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 @endif inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Home
|
||||
</a>
|
||||
<a href="{{ route('docs.quickstart') }}" class="@if(request()->routeIs('docs.quickstart')) border-indigo-500 text-gray-900 @else border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 @endif inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Quick Start
|
||||
</a>
|
||||
<a href="{{ route('docs.documentation') }}" class="@if(request()->routeIs('docs.documentation')) border-indigo-500 text-gray-900 @else border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 @endif inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main>
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-white border-t border-gray-200 mt-12">
|
||||
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||
<p class="text-center text-sm text-gray-500">
|
||||
Built with <a href="https://laravel.com/docs/mcp" class="text-indigo-600 hover:text-indigo-500">Laravel MCP</a> for <a href="https://docs.fivem.net" class="text-indigo-600 hover:text-indigo-500">FiveM</a> development
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Add copy buttons to all code blocks
|
||||
const codeBlocks = document.querySelectorAll('pre');
|
||||
|
||||
codeBlocks.forEach(function(pre) {
|
||||
// Wrap the pre in a div if not already wrapped
|
||||
if (!pre.parentElement.classList.contains('code-block-wrapper')) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'code-block-wrapper';
|
||||
pre.parentNode.insertBefore(wrapper, pre);
|
||||
wrapper.appendChild(pre);
|
||||
}
|
||||
|
||||
const wrapper = pre.parentElement;
|
||||
|
||||
// Create copy button
|
||||
const copyButton = document.createElement('button');
|
||||
copyButton.className = 'copy-button';
|
||||
copyButton.innerHTML = `
|
||||
<svg class="copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
<span class="copy-text">Copy</span>
|
||||
`;
|
||||
|
||||
copyButton.addEventListener('click', function() {
|
||||
const code = pre.querySelector('code');
|
||||
const text = code ? code.textContent : pre.textContent;
|
||||
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
// Show success state
|
||||
copyButton.classList.add('copied');
|
||||
copyButton.innerHTML = `
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span>Copied!</span>
|
||||
`;
|
||||
|
||||
// Reset after 2 seconds
|
||||
setTimeout(function() {
|
||||
copyButton.classList.remove('copied');
|
||||
copyButton.innerHTML = `
|
||||
<svg class="copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
<span class="copy-text">Copy</span>
|
||||
`;
|
||||
}, 2000);
|
||||
}).catch(function(err) {
|
||||
console.error('Failed to copy:', err);
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.appendChild(copyButton);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,397 @@
|
||||
@extends('docs.layout')
|
||||
|
||||
@section('title', 'Quick Start Guide - FiveM MCP Server')
|
||||
|
||||
@section('content')
|
||||
<style>
|
||||
.tab-button {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-white shadow rounded-lg p-8">
|
||||
<h1 class="text-3xl font-extrabold text-gray-900 mb-6">FiveM MCP Quick Start Guide</h1>
|
||||
|
||||
<!-- What is this -->
|
||||
<section class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">What is this?</h2>
|
||||
<p class="text-gray-700 mb-4">
|
||||
This MCP server gives AI assistants (like Claude) access to FiveM development tools including:
|
||||
</p>
|
||||
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li>Documentation search</li>
|
||||
<li>Native function lookups</li>
|
||||
<li>Manifest generation</li>
|
||||
<li>Event references</li>
|
||||
<li>Resource boilerplate generation</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Choose Setup Type -->
|
||||
<section class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Choose Your Setup</h2>
|
||||
|
||||
<!-- Tab Buttons -->
|
||||
<div class="border-b border-gray-200 mb-6">
|
||||
<nav class="-mb-px flex space-x-8">
|
||||
<button
|
||||
data-tab="remote"
|
||||
class="tab-button active border-b-2 border-indigo-500 py-4 px-1 text-center text-sm font-medium text-indigo-600"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<span class="mr-2">🌐</span>
|
||||
Remote Server
|
||||
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">
|
||||
Recommended
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
data-tab="local"
|
||||
class="tab-button border-b-2 border-transparent py-4 px-1 text-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<span class="mr-2">💻</span>
|
||||
Local Installation
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content Container -->
|
||||
<div class="tab-content-container">
|
||||
<!-- Remote Setup Tab -->
|
||||
<div data-tab-content="remote" class="tab-content active">
|
||||
<div class="bg-indigo-50 border-l-4 border-indigo-500 p-4 mb-6">
|
||||
<p class="text-sm text-indigo-800">
|
||||
<strong>Easy setup!</strong> Connect to a hosted MCP server. Works from anywhere without any local installation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 mb-6">
|
||||
If someone is hosting the FiveM MCP server for you, follow these steps to connect:
|
||||
</p>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">1. Get the Server URL</h3>
|
||||
<p class="text-gray-700 mb-3">
|
||||
Ask your server administrator for the MCP server URL. It will look like:
|
||||
</p>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>https://your-domain.com/mcp</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">2. Configure Claude Desktop</h3>
|
||||
<p class="text-gray-700 mb-3">Edit your Claude Desktop configuration file:</p>
|
||||
<ul class="list-disc list-inside text-gray-700 mb-3 ml-4">
|
||||
<li><strong>macOS:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">~/Library/Application Support/Claude/claude_desktop_config.json</code></li>
|
||||
<li><strong>Windows:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">%APPDATA%\Claude\claude_desktop_config.json</code></li>
|
||||
</ul>
|
||||
|
||||
<p class="text-gray-700 mb-3">Add this configuration (replace the URL with your actual server URL):</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcpServers": {
|
||||
"fivem": {
|
||||
"url": "https://your-domain.com/mcp"
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-3 mt-6"><strong>For VSCode (Cline Extension):</strong></p>
|
||||
<p class="text-gray-700 mb-3">Edit your VSCode <code class="bg-gray-100 px-2 py-1 rounded text-sm">settings.json</code>:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"url": "https://your-domain.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">3. Restart Claude Desktop</h3>
|
||||
<p class="text-gray-700">
|
||||
Completely quit and restart Claude Desktop for changes to take effect.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Step 4 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">4. Verify Connection</h3>
|
||||
<p class="text-gray-700">
|
||||
In Claude Desktop, you should see a small 🔌 icon or indication that the FiveM MCP server is connected.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-green-50 border-l-4 border-green-500 p-4">
|
||||
<p class="text-sm text-green-800">
|
||||
<strong>That's it!</strong> You're now connected to the remote MCP server and can start using FiveM development tools with Claude.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Local Setup Tab -->
|
||||
<div data-tab-content="local" class="tab-content hidden">
|
||||
<div class="bg-gray-50 border-l-4 border-gray-400 p-4 mb-6">
|
||||
<p class="text-sm text-gray-700">
|
||||
<strong>Advanced setup.</strong> Run the MCP server on your own machine. Requires PHP and Laravel installation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 mb-6">
|
||||
If you want to run the MCP server on your own machine:
|
||||
</p>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">1. Test the Server</h3>
|
||||
<p class="text-gray-700 mb-3">First, verify the server works:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>cd /path/to/fivem-mcp
|
||||
php artisan mcp:inspector fivem</code></pre>
|
||||
<p class="text-gray-600 text-sm mt-2">This opens a web inspector where you can test all tools.</p>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">2. Configure Claude Desktop</h3>
|
||||
<p class="text-gray-700 mb-3">Edit your Claude Desktop configuration file:</p>
|
||||
<ul class="list-disc list-inside text-gray-700 mb-3 ml-4">
|
||||
<li><strong>macOS:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">~/Library/Application Support/Claude/claude_desktop_config.json</code></li>
|
||||
<li><strong>Windows:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-sm">%APPDATA%\Claude\claude_desktop_config.json</code></li>
|
||||
</ul>
|
||||
|
||||
<p class="text-gray-700 mb-3">Add this configuration:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto mb-4"><code>{
|
||||
"mcpServers": {
|
||||
"fivem": {
|
||||
"command": "php",
|
||||
"args": [
|
||||
"artisan",
|
||||
"mcp:start",
|
||||
"fivem"
|
||||
],
|
||||
"cwd": "/path/to/fivem-mcp"
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-3"><strong>Or use the full PHP path (e.g., for Herd):</strong></p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcpServers": {
|
||||
"fivem": {
|
||||
"command": "/path/to/php",
|
||||
"args": [
|
||||
"/path/to/fivem-mcp/artisan",
|
||||
"mcp:start",
|
||||
"fivem"
|
||||
]
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-3 mt-6"><strong>For VSCode (Cline Extension):</strong></p>
|
||||
<p class="text-gray-700 mb-3">Edit your VSCode <code class="bg-gray-100 px-2 py-1 rounded text-sm">settings.json</code>:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto mb-4"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"command": "php",
|
||||
"args": ["artisan", "mcp:start", "fivem"],
|
||||
"cwd": "/path/to/fivem-mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<p class="text-gray-700 mb-3"><strong>Or with full paths:</strong></p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"fivem": {
|
||||
"command": "/path/to/php",
|
||||
"args": ["/path/to/fivem-mcp/artisan", "mcp:start", "fivem"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">3. Restart Claude Desktop</h3>
|
||||
<p class="text-gray-700">
|
||||
Completely quit and restart Claude Desktop for changes to take effect.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Step 4 -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">4. Verify Connection</h3>
|
||||
<p class="text-gray-700">
|
||||
In Claude Desktop, you should see a small 🔌 icon or indication that the FiveM MCP server is connected.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Quick Examples -->
|
||||
<section class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Quick Examples</h2>
|
||||
<p class="text-gray-700 mb-4">Once connected, try these prompts in Claude:</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="border-l-4 border-indigo-500 pl-4">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example 1: Search Documentation</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Search FiveM docs for "state bags"</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="border-l-4 border-indigo-500 pl-4">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example 2: Look Up a Native</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>What does the GetPlayerPed native function do? Show me an example.</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="border-l-4 border-indigo-500 pl-4">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example 3: Generate a Manifest</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Generate a fxmanifest.lua for my resource called "vehicle-shop" using ESX framework</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="border-l-4 border-indigo-500 pl-4">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example 4: List Events</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Show me all FiveM core server events</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="border-l-4 border-indigo-500 pl-4">
|
||||
<h4 class="font-semibold text-gray-900 mb-2">Example 5: Create Boilerplate</h4>
|
||||
<pre class="bg-gray-100 rounded p-3 text-sm"><code>Create a complete resource structure for a QBCore script called "bank-heist" with NUI</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Troubleshooting -->
|
||||
<section class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Troubleshooting</h2>
|
||||
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Server Not Connecting</h3>
|
||||
<ol class="list-decimal list-inside text-gray-700 space-y-3 ml-4">
|
||||
<li>
|
||||
<strong>Check PHP Path:</strong> Make sure the PHP path in your config is correct
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-3 mt-2 overflow-x-auto"><code>which php
|
||||
# or for Herd:
|
||||
ls -la "~/Library/Application Support/Herd/bin/"</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Test Manually:</strong> Run the server command directly
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-3 mt-2 overflow-x-auto"><code>cd /path/to/fivem-mcp
|
||||
php artisan mcp:start fivem</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Check Logs:</strong> Look at Claude Desktop logs
|
||||
<ul class="list-disc list-inside ml-6 mt-2">
|
||||
<li>macOS: <code class="bg-gray-100 px-2 py-1 rounded text-sm">~/Library/Logs/Claude/</code></li>
|
||||
<li>Windows: Check Event Viewer or app logs</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Permission Issues</h3>
|
||||
<p class="text-gray-700 mb-3">If you get permission errors:</p>
|
||||
<pre class="bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto"><code>cd /path/to/fivem-mcp
|
||||
chmod +x artisan</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Path Issues</h3>
|
||||
<p class="text-gray-700">
|
||||
Make sure to use <strong>absolute paths</strong> in your config, not relative paths.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Next Steps -->
|
||||
<section class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Next Steps</h2>
|
||||
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li><a href="{{ route('docs.documentation') }}" class="text-indigo-600 hover:text-indigo-500">Read the full documentation</a></li>
|
||||
<li>Explore all 5 tools in the MCP Inspector</li>
|
||||
<li>Try creating a complete FiveM resource with AI assistance</li>
|
||||
<li>Contribute new tools or improve existing ones</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Support -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-4">Support</h2>
|
||||
<p class="text-gray-700 mb-3">For issues:</p>
|
||||
<ol class="list-decimal list-inside text-gray-700 space-y-2 ml-4">
|
||||
<li>Check <a href="https://laravel.com/docs/mcp" class="text-indigo-600 hover:text-indigo-500">Laravel MCP Documentation</a></li>
|
||||
<li>Check <a href="https://docs.fivem.net/" class="text-indigo-600 hover:text-indigo-500">FiveM Documentation</a></li>
|
||||
<li>Review server logs</li>
|
||||
<li>Open a GitHub issue</li>
|
||||
</ol>
|
||||
<p class="text-gray-700 mt-6 text-lg font-semibold">Happy coding! 🎮</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tabButtons = document.querySelectorAll('.tab-button');
|
||||
const tabContents = document.querySelectorAll('.tab-content');
|
||||
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetTab = this.dataset.tab;
|
||||
|
||||
// Remove active class from all buttons
|
||||
tabButtons.forEach(btn => {
|
||||
btn.classList.remove('active', 'border-indigo-500', 'text-indigo-600');
|
||||
btn.classList.add('border-transparent', 'text-gray-500');
|
||||
});
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.remove('border-transparent', 'text-gray-500');
|
||||
this.classList.add('active', 'border-indigo-500', 'text-indigo-600');
|
||||
|
||||
// Hide all tab contents
|
||||
tabContents.forEach(content => {
|
||||
content.classList.add('hidden');
|
||||
content.classList.remove('active');
|
||||
});
|
||||
|
||||
// Show target tab content
|
||||
const targetContent = document.querySelector(`[data-tab-content="${targetTab}"]`);
|
||||
if (targetContent) {
|
||||
targetContent.classList.remove('hidden');
|
||||
targetContent.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,10 @@
|
||||
FiveM Events Reference
|
||||
==================================================
|
||||
|
||||
@foreach($eventsByType as $type => $events)
|
||||
{{ strtoupper($type) }} EVENTS:
|
||||
@foreach($events as $event)
|
||||
• {{ $event['name'] }} - {{ $event['description'] }}
|
||||
@endforeach
|
||||
|
||||
@endforeach
|
||||
@@ -0,0 +1,30 @@
|
||||
Event: {{ $event['name'] }}
|
||||
==================================================
|
||||
|
||||
Type: {{ $event['type'] }}
|
||||
Side: {{ $event['side'] }}
|
||||
|
||||
Description:
|
||||
{{ $event['description'] }}
|
||||
|
||||
@if(!empty($event['parameters']))
|
||||
Parameters:
|
||||
@foreach($event['parameters'] as $param)
|
||||
• {{ $param['name'] }} ({{ $param['type'] }}): {{ $param['description'] }}
|
||||
@endforeach
|
||||
|
||||
@endif
|
||||
@php
|
||||
$exampleKey = $language === 'js' ? 'js_example' : 'lua_example';
|
||||
$langName = $language === 'js' ? 'JavaScript' : 'Lua';
|
||||
@endphp
|
||||
@if(isset($event[$exampleKey]))
|
||||
{{ $langName }} Example:
|
||||
```{{ $language }}
|
||||
{{ $event[$exampleKey] }}
|
||||
```
|
||||
|
||||
@endif
|
||||
@if(isset($event['documentation']))
|
||||
Documentation: {{ $event['documentation'] }}
|
||||
@endif
|
||||
@@ -0,0 +1,32 @@
|
||||
Native Function: {{ $native['name'] }}
|
||||
==================================================
|
||||
|
||||
Namespace: {{ $native['namespace'] }}
|
||||
|
||||
Description:
|
||||
{{ $native['description'] }}
|
||||
|
||||
@if(!empty($native['parameters']))
|
||||
Parameters:
|
||||
@foreach($native['parameters'] as $param)
|
||||
• {{ $param['name'] }} ({{ $param['type'] }}): {{ $param['description'] }}
|
||||
@endforeach
|
||||
|
||||
@endif
|
||||
Returns: {{ $native['returns']['type'] }}
|
||||
@if($native['returns']['description'] !== 'No return value')
|
||||
{{ $native['returns']['description'] }}
|
||||
@endif
|
||||
|
||||
@php
|
||||
$exampleKey = $language === 'js' ? 'js_example' : 'lua_example';
|
||||
$langName = $language === 'js' ? 'JavaScript' : 'Lua';
|
||||
@endphp
|
||||
@if(isset($native[$exampleKey]))
|
||||
{{ $langName }} Example:
|
||||
```{{ $language }}
|
||||
{{ $native[$exampleKey] }}
|
||||
```
|
||||
|
||||
@endif
|
||||
Documentation: https://docs.fivem.net/natives/?_{{ $native['name'] }}
|
||||
@@ -0,0 +1,11 @@
|
||||
FiveM Resource Boilerplate: {{ $resourceName }}
|
||||
==================================================
|
||||
|
||||
Generated files:
|
||||
|
||||
@foreach($files as $filename => $content)
|
||||
📄 {{ $filename }}
|
||||
--------------------------------------------------
|
||||
{{ $content }}
|
||||
|
||||
@endforeach
|
||||
@@ -0,0 +1,258 @@
|
||||
# FiveM Development Best Practices
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### 1. Use Citizen.Wait() Efficiently
|
||||
**Bad:**
|
||||
```lua
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(0) -- Runs every frame!
|
||||
-- Code that doesn't need to run every frame
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```lua
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(1000) -- Runs once per second
|
||||
-- Code here
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### 2. Avoid GetPlayerPed(-1) in Loops
|
||||
**Bad:**
|
||||
```lua
|
||||
while true do
|
||||
local ped = GetPlayerPed(-1) -- Called every iteration
|
||||
local coords = GetEntityCoords(ped)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```lua
|
||||
local ped = PlayerPedId() -- Cache outside loop
|
||||
while true do
|
||||
local coords = GetEntityCoords(ped)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
```
|
||||
|
||||
### 3. Use Distance Checks Before Expensive Operations
|
||||
```lua
|
||||
local myCoords = GetEntityCoords(PlayerPedId())
|
||||
local distance = #(myCoords - targetCoords)
|
||||
|
||||
if distance < 100.0 then
|
||||
-- Only do expensive operations when close
|
||||
DrawMarker(...)
|
||||
end
|
||||
```
|
||||
|
||||
### 4. Batch Database Queries
|
||||
**Bad:**
|
||||
```lua
|
||||
for _, player in pairs(players) do
|
||||
MySQL.Async.fetchAll('SELECT * FROM users WHERE id = @id', {
|
||||
['@id'] = player.id
|
||||
})
|
||||
end
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```lua
|
||||
local ids = {}
|
||||
for _, player in pairs(players) do
|
||||
table.insert(ids, player.id)
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM users WHERE id IN (@ids)', {
|
||||
['@ids'] = table.concat(ids, ',')
|
||||
})
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### 1. Always Validate Server Events
|
||||
```lua
|
||||
RegisterNetEvent('myresource:giveMoney', function(amount)
|
||||
local _source = source
|
||||
|
||||
-- Validate source
|
||||
if not _source or _source < 1 then return end
|
||||
|
||||
-- Validate amount
|
||||
if type(amount) ~= 'number' or amount <= 0 or amount > 10000 then
|
||||
print(('Invalid amount from player %s'):format(_source))
|
||||
return
|
||||
end
|
||||
|
||||
-- Process only after validation
|
||||
Player(_source).addMoney(amount)
|
||||
end)
|
||||
```
|
||||
|
||||
### 2. Never Trust Client Data
|
||||
- All game-changing logic should be on the server
|
||||
- Validate all client requests
|
||||
- Use server-side checks for important operations
|
||||
|
||||
### 3. Don't Expose Admin Events
|
||||
**Bad:**
|
||||
```lua
|
||||
RegisterNetEvent('admin:giveWeapon') -- Client can trigger!
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```lua
|
||||
-- Use server commands instead
|
||||
RegisterCommand('giveweapon', function(source, args)
|
||||
if IsPlayerAceAllowed(source, 'admin.weapons') then
|
||||
-- Safe to execute
|
||||
end
|
||||
end, true)
|
||||
```
|
||||
|
||||
## Code Organization
|
||||
|
||||
### 1. Use Config Files
|
||||
```lua
|
||||
-- config.lua
|
||||
Config = {}
|
||||
Config.MaxDistance = 100.0
|
||||
Config.CheckInterval = 1000
|
||||
Config.EnableDebug = false
|
||||
|
||||
-- client.lua
|
||||
if distance < Config.MaxDistance then
|
||||
-- Easy to tweak without touching logic
|
||||
end
|
||||
```
|
||||
|
||||
### 2. Modularize Your Code
|
||||
```lua
|
||||
-- utils.lua
|
||||
function GetPlayerDistance(player1, player2)
|
||||
local coords1 = GetEntityCoords(GetPlayerPed(player1))
|
||||
local coords2 = GetEntityCoords(GetPlayerPed(player2))
|
||||
return #(coords1 - coords2)
|
||||
end
|
||||
|
||||
-- main.lua
|
||||
local distance = GetPlayerDistance(source, target)
|
||||
```
|
||||
|
||||
### 3. Use Proper Event Naming
|
||||
```lua
|
||||
-- Good naming convention
|
||||
RegisterNetEvent('resourcename:category:action')
|
||||
RegisterNetEvent('cardealer:purchase:vehicle')
|
||||
RegisterNetEvent('banking:account:withdraw')
|
||||
|
||||
-- Avoid generic names
|
||||
RegisterNetEvent('action') -- Too generic!
|
||||
RegisterNetEvent('event1') -- Not descriptive!
|
||||
```
|
||||
|
||||
## Resource Management
|
||||
|
||||
### 1. Clean Up After Yourself
|
||||
```lua
|
||||
AddEventHandler('onResourceStop', function(resource)
|
||||
if resource == GetCurrentResourceName() then
|
||||
-- Clean up threads, blips, entities, etc.
|
||||
isRunning = false
|
||||
RemoveBlip(myBlip)
|
||||
DeleteEntity(myVehicle)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### 2. Don't Create Unnecessary Entities
|
||||
```lua
|
||||
-- Delete vehicles when no longer needed
|
||||
if DoesEntityExist(vehicle) then
|
||||
DeleteEntity(vehicle)
|
||||
end
|
||||
|
||||
-- Clean up peds
|
||||
if DoesEntityExist(ped) then
|
||||
DeletePed(ped)
|
||||
end
|
||||
```
|
||||
|
||||
### 3. Manage Network Ownership
|
||||
```lua
|
||||
-- Set network IDs for entities you want to sync
|
||||
NetworkRegisterEntityAsNetworked(entity)
|
||||
local netId = NetworkGetNetworkIdFromEntity(entity)
|
||||
```
|
||||
|
||||
## Testing & Debugging
|
||||
|
||||
### 1. Use Conditional Debug Prints
|
||||
```lua
|
||||
if Config.Debug then
|
||||
print(('[DEBUG] Player %s at coords: %s'):format(source, coords))
|
||||
end
|
||||
```
|
||||
|
||||
### 2. Error Handling
|
||||
```lua
|
||||
local success, error = pcall(function()
|
||||
-- Code that might error
|
||||
MySQL.Async.fetchAll(query)
|
||||
end)
|
||||
|
||||
if not success then
|
||||
print(('Error: %s'):format(error))
|
||||
end
|
||||
```
|
||||
|
||||
### 3. Use Convars for Configuration
|
||||
```lua
|
||||
-- Set in server.cfg
|
||||
setr myresource:maxdistance "100"
|
||||
|
||||
-- Read in script
|
||||
local maxDistance = GetConvarInt('myresource:maxdistance', 50)
|
||||
```
|
||||
|
||||
## Framework-Specific Tips
|
||||
|
||||
### ESX
|
||||
```lua
|
||||
-- Always check if ESX is loaded
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
|
||||
-- Use ESX callbacks for server communication
|
||||
ESX.TriggerServerCallback('myresource:getData', function(data)
|
||||
print(data)
|
||||
end)
|
||||
```
|
||||
|
||||
### QBCore
|
||||
```lua
|
||||
-- Get QBCore object properly
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
-- Use QBCore functions
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
if Player then
|
||||
Player.Functions.AddMoney('cash', amount)
|
||||
end
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Using GetPlayerPed(-1) instead of PlayerPedId()** - Less efficient
|
||||
2. **Not cleaning up on resource restart** - Memory leaks
|
||||
3. **Trusting client-side coordinates** - Security risk
|
||||
4. **Running expensive operations every frame** - Performance issues
|
||||
5. **Not validating server events** - Exploits waiting to happen
|
||||
6. **Hardcoding values** - Use config files instead
|
||||
7. **Not using exports properly** - Creates tight coupling
|
||||
@@ -0,0 +1,178 @@
|
||||
# Common FiveM Code Snippets
|
||||
|
||||
Quick reference for frequently used code patterns.
|
||||
|
||||
## Player Distance Check
|
||||
|
||||
### Lua
|
||||
```lua
|
||||
-- Check distance between two players
|
||||
local ped1 = GetPlayerPed(source)
|
||||
local ped2 = GetPlayerPed(targetPlayer)
|
||||
local coords1 = GetEntityCoords(ped1)
|
||||
local coords2 = GetEntityCoords(ped2)
|
||||
local distance = #(coords1 - coords2)
|
||||
|
||||
if distance < 5.0 then
|
||||
-- Players are close enough
|
||||
end
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// Check distance between two players
|
||||
const ped1 = GetPlayerPed(source);
|
||||
const ped2 = GetPlayerPed(targetPlayer);
|
||||
const coords1 = GetEntityCoords(ped1, false);
|
||||
const coords2 = GetEntityCoords(ped2, false);
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(coords1[0] - coords2[0], 2) +
|
||||
Math.pow(coords1[1] - coords2[1], 2) +
|
||||
Math.pow(coords1[2] - coords2[2], 2)
|
||||
);
|
||||
|
||||
if (distance < 5.0) {
|
||||
// Players are close enough
|
||||
}
|
||||
```
|
||||
|
||||
## Safe Event Handler
|
||||
|
||||
### Lua
|
||||
```lua
|
||||
-- Server-side event with validation
|
||||
RegisterNetEvent('myresource:doSomething', function(data)
|
||||
local _source = source
|
||||
|
||||
-- Validate source
|
||||
if not _source or _source < 1 then return end
|
||||
|
||||
-- Validate data
|
||||
if type(data) ~= 'table' or not data.id then return end
|
||||
|
||||
-- Process event safely
|
||||
print(('Player %s triggered event with ID: %s'):format(_source, data.id))
|
||||
end)
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// Server-side event with validation
|
||||
onNet('myresource:doSomething', (data) => {
|
||||
const _source = source;
|
||||
|
||||
// Validate source
|
||||
if (!_source || _source < 1) return;
|
||||
|
||||
// Validate data
|
||||
if (typeof data !== 'object' || !data.id) return;
|
||||
|
||||
// Process event safely
|
||||
console.log(`Player ${_source} triggered event with ID: ${data.id}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Thread with Cleanup
|
||||
|
||||
### Lua
|
||||
```lua
|
||||
-- Citizen thread with proper cleanup
|
||||
local isRunning = true
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while isRunning do
|
||||
Citizen.Wait(0)
|
||||
|
||||
-- Your code here
|
||||
if IsControlJustPressed(0, 38) then -- E key
|
||||
print('E pressed!')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cleanup function
|
||||
AddEventHandler('onResourceStop', function(resource)
|
||||
if resource == GetCurrentResourceName() then
|
||||
isRunning = false
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// SetInterval with cleanup
|
||||
let isRunning = true;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
if (!isRunning) {
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Your code here
|
||||
if (IsControlJustPressed(0, 38)) { // E key
|
||||
console.log('E pressed!');
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// Cleanup
|
||||
on('onResourceStop', (resource) => {
|
||||
if (resource === GetCurrentResourceName()) {
|
||||
isRunning = false;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Database Query with Callback
|
||||
|
||||
### Lua
|
||||
```lua
|
||||
-- MySQL async query pattern
|
||||
MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
print(('Found user: %s'):format(result[1].name))
|
||||
else
|
||||
print('User not found')
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// oxmysql query pattern
|
||||
const result = await exports.oxmysql.query('SELECT * FROM users WHERE identifier = ?', [identifier]);
|
||||
|
||||
if (result.length > 0) {
|
||||
console.log(`Found user: ${result[0].name}`);
|
||||
} else {
|
||||
console.log('User not found');
|
||||
}
|
||||
```
|
||||
|
||||
## NUI Callback
|
||||
|
||||
### Lua
|
||||
```lua
|
||||
-- Client-side NUI callback handler
|
||||
RegisterNUICallback('buttonClick', function(data, cb)
|
||||
-- Process the callback
|
||||
print(('Button clicked: %s'):format(data.button))
|
||||
|
||||
-- Send response back to NUI
|
||||
cb({ success = true, message = 'Action completed' })
|
||||
end)
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
```javascript
|
||||
// Client-side NUI callback handler
|
||||
RegisterNUICallback('buttonClick', (data, cb) => {
|
||||
// Process the callback
|
||||
console.log(`Button clicked: ${data.button}`);
|
||||
|
||||
// Send response back to NUI
|
||||
cb({ success: true, message: 'Action completed' });
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,260 @@
|
||||
# FiveM Framework Comparison
|
||||
|
||||
## Overview
|
||||
|
||||
| Feature | Standalone | ESX | QBCore |
|
||||
|---------|-----------|-----|--------|
|
||||
| **Complexity** | Low | Medium | Medium-High |
|
||||
| **Player System** | None | Built-in | Built-in |
|
||||
| **Inventory** | None | Basic | Advanced |
|
||||
| **Database** | Optional | MySQL | MySQL/oxmysql |
|
||||
| **Jobs** | Manual | Built-in | Built-in |
|
||||
| **Community** | N/A | Large | Growing |
|
||||
| **Learning Curve** | Easy | Moderate | Moderate-Hard |
|
||||
|
||||
## Standalone
|
||||
|
||||
### When to Use
|
||||
- Simple resources (maps, MLOs, vehicles)
|
||||
- Custom systems from scratch
|
||||
- Learning FiveM basics
|
||||
- No framework dependencies needed
|
||||
|
||||
### Pros
|
||||
- Complete control
|
||||
- No framework overhead
|
||||
- Lightweight
|
||||
- No version conflicts
|
||||
|
||||
### Cons
|
||||
- Must build everything yourself
|
||||
- No standardized player system
|
||||
- Harder to integrate with other resources
|
||||
- More initial work
|
||||
|
||||
### Example Code
|
||||
```lua
|
||||
-- No framework, direct natives
|
||||
RegisterNetEvent('myresource:doSomething', function(data)
|
||||
local ped = GetPlayerPed(source)
|
||||
local coords = GetEntityCoords(ped)
|
||||
|
||||
TriggerClientEvent('myresource:response', source, {
|
||||
success = true,
|
||||
coords = coords
|
||||
})
|
||||
end)
|
||||
```
|
||||
|
||||
## ESX (es_extended)
|
||||
|
||||
### When to Use
|
||||
- Building roleplay servers
|
||||
- Need player accounts/jobs/inventory
|
||||
- Want established ecosystem
|
||||
- Prefer simpler structure
|
||||
|
||||
### Pros
|
||||
- Huge community and resources
|
||||
- Lots of free scripts available
|
||||
- Well-documented
|
||||
- Mature and stable
|
||||
- Simpler than QBCore
|
||||
|
||||
### Cons
|
||||
- Older codebase
|
||||
- Some performance issues
|
||||
- Less modern features
|
||||
- Can feel outdated
|
||||
|
||||
### Core Features
|
||||
- Player accounts and identifiers
|
||||
- Job system
|
||||
- Basic inventory
|
||||
- Money (cash, bank, black money)
|
||||
- Vehicle ownership
|
||||
- Society system (businesses)
|
||||
|
||||
### Example Code
|
||||
```lua
|
||||
-- Server-side ESX
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
|
||||
RegisterNetEvent('myresource:buyItem', function(itemName, amount)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
if xPlayer.getMoney() >= price then
|
||||
xPlayer.removeMoney(price)
|
||||
xPlayer.addInventoryItem(itemName, amount)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Client-side ESX
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
|
||||
RegisterNetEvent('esx:playerLoaded', function(playerData)
|
||||
PlayerData = playerData
|
||||
end)
|
||||
```
|
||||
|
||||
### Common ESX Functions
|
||||
```lua
|
||||
-- Server-side
|
||||
xPlayer.getMoney()
|
||||
xPlayer.addMoney(amount)
|
||||
xPlayer.removeMoney(amount)
|
||||
xPlayer.getAccount(account).money
|
||||
xPlayer.addAccountMoney(account, amount)
|
||||
xPlayer.removeAccountMoney(account, amount)
|
||||
xPlayer.getInventoryItem(item)
|
||||
xPlayer.addInventoryItem(item, count)
|
||||
xPlayer.removeInventoryItem(item, count)
|
||||
xPlayer.getJob()
|
||||
xPlayer.setJob(job, grade)
|
||||
|
||||
-- Client-side
|
||||
ESX.ShowNotification(msg)
|
||||
ESX.UI.Menu.Open(type, namespace, name, data, submit, cancel)
|
||||
ESX.Game.SpawnVehicle(model, coords, heading, cb)
|
||||
ESX.Game.DeleteVehicle(vehicle)
|
||||
```
|
||||
|
||||
## QBCore (qb-core)
|
||||
|
||||
### When to Use
|
||||
- Modern roleplay servers
|
||||
- Need advanced features
|
||||
- Want active development
|
||||
- Prefer modular design
|
||||
|
||||
### Pros
|
||||
- Modern codebase
|
||||
- Active development
|
||||
- Advanced inventory
|
||||
- Better performance optimization
|
||||
- More features out of box
|
||||
- Flexible metadata system
|
||||
|
||||
### Cons
|
||||
- Steeper learning curve
|
||||
- Smaller community than ESX
|
||||
- Fewer free resources
|
||||
- More complex structure
|
||||
- Breaking changes in updates
|
||||
|
||||
### Core Features
|
||||
- Advanced player management
|
||||
- Comprehensive inventory system
|
||||
- Metadata system
|
||||
- Gang system
|
||||
- Crafting system
|
||||
- Housing system
|
||||
- Vehicle keys
|
||||
- Stash system
|
||||
|
||||
### Example Code
|
||||
```lua
|
||||
-- Server-side QBCore
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
RegisterNetEvent('myresource:buyItem', function(itemName, amount)
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
|
||||
if Player.Functions.RemoveMoney('cash', price) then
|
||||
Player.Functions.AddItem(itemName, amount)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Client-side QBCore
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
|
||||
PlayerData = QBCore.Functions.GetPlayerData()
|
||||
end)
|
||||
```
|
||||
|
||||
### Common QBCore Functions
|
||||
```lua
|
||||
-- Server-side
|
||||
Player.Functions.AddMoney(type, amount)
|
||||
Player.Functions.RemoveMoney(type, amount)
|
||||
Player.Functions.GetMoney(type)
|
||||
Player.Functions.AddItem(item, amount, slot, info)
|
||||
Player.Functions.RemoveItem(item, amount, slot)
|
||||
Player.Functions.GetItemByName(item)
|
||||
Player.Functions.SetJob(job, grade)
|
||||
Player.Functions.SetGang(gang, grade)
|
||||
Player.Functions.SetMetaData(key, val)
|
||||
Player.Functions.GetMetaData(key)
|
||||
|
||||
-- Client-side
|
||||
QBCore.Functions.Notify(text, type, length)
|
||||
QBCore.Functions.DrawText(text, position)
|
||||
QBCore.Functions.SpawnVehicle(model, cb, coords, isnetworked)
|
||||
QBCore.Functions.DeleteVehicle(vehicle)
|
||||
QBCore.Functions.GetPlayerData()
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Standalone to ESX
|
||||
1. Add `@es_extended/locale.lua` to fxmanifest
|
||||
2. Add ESX dependency
|
||||
3. Replace custom player system with ESX.GetPlayerFromId()
|
||||
4. Convert money/inventory to ESX functions
|
||||
|
||||
### Standalone to QBCore
|
||||
1. Add QB dependency to fxmanifest
|
||||
2. Replace player system with QBCore.Functions.GetPlayer()
|
||||
3. Convert all money/inventory operations
|
||||
4. Update notifications to QBCore.Functions.Notify()
|
||||
|
||||
### ESX to QBCore
|
||||
Common conversions:
|
||||
|
||||
| ESX | QBCore |
|
||||
|-----|--------|
|
||||
| `ESX.GetPlayerFromId(source)` | `QBCore.Functions.GetPlayer(source)` |
|
||||
| `xPlayer.getMoney()` | `Player.Functions.GetMoney('cash')` |
|
||||
| `xPlayer.addMoney(amount)` | `Player.Functions.AddMoney('cash', amount)` |
|
||||
| `xPlayer.getInventoryItem(item)` | `Player.Functions.GetItemByName(item)` |
|
||||
| `ESX.ShowNotification(msg)` | `QBCore.Functions.Notify(msg)` |
|
||||
| `xPlayer.getJob()` | `Player.PlayerData.job` |
|
||||
| `xPlayer.identifier` | `Player.PlayerData.citizenid` |
|
||||
|
||||
## Which Should You Choose?
|
||||
|
||||
### Choose Standalone If:
|
||||
- Building simple resources
|
||||
- Learning FiveM development
|
||||
- Don't need player/economy system
|
||||
- Want maximum flexibility
|
||||
|
||||
### Choose ESX If:
|
||||
- Building traditional RP server
|
||||
- Want lots of compatible resources
|
||||
- Need stable, proven system
|
||||
- Prefer simpler structure
|
||||
- Large community support is priority
|
||||
|
||||
### Choose QBCore If:
|
||||
- Want modern features
|
||||
- Building advanced RP server
|
||||
- Need better performance
|
||||
- Want active development
|
||||
- Flexible enough to customize deeply
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
**Standalone**: Best (no framework overhead)
|
||||
**ESX**: Good (some overhead from player system)
|
||||
**QBCore**: Good-Very Good (optimized but more features)
|
||||
|
||||
## Conclusion
|
||||
|
||||
All three options are valid depending on your needs:
|
||||
- **Standalone** for simple/custom projects
|
||||
- **ESX** for traditional RP with maximum compatibility
|
||||
- **QBCore** for modern RP with advanced features
|
||||
|
||||
The "best" framework depends on your specific requirements, technical skill, and server goals.
|
||||
@@ -0,0 +1,8 @@
|
||||
FiveM Documentation Results for: {{ $query }}
|
||||
|
||||
@foreach($results as $result)
|
||||
📚 {{ $result['title'] }}
|
||||
URL: {{ $result['url'] }}
|
||||
{{ $result['description'] }}
|
||||
|
||||
@endforeach
|
||||
@@ -0,0 +1,31 @@
|
||||
// Client-side script
|
||||
|
||||
@if($framework === 'esx')
|
||||
let ESX = null;
|
||||
|
||||
on('esx:getSharedObject', (obj) => { ESX = obj; });
|
||||
|
||||
@elseif($framework === 'qbcore')
|
||||
const QBCore = exports['qb-core'].GetCoreObject();
|
||||
|
||||
@endif
|
||||
// Resource started
|
||||
on('onClientResourceStart', (resourceName) => {
|
||||
if (GetCurrentResourceName() !== resourceName) return;
|
||||
|
||||
console.log('Resource started on client');
|
||||
|
||||
// Your initialization code here
|
||||
});
|
||||
|
||||
// Example command
|
||||
RegisterCommand('test', () => {
|
||||
emit('chat:addMessage', {
|
||||
args: ['Script', 'Test command executed!']
|
||||
});
|
||||
}, false);
|
||||
|
||||
// Example thread
|
||||
setInterval(() => {
|
||||
// Your periodic code here
|
||||
}, 1000);
|
||||
@@ -0,0 +1,33 @@
|
||||
-- Client-side script
|
||||
|
||||
@if($framework === 'esx')
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
|
||||
@elseif($framework === 'qbcore')
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
@endif
|
||||
-- Resource started
|
||||
AddEventHandler('onClientResourceStart', function(resourceName)
|
||||
if GetCurrentResourceName() ~= resourceName then return end
|
||||
|
||||
print('Resource started on client')
|
||||
|
||||
-- Your initialization code here
|
||||
end)
|
||||
|
||||
-- Example command
|
||||
RegisterCommand('test', function()
|
||||
TriggerEvent('chat:addMessage', {
|
||||
args = { 'Script', 'Test command executed!' }
|
||||
})
|
||||
end, false)
|
||||
|
||||
-- Example thread
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(1000)
|
||||
|
||||
-- Your periodic code here
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,14 @@
|
||||
// Configuration
|
||||
const Config = {
|
||||
Debug: false,
|
||||
|
||||
// Add your config options here
|
||||
Options: {
|
||||
example: true
|
||||
}
|
||||
};
|
||||
|
||||
// Export for use in other files
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports('GetConfig', () => Config);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
Config = {}
|
||||
|
||||
Config.Debug = false
|
||||
|
||||
-- Add your config options here
|
||||
Config.Options = {
|
||||
example = true
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
fx_version 'cerulean'
|
||||
game 'gta5'
|
||||
|
||||
author '{{ $author }}'
|
||||
description '{{ $description }}'
|
||||
version '{{ $version }}'
|
||||
|
||||
@if($framework === 'esx')
|
||||
shared_script '@es_extended/imports.lua'
|
||||
|
||||
@elseif($framework === 'qbcore')
|
||||
shared_script '@qb-core/shared/locale.lua'
|
||||
|
||||
@endif
|
||||
shared_scripts {
|
||||
@if($framework === 'esx')
|
||||
'@es_extended/locale.lua',
|
||||
'locales/*.lua',
|
||||
@endif
|
||||
'config.{{ $scriptExt }}'
|
||||
}
|
||||
|
||||
@if($includeClient)
|
||||
client_scripts {
|
||||
'client/main.{{ $scriptExt }}'
|
||||
}
|
||||
|
||||
@endif
|
||||
@if($includeServer)
|
||||
server_scripts {
|
||||
@if($framework === 'esx')
|
||||
'@mysql-async/lib/MySQL.lua',
|
||||
@elseif($framework === 'qbcore')
|
||||
'@oxmysql/lib/MySQL.lua',
|
||||
@endif
|
||||
'server/main.{{ $scriptExt }}'
|
||||
}
|
||||
|
||||
@endif
|
||||
-- Uncomment if using NUI
|
||||
-- ui_page 'html/index.html'
|
||||
--
|
||||
-- files {
|
||||
-- 'html/index.html',
|
||||
-- 'html/style.css',
|
||||
-- 'html/script.js'
|
||||
-- }
|
||||
|
||||
-- dependencies {
|
||||
@if($framework === 'esx')
|
||||
-- 'es_extended'
|
||||
@elseif($framework === 'qbcore')
|
||||
-- 'qb-core'
|
||||
@endif
|
||||
-- }
|
||||
|
||||
lua54 'yes'
|
||||
@@ -0,0 +1,40 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FiveM NUI</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" style="display: none;">
|
||||
<div class="container">
|
||||
<h1>FiveM NUI Interface</h1>
|
||||
<p>Your UI content here</p>
|
||||
<button id="closeBtn">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
window.addEventListener('message', (event) => {
|
||||
const data = event.data;
|
||||
|
||||
if (data.action === 'show') {
|
||||
$('#app').fadeIn();
|
||||
} else if (data.action === 'hide') {
|
||||
$('#app').fadeOut();
|
||||
}
|
||||
});
|
||||
|
||||
$('#closeBtn').click(() => {
|
||||
$.post(`https://${{ '{GetParentResourceName()}' }}/close`, JSON.stringify({}));
|
||||
$('#app').fadeOut();
|
||||
});
|
||||
|
||||
$(document).keyup((e) => {
|
||||
if (e.key === 'Escape') {
|
||||
$.post(`https://${{ '{GetParentResourceName()}' }}/close`, JSON.stringify({}));
|
||||
$('#app').fadeOut();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
window.addEventListener('message', function(event) {
|
||||
local data = event.data
|
||||
|
||||
if data.action == 'show' then
|
||||
$('#app').fadeIn()
|
||||
elseif data.action == 'hide' then
|
||||
$('#app').fadeOut()
|
||||
end
|
||||
end)
|
||||
|
||||
$('#closeBtn').click(function()
|
||||
$.post('https://' .. GetParentResourceName() .. '/close', JSON.stringify({}))
|
||||
$('#app').fadeOut()
|
||||
end)
|
||||
|
||||
$(document).keyup(function(e)
|
||||
if e.key == 'Escape' then
|
||||
$.post('https://' .. GetParentResourceName() .. '/close', JSON.stringify({}))
|
||||
$('#app').fadeOut()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,37 @@
|
||||
// Server-side script
|
||||
|
||||
@if($framework === 'esx')
|
||||
const ESX = exports['es_extended'].getSharedObject();
|
||||
|
||||
@elseif($framework === 'qbcore')
|
||||
const QBCore = exports['qb-core'].GetCoreObject();
|
||||
|
||||
@endif
|
||||
// Resource started
|
||||
on('onServerResourceStart', (resourceName) => {
|
||||
if (GetCurrentResourceName() !== resourceName) return;
|
||||
|
||||
console.log('Resource started on server');
|
||||
|
||||
// Your initialization code here
|
||||
});
|
||||
|
||||
// Player connecting
|
||||
on('playerConnecting', (name, setKickReason, deferrals) => {
|
||||
const player = global.source;
|
||||
deferrals.defer();
|
||||
|
||||
// Check player here
|
||||
|
||||
deferrals.done();
|
||||
});
|
||||
|
||||
// Example server event
|
||||
onNet('resourceName:serverEvent', (data) => {
|
||||
const player = global.source;
|
||||
|
||||
console.log(`Received event from player ${{ '{player}' }}`);
|
||||
|
||||
// Trigger back to client
|
||||
emitNet('resourceName:clientEvent', player, { success: true });
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Server-side script
|
||||
|
||||
@if($framework === 'esx')
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
|
||||
@elseif($framework === 'qbcore')
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
@endif
|
||||
-- Resource started
|
||||
AddEventHandler('onServerResourceStart', function(resourceName)
|
||||
if GetCurrentResourceName() ~= resourceName then return end
|
||||
|
||||
print('Resource started on server')
|
||||
|
||||
-- Your initialization code here
|
||||
end)
|
||||
|
||||
-- Player connecting
|
||||
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
|
||||
local player = source
|
||||
deferrals.defer()
|
||||
|
||||
-- Check player here
|
||||
|
||||
deferrals.done()
|
||||
end)
|
||||
|
||||
-- Example server event
|
||||
RegisterNetEvent('resourceName:serverEvent', function(data)
|
||||
local player = source
|
||||
|
||||
print('Received event from player ' .. player)
|
||||
|
||||
-- Trigger back to client
|
||||
TriggerClientEvent('resourceName:clientEvent', player, { success = true })
|
||||
end)
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Mcp\Servers\FiveMServer;
|
||||
use Laravel\Mcp\Facades\Mcp;
|
||||
|
||||
Mcp::local('fivem', FiveMServer::class);
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote');
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\DocsController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', [DocsController::class, 'index'])->name('docs.index');
|
||||
Route::get('/quickstart', [DocsController::class, 'quickstart'])->name('docs.quickstart');
|
||||
Route::get('/documentation', [DocsController::class, 'documentation'])->name('docs.documentation');
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!private/
|
||||
!public/
|
||||
!.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,9 @@
|
||||
compiled.php
|
||||
config.php
|
||||
down
|
||||
events.scanned.php
|
||||
maintenance.php
|
||||
routes.php
|
||||
routes.scanned.php
|
||||
schedule-*
|
||||
services.json
|
||||
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!data/
|
||||
!.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user