
Я построил пользовательский MCP -сервер в Голанге, чтобы сделать Клод умнее - вот как
4 августа 2025 г.Я чувствую, что мы находимся в точке, когда протокол контекста модели (MCP) кажется почти синонимом Genai Engineering. Антропический представил MCP в ноябре 2024 года, революционизируя Genai Engineering. Это привело нас к тому, что, как инженеры, мы можем реализовать различные инструменты на основе нашего варианта использования и направлять LLM по нашему выбору использовать эти инструменты, не выходя из наших любимых IDES или настольных клиентов, таких как Claude. С момента его появления были разработаны многочисленные серверы MCP, разработанные энтузиазмом инженеров. Это было возможно из -за быстрого развития MCP SDK на различных языках, включаяПитонВМашинописьи совсем недавно,ГолангПолем (ура 🚀). Я виделэто официальное предложение о Golang SDK, и я знал, что должен был взять это за вращение!
Что такое MCP, спросите вы?
Давайте начнем с быстрого вступления в MCP, что не хватаетПротокол контекста моделиПолем До недавнего времени AI Engineering требовалась тщательной оценки возможностей различных LLMS, чтобы выбрать правильный. С MCP вы можете выбрать LLM по вашему выбору и расширить его возможности, внедрив пользовательские инструменты и подключившись к внешним источникам данных сами! Основными конструкциями протокола являются:
- MCP клиент
- MCP -сервер
- Возможности (инструменты, ресурсы, подсказки)
- LLM
Сервер MCP имеет определенные возможности, определяемые:
- Инструменты - функции, которые он может выполнить, например, список всех электронных писем от отправителя, поищите пиар на GitHub
- Ресурсы - внешний источник данных, такой как список документов, которые вы хотите, чтобы LLM ссылался на
- Подсказки - набор шаблонов, которые помогают пользователям быстро получить желаемые ответы
Уже есть много серверов MCP, которые вы можете начать использовать для своих приложений. Вы можете ссылаться на эту компиляцию потрясающих серверов MCP:https://github.com/punkpeye/awesome-mcp-servers
BYOM (принесите свой собственный сервер MCP)
В этом посте я хочу рассказать о том, как мы можем разработать наш собственный сервер MCP с помощью Golang. Несколько дней назад, для любого восстановления, и я хотел пройти все репозитории вKubernetes Github OrgПолем
Теперь я мог бы использовать официальный сервер GitHub MCP, но, хотя он предлагает отличный набор инструментов для репозитории и организаций, у него не было прямого инструмента для перечисления всех репозиториев в организации. Вот почему это казалось хорошей возможностью научиться разработать сервер MCP для конкретного инструмента, который мне нужен во время использования Golang.
Разработка сервера MCP в Голанге
Это официальный Golang MCP SDK:https://github.com/modelcontextprotocol/go-sdkПолем Readme и примеры/ папки содержат отличные примеры при разработке сервера MCP и клиента.
Следуя этим примерам, я решил создать сервер MCP следующим образом:
server := mcp.NewServer(&mcp.Implementation{
Name: "demo-github-mcp",
Title: "A demo github mcp server",
Version: "0.0.1",
}, nil)
Следующим шагом было предоставление сервера возможности перечисления всех репозиториев в организации GitHub. Это можно сделать, внедрив инструмент типового Toolhandler, предоставленного SDK
type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)
После этого я создалListRepositories
Инструмент, который принимает следующие аргументы, связанные с GitHub Org в качестве входного:
// User can pass in either the name of the org (example: kubernetes), or its URL (example: https://github.com/kubernetes)
type GithubOrgArgs struct {
Name string
URL string
}
func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) {
Теперь давайте пройдемся по телу Listrepositories пошаговать:
- Проверка ввода, чтобы убедиться, что мы принимаем только действительные аргументы
if params == nil {
return nil, fmt.Errorf("empty params")
}
args := params.Arguments
if args.Name == "" && args.URL == "" {
return nil, fmt.Errorf("empty args")
}
- Формирование URL GitHub API с ввода на основеGitHub API Docs
var apiURL string
var organization string
if args.URL != "" {
// If URL is provided, extract org name and build 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 {
// Use the provided organization name
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
organization = args.Name
}
apiURL = apiURL + "?per_page=100"
- Отправить запрос и получить ответ
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))
}
- Анализировать ответ от GitHub API
type repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
Private bool `json:"private"`
}
// Parse the JSON response
var repositories []repository
if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
- Вернуть ответ как текстовый контекст
var result strings.Builder
result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization))
for _, repo := range repositories {
result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL))
}
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: result.String()},
},
}, nil
После определения инструмента следующим шагом является регистрация его на сервере MCP:
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)
Далее пришло время запустить сервер! Для этого демонстрационного сервера MCP я использую транспорт Stdio, который позволяет серверу общаться через Stdin и Stdout. Это стандартный подход для локальной интеграции MCP с такими клиентами, как Claude Desktop или VSCODE.
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...")
Это то, как выглядит код, со всем собранным:
func main() {
server := mcp.NewServer(&mcp.Implementation{
Name: "demo-github-mcp",
Title: "A demo github mcp server",
Version: "0.0.1",
}, nil)
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)
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...")
}
type GithubOrgArgs struct {
Name string
URL string
}
func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) {
if params == nil {
return nil, fmt.Errorf("empty params")
}
args := params.Arguments
if args.Name == "" && args.URL == "" {
return nil, fmt.Errorf("empty args")
}
var apiURL string
var organization string
if args.URL != "" {
// If URL is provided, extract org name and build 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 {
// Use the provided organization name
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
organization = args.Name
}
apiURL = apiURL + "?per_page=100"
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))
}
type repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
Private bool `json:"private"`
}
// Parse the JSON response
var repositories []repository
if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
var result strings.Builder
result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization))
for _, repo := range repositories {
result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL))
}
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: result.String()},
},
}, nil
}
Теперь последний шаг - составить это и генерировать исполняемый файл с:
go build
Использование сервера MCP с клиентом, таким как Claude
Давайте посмотрим, как добавить этот сервер в рабочий стол Клода.
- Откройте приложение Claude Desktop и перейдите кКлод -> Настройки -> РазработчикПолем
- Здесь вы увидите раздел под названиемМестные серверы MCPПолем Нажмите на кнопку ниже.Редактировать конфигурациюПолем Это откроет файл конфигурации Claude.
- Отредактируйте файл, чтобы добавить сервер MCP следующим образом:
{
"mcpServers": {
"demo-github-mcp": {
"command": "/path/to/executable/generated/from/go build",
"args": []
}
}
}
- Перезапустите приложение Claude (в настоящее время это единственный способ обновить серверы MCP в Claude)
И теперь пришло время проверить это! Это подсказка, которую я дал:
List all repositories in the Kubernetes github org
Клод узнал локально запущенный сервер Demo-GitHub-MCP и спросил, может ли он использовать это!
Как только я его утвердил, Клод перечислил репозитории в организации и даже отобрал используемый инструмент (списки-репозитории) в начале ответа:
И вот и мы! Мы увидели, как мы можем разработать простой сервер MCP с помощью Golang, и использовать его с помощью клиентов MCP, таких как Claude!
«Ага» момент
Когда я разрабатывал этот сервер, я продолжал спрашивать себя: «Если я пишу код для перечисления репозиториев, что будет делать LLM?», А затем он вдруг нажимал - LLM позволяет общаться с этим сервером через естественный язык!
Когда Claude Sonnet 4 прочитайте мою подсказку - «Перечислите все репозитории в Kubernetes Github Org» - и, по своему усмотрению, что локально управляемый MCP -сервер будет работать лучше всего для подсказки, мой разум был взорван ». На этом этапе мне не нужно было предоставлять ему какую -либо информацию о названии инструмента или о принятых параметрах. Я задал свой вопрос с помощью простого предложения, и он выяснил, какой инструмент использовать, как позвонить в инструмент, и выполнил работу!
Соображения производства
Хотя наш сервер MCP отлично подходит для демонстрационных целей, для использования производства отсутствует несколько важных функций:
- Обработка страниц: API GitHub возвращает полученные на страре результаты, по умолчанию 30 записей на страницу. В приведенном выше коде я увеличил это до 100 с
per_page
Параметр запроса. Для производственного кода вы обработаете заголовок ответаLink
Чтобы получить информацию о следующей странице и общее количество страниц. (Смэтот) - Ограничение скорости: Приведенный выше код не учитываетсяОграничения скорости API GitHub
- Аутентификация: Аутифицированные запросы имеют более высокий предел ставки, чем неавентицентричные запросы. Аутентификация также требуется для запросов на частные репозитории GitHub/Enterprise GitHub.
Что дальше?
Этот демонстрационный сервер MCP Github был хорошим началом. Но главная причина, по которой я пишу это, заключается в том, чтобы продемонстрировать, как легко можно настроить ваш любимый инструмент Genai по вашему вкусу! Также удивительно просто начать работу с MCP -сервером в Голанге для ваших собственных проектов! Я не могу дождаться, чтобы увидеть мощные услуги, созданные из сочетания гибкости MCP и высокоэффективного характера Голанга!
Оригинал