본문 바로가기

Laravel MCP

업데이트됨

번역일: 2026년 10월 26일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 10월 10일
번역 갱신
2026년 10월 26일

Laravel MCP

소개

Laravel MCP는 Model Context Protocol을 통해 AI 클라이언트가 Laravel 애플리케이션과 상호작용할 수 있도록 해 주는, 간단하고 우아한 방법을 제공합니다. 서버, Tool, 리소스, 프롬프트를 표현력 있는 fluent 인터페이스로 정의할 수 있어, 애플리케이션 위에서 AI 기반 상호작용을 손쉽게 구현할 수 있습니다.

NOTE

MCP(Model Context Protocol)는 AI 클라이언트(예: Claude, 코딩 에이전트)가 외부 애플리케이션의 기능과 데이터를 표준화된 방식으로 호출하도록 하는 프로토콜입니다. 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 서버를 생성할 수 있습니다. 서버는 Tool, 리소스, 프롬프트와 같은 MCP 기능을 AI 클라이언트에 노출하는 중심 통신 지점 역할을 합니다.

php artisan make:mcp-server WeatherServer

이 명령어는 app/Mcp/Servers 디렉터리에 새 서버 클래스를 생성합니다. 생성된 서버 클래스는 Laravel MCP의 기본 Laravel\Mcp\Server 클래스를 상속하며, 서버를 설정하고 Tool, 리소스, 프롬프트를 등록하기 위한 속성(attribute)과 프로퍼티를 제공합니다.

<?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는 서버 디스커버리, 프리미티브 목록 조회, 리소스 읽기처럼 캐시될 수 있는 응답에 캐시 힌트를 포함합니다. 기본적으로 이러한 응답은 private으로 표시되며 TTL(유효 시간)은 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 속성은 메서드별 힌트와 서버의 기본 힌트보다 모두 우선합니다.

Tool

Tool은 서버가 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(), ]; } }

Tool 생성

Tool을 생성하려면 make:mcp-tool Artisan 명령어를 실행합니다.

php artisan make:mcp-tool CurrentWeatherTool

Tool을 만든 뒤에는 서버의 $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, ]; }

검색 가능한 Tool 카탈로그

Tool이 많은 서버에서는 모든 Tool을 AI 클라이언트에 일일이 알리는 대신, 일부 Tool을 검색 가능한 카탈로그에 넣을 수 있습니다. 검색 가능한 카탈로그는 두 가지 Tool을 노출합니다. 하나는 Tool 이름, 설명, 입력 스키마를 기준으로 카탈로그를 검색하는 search_tools이고, 다른 하나는 검색으로 찾은 Tool을 하나 이상 호출하는 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은 AI 클라이언트에 직접 노출되고, 과거 날씨 Tool과 기상 경보 Tool은 검색 가능한 카탈로그를 통해 사용할 수 있습니다. 카탈로그의 Tool을 검색하거나 실행할 때도 조건부 Tool 등록 규칙은 그대로 적용됩니다.

execute_tools 한 번의 호출에서 실행할 수 있는 Tool의 최대 개수와 응답의 최대 크기는 각각 mcp.tool_search.max_tool_calls, mcp.tool_search.max_output_bytes 설정 값으로 제어합니다.

NOTE

Tool이 수십 개로 늘어나면 모든 Tool 정의를 AI 클라이언트의 컨텍스트에 넣는 비용이 커집니다. 자주 쓰는 Tool만 직접 노출하고 나머지는 검색 카탈로그에 두면 컨텍스트 사용량을 줄일 수 있습니다.

Tool 이름, 제목, 설명

기본적으로 Tool의 이름과 제목은 클래스 이름에서 파생됩니다. 예를 들어 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 { // ... }

Tool 설명은 자동으로 생성되지 않습니다. 항상 Description 속성으로 의미 있는 설명을 제공해야 합니다.

use Laravel\Mcp\Server\Attributes\Description; #[Description('Fetches the current weather forecast for a specified location.')] class CurrentWeatherTool extends Tool { // }

NOTE

설명은 Tool 메타데이터에서 매우 중요한 부분입니다. AI 모델이 Tool을 언제, 어떻게 사용해야 하는지 판단하는 데 도움이 되기 때문입니다.

Tool 입력 스키마

Tool은 입력 스키마를 정의하여 AI 클라이언트로부터 받을 인자를 지정할 수 있습니다. Laravel의 Illuminate\Contracts\JsonSchema\JsonSchema 빌더를 사용해 Tool의 입력 요구 사항을 정의합니다.

<?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'), ]; } }

Tool 출력 스키마

Tool은 출력 스키마를 정의하여 응답의 구조를 지정할 수 있습니다. 이렇게 하면 파싱 가능한 Tool 결과가 필요한 AI 클라이언트와 더 잘 통합됩니다. outputSchema 메서드를 사용해 Tool의 출력 구조를 정의합니다.

<?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(), ]; } }

