This documentation is currently being translated. Some pages may appear in Korean.
Skip to main content
Version: 13.x

Laravel MCP (Laravel MCP)

소개 (Introduction)

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

설치 (Installation)

시작하려면 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 서버를 등록합니다.

서버 생성 (Creating Servers)

make:mcp-server Artisan 명령어를 사용하여 MCP 서버를 생성할 수 있습니다. 서버는 AI 클라이언트에 도구, 리소스, 프롬프트와 같은 MCP 기능을 노출하는 중앙 통신 지점 역할을 합니다.

php artisan make:mcp-server WeatherServer

이 명령어는 app/Mcp/Servers 디렉터리에 새 서버 클래스를 생성합니다. 생성된 서버 클래스는 Laravel MCP의 기본 Laravel\Mcp\Server 클래스를 확장하며, 서버를 설정하고 도구, 리소스, 프롬프트를 등록하기 위한 속성과 속성(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);

일반 라우트와 마찬가지로, 웹 서버를 보호하기 위해 Middleware를 적용할 수 있습니다.

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)

도구를 사용하면 서버가 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(),
];
}
}

도구 생성

도구를 생성하려면 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
{
/**
* The tools registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
CurrentWeatherTool::class,
];
}

도구 이름, 제목, 설명

기본적으로 도구의 이름과 제목은 클래스 이름에서 파생됩니다. 예를 들어 CurrentWeatherTool의 이름은 current-weather가 되고, 제목은 Current Weather Tool이 됩니다. NameTitle 속성(attribute)을 사용하여 이 값을 사용자 지정할 수 있습니다.

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

도구 설명은 자동으로 생성되지 않습니다. 항상 Description 속성(attribute)을 사용하여 의미 있는 설명을 제공해야 합니다.

use Laravel\Mcp\Server\Attributes\Description;

#[Description('Fetches the current weather forecast for a specified location.')]
class CurrentWeatherTool extends Tool
{
//
}

설명은 도구 메타데이터의 중요한 부분입니다. AI 모델이 도구를 언제, 어떻게 효과적으로 사용할지 이해하는 데 도움이 되기 때문입니다.

도구 입력 스키마

도구는 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
{
/**
* 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'),
];
}
}

도구 출력 스키마

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

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

도구 인수 유효성 검증

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
{
/**
* 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.',
]);

도구 의존성 주입

Laravel 서비스 컨테이너는 모든 도구를 해결하는 데 사용됩니다. 따라서 도구의 생성자에서 필요한 의존성을 타입 힌트할 수 있습니다. 선언된 의존성은 자동으로 해결되어 도구 인스턴스에 주입됩니다.

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

// ...
}

생성자 주입뿐만 아니라 도구의 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);

// ...
}
}

도구 애노테이션

AI 클라이언트에 추가 메타데이터를 제공하기 위해 도구에 애노테이션을 추가할 수 있습니다. 이러한 애노테이션은 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도구가 외부 엔티티와 상호작용할 수 있음을 나타냅니다.

애노테이션 값은 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
{
//
}

조건부 도구 등록

도구 클래스에서 shouldRegister 메서드를 구현하여 런타임에 조건부로 도구를 등록할 수 있습니다. 이 메서드를 사용하면 애플리케이션 상태, 설정 또는 요청 매개변수에 따라 도구를 사용할 수 있어야 하는지 결정할 수 있습니다.

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

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

도구 응답

도구는 반드시 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');
}

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

return Response::error('Unable to fetch weather data. Please try again.');

이미지 또는 오디오 콘텐츠를 반환하려면 imageaudio 메서드를 사용합니다.

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 인스턴스 배열을 반환하여 여러 콘텐츠를 반환할 수 있습니다.

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 메서드를 사용하여 구조화된 콘텐츠를 반환할 수 있습니다. 이를 통해 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',
]);

스트리밍 응답

오래 실행되는 작업이나 실시간 데이터 스트리밍의 경우, 도구는 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
{
/**
* 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된 각 메시지를 이벤트로 클라이언트에 전송합니다.

프롬프트 (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
{
/**
* 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가 됩니다. NameTitle 속성을 사용하여 이러한 값을 사용자 지정할 수 있습니다.

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

설명은 프롬프트 메타데이터에서 매우 중요한 부분입니다. 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 어시스턴트가 보낸 것으로 처리되어야 함을 나타낼 수 있습니다. 일반 메시지는 사용자 입력으로 처리됩니다.

리소스 (Resources)

리소스를 사용하면 서버가 AI 클라이언트가 읽고 언어 모델과 상호작용할 때 컨텍스트로 활용할 수 있는 데이터와 콘텐츠를 노출할 수 있습니다. 리소스는 문서, 설정, 또는 AI 응답에 도움이 되는 모든 데이터처럼 정적 또는 동적 정보를 공유하는 방법을 제공합니다.

리소스 생성 (Creating Resources)

리소스를 생성하려면 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가 됩니다. NameTitle 속성을 사용하여 이 값을 직접 지정할 수 있습니다.

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

설명은 리소스 메타데이터에서 매우 중요한 부분입니다. 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입니다.

UriMimeType 속성을 사용하여 이 값을 직접 지정할 수 있습니다.

<?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 클라이언트가 리소스 콘텐츠를 적절하게 처리하고 해석하는 방법을 결정하는 데 도움이 됩니다.

리소스 요청

도구와 프롬프트와 달리 리소스는 입력 스키마나 인수를 정의할 수 없습니다. 하지만 리소스의 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 클라이언트를 위한 추가 메타데이터를 제공할 수 있습니다. 애노테이션은 속성을 통해 리소스에 추가합니다.

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

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

앱 (Apps)

Laravel MCP는 MCP Apps를 지원합니다. MCP Apps는 Model Context Protocol의 확장 기능으로, 지원되는 호스트에서 도구가 샌드박스 처리된 iframe 안에 인터랙티브 HTML 애플리케이션을 렌더링할 수 있게 해줍니다. 이를 통해 단순 텍스트 응답을 넘어 대시보드, 폼, 시각화, 그 밖의 풍부한 경험을 만들 수 있습니다.

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

  • 애플리케이션용 자체 포함 HTML을 반환하는 앱 리소스
  • #[RendersApp] 속성을 사용해 앱 리소스에 연결된 도구. 도구가 호출되면 호스트가 연결된 리소스를 가져와 렌더링합니다.

앱 리소스 생성

make:mcp-app-resource Artisan 명령어를 사용하여 앱 리소스를 만들 수 있습니다.

php artisan make:mcp-app-resource WeatherDashboardApp

이 명령어는 app/Mcp/Resources에 PHP 클래스 하나와 resources/views/mcp에 Blade 뷰 하나, 총 두 파일을 생성합니다. 뷰 이름은 클래스 이름에서 자동으로 추론됩니다. 예를 들어 WeatherDashboardAppmcp.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 명세를 참고하십시오.

도구에서 앱 렌더링

앱 리소스를 표시하려면 #[RendersApp] 속성을 사용하여 도구에 연결합니다. 도구가 호출되면 Laravel MCP는 도구 메타데이터에 리소스 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는 자동으로 io.modelcontextprotocol/ui 기능을 알립니다. 따라서 추가 서버 설정은 필요하지 않습니다.

앱 도구 가시성

#[RendersApp] 도구는 visibility 인수를 통해 누가 호출할 수 있는지 제한할 수 있습니다. 이는 UI가 데이터를 로드하거나 새로 고치기 위해 호출하지만, 모델에는 보이지 않아야 하는 비공개 앱 전용 도구를 노출할 때 유용합니다.

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 enum에는 ModelApp 두 가지 케이스가 있으며 기본값은 둘 다입니다. UI가 직접 호출하는 백엔드 작업에는 [Visibility::App]을 사용하고, UI에서 도구를 사용할 수 없게 만들려면 [Visibility::Model]을 사용합니다.

앱 설정

앱 리소스의 #[AppMeta] 속성은 iframe의 Content Security Policy, 브라우저 권한, 그리고 뷰의 <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 enum에는 Library::Tailwind, Library::Alpine 같은 일반적인 프론트엔드 라이브러리를 위한 사전 설정 CDN 스크립트가 포함되어 있으며, 해당 CDN 출처는 CSP에 자동으로 병합됩니다. Permission enum은 Camera, Microphone, Geolocation, ClipboardWrite 같은 브라우저 권한을 다룹니다.

계산되거나 동적인 설정이 필요하다면 Laravel\Mcp\Server\Ui 네임스페이스의 fluent AppMeta, Csp, Permissions 빌더를 사용하여 리소스의 appMeta 메서드를 재정의하십시오.

Boost로 앱 빌드

Laravel MCP에는 MCP Apps를 빌드하기 위한 전용 Boost 스킬 참조가 포함되어 있습니다. Laravel Boost가 설치되어 있다면, AI 코딩 에이전트가 mcp-development 스킬을 호출하여 앱 리소스, Blade 뷰, 연결된 도구를 스캐폴딩하도록 요청할 수 있습니다.

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

메타데이터 (Metadata)

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

// ...
}

인증 (Authentication)

라우트와 마찬가지로, Middleware를 사용하여 웹 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 discovery 및 클라이언트 등록 라우트를 등록합니다. 그런 다음 routes/ai.php 파일의 Mcp::web 라우트에 Passport의 auth:api Middleware를 적용합니다.

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 모델, 새 인증 guard, Passport 키가 준비되어 있어야 합니다.

다음으로 Laravel MCP가 제공하는 Passport 인가 뷰를 퍼블리시해야 합니다.

php artisan vendor:publish --tag=mcp-views

그런 다음 Passport::authorizationView 메서드를 사용하여 Passport가 이 뷰를 사용하도록 지시합니다. 일반적으로 이 메서드는 애플리케이션의 AppServiceProviderboot 메서드에서 호출해야 합니다.

use Laravel\Passport\Passport;

/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::authorizationView(function ($parameters) {
return view('mcp.authorize', $parameters);
});
}

이 뷰는 인증 과정에서 최종 사용자에게 표시되어 AI 에이전트의 인증 시도를 거부하거나 승인할 수 있게 합니다.

인가 화면 예시

이 시나리오에서는 OAuth를 기본 인증 가능 모델로 이어 주는 변환 계층으로만 사용합니다. scopes 같은 OAuth의 많은 측면은 무시합니다.

기존 Passport 설치 사용

애플리케이션이 이미 Laravel Passport를 사용하고 있다면, Laravel MCP는 기존 Passport 설치 안에서 원활하게 동작해야 합니다. 다만 OAuth는 주로 기본 인증 가능 모델로 이어 주는 변환 계층으로 사용되므로 현재 custom scopes는 지원되지 않습니다.

Laravel MCP는 앞에서 설명한 Mcp::oauthRoutes 메서드를 통해 단일 mcp:use scope를 추가하고, 알리고, 사용합니다.

Passport와 Sanctum

OAuth2.1은 Model Context Protocol 명세에 문서화된 인증 메커니즘이며, MCP 클라이언트 사이에서 가장 널리 지원됩니다. 이러한 이유로 가능하다면 Passport 사용을 권장합니다.

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

Sanctum

MCP 서버를 Sanctum으로 보호하려면, routes/ai.php 파일에서 서버에 Sanctum의 인증 Middleware를 추가하기만 하면 됩니다. 그런 다음 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 토큰을 발급한다면, 원하는 Middleware를 Mcp::web 라우트에 할당하여 MCP 서버를 인증할 수 있습니다. 사용자 정의 Middleware는 들어오는 MCP 요청을 인증하기 위해 Authorization 헤더를 직접 검사할 수 있습니다.

인가 (Authorization)

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

// ...
}

서버 테스트 (Testing Servers)

내장 MCP Inspector를 사용하거나 단위 테스트를 작성하여 MCP 서버를 테스트할 수 있습니다.

MCP Inspector

MCP Inspector는 MCP 서버를 테스트하고 디버깅하기 위한 대화형 도구입니다. 이 도구를 사용해 서버에 연결하고, 인증을 확인하며, 도구, 리소스, 프롬프트를 실행해 볼 수 있습니다.

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

# Web server...
php artisan mcp:inspector mcp/weather

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

이 명령어는 MCP Inspector를 실행하고, 모든 설정이 올바르게 구성되었는지 확인하기 위해 MCP 클라이언트에 복사해 넣을 수 있는 클라이언트 설정을 제공합니다. 웹 서버가 인증 Middleware로 보호되고 있다면, 연결할 때 Authorization bearer token과 같은 필수 헤더를 포함해야 합니다.

단위 테스트

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

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

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

응답을 받은 후에는 다양한 assertion 메서드를 사용해 응답의 내용과 상태를 확인할 수 있습니다.

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

assertSentNotificationassertNotificationCount 메서드를 사용해 알림이 전송되었는지 확인할 수 있습니다.

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