> ## 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 伺服器

> 使用 laravel/mcp 包構建可用於生產環境的 MCP 伺服器。涵蓋工具、資源、提示的實現，以及認證、測試與部署等實戰知識。

## 什麼是 MCP 伺服器（進階版）

**Model Context Protocol（MCP）** 是讓 AI 客戶端（如 Claude、Cursor、GitHub Copilot 等）與應用透過標準化協議進行通訊的規範。MCP 有 3 類主要原語。

| 原語                | 職責         | 典型用途              |
| ----------------- | ---------- | ----------------- |
| **工具（Tools）**     | AI 可呼叫的函式  | 資料操作、外部 API 整合、計算 |
| **資源（Resources）** | AI 可讀取的上下文 | 文件、配置資訊、動態資料      |
| **提示（Prompts）**   | 可複用的模板     | 常用查詢、工作流引導        |

用 Laravel 構建 MCP 伺服器的好處在於可以直接使用 Eloquent、快取、認證、驗證等 Laravel 生態。

<Info>
  本進階指南聚焦於實戰實現。有關 MCP 的基礎概念請參考 [中級：Laravel MCP](/zh-TW/mcp)。
</Info>

## 安裝與初始化

<Steps>
  <Step title="安裝包">
    使用 Composer 安裝包。

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

  <Step title="釋出路由檔案">
    使用 `vendor:publish` 生成用於註冊 MCP 伺服器的 `routes/ai.php`。

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

  <Step title="生成伺服器類">
    透過 Artisan 命令建立伺服器類。

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

    在生成的 `app/Mcp/Servers/DatabaseServer.php` 中註冊 Tool、Resource、Prompt。
  </Step>

  <Step title="註冊伺服器">
    在 `routes/ai.php` 中將伺服器註冊到路由。

    <CodeGroup>
      ```php Web 伺服器 theme={null}
      use App\Mcp\Servers\DatabaseServer;
      use Laravel\Mcp\Facades\Mcp;

      Mcp::web('/mcp/database', DatabaseServer::class);
      ```

      ```php 本地伺服器 theme={null}
      use App\Mcp\Servers\DatabaseServer;
      use Laravel\Mcp\Facades\Mcp;

      Mcp::local('database', DatabaseServer::class);
      ```
    </CodeGroup>

    Web 伺服器透過 HTTP POST 訪問。本地伺服器則作為 Artisan 命令執行，用於與 CLI 型 AI 客戶端整合。
  </Step>
</Steps>

## Tool 的實現

Tool 是 AI 客戶端可呼叫的函式。可以直接使用 Laravel 的服務容器、驗證、Eloquent。

### 建立 Tool

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

在生成的類中實現 `handle` 與 `schema` 方法。

### 引數定義（Schema）

在 `schema` 方法中使用 `Illuminate\Contracts\JsonSchema\JsonSchema` builder 定義接受的引數。

```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('按姓名或郵箱搜尋使用者。')]
class SearchUsersTool extends Tool
{
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'query'   => 'required|string|max:100',
            'limit'   => 'integer|min:1|max:50',
            'role'    => 'nullable|string|in:admin,editor,viewer',
        ], [
            'query.required' => '請指定搜尋關鍵詞，例如 "張三" 或 "zhangsan@example.com"',
            'limit.max'      => '獲取條數最多為 50 條。',
            'role.in'        => '角色必須為 admin、editor、viewer 之一。',
        ]);

        $users = \App\Models\User::query()
            ->where(function ($q) use ($validated) {
                $q->where('name', 'like', "%{$validated['query']}%")
                  ->orWhere('email', 'like', "%{$validated['query']}%");
            })
            ->when(isset($validated['role']), fn ($q) => $q->where('role', $validated['role']))
            ->limit($validated['limit'] ?? 10)
            ->get(['id', 'name', 'email', 'role']);

        if ($users->isEmpty()) {
            return Response::text('未找到符合條件的使用者。');
        }

        $result = $users->map(fn ($u) => "ID:{$u->id} {$u->name} <{$u->email}> [{$u->role}]")
                        ->implode("\n");

        return Response::text($result);
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'query' => $schema->string()
                ->description('搜尋關鍵詞。按姓名或郵箱搜尋。')
                ->required(),

            'limit' => $schema->integer()
                ->description('返回條數上限（預設 10，最大 50）。')
                ->minimum(1)
                ->maximum(50)
                ->default(10),

            'role' => $schema->string()
                ->description('要篩選的角色。admin、editor 或 viewer。')
                ->enum(['admin', 'editor', 'viewer']),
        ];
    }
}
```

### Tool 註解