Tool 인자 유효성 검증

JSON Schema 정의는 Tool 인자의 기본적인 구조를 제공하지만, 더 복잡한 유효성 검증 규칙을 적용해야 할 때도 있습니다.

Laravel MCP는 Laravel의 유효성 검증 기능과 매끄럽게 통합됩니다. Tool의 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.', ]);

Tool 의존성 주입

모든 Tool은 Laravel 서비스 컨테이너를 통해 해석(resolve)됩니다. 따라서 Tool에 필요한 의존성을 생성자에 타입 힌트로 선언하기만 하면, 선언한 의존성이 자동으로 해석되어 Tool 인스턴스에 주입됩니다.

<?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, ) {} // ... }

생성자 주입 외에도 Tool의 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); // ... } }

Tool 애너테이션

애너테이션을 사용하면 Tool에 추가 메타데이터를 부여하여 AI 클라이언트에 전달할 수 있습니다. 이 애너테이션은 AI 모델이 Tool의 동작 방식과 기능을 이해하는 데 도움이 됩니다. 애너테이션은 속성(attribute)을 통해 Tool에 추가합니다.

<?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 { // }

사용할 수 있는 애너테이션은 다음과 같습니다.

애너테이션타입설명
#[IsReadOnly]booleanTool이 자신의 실행 환경을 변경하지 않음을 나타냅니다.
#[IsDestructive]booleanTool이 파괴적인 업데이트를 수행할 수 있음을 나타냅니다(읽기 전용이 아닐 때만 의미가 있습니다).
#[IsIdempotent]boolean같은 인자로 반복 호출해도 추가적인 효과가 없음을 나타냅니다(읽기 전용이 아닐 때).
#[IsOpenWorld]booleanTool이 외부 엔터티와 상호작용할 수 있음을 나타냅니다.

애너테이션 값은 boolean 인자를 사용해 명시적으로 지정할 수도 있습니다.

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 { // }

조건부 Tool 등록

Tool 클래스에 shouldRegister 메서드를 구현하면 런타임에 조건부로 Tool을 등록할 수 있습니다. 이 메서드를 통해 애플리케이션 상태, 설정 또는 요청 파라미터에 따라 Tool을 사용할 수 있는지 결정할 수 있습니다.

<?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; } }

Tool의 shouldRegister 메서드가 false를 반환하면, 해당 Tool은 사용 가능한 Tool 목록에 나타나지 않으며 AI 클라이언트가 호출할 수도 없습니다.

Tool 응답

Tool은 반드시 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'); }

Tool 실행 중 오류가 발생했음을 나타내려면 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 인스턴스의 배열을 반환하면 Tool이 여러 개의 콘텐츠를 반환할 수 있습니다.

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 메서드를 사용하면 Tool이 구조화된 콘텐츠를 반환할 수 있습니다. AI 클라이언트에는 파싱 가능한 데이터를 제공하면서, JSON으로 인코딩된 텍스트 표현도 함께 제공하여 하위 호환성을 유지합니다.

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', ]);

스트리밍 응답

오래 걸리는 작업이나 실시간 데이터 스트리밍이 필요한 경우, Tool은 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) 스트림을 열고, yield된 각 메시지를 이벤트로 클라이언트에 전송합니다.

프롬프트

프롬프트를 사용하면 서버가 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 인스턴스의 iterable을 반환할 수 있습니다. 이 응답들은 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 클라이언트가 리소스 콘텐츠를 어떻게 처리하고 해석할지 결정하는 데 도움이 됩니다.

리소스 요청

Tool이나 프롬프트와 달리 리소스는 입력 스키마나 인자를 정의할 수 없습니다. 그래도 리소스의 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 클라이언트에 전달할 수 있습니다. 애너테이션은 속성(attribute)을 통해 리소스에 추가합니다.

