Научитесь создавать MCP сервер на Python с нуля: инструменты, ресурсы и промпты. Подключите к Claude Desktop или Claude Code. Рабочий пример с GitHub API.
Model Context Protocol (MCP) — это открытый протокол, который позволяет AI-ассистентам (Claude, Cursor и другим) вызывать внешние сервисы стандартизированным образом. За два года MCP превратился из нишевого проекта Anthropic в индустриальный стандарт: 97 миллионов загрузок SDK ежемесячно, поддержка ведущих AI-инструментов и место под управлением Linux Foundation. Если вы пишете AI-агентов или интегрируете LLM в свои проекты, MCP — это способ дать им доступ к реальным данным и действиям.
В этом руководстве мы с нуля напишем MCP сервер на Python, который работает с GitHub API. Вы узнаете, как определять инструменты, ресурсы и промпты, тестировать сервер с помощью MCP Inspector и подключать его к Claude Desktop или Claude Code. Код можно адаптировать под любой внешний API.
uv (рекомендуется) или pipMCP использует протокол JSON-RPC для обмена сообщениями между AI-клиентом и сервером. Сервер предоставляет три основных типа сущностей:
В качестве транспорта используем stdio — сервер запускается как подпроцесс и общается через стандартный ввод/вывод. Это работает с Claude Desktop и Claude Code без дополнительной настройки.
Создайте новую директорию и установите MCP SDK с дополнительным пакетом [cli], который включает dev-сервер и инспектор:
mkdir github-mcp-server
cd github-mcp-server
uv init .
uv add "mcp[cli]" httpxЕсли вы используете pip:
pip install "mcp[cli]" httpxПроект состоит из трёх файлов: server.py, pyproject.toml (если используете uv) и опционального .env для GitHub токена.
Начнём с простейшего рабочего примера, чтобы убедиться, что SDK настроен корректно:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-mcp")
@mcp.tool()
def greet(name: str) -> str:
"""Возвращает персонализированное приветствие. Используй, когда нужно поприветствовать кого-то."""
return f"Привет, {name}! Твой MCP сервер работает."
if __name__ == "__main__":
mcp.run(transport="stdio")Запустите uv run mcp dev server.py и откройте http://localhost:5173. Перейдите на вкладку Tools, вызовите greet с любым именем и проверьте ответ. Обратите внимание: FastMCP автоматически генерирует JSON-RPC обвязку, декоратор @mcp.tool() создаёт схему из аннотаций типов, а docstring используется AI для принятия решения о вызове инструмента — пишите её чётко.
Теперь заменим минимальный пример на полноценный сервер с двумя инструментами: получение метаданных репозитория и список открытых issues. Используем httpx для асинхронных запросов к GitHub API.
import os, logging, sys, httpx
from pydantic import BaseModel
from mcp.server.fastmcp import FastMCP
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logger = logging.getLogger(__name__)
mcp = FastMCP("github-tools", instructions=(
"Этот сервер предоставляет инструменты для работы с GitHub API. "
"Используй get_repo_info для получения метаданных репозитория. "
"Используй list_open_issues для получения открытых issues."
))
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
def _github_headers() -> dict[str, str]:
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
if GITHUB_TOKEN:
headers["Authorization"] = f"Bearer {GITHUB_TOKEN}"
return headers
class RepoInfo(BaseModel):
full_name: str
description: str | None
stars: int
forks: int
open_issues: int
language: str | None
url: str
@mcp.tool()
async def get_repo_info(owner: str, repo: str) -> RepoInfo:
"""
Получить метаданные репозитория GitHub: звёзды, форки, количество открытых issues.
Args:
owner: Имя пользователя или организации GitHub (например 'anthropics')
repo: Название репозитория (например 'claude-code')
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.github.com/repos/{owner}/{repo}",
headers=_github_headers(),
timeout=10.0,
)
response.raise_for_status()
data = response.json()
return RepoInfo(
full_name=data["full_name"],
description=data.get("description"),
stars=data["stargazers_count"],
forks=data["forks_count"],
open_issues=data["open_issues_count"],
language=data.get("language"),
url=data["html_url"],
)
@mcp.tool()
async def list_open_issues(owner: str, repo: str, limit: int = 10) -> str:
"""
Вывести открытые issues репозитория, отсортированные по дате обновления.
Args:
owner: Имя пользователя или организации GitHub
repo: Название репозитория
limit: Максимум issues (1-30, по умолчанию 10)
"""
limit = max(1, min(limit, 30))
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.github.com/repos/{owner}/{repo}/issues",
headers=_github_headers(),
params={"state": "open", "per_page": limit, "sort": "updated"},
timeout=10.0,
)
response.raise_for_status()
issues = response.json()
if not issues:
return f"Открытых issues в {owner}/{repo} не найдено."
lines = [f"Открытые issues в **{owner}/{repo}** (показано {len(issues)}):\n"]
for issue in issues:
lines.append(f"- #{issue['number']}: {issue['title']}")
return "\n".join(lines)
if __name__ == "__main__":
mcp.run(transport="stdio")Важные решения: возврат Pydantic модели даёт AI типизированный структурированный ответ, что надёжнее парсинга строк. Для строковых инструментов лучше перехватывать исключения и возвращать сообщение об ошибке — необработанное исключение может убить соединение. Всегда ограничивайте числовые параметры, такие как limit — AI может передать 0, 100 или строку.
Ресурсы позволяют AI читать данные пассивно. Вот ресурс, сообщающий, настроен ли GitHub токен, и динамический ресурс для получения README репозитория:
@mcp.resource("config://github-tools/status")
def server_status() -> str:
"""Сообщить, настроен ли GitHub токен."""
auth_status = "аутентифицирован" if GITHUB_TOKEN else "не аутентифицирован (ограничение 60 запросов/час)"
return f"GitHub Tools MCP Сервер\nСтатус: {auth_status}"
@mcp.resource("github://repos/{owner}/{repo}/readme")
async def get_readme(owner: str, repo: str) -> str:
"""Получить содержимое README репозитория."""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.github.com/repos/{owner}/{repo}/readme",
headers={**_github_headers(), "Accept": "application/vnd.github.raw+json"},
timeout=10.0,
)
if response.status_code == 404:
return "README для этого репозитория не найден."
response.raise_for_status()
return response.textПромпты — это шаблоны инструкций, которые любой MCP клиент может вызвать по имени. Этот промпт структурирует запрос на ревью кода, используя наши инструменты:
@mcp.prompt()
def review_pull_request(owner: str, repo: str, pr_number: int) -> str:
"""
Шаблон промпта для ревью пул-реквеста на GitHub.
"""
return (
f"Пожалуйста, проверьте пул-реквест #{pr_number} в {owner}/{repo}.\n"
f"Начните с получения информации о репозитории через get_repo_info,\n"
f"затем выведите открытые issues, чтобы понять контекст проекта.\n"
f"Сосредоточьтесь на корректности, производительности и соблюдении паттернов проекта."
)Быстрейший способ проверить сервер — встроенный инспектор, не требующий Claude. Запустите uv run mcp dev server.py и откройте http://localhost:5173.
Установите GITHUB_TOKEN в разделе Environment Variables перед подключением. Затем тестируйте инструменты на вкладке Tools, проверяйте ресурсы на вкладке Resources и промпты на вкладке Prompts. Если что-то не работает, панель Logs показывает сырой JSON-RPC обмен — это самый прямой способ найти проблему.
Откройте файл конфигурации Claude Desktop: на macOS это ~/Library/Application Support/Claude/claude_desktop_config.json, на Windows — %APPDATA%\Claude\claude_desktop_config.json. Добавьте ваш сервер в раздел mcpServers:
{
"mcpServers": {
"github-tools": {
"command": "uv",
"args": ["run", "--with", "mcp[cli]", "--with", "httpx", "python", "/абсолютный/путь/к/github-mcp-server/server.py"],
"env": {
"PYTHONUNBUFFERED": "1",
"GITHUB_TOKEN": "ваш_github_токен"
}
}
}
}Используйте uv run, а не голый python — Claude Desktop запускает своё окружение без вашего PATH, поэтому голый python часто не работает. Полностью выйдите и перезапустите Claude Desktop после сохранения. Иконка плагина в поле ввода подтверждает подключение сервера.
Для Claude Code используйте CLI команду claude mcp add. Разделитель -- обязателен для отделения имени сервера от команды запуска:
claude mcp add github-tools \
-e GITHUB_TOKEN=ваш_токен \
-e PYTHONUNBUFFERED=1 \
-- uv run --with "mcp[cli]" --with httpx python /абсолютный/путь/к/server.pyЧтобы поделиться конфигурацией сервера с командой, используйте --scope project. Это создаст файл .mcp.json в корне репозитория и предложит участникам активировать его при открытии проекта. Выполните claude mcp list для проверки регистрации.
Теперь у вас есть работающий MCP сервер на Python, который можно адаптировать под любые задачи. Делитесь опытом в комментариях!
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →