InfoGrab DocsInfoGrab Docs

MCP 서버 개발 가이드라인

요약

이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다. 더 자세한 로깅을 위해 mcp-remote 명령에 --debug를 추가합니다. Claude Desktop은 지원되지 않는 버전의 Node.js를 사용합니다.

이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다.

개발 환경 설정#

개발 환경을 설정하려면:

  1. GDK에서 HTTPS 활성화 및 구성.
  2. node를 설치하고 mcp-remote를 전역으로 설치합니다. GDK에는 Node.js가 포함되어 있지만 설치된 AI 어시스턴트는 GDK 버전을 사용할 수 없습니다.
  3. 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 도구 제안 이슈를 생성하고 템플릿 지침을 따르세요.

Note

도구 구현 위치는 GitLab 리소스와의 상호작용에 따라 달라집니다:

  • GitLab 리소스와 상호작용하는 도구는 궁극적으로 MCP 서버에 있어야 하지만, 단기 또는 긴급한 필요의 경우 에이전트 플랫폼에 구현할 수 있습니다.
  • GitLab 리소스와 상호작용하지 않는 도구는 에이전트 플랫폼에 구현해야 합니다.

MCP 서버 기능을 에이전트 플랫폼에 통합하는 작업을 진행 중입니다. 이 이슈를 통해 진행 상황을 추적할 수 있습니다.

모든 엔지니어가 도구 제안 프로세스를 따르고 사용 사례에 대한 명확한 설명을 제공하도록 강력히 권장합니다.

도구 이름 지정 및 통합 규칙#

도구를 추가하거나 통합할 때는 도구 표면을 작고 예측 가능하게 유지하기 위해 다음 규칙을 따르세요. 이 규칙은 기능을 잃지 않으면서 도구 수와 토큰 오버헤드를 줄입니다.

동사 클래스: 모든 도구 이름은 verb_object 형태를 사용하며, 동사는 작업 클래스를 나타냅니다:

  • 단일 객체에는 get_, 컬렉션에는 list_를 사용합니다.
  • 생성 및 업데이트 필드 변형에는 save_를 사용합니다. id의 존재 여부가 해당 작업이 생성 작업인지 업데이트 작업인지를 정의합니다. 생성 시 필수인 파라미터는 도구 정의에 그렇게 표시해야 합니다. 의도적인 예외: save_ 도구는 필드 변형이 아닌 수명 주기 액션(예: retry, cancel)을 action 파라미터 뒤에 묶을 수도 있습니다. 단, 해당 액션이 그 도구가 생성하는 것과 동일한 리소스에서 동작하고 자체 전용 도구를 둘 만하지 않은 경우에 한합니다. 상위 식별자가 아니라 리소스 자체의 ID 존재 여부로 라우팅하세요. 예를 들어 save_pipelinepipeline_id가 없으면 생성으로, pipeline_id가 있고 action이 함께 전달되면 해당 파이프라인에 대한 수명 주기 전환으로 처리합니다. 이를 도구 설명에 문서화하고 의도적인 예외로 기록하세요(아래 참조).
  • 실제 삭제 작업에는 delete_를 사용합니다. 더 나은 거버넌스 처리를 위해 이를 save_에 묶어서는 안 됩니다.
  • add_ 또는 이 패턴에서 벗어난 다른 형태는 커밋, 브랜치, 세션처럼 일반적인 CRUD 형태를 갖지 않는 객체에 예약되어 있습니다(예: add_commit, add_branch).