<?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 { // }

사용할 수 있는 애너테이션은 다음과 같습니다.

애너테이션타입설명
#[Audience]Role 또는 배열대상 독자를 지정합니다(Role::User, Role::Assistant 또는 둘 다).
#[Priority]float리소스의 중요도를 나타내는 0.0에서 1.0 사이의 숫자 점수입니다.
#[LastModified]string리소스가 마지막으로 업데이트된 시각을 나타내는 ISO 8601 타임스탬프입니다.

조건부 리소스 등록

리소스 클래스에 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')));

blob 콘텐츠를 반환할 때 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 Apps를 지원합니다. MCP Apps는 Model Context Protocol의 확장으로, 지원하는 호스트에서 Tool이 샌드박스 처리된 iframe 안에 인터랙티브 HTML 애플리케이션을 렌더링할 수 있게 해 줍니다. 이를 통해 대시보드, 폼, 시각화 등 일반 텍스트 응답을 넘어서는 풍부한 경험을 만들 수 있습니다.

MCP 앱은 함께 동작하는 두 부분으로 구성됩니다.

  • 애플리케이션의 자체 완결형 HTML을 반환하는 앱 리소스
  • #[RendersApp] 속성으로 앱 리소스와 연결된 Tool. Tool이 호출되면 호스트가 연결된 리소스를 가져와 렌더링합니다.

앱 리소스 생성

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 명세를 참고하세요.

Tool에서 앱 렌더링

앱 리소스를 표시하려면 #[RendersApp] 속성으로 Tool을 앱 리소스에 연결합니다. Tool이 호출되면 Laravel MCP가 Tool 메타데이터에 리소스의 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.'); } }

AppResource가 하나라도 등록되면 Laravel MCP는 서버의 extensions 기능(capability)에 io.modelcontextprotocol/ui 확장을 자동으로 알리므로, 별도의 서버 설정은 필요하지 않습니다.

앱 Tool 가시성

각 #[RendersApp] Tool은 visibility 인자로 누가 해당 Tool을 호출할 수 있는지 제한할 수 있습니다. UI가 데이터를 불러오거나 갱신하기 위해 호출하는 앱 전용 비공개 Tool을 만들고, 이를 모델에는 노출하지 않으려 할 때 유용합니다.

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의 콘텐츠 보안 정책(CSP), 브라우저 권한, 그리고 뷰의 <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 { // ... }

Library 열거형에는 Library::Tailwind, Library::Alpine처럼 자주 쓰는 프런트엔드 라이브러리용으로 미리 구성된 CDN 스크립트가 들어 있으며, 해당 CDN 출처(origin)는 CSP에 자동으로 병합됩니다. Permission 열거형은 Camera, Microphone, Geolocation, ClipboardWrite 같은 브라우저 권한을 다룹니다.

값을 계산하거나 동적으로 설정해야 한다면, 리소스의 appMeta 메서드를 오버라이드하고 Laravel\Mcp\Server\Ui 네임스페이스의 AppMeta, Csp, Permissions 플루언트 빌더를 사용하세요.

Boost로 앱 만들기

Laravel MCP에는 MCP 앱 제작을 위한 Boost 스킬 레퍼런스가 포함되어 있습니다. Laravel Boost가 설치되어 있다면 AI 코딩 에이전트가 mcp-development 스킬을 호출해 앱 리소스, Blade 뷰, 연결된 도구를 한 번에 스캐폴딩하도록 요청할 수 있습니다.

클라이언트 측 API 전체와 스키마 세부 사항을 포함한 전체 프로토콜 레퍼런스는 공식 MCP Apps 문서를 참고하세요.

메타데이터

Laravel MCP는 MCP 사양에 정의된 _meta 필드도 지원합니다. 일부 MCP 클라이언트나 연동에서 이 필드를 요구합니다. 메타데이터는 도구, 리소스, 프롬프트를 비롯한 모든 MCP 프리미티브와 그 응답에 적용할 수 있습니다.

개별 응답 콘텐츠에는 withMeta 메서드로 메타데이터를 붙일 수 있습니다.

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]); }

응답 전체(envelope)에 적용되는 결과 수준 메타데이터가 필요하다면, 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 메서드로 정의한 아이콘은 자동으로 합쳐집니다. 아이콘 경로는 다음 규칙으로 해석됩니다.

  • https:나 data:처럼 URI 스킴이 있는 경로는 그대로 사용됩니다.
  • 상대 경로는 라라벨의 asset 헬퍼를 통해 URL로 변환됩니다.

