본문 바로가기

Laravel MCP

번역일: 2026년 6월 20일

Laravel MCP

소개

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

설치

Composer를 사용해 Laravel MCP를 프로젝트에 설치합니다:

composer require 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; class WeatherServer extends Server { /** * 이 MCP 서버에 등록된 도구 목록. * * @var array<int, class-string<\Laravel\Mcp\Server\Tool>> */ protected array $tools = [ // ExampleTool::class, ]; /** * 이 MCP 서버에 등록된 리소스 목록. * * @var array<int, class-string<\Laravel\Mcp\Server\Resource>> */ protected array $resources = [ // ExampleResource::class, ]; /** * 이 MCP 서버에 등록된 프롬프트 목록. * * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>> */ protected array $prompts = [ // ExamplePrompt::class, ]; }

서버 등록

서버를 생성한 뒤에는 routes/ai.php 파일에 등록해야 외부에서 접근할 수 있습니다. Laravel MCP는 두 가지 등록 방식을 제공합니다. web은 HTTP로 접근 가능한 서버, 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 명령어로 실행되며, 개발, 테스트, 또는 로컬 AI 어시스턴트 연동에 적합합니다. local 메서드로 등록합니다:

use App\Mcp\Servers\WeatherServer; use Laravel\Mcp\Facades\Mcp; Mcp::local('weather', WeatherServer::class);

등록 후, 일반적으로 mcp:start 명령어를 직접 실행할 필요는 없습니다. MCP 클라이언트(AI 에이전트)가 필요할 때 서버를 자동으로 시작하고 종료하도록 설정하면 됩니다:

php artisan mcp:start weather

도구 (Tools)

도구는 AI 클라이언트가 호출할 수 있는 기능을 서버에 노출합니다. 언어 모델이 특정 동작을 수행하거나, 코드를 실행하거나, 외부 시스템과 상호작용할 수 있도록 합니다.

도구 생성

make:mcp-tool Artisan 명령어로 도구를 생성합니다:

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 { /** * 이 MCP 서버에 등록된 도구 목록. * * @var array<int, class-string<\Laravel\Mcp\Server\Tool>> */ protected array $tools = [ CurrentWeatherTool::class, ]; }

도구 이름, 제목, 설명

기본적으로 도구의 이름과 제목은 클래스 이름에서 자동으로 생성됩니다. 예를 들어 CurrentWeatherTool은 이름이 current-weather, 제목이 Current Weather Tool이 됩니다. $name$title 속성을 정의하여 이 값을 직접 지정할 수 있습니다:

class CurrentWeatherTool extends Tool { /** * 도구 이름. */ protected string $name = 'get-optimistic-weather'; /** * 도구 제목. */ protected string $title = 'Get Optimistic Weather Forecast'; // ... }

도구 설명은 자동으로 생성되지 않습니다. $description 속성을 정의하여 항상 의미 있는 설명을 제공하세요:

class CurrentWeatherTool extends Tool { /** * 도구 설명. */ protected string $description = '지정한 위치의 현재 날씨 예보를 가져옵니다.'; // }

NOTE

설명은 도구 메타데이터의 핵심 요소입니다. AI 모델이 이 도구를 언제, 어떻게 사용해야 하는지 이해하는 데 직접적으로 활용됩니다.

도구 입력 스키마

도구는 입력 스키마를 정의하여 AI 클라이언트로부터 어떤 인수를 받을지 명시할 수 있습니다. Illuminate\JsonSchema\JsonSchema 빌더를 사용하여 입력 요구사항을 정의합니다:

<?php namespace App\Mcp\Tools; use Illuminate\JsonSchema\JsonSchema; use Laravel\Mcp\Server\Tool; class CurrentWeatherTool extends Tool { /** * 도구 입력 스키마 반환. * * @return array<string, JsonSchema> */ public function schema(JsonSchema $schema): array { return [ 'location' => $schema->string() ->description('날씨를 조회할 위치.') ->required(), 'units' => $schema->enum(['celsius', 'fahrenheit']) ->description('온도 단위.') ->default('celsius'), ]; } }

도구 인수 유효성 검사

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 { /** * 도구 요청 처리. */ public function handle(Request $request): Response { $validated = $request->validate([ 'location' => 'required|string|max:100', 'units' => 'in:celsius,fahrenheit', ]); // 검사된 인수로 날씨 데이터 조회... } }

유효성 검사 실패 시 AI 클라이언트는 제공된 오류 메시지를 바탕으로 동작합니다. 따라서 명확하고 조치 가능한 오류 메시지를 작성하는 것이 매우 중요합니다:

$validated = $request->validate([ 'location' => ['required', 'string', 'max:100'], 'units' => 'in:celsius,fahrenheit', ], [ 'location.required' => '날씨를 조회할 위치를 지정해야 합니다. 예: "서울" 또는 "부산".', 'units.in' => '온도 단위는 "celsius" 또는 "fahrenheit" 중 하나여야 합니다.', ]);

도구 의존성 주입

모든 도구는 Laravel 서비스 컨테이너를 통해 resolve됩니다. 생성자에 타입 힌트를 선언하면 의존성이 자동으로 주입됩니다:

<?php namespace App\Mcp\Tools; use App\Repositories\WeatherRepository; use Laravel\Mcp\Server\Tool; class CurrentWeatherTool extends Tool { /** * 새 도구 인스턴스 생성. */ 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 { /** * 도구 요청 처리. */ public function handle(Request $request, WeatherRepository $weather): Response { $location = $request->get('location'); $forecast = $weather->getForecastFor($location); // ... } }

도구 어노테이션

PHP 어트리뷰트(attribute)를 사용하여 도구에 어노테이션을 추가하면 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 { // }

사용 가능한 어노테이션은 다음과 같습니다:

어노테이션타입설명
#[IsReadOnly]boolean도구가 환경을 변경하지 않음을 나타냅니다.
#[IsDestructive]boolean도구가 파괴적인 업데이트를 수행할 수 있음을 나타냅니다 (읽기 전용이 아닐 때만 유효).
#[IsIdempotent]boolean동일한 인수로 반복 호출해도 추가 효과가 없음을 나타냅니다 (읽기 전용이 아닐 때만 유효).
#[IsOpenWorld]boolean도구가 외부 엔티티와 상호작용할 수 있음을 나타냅니다.

조건부 도구 등록

도구 클래스에 shouldRegister 메서드를 구현하면 런타임에 도구를 조건부로 등록할 수 있습니다. 애플리케이션 상태, 설정, 또는 요청 정보에 따라 도구 노출 여부를 동적으로 결정할 수 있습니다:

<?php namespace App\Mcp\Tools; use Laravel\Mcp\Request; use Laravel\Mcp\Server\Tool; class CurrentWeatherTool extends Tool { /** * 도구를 등록할지 여부 결정. */ 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; /** * 도구 요청 처리. */ public function handle(Request $request): Response { // ... return Response::text('날씨 요약: 맑음, 22°C'); }

도구 실행 중 오류가 발생했음을 나타내려면 error 메서드를 사용합니다:

return Response::error('날씨 데이터를 가져올 수 없습니다. 잠시 후 다시 시도하세요.');

복수 콘텐츠 응답

도구는 Response 인스턴스 배열을 반환하여 여러 콘텐츠를 함께 전달할 수 있습니다:

use Laravel\Mcp\Request; use Laravel\Mcp\Response; /** * 도구 요청 처리. * * @return array<int, \Laravel\Mcp\Response> */ public function handle(Request $request): array { // ... return [ Response::text('날씨 요약: 맑음, 22°C'), Response::text("**상세 예보**\n- 오전: 18°C\n- 오후: 25°C\n- 저녁: 21°C"), ]; }

스트리밍 응답

장시간 실행되는 작업이나 실시간 데이터 스트리밍의 경우, handle 메서드에서 제너레이터(generator)를 반환하면 최종 응답 전에 중간 업데이트를 클라이언트에 전송할 수 있습니다:

<?php namespace App\Mcp\Tools; use Generator; use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; class CurrentWeatherTool extends Tool { /** * 도구 요청 처리. * * @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된 각 메시지가 이벤트로 클라이언트에 전송됩니다.

프롬프트 (Prompts)

프롬프트는 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 { /** * 이 MCP 서버에 등록된 프롬프트 목록. * * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>> */ protected array $prompts = [ DescribeWeatherPrompt::class, ]; }

프롬프트 이름, 제목, 설명

기본적으로 프롬프트의 이름과 제목은 클래스 이름에서 자동으로 생성됩니다. 예를 들어 DescribeWeatherPrompt는 이름이 describe-weather, 제목이 Describe Weather Prompt가 됩니다. $name$title 속성으로 직접 지정할 수 있습니다:

class DescribeWeatherPrompt extends Prompt { /** * 프롬프트 이름. */ protected string $name = 'weather-assistant'; /** * 프롬프트 제목. */ protected string $title = 'Weather Assistant Prompt'; // ... }

프롬프트 설명은 자동으로 생성되지 않습니다. $description 속성을 정의하여 항상 의미 있는 설명을 제공하세요:

class DescribeWeatherPrompt extends Prompt { /** * 프롬프트 설명. */ protected string $description = '지정한 위치의 날씨를 자연어로 설명하는 프롬프트를 생성합니다.'; // }

NOTE

설명은 프롬프트 메타데이터의 핵심 요소입니다. AI 모델이 이 프롬프트를 언제, 어떻게 활용하면 좋은지 이해하는 데 직접 활용됩니다.

프롬프트 인수

프롬프트는 인수를 정의하여 AI 클라이언트가 템플릿을 특정 값으로 커스터마이즈할 수 있게 합니다. arguments 메서드로 허용할 인수를 정의합니다:

<?php namespace App\Mcp\Prompts; use Laravel\Mcp\Server\Prompt; use Laravel\Mcp\Server\Prompts\Argument; class DescribeWeatherPrompt extends Prompt { /** * 프롬프트 인수 반환. * * @return array<int, \Laravel\Mcp\Server\Prompts\Argument> */ public function arguments(): array { return [ new Argument( name: 'tone', description: '날씨 설명에 사용할 어조 (예: 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 { /** * 프롬프트 요청 처리. */ public function handle(Request $request): Response { $validated = $request->validate([ 'tone' => 'required|string|max:50', ]); $tone = $validated['tone']; // 지정된 어조로 프롬프트 응답 생성... } }

유효성 검사 실패 시 AI 클라이언트는 제공된 오류 메시지를 바탕으로 동작합니다. 명확하고 조치 가능한 오류 메시지를 작성하는 것이 중요합니다:

$validated = $request->validate([ 'tone' => ['required', 'string', 'max:50'], ], [ 'tone.*' => '날씨 설명에 사용할 어조를 지정해야 합니다. 예: "formal", "casual", "humorous".', ]);

프롬프트 의존성 주입

모든 프롬프트는 Laravel 서비스 컨테이너를 통해 resolve됩니다. 생성자에 타입 힌트를 선언하면 의존성이 자동으로 주입됩니다:

<?php namespace App\Mcp\Prompts; use App\Repositories\WeatherRepository; use Laravel\Mcp\Server\Prompt; class DescribeWeatherPrompt extends Prompt { /** * 새 프롬프트 인스턴스 생성. */ 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 { /** * 프롬프트 요청 처리. */ public function handle(Request $request, WeatherRepository $weather): Response { $isAvailable = $weather->isServiceAvailable(); // ... } }

조건부 프롬프트 등록

프롬프트 클래스에 shouldRegister 메서드를 구현하면 런타임에 프롬프트를

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

번역일: 2026년 6월 20일