> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel MCP

> Come integrare un server Model Context Protocol (MCP) in un'app Laravel. Definisci tool, resource e prompt con cui gli agenti AI possono interagire con la tua app.

## Cos'è MCP

Il **Model Context Protocol (MCP)** è una specifica che consente a client AI (Claude, Cursor, GitHub Copilot, ecc.) e alle applicazioni di comunicare tramite un protocollo standardizzato. Implementando un server MCP fai in modo che gli agenti AI possano accedere ai dati della tua app Laravel ed eseguire azioni.

<Info>
  Laravel MCP è il pacchetto ufficiale aggiunto in Laravel 13, `laravel/mcp`. Offre tutto il necessario per costruire server MCP.
</Info>

Un server MCP può offrire tre tipi di funzionalità:

| Funzionalità | Descrizione                                                                         |
| ------------ | ----------------------------------------------------------------------------------- |
| **Tool**     | Funzioni chiamabili dal client (ricerca, aggiornamento, chiamate API esterne, ecc.) |
| **Resource** | Dati e contesto letti dal client                                                    |
| **Prompt**   | Template di prompt riutilizzabili                                                   |

## Installazione

```shell theme={null}
composer require laravel/mcp
```

Poi:

```shell theme={null}
php artisan vendor:publish --tag=ai-routes
```

Viene creato `routes/ai.php` per registrare i server MCP.

## Creazione del server

```shell theme={null}
php artisan make:mcp-server WeatherServer
```

In `app/Mcp/Servers`:

```php theme={null}
<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
use Laravel\Mcp\Server;

#[Name('Weather Server')]
#[Version('1.0.0')]
#[Instructions('This server provides weather information and forecasts.')]
class WeatherServer extends Server
{
    protected array $tools = [
        // GetCurrentWeatherTool::class,
    ];

    protected array $resources = [
        // WeatherGuidelinesResource::class,
    ];

    protected array $prompts = [
        // DescribeWeatherPrompt::class,
    ];
}
```

### Registrazione

In `routes/ai.php` puoi registrarlo come **web** o **locale**.

#### Server web

Accessibile via POST HTTP. Adatto a client AI remoti e integrazioni web.

```php theme={null}
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class);
```

Con middleware:

```php theme={null}
Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);
```

#### Server locale

Comando Artisan. Adatto a client AI locali (es. Claude Desktop).

```php theme={null}
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('weather', WeatherServer::class);
```

<Tip>
  Di solito il client MCP avvia automaticamente il server locale: non serve eseguire manualmente `mcp:start`.
</Tip>

## Tool

I tool sono funzioni chiamabili dai client AI.

### Creare un tool

```shell theme={null}
php artisan make:mcp-tool CurrentWeatherTool
```

Registralo:

```php theme={null}
use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    protected array $tools = [
        CurrentWeatherTool::class,
    ];
}
```

Esempio:

```php theme={null}
<?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('Fetches the current weather forecast for a specified location.')]
class CurrentWeatherTool extends Tool
{
    public function handle(Request $request): Response
    {
        $location = $request->get('location');

        // Recupero dei dati meteo...

        return Response::text('The weather is sunny, 22°C.');
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'location' => $schema->string()
                ->description('The location to get the weather for.')
                ->required(),
        ];
    }
}
```

### Nome e titolo

Dedotti dal nome della classe (`CurrentWeatherTool` → `current-weather`, "Current Weather Tool"). Personalizzabili con `Name` e `Title`.

```php theme={null}
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;

#[Name('get-optimistic-weather')]
#[Title('Get Optimistic Weather Forecast')]
class CurrentWeatherTool extends Tool
{
    // ...
}
```

<Warning>
  La `Description` non è generata automaticamente ed è indispensabile perché l'AI capisca come usare il tool.
</Warning>

### Schema di input