인증

라우트와 마찬가지로 미들웨어로 웹 MCP 서버를 인증할 수 있습니다. MCP 서버에 인증을 추가하면 사용자는 서버의 어떤 기능을 쓰기 전에 먼저 인증해야 합니다.

이 절은 외부 AI 클라이언트가 여러분 애플리케이션의 MCP 서버에 접근하는 경우를 다룹니다. 사용자의 자격 증명으로 애플리케이션이 다른 서비스의 MCP 서버에 연결하는 방법은 클라이언트 인증을 참고하세요.

MCP 서버 접근을 인증하는 방법은 두 가지입니다. 하나는 Laravel Sanctum을 이용한 간단한 토큰 기반 인증이나 Authorization HTTP 헤더로 전달되는 임의의 토큰을 사용하는 방식이고, 다른 하나는 Laravel Passport를 이용한 OAuth 인증입니다.

OAuth 2.1

웹 기반 MCP 서버를 보호하는 가장 견고한 방법은 Laravel Passport를 이용한 OAuth입니다.

OAuth로 MCP 서버를 인증할 때는 routes/ai.php 파일에서 Mcp::oauthRoutes 메서드를 호출해 필요한 OAuth2 디스커버리 및 클라이언트 등록 라우트를 등록하세요. 그런 다음 routes/ai.php의 Mcp::web 라우트에 Passport의 auth:api 미들웨어를 적용합니다.

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::authorizationView 메서드로 Passport가 이 뷰를 사용하도록 지정합니다. 보통 애플리케이션의 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

원문에는 이 위치에 권한 부여 화면 예시 스크린샷(base64 data URI 이미지, 대체 텍스트 "Authorization screen example")이 포함되어 있으나, 이 단계의 번역 결과에는 인라인 base64 데이터를 옮기지 않았습니다.

NOTE

이 시나리오에서 OAuth는 내부의 인증 가능한(authenticatable) 모델로 연결해 주는 변환 계층 역할만 합니다. 스코프(scope)를 비롯한 OAuth의 많은 요소는 사용하지 않습니다.

기존 Passport 설치 사용하기

애플리케이션이 이미 Laravel Passport를 사용 중이라면, Laravel MCP는 기존 Passport 설치 위에서 문제없이 동작합니다. 다만 OAuth를 주로 내부 인증 가능 모델로의 변환 계층으로 쓰기 때문에, 커스텀 스코프는 현재 지원되지 않습니다.

앞서 설명한 Mcp::oauthRoutes 메서드를 호출하면 Laravel MCP가 mcp:use 스코프 하나를 추가하고, 이를 공개(advertise)하며, 실제로 사용합니다.

Passport와 Sanctum 비교

OAuth 2.1은 Model Context Protocol 사양에 문서화된 인증 방식이며 MCP 클라이언트 사이에서 가장 널리 지원됩니다. 그래서 가능하다면 Passport를 사용할 것을 권장합니다.

이미 Sanctum을 사용 중인 애플리케이션이라면 Passport를 추가하는 일이 번거로울 수 있습니다. 이 경우에는 OAuth만 지원하는 MCP 클라이언트를 반드시 써야 하는 분명한 요구 사항이 생기기 전까지 Passport 없이 Sanctum만 사용하기를 권장합니다.

OAuth 라우트에 요청 제한 적용하기

MCP 사양과 RFC 7591이 요구하는 대로, Mcp::oauthRoutes가 등록하는 동적 클라이언트 등록 라우트는 공개되어 있어 어떤 MCP 클라이언트든 인증 없이 스스로를 등록할 수 있습니다. 등록할 때마다 데이터베이스에 새 OAuth 클라이언트가 생성되므로, 애플리케이션 트래픽에 맞는 요청 제한(rate limiting)으로 이 라우트를 보호해야 합니다. 라라벨의 throttle 미들웨어를 적용한 라우트 그룹 안에서 라우트를 등록하면 됩니다.

use Illuminate\Support\Facades\Route; use Laravel\Mcp\Facades\Mcp; Route::middleware('throttle:10,1')->group(function () { Mcp::oauthRoutes(); });