리소스 식별: 모든 프로젝트 범위 도구는 동일한 방식으로 대상을 식별합니다. 각 도구는 자체 입력 스키마에 url, project_id 및 리소스의 내부 ID를 선언합니다. 이러한 파라미터를 해석하려면 Mcp::Tools::Concerns::UrlParserMcp::Tools::Concerns::ResourceFinder concern을 포함하세요. 호출자는 다음 중 하나를 제공합니다:

  • url - 전체 경로를 인코딩한 전체 GitLab URL(예: https://gitlab.com/group/project/-/merge_requests/1), 또는
  • ID 그룹 - project_id(숫자 ID 또는 gitlab-org%2Fgitlab과 같은 URL 인코딩된 경로)와 리소스의 내부 ID(merge_request_iid, work_item_iid, commit_sha 등).

프로젝트 식별자와 내부 ID는 별도의 파라미터로 유지하세요. 이들을 단일 id로 묶지 마세요. 이들은 서로 다른 값이며(iid/sha는 프로젝트 범위로 지정되어 project_id 없이는 의미가 없습니다), url은 이미 이들을 하나로 합쳐주는 단일 값 편의 수단입니다. url과 ID가 모두 제공되면 서로 교차 검증되며 불일치 시 오류가 발생합니다. 작업 항목 도구는 동일한 그룹에서 group_id 또는 project_id를 허용합니다.

선택적 파라미터: Base::BaseService는 선택적 파라미터에 대한 명시적 null 또는 "" 값을 키가 생략된 것과 동일하게 처리하므로, 모든 스키마 속성을 채우는 호출자도 선택적 enum 파라미터에 대한 검증을 통과합니다.

읽기(단일 대 컬렉션):

  • 하나의 상위 객체로 범위가 지정된 패싯은 별도의 list_* 도구가 아니라 include 파라미터를 통해 해당 객체의 get_ 도구에 통합됩니다(예: include: ["diffs"]를 사용하는 get_merge_request). 유효한 값은 diffs, commits, notes, pipelines, discussions입니다. 호출당 하나의 패싯만 지원하더라도 include를 열거형 값의 배열로 선언하고 maxItems로 제한하세요. 나중에 그 상한을 올리는 것은 추가적인 변경이지만, 파라미터를 문자열에서 배열로 바꾸는 것은 기존 호출자를 중단시킵니다. 단일 패싯으로 범위가 지정된 필터(job_status)를 포함하여 이 패턴을 구현한 예시는 get_pipeline(include: ["jobs"]/["downstream_pipelines"]/["bridge_jobs"])을 참조하세요.
  • 자체적으로 조회할 수 있는 독립적인 컬렉션은 자체 list_ 도구를 갖습니다(예: list_merge_requests, list_pipelines).
  • 패싯 범위 페이지네이션은 get_ 리더에 위치하며 관련된 include 값에만 적용됩니다. 이를 파라미터 설명에 문서화하세요.
  • diff가 페이로드의 대부분을 차지하는 diff 포함 읽기에는 detail 열거형(none/stats/full_patch)을 추가하세요(예: get_commitdiff 패싯과 get_merge_requestdiffs 패싯). 더 적합한 조절 수단이 이미 있는 곳에는 detail을 소급 적용하지 마세요. 파일 내용은 줄 페이지네이션(offset/limit)을, 작업 로그는 바이트 페이지네이션(byte_offset/byte_limit)을 사용하는데, 이러한 API에는 diff 방식의 상세 수준이 없기 때문입니다.
  • 한 패싯이 다른 패싯의 부분집합인 경우 새 패싯이나 새 도구보다 필터 파라미터를 선호하세요(예: 별도의 failing_jobs 패싯 대신 job_status: failed 필터).

페이지네이션: 도구의 페이지네이션 방식은 서버 전역의 단일 규칙을 강요하는 대신 그 도구가 감싸는 엔드포인트의 방식을 그대로 따릅니다. 방식 간 변환을 하지 마세요(예: 페이지 번호를 불투명한 커서로 감싸지 마세요). 오프셋 페이지네이션 위에 합성한 커서는 커서 안정성을 얻지 못한 채 메커니즘만 숨기고, 엔드포인트가 제공하는 보장에 대해 호출자를 오해하게 만듭니다.

  • REST 기반 도구page(1부터 시작, 기본값 1)와 per_page(기본값 20, 최대 100)를 사용하는 오프셋 페이지네이션을 사용하며, page, per_page, has_more를 포함하는 metadata 객체를 반환합니다. list_repository_tree, list_branches, list_commits, list_pipelines, search와 같은 도구에 적용됩니다.
  • GraphQL 기반 도구first(기본값 20, 최대 100)와 after(불투명한 커서)를 사용하는 네이티브 커서 페이지네이션을 사용하며, endCursorhasNextPage를 포함하는 pageInfo 객체를 반환합니다. list_work_items, list_merge_requests와 같은 도구에 적용됩니다.
  • 콘텐츠 리더는 항목 페이지네이션 대신 페이로드에 맞는 범위 윈도우를 사용합니다. 파일 내용은 줄 페이지네이션(offset/limit)을, 작업 로그는 바이트 페이지네이션(byte_offset/byte_limit)을 사용합니다. 다음 윈도우를 가져오는 방법을 호출자에게 알려주는 system_instruction을 반환하세요.
  • get_ 리더의 패싯 범위 페이지네이션에는 패싯 이름을 접두사로 붙이며(예: get_merge_requestnotes_page/notes_per_page, get_commitcomments_page/comments_per_page), 해당 패싯을 지원하는 엔드포인트의 방식을 따릅니다.

확산보다 통합:

  • 리소스별 검색 도구를 추가하는 대신 범위가 지정된 검색 변형들을 scope 파라미터가 있는 통합 search 도구로 병합하세요.

의도적인 예외를 문서화하세요. 도구가 의도적으로 규칙을 위반하는 경우(일회성 액션 동사, 하나의 리소스에 대한 두 번째 write_ 도구), 실수로 오인되지 않도록 제안서에 의도적임을 기록하세요.

REST API 경로에서 도구 구현#

머지 리퀘스트는 API 경로에서 MCP 도구를 만드는 프로세스를 정의합니다.

API 경로 정의에 다음 route_setting을 추가합니다:

route_setting :mcp, tool_name: :get_issue, params: [:id, :issue_iid], resource_name: "issue"
  • 도구 목록에 get_issue 도구를 추가하고 실행을 활성화합니다.
  • 도구 및 파라미터 설명은 OpenAPI 경로 정의에서 가져옵니다.
  • 허용된 파라미터는 params 인수로 필터링됩니다. 예를 들어, idissue_iid만 공개되고 허용됩니다.
  • 도구가 호출될 때 경로 코드는 전달된 파라미터와 함께 직접 실행됩니다.
  • 선택적 resource_name 필드는 리소스별 404 오류 메시지를 제공합니다(예: 일반적인 "404 Not Found" 대신 "404 Issue Not Found"). "issue" 또는 "merge request"와 같은 소문자 문자열을 사용합니다. 렌더링된 메시지에서는 첫 글자가 대문자로 표시됩니다.

머지 리퀘스트는 더 많은 예시를 제공합니다.

집계된 REST API 도구 구현#

집계된 API 도구는 여러 관련 API 도구를 단일 통합 인터페이스로 결합하여 도구 수를 줄이고 사용자 경험을 향상시킵니다. 검색 도구는 전역, 그룹, 프로젝트 검색을 하나의 도구로 통합하여 이 패턴을 보여줍니다.

집계 도구를 사용해야 하는 경우:

비슷한 목적을 제공하지만 다른 범위(전역, 그룹, 프로젝트)에서 작동하는 여러 API 엔드포인트가 있을 때 집계 도구를 사용하세요. 이는 세 개의 도구 대신 하나의 도구를 제시하여 LLM의 인지 부하를 줄입니다.

구현 단계:

  1. Mcp::Tools::Base::AggregatedService를 상속하는 집계 서비스 클래스를 생성합니다:
module Mcp
  module Tools
    class ExampleAggregatedService < Base::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
  1. 경로 정의에서 기본 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]
  1. Mcp::Tools::Manageraggregators가 지정된 경로를 스캔하고 수집된 도구로 집계기 클래스를 인스턴스화하여 집계된 도구를 자동으로 발견합니다.

GraphQL 기반 도구 구현#

GitLab GraphQL API를 사용하는 MCP 도구의 경우 GraphQL 통합 가이드라인을 참조하세요.