```php theme={null}
public function schema(JsonSchema $schema): array
{
    return [
        'location' => $schema->string()
            ->description('The location to get the weather for.')
            ->required(),

        'units' => $schema->string()
            ->enum(['celsius', 'fahrenheit'])
            ->description('The temperature units to use.')
            ->default('celsius'),
    ];
}
```

### Schema di output

```php theme={null}
public function outputSchema(JsonSchema $schema): array
{
    return [
        'temperature' => $schema->number()
            ->description('Temperature in Celsius')
            ->required(),

        'conditions' => $schema->string()
            ->description('Weather conditions')
            ->required(),

        'humidity' => $schema->integer()
            ->description('Humidity percentage')
            ->required(),
    ];
}
```

### Validazione

```php theme={null}
public function handle(Request $request): Response
{
    $validated = $request->validate([
        'location' => 'required|string|max:100',
        'units' => 'in:celsius,fahrenheit',
    ], [
        'location.required' => 'You must specify a location. For example, "New York City" or "Tokyo".',
        'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.',
    ]);

    // Elabora con i dati validati...
}
```

<Tip>
  Alle validation errors i client AI riprovano seguendo il messaggio: rendilo chiaro e azionabile.
</Tip>

### Dependency injection

```php theme={null}
<?php

namespace App\Mcp\Tools;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    public function __construct(
        protected WeatherRepository $weather,
    ) {}

    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $location = $request->get('location');
        $forecast = $weather->getForecastFor($location);

        return Response::text("Forecast: {$forecast}");
    }
}
```

### Annotazioni

```php theme={null}
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[IsIdempotent]
#[IsReadOnly]
class CurrentWeatherTool extends Tool
{
    // ...
}
```

| Annotazione        | Descrizione                                          |
| ------------------ | ---------------------------------------------------- |
| `#[IsReadOnly]`    | Il tool non modifica l'ambiente                      |
| `#[IsDestructive]` | Il tool può eseguire aggiornamenti distruttivi       |
| `#[IsIdempotent]`  | Nessun side-effect con più chiamate con stessi input |
| `#[IsOpenWorld]`   | Il tool può interagire con entità esterne            |

### Registrazione condizionale

```php theme={null}
public function shouldRegister(Request $request): bool
{
    return $request?->user()?->subscribed() ?? false;
}
```

### Response

<AccordionGroup>
  <Accordion title="Testo">
    ```php theme={null}
    return Response::text('Weather Summary: Sunny, 22°C');
    ```
  </Accordion>

  <Accordion title="Errore">
    ```php theme={null}
    return Response::error('Unable to fetch weather data. Please try again.');
    ```
  </Accordion>

  <Accordion title="Immagini/audio">
    ```php theme={null}
    return Response::image(file_get_contents(storage_path('weather/radar.png')), 'image/png');

    return Response::audio(file_get_contents(storage_path('weather/alert.mp3')), 'audio/mp3');

    return Response::fromStorage('weather/radar.png');
    ```
  </Accordion>

  <Accordion title="Contenuti multipli">
    ```php theme={null}
    public function handle(Request $request): array
    {
        return [
            Response::text('Weather Summary: Sunny, 22°C'),
            Response::text("**Detailed Forecast**\n- Morning: 18°C\n- Afternoon: 25°C"),
        ];
    }
    ```
  </Accordion>

  <Accordion title="Response strutturata">
    ```php theme={null}
    return Response::structured([
        'temperature' => 22.5,
        'conditions' => 'Partly cloudy',
        'humidity' => 65,
    ]);
    ```
  </Accordion>

  <Accordion title="Streaming">
    ```php theme={null}
    public function handle(Request $request): Generator
    {
        $locations = $request->array('locations');

        foreach ($locations as $index => $location) {
            yield Response::notification('processing/progress', [
                'current' => $index + 1,
                'total' => count($locations),
                'location' => $location,
            ]);

            yield Response::text($this->forecastFor($location));
        }
    }
    ```
  </Accordion>
</AccordionGroup>

## Prompt

Prompt riutilizzabili.

### Creare un prompt

