Golang으로 커스텀 MCP 서버 구축하기: Claude를 더 스마트하게 만드는 방법
개요
이 문서는 Golang을 사용하여 Model Context Protocol(MCP) 서버를 구축하는 실제 경험과 방법을 다룹니다. 작성자 Rajashree M이 2025년 8월 4일에 발표한 실무 가이드입니다.
핵심 내용
MCP란 무엇인가?
**Model Context Protocol(MCP)**는 Anthropic에서 2024년 11월에 도입한 혁신적인 기술로, GenAI 엔지니어링의 새로운 패러다임을 제시합니다.
MCP의 주요 구성 요소:
- MCP 클라이언트: 사용자 인터페이스 역할
- MCP 서버: 실제 기능을 제공하는 백엔드
- 기능(Capabilities): 도구, 리소스, 프롬프트
- LLM: 언어 모델
MCP 서버의 핵심 기능:
- 도구(Tools): 실행 가능한 함수들 (예: 특정 발신자의 모든 이메일 나열, GitHub PR 조회)
- 리소스(Resources): 외부 데이터 소스 (예: LLM이 참조할 문서 목록)
- 프롬프트(Prompts): 사용자가 원하는 답변을 빠르게 얻을 수 있는 템플릿 세트
프로젝트 배경 및 목표
작성자는 Kubernetes GitHub 조직의 모든 저장소를 살펴보고 싶었지만, 기존 GitHub MCP 서버에는 조직의 모든 저장소를 직접 나열하는 도구가 없었습니다. 이것이 Golang을 사용하여 특정 도구를 위한 MCP 서버를 개발하는 좋은 기회가 되었습니다.
단계별 구현 과정
1단계: 기본 MCP 서버 설정
server := mcp.NewServer(&mcp.Implementation{
Name: "demo-github-mcp",
Title: "A demo github mcp server",
Version: "0.0.1",
}, nil)
2단계: 도구 핸들러 구현
도구 핸들러 타입 정의:
type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)
입력 인수 구조체:
type GithubOrgArgs struct {
Name string // 조직 이름 (예: kubernetes)
URL string // 조직 URL (예: https://github.com/kubernetes)
}
3단계: ListRepositories 함수 상세 구현
입력 검증
if params == nil {
return nil, fmt.Errorf("empty params")
}
args := params.Arguments
if args.Name == "" && args.URL == "" {
return nil, fmt.Errorf("empty args")
}
GitHub API URL 생성
var apiURL string
var organization string
if args.URL != "" {
// URL이 제공된 경우 조직 이름 추출 및 API URL 구축
url := strings.TrimPrefix(args.URL, "https://")
url = strings.TrimPrefix(url, "http://")
url = strings.TrimPrefix(url, "github.com/")
url = strings.TrimSuffix(url, "/")
orgName := strings.Split(url, "/")[0]
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName)
organization = orgName
} else {
// 제공된 조직 이름 사용
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
organization = args.Name
}
apiURL = apiURL + "?per_page=100"
HTTP 요청 전송 및 응답 처리
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body))
}
JSON 응답 파싱
type repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
Private bool `json:"private"`
}
var repositories []repository
if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
4단계: 도구 등록
mcp.AddTool(server, &mcp.Tool{
Name: "list-repositories",
Description: "A tool to list all repositories in a Github org",
InputSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"name": {
Type: "string",
Description: "GitHub organization name (e.g., kubernetes)",
},
"url": {
Type: "string",
Description: "GitHub organization URL (e.g., https://github.com/kubernetes)",
},
},
},
}, ListRepositories)
5단계: 서버 시작
STDIO 전송 사용 (Claude Desktop이나 VSCode와 같은 로컬 MCP 통합의 표준 접근 방식):
t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr)
log.Println("🚀 MCP server starting up...")
if err := server.Run(context.Background(), t); err != nil {
log.Printf("Server failed: %v", err)
}
log.Println("🚀 MCP server shutting down...")
6단계: 빌드 및 실행
go build
Claude와의 통합 설정
Claude Desktop 설정 과정
- Claude 데스크톱 앱 열기
- Claude → Settings → Developer로 이동
- Local MCP Servers 섹션에서 Edit Config 클릭
- 설정 파일 편집:
{
"mcpServers": {
"demo-github-mcp": {
"command": "/path/to/executable/generated/from/go build",
"args": []
}
}
}
- Claude 앱 재시작 (현재 MCP 서버를 새로고침하는 유일한 방법)
실제 테스트 결과
사용자 프롬프트: “List all repositories in the Kubernetes github org”
결과:
- Claude가 로컬에서 실행 중인 demo-github-mcp 서버를 인식
- 사용자에게 서버 사용 승인 요청
- 승인 후 조직의 저장소 목록 표시
- 응답 시작 부분에 사용된 도구(list-repositories) 표시
핵심 깨달음 - “AHA” 순간
중요한 통찰: LLM이 자연어를 통해 이 서버와의 통신을 가능하게 한다는 점입니다.
Claude Sonnet 4가 "Kubernetes GitHub org의 모든 저장소 나열"이라는 프롬프트를 읽고, 로컬에서 실행 중인 MCP 서버가 해당 프롬프트에 가장 적합하다는 것을 스스로 인식했을 때의 놀라움이 컸습니다.
핵심 포인트:
- 도구 이름이나 허용 매개변수에 대한 정보를 제공할 필요가 없었음
- 간단한 문장으로 질문했을 뿐인데, Claude가 어떤 도구를 사용할지, 도구를 어떻게 호출할지 스스로 파악
- 작업을 완료함
실용적인 팁 및 주의사항
프로덕션 환경 고려사항
현재 구현의 한계점과 개선 필요 사항:
-
페이지네이션 처리
- GitHub API는 페이지네이션된 결과를 반환 (기본값: 페이지당 30개 항목)
- 현재 코드에서는 per_page 쿼리 매개변수로 100개로 증가시킴
- 프로덕션에서는 응답 헤더 Link를 처리하여 다음 페이지와 총 페이지 수 정보 획득 필요
-
속도 제한 관리
- 현재 코드는 GitHub API 속도 제한을 고려하지 않음
- 프로덕션 환경에서는 적절한 속도 제한 처리 필요
-
인증 처리
- 인증된 요청은 비인증 요청보다 높은 속도 제한을 가짐
- 비공개 GitHub 저장소/기업용 GitHub에 대한 요청에는 인증이 필요
주요 성공 요소
- 간단성: Golang에서 MCP 서버 시작이 놀랍도록 간단함
- 유연성: 개인의 필요에 맞게 GenAI 도구를 사용자 정의하는 것이 얼마나 쉬운지 보여줌
- 성능: MCP의 유연성과 Golang의 고성능적 특성의 조합으로 강력한 서비스 생성 가능
참고 자료
학습 리소스
- 공식 Golang MCP SDK: GitHub - modelcontextprotocol/go-sdk: The official Go SDK for Model Context Protocol servers and clients. Maintained in collaboration with Google.
- MCP 서버 모음: GitHub - punkpeye/awesome-mcp-servers: A collection of MCP servers.
- GitHub API 페이지네이션 가이드: GitHub API 문서 참조
기술 스택
- 언어: Golang
- 프로토콜: Model Context Protocol (MCP)
- API: GitHub REST API v3
- 클라이언트: Claude Desktop
- 전송 방식: STDIO Transport
결론
이 프로젝트는 MCP의 유연성과 Golang의 고성능적 특성을 결합하여 강력한 서비스를 만들 수 있음을 보여줍니다. 데모용 GitHub MCP 서버는 좋은 시작점이었으며, 개인 프로젝트에서 Golang으로 MCP 서버를 시작하는 것이 놀랍도록 간단하다는 것을 증명했습니다.
미래 전망: MCP의 유연성과 Golang의 고성능 특성의 조합으로 생성될 강력한 서비스들에 대한 기대감이 높습니다.