사용자 정의 도구
C++ 플러그인에서 Unreal MCP에 자체 AI 도구, RQL 모듈, 프롬프트, 리소스를 추가하고 실행 중인 게임에서 게임플레이 도구를 노출하세요.
Unreal MCP를 사용하면 자체 게임 프로젝트의 플러그인을 포함한 모든 Unreal 플러그인이 AI 에이전트를 위한 기능을 추가할 수 있습니다. Unreal MCP를 포크할 필요 없이 작은 공개 인터페이스를 구현하고 Unreal **모듈형 기능(modular feature)**으로 등록하면, 모듈이 로드될 때 플러그인이 이를 자동으로 인식합니다.
이 페이지는 C++ 개발자를 위한 것입니다. 엔진 지원 범위만 넓히고 싶다면 먼저 미리 만들어진 확장을 확인하세요.
적절한 기여 유형 선택
| 에이전트가 수행하길 원하는 작업 | 기여 유형 | 인터페이스 |
|---|---|---|
| 엔진 기능 또는 자체 타입의 에디터 데이터 읽기 및 변경 | RQL 모듈 (에디터 작업에 권장) | IUnrealMcpRqlModule |
| 데이터 접근이 아닌 액션 실행, 또는 실행 중인 게임 내에서 동작 | 도구(Tools) | IUnrealMcpToolProvider |
| 재사용 가능한 프롬프트 템플릿 가져오기 | 프롬프트(Prompts) | IUnrealMcpPromptProvider |
| URI로 고정 콘텐츠 읽기 | 리소스(Resources) | IUnrealMcpResourceProvider |
모든 자사 확장은 RQL 모듈이므로, 에이전트는 에디터의 나머지 부분과 동일한 rql-query 및 rql-mutate 도구를 통해 이들에 접근합니다.
옵션 1: RQL 모듈
에디터 모듈의 Build.cs에서 UnrealMcpRql 모듈에 의존성을 추가하고, .uplugin의 Plugins 배열에 UnrealMCP 플러그인을 선언하세요. 그런 다음 모듈을 구현합니다:
#include "IUnrealMcpRqlModule.h"
class FMyRqlModule : public IUnrealMcpRqlModule
{
public:
virtual FString GetRqlModuleName() const override { return TEXT("Niagara"); }
virtual FString GetExtensionId() const override { return TEXT("com.example.niagara-ai"); }
virtual FString GetExtensionVersion() const override { return UnrealMcpRql::PluginVersion(TEXT("MyNiagaraAI")); }
virtual void ConfigureRql(FUnrealMcpRqlModuleContext& Context) override
{
Context.AddAssetSource(TEXT("niagaraSystems"), TEXT("Niagara system assets"), UNiagaraSystem::StaticClass());
Context.AddApiSource(TEXT("niagara"), TEXT("Niagara operations"), UMyNiagaraOperations::StaticClass(),
TEXT("NiagaraOperations"));
Context.AddGuide(TEXT("Niagara"), TEXT("... markdown with example queries ..."));
}
};
IMPLEMENT_MODULE(TUnrealMcpRqlEditorModule<FMyRqlModule>, MyNiagaraAI)컨텍스트가 추가할 수 있는 항목:
- 소스(Source) (쿼리가 시작되는 지점):
AddObjectSource,AddObjectListSource,AddAssetSource,AddClassDefaultsSource. - 오퍼레이션(Operation) (
call로 호출됨):AddApiClass또는AddApiSource는 클래스의 모든UFUNCTION을 호출 가능하게 만듭니다. 이들을UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))로 선언하세요. 각 함수 위의 문서 주석이 에이전트가 읽는 설명이 됩니다. - 멤버(Member): 리플렉션이 숨기는 프로퍼티에는
ExposeProperties, 읽기 전용 계산 멤버에는AddJsonMember,AddObjectMember,AddObjectListMember를 사용하세요. - 가이드(Guide):
AddGuide는unreal-rql스킬에 섹션을 추가합니다.
뮤테이션은 하나의 에디터 트랜잭션에서 실행되므로 객체는 RF_Transactional로 생성하고 변경하는 대상에는 Modify()를 호출하세요. 에디터의 Extensions 섹션에 모듈이 표시되며 비활성화할 수 있습니다. 빌드 가능한 참고 예제는 Unreal-MCP 저장소의 samples/UnrealAITemplate 플러그인입니다.
옵션 2: 사용자 정의 도구
1단계: 의존성 추가
모듈의 Build.cs에서:
PublicDependencyModuleNames.AddRange(new string[] { "Core" });
PrivateDependencyModuleNames.AddRange(new string[]
{
"CoreUObject", "Engine", "Projects",
"Json", // the registry header includes Dom/JsonObject.h
"UnrealMcpEditor", // the extension contract and tool registry
});.uplugin에서 로드 순서가 해결되도록 플러그인 의존성을 추가하세요:
{
"Plugins": [ { "Name": "UnrealMCP", "Enabled": true } ]
}2단계: 프로바이더 구현
#include "IUnrealMcpToolProvider.h"
#include "UnrealMcpToolRegistry.h"
class FMyExtensionProvider : public IUnrealMcpToolProvider
{
public:
virtual FString GetExtensionId() const override { return TEXT("com.foo.my-extension"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("Foo", "Name", "My Extension"); }
virtual FString GetExtensionVersion() const override { return TEXT("1.0.0"); }
virtual void RegisterTools(FUnrealMcpToolRegistry& Registry) override
{
Registry.Tool(TEXT("hello-extension"))
.Title(TEXT("Hello Extension"))
.Description(TEXT("Returns a friendly greeting."))
.ParamString(TEXT("name"), TEXT("Who to greet. Defaults to 'world'."))
.ReadOnlyHint(true).DestructiveHint(false).IdempotentHint(true).OpenWorldHint(false)
.Handle([](const FUnrealMcpToolCall& Call) -> FUnrealMcpToolResult
{
const FString Name = Call.Has(TEXT("name")) ? Call.GetString(TEXT("name")) : TEXT("world");
return FUnrealMcpToolResult::Success(FString::Printf(TEXT("Hello, %s!"), *Name));
});
}
};빌더는 .Title, .Description, 파라미터 헬퍼인 .ParamString, .ParamInt, .ParamNumber, .ParamBool, .ParamVector(필수 항목에는 EUnrealMcpParamRequirement::Required 전달), 네 가지 MCP 힌트, 그리고 .Handle을 제공합니다. 핸들러는 게임 스레드에서 실행되므로 에디터 및 UObject API를 직접 호출할 수 있습니다.
중요한 규칙:
- 도구 id는 케밥 케이스입니다: 소문자와 숫자를 단일 하이픈으로 구분합니다.
- 모든 도구에 제목과 네 가지 힌트를 모두 지정하세요. 삭제하거나 덮어쓰는 모든 작업은 파괴적(destructive)으로 표시하세요.
.ExtensionId(...)를 직접 호출하지 마세요.GetExtensionId()가 자동으로 적용됩니다.
3단계: 모듈형 기능으로 등록
virtual void StartupModule() override
{
Provider = MakeUnique<FMyExtensionProvider>();
IModularFeatures::Get().RegisterModularFeature(
IUnrealMcpToolProvider::GetModularFeatureName(), Provider.Get());
}
virtual void ShutdownModule() override
{
if (Provider.IsValid())
{
IModularFeatures::Get().UnregisterModularFeature(
IUnrealMcpToolProvider::GetModularFeatureName(), Provider.Get());
Provider.Reset();
}
}프로바이더는 등록된 동안 유지하고 종료 시 반드시 등록 해제하세요.
등록 동작 방식
- 검색 및 핫 리로드. 프로바이더는 에디터 시작 시 검색됩니다. 이후 모듈을 로드하거나 언로드하면 레지스트리가 재구성되며 AI 클라이언트는 변경 사항을 자동으로 확인합니다.
- 결정적 순서. 프로바이더는 확장 id 오름차순으로 병합되며, 한 프로바이더 내의 도구는 선언 순서를 유지합니다.
- 격리. 유효하지 않은 도구(잘못된 id, 깨진 스키마, 핸들러 없음) 또는 중복된 도구 이름은 제외되고 해당 확장 항목에 기록됩니다. 다른 도구와 다른 확장은 영향을 받지 않습니다. 핸들러 내부의 크래시는 여전히 에디터 크래시이므로 입력을 검증하고
FUnrealMcpToolResult::Error(...)를 반환하세요. - 가시성 및 토글. 사용자의 도구는 MCP Tools 창에서 기본 제공 도구와 동일한 Visible 체크박스를 갖습니다. 비활성화된 확장은 아무것도 기여하지 않습니다.
프롬프트와 리소스
프롬프트와 리소스는 IUnrealMcpPromptProvider와 FUnrealMcpPromptRegistry, IUnrealMcpResourceProvider와 FUnrealMcpResourceRegistry를 사용하는 동일한 패턴을 따릅니다. 프롬프트는 동일한 Param* 헬퍼로 매개변수를 선언하고 역할 태그가 붙은 메시지를 반환합니다. 리소스는 URI로 식별되며 텍스트 또는 MIME 타입이 지정된 base64 blob을 반환합니다. 현재는 고정 URI만 지원되며 템플릿 URI는 지원되지 않습니다. 내장된 level-design-brief 프롬프트와 unreal://project/levels 리소스가 동작하는 참고 예시입니다.
에이전트로 도구 생성하기
플러그인을 소스에서 설치한 경우, unreal-skill-create 시스템 도구가 플러그인의 에디터 모듈에 새로운 C++ 도구 파일을 작성합니다. 이 도구는 에디터를 다시 빌드한 후(Live Coding 또는 프로젝트 재빌드)에만 존재합니다.
인게임(런타임) 도구
Unreal MCP는 실행 중인 게임(Play In Editor, Standalone 또는 패키징된 Development 빌드)에도 연결하여 AI 에이전트가 게임을 제어할 수 있게 합니다. 런타임 연결에서는 엔진 도구가 제공되지 않으며, 게임이 등록한 도구만 사용할 수 있습니다.
- 게임 모듈이
UnrealMcpRuntime(UnrealMcpEditor가 아님)에 의존하도록 설정하고 위와 동일한 방식으로 도구를 작성하세요. - **Project Settings, Plugins, Unreal MCP (Runtime)**을 활성화하세요. 이 스위치는 기본적으로 꺼져 있으며
DefaultGame.ini에 저장됩니다. - 명시적으로 연결하세요. 게임은 스스로 연결하지 않습니다.
C++에서:
#include "UnrealMcpRuntimeSubsystem.h"
if (UUnrealMcpRuntimeSubsystem* Mcp = UUnrealMcpRuntimeSubsystem::Get(this))
{
Mcp->RegisterToolProvider(MyToolProvider);
Mcp->Connect(TEXT("http://localhost:8080"), TEXT("my-token"));
}블루프린트에서는 Get Unreal MCP Runtime Subsystem을 사용한 뒤 Connect, Disconnect, Is Connected를 사용하세요. 콘솔에서:
UnrealMcp.Connect <host> [token]
UnrealMcp.DisconnectConnect()에 내장된 안전 규칙:
- Shipping 빌드는
bUnrealMcpAllowShipping플래그를 컴파일하지 않는 한 연결을 거부합니다. bAllowRemoteHost = true를 전달하지 않는 한 루프백 호스트만 허용됩니다.- 런타임 모드는 데스크톱 플랫폼(Windows, macOS, Linux)에서만 작동합니다.
저장소의 samples/UnrealAIRuntimeSample 플러그인은 실행 중인 게임에서 월드의 time dilation을 읽고 설정하는 game-time-dilation 도구를 추가합니다. 런타임 도구가 필요하지 않다면 Installation에 설명된 대로 패키징된 빌드에서 해당 플러그인을 제외하세요.