使用 MCP 協議提供的註解可以讓 AI 客戶端判斷工具的安全性。

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

#[IsReadOnly]      // 不改變環境的只讀工具
#[IsIdempotent]    // 相同引數多次呼叫結果一致
class SearchUsersTool extends Tool
{
    // ...
}
```

| 註解                 | 說明           |
| ------------------ | ------------ |
| `#[IsReadOnly]`    | 不修改資料        |
| `#[IsDestructive]` | 執行刪除等破壞性操作   |
| `#[IsIdempotent]`  | 相同引數重複執行是安全的 |
| `#[IsOpenWorld]`   | 與外部實體通訊      |

### 結構化響應

若希望返回易於 AI 客戶端解析的 JSON 響應，使用 `Response::structured`。

```php theme={null}
return Response::structured([
    'total' => $users->count(),
    'users' => $users->map(fn ($u) => [
        'id'    => $u->id,
        'name'  => $u->name,
        'email' => $u->email,
    ])->toArray(),
]);
```

### 流式響應

對耗時較長的處理，返回 Generator 可以實現進度流式推送。

```php theme={null}
use Generator;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

public function handle(Request $request): Generator
{
    $items = \App\Models\Product::all();
    $total = $items->count();

    foreach ($items as $index => $item) {
        yield Response::notification('processing/progress', [
            'current' => $index + 1,
            'total'   => $total,
            'name'    => $item->name,
        ]);

        // 重處理...
        yield Response::text("已完成處理：{$item->name}");
    }
}
```

在 Web 伺服器上，流式響應會自動作為 SSE（Server-Sent Events）流傳送。

### 條件註冊

可以僅向滿足特定條件的使用者暴露工具。

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

## Resource 的實現

Resource 是 AI 客戶端作為上下文載入的資料，如文件、配置、動態資料。

### 靜態資源

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

```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\Attributes\Uri;
use Laravel\Mcp\Server\Resource;

#[Uri('app://resources/documentation')]
#[MimeType('text/markdown')]
#[Description('應用的 API 文件與業務規則概述。')]
class AppDocumentationResource extends Resource
{
    public function handle(Request $request): Response
    {
        $content = \Illuminate\Support\Facades\Storage::get('docs/api-overview.md')
            ?? '未找到文件。';

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

### 動態資源（URI 模板）

使用 URI 模板可以根據 URL 引數提供動態資源。

```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;

#[MimeType('application/json')]
#[Description('根據使用者 ID 獲取使用者資訊。')]
class UserProfileResource extends Resource implements HasUriTemplate
{
    public function uriTemplate(): UriTemplate
    {
        return new UriTemplate('app://users/{userId}/profile');
    }

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

        $user = \App\Models\User::find($userId);

        if (! $user) {
            return Response::error("未找到使用者 ID {$userId}。");
        }