MCP 클라이언트가 동적으로 스스로를 등록할 필요가 없다면 registration 인수에 false를 전달해 클라이언트 등록 라우트를 완전히 비활성화할 수 있습니다.

Mcp::oauthRoutes(registration: false);

Sanctum

Sanctum으로 MCP 서버를 보호하려면 routes/ai.php 파일에서 서버에 Sanctum의 인증 미들웨어를 추가하기만 하면 됩니다. 그런 다음 MCP 클라이언트가 Authorization: Bearer <token> 헤더를 보내도록 해야 인증이 성공합니다.

use App\Mcp\Servers\WeatherExample; use Laravel\Mcp\Facades\Mcp; Mcp::web('/mcp/demo', WeatherExample::class) ->middleware('auth:sanctum');

커스텀 MCP 인증

애플리케이션이 자체 API 토큰을 발급한다면, Mcp::web 라우트에 원하는 미들웨어를 지정해 MCP 서버를 인증할 수 있습니다. 커스텀 미들웨어에서 Authorization 헤더를 직접 확인해 들어오는 MCP 요청을 인증하면 됩니다.

인가

$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는 서버를 만드는 기능 외에도 자사(first-party)·타사(third-party)를 가리지 않고 다른 MCP 서버에 연결하는 클라이언트를 제공합니다. 클라이언트를 사용하면 애플리케이션이 MCP 서버가 노출한 도구를 찾아 호출할 수 있으며, 외부 MCP 서버가 제공하는 기능을 AI 에이전트에 연결할 때 특히 유용합니다.

예를 들어 채팅 애플리케이션이 각 사용자의 GitHub 토큰으로 GitHub의 MCP 서버에 연결하면, 에이전트가 해당 사용자의 저장소에 관한 질문에 답할 수 있습니다. 이 경우 애플리케이션은 MCP 클라이언트이므로 MCP 서버를 만들거나 Mcp::web 라우트를 등록할 필요가 없습니다.

서버에 연결하기

HTTP로 접근 가능한 MCP 서버에는 서버 URL을 전달해 Client::web 메서드로 연결할 수 있습니다.

use Laravel\Mcp\Client; $client = Client::web('https://api.githubcopilot.com/mcp/');

명령어로 실행되는 로컬 MCP 서버에 연결하려면 Client::local 메서드에 서버를 시작하는 명령어와 필요한 인수를 전달하세요.

use Laravel\Mcp\Client; $client = Client::local('php', ['artisan', 'mcp:start']);

클라이언트는 지연 연결(lazy connection) 방식이라, 도구 목록을 조회하거나 도구를 처음 호출할 때 자동으로 연결을 맺습니다. 연결을 직접 관리해야 한다면 connect, connected, disconnect 메서드를 사용할 수 있습니다.

$client->connect(); if ($client->connected()) { $capabilities = $client->capabilities(); $server = $client->serverInfo(); } $client->disconnect();

withTimeout 메서드로 요청 타임아웃을 조정할 수 있습니다.

$client = Client::web('https://api.githubcopilot.com/mcp/')->withTimeout(30);

이름 있는 클라이언트

필요할 때마다 클라이언트를 새로 만드는 대신, 재사용할 수 있는 이름 있는 클라이언트를 등록할 수도 있습니다. registerClient 메서드는 외부로 나가는 연결에 이름을 붙일 뿐이며, MCP 서버를 노출하거나 사용자를 인가하지 않습니다. 등록은 보통 서비스 프로바이더의 boot 메서드에서 Mcp 파사드로 합니다.

use Laravel\Mcp\Client; use Laravel\Mcp\Facades\Mcp; Mcp::registerClient('github', fn () => Client::web('https://api.githubcopilot.com/mcp/'));

등록한 뒤에는 애플리케이션 어디서든 이름으로 클라이언트를 가져올 수 있습니다.

use Laravel\Mcp\Facades\Mcp; $client = Mcp::client('github');

이름 있는 클라이언트는 요청당 한 번만 해석(resolve)되며, 요청 라이프사이클이 끝나면 자동으로 연결이 해제됩니다.

클라이언트 인증

베어러 토큰

베어러 토큰으로 보호되는 웹 MCP 서버에 연결하려면 withToken 메서드를 사용하세요. 토큰 문자열을 직접 전달하거나, 토큰을 지연 방식으로 가져오는 클로저를 전달할 수 있습니다.

