Laravel MCP

번역일: 2026년 6월 25일

Laravel MCP

소개

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

설치

Composer를 사용해 Laravel MCP 패키지를 설치합니다:

composer require laravel/mcp

라우트 파일 게시

설치 후, 아래 Artisan 명령어로 MCP 서버를 정의할 routes/ai.php 파일을 게시합니다:

php artisan vendor:publish --tag=ai-routes

이 명령어를 실행하면 애플리케이션의 routes 디렉토리에 routes/ai.php 파일이 생성됩니다. MCP 서버는 이 파일에서 등록합니다.

서버 생성

make:mcp-server Artisan 명령어로 MCP 서버를 생성할 수 있습니다. 서버는 툴, 리소스, 프롬프트 등 MCP 기능을 AI 클라이언트에 노출하는 중심 역할을 합니다:

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는 두 가지 등록 방법을 제공합니다: 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 명령어로 실행되며, 개발·테스트 환경이나 로컬 AI 어시스턴트 연동에 적합합니다. local 메서드로 등록합니다:

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

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

php artisan mcp:start weather

툴은 서버가 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 = '낙관적 날씨 예보 가져오기'; // ... }

툴 설명은 자동으로 생성되지 않습니다. $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 클라이언트는 제공된 오류 메시지를 기반으로 동작합니다. 따라서 AI가 올바르게 재시도할 수 있도록 명확하고 실행 가능한 오류 메시지를 작성하는 것이 중요합니다:

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

툴 의존성 주입

모든 툴은 Laravel 서비스 컨테이너를 통해 해석됩니다. 생성자에서 필요한 의존성을 타입힌트로 선언하면 자동으로 주입됩니다:

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

툴 어노테이션

툴에 어노테이션을 추가하여 AI 클라이언트에 추가 메타데이터를 제공할 수 있습니다. 어노테이션은 AI 모델이 툴의 동작과 특성을 이해하는 데 도움을 주며, PHP 어트리뷰트로 선언합니다:

<?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 메서드에서 제너레이터를 반환할 수 있습니다. 이를 통해 최종 응답 전에 중간 업데이트를 클라이언트에 전송할 수 있습니다:

<?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된 각 메시지가 이벤트로 클라이언트에 전송됩니다.

프롬프트

프롬프트는 서버가 재사용 가능한 프롬프트 템플릿을 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 = '날씨 어시스턴트 프롬프트'; // ... }

프롬프트 설명은 자동으로 생성되지 않습니다. $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 서비스 컨테이너를 통해 해석됩니다. 생성자에서 필요한 의존성을 타입힌트로 선언하면 자동으로 주입됩니다:

<?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 메서드에서도 의존성을 타입힌트로 선언할 수 있습니다. 서비스 컨테이너가 메서드 호출 시 자동으로 해석

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

번역일: 2026년 6월 25일