        return Response::text(json_encode([
            'id'         => $user->id,
            'name'       => $user->name,
            'email'      => $user->email,
            'created_at' => $user->created_at->toIso8601String(),
        ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
    }
}
```

AI 客戶端會請求 `app://users/42/profile` 這樣的 URI，`{userId}` 的值可以透過 `$request->get('userId')` 獲得。

### Resource 註解

可以顯式宣告資源的優先順序與受眾。

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

#[Audience(Role::Assistant)]   // 面向 AI 助手
#[Priority(0.8)]               // 重要程度（0.0〜1.0）
class AppDocumentationResource extends Resource
{
    // ...
}
```

## Prompt 的實現

Prompt 是 AI 客戶端可使用的可複用模板。用於將常用查詢或複雜工作流標準化。

### 建立 Prompt

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

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

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;

#[Description('用於生成資料分析報告的 Prompt 模板。')]
class DataAnalysisPrompt extends Prompt
{
    public function arguments(): array
    {
        return [
            new Argument(
                name: 'target',
                description: '分析物件。例如："上月銷售"、"使用者註冊數變化"',
                required: true,
            ),
            new Argument(
                name: 'format',
                description: '輸出格式。"summary"（摘要）或 "detail"（詳細）',
                required: false,
            ),
        ];
    }

    public function handle(Request $request): array
    {
        $validated = $request->validate([
            'target' => 'required|string|max:200',
            'format' => 'nullable|in:summary,detail',
        ]);

        $target = $validated['target'];
        $format = $validated['format'] ?? 'summary';
        $instruction = $format === 'detail'
            ? '請生成包含數值、趨勢、異常值與推薦操作的詳細報告。'
            : '請生成包含關鍵指標與三條重要洞察的簡潔摘要。';

        return [
            Response::text(
                "你是一名資料分析師。{$instruction}"
            )->asAssistant(),
            Response::text(
                "請分析以下資料：{$target}"
            ),
        ];
    }
}
```

<Tip>
  使用 `asAssistant()` 後訊息會被視作 AI 助手的發言。將系統提示與使用者訊息組合，可以細緻控制 AI 的行為。
</Tip>

## 認證與授權

### 使用 Sanctum 的令牌認證

這是最簡單的認證方式。MCP 客戶端需帶上 `Authorization: Bearer <token>` 頭。

```php theme={null}
// routes/ai.php
Mcp::web('/mcp/database', DatabaseServer::class)
    ->middleware('auth:sanctum');
```

### 基於 OAuth 2.1 的認證

若需要更穩固的認證，使用 Laravel Passport。

```php theme={null}
// routes/ai.php
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

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

採用 OAuth 認證時，請釋出 MCP 提供的授權檢視，並在 `AppServiceProvider` 中做設定。

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

```php theme={null}
// app/Providers/AppServiceProvider.php
use Laravel\Passport\Passport;

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

### 使用自定義中介軟體的認證

若使用自定義 API 令牌，可透過中介軟體校驗 `Authorization` 頭。

```php theme={null}
// app/Http/Middleware/McpTokenMiddleware.php
public function handle($request, Closure $next)
{
    $token = $request->bearerToken();

    if (! $token || ! \App\Models\ApiToken::where('token', hash('sha256', $token))->exists()) {
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    return $next($request);
}
```

### Tool 內部的授權

在 Tool 或 Resource 的 `handle` 中可以透過 `$request->user()` 做細粒度授權檢查。

```php theme={null}
public function handle(Request $request): Response
{
    $user = $request->user();

    if (! $user?->can('manage-users')) {
        return Response::error('你沒有使用該工具的許可權，請聯絡管理員。');
    }

    // 繼續已授權的處理...
}
```

<Warning>
  `shouldRegister` 只是將工具從列表中隱藏。呼叫工具時的授權檢查必須在 `handle` 方法內進行。
</Warning>

## 實戰示例：資料庫操作工具

以下是使用 Eloquent 檢索與建立資料的完整示例。

### 伺服器類

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

namespace App\Mcp\Servers;

use App\Mcp\Tools\CreatePostTool;
use App\Mcp\Tools\SearchPostsTool;
use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
use Laravel\Mcp\Server;

#[Name('Blog Management Server')]
#[Version('1.0.0')]
#[Instructions('可以搜尋和建立部落格文章的 MCP 伺服器。')]
class BlogServer extends Server
{
    protected array $tools = [
        SearchPostsTool::class,
        CreatePostTool::class,
    ];
}
```

### 檢索工具（只讀）

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

namespace App\Mcp\Tools;

use App\Models\Post;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[Description('按關鍵詞或狀態搜尋部落格文章。')]
#[IsReadOnly]
#[IsIdempotent]
class SearchPostsTool extends Tool
{
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'keyword' => 'nullable|string|max:100',
            'status'  => 'nullable|in:draft,published,archived',
            'limit'   => 'integer|min:1|max:20',
        ]);

        $posts = Post::query()
            ->when($validated['keyword'] ?? null, fn ($q, $kw) =>
                $q->where('title', 'like', "%{$kw}%")
                  ->orWhere('body', 'like', "%{$kw}%")
            )
            ->when($validated['status'] ?? null, fn ($q, $s) => $q->where('status', $s))
            ->latest()
            ->limit($validated['limit'] ?? 5)
            ->get(['id', 'title', 'status', 'published_at']);

        if ($posts->isEmpty()) {
            return Response::text('未找到符合條件的文章。');
        }

        return Response::structured([
            'total' => $posts->count(),
            'posts' => $posts->map(fn ($p) => [
                'id'           => $p->id,
                'title'        => $p->title,
                'status'       => $p->status,
                'published_at' => $p->published_at?->toIso8601String(),
            ])->toArray(),
        ]);
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'keyword' => $schema->string()
                ->description('標題或正文中包含的關鍵詞。'),

            'status' => $schema->string()
                ->description('文章狀態。')
                ->enum(['draft', 'published', 'archived']),

            'limit' => $schema->integer()
                ->description('返回條數上限（預設 5，最大 20）。')
                ->default(5),
        ];
    }
}
```

### 建立工具（寫入）

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

namespace App\Mcp\Tools;

use App\Models\Post;
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('以草稿形式建立新的部落格文章。')]
class CreatePostTool extends Tool
{
    public function handle(Request $request): Response
    {
        $user = $request->user();

        if (! $user?->can('create-posts')) {
            return Response::error('你沒有建立文章的許可權。');
        }

        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'tags'  => 'nullable|array',
            'tags.*' => 'string|max:50',
        ], [
            'title.required' => '請指定文章標題。',
            'body.required'  => '請指定文章正文。',
        ]);

