라라벨 MCP
- Introduction
- Installation
- Creating Servers
- Tools
- Prompts
- Resources
- Apps
- Metadata
- Icons
- Authentication
- Authorization
- MCP Client
- Testing Servers
소개
Laravel MCP는 AI 클라이언트가 모델 컨텍스트 프로토콜을 통해 귀하의 Laravel 애플리케이션과 상호작용할 수 있는 간단하고 우아한 방법을 제공합니다. 이는 서버, 도구, 리소스 및 프롬프트를 정의하여 AI 기반 상호작용을 애플리케이션에 적용할 수 있는 표현력 있고 유려한 인터페이스를 제공합니다.
설치
시작하려면 Composer 패키지 관리자를 사용하여 Laravel MCP를 프로젝트에 설치하세요:
composer require laravel/mcp
게시 경로
Laravel MCP를 설치한 후, vendor:publish Artisan 명령을 실행하여 MCP 서버를 정의할 routes/ai.php 파일을 게시하세요:
php artisan vendor:publish --tag=ai-routes
이 명령은 애플리케이션의 routes 디렉토리에 routes/ai.php 파일을 생성하며, 이 파일을 MCP 서버를 등록하는 데 사용하게 됩니다.
서버 생성
make:mcp-server Artisan 명령을 사용하여 MCP 서버를 생성할 수 있습니다. 서버는 AI 클라이언트에 도구, 리소스, 프롬프트와 같은 MCP 기능을 노출하는 중앙 통신 지점 역할을 합니다:
php artisan make:mcp-server WeatherServer
이 명령은 app/Mcp/Servers 디렉토리에 새 서버 클래스를 생성합니다. 생성된 서버 클래스는 Laravel MCP의 기본 Laravel\Mcp\Server 클래스를 확장하며, 서버를 구성하고 도구, 리소스 및 프롬프트를 등록하기 위한 속성과 특성을 제공합니다:
<?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
{
/**
* The tools registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
// GetCurrentWeatherTool::class,
];
/**
* The resources registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
*/
protected array $resources = [
// WeatherGuidelinesResource::class,
];
/**
* The prompts registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
*/
protected array $prompts = [
// DescribeWeatherPrompt::class,
];
}
서버 등록
서버를 생성한 후에는 routes/ai.php 파일에 등록하여 접근할 수 있도록 해야 합니다. Laravel MCP는 서버 등록을 위한 두 가지 방법을 제공합니다: HTTP 접근이 가능한 서버의 경우 web, 명령어 기반 서버의 경우 local.
웹 서버
웹 서버는 가장 일반적인 서버 유형이며 HTTP POST 요청을 통해 접근할 수 있어 원격 AI 클라이언트나 웹 기반 통합에 이상적입니다. web 방법을 사용하여 웹 서버를 등록하십시오:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/weather', WeatherServer::class);
일반 경로와 마찬가지로 웹 서버를 보호하기 위해 미들웨어를 적용할 수 있습니다:
Mcp::web('/mcp/weather', WeatherServer::class)
->middleware(['throttle:mcp']);
로컬 서버
로컬 서버는 Artisan 명령어로 실행되며, Laravel Boost와 같은 로컬 AI 어시스턴트 통합을 구축하는 데 완벽합니다. local 방법을 사용하여 로컬 서버를 등록하세요:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::local('weather', WeatherServer::class);
한 번 등록하면 일반적으로 mcp:start Artisan 명령을 수동으로 실행할 필요가 없습니다. 대신 MCP 클라이언트(AI 에이전트)를 구성하여 서버를 시작하거나 MCP Inspector를 사용하십시오.
캐시 힌트
Laravel MCP는 서버 검색, 기본 목록, 리소스 읽기 등 캐시할 수 있는 응답과 함께 캐시 힌트를 포함합니다. 기본적으로 이러한 응답은 TTL(Time To Live)이 0밀리초인 비공개로 표시됩니다.
Cacheable 속성을 사용하여 서버의 기본 캐시 힌트를 사용자 정의할 수 있습니다:
use Laravel\Mcp\Enums\CacheScope;
use Laravel\Mcp\Server\Attributes\Cacheable;
#[Cacheable(ttlMs: 60_000, scope: CacheScope::Public)]
class WeatherServer extends Server
{
/**
* Get the cache hints for individual MCP methods.
*
* @return array<string, \Laravel\Mcp\Server\Attributes\Cacheable>
*/
protected function cacheHints(): array
{
return [
'tools/list' => new Cacheable(ttlMs: 30_000, scope: CacheScope::Public),
];
}
}
CacheScope::Private 범위는 캐시된 응답을 동일한 인증 컨텍스트로 제한하는 반면, CacheScope::Public는 응답을 사용자 간에 공유할 수 있게 합니다. 캐시 힌트는 권고사항이며, 실제로 응답이 캐시되는지 여부는 MCP 클라이언트나 호스트가 결정합니다. cacheHints가 반환하는 메서드별 힌트는 서버의 Cacheable 속성보다 우선합니다.
리소스 클래스에 Cacheable 속성을 적용하여 개별 리소스에 대한 서버의 캐시 힌트를 재정의할 수 있습니다:
#[Cacheable(ttlMs: 300_000, scope: CacheScope::Public)]
class WeatherGuidelinesResource extends Resource
{
// ...
}
리소스의 Cacheable 속성은 메서드별 힌트와 서버의 기본 힌트보다 우선합니다.
도구
도구는 서버가 AI 클라이언트가 호출할 수 있는 기능을 노출할 수 있게 합니다. 이는 언어 모델이 동작을 수행하거나, 코드를 실행하거나, 외부 시스템과 상호작용할 수 있게 합니다:
<?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
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$location = $request->get('location');
// Get weather...
return Response::text('The weather is...');
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
public function schema(JsonSchema $schema): array
{
return [
'location' => $schema->string()
->description('The location to get the weather for.')
->required(),
];
}
}
도구 만들기
도구를 만들려면 make:mcp-tool 장인 명령을 실행하세요:
php artisan make:mcp-tool CurrentWeatherTool
도구를 만든 후에는 서버의 $tools 속성에 등록하세요:
<?php
namespace App\Mcp\Servers;
use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;
class WeatherServer extends Server
{
/**
* The tools registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
CurrentWeatherTool::class,
];
}
검색 가능한 도구 카탈로그
많은 도구를 가진 서버는 모든 도구를 AI 클라이언트에 광고하는 대신 일부 도구를 검색 가능한 카탈로그에 배치할 수 있습니다. 검색 가능한 카탈로그는 두 가지 도구를 제공합니다: search_tools, 도구 이름, 설명 및 입력 스키마로 카탈로그를 검색하며; execute_tools, 검색 결과로 반환된 하나 이상의 도구를 실행합니다.
검색 가능한 카탈로그를 생성하려면 서버의 $tools 속성에서 배열 키로 ToolSearch 클래스를 사용하십시오:
<?php
namespace App\Mcp\Servers;
use App\Mcp\Tools\CurrentWeatherTool;
use App\Mcp\Tools\HistoricalWeatherTool;
use App\Mcp\Tools\WeatherAlertsTool;
use Laravel\Mcp\Server;
use Laravel\Mcp\Server\Tools\ToolSearch;
class WeatherServer extends Server
{
/**
* The tools registered with this MCP server.
*
* @var array<int|string, \Laravel\Mcp\Server\Tool|class-string<\Laravel\Mcp\Server\Tool>|array<int, \Laravel\Mcp\Server\Tool|class-string<\Laravel\Mcp\Server\Tool>>>
*/
protected array $tools = [
CurrentWeatherTool::class,
ToolSearch::class => [
HistoricalWeatherTool::class,
WeatherAlertsTool::class,
],
];
}
이 예제에서는 CurrentWeatherTool가 직접 광고되고 있으며, 과거 날씨 및 날씨 경보 도구는 검색 가능한 카탈로그를 통해 이용할 수 있습니다. 카탈로그 도구를 검색하거나 실행할 때 조건부 도구 등록도 여전히 적용됩니다.
한 번의 execute_tools 호출에서 실행될 수 있는 도구의 최대 수와 최대 응답 크기는 mcp.tool_search.max_tool_calls 및 mcp.tool_search.max_output_bytes 구성 값으로 제어됩니다.
도구 이름, 제목 및 설명
기본적으로 도구의 이름과 제목은 클래스 이름에서 파생됩니다. 예를 들어, CurrentWeatherTool의 이름은 current-weather이고, 제목은 Current Weather Tool가 됩니다. 이러한 값은 Name 및 Title 속성을 사용하여 사용자 정의할 수 있습니다.
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
{
// ...
}
도구 설명은 자동으로 생성되지 않습니다. 항상 Description 속성을 사용하여 의미 있는 설명을 제공해야 합니다:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Fetches the current weather forecast for a specified location.')]
class CurrentWeatherTool extends Tool
{
//
}
[!NOTE] 설명은 도구 메타데이터의 중요한 부분으로, AI 모델이 도구를 언제 어떻게 효과적으로 사용할지 이해하는 데 도움을 줍니다.
도구 입력 스키마
도구는 입력 스키마를 정의하여 AI 클라이언트로부터 어떤 인수를 받을 수 있는지 명시할 수 있습니다. Laravel의 Illuminate\Contracts\JsonSchema\JsonSchema 빌더를 사용하여 도구의 입력 요구 사항을 정의하세요:
<?php
namespace App\Mcp\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
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'),
];
}
}
도구 출력 스키마
도구는 출력 스키마를 정의하여 응답의 구조를 명시할 수 있습니다. 이를 통해 구문 분석 가능한 도구 결과가 필요한 AI 클라이언트와의 통합을 개선할 수 있습니다. outputSchema 방법을 사용하여 도구의 출력 구조를 정의하세요:
<?php
namespace App\Mcp\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Get the tool's output schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
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(),
];
}
}
도구 인수 검증
JSON 스키마 정의는 도구 인수에 대한 기본 구조를 제공하지만, 더 복잡한 검증 규칙을 적용하고 싶을 수도 있습니다.
Laravel MCP는 Laravel의 검증 기능과 원활하게 통합됩니다. 도구의 handle 메서드 내에서 들어오는 도구 인수를 검증할 수 있습니다:
<?php
namespace App\Mcp\Tools;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$validated = $request->validate([
'location' => 'required|string|max:100',
'units' => 'in:celsius,fahrenheit',
]);
// Fetch weather data using the validated arguments...
}
}
검증 실패 시, AI 클라이언트는 제공한 오류 메시지를 기반으로 동작합니다. 따라서 명확하고 실행 가능한 오류 메시지를 제공하는 것이 중요합니다:
$validated = $request->validate([
'location' => ['required','string','max:100'],
'units' => 'in:celsius,fahrenheit',
],[
'location.required' => 'You must specify a location to get the weather for. For example, "New York City" or "Tokyo".',
'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.',
]);
도구 의존성 주입
Laravel 서비스 컨테이너는 모든 도구를 해결하는 데 사용됩니다. 결과적으로, 도구의 생성자에서 필요할 수 있는 모든 의존성을 타입 힌트로 지정할 수 있습니다. 선언된 의존성은 자동으로 해결되어 도구 인스턴스에 주입됩니다:
<?php
namespace App\Mcp\Tools;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Create a new tool instance.
*/
public function __construct(
protected WeatherRepository $weather,
) {}
// ...
}
생성자 주입 외에도, 도구의 handle() 메서드에서 의존성을 타입 힌트할 수도 있습니다. 메서드가 호출될 때 서비스 컨테이너가 자동으로 의존성을 해결하고 주입합니다:
<?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
{
/**
* Handle the tool request.
*/
public function handle(Request $request, WeatherRepository $weather): Response
{
$location = $request->get('location');
$forecast = $weather->getForecastFor($location);
// ...
}
}
도구 주석
주석으로 도구를 향상시켜 AI 클라이언트에 추가 메타데이터를 제공할 수 있습니다. 이러한 주석은 AI 모델이 도구의 동작과 기능을 이해하는 데 도움이 됩니다. 주석은 속성을 통해 도구에 추가됩니다:
<?php
namespace App\Mcp\Tools;
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
{
//
}
사용 가능한 주석에는 다음이 포함됩니다:
주석 값은 불리언 인수를 사용하여 명시적으로 설정할 수 있습니다:
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tools\Annotations\IsDestructive;
use Laravel\Mcp\Server\Tools\Annotations\IsOpenWorld;
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tool;
#[IsReadOnly(true)]
#[IsDestructive(false)]
#[IsOpenWorld(false)]
#[IsIdempotent(true)]
class CurrentWeatherTool extends Tool
{
//
}
조건부 도구 등록
도구 클래스에서 shouldRegister 메서드를 구현함으로써 런타임에 조건부로 도구를 등록할 수 있습니다. 이 메서드는 애플리케이션 상태, 구성 또는 요청 매개변수를 기반으로 도구를 사용할 수 있는지 여부를 결정할 수 있게 해줍니다:
<?php
namespace App\Mcp\Tools;
use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Determine if the tool should be registered.
*/
public function shouldRegister(Request $request): bool
{
return $request?->user()?->subscribed() ?? false;
}
}
도구의 shouldRegister 메서드가 false를 반환하면, 해당 도구는 사용 가능한 도구 목록에 나타나지 않으며 AI 클라이언트가 호출할 수 없습니다.
도구 응답
도구는 Laravel\Mcp\Response의 인스턴스를 반환해야 합니다. Response 클래스는 다양한 유형의 응답을 생성하기 위한 몇 가지 편리한 메서드를 제공합니다:
간단한 텍스트 응답의 경우, text 메서드를 사용하세요:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
// ...
return Response::text('Weather Summary: Sunny, 72°F');
}
도구 실행 중 오류가 발생했음을 나타내려면 error 메서드를 사용하세요:
return Response::error('Unable to fetch weather data. Please try again.');
이미지 또는 오디오 콘텐츠를 반환하려면 image 및 audio 메서드를 사용하십시오:
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');
fromStorage 메서드를 사용하여 Laravel 파일 시스템 디스크에서 이미지 및 오디오 콘텐츠를 직접 로드할 수도 있습니다. MIME 유형은 파일에서 자동으로 감지됩니다:
return Response::fromStorage('weather/radar.png');
필요한 경우 특정 디스크를 지정하거나 MIME 유형을 덮어쓸 수 있습니다:
return Response::fromStorage('weather/radar.png', disk: 's3');
return Response::fromStorage('weather/radar.png', mimeType: 'image/webp');
다중 콘텐츠 응답
도구는 Response 인스턴스 배열을 반환함으로써 여러 개의 콘텐츠를 반환할 수 있습니다:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*
* @return array<int, \Laravel\Mcp\Response>
*/
public function handle(Request $request): array
{
// ...
return [
Response::text('Weather Summary: Sunny, 72°F'),
Response::text("**Detailed Forecast**\n- Morning: 65°F\n- Afternoon: 78°F\n- Evening: 70°F")
];
}
구조화된 응답
도구들은 structured 방법을 사용하여 구조화된 콘텐츠를 반환할 수 있습니다. 이는 JSON 인코딩된 텍스트 표현을 유지하면서 AI 클라이언트에 파싱 가능한 데이터를 제공합니다:
return Response::structured([
'temperature' => 22.5,
'conditions' => 'Partly cloudy',
'humidity' => 65,
]);
구조화된 콘텐츠와 함께 사용자 지정 텍스트를 제공해야 하는 경우, 응답 생성기에서 withStructuredContent 방식을 사용하세요:
return Response::make(
Response::text('Weather is 22.5°C and sunny')
)->withStructuredContent([
'temperature' => 22.5,
'conditions' => 'Sunny',
]);
스트리밍 응답
장기 실행 작업이나 실시간 데이터 스트리밍의 경우, 도구는 handle 메서드에서 제너레이터를 반환할 수 있습니다. 이를 통해 최종 응답 전에 클라이언트로 중간 업데이트를 보낼 수 있습니다:
<?php
namespace App\Mcp\Tools;
use Generator;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Handle the tool request.
*
* @return \Generator<int, \Laravel\Mcp\Response>
*/
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));
}
}
}
웹 기반 서버를 사용할 때, 스트리밍 응답은 자동으로 SSE(Server-Sent Events) 스트림을 열어 각 생성된 메시지를 이벤트로 클라이언트에 전송합니다.
프롬프트
프롬프트는 서버가 재사용 가능한 프롬프트 템플릿을 공유할 수 있게 해주며, AI 클라이언트가 언어 모델과 상호작용할 때 사용할 수 있습니다. 이들은 일반적인 쿼리와 상호작용을 구조화하는 표준화된 방법을 제공합니다.
프롬프트 생성하기
프롬프트를 생성하려면 make:mcp-prompt Artisan 명령어를 실행하세요:
php artisan make:mcp-prompt DescribeWeatherPrompt
프롬프트를 생성한 후, 서버의 $prompts 속성에 등록하세요:
<?php
namespace App\Mcp\Servers;
use App\Mcp\Prompts\DescribeWeatherPrompt;
use Laravel\Mcp\Server;
class WeatherServer extends Server
{
/**
* The prompts registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
*/
protected array $prompts = [
DescribeWeatherPrompt::class,
];
}
프롬프트 이름, 제목 및 설명
기본적으로 프롬프트의 이름과 제목은 클래스 이름에서 파생됩니다. 예를 들어, DescribeWeatherPrompt는 이름이 describe-weather이고 제목이 Describe Weather Prompt가 됩니다. Name 및 Title 속성을 사용하여 이러한 값을 사용자 정의할 수 있습니다:
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;
#[Name('weather-assistant')]
#[Title('Weather Assistant Prompt')]
class DescribeWeatherPrompt extends Prompt
{
// ...
}
프롬프트 설명은 자동으로 생성되지 않습니다. 항상 Description 속성을 사용하여 의미 있는 설명을 제공해야 합니다:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Generates a natural-language explanation of the weather for a given location.')]
class DescribeWeatherPrompt extends Prompt
{
//
}
[!NOTE] 설명은 프롬프트 메타데이터의 중요한 부분으로, AI 모델이 언제 그리고 어떻게 프롬프트를 최적으로 활용할 수 있는지 이해하는 데 도움이 됩니다.
프롬프트 인수
프롬프트는 AI 클라이언트가 특정 값을 사용하여 프롬프트 템플릿을 사용자 정의할 수 있도록 하는 인수를 정의할 수 있습니다. 프롬프트가 허용하는 인수를 정의하려면 arguments 방법을 사용하세요:
<?php
namespace App\Mcp\Prompts;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;
class DescribeWeatherPrompt extends Prompt
{
/**
* Get the prompt's arguments.
*
* @return array<int, \Laravel\Mcp\Server\Prompts\Argument>
*/
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,
),
];
}
}
프롬프트 인수 검증
프롬프트 인수는 정의에 따라 자동으로 검증되지만, 더 복잡한 검증 규칙을 적용하고 싶을 수도 있습니다.
Laravel MCP는 Laravel의 검증 기능과 원활하게 통합됩니다. 프롬프트의 handle 메서드 내에서 들어오는 프롬프트 인수를 검증할 수 있습니다:
<?php
namespace App\Mcp\Prompts;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;
class DescribeWeatherPrompt extends Prompt
{
/**
* Handle the prompt request.
*/
public function handle(Request $request): Response
{
$validated = $request->validate([
'tone' => 'required|string|max:50',
]);
$tone = $validated['tone'];
// Generate the prompt response using the given tone...
}
}
검증 실패 시, AI 클라이언트는 제공한 오류 메시지를 기반으로 작동합니다. 따라서 명확하고 실행 가능한 오류 메시지를 제공하는 것이 중요합니다:
$validated = $request->validate([
'tone' => ['required','string','max:50'],
],[
'tone.*' => 'You must specify a tone for the weather description. Examples include "formal", "casual", or "humorous".',
]);
프롬프트 의존성 주입
Laravel 서비스 컨테이너는 모든 프롬프트를 해결하는 데 사용됩니다. 결과적으로, 프롬프트의 생성자에서 필요할 수 있는 모든 의존성을 타입 힌트할 수 있습니다. 선언된 의존성은 자동으로 해결되고 프롬프트 인스턴스에 주입됩니다:
<?php
namespace App\Mcp\Prompts;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Prompt;
class DescribeWeatherPrompt extends Prompt
{
/**
* Create a new prompt instance.
*/
public function __construct(
protected WeatherRepository $weather,
) {}
//
}
생성자 주입 외에도, 프롬프트의 handle 메서드에서 의존성을 타입 힌트할 수 있습니다. 메서드가 호출될 때 서비스 컨테이너가 의존성을 자동으로 해결하고 주입합니다:
<?php
namespace App\Mcp\Prompts;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;
class DescribeWeatherPrompt extends Prompt
{
/**
* Handle the prompt request.
*/
public function handle(Request $request, WeatherRepository $weather): Response
{
$isAvailable = $weather->isServiceAvailable();
// ...
}
}
조건부 프롬프트 등록
프롬프트 클래스에서 shouldRegister 메서드를 구현하여 런타임에 조건부로 프롬프트를 등록할 수 있습니다. 이 메서드를 통해 애플리케이션 상태, 구성 또는 요청 매개변수를 기반으로 프롬프트를 사용 가능하게 할지 여부를 결정할 수 있습니다:
<?php
namespace App\Mcp\Prompts;
use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Prompt;
class CurrentWeatherPrompt extends Prompt
{
/**
* Determine if the prompt should be registered.
*/
public function shouldRegister(Request $request): bool
{
return $request?->user()?->subscribed() ?? false;
}
}
프롬프트의 shouldRegister 메서드가 false를 반환하면, 사용 가능한 프롬프트 목록에 나타나지 않으며 AI 클라이언트에서 호출할 수 없습니다.
프롬프트 응답
프롬프트는 단일 Laravel\Mcp\Response 또는 Laravel\Mcp\Response 인스턴스의 반복 가능한 객체를 반환할 수 있습니다. 이러한 응답은 AI 클라이언트로 전송될 콘텐츠를 캡슐화합니다:
<?php
namespace App\Mcp\Prompts;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;
class DescribeWeatherPrompt extends Prompt
{
/**
* Handle the prompt request.
*
* @return array<int, \Laravel\Mcp\Response>
*/
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 New York City?";
return [
Response::text($systemMessage)->asAssistant(),
Response::text($userMessage),
];
}
}
asAssistant() 방법을 사용하여 응답 메시지가 AI 어시스턴트에서 온 것으로 처리되어야 한다는 것을 표시할 수 있으며, 일반 메시지는 사용자 입력으로 처리됩니다.
리소스
리소스는 서버가 데이터를 공개하고 AI 클라이언트가 언어 모델과 상호작용할 때 참고 자료로 사용할 수 있도록 합니다. 문서, 구성 또는 AI 응답에 도움을 주는 데이터를 공유하는 방법을 제공합니다.
리소스 생성하기
리소스를 생성하려면 make:mcp-resource Artisan 명령을 실행하세요:
php artisan make:mcp-resource WeatherGuidelinesResource
리소스를 생성한 후, 서버의 $resources 속성에 등록하세요:
<?php
namespace App\Mcp\Servers;
use App\Mcp\Resources\WeatherGuidelinesResource;
use Laravel\Mcp\Server;
class WeatherServer extends Server
{
/**
* The resources registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
*/
protected array $resources = [
WeatherGuidelinesResource::class,
];
}
리소스 이름, 제목 및 설명
기본적으로 리소스의 이름과 제목은 클래스 이름에서 파생됩니다. 예를 들어, WeatherGuidelinesResource의 이름은 weather-guidelines이고 제목은 Weather Guidelines Resource가 됩니다. 이러한 값을 Name 및 Title 속성을 사용하여 사용자 지정할 수 있습니다:
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;
#[Name('weather-api-docs')]
#[Title('Weather API Documentation')]
class WeatherGuidelinesResource extends Resource
{
// ...
}
리소스 설명은 자동으로 생성되지 않습니다. 항상 Description 속성을 사용하여 의미 있는 설명을 제공해야 합니다:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Comprehensive guidelines for using the Weather API.')]
class WeatherGuidelinesResource extends Resource
{
//
}
[!NOTE] 설명은 리소스 메타데이터의 중요한 부분으로, AI 모델이 리소스를 언제 어떻게 효과적으로 사용할지 이해하는 데 도움을 줍니다.
리소스 템플릿
리소스 템플릿은 서버가 변수와 함께 URI 패턴에 맞는 동적 리소스를 노출할 수 있게 합니다. 각각의 리소스에 대해 정적인 URI를 정의하는 대신, 템플릿 패턴을 기반으로 여러 URI를 처리하는 단일 리소스를 생성할 수 있습니다.
리소스 템플릿 생성
리소스 템플릿을 생성하려면, 리소스 클래스에서 HasUriTemplate 인터페이스를 구현하고 UriTemplate 인스턴스를 반환하는 uriTemplate 메서드를 정의하세요:
<?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
{
/**
* Get the URI template for this resource.
*/
public function uriTemplate(): UriTemplate
{
return new UriTemplate('file://users/{userId}/files/{fileId}');
}
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
$userId = $request->get('userId');
$fileId = $request->get('fileId');
// Fetch and return the file content...
return Response::text($content);
}
}
리소스가 HasUriTemplate 인터페이스를 구현하면 정적 리소스가 아니라 리소스 템플릿으로 등록됩니다. 그 후 AI 클라이언트는 템플릿 패턴과 일치하는 URI를 사용하여 리소스를 요청할 수 있으며, URI의 변수들은 자동으로 추출되어 리소스의 handle 메서드에서 사용할 수 있게 됩니다.
URI 템플릿 문법
URI 템플릿은 중괄호로 묶인 자리 표시자를 사용하여 URI 내의 변수 세그먼트를 정의합니다:
new UriTemplate('file://users/{userId}');
new UriTemplate('file://users/{userId}/files/{fileId}');
new UriTemplate('https://api.example.com/{version}/{resource}/{id}');
템플릿 변수 접근하기
URI가 리소스 템플릿과 일치하면 추출된 변수들이 자동으로 요청에 병합되며, get 메서드를 사용하여 접근할 수 있습니다:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Contracts\HasUriTemplate;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Support\UriTemplate;
class UserProfileResource extends Resource implements HasUriTemplate
{
public function uriTemplate(): UriTemplate
{
return new UriTemplate('file://users/{userId}/profile');
}
public function handle(Request $request): Response
{
// Access the extracted variable
$userId = $request->get('userId');
// Access the full URI if needed
$uri = $request->uri();
// Fetch user profile...
return Response::text("Profile for user {$userId}");
}
}
Request 객체는 추출된 변수와 요청된 원래 URI를 모두 제공하여 리소스 요청을 처리하는 데 전체 컨텍스트를 제공합니다.
리소스 URI 및 MIME 유형
각 리소스는 고유한 URI로 식별되며, AI 클라이언트가 리소스의 형식을 이해할 수 있도록 도움을 주는 관련 MIME 유형을 가집니다.
기본적으로 리소스의 URI는 리소스 이름을 기반으로 생성되므로 WeatherGuidelinesResource의 URI는 weather://resources/weather-guidelines가 됩니다. 기본 MIME 유형은 text/plain입니다.
Uri 및 MimeType 속성을 사용하여 이러한 값을 사용자 정의할 수 있습니다:
<?php
namespace App\Mcp\Resources;
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
{
}
URI와 MIME 유형은 AI 클라이언트가 리소스 내용을 적절하게 처리하고 해석하는 방법을 결정하는 데 도움을 줍니다.
리소스 요청
도구 및 프롬프트와 달리, 리소스는 입력 스키마나 인수를 정의할 수 없습니다. 그러나 여전히 리소스의 handle 메서드 내에서 요청 객체와 상호작용할 수 있습니다:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Resource;
class WeatherGuidelinesResource extends Resource
{
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
// ...
}
}
리소스 의존성 주입
Laravel 서비스 컨테이너는 모든 리소스를 해결하는 데 사용됩니다. 결과적으로, 리소스가 필요로 할 수 있는 모든 의존성을 생성자의 타입 힌트로 지정할 수 있습니다. 선언된 의존성은 자동으로 해결되어 리소스 인스턴스에 주입됩니다:
<?php
namespace App\Mcp\Resources;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Resource;
class WeatherGuidelinesResource extends Resource
{
/**
* Create a new resource instance.
*/
public function __construct(
protected WeatherRepository $weather,
) {}
// ...
}
생성자 주입 외에도, 리소스의 handle 메서드에서 의존성을 타입 힌트할 수 있습니다. 메서드가 호출될 때 서비스 컨테이너가 자동으로 의존성을 해결하고 주입합니다:
<?php
namespace App\Mcp\Resources;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Resource;
class WeatherGuidelinesResource extends Resource
{
/**
* Handle the resource request.
*/
public function handle(WeatherRepository $weather): Response
{
$guidelines = $weather->guidelines();
return Response::text($guidelines);
}
}
리소스 주석
리소스에 주석을 추가하여 AI 클라이언트에 추가 메타데이터를 제공할 수 있습니다. 주석은 속성을 통해 리소스에 추가됩니다:
<?php
namespace App\Mcp\Resources;
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
{
//
}
사용 가능한 주석에는 다음이 포함됩니다:
조건부 리소스 등록
리소스 클래스에서 shouldRegister 메서드를 구현하여 실행 시 조건부로 리소스를 등록할 수 있습니다. 이 메서드를 사용하면 애플리케이션 상태, 구성 또는 요청 매개변수를 기반으로 리소스를 사용할 수 있는지 여부를 결정할 수 있습니다:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Resource;
class WeatherGuidelinesResource extends Resource
{
/**
* Determine if the resource should be registered.
*/
public function shouldRegister(Request $request): bool
{
return $request?->user()?->subscribed() ?? false;
}
}
리소스의 shouldRegister 메서드가 false를 반환하면, 해당 리소스는 사용 가능한 리소스 목록에 나타나지 않으며 AI 클라이언트가 접근할 수 없습니다.
리소스 응답
리소스는 Laravel\Mcp\Response의 인스턴스를 반환해야 합니다. Response 클래스는 다양한 유형의 응답을 생성할 수 있는 여러 편리한 메서드를 제공합니다:
간단한 텍스트 내용을 위해서는 text 메서드를 사용하십시오:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
// ...
return Response::text($weatherData);
}
자원 링크 응답
자원 링크를 반환하려면 URI와 이름을 제공하여 resourceLink 방법을 사용하세요. 내장 자원과 달리, 자원 링크는 AI 클라이언트가 독립적으로 가져오는 URI 포인터를 반환합니다:
return Response::resourceLink(
uri: 'file:///data/report.json',
name: 'monthly-report',
mimeType: 'application/json',
);
등록된 리소스 클래스나 인스턴스를 전달할 수도 있으며, 이는 리소스의 URI, 이름, 제목, 설명 및 MIME 유형을 자동으로 상속합니다:
return Response::resourceLink(new WeatherForecastResource);
Blob 응답
Blob 내용을 반환하려면 blob 메서드를 사용하고, blob 내용을 제공하십시오:
return Response::blob(file_get_contents(storage_path('weather/radar.png')));
블롭 콘텐츠를 반환할 때 MIME 유형은 리소스에 설정된 MIME 유형에 따라 결정됩니다:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Resource;
#[MimeType('image/png')]
class WeatherGuidelinesResource extends Resource
{
//
}
오류 응답
리소스 검색 중 오류가 발생했음을 나타내려면 error() 메서드를 사용하십시오:
return Response::error('Unable to fetch weather data for the specified location.');
앱
Laravel MCP는 MCP 앱을 지원하며, 이는 도구가 지원되는 호스트 내의 샌드박스된 iframe에서 상호작용 HTML 애플리케이션을 렌더링할 수 있도록 하는 모델 컨텍스트 프로토콜의 확장입니다. 이를 통해 단순한 텍스트 응답을 넘어 대시보드, 폼, 시각화 및 기타 풍부한 경험을 구축할 수 있습니다.
MCP 앱은 함께 작동하는 두 가지 부분으로 구성됩니다:
- 애플리케이션의 독립 실행형 HTML을 반환하는 앱 리소스
#[RendersApp]속성을 사용하여 앱 리소스에 연결된 도구. 도구가 호출되면 호스트가 연결된 리소스를 가져와 렌더링합니다.
앱 리소스 생성
make:mcp-app-resource Artisan 명령어를 사용하여 앱 리소스를 생성할 수 있습니다:
php artisan make:mcp-app-resource WeatherDashboardApp
이 명령은 두 개의 파일을 생성합니다: app/Mcp/Resources에 있는 PHP 클래스와 resources/views/mcp에 있는 Blade 뷰. 뷰 이름은 클래스 이름에서 자동으로 추론됩니다. 예를 들어, WeatherDashboardApp는 mcp.weather-dashboard-app에 매핑됩니다:
<?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
{
/**
* Handle the app resource request.
*/
public function handle(Request $request): Response
{
return Response::view('mcp.weather-dashboard-app', [
'title' => $this->title(),
]);
}
}
AppResource은 기본 Resource 클래스를 확장하며 MCP Apps 명세에서 요구하는 ui:// URI 스킴과 text/html;profile=mcp-app MIME 타입을 자동으로 구성합니다. 다른 리소스와 마찬가지로, 서버의 $resources 배열에 등록해야 합니다.
생성된 Blade 뷰는 <x-mcp::app> 컴포넌트를 사용하며, 클라이언트 측 MCP SDK가 번들로 포함되어 사용 준비가 완료된 완전한 HTML 문서를 렌더링합니다:
<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 글로벌은 번들 SDK에 의해 제공되며, iframe을 서버에 연결하고, 호스트 테마를 적용하며, callServerTool, sendMessage, openLink와 같은 도우미와 이벤트 콜백을 노출하는 기능을 처리합니다. 클라이언트측 전체 API에 대해서는 MCP Apps 사양을 참조하십시오.
도구에서 앱 렌더링
앱 리소스를 표시하려면 #[RendersApp] 속성을 사용하여 도구를 해당 리소스에 연결하십시오. 도구가 호출되면, Laravel MCP는 리소스의 URI를 도구 메타데이터에 포함시켜 호스트가 샌드박스 iframe에서 앱을 렌더링할 수 있도록 합니다:
<?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
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
return Response::text('Weather dashboard loaded.');
}
}
Laravel MCP는 어떤 AppResource가 등록될 때마다 서버의 extensions 기능 내에서 io.modelcontextprotocol/ui 확장 기능을 자동으로 광고하므로 추가 서버 구성이 필요하지 않습니다.
앱 도구 가시성
각 #[RendersApp] 도구는 visibility 인수를 통해 호출할 수 있는 사용자를 제한할 수 있습니다. 이는 UI가 데이터를 로드하거나 새로 고치기 위해 호출하지만 모델에 도구를 표시하지 않고 앱 전용 비공개 도구를 공개하는 데 유용합니다:
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Ui\Enums\Visibility;
#[RendersApp(resource: WeatherDashboardApp::class, visibility: [Visibility::App])]
class GetWeatherData extends Tool
{
// ...
}
Visibility 열거형에는 Model와 App 두 가지 경우가 있으며, 기본적으로 둘 다 사용됩니다. UI가 직접 호출하는 백엔드 작업에는 [Visibility::App]를 사용하고, UI에서 도구를 사용할 수 없게 하려면 [Visibility::Model]를 사용하세요.
앱 구성
앱 리소스의 #[AppMeta] 속성은 iframe의 콘텐츠 보안 정책, 브라우저 권한, 그리고 뷰의 <head>에 포함되어야 하는 모든 라이브러리 스크립트를 구성합니다:
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
{
// ...
}
The Library enum includes pre-configured CDN scripts for common front-end libraries, such as Library::Tailwind and Library::Alpine, and their CDN origins are automatically merged into the CSP. The Permission enum covers browser permissions such as Camera, Microphone, Geolocation, and ClipboardWrite.
For computed or dynamic configuration, override the appMeta method on your resource using the fluent AppMeta, Csp, and Permissions builders from the Laravel\Mcp\Server\Ui namespace.
Building Apps With Boost
Laravel MCP includes a dedicated Boost skill reference for building MCP Apps. If you have Laravel Boost installed, your AI coding agent can invoke the mcp-development skill and ask it to scaffold an app resource, Blade view, and linked tool for you.
For the complete protocol reference, including the full client-side API and schema details, see the official MCP Apps documentation.
Metadata
Laravel MCP also supports the _meta field as defined in the MCP specification, which is required by certain MCP clients or integrations. Metadata can be applied to all MCP primitives, including tools, resources, and prompts, as well as their responses.
You can attach metadata to individual response content using the withMeta method:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
return Response::text('The weather is sunny.')
->withMeta(['source' => 'weather-api', 'cached' => true]);
}
전체 응답 패키지에 적용되는 결과 수준 메타데이터의 경우, Response::make로 응답을 감싸고 반환된 응답 팩토리 인스턴스에서 withMeta를 호출하십시오:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\ResponseFactory;
/**
* Handle the tool request.
*/
public function handle(Request $request): ResponseFactory
{
return Response::make(
Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
}
도구, 리소스 또는 프롬프트 자체에 메타데이터를 첨부하려면, 클래스에 $meta 속성을 정의하세요:
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Fetches the current weather forecast.')]
class CurrentWeatherTool extends Tool
{
protected ?array $meta = [
'version' => '2.0',
'author' => 'Weather Team',
];
// ...
}
아이콘
MCP 클라이언트는 귀하의 서버와 그 프리미티브에 대한 아이콘을 표시할 수 있습니다. Icon 속성을 사용하여 서버, 도구, 리소스 또는 프롬프트에 아이콘을 선언할 수 있습니다:
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
{
// ...
}
Icon 속성은 반복 가능하므로, 서로 다른 크기나 밝은 테마와 어두운 테마 변형을 제공하기 위해 여러 아이콘을 선언할 수 있습니다.
또는 icons 메서드를 재정의하여 프로그래밍 방식으로 아이콘을 정의할 수 있습니다. 이는 아이콘이 런타임 조건에 따라 달라질 때 유용합니다:
use Laravel\Mcp\Schema\Icon;
class CurrentWeatherTool extends Tool
{
/**
* Get the tool's icons.
*
* @return array<int, Icon>
*/
public function icons(): array
{
return [
Icon::from('mcp/tool.png', mimeType: 'image/png'),
];
}
}
Icons defined via the attribute and the icons method are combined automatically. Icon paths are resolved as follows:
- Paths with a URI scheme, such as
https:ordata:, are used as-is. - Relative paths are resolved to a URL using Laravel’s
assethelper.
Authentication
Just like routes, you can authenticate web MCP servers with middleware. Adding authentication to your MCP server will require a user to authenticate before using any capability of the server.
There are two ways to authenticate access to your MCP server: simple, token based authentication via Laravel Sanctum or any token which is passed via the Authorization HTTP header. Or, you may authenticate via OAuth using Laravel Passport.
OAuth 2.1
The most robust way to protect your web-based MCP servers is with OAuth using Laravel Passport.
When authenticating your MCP server via OAuth, invoke the Mcp::oauthRoutes method in your routes/ai.php file to register the required OAuth2 discovery and client registration routes. Then, apply Passport’s auth:api middleware to your Mcp::web route in your routes/ai.php file:
use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;
Mcp::oauthRoutes();
Mcp::web('/mcp/weather', WeatherExample::class)
->middleware('auth:api');
새로운 Passport 설치
귀하의 애플리케이션이 아직 Laravel Passport를 사용하지 않는 경우, Passport를 애플리케이션에 추가하기 위해 Passport의 설치 및 배포 가이드를 따르십시오. 진행하기 전에 OAuthenticatable 모델, 새로운 인증 가드 및 Passport 키가 있어야 합니다.
다음으로, Laravel MCP에서 제공하는 Passport 인증 뷰를 게시해야 합니다:
php artisan vendor:publish --tag=mcp-views
그런 다음 Passport에게 Passport::authorizationView 메서드를 사용하여 이 뷰를 사용하도록 지시하십시오. 일반적으로 이 메서드는 애플리케이션의 AppServiceProvider의 boot 메서드에서 호출되어야 합니다:
use Laravel\Passport\Passport;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::authorizationView(function ($parameters) {
return view('mcp.authorize', $parameters);
});
}
이 뷰는 AI 에이전트의 인증 시도를 거부하거나 승인하기 위해 인증 중에 최종 사용자에게 표시됩니다.
[!NOTE] In this scenario, we’re simply using OAuth as a translation layer to the underlying authenticatable model. We are ignoring many aspects of OAuth, such as scopes.
Using an Existing Passport Installation
If your application is already using Laravel Passport, Laravel MCP should work seamlessly within your existing Passport installation, but custom scopes aren’t currently supported as OAuth is primarily used as a translation layer to the underlying authenticatable model.
Laravel MCP, via the Mcp::oauthRoutes method discussed above, adds, advertises, and uses a single mcp:use scope.
Passport vs. Sanctum
OAuth2.1 is the documented authentication mechanism in the Model Context Protocol specification, and is the most widely supported among MCP clients. For that reason, we recommend using Passport when possible.
If your application is already using Sanctum then adding Passport may be cumbersome. In this instance, we recommend using Sanctum without Passport until you have a clear, necessary requirement to use an MCP client that only supports OAuth.
Sanctum
If you would like to protect your MCP server using Sanctum, simply add Sanctum’s authentication middleware to your server in your routes/ai.php file. Then, ensure your MCP clients provide an Authorization: Bearer <token> header to ensure successful authentication:
use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/demo', WeatherExample::class)
->middleware('auth:sanctum');
사용자 정의 MCP 인증
애플리케이션이 자체 커스텀 API 토큰을 발급하는 경우, Mcp::web 경로에 원하는 미들웨어를 할당하여 MCP 서버를 인증할 수 있습니다. 사용자 정의 미들웨어는 수신된 MCP 요청을 인증하기 위해 Authorization 헤더를 수동으로 검사할 수 있습니다.
권한
$request->user() 메서드를 통해 현재 인증된 사용자에 접근할 수 있으며, 이를 통해 MCP 도구와 리소스 내에서 권한 확인을 수행할 수 있습니다:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
if (! $request->user()->can('read-weather')) {
return Response::error('Permission denied.');
}
// ...
}
MCP 클라이언트
서버를 구축하는 것 외에도, Laravel MCP에는 서드파티나 퍼스트파티 MCP 서버에 연결하기 위한 클라이언트가 포함되어 있습니다. 이 클라이언트를 사용하면 애플리케이션이 MCP 서버에서 제공하는 도구를 검색하고 호출할 수 있으며, 이는 특히 외부 MCP 서버가 제공하는 기능에 AI 에이전트가 접근할 수 있도록 하는 데 유용합니다.
서버에 연결하기
HTTP로 접근 가능한 MCP 서버에 Client::web 메서드를 사용하여 서버의 URL을 전달함으로써 연결할 수 있습니다:
use Laravel\Mcp\Client;
$client = Client::web('https://mcp.example.com');
명령어로 실행되는 로컬 MCP 서버에 연결하려면, Client::local 방법을 사용하여 서버를 시작하는 데 필요한 명령과 모든 인수를 제공하십시오:
use Laravel\Mcp\Client;
$client = Client::local('php', ['artisan', 'mcp:start']);
클라이언트는 지연 연결되며, 도구를 나열하거나 호출할 때 처음으로 자동으로 연결을 설정합니다. 연결을 수동으로 관리해야 하는 경우, connect, connected, disconnect 메서드를 사용할 수 있습니다:
$client->connect();
if ($client->connected()) {
$capabilities = $client->capabilities();
$server = $client->serverInfo();
}
$client->disconnect();
withTimeout 메서드를 사용하여 요청 제한 시간을 사용자 정의할 수 있습니다:
$client = Client::web('https://mcp.example.com')->withTimeout(30);
명명된 클라이언트
매번 클라이언트를 생성하는 대신, 재사용 가능한 명명된 클라이언트를 등록할 수 있습니다. 이는 일반적으로 서비스 제공자의 boot 메서드에서 Mcp 퍼사드를 사용하여 수행됩니다:
use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;
Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com'));
한 번 등록되면, 애플리케이션 내 어디에서든 이름으로 클라이언트를 해결할 수 있습니다:
use Laravel\Mcp\Facades\Mcp;
$client = Mcp::client('github');
지정된 클라이언트는 요청당 한 번만 해결되며, 요청 생명주기가 끝나면 자동으로 연결이 끊깁니다.
클라이언트 인증
Bearer 토큰으로 보호된 웹 MCP 서버에 연결하려면 withToken 방법을 사용하십시오. 토큰 문자열을 전달하거나 토큰을 지연적으로 해결하는 클로저를 전달할 수 있습니다:
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로 보호된 서버의 경우, withOAuth 방법을 사용하여 클라이언트를 구성하십시오. 이것은 자신의 서버를 OAuth로 보호하는 것에 대한 클라이언트 측 대응입니다:
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'),
));
[!NOTE]
clientId및clientSecret인수는 생략할 수 있습니다. Laravel은 권한 부여 서버가 이를 지원할 경우 클라이언트 ID 메타데이터 문서를 사용하며, 이전 서버의 경우에는 동적 클라이언트 등록으로 대체됩니다.
권한 부여 서버는 권한 부여 서버 메타데이터에서 S256 PKCE 코드 챌린지 방식을 지원한다고 광고해야 합니다. PKCE 지원이 광고되지 않은 경우 Laravel은 권한 부여 시도를 거부합니다.
다음으로, routes/ai.php 파일에서 oAuthRoutesFor 메서드를 사용하여 명명된 클라이언트의 OAuth 경로를 등록합니다. 제공하는 클로저는 권한 부여 코드가 액세스 토큰으로 교환된 후 클라이언트 이름과 결과 TokenSet를 받습니다:
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');
});
이것은 세 가지 이름이 지정된 라우트를 등록합니다: 사용자를 인증 서버로 리디렉션하는 연결 라우트(mcp.oauth.{client}.connect), 인증 코드를 교환하고 사용자의 핸들러를 호출하는 콜백 라우트(mcp.oauth.{client}.callback), 그리고 공개 클라이언트 ID 메타데이터 문서 라우트(mcp.oauth.{client}.client-metadata)입니다. 연결 및 콜백 라우트는 기본적으로 web 미들웨어 그룹을 사용하며, middleware 인수를 사용하여 이를 재정의할 수 있습니다. 메타데이터 라우트는 인증 서버가 이를 가져올 수 있어야 하기 때문에 이 미들웨어를 사용하지 않습니다.
메타데이터 문서는 귀하의 애플리케이션을 공개 OAuth 클라이언트로 설명하며, 애플리케이션의 APP_URL를 사용하여 클라이언트 ID와 콜백 URL을 생성합니다. 따라서 프로덕션 환경에서 APP_URL 환경 변수가 올바르게 설정되어 있는지 확인해야 합니다. 메타데이터 경로를 사용자 정의하고 clientMetadataUri 및 clientMetadata 인수를 사용하여 추가 메타데이터를 제공할 수 있습니다:
use Laravel\Mcp\Client\OAuth\TokenSet;
use Laravel\Mcp\Facades\Mcp;
Mcp::oAuthRoutesFor(
'github',
function (string $client, TokenSet $token) {
// Store the token...
return redirect('/dashboard');
},
clientMetadataUri: 'oauth/github/client.json',
clientMetadata: [
'client_name' => 'Acme Weather Dashboard',
'logo_uri' => 'https://acme.com/logo.png',
],
);
승인 흐름을 시작하려면 사용자를 연결 경로로 리디렉션하십시오:
return redirect()->route('mcp.oauth.github.connect');
도구
tools 메서드를 사용하여 MCP 서버가 제공하는 도구를 검색할 수 있으며, 이 메서드는 이름을 키로 하는 도구 모음을 반환합니다:
use Laravel\Mcp\Facades\Mcp;
$tools = Mcp::client('github')->tools();
foreach ($tools as $tool) {
$tool->name;
$tool->title;
$tool->description;
$tool->inputSchema;
}
클라이언트는 사용 가능한 모든 도구를 자동으로 페이징합니다. limit 인수를 사용하여 반환되는 도구의 수를 제한할 수 있습니다:
$tools = Mcp::client('github')->tools(limit: 10);
도구를 호출하려면 callTool 메서드를 사용하고, 도구 이름과 인수 배열을 전달하세요. 반환된 ToolResult 인스턴스는 도구 응답을 제공합니다:
use Laravel\Mcp\Facades\Mcp;
$result = Mcp::client('github')->callTool('current-weather', [
'location' => 'New York',
]);
$result->text(); // The text content of the response...
(string) $result; // Equivalent to calling text()...
$result->isError; // Whether the tool reported an error...
$result->structuredContent; // Structured content, if any...
또는 나열된 도구 인스턴스에서 직접 도구를 호출할 수도 있습니다:
$tools = Mcp::client('github')->tools();
$result = $tools['current-weather']->call([
'location' => 'New York',
]);
만약 당신이 Laravel AI SDK를 사용하여 에이전트를 구축하고 있다면, 모델이 프롬프트에 응답하는 동안 호출할 수 있도록 MCP 클라이언트에서 에이전트로 직접 도구를 제공할 수도 있습니다. 자세한 내용은 AI SDK 문서의 MCP 도구 섹션을 참조하세요.
프롬프트
prompts 메서드를 사용하여 MCP 서버가 공개한 프롬프트를 검색할 수 있으며, 이는 이름으로 키가 지정된 프롬프트 컬렉션을 반환합니다:
use Laravel\Mcp\Facades\Mcp;
$prompts = Mcp::client('github')->prompts();
foreach ($prompts as $prompt) {
$prompt->name;
$prompt->title;
$prompt->description;
$prompt->arguments;
}
클라이언트는 사용 가능한 모든 프롬프트를 자동으로 페이지 단위로 나눕니다. limit 인수를 사용하여 반환되는 프롬프트 수를 제한할 수 있습니다:
$prompts = Mcp::client('github')->prompts(limit: 10);
프롬프트를 가져오려면 getPrompt 메서드를 사용하고, 프롬프트 이름과 인수 배열을 전달하십시오. 반환된 PromptResult 인스턴스는 생성된 메시지를 제공합니다:
use Laravel\Mcp\Facades\Mcp;
$result = Mcp::client('github')->getPrompt('describe-weather', [
'location' => 'New York',
]);
$result->text(); // The text content of the messages...
(string) $result; // Equivalent to calling text()...
$result->messages; // The raw messages returned by the prompt...
$result->description; // The prompt description, if any...
리소스
MCP 서버가 노출한 리소스를 resources 방법을 사용하여 가져올 수 있으며, 이 방법은 URI를 키로 하는 리소스 컬렉션을 반환합니다:
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;
}
클라이언트는 사용 가능한 모든 리소스를 자동으로 페이징합니다. limit 인수를 사용하여 반환되는 리소스 수를 제한할 수 있습니다:
$resources = Mcp::client('github')->resources(limit: 10);
리소스를 읽으려면, 리소스 URI를 전달하여 readResource 메서드를 사용하십시오. 반환된 ResourceReadResult 인스턴스는 리소스 내용을 제공합니다:
use Laravel\Mcp\Facades\Mcp;
$result = Mcp::client('github')->readResource('weather://guidelines');
$result->content(); // The content of the resource, decoding base64 blobs as needed...
(string) $result; // Equivalent to calling content()...
$result->mimeType(); // The MIME type of the resource, if any...
$result->contents; // The raw contents returned by the resource...
서버 테스트
내장 MCP 검사기(MCP Inspector)를 사용하거나 단위 테스트를 작성하여 MCP 서버를 테스트할 수 있습니다.
MCP 검사기
MCP 검사기는 MCP 서버를 테스트하고 디버깅하기 위한 상호작용 도구입니다. 이를 사용하여 서버에 연결하고, 인증을 확인하며, 도구, 리소스 및 프롬프트를 시험해 볼 수 있습니다.
등록된 서버라면 어느 서버에서든 검사기를 실행할 수 있습니다:
# Web server...
php artisan mcp:inspector mcp/weather
# Local server named "weather"...
php artisan mcp:inspector weather
이 명령은 MCP 인스펙터를 실행하고 클라이언트 설정을 제공하며, 이를 MCP 클라이언트에 복사하여 모든 설정이 올바르게 구성되었는지 확인할 수 있습니다. 웹 서버가 인증 미들웨어로 보호되는 경우, 연결 시 Authorization 베어러 토큰과 같은 필요한 헤더를 포함해야 합니다.
단위 테스트
MCP 서버, 도구, 리소스 및 프롬프트에 대한 단위 테스트를 작성할 수 있습니다.
시작하려면 새 테스트 케이스를 생성하고 등록하는 서버에서 원하는 프리미티브를 호출하십시오. 예를 들어, WeatherServer에서 도구를 테스트하려면:```php tab=Pest
test(‘tool’, function () {
$response = WeatherServer::tool(CurrentWeatherTool::class, [
‘location’ => ‘New York City’,
‘units’ => ‘fahrenheit’,
]);
$response
->assertOk()
->assertSee('The current weather in New York City is 72°F and sunny.'); }); ```
```php tab=PHPUnit /**
-
Test a tool. */ public function test_tool(): void { $response = WeatherServer::tool(CurrentWeatherTool::class, [ ‘location’ => ‘New York City’, ‘units’ => ‘fahrenheit’, ]);
$response ->assertOk() ->assertSee(‘The current weather in New York City is 72°F and sunny.’); } ```
마찬가지로, 프롬프트와 자료를 테스트할 수 있습니다:
$response = WeatherServer::prompt(...);
$response = WeatherServer::resource(...);
원시 함수를 호출하기 전에 actingAs 메서드를 체인함으로써 인증된 사용자로서 동작할 수도 있습니다:
$response = WeatherServer::actingAs($user)->tool(...);
응답을 받으면 다양한 검증 방법을 사용하여 응답의 내용과 상태를 확인할 수 있습니다.
응답이 성공적임을 assertOk 방법을 사용하여 검증할 수 있습니다. 이 방법은 응답에 오류가 없는지 확인합니다:
$response->assertOk();
assertSee 방법을 사용하여 응답에 특정 텍스트가 포함되어 있다고 주장할 수 있습니다:
$response->assertSee('The current weather in New York City is 72°F and sunny.');
assertHasErrors 방법을 사용하여 응답에 오류가 있다고 주장할 수 있습니다:
$response->assertHasErrors();
$response->assertHasErrors([
'Something went wrong.',
]);
assertHasNoErrors 방법을 사용하여 응답에 오류가 없다고 주장할 수 있습니다:
$response->assertHasNoErrors();
assertName(), assertTitle(), assertDescription() 메서드를 사용하여 응답에 특정 메타데이터가 포함되어 있다고 주장할 수 있습니다:
$response->assertName('current-weather');
$response->assertTitle('Current Weather Tool');
$response->assertDescription('Fetches the current weather forecast for a specified location.');
알림이 assertSentNotification 및 assertNotificationCount 방법을 사용하여 전송되었다고 주장할 수 있습니다:
$response->assertSentNotification('processing/progress', [
'step' => 1,
'total' => 5,
]);
$response->assertSentNotification('processing/progress', [
'step' => 2,
'total' => 5,
]);
$response->assertNotificationCount(5);
마지막으로, 원시 응답 내용을 검사하고 싶다면 dd 또는 dump 메서드를 사용하여 디버깅 목적으로 응답을 출력할 수 있습니다:
$response->dd();
$response->dump();