MCP 서버 개발 가이드라인
이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다. 더 자세한 로깅을 위해 mcp-remote 명령에 --debug를 추가합니다. Claude Desktop은 지원되지 않는 버전의 Node.js를 사용합니다.
이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다.
개발 환경 설정#
개발 환경을 설정하려면:
- GDK에서 HTTPS 활성화 및 구성.
node를 설치하고mcp-remote를 전역으로 설치합니다. GDK에는 Node.js가 포함되어 있지만 설치된 AI 어시스턴트는 GDK 버전을 사용할 수 없습니다.- AI 어시스턴트를 MCP 서버에 연결.
디버깅 및 문제 해결#
Cursor 디버그#
더 자세한 로깅을 위해 mcp-remote 명령에 --debug를 추가합니다. 출력을 열고 MCP:SERVERNAME을 선택하여 MCP 서버 로그를 확인합니다. 아래 예시의 경우 MCP:user-GitLab-GDK입니다.
{
"mcpServers": {
"GitLab-GDK": {
"command": "npx",
"args": [
"mcp-remote",
"https://gdk.test:3443/api/v4/mcp",
"--debug"
],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
}
Claude Desktop 디버그#
Node.js 버전 확인#
Claude Desktop은 지원되지 않는 버전의 Node.js를 사용합니다. 특정 버전을 사용하는 커스텀 래퍼 스크립트를 생성합니다:
#!/bin/bash
# Force use of your Node.js version
NODE_BIN="/PATH_TO_NODE_INSTALL/node/22.17.0/bin/node"
MCP_REMOTE_BIN="/PATH_TO_NODE_INSTALL/node/22.17.0/bin/mcp-remote"
# Run mcp-remote with your Node.js
exec "$NODE_BIN" "$MCP_REMOTE_BIN" "$@"
Claude Desktop 구성에서 래퍼 스크립트를 사용합니다.
{
"mcpServers": {
"GitLab-GDK": {
"command": "/PATH_TO_REMOTE_WRAPPER_SCRIPT/mcp-remote-wrapper",
"args": [
"https://gdk.test:3443/api/v4/mcp",
"--debug"
],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
}
mcp-remote 디버그#
AI 어시스턴트 외부에서 mcp-remote에서 GDK로의 인증을 테스트합니다:
NODE_TLS_REJECT_UNAUTHORIZED=0 npx mcp-remote https://gdk.test:3443/api/v4/mcp --debug
브랜치를 전환하면 로그에 UNABLE_TO_VERIFY_LEAF_SIGNATURE 오류를 포함한 인증 문제가 발생할 수 있습니다. 체인의 신뢰할 수 없는 인증서 오류는 TLS를 사용하는 GDK 인스턴스에 특정합니다. 이 오류는 npx를 통한 Node.js 및 mcp-remote 라이브러리의 https 클라이언트에서 발생합니다. https 클라이언트는 번들된 인증 기관 목록 외부에서 서명된 인증서를 신뢰하지 않습니다.
인증 문제가 발생하면 최후의 수단으로 ~/.mcp-auth 디렉토리를 삭제하면 mcp-remote에 대해 저장된 자격증명이 초기화됩니다. AI 어시스턴트가 MCP 서버에 다시 연결할 때 브라우저 창이 열려 인증을 묻습니다.
rm -rf ~/.mcp-auth
MCP 인스펙터로 디버그#
MCP 인스펙터는 MCP 서버를 테스트하고 디버깅하기 위한 대화형 개발자 도구입니다.
다음 명령은 서버에 연결하고 MCP 도구를 나열하고 실행하기 위한 직관적인 Web UI를 엽니다:
npx -y @modelcontextprotocol/inspector npx
개발 워크플로우#
유용한 링크#
새 도구 추가#
도구 제안 프로세스#
현재 개발 가이드라인은 초기 개발 단계에 있습니다. 커스텀 및 집계 도구에 대한 도구 개발 표준을 계속 확립하면서 구현 전에 제안된 도구를 평가하고 새 MCP 도구를 계획하는 팀을 안내하기 위해 임시 mcp-tool-review-board 위원회를 만들었습니다.
새 도구를 추가하려면 MCP 도구 제안 이슈를 생성하고 템플릿 지침을 따르세요.
도구 구현 위치는 GitLab 리소스와의 상호작용에 따라 달라집니다:
- GitLab 리소스와 상호작용하는 도구는 궁극적으로 MCP 서버에 있어야 하지만, 단기 또는 긴급한 필요의 경우 에이전트 플랫폼에 구현할 수 있습니다.
- GitLab 리소스와 상호작용하지 않는 도구는 에이전트 플랫폼에 구현해야 합니다.
MCP 서버 기능을 에이전트 플랫폼에 통합하는 작업을 진행 중입니다. 이 이슈를 통해 진행 상황을 추적할 수 있습니다.
모든 엔지니어가 도구 제안 프로세스를 따르고 사용 사례에 대한 명확한 설명을 제공하도록 강력히 권장합니다.
REST API 경로에서 도구 구현#
이 머지 리퀘스트는 API 경로에서 MCP 도구를 만드는 프로세스를 정의합니다.
API 경로 정의에 다음 route_setting을 추가합니다:
route_setting :mcp, tool_name: :get_merge_request, params: [:id, :merge_request_iid], resource_name: "merge request"
- 도구 목록에
get_merge_request도구를 추가하고 실행을 활성화합니다. - 도구 및 파라미터 설명은 OpenAPI 경로 정의에서 가져옵니다.
- 허용된 파라미터는
params인수로 필터링됩니다. 예를 들어,id와merge_request_iid만 공개되고 허용됩니다. - 도구가 호출될 때 경로 코드는 전달된 파라미터와 함께 직접 실행됩니다.
- 선택적
resource_name필드는 리소스별 404 오류 메시지를 제공합니다(예: 일반적인"404 Not Found"대신"404 Merge request Not Found")."issue"또는"merge request"와 같은 소문자 문자열을 사용합니다. 렌더링된 메시지에서는 첫 글자가 대문자로 표시됩니다.
이 머지 리퀘스트는 더 많은 예시를 제공합니다.
집계된 REST API 도구 구현#
집계된 API 도구는 여러 관련 API 도구를 단일 통합 인터페이스로 결합하여 도구 수를 줄이고 사용자 경험을 향상시킵니다. 검색 도구는 전역, 그룹, 프로젝트 검색을 하나의 도구로 통합하여 이 패턴을 보여줍니다.
집계 도구를 사용해야 하는 경우:
비슷한 목적을 제공하지만 다른 범위(전역, 그룹, 프로젝트)에서 작동하는 여러 API 엔드포인트가 있을 때 집계 도구를 사용하세요. 이는 세 개의 도구 대신 하나의 도구를 제시하여 LLM의 인지 부하를 줄입니다.
구현 단계:
Mcp::Tools::AggregatedService를 상속하는 집계 서비스 클래스를 생성합니다:
module Mcp
module Tools
class ExampleAggregatedService < AggregatedService
include Gitlab::Utils::StrongMemoize
extend ::Gitlab::Utils::Override
register_version '0.1.0', {
description: 'My example aggregated tool',
input_schema: {
type: 'object',
properties: {},
required: []
}
}
override :tool_name
def self.tool_name
'new_tool'
end
override :select_tool
def select_tool(args)
tool_name = if args[:group_id]
:example_tool_for_group
elsif args[:project_id]
:example_tool_for_project
end
tools.find { |tool| tool.name.to_sym == tool_name }
end
override :transform_arguments
def transform_arguments(args)
if args[:group_id]
args.merge(id: args[:group_id])
elsif args[:project_id]
args.merge(id: args[:project_id])
else
args
end
end
end
end
end
- 경로 정의에서 기본 API 도구를 집계기에 등록합니다:
route_setting :mcp, tool_name: :example_tool_for_group, params: [:id], aggregators: [::Mcp::Tools::ExampleAggregatedService]
route_setting :mcp, tool_name: :example_tool_for_project, params: [:id], aggregators: [::Mcp::Tools::ExampleAggregatedService]
Mcp::Tools::Manager는aggregators가 지정된 경로를 스캔하고 수집된 도구로 집계기 클래스를 인스턴스화하여 집계된 도구를 자동으로 발견합니다.
GraphQL 기반 도구 구현#
GitLab GraphQL API를 사용하는 MCP 도구의 경우 GraphQL 통합 가이드라인을 참조하세요.
커스텀 도구 구현#
API 노출과 별도로 유지해야 하는 별도 기능을 가진 도구의 경우 독립적인 클래스를 정의할 수 있습니다(이 예시 참조).
AI 에이전트 아키텍처에서의 도구 확산 위험#
너무 많은 도구 추가 시 핵심 문제#
- 성능 저하
- 컨텍스트 팽창: 모든 도구 정의(이름, 설명, JSON 스키마)가 모델의 프롬프트 컨텍스트에 추가되어 입력 토큰 수가 증가합니다.
- 지연 시간 증가: 더 큰 프롬프트는 응답 시간을 늦춥니다.
- 더 높은 비용: 더 많은 토큰 = 요청당 더 높은 API 비용
- 정확도 저하: 연구에 따르면 더 많은 컨텍스트와 더 많은 도구 모두 에이전트 성능을 저하시킵니다.
- 궤적 저하: 더 긴 추론 경로가 필요한 에이전트는 도구 확산과 함께 더 빠르게 저하됩니다.
- 도구 선택 혼란
- 결정 복잡성: 더 많은 도구는 모델이 올바른 도구를 선택하기 어렵게 합니다.
- 파라미터 설계 문제: LLM이 훈련된 공개 형식(프로젝트 경로, IID) 대신 내부 ID(GitLab 전역 ID 등)를 사용하면 도구 사용 효과가 줄어듭니다.
- 유사한 도구 간의 모호성: 모델은 유사한 목적의 도구(예: 로컬 대 원격 파일 작업)를 구분하는 데 어려움을 겪습니다.
- 사용자 경험 영향
- 도구 체이닝 소음: 더 많은 도구는 단일 요청에서 더 많은 도구 호출로 이어져 소음과 산만함을 만듭니다.
- 느린 응답: 처리 시간 증가는 사용자 만족도에 영향을 미칩니다.
- 잘못된 도구 선택: 잘못된 도구 선택은 잘못되거나 관련 없는 응답으로 이어집니다.
도구 추가 전에 개발자가 답해야 할 질문#
- 필요성: 이것이 정말 새로운 기능인가, 아니면 파라미터 조정으로 기존 도구가 처리할 수 있는가?
- 통합 가능성: 이 기능이 열거형이나 파라미터를 사용하여 기존 도구와 병합될 수 있는가?
- 파라미터 설계: 모델의 훈련 데이터와 일치하는 공개적으로 인식 가능한 식별자를 사용하는가?
- 평가 전략: 이 도구가 전반적인 에이전트 성능을 향상시키는지 저하시키는지 측정하는 방법은?
- 컨텍스트 관리: 이 도구의 정의가 실제 추론에 사용 가능한 컨텍스트 창에 어떻게 영향을 미치는가?
- 도구 라우팅: 이 도구가 메인 에이전트 도구 세트보다 특수 서브 에이전트의 일부여야 하는가?
- 권한 모델: 이 도구가 권한/가격 모델과 어떻게 상호작용하는가? 복잡성을 만드는가?
- 의미론적 구별: 이 도구가 도구 세트의 다른 도구들과 명확하게 구별될 수 있는가?
더 많은 도구가 항상 더 좋은 것은 아닙니다. 연구에 따르면 컨텍스트 크기와 도구 수 모두 수익이 감소하며 결국 성능 저하로 이어집니다. 도구 세트를 지속적으로 확장하는 대신 도구 통합, 특수 서브 에이전트 또는 동적 도구 라우팅을 고려하세요.
기존 도구 수정#
MCP 도구는 소비자에 대한 호환성 변경을 방지하기 위해 시맨틱 버저닝을 사용합니다. 도구를 수정할 때 이 머지 리퀘스트에서 도입된 버저닝 시스템을 사용하세요.
버저닝이 중요한 이유:
LLM과 AI 에이전트는 도구 스키마를 캐시하고 특정 도구 동작을 기반으로 워크플로우를 구축합니다. 도구 파라미터, 설명 또는 출력 형식의 변경은 기존 통합을 중단시킬 수 있습니다. 버저닝은 하위 호환성을 유지하면서 안전한 발전을 허용합니다.
버전 등록 패턴:
집계된 API, 커스텀 및 GraphQL 도구의 경우 register_version을 사용하여 버전을 등록합니다:
module Mcp
module Tools
class GetServerVersionService < CustomService
register_version '0.1.0', {
description: 'Get the current version of MCP server.',
input_schema: {
type: 'object',
properties: {},
required: []
}
}
def perform_0_1_0(_arguments = {})
data = { version: Gitlab::VERSION, revision: Gitlab.revision }
formatted_content = [{ type: 'text', text: data[:version] }]
::Mcp::Tools::Response.success(formatted_content, data)
end
override :perform_default
def perform_default(arguments = {})
perform_0_1_0(arguments)
end
end
end
end
새 버전 추가:
도구의 동작을 수정해야 할 때:
- 업데이트된 메타데이터로 새 버전을 등록합니다:
register_version '0.2.0', {
description: 'Get version with additional metadata.',
input_schema: {
type: 'object',
properties: {
include_metadata: {
type: 'boolean',
description: 'Include additional metadata'
}
},
required: []
}
}
- 버전별 메서드를 구현합니다:
def perform_0_2_0(arguments = {})
data = {
version: Gitlab::VERSION,
revision: Gitlab.revision
}
if arguments[:include_metadata]
data[:metadata] = { build_date: Time.current }
end
formatted_content = [{ type: 'text', text: data[:version] }]
::Mcp::Tools::Response.success(formatted_content, data)
end
- 최신 버전을 사용하도록
perform_default를 업데이트합니다:
override :perform_default
def perform_default(arguments = {})
perform_0_2_0(arguments)
end
API 도구의 경우:
API 도구는 자동으로 버전 0.1.0을 기본값으로 사용합니다. 필요한 경우 경로 설정에 버전을 지정할 수 있습니다:
route_setting :mcp, tool_name: :get_merge_request,
params: [:id, :merge_request_iid],
version: '1.0.0'
경로에서의 API 도구는 도구당 단일 버전을 사용합니다. 여러 버전이 필요한 도구의 경우 커스텀 도구로 구현하는 것을 고려하세요.
버전 지원 정책:
프레임워크는 버전이 지정되지 않은 경우 자동으로 최신 버전을 사용합니다. 소비자는 도구 호출 중에 특정 버전을 요청할 수 있습니다. 버전을 폐기할 때는 다중 버전 호환성 가이드라인을 따르세요.
도구 이름 변경#
도구 이름 변경은 하위 호환성을 유지하기 위해 도구 별칭을 사용해야 합니다. 연결된 클라이언트는 도구 이름을 캐시하며 도구 이름이 변경될 때 자동으로 새로 고치지 않습니다. 이 머지 리퀘스트에서 도입된 별칭 시스템은 기존 통합을 중단하지 않고 우아한 이름 변경을 허용합니다.
별칭이 필요한 이유:
MCP 클라이언트는 tools/list에서 도구 목록을 캐시하며 도구가 변경될 때 자동으로 다시 가져오지 않습니다. 도구 이름을 변경하면 클라이언트가 존재하지 않는 도구 이름을 호출하여 오류나 무한 대기가 발생합니다. MCP 사양은 변경 사항을 클라이언트에 알리기 위해 notifications/tools/list_changed를 지원하지만 GitLab MCP 서버는 이를 구현하지 않습니다(이 이슈에서 추적 중).
구현 단계:
- 도구 클래스에서
tool_aliases를 오버라이드하여 이전 이름을 포함합니다:
module Mcp
module Tools
class RenamedService < AggregatedService
override :tool_name
def self.tool_name
'new_name'
end
override :tool_aliases
def self.tool_aliases
['old_name']
end
end
end
end
-
새 도구 이름을 사용하도록 모든 참조를 업데이트합니다:
tool_name:이 있는 경로 설정- 테스트 파일
- 문서
- 하드코딩된 도구 이름 참조
-
Mcp::Tools::Manager는get_tool호출 중에 별칭을 자동으로 해결하므로 이전 이름을 사용하는 클라이언트는 계속 작동합니다.
중요 사항:
list_tools는 별칭이 아닌 표준 도구 이름만 반환합니다.- 별칭은 커스텀, GraphQL, API 및 집계된 도구 등 모든 도구 유형에서 작동합니다.
- 별칭 해결은 모든 도구 레지스트리를 확인하는
Manager#resolve_alias에서 발생합니다. - 클라이언트가 도구 목록을 새로 고칠 충분한 시간이 지난 후 향후 릴리스에서 별칭을 제거할 계획입니다.
폐기 타임라인:
릴리스 M: 별칭 추가 및 도구 이름 변경 릴리스 M+1: 클라이언트가 도구 목록을 새로 고칠 시간이 지난 후 별칭 제거
이 접근법은 도구 이름 변경 중에 연결된 클라이언트에 대한 무중단을 보장합니다.