AI 코딩 어시스턴트로 GraphQL 기반 도구를 스캐폴딩하려면 gitlab-mcp-tool-builder 스킬을 사용하세요. 이 스킬은 이 페이지와 GraphQL 통합 가이드라인의 지침을 요약하고, 흔히 겪는 함정을 추가로 담고 있으며, 도구 클래스, 서비스 클래스, .graphql 작업 파일, 매니저 등록, 스펙 작성 과정을 안내합니다. 이 저장소에는 .claude/skills/gitlab-mcp-tool-builder/에 이 스킬이 포함되어 있으므로 따로 설치할 것이 없습니다:

  • Claude Code와 GitLab Duo CLI는 세션 시작 시 이 스킬을 발견합니다. 어시스턴트에게 GraphQL MCP 도구를 빌드하거나 스캐폴딩하도록 요청하거나, 스킬을 이름으로 직접 호출하세요. GitLab Duo CLI가 스킬을 발견하고 실행하는 방법은 에이전트 스킬을 참조하세요.
  • AGENTS.md 규칙을 따르는 어시스턴트는 .agents/skills 심볼릭 링크를 통해 동일한 스킬을 로드합니다.

가이드라인이 신뢰할 수 있는 원본입니다. 스킬과 가이드라인이 서로 다를 경우 가이드라인을 따르고 스킬을 업데이트하세요.

커스텀 도구 구현#

API 노출과 별도로 유지해야 하는 별도 기능을 가진 도구의 경우 독립적인 클래스를 정의할 수 있습니다(이 예시 참조).

AI 에이전트 아키텍처에서의 도구 확산 위험#

너무 많은 도구 추가 시 핵심 문제#
  1. 성능 저하
  • 컨텍스트 팽창: 모든 도구 정의(이름, 설명, JSON 스키마)가 모델의 프롬프트 컨텍스트에 추가되어 입력 토큰 수가 증가합니다.
  • 지연 시간 증가: 더 큰 프롬프트는 응답 시간을 늦춥니다.
  • 더 높은 비용: 더 많은 토큰 = 요청당 더 높은 API 비용
  • 정확도 저하: 연구에 따르면 더 많은 컨텍스트와 더 많은 도구 모두 에이전트 성능을 저하시킵니다.
  • 궤적 저하: 더 긴 추론 경로가 필요한 에이전트는 도구 확산과 함께 더 빠르게 저하됩니다.
  1. 도구 선택 혼란
  • 결정 복잡성: 더 많은 도구는 모델이 올바른 도구를 선택하기 어렵게 합니다.
  • 파라미터 설계 문제: LLM이 훈련된 공개 형식(프로젝트 경로, IID) 대신 내부 ID(GitLab 전역 ID 등)를 사용하면 도구 사용 효과가 줄어듭니다.
  • 유사한 도구 간의 모호성: 모델은 유사한 목적의 도구(예: 로컬 대 원격 파일 작업)를 구분하는 데 어려움을 겪습니다.
  1. 사용자 경험 영향
  • 도구 체이닝 소음: 더 많은 도구는 단일 요청에서 더 많은 도구 호출로 이어져 소음과 산만함을 만듭니다.
  • 느린 응답: 처리 시간 증가는 사용자 만족도에 영향을 미칩니다.
  • 잘못된 도구 선택: 잘못된 도구 선택은 잘못되거나 관련 없는 응답으로 이어집니다.
도구 추가 전에 개발자가 답해야 할 질문#
  1. 필요성: 이것이 정말 새로운 기능인가, 아니면 파라미터 조정으로 기존 도구가 처리할 수 있는가?
  2. 통합 가능성: 이 기능이 열거형이나 파라미터를 사용하여 기존 도구와 병합될 수 있는가?
  3. 파라미터 설계: 모델의 훈련 데이터와 일치하는 공개적으로 인식 가능한 식별자를 사용하는가?
  4. 평가 전략: 이 도구가 전반적인 에이전트 성능을 향상시키는지 저하시키는지 측정하는 방법은?
  5. 컨텍스트 관리: 이 도구의 정의가 실제 추론에 사용 가능한 컨텍스트 창에 어떻게 영향을 미치는가?
  6. 도구 라우팅: 이 도구가 메인 에이전트 도구 세트보다 특수 서브 에이전트의 일부여야 하는가?
  7. 권한 모델: 이 도구가 권한/가격 모델과 어떻게 상호작용하는가? 복잡성을 만드는가?
  8. 의미론적 구별: 이 도구가 도구 세트의 다른 도구들과 명확하게 구별될 수 있는가?
Warning

더 많은 도구가 항상 더 좋은 것은 아닙니다. 연구에 따르면 컨텍스트 크기와 도구 수 모두 수익이 감소하며 결국 성능 저하로 이어집니다. 도구 세트를 지속적으로 확장하는 대신 도구 통합, 특수 서브 에이전트 또는 동적 도구 라우팅을 고려하세요.

기존 도구 수정#

MCP 도구는 소비자에 대한 호환성 변경을 방지하기 위해 시맨틱 버저닝을 사용합니다. 도구를 수정할 때 이 머지 리퀘스트에서 도입된 버저닝 시스템을 사용하세요.

버저닝이 중요한 이유:

LLM과 AI 에이전트는 도구 스키마를 캐시하고 특정 도구 동작을 기반으로 워크플로우를 구축합니다. 도구 파라미터, 설명 또는 출력 형식의 변경은 기존 통합을 중단시킬 수 있습니다. 버저닝은 하위 호환성을 유지하면서 안전한 발전을 허용합니다.

버전 등록 패턴:

집계된 API, 커스텀 및 GraphQL 도구의 경우 register_version을 사용하여 버전을 등록합니다:

module Mcp
  module Tools
    class GetServerVersionService < Base::CustomService
      register_version '0.1.0', {
        description: 'Get the current version of MCP server.',
        input_schema: {
          type: 'object',
          properties: {},
          required: []
        }
      }

      def perform_v0_1_0(_arguments = {})
        data = { version: Gitlab::VERSION, revision: Gitlab.revision }
        formatted_content = [{ type: 'text', text: data[:version] }]
        ::Mcp::Tools::Base::Response.success(formatted_content, data)
      end

      override :perform_default
      def perform_default(arguments = {})
        perform_v0_1_0(arguments)
      end
    end
  end
end

새 버전 추가:

도구의 동작을 수정해야 할 때:

  1. 업데이트된 메타데이터로 새 버전을 등록합니다:
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: []
  }
}
  1. 버전별 메서드를 구현합니다:
def perform_v0_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::Base::Response.success(formatted_content, data)
end
  1. 최신 버전을 사용하도록 perform_default를 업데이트합니다:
override :perform_default
def perform_default(arguments = {})
  perform_v0_2_0(arguments)
end

API 도구의 경우:

API 도구는 자동으로 버전 0.1.0을 기본값으로 사용합니다. 필요한 경우 경로 설정에 버전을 지정할 수 있습니다:

route_setting :mcp, tool_name: :get_issue,
  params: [:id, :issue_iid],
  version: '1.0.0'
Note

경로에서의 API 도구는 도구당 단일 버전을 사용합니다. 여러 버전이 필요한 도구의 경우 커스텀 도구로 구현하는 것을 고려하세요.

버전 지원 정책:

프레임워크는 버전이 지정되지 않은 경우 자동으로 최신 버전을 사용합니다. 소비자는 도구 호출 중에 특정 버전을 요청할 수 있습니다. 버전을 폐기할 때는 다중 버전 호환성 가이드라인을 따르세요.

도구 이름 변경#

도구 이름 변경은 하위 호환성을 유지하기 위해 도구 별칭을 사용해야 합니다. 연결된 클라이언트는 도구 이름을 캐시하며 도구 이름이 변경될 때 자동으로 새로 고치지 않습니다. 이 머지 리퀘스트에서 도입된 별칭 시스템은 기존 통합을 중단하지 않고 우아한 이름 변경을 허용합니다.

별칭이 필요한 이유:

MCP 클라이언트는 tools/list에서 도구 목록을 캐시하며 도구가 변경될 때 자동으로 다시 가져오지 않습니다. 도구 이름을 변경하면 클라이언트가 존재하지 않는 도구 이름을 호출하여 오류나 무한 대기가 발생합니다. MCP 사양은 변경 사항을 클라이언트에 알리기 위해 notifications/tools/list_changed를 지원하지만 GitLab MCP 서버는 이를 구현하지 않습니다(이 이슈에서 추적 중).

구현 단계:

  1. 도구 클래스에서 tool_aliases를 오버라이드하여 이전 이름을 포함합니다:
module Mcp
  module Tools
    class RenamedService < Base::AggregatedService
      override :tool_name
      def self.tool_name
        'new_name'
      end

      override :tool_aliases
      def self.tool_aliases
        ['old_name']
      end
    end
  end
end

route_setting :mcp를 통해 정의된 API 도구의 경우, 클래스 메서드를 오버라이드하는 대신 tool_aliases: 설정으로 별칭을 선언합니다. ApiTool은 모든 경로가 공유하는 단일 클래스이므로 별칭은 경로별로 지정해야 합니다:

route_setting :mcp, tool_name: :new_name,
  params: [:id],
  tool_aliases: [:old_name]
  1. 새 도구 이름을 사용하도록 모든 참조를 업데이트합니다:

    • tool_name:이 있는 경로 설정
    • 테스트 파일
    • 문서
    • 하드코딩된 도구 이름 참조
  2. Mcp::Tools::Managerget_tool 호출 중에 별칭을 자동으로 해결하므로 이전 이름을 사용하는 클라이언트는 계속 작동합니다.

중요 사항:

  • list_tools는 별칭이 아닌 표준 도구 이름만 반환합니다.
  • 별칭은 모든 도구 유형에서 작동합니다. 커스텀, GraphQL 및 집계된 도구는 도구 클래스에서 self.tool_aliases를 오버라이드합니다. API 도구는 tool_aliases: 경로 설정을 통해 별칭을 선언합니다.
  • aggregators:도 함께 설정하는 경로에서 tool_aliases:는 효과가 없습니다. 집계된 도구의 별칭은 집계기 클래스의 self.tool_aliases에서 가져옵니다.
  • 별칭 해결은 모든 도구 레지스트리를 확인하는 Manager#resolve_alias에서 발생합니다.
  • 클라이언트가 도구 목록을 새로 고칠 충분한 시간이 지난 후 향후 릴리스에서 별칭을 제거할 계획입니다.

폐기 타임라인:

릴리스 M: 별칭 추가 및 도구 이름 변경 릴리스 M+1: 클라이언트가 도구 목록을 새로 고칠 시간이 지난 후 별칭 제거

이 접근법은 도구 이름 변경 중에 연결된 클라이언트에 대한 무중단을 보장합니다.

집계된 도구에서 액션 분리#

여러 작업을 하나의 파라미터(예: action 또는 불리언 플래그) 뒤에 묶은 집계된 도구는 list/save 분리 규칙에 따라 컬렉션 읽기 액션을 위한 전용 list_ 도구를 가질 수 있습니다. 이름 변경과 달리 호출 형태가 바뀌므로(호출자가 더 이상 선택자 파라미터를 전달하지 않음) 도구 별칭으로는 동작을 보존할 수 없습니다. 이전 액션을 사용하던 호출자는 새 도구로 명시적으로 마이그레이션해야 합니다.

MCP 서버 도구에서 이전 도구의 블록에 [Removed] 항목을 추가해 어떤 액션이 어느 도구로 이동했는지 기록하고, 새 도구에 대한 일반적인 [Introduced] 항목도 함께 문서화하세요. 예시는 해당 페이지의 list_pipelinesmanage_pipeline 항목을 참조하세요.

MCP 서버 개발 가이드라인