use Illuminate\Support\Facades\Auth; use Laravel\Mcp\Client; $client = Client::web('https://api.githubcopilot.com/mcp/')->withToken( config('services.github_mcp.token'), ); $client = Client::web('https://api.githubcopilot.com/mcp/')->withToken( fn () => Auth::user()->github_mcp_token, );

Socialite나 다른 계정 연동 흐름으로 이미 적절한 토큰을 발급받았다면 그 토큰을 withToken에 전달하면 됩니다. 이 경우 MCP OAuth 라우트를 등록할 필요가 없습니다.

OAuth로 사용자 계정 연동하기

사용자가 프로젝트 관리 서비스를 연결한 뒤 에이전트에게 "내 작업 중 기한이 지난 것은?" 같은 질문을 하는 채팅 애플리케이션을 생각해 보세요. 외부 프로젝트 관리 서비스는 OAuth로 보호되는 MCP 서버를 제공합니다. 애플리케이션은 각 사용자를 서비스의 동의 화면으로 리다이렉트하고, 사용자의 승인을 토큰으로 교환한 뒤, 이후 도구를 호출할 때 쓸 수 있도록 그 토큰을 저장해야 합니다.

Laravel MCP는 withOAuth와 Mcp::oAuthRoutesFor로 이 계정 연동 흐름을 처리합니다. 먼저 서비스 프로바이더에서 이름 있는 클라이언트를 등록하세요. OAuth 클라이언트 자격 증명은 서비스에 여러분의 애플리케이션을 식별시키고, withToken은 도구를 호출할 때 연결된 사용자의 저장된 액세스 토큰을 제공합니다.

use Illuminate\Support\Facades\Auth; use Laravel\Mcp\Client; use Laravel\Mcp\Facades\Mcp; Mcp::registerClient('projects', fn () => Client::web('https://projects.example.com/mcp') ->withOAuth( clientId: config('services.projects.client_id'), clientSecret: config('services.projects.client_secret'), ) ->withToken(fn () => Auth::user()->projects_token));

NOTE

clientId와 clientSecret 인수는 생략할 수 있습니다. 인가 서버가 Client ID Metadata Document를 지원하면 라라벨이 이를 사용하고, 레거시 서버라면 동적 클라이언트 등록으로 대체합니다.

인가 서버는 인가 서버 메타데이터에서 S256 PKCE 코드 챌린지 방식을 지원한다고 알려야(advertise) 합니다. PKCE 지원이 공개되어 있지 않으면 Laravel은 인가 시도를 거부합니다.

다음으로 routes/ai.php 파일에 계정 연동 라우트를 등록합니다. 콜백은 클라이언트 이름과 결과 TokenSet을 전달받으므로, 로그인한 사용자를 위해 토큰을 저장할 수 있습니다.

use Illuminate\Support\Facades\Auth; use Laravel\Mcp\Client\OAuth\TokenSet; use Laravel\Mcp\Facades\Mcp; Mcp::oAuthRoutesFor('projects', function (string $client, TokenSet $token) { Auth::user()->update([ 'projects_token' => $token->accessToken, ]); return redirect('/chat'); }, middleware: ['web', 'auth']);

oAuthRoutesFor 메서드는 이름이 붙은 라우트 세 개를 등록합니다. 사용자를 인가 서버로 리다이렉트하는 연결 라우트(mcp.oauth.{client}.connect), 인가 코드를 교환하고 여러분의 핸들러를 호출하는 콜백 라우트(mcp.oauth.{client}.callback), 그리고 공개 Client ID Metadata Document 라우트(mcp.oauth.{client}.client-metadata)입니다.

연결 라우트와 콜백 라우트는 기본적으로 web 미들웨어 그룹을 사용합니다. 위의 계정 연동 예제는 로그인한 사용자를 요구하도록 auth를 추가했습니다. 메타데이터 라우트에는 이 미들웨어를 적용하지 않는데, 인가 서버가 이 문서를 가져올 수 있어야 하기 때문입니다.

"프로젝트 서비스 연결" 버튼은 사용자를 연결 라우트로 보내면 됩니다.

return redirect()->route('mcp.oauth.projects.connect');

Laravel은 사용자를 서비스로 리다이렉트해 접근을 승인하게 하고, 사용자가 돌아오면 인가 코드를 교환한 뒤 콜백을 호출합니다. 이후 채팅 요청에서는 이름 있는 클라이언트가 저장된 토큰을 사용합니다. 예를 들어 AI SDK 에이전트가 해당 서비스의 도구를 노출할 수 있습니다.

public function tools(): iterable { return [ ...Mcp::client('projects')->tools(), ]; }

토큰은 암호화 캐스트로 저장하고 모델 직렬화에서 숨기세요. 서비스가 만료되는 토큰을 발급한다면 TokenSet의 리프레시 토큰과 만료 정보도 함께 저장하고, 도구를 호출하기 전에 갱신을 처리해야 합니다. 큐에서 실행되는 에이전트에서는 Auth::user() 대신 명시적으로 전달받은 사용자로 자격 증명을 가져오세요.

NOTE

Mcp::oAuthRoutesFor는 사용자를 외부 서비스에 연결합니다. 반대로 Mcp::oauthRoutes는 외부 AI 클라이언트가 Passport를 이용해 여러분의 MCP 서버에 인증하도록 해 줍니다. 위의 계정 연동 흐름에는 Passport도 Mcp::oauthRoutes도 필요하지 않습니다.

클라이언트 메타데이터

메타데이터 문서는 애플리케이션을 공개(public) OAuth 클라이언트로 설명하며, 애플리케이션의 APP_URL을 사용해 클라이언트 ID와 콜백 URL을 생성합니다. 따라서 운영 환경에서는 APP_URL 환경 변수가 올바르게 설정되어 있는지 반드시 확인하세요. clientMetadataUri와 clientMetadata 인수로 메타데이터 라우트를 변경하고 추가 메타데이터를 제공할 수 있습니다.

use Laravel\Mcp\Client\OAuth\TokenSet; use Laravel\Mcp\Facades\Mcp; Mcp::oAuthRoutesFor( 'projects', function (string $client, TokenSet $token) { // Store the token... return redirect('/dashboard'); }, clientMetadataUri: 'oauth/projects/client.json', clientMetadata: [ 'client_name' => 'Acme Dashboard', 'logo_uri' => 'https://acme.com/logo.png', ], );

도구

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('list_issues', [ 'owner' => 'laravel', 'repo' => 'framework', ]); $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['list_issues']->call([ 'owner' => 'laravel', 'repo' => 'framework', ]);

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('issue_to_fix_workflow', [ 'owner' => 'laravel', 'repo' => 'framework', 'title' => 'Fix typo in README', 'description' => 'The installation section has a typo.', ]); $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...

리소스

resources 메서드로 MCP 서버가 노출한 리소스를 가져올 수 있습니다. 이 메서드는 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);

리소스를 읽으려면 readResource 메서드에 리소스 URI를 전달하세요. 반환되는 ResourceReadResult 인스턴스가 리소스 내용을 제공합니다.

use Laravel\Mcp\Facades\Mcp; $result = Mcp::client('github')->readResource('repo://laravel/framework/contents/README.md'); $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 Inspector를 사용하거나 단위 테스트를 작성해 MCP 서버를 테스트할 수 있습니다.

MCP Inspector

MCP Inspector는 MCP 서버를 테스트하고 디버깅하기 위한 인터랙티브 도구입니다. 이 도구로 서버에 연결해 인증을 확인하고 도구, 리소스, 프롬프트를 직접 실행해 볼 수 있습니다.

등록된 모든 서버에 대해 인스펙터를 실행할 수 있습니다.

# Web server...php artisan mcp:inspector mcp/weather# Local server named "weather"...php artisan mcp:inspector weather

이 명령은 MCP Inspector를 실행하고, MCP 클라이언트에 복사해 넣을 수 있는 클라이언트 설정값을 알려 줍니다. 이 값으로 설정이 올바른지 확인할 수 있습니다. 웹 서버가 인증 미들웨어로 보호되어 있다면, 연결할 때 Authorization 베어러 토큰 같은 필수 헤더를 반드시 포함하세요.

단위 테스트

MCP 서버, 도구, 리소스, 프롬프트에 대한 단위 테스트를 작성할 수 있습니다.

시작하려면 새 테스트 케이스를 만들고, 대상 프리미티브를 등록한 서버에서 해당 프리미티브를 호출하세요. 예를 들어 WeatherServer의 도구를 테스트하려면 다음과 같이 작성합니다.

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.'); });

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 메서드로 알림(notification)이 전송되었는지 단언할 수 있습니다.

$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();

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 10월 26일