```shell theme={null}
php artisan make:mcp-prompt DescribeWeatherPrompt
```

```php theme={null}
use App\Mcp\Prompts\DescribeWeatherPrompt;

class WeatherServer extends Server
{
    protected array $prompts = [
        DescribeWeatherPrompt::class,
    ];
}
```

### Argomenti

```php theme={null}
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;

class DescribeWeatherPrompt extends Prompt
{
    public function arguments(): array
    {
        return [
            new Argument(
                name: 'tone',
                description: 'The tone to use in the weather description (e.g., formal, casual, humorous).',
                required: true,
            ),
        ];
    }
}
```

### Validazione

```php theme={null}
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;

class DescribeWeatherPrompt extends Prompt
{
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'tone' => 'required|string|max:50',
        ]);

        $tone = $validated['tone'];

        // Genera il prompt con il tono...
    }
}
```

Messaggi personalizzati:

```php theme={null}
$validated = $request->validate([
    'tone' => ['required', 'string', 'max:50'],
], [
    'tone.*' => 'Specifica un tono. Esempio: "formal", "casual", "humorous".',
]);
```

### Dependency injection

```php theme={null}
<?php

namespace App\Mcp\Prompts;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Prompt;

class DescribeWeatherPrompt extends Prompt
{
    public function __construct(
        protected WeatherRepository $weather,
    ) {}
}
```

```php theme={null}
public function handle(Request $request, WeatherRepository $weather): Response
{
    $isAvailable = $weather->isServiceAvailable();

    // ...
}
```

### Registrazione condizionale

```php theme={null}
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Prompt;

class CurrentWeatherPrompt extends Prompt
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}
```

### Response del prompt

Messaggi utente e assistente con `asAssistant()`.

```php theme={null}
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;

class DescribeWeatherPrompt extends Prompt
{
    public function handle(Request $request): array
    {
        $tone = $request->string('tone');

        $systemMessage = "You are a helpful weather assistant. Please provide a weather description in a {$tone} tone.";
        $userMessage = 'What is the current weather like in Tokyo?';

        return [
            Response::text($systemMessage)->asAssistant(),
            Response::text($userMessage),
        ];
    }
}
```

## Resource

Dati/informazioni che il client legge come contesto.

### Creare una resource

```shell theme={null}
php artisan make:mcp-resource WeatherGuidelinesResource
```

```php theme={null}
use App\Mcp\Resources\WeatherGuidelinesResource;

class WeatherServer extends Server
{
    protected array $resources = [
        WeatherGuidelinesResource::class,
    ];
}
```

```php theme={null}
<?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('Comprehensive guidelines for using the Weather API.')]
class WeatherGuidelinesResource extends Resource
{
    public function handle(Request $request): Response
    {
        $guidelines = "# Weather API Guidelines\n\n- Always specify a location...";

        return Response::text($guidelines);
    }
}
```

### URI e MIME type

Default: URI generato dal nome della classe (`weather://resources/weather-guidelines`). Personalizza con `Uri` e `MimeType`.

```php theme={null}
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Attributes\Uri;
use Laravel\Mcp\Server\Resource;

#[Uri('weather://resources/guidelines')]
#[MimeType('application/pdf')]
class WeatherGuidelinesResource extends Resource
{
    // ...
}
```

### Template di URI

Con `HasUriTemplate`:

```php theme={null}
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Contracts\HasUriTemplate;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Support\UriTemplate;

#[Description('Access user files by ID')]
#[MimeType('text/plain')]
class UserFileResource extends Resource implements HasUriTemplate
{
    public function uriTemplate(): UriTemplate
    {
        return new UriTemplate('file://users/{userId}/files/{fileId}');
    }

    public function handle(Request $request): Response
    {
        $userId = $request->get('userId');
        $fileId = $request->get('fileId');

        return Response::text("File {$fileId} for user {$userId}");
    }
}
```

Le variabili dall'URI sono disponibili con `get`.

