Laravel MCP
번역일: 2026년 6월 25일
Laravel MCP
소개
Laravel MCP는 AI 클라이언트가 Model Context Protocol을 통해 Laravel 애플리케이션과 상호작용할 수 있도록 해주는 패키지입니다. 서버, Tool, Resource, Prompt를 유창하고 표현력 있는 인터페이스로 정의하여 AI 기반의 애플리케이션 연동을 손쉽게 구현할 수 있습니다.
설치
Composer로 Laravel MCP를 설치합니다:
composer require laravel/mcp라우트 퍼블리시
설치 후 vendor:publish Artisan 명령어로 routes/ai.php 파일을 게시합니다. MCP 서버는 이 파일에서 등록합니다:
php artisan vendor:publish --tag=ai-routes이 명령어를 실행하면 애플리케이션의 routes 디렉터리에 routes/ai.php 파일이 생성됩니다.
서버 생성
make:mcp-server Artisan 명령어로 MCP 서버를 생성합니다. 서버는 Tool, Resource, Prompt 등의 MCP 기능을 AI 클라이언트에 노출하는 중심 허브 역할을 합니다:
php artisan make:mcp-server WeatherServer이 명령어는 app/Mcp/Servers 디렉터리에 서버 클래스를 생성합니다. 생성된 클래스는 Laravel\Mcp\Server를 상속하며, PHP 어트리뷰트(attribute)와 속성(property)을 통해 서버를 구성하고 Tool, Resource, Prompt를 등록합니다:
<?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('이 서버는 날씨 정보와 예보를 제공합니다.')]
class WeatherServer extends Server
{
/**
* 이 MCP 서버에 등록된 Tool 목록
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
// GetCurrentWeatherTool::class,
];
/**
* 이 MCP 서버에 등록된 Resource 목록
*
* @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
*/
protected array $resources = [
// WeatherGuidelinesResource::class,
];
/**
* 이 MCP 서버에 등록된 Prompt 목록
*
* @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를 활용하세요.
Tools
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('지정된 위치의 현재 날씨 예보를 가져옵니다.')]
class CurrentWeatherTool extends Tool
{
/**
* Tool 요청을 처리합니다.
*/
public function handle(Request $request): Response
{
$location = $request->get('location');
// 날씨 정보 조회...
return Response::text('현재 날씨는...');
}
/**
* Tool의 입력 스키마를 반환합니다.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
public function schema(JsonSchema $schema): array
{
return [
'location' => $schema->string()
->description('날씨를 조회할 위치')
->required(),
];
}
}Tool 생성
make:mcp-tool Artisan 명령어로 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
{
/**
* 이 MCP 서버에 등록된 Tool 목록
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
CurrentWeatherTool::class,
];
}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('지정된 위치의 현재 날씨 예보를 가져옵니다.')]
class CurrentWeatherTool extends Tool
{
//
}NOTE
설명은 AI 모델이 Tool을 언제, 어떻게 사용할지 판단하는 데 핵심적인 메타데이터입니다. 명확하고 구체적으로 작성하세요.
Tool 입력 스키마
Tool은 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
{
/**
* Tool의 입력 스키마를 반환합니다.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
public function schema(JsonSchema $schema): array
{
return [
'location' => $schema->string()
->description('날씨를 조회할 위치')
->required(),
'units' => $schema->string()
->enum(['celsius', 'fahrenheit'])
->description('온도 단위')
->default('celsius'),
];
}
}Tool 출력 스키마
Tool은 출력 스키마를 정의하여 응답의 구조를 명시할 수 있습니다. 파싱 가능한 결과가 필요한 AI 클라이언트와의 통합에 유용합니다. outputSchema 메서드를 사용합니다:
<?php
namespace App\Mcp\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Tool의 출력 스키마를 반환합니다.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
public function outputSchema(JsonSchema $schema): array
{
return [
'temperature' => $schema->number()
->description('섭씨 온도')
->required(),
'conditions' => $schema->string()
->description('날씨 상태')
->required(),
'humidity' => $schema->integer()
->description('습도 (%)')
->required(),
];
}
}Tool 인자 유효성 검사
JSON Schema 정의는 기본적인 구조를 제공하지만, 더 복잡한 유효성 검사 규칙이 필요할 수 있습니다.
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
{
/**
* 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"여야 합니다.',
]);Tool 의존성 주입
모든 Tool은 Laravel 서비스 컨테이너를 통해 리졸브됩니다. 생성자에 타입 힌트를 선언하면 의존성이 자동으로 주입됩니다:
<?php
namespace App\Mcp\Tools;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* 새 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
{
/**
* Tool 요청을 처리합니다.
*/
public function handle(Request $request, WeatherRepository $weather): Response
{
$location = $request->get('location');
$forecast = $weather->getForecastFor($location);
// ...
}
}Tool 어노테이션
어노테이션을 사용해 AI 클라이언트에 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] | boolean | Tool이 환경을 변경하지 않음을 나타냅니다. |
#[IsDestructive] | boolean | Tool이 파괴적인 변경을 수행할 수 있음을 나타냅니다(읽기 전용이 아닐 때만 유효). |
#[IsIdempotent] | boolean | 동일한 인자로 반복 호출해도 추가 효과가 없음을 나타냅니다(읽기 전용이 아닐 때). |
#[IsOpenWorld] | boolean | Tool이 외부 엔티티와 상호작용할 수 있음을 나타냅니다. |
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 등록
shouldRegister 메서드를 구현하면 런타임에 Tool 등록 여부를 동적으로 결정할 수 있습니다. 애플리케이션 상태, 설정, 또는 요청 파라미터에 따라 Tool 사용 가능 여부를 제어할 때 유용합니다:
<?php
namespace App\Mcp\Tools;
use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Tool 등록 여부를 결정합니다.
*/
public function shouldRegister(Request $request): bool
{
return $request?->user()?->subscribed() ?? false;
}
}shouldRegister가 false를 반환하면 해당 Tool은 사용 가능한 목록에 노출되지 않으며 AI 클라이언트가 호출할 수 없습니다.
Tool 응답
Tool은 Laravel\Mcp\Response 인스턴스를 반환해야 합니다. Response 클래스는 다양한 유형의 응답을 생성하는 편의 메서드를 제공합니다.
단순 텍스트 응답은 text 메서드를 사용합니다:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Tool 요청을 처리합니다.
*/
public function handle(Request $request): Response
{
// ...
return Response::text('날씨 요약: 맑음, 22°C');
}Tool 실행 중 오류가 발생했음을 나타내려면 error 메서드를 사용합니다:
return Response::error('날씨 데이터를 가져올 수 없습니다. 다시 시도해 주세요.');이미지나 오디오 콘텐츠를 반환하려면 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');Laravel 파일시스템 디스크에서 직접 이미지나 오디오를 로드하려면 fromStorage 메서드를 사용하세요. 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');다중 콘텐츠 응답
Tool은 Response 인스턴스 배열을 반환해 여러 개의 콘텐츠를 한 번에 전달할 수 있습니다:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Tool 요청을 처리합니다.
*
* @return array<int, \Laravel\Mcp\Response>
*/
public function handle(Request $request): array
{
// ...
return [
Response::text('날씨 요약: 맑음, 22°C'),
Response::text('**상세 예보**\n- 오전: 18°C\n- 오후: 26°C\n- 저녁: 21°C'),
];
}구조화된 응답
structured 메서드를 사용하면 구조화된 콘텐츠를 반환할 수 있습니다. AI 클라이언트가 파싱 가능한 데이터를 받으면서 하위 호환성을 위한 JSON 인코딩 텍스트 표현도 함께 제공됩니다:
return Response::structured([
'temperature' => 22.5,
'conditions' => '구름 조금',
'humidity' => 65,
]);구조화된 콘텐츠와 함께 커스텀 텍스트를 제공해야 한다면 withStructuredContent 메서드를 사용합니다:
return Response::make(
Response::text('현재 날씨: 22.5°C, 맑음')
)->withStructuredContent([
'temperature' => 22.5,
'conditions' => '맑음',
]);스트리밍 응답
장시간 실행되는 작업이나 실시간 데이터 스트리밍의 경우, 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
{
/**
* 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
Prompt는 AI 클라이언트가 언어 모델과 상호작용할 때 사용할 수 있는 재사용 가능한 프롬프트 템플릿을 서버에 공유합니다. 일반적인 쿼리와 상호작용을 표준화된 방식으로 구조화할 수 있습니다.
Prompt 생성
make:mcp-prompt Artisan 명령어로 Prompt를 생성합니다:
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 서버에 등록된 Prompt 목록
*
* @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
*/
protected array $prompts = [
DescribeWeatherPrompt::class,
];
}Prompt 이름, 제목, 설명
기본적으로 Prompt의 이름과 제목은 클래스명에서 자동으로 생성됩니다. 예를 들어 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
{
// ...
}Prompt 설명은 자동으로 생성되지 않습니다. 반드시 Description 어트리뷰트로 의미 있는 설명을 제공하세요:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('주어진 위치의 날씨를 자연어로 설명하는 프롬프트를 생성합니다.')]
class DescribeWeatherPrompt extends Prompt
{
//
}NOTE
설명은 AI 모델이 Prompt를 언제, 어떻게 활용할지 이해하는 데 핵심적인 메타데이터입니다.
Prompt 인자
Prompt는 AI 클라이언트가 템플릿을 커스터마이즈할 수 있도록 인자를 정의할 수 있습니다.