GitLab v19.3
원문 보기

요약

이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다. 더 자세한 로깅을 위해 mcp-remote 명령에 --debug를 추가합니다. Claude Desktop은 지원되지 않는 버전의 Node.js를 사용합니다.

이 페이지는 GitLab MCP 서버를 개발하고 사용하는 방법에 대한 정보를 포함합니다.

개발 환경 설정#

개발 환경을 설정하려면:

  1. GDK에서 HTTPS 활성화 및 구성.
  2. node를 설치하고 mcp-remote를 전역으로 설치합니다. GDK에는 Node.js가 포함되어 있지만 설치된 AI 어시스턴트는 GDK 버전을 사용할 수 없습니다.
  3. 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 도구 제안 이슈를 생성하고 템플릿 지침을 따르세요.

Note

도구 구현 위치는 GitLab 리소스와의 상호작용에 따라 달라집니다:

  • GitLab 리소스와 상호작용하는 도구는 궁극적으로 MCP 서버에 있어야 하지만, 단기 또는 긴급한 필요의 경우 에이전트 플랫폼에 구현할 수 있습니다.
  • GitLab 리소스와 상호작용하지 않는 도구는 에이전트 플랫폼에 구현해야 합니다.

MCP 서버 기능을 에이전트 플랫폼에 통합하는 작업을 진행 중입니다. 이 이슈를 통해 진행 상황을 추적할 수 있습니다.

모든 엔지니어가 도구 제안 프로세스를 따르고 사용 사례에 대한 명확한 설명을 제공하도록 강력히 권장합니다.

도구 이름 지정 및 통합 규칙#

도구를 추가하거나 통합할 때는 도구 표면을 작고 예측 가능하게 유지하기 위해 다음 규칙을 따르세요. 이 규칙은 기능을 잃지 않으면서 도구 수와 토큰 오버헤드를 줄입니다.

동사 클래스: 모든 도구 이름은 verb_object 형태를 사용하며, 동사는 작업 클래스를 나타냅니다:

  • 단일 객체에는 get_, 컬렉션에는 list_를 사용합니다.
  • 생성 및 업데이트 필드 변형에는 save_를 사용합니다. id의 존재 여부가 해당 작업이 생성 작업인지 업데이트 작업인지를 정의합니다. 생성 시 필수인 파라미터는 도구 정의에 그렇게 표시해야 합니다. 의도적인 예외: save_ 도구는 필드 변형이 아닌 수명 주기 액션(예: retry, cancel)을 action 파라미터 뒤에 묶을 수도 있습니다. 단, 해당 액션이 그 도구가 생성하는 것과 동일한 리소스에서 동작하고 자체 전용 도구를 둘 만하지 않은 경우에 한합니다. 상위 식별자가 아니라 리소스 자체의 ID 존재 여부로 라우팅하세요. 예를 들어 save_pipelinepipeline_id가 없으면 생성으로, pipeline_id가 있고 action이 함께 전달되면 해당 파이프라인에 대한 수명 주기 전환으로 처리합니다. 이를 도구 설명에 문서화하고 의도적인 예외로 기록하세요(아래 참조).
  • 실제 삭제 작업에는 delete_를 사용합니다. 더 나은 거버넌스 처리를 위해 이를 save_에 묶어서는 안 됩니다.
  • add_ 또는 이 패턴에서 벗어난 다른 형태는 커밋, 브랜치, 세션처럼 일반적인 CRUD 형태를 갖지 않는 객체에 예약되어 있습니다(예: add_commit, add_branch).

