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