### Richiesta della resource

Le resource non hanno schema di input né argomenti, ma puoi accedere alle info della richiesta.

```php theme={null}
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    public function handle(Request $request): Response
    {
        // Info della richiesta...
    }
}
```

### DI nelle resource

```php theme={null}
<?php

namespace App\Mcp\Resources;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    public function __construct(
        protected WeatherRepository $weather,
    ) {}
}
```

```php theme={null}
public function handle(WeatherRepository $weather): Response
{
    return Response::text($weather->guidelines());
}
```

### Annotazioni

```php theme={null}
use Laravel\Mcp\Enums\Role;
use Laravel\Mcp\Server\Annotations\Audience;
use Laravel\Mcp\Server\Annotations\LastModified;
use Laravel\Mcp\Server\Annotations\Priority;
use Laravel\Mcp\Server\Resource;

#[Audience(Role::User)]
#[LastModified('2025-01-12T15:00:58Z')]
#[Priority(0.9)]
class UserDashboardResource extends Resource
{
    // ...
}
```

| Annotazione       | Tipo         | Descrizione                                                |
| ----------------- | ------------ | ---------------------------------------------------------- |
| `#[Audience]`     | Role o array | Destinatario (`Role::User`, `Role::Assistant`, o entrambi) |
| `#[Priority]`     | float        | Importanza (0.0–1.0)                                       |
| `#[LastModified]` | string       | Data ISO 8601                                              |

### Registrazione condizionale

```php theme={null}
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}
```

### Response di una resource

```php theme={null}
return Response::text($weatherData);
```

#### Resource link

```php theme={null}
return Response::resourceLink(
    uri: 'file:///data/report.json',
    name: 'monthly-report',
    mimeType: 'application/json',
);
```

Passando una classe registrata eredita URI, nome, titolo, descrizione, MIME.

```php theme={null}
return Response::resourceLink(new WeatherForecastResource);
```

#### Blob

```php theme={null}
return Response::blob(file_get_contents(storage_path('weather/radar.png')));
```

```php theme={null}
#[MimeType('image/png')]
class WeatherGuidelinesResource extends Resource
{
    // ...
}
```

#### Errore

```php theme={null}
return Response::error('Impossibile ottenere i dati meteo per il luogo richiesto.');
```

## App

Laravel MCP supporta le [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview): estensione del protocollo per applicazioni HTML interattive renderizzate in iframe sandbox all'interno dell'host. Puoi costruire dashboard, form, visualizzazioni.

Un'app MCP ha due parti:

* **App resource** — restituisce l'HTML autonomo
* **Tool** — collegato alla resource con l'attribute `#[RendersApp]`

### Creare una app resource

```shell theme={null}
php artisan make:mcp-app-resource WeatherDashboardApp
```

Crea due file: PHP in `app/Mcp/Resources` e vista Blade in `resources/views/mcp` (dedotta dal nome della classe; `WeatherDashboardApp` → `mcp.weather-dashboard-app`).

```php theme={null}
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;

#[Description('An interactive weather dashboard.')]
#[AppMeta]
class WeatherDashboardApp extends AppResource
{
    public function handle(Request $request): Response
    {
        return Response::view('mcp.weather-dashboard-app', [
            'title' => $this->title(),
        ]);
    }
}
```

`AppResource` estende `Resource` e configura URI `ui://` e MIME `text/html;profile=mcp-app`. Registrala nel server come le altre.

La vista Blade usa `<x-mcp::app>`, che include l'SDK client-side MCP.

```blade theme={null}
<x-mcp::app :title="$title">
    <x-slot:head>
        <script type="module">
        createMcpApp(async (app) => {
            document.getElementById('run-btn').addEventListener('click', async () => {
                const result = await app.callServerTool('get-weather-data', {});
                document.getElementById('output').textContent = result.content[0]?.text ?? '';
            });
        });
        </script>
    </x-slot:head>

    <div id="app">
        <button id="run-btn">Refresh</button>
        <p id="output"></p>
    </div>
</x-mcp::app>
```

`createMcpApp` è fornita dall'SDK. Gestisce connessione dell'iframe, tema dell'host, helper come `callServerTool`, `sendMessage`, `openLink`. Vedi le [specifiche MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview).

### Renderizzare l'app da un tool

Con `#[RendersApp]`:

```php theme={null}
<?php

namespace App\Mcp\Tools;

use App\Mcp\Resources\WeatherDashboardApp;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Tool;

#[RendersApp(resource: WeatherDashboardApp::class)]
class ShowWeatherDashboard extends Tool
{
    public function handle(Request $request): Response
    {
        return Response::text('Weather dashboard loaded.');
    }
}
```

<Info>
  Con una `AppResource` registrata, Laravel MCP annuncia automaticamente la capability `io.modelcontextprotocol/ui`.
</Info>

### Visibilità dei tool app

Con `visibility` limiti chi può chiamare il tool. Utile per tool riservati all'UI dell'app.

```php theme={null}
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Ui\Enums\Visibility;

#[RendersApp(resource: WeatherDashboardApp::class, visibility: [Visibility::App])]
class GetWeatherData extends Tool
{
    // ...
}
```

L'enum `Visibility` ha `Model` e `App` (default: entrambi). `[Visibility::App]` per backend usati dall'UI; `[Visibility::Model]` per nascondere all'UI.

### Configurazione dell'app

Con `#[AppMeta]` configuri CSP dell'iframe, permessi del browser, script di libreria da includere nel `<head>`.

```php theme={null}
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Ui\Enums\Library;
use Laravel\Mcp\Server\Ui\Enums\Permission;

#[AppMeta(
    connectDomains: ['https://api.weather.com'],
    permissions: [Permission::Geolocation],
    libraries: [Library::Tailwind, Library::Alpine],
)]
class WeatherDashboardApp extends AppResource
{
    // ...
}
```

`Library::Tailwind`, `Library::Alpine` ecc. hanno gli script CDN preimpostati; le origini vanno automaticamente in CSP. `Permission::Camera`, `Microphone`, `Geolocation`, `ClipboardWrite` per i permessi.

<Tip>
  Per config dinamica, sovrascrivi `appMeta` sulla resource usando i builder fluenti `AppMeta`, `Csp`, `Permissions` in `Laravel\Mcp\Server\Ui`.
</Tip>

### Sviluppo con Boost

Laravel MCP include un riferimento di [Boost](/it/boost) `mcp-development` per costruire MCP Apps. L'agente può generare automaticamente app resource, viste Blade e tool collegati.

Per il riferimento completo del protocollo consulta la [documentazione ufficiale MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview).

## Metadata

Puoi aggiungere `_meta` alle response.

```php theme={null}
return Response::text('The weather is sunny.')
    ->withMeta(['source' => 'weather-api', 'cached' => true]);
```

All'envelope della response:

```php theme={null}
return Response::make(
    Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
```

Metadata di classe con `$meta`:

```php theme={null}
class CurrentWeatherTool extends Tool
{
    protected ?array $meta = [
        'version' => '2.0',
        'author' => 'Weather Team',
    ];
}
```

## Icone

Con `Icon` puoi dichiarare icone su server, tool, resource, prompt.

```php theme={null}
use Laravel\Mcp\Enums\IconTheme;
use Laravel\Mcp\Server\Attributes\Icon;

#[Icon('mcp/server.png', mimeType: 'image/png', sizes: ['48x48'])]
#[Icon('mcp/server-dark.svg', theme: IconTheme::Dark)]
class WeatherServer extends Server
{
    // ...
}
```

L'attribute è ripetibile: puoi fornire varianti di dimensione e tema.

Oppure sovrascrivi `icons()` per definire icone in modo programmatico.

```php theme={null}
use Laravel\Mcp\Schema\Icon;

class CurrentWeatherTool extends Tool
{
    /**
     * @return array<int, Icon>
     */
    public function icons(): array
    {
        return [
            Icon::from('mcp/tool.png', mimeType: 'image/png'),
        ];
    }
}
```

Icone da attribute e metodo si uniscono. I path si risolvono così:

* URI (`https:`, `data:`) restano invariati
* Path relativi passano dall'helper `asset` di Laravel

## Autenticazione

I server web possono usare i middleware standard di Laravel.

### Sanctum

```php theme={null}
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware('auth:sanctum');
```

### OAuth 2.1

Con Passport:

```php theme={null}
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware('auth:api');
```

Pubblica le viste e configurale:

```shell theme={null}
php artisan vendor:publish --tag=mcp-views
```

```php theme={null}
use Laravel\Passport\Passport;

Passport::authorizationView(function ($parameters) {
    return view('mcp.authorize', $parameters);
});
```

## Autorizzazione

```php theme={null}
public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('Permission denied.');
    }

    // ...
}
```

## Client MCP

Oltre al server, Laravel MCP offre un client per connettersi ad altri server MCP e usare i tool esposti. Utile per fornire tool esterni ai propri [agenti AI](/it/ai-sdk#tool-mcp).

### Connessione

HTTP:

```php theme={null}
use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com');
```

Locale:

```php theme={null}
use Laravel\Mcp\Client;

$client = Client::local('php', ['artisan', 'mcp:start']);
```

Il client fa lazy connect. Puoi gestire la connessione:

```php theme={null}
$client->connect();

$client->ping();

if ($client->connected()) {
    // ...
}

$client->disconnect();
```

Timeout:

```php theme={null}
$client = Client::web('https://mcp.example.com')->withTimeout(30);
```

### Client con nome

Registra client riutilizzabili:

```php theme={null}
use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com'));
```

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$client = Mcp::client('github');
```

I client con nome sono risolti una volta per richiesta e disconnessi automaticamente a fine ciclo.

### Autenticazione del client

Bearer token:

```php theme={null}
use Illuminate\Support\Facades\Auth;
use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com')->withToken($token);

$client = Client::web('https://mcp.example.com')->withToken(
    fn () => Auth::user()->mcpToken(),
);
```

OAuth 2.1:

```php theme={null}
use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com')->withOAuth(
    clientId: config('services.github_mcp.client_id'),
    clientSecret: config('services.github_mcp.client_secret'),
));
```

<Info>
  Se il server MCP supporta la [Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591), puoi omettere `clientId` e `clientSecret`.
</Info>

Registra le route OAuth in `routes/ai.php` con `oAuthRoutesFor`. La closure riceve il nome del client e il `TokenSet` dopo lo scambio del codice.

```php theme={null}
use Illuminate\Support\Facades\Auth;
use Laravel\Mcp\Client\OAuth\TokenSet;
use Laravel\Mcp\Facades\Mcp;

Mcp::oAuthRoutesFor('github', function (string $client, TokenSet $token) {
    Auth::user()->update([
        'github_mcp_token' => $token->accessToken,
    ]);

    return redirect('/dashboard');
});
```

Vengono create due route con nome: `mcp.oauth.{client}.connect` (redirect al server di autorizzazione) e `mcp.oauth.{client}.callback` (scambio del codice, chiamata all'handler). Usano il middleware `web` (sovrascrivibile via argomento `middleware`).

Per iniziare il flow:

```php theme={null}
return redirect()->route('mcp.oauth.github.connect');
```

### Tool

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$tools = Mcp::client('github')->tools();

foreach ($tools as $tool) {
    $tool->name;
    $tool->title;
    $tool->description;
    $tool->inputSchema;
}
```

Paginazione gestita automaticamente. Limita con `limit`:

```php theme={null}
$tools = Mcp::client('github')->tools(limit: 10);
```

Chiamata:

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->callTool('current-weather', [
    'location' => 'New York',
]);

$result->text();
(string) $result;
$result->isError;
$result->structuredContent;
```

Dall'istanza del tool:

```php theme={null}
$tools = Mcp::client('github')->tools();

$result = $tools['current-weather']->call([
    'location' => 'New York',
]);
```

Se usi il [Laravel AI SDK](/it/ai-sdk), passa i tool del client direttamente all'agente: [Tool MCP](/it/ai-sdk#tool-mcp).

### Prompt

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$prompts = Mcp::client('github')->prompts();

foreach ($prompts as $prompt) {
    $prompt->name;
    $prompt->title;
    $prompt->description;
    $prompt->arguments;
}
```

Limita con `limit`:

```php theme={null}
$prompts = Mcp::client('github')->prompts(limit: 10);
```

Recupero:

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->getPrompt('describe-weather', [
    'location' => 'New York',
]);

$result->text();
(string) $result;
$result->messages;
$result->description;
```

### Resource

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$resources = Mcp::client('github')->resources();

foreach ($resources as $resource) {
    $resource->uri;
    $resource->name;
    $resource->title;
    $resource->description;
    $resource->mimeType;
    $resource->size;
}
```

```php theme={null}
$resources = Mcp::client('github')->resources(limit: 10);
```

Lettura:

```php theme={null}
use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->readResource('weather://guidelines');

$result->content();
(string) $result;
$result->mimeType();
$result->contents;
```

## Test

### MCP Inspector

```shell theme={null}
# Server web
php artisan mcp:inspector mcp/weather

# Server locale (nome "weather")
php artisan mcp:inspector weather
```

Copia la config del client. Se hai autenticazione, includi l'header Authorization.

### Test unitari

<CodeGroup>
  ```php Pest theme={null}
  test('tool', function () {
      $response = WeatherServer::tool(CurrentWeatherTool::class, [
          'location' => 'Tokyo',
          'units' => 'celsius',
      ]);

      $response
          ->assertOk()
          ->assertSee('The current weather in Tokyo is 22°C and sunny.');
  });
  ```

  ```php PHPUnit theme={null}
  public function test_tool(): void
  {
      $response = WeatherServer::tool(CurrentWeatherTool::class, [
          'location' => 'Tokyo',
          'units' => 'celsius',
      ]);

      $response
          ->assertOk()
          ->assertSee('The current weather in Tokyo is 22°C and sunny.');
  }
  ```
</CodeGroup>

Prompt e resource:

```php theme={null}
$response = WeatherServer::prompt(DescribeWeatherPrompt::class, ['tone' => 'casual']);
$response = WeatherServer::resource(WeatherGuidelinesResource::class);
```

Come utente autenticato:

```php theme={null}
$response = WeatherServer::actingAs($user)->tool(CurrentWeatherTool::class, [...]);
```

Asserzioni principali:

```php theme={null}
$response->assertOk();
$response->assertSee('...');
```

Errori:

```php theme={null}
$response->assertHasErrors();

$response->assertHasErrors([
    'Something went wrong.',
]);

$response->assertHasNoErrors();
```

Nome, titolo, descrizione:

```php theme={null}
$response->assertName('current-weather');
$response->assertTitle('Current Weather Tool');
$response->assertDescription('Fetches the current weather forecast for a specified location.');
```

Notifiche di streaming:

```php theme={null}
$response->assertSentNotification('processing/progress', [
    'step' => 1,
    'total' => 5,
]);

$response->assertSentNotification('processing/progress', [
    'step' => 2,
    'total' => 5,
]);

$response->assertNotificationCount(5);
```

Debug:

```php theme={null}
$response->dd();
$response->dump();
```


## Related topics

- [Creare un server MCP con Laravel](/it/advanced/mcp-server.md)
- [Laravel AI SDK](/it/ai-sdk.md)
- [Laravel Console Starter](/it/packages/laravel-console-starter/index.md)
- [MCP](/it/packages/laravel-copilot-sdk/mcp.md)
- [Laravel Boost](/it/boost.md)