리소스 식별: 모든 프로젝트 범위 도구는 동일한 방식으로 대상을 식별합니다. 각 도구는 자체 입력 스키마에 url, project_id 및 리소스의 내부 ID를 선언합니다. 이러한 파라미터를 해석하려면 Mcp::Tools::Concerns::UrlParserMcp::Tools::Concerns::ResourceFinder concern을 포함하세요. 호출자는 다음 중 하나를 제공합니다:

  • url - 전체 경로를 인코딩한 전체 GitLab URL(예: https://gitlab.com/group/project/-/merge_requests/1), 또는
  • ID 그룹 - project_id(숫자 ID 또는 gitlab-org%2Fgitlab과 같은 URL 인코딩된 경로)와 리소스의 내부 ID(merge_request_iid, work_item_iid, commit_sha 등).

프로젝트 식별자와 내부 ID는 별도의 파라미터로 유지하세요. 이들을 단일 id로 묶지 마세요. 이들은 서로 다른 값이며(iid/sha는 프로젝트 범위로 지정되어 project_id 없이는 의미가 없습니다), url은 이미 이들을 하나로 합쳐주는 단일 값 편의 수단입니다. url과 ID가 모두 제공되면 서로 교차 검증되며 불일치 시 오류가 발생합니다. 작업 항목 도구는 동일한 그룹에서 group_id 또는 project_id를 허용합니다.

선택적 파라미터: Base::BaseService는 선택적 파라미터에 대한 명시적 null 또는 "" 값을 키가 생략된 것과 동일하게 처리하므로, 모든 스키마 속성을 채우는 호출자도 선택적 enum 파라미터에 대한 검증을 통과합니다.

읽기(단일 대 컬렉션):

  • 하나의 상위 객체로 범위가 지정된 패싯은 별도의 list_* 도구가 아니라 include 파라미터를 통해 해당 객체의 get_ 도구에 통합됩니다(예: include: ["diffs"]를 사용하는 get_merge_request). 유효한 값은 diffs, commits, notes, pipelines, discussions입니다. 호출당 하나의 패싯만 지원하더라도 include를 열거형 값의 배열로 선언하고 maxItems로 제한하세요. 나중에 그 상한을 올리는 것은 추가적인 변경이지만, 파라미터를 문자열에서 배열로 바꾸는 것은 기존 호출자를 중단시킵니다. 단일 패싯으로 범위가 지정된 필터(job_status)를 포함하여 이 패턴을 구현한 예시는 get_pipeline(include: ["jobs"]/["downstream_pipelines"]/["bridge_jobs"])을 참조하세요.
  • 자체적으로 조회할 수 있는 독립적인 컬렉션은 자체 list_ 도구를 갖습니다(예: list_merge_requests, list_pipelines).
  • 패싯 범위 페이지네이션은 get_ 리더에 위치하며 관련된 include 값에만 적용됩니다. 이를 파라미터 설명에 문서화하세요.
  • diff가 페이로드의 대부분을 차지하는 diff 포함 읽기에는 detail 열거형(none/stats/full_patch)을 추가하세요(예: get_commitdiff 패싯과 get_merge_requestdiffs 패싯). 더 적합한 조절 수단이 이미 있는 곳에는 detail을 소급 적용하지 마세요. 파일 내용은 줄 페이지네이션(offset/limit)을, 작업 로그는 바이트 페이지네이션(byte_offset/byte_limit)을 사용하는데, 이러한 API에는 diff 방식의 상세 수준이 없기 때문입니다.
  • 한 패싯이 다른 패싯의 부분집합인 경우 새 패싯이나 새 도구보다 필터 파라미터를 선호하세요(예: 별도의 failing_jobs 패싯 대신 job_status: failed 필터).

페이지네이션: 도구의 페이지네이션 방식은 서버 전역의 단일 규칙을 강요하는 대신 그 도구가 감싸는 엔드포인트의 방식을 그대로 따릅니다. 방식 간 변환을 하지 마세요(예: 페이지 번호를 불투명한 커서로 감싸지 마세요). 오프셋 페이지네이션 위에 합성한 커서는 커서 안정성을 얻지 못한 채 메커니즘만 숨기고, 엔드포인트가 제공하는 보장에 대해 호출자를 오해하게 만듭니다.

  • REST 기반 도구page(1부터 시작, 기본값 1)와 per_page(기본값 20, 최대 100)를 사용하는 오프셋 페이지네이션을 사용하며, page, per_page, has_more를 포함하는 metadata 객체를 반환합니다. list_repository_tree, list_branches, list_commits, list_pipelines, search와 같은 도구에 적용됩니다.
  • GraphQL 기반 도구first(기본값 20, 최대 100)와 after(불투명한 커서)를 사용하는 네이티브 커서 페이지네이션을 사용하며, endCursorhasNextPage를 포함하는 pageInfo 객체를 반환합니다. list_work_items, list_merge_requests와 같은 도구에 적용됩니다.
  • 콘텐츠 리더는 항목 페이지네이션 대신 페이로드에 맞는 범위 윈도우를 사용합니다. 파일 내용은 줄 페이지네이션(offset/limit)을, 작업 로그는 바이트 페이지네이션(byte_offset/byte_limit)을 사용합니다. 다음 윈도우를 가져오는 방법을 호출자에게 알려주는 system_instruction을 반환하세요.
  • get_ 리더의 패싯 범위 페이지네이션에는 패싯 이름을 접두사로 붙이며(예: get_merge_requestnotes_page/notes_per_page, get_commitcomments_page/comments_per_page), 해당 패싯을 지원하는 엔드포인트의 방식을 따릅니다.

확산보다 통합:

  • 리소스별 검색 도구를 추가하는 대신 범위가 지정된 검색 변형들을 scope 파라미터가 있는 통합 search 도구로 병합하세요.

의도적인 예외를 문서화하세요. 도구가 의도적으로 규칙을 위반하는 경우(일회성 액션 동사, 하나의 리소스에 대한 두 번째 write_ 도구), 실수로 오인되지 않도록 제안서에 의도적임을 기록하세요.

REST API 경로에서 도구 구현#

머지 리퀘스트는 API 경로에서 MCP 도구를 만드는 프로세스를 정의합니다.

API 경로 정의에 다음 route_setting을 추가합니다:

route_setting :mcp, tool_name: :get_issue, params: [:id, :issue_iid], resource_name: "issue"
  • 도구 목록에 get_issue 도구를 추가하고 실행을 활성화합니다.
  • 도구 및 파라미터 설명은 OpenAPI 경로 정의에서 가져옵니다.
  • 허용된 파라미터는 params 인수로 필터링됩니다. 예를 들어, idissue_iid만 공개되고 허용됩니다.
  • 도구가 호출될 때 경로 코드는 전달된 파라미터와 함께 직접 실행됩니다.
  • 선택적 resource_name 필드는 리소스별 404 오류 메시지를 제공합니다(예: 일반적인 "404 Not Found" 대신 "404 Issue Not Found"). "issue" 또는 "merge request"와 같은 소문자 문자열을 사용합니다. 렌더링된 메시지에서는 첫 글자가 대문자로 표시됩니다.

머지 리퀘스트는 더 많은 예시를 제공합니다.

집계된 REST API 도구 구현#

집계된 API 도구는 여러 관련 API 도구를 단일 통합 인터페이스로 결합하여 도구 수를 줄이고 사용자 경험을 향상시킵니다. 검색 도구는 전역, 그룹, 프로젝트 검색을 하나의 도구로 통합하여 이 패턴을 보여줍니다.

집계 도구를 사용해야 하는 경우:

비슷한 목적을 제공하지만 다른 범위(전역, 그룹, 프로젝트)에서 작동하는 여러 API 엔드포인트가 있을 때 집계 도구를 사용하세요. 이는 세 개의 도구 대신 하나의 도구를 제시하여 LLM의 인지 부하를 줄입니다.

구현 단계:

  1. Mcp::Tools::Base::AggregatedService를 상속하는 집계 서비스 클래스를 생성합니다:
module Mcp
  module Tools
    class ExampleAggregatedService < Base::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
  1. 경로 정의에서 기본 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]
  1. Mcp::Tools::Manageraggregators가 지정된 경로를 스캔하고 수집된 도구로 집계기 클래스를 인스턴스화하여 집계된 도구를 자동으로 발견합니다.

GraphQL 기반 도구 구현#