        $post = Post::create([
            'title'   => $validated['title'],
            'body'    => $validated['body'],
            'status'  => 'draft',
            'user_id' => $user->id,
        ]);

        if (! empty($validated['tags'])) {
            $post->syncTags($validated['tags']);
        }

        return Response::structured([
            'id'         => $post->id,
            'title'      => $post->title,
            'status'     => $post->status,
            'created_at' => $post->created_at->toIso8601String(),
            'message'    => '文章已建立為草稿。',
        ]);
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'title' => $schema->string()
                ->description('文章標題（最多 255 字元）。')
                ->required(),

            'body' => $schema->string()
                ->description('文章正文。支援 Markdown。')
                ->required(),

            'tags' => $schema->array()
                ->description('要打上的標籤陣列。例如：["Laravel", "PHP"]'),
        ];
    }
}
```

## 實戰示例：檔案系統操作工具

使用 Storage Facade 操作檔案的示例。

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

namespace App\Mcp\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Storage;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[Description('獲取儲存中的檔案列表，並讀取文字檔案內容。')]
#[IsReadOnly]
class ReadFileTool extends Tool
{
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'path'    => 'required|string|max:500',
            'disk'    => 'nullable|string|in:local,public,s3',
        ]);

        $disk = $validated['disk'] ?? 'local';
        $path = $validated['path'];

        // 防止目錄遍歷攻擊
        if (str_contains($path, '..')) {
            return Response::error('指定了無效路徑。');
        }

        if (! Storage::disk($disk)->exists($path)) {
            return Response::error("未找到檔案：{$path}");
        }

        $mimeType = Storage::disk($disk)->mimeType($path);

        // 僅返回文字檔案內容
        if (str_starts_with($mimeType, 'text/') || $mimeType === 'application/json') {
            $content = Storage::disk($disk)->get($path);
            return Response::text($content);
        }

        // 圖片檔案以二進位制返回
        if (str_starts_with($mimeType, 'image/')) {
            return Response::fromStorage($path, disk: $disk);
        }

        return Response::error("不支援的檔案格式：{$mimeType}");
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'path' => $schema->string()
                ->description('要讀取的檔案路徑。例如："reports/2025-01.csv"')
                ->required(),

            'disk' => $schema->string()
                ->description('使用的儲存磁碟。')
                ->enum(['local', 'public', 's3'])
                ->default('local'),
        ];
    }
}
```

<Warning>
  檔案操作工具必須對路徑做規範化處理，避免訪問允許目錄以外的位置。含 `..` 的路徑應當拒絕。
</Warning>

## 測試

MCP 伺服器、Tool、Resource、Prompt 都可以用 Laravel 標準測試機制編寫單元測試。

### 測試 Tool

透過 `Server::tool()` 方法直接呼叫工具進行測試。

<CodeGroup>
  ```php Pest theme={null}
  use App\Mcp\Servers\BlogServer;
  use App\Mcp\Tools\SearchPostsTool;
  use App\Models\Post;
  use App\Models\User;

  test('可以搜尋文章', function () {
      Post::factory()->create(['title' => 'Laravel 基礎', 'status' => 'published']);
      Post::factory()->create(['title' => 'PHP 高階', 'status' => 'draft']);

      $response = BlogServer::tool(SearchPostsTool::class, [
          'keyword' => 'Laravel',
          'status'  => 'published',
      ]);

      $response
          ->assertOk()
          ->assertSee('Laravel 基礎');
  });

  test('無許可權使用者不能建立文章', function () {
      $user = User::factory()->create();

      $response = BlogServer::actingAs($user)
          ->tool(\App\Mcp\Tools\CreatePostTool::class, [
              'title' => '測試文章',
              'body'  => '正文',
          ]);

      $response->assertSee('你沒有建立文章的許可權');
  });
  ```

  ```php PHPUnit theme={null}
  use App\Mcp\Servers\BlogServer;
  use App\Mcp\Tools\SearchPostsTool;
  use App\Models\Post;
  use App\Models\User;

  public function test_可以搜尋文章(): void
  {
      Post::factory()->create(['title' => 'Laravel 基礎', 'status' => 'published']);
      Post::factory()->create(['title' => 'PHP 高階', 'status' => 'draft']);

      $response = BlogServer::tool(SearchPostsTool::class, [
          'keyword' => 'Laravel',
          'status'  => 'published',
      ]);

      $response
          ->assertOk()
          ->assertSee('Laravel 基礎');
  }

  public function test_無許可權使用者不能建立文章(): void
  {
      $user = User::factory()->create();

      $response = BlogServer::actingAs($user)
          ->tool(\App\Mcp\Tools\CreatePostTool::class, [
              'title' => '測試文章',
              'body'  => '正文',
          ]);

      $response->assertSee('你沒有建立文章的許可權');
  }
  ```
</CodeGroup>

### Resource 與 Prompt 的測試

```php theme={null}
// Resource 測試
$response = BlogServer::resource(\App\Mcp\Resources\AppDocumentationResource::class);
$response->assertOk()->assertSee('API');

// Prompt 測試
$response = BlogServer::prompt(\App\Mcp\Prompts\DataAnalysisPrompt::class, [
    'target' => '上月銷售',
    'format' => 'summary',
]);
$response->assertOk()->assertSee('資料分析師');
```

### 主要斷言方法

| 方法                                       | 說明          |
| ---------------------------------------- | ----------- |
| `assertOk()`                             | 斷言響應無錯誤     |
| `assertSee($text)`                       | 斷言響應中包含指定文字 |
| `assertHasErrors()`                      | 斷言響應存在錯誤    |
| `assertHasNoErrors()`                    | 斷言響應無錯誤     |
| `assertName($name)`                      | 斷言 Tool 名   |
| `assertSentNotification($method, $data)` | 斷言已傳送通知     |
| `assertNotificationCount($count)`        | 斷言通知傳送次數    |

### 使用 MCP Inspector 除錯

對於互動式除錯，使用 MCP Inspector。

```shell theme={null}
# 檢查 Web 伺服器
php artisan mcp:inspector mcp/database

# 檢查本地伺服器
php artisan mcp:inspector database
```

## 部署要點

### HTTP 流式與 SSE

在 Web 伺服器上使用流式響應（Generator）時，請檢查 Web 伺服器配置。

<CodeGroup>
  ```nginx Nginx theme={null}
  location /mcp {
      proxy_pass http://127.0.0.1:8000;
      proxy_buffering off;          # SSE 需要關閉緩衝
      proxy_cache off;
      proxy_read_timeout 3600s;     # 支援長連線
      proxy_set_header Connection '';
      chunked_transfer_encoding on;
  }
  ```

  ```apache Apache theme={null}
  # 使用 mod_proxy_http
  ProxyPass /mcp http://127.0.0.1:8000/mcp
  ProxyPassReverse /mcp http://127.0.0.1:8000/mcp
  SetEnv proxy-sendchunks 1
  ```
</CodeGroup>

### 與 Laravel Octane 組合

對高併發的 MCP 伺服器，可考慮使用 Laravel Octane（FrankenPHP 或 Swoole），能顯著減少每次請求的開銷。

<Warning>
  使用 Octane 時請求間會共享狀態。請注意不要在 Tool 中使用靜態屬性或全域性狀態。
</Warning>

### 限流

用 `throttle` 中介軟體限制對 MCP 伺服器的請求。

```php theme={null}
// 在 config/cache.php 中定義專用限流器
// routes/ai.php
Mcp::web('/mcp/database', DatabaseServer::class)
    ->middleware(['auth:sanctum', 'throttle:60,1']);
```

### 快取

對頻繁呼叫的只讀 Tool 建議使用快取。

```php theme={null}
public function handle(Request $request): Response
{
    $data = \Illuminate\Support\Facades\Cache::remember(
        "mcp:search:{$request->get('keyword')}",
        now()->addMinutes(5),
        fn () => $this->fetchFromDatabase($request)
    );

    return Response::structured($data);
}
```

### 日誌與監控

對 MCP Tool 呼叫記錄日誌，可以掌握 AI 客戶端的使用情況。

```php theme={null}
public function handle(Request $request): Response
{
    \Illuminate\Support\Facades\Log::info('MCP tool called', [
        'tool'   => static::class,
        'user'   => $request->user()?->id,
        'params' => $request->all(),
    ]);

    // ...
}
```

<Tip>
  生產環境推薦使用 Laravel Telescope 或 Sentry 監控 MCP 伺服器的效能與異常。
</Tip>


## Related topics

- [進階主題](/zh-TW/advanced/index.md)
- [Laravel 13 新功能彙總](/zh-TW/blog/laravel-13-new-features.md)
- [建立 Boost 的自定義 Agent](/zh-TW/advanced/boost-custom-agent.md)
- [安裝](/zh-TW/installation.md)
- [建立 AI SDK 的自定義 Provider](/zh-TW/advanced/ai-sdk-custom-provider.md)