GitLab GraphQL API를 사용하는 MCP 도구의 경우 GraphQL 통합 가이드라인을 참조하세요.

AI 코딩 어시스턴트로 GraphQL 기반 도구를 스캐폴딩하려면 gitlab-mcp-tool-builder 스킬을 사용하세요. 이 스킬은 이 페이지와 GraphQL 통합 가이드라인의 지침을 요약하고, 흔히 겪는 함정을 추가로 담고 있으며, 도구 클래스, 서비스 클래스, .graphql 작업 파일, 매니저 등록, 스펙 작성 과정을 안내합니다. 이 저장소에는 .claude/skills/gitlab-mcp-tool-builder/에 이 스킬이 포함되어 있으므로 따로 설치할 것이 없습니다:

  • Claude Code와 GitLab Duo CLI는 세션 시작 시 이 스킬을 발견합니다. 어시스턴트에게 GraphQL MCP 도구를 빌드하거나 스캐폴딩하도록 요청하거나, 스킬을 이름으로 직접 호출하세요. GitLab Duo CLI가 스킬을 발견하고 실행하는 방법은 에이전트 스킬을 참조하세요.
  • AGENTS.md 규칙을 따르는 어시스턴트는 .agents/skills 심볼릭 링크를 통해 동일한 스킬을 로드합니다.

가이드라인이 신뢰할 수 있는 원본입니다. 스킬과 가이드라인이 서로 다를 경우 가이드라인을 따르고 스킬을 업데이트하세요.

커스텀 도구 구현#

API 노출과 별도로 유지해야 하는 별도 기능을 가진 도구의 경우 독립적인 클래스를 정의할 수 있습니다(이 예시 참조).

AI 에이전트 아키텍처에서의 도구 확산 위험#

너무 많은 도구 추가 시 핵심 문제#
  1. 성능 저하
  • 컨텍스트 팽창: 모든 도구 정의(이름, 설명, JSON 스키마)가 모델의 프롬프트 컨텍스트에 추가되어 입력 토큰 수가 증가합니다.
  • 지연 시간 증가: 더 큰 프롬프트는 응답 시간을 늦춥니다.
  • 더 높은 비용: 더 많은 토큰 = 요청당 더 높은 API 비용
  • 정확도 저하: 연구에 따르면 더 많은 컨텍스트와 더 많은 도구 모두 에이전트 성능을 저하시킵니다.
  • 궤적 저하: 더 긴 추론 경로가 필요한 에이전트는 도구 확산과 함께 더 빠르게 저하됩니다.
  1. 도구 선택 혼란
  • 결정 복잡성: 더 많은 도구는 모델이 올바른 도구를 선택하기 어렵게 합니다.
  • 파라미터 설계 문제: LLM이 훈련된 공개 형식(프로젝트 경로, IID) 대신 내부 ID(GitLab 전역 ID 등)를 사용하면 도구 사용 효과가 줄어듭니다.
  • 유사한 도구 간의 모호성: 모델은 유사한 목적의 도구(예: 로컬 대 원격 파일 작업)를 구분하는 데 어려움을 겪습니다.
  1. 사용자 경험 영향
  • 도구 체이닝 소음: 더 많은 도구는 단일 요청에서 더 많은 도구 호출로 이어져 소음과 산만함을 만듭니다.
  • 느린 응답: 처리 시간 증가는 사용자 만족도에 영향을 미칩니다.
  • 잘못된 도구 선택: 잘못된 도구 선택은 잘못되거나 관련 없는 응답으로 이어집니다.
도구 추가 전에 개발자가 답해야 할 질문#
  1. 필요성: 이것이 정말 새로운 기능인가, 아니면 파라미터 조정으로 기존 도구가 처리할 수 있는가?
  2. 통합 가능성: 이 기능이 열거형이나 파라미터를 사용하여 기존 도구와 병합될 수 있는가?
  3. 파라미터 설계: 모델의 훈련 데이터와 일치하는 공개적으로 인식 가능한 식별자를 사용하는가?
  4. 평가 전략: 이 도구가 전반적인 에이전트 성능을 향상시키는지 저하시키는지 측정하는 방법은?
  5. 컨텍스트 관리: 이 도구의 정의가 실제 추론에 사용 가능한 컨텍스트 창에 어떻게 영향을 미치는가?
  6. 도구 라우팅: 이 도구가 메인 에이전트 도구 세트보다 특수 서브 에이전트의 일부여야 하는가?
  7. 권한 모델: 이 도구가 권한/가격 모델과 어떻게 상호작용하는가? 복잡성을 만드는가?
  8. 의미론적 구별: 이 도구가 도구 세트의 다른 도구들과 명확하게 구별될 수 있는가?
Warning

더 많은 도구가 항상 더 좋은 것은 아닙니다. 연구에 따르면 컨텍스트 크기와 도구 수 모두 수익이 감소하며 결국 성능 저하로 이어집니다. 도구 세트를 지속적으로 확장하는 대신 도구 통합, 특수 서브 에이전트 또는 동적 도구 라우팅을 고려하세요.

기존 도구 수정#

MCP 도구는 소비자에 대한 호환성 변경을 방지하기 위해 시맨틱 버저닝을 사용합니다. 도구를 수정할 때 이 머지 리퀘스트에서 도입된 버저닝 시스템을 사용하세요.

버저닝이 중요한 이유:

LLM과 AI 에이전트는 도구 스키마를 캐시하고 특정 도구 동작을 기반으로 워크플로우를 구축합니다. 도구 파라미터, 설명 또는 출력 형식의 변경은 기존 통합을 중단시킬 수 있습니다. 버저닝은 하위 호환성을 유지하면서 안전한 발전을 허용합니다.

버전 등록 패턴:

집계된 API, 커스텀 및 GraphQL 도구의 경우 register_version을 사용하여 버전을 등록합니다:

module Mcp
  module Tools
    class GetServerVersionService < Base::CustomService
      register_version '0.1.0', {
        description: 'Get the current version of MCP server.',
        input_schema: {
          type: 'object',
          properties: {},
          required: []
        }
      }

      def perform_v0_1_0(_arguments = {})
        data = { version: Gitlab::VERSION, revision: Gitlab.revision }
        formatted_content = [{ type: 'text', text: data[:version] }]
        ::Mcp::Tools::Base::Response.success(formatted_content, data)
      end

      override :perform_default
      def perform_default(arguments = {})
        perform_v0_1_0(arguments)
      end
    end
  end
end

새 버전 추가:

도구의 동작을 수정해야 할 때:

  1. 업데이트된 메타데이터로 새 버전을 등록합니다:
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: []
  }
}
  1. 버전별 메서드를 구현합니다:
def perform_v0_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::Base::Response.success(formatted_content, data)
end
  1. 최신 버전을 사용하도록 perform_default를 업데이트합니다:
override :perform_default
def perform_default(arguments = {})
  perform_v0_2_0(arguments)
end

API 도구의 경우:

API 도구는 자동으로 버전 0.1.0을 기본값으로 사용합니다. 필요한 경우 경로 설정에 버전을 지정할 수 있습니다:

route_setting :mcp, tool_name: :get_issue,
  params: [:id, :issue_iid],
  version: '1.0.0'
Note

경로에서의 API 도구는 도구당 단일 버전을 사용합니다. 여러 버전이 필요한 도구의 경우 커스텀 도구로 구현하는 것을 고려하세요.

버전 지원 정책:

프레임워크는 버전이 지정되지 않은 경우 자동으로 최신 버전을 사용합니다. 소비자는 도구 호출 중에 특정 버전을 요청할 수 있습니다. 버전을 폐기할 때는 다중 버전 호환성 가이드라인을 따르세요.

도구 이름 변경#

도구 이름 변경은 하위 호환성을 유지하기 위해 도구 별칭을 사용해야 합니다. 연결된 클라이언트는 도구 이름을 캐시하며 도구 이름이 변경될 때 자동으로 새로 고치지 않습니다. 이 머지 리퀘스트에서 도입된 별칭 시스템은 기존 통합을 중단하지 않고 우아한 이름 변경을 허용합니다.

별칭이 필요한 이유:

MCP 클라이언트는 tools/list에서 도구 목록을 캐시하며 도구가 변경될 때 자동으로 다시 가져오지 않습니다. 도구 이름을 변경하면 클라이언트가 존재하지 않는 도구 이름을 호출하여 오류나 무한 대기가 발생합니다. MCP 사양은 변경 사항을 클라이언트에 알리기 위해 notifications/tools/list_changed를 지원하지만 GitLab MCP 서버는 이를 구현하지 않습니다(이 이슈에서 추적 중).

구현 단계:

  1. 도구 클래스에서 tool_aliases를 오버라이드하여 이전 이름을 포함합니다:
module Mcp
  module Tools
    class RenamedService < Base::AggregatedService
      override :tool_name
      def self.tool_name
        'new_name'
      end

      override :tool_aliases
      def self.tool_aliases
        ['old_name']
      end
    end
  end
end

route_setting :mcp를 통해 정의된 API 도구의 경우, 클래스 메서드를 오버라이드하는 대신 tool_aliases: 설정으로 별칭을 선언합니다. ApiTool은 모든 경로가 공유하는 단일 클래스이므로 별칭은 경로별로 지정해야 합니다:

route_setting :mcp, tool_name: :new_name,
  params: [:id],
  tool_aliases: [:old_name]
  1. 새 도구 이름을 사용하도록 모든 참조를 업데이트합니다:

    • tool_name:이 있는 경로 설정
    • 테스트 파일
    • 문서
    • 하드코딩된 도구 이름 참조
  2. Mcp::Tools::Managerget_tool 호출 중에 별칭을 자동으로 해결하므로 이전 이름을 사용하는 클라이언트는 계속 작동합니다.

중요 사항:

  • list_tools는 별칭이 아닌 표준 도구 이름만 반환합니다.
  • 별칭은 모든 도구 유형에서 작동합니다. 커스텀, GraphQL 및 집계된 도구는 도구 클래스에서 self.tool_aliases를 오버라이드합니다. API 도구는 tool_aliases: 경로 설정을 통해 별칭을 선언합니다.
  • aggregators:도 함께 설정하는 경로에서 tool_aliases:는 효과가 없습니다. 집계된 도구의 별칭은 집계기 클래스의 self.tool_aliases에서 가져옵니다.
  • 별칭 해결은 모든 도구 레지스트리를 확인하는 Manager#resolve_alias에서 발생합니다.
  • 클라이언트가 도구 목록을 새로 고칠 충분한 시간이 지난 후 향후 릴리스에서 별칭을 제거할 계획입니다.

폐기 타임라인:

릴리스 M: 별칭 추가 및 도구 이름 변경 릴리스 M+1: 클라이언트가 도구 목록을 새로 고칠 시간이 지난 후 별칭 제거

이 접근법은 도구 이름 변경 중에 연결된 클라이언트에 대한 무중단을 보장합니다.

집계된 도구에서 액션 분리#

여러 작업을 하나의 파라미터(예: action 또는 불리언 플래그) 뒤에 묶은 집계된 도구는 list/save 분리 규칙에 따라 컬렉션 읽기 액션을 위한 전용 list_ 도구를 가질 수 있습니다. 이름 변경과 달리 호출 형태가 바뀌므로(호출자가 더 이상 선택자 파라미터를 전달하지 않음) 도구 별칭으로는 동작을 보존할 수 없습니다. 이전 액션을 사용하던 호출자는 새 도구로 명시적으로 마이그레이션해야 합니다.

MCP 서버 도구에서 이전 도구의 블록에 [Removed] 항목을 추가해 어떤 액션이 어느 도구로 이동했는지 기록하고, 새 도구에 대한 일반적인 [Introduced] 항목도 함께 문서화하세요. 예시는 해당 페이지의 list_pipelinesmanage_pipeline 항목을 참조하세요.