0Pricing
AI Engineering Academy · Урок

Создание первого сервера MCP

Используйте Python MCP SDK, чтобы создать сервер, предоставляющий ресурсы, инструменты и запросы, а затем подключите его к Claude Desktop и проверьте работу всего решения от начала до конца.

«Создание первого сервера MCP» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.

Настройка проекта для сервера MCP

Для создания сервера MCP на Python нужны SDK mcp и среда Python. Вы создадите сервер, к которому Claude Desktop или любой клиент MCP сможет подключиться через stdio. Начните с установки пакета и создания файла сервера.

# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server

# Create a virtual environment
# python -m venv venv && source venv/bin/activate

# Install the MCP SDK
# pip install mcp httpx

# Project structure:
# my_mcp_server/
#   server.py          <- Your MCP server
#   requirements.txt
#   README.md

Создание объекта сервера

Импортируйте компоненты из пакета mcp и создайте экземпляр Server с именем Вашего сервера. Это имя отображается у клиентов в списке серверов MCP, поэтому выберите понятное описание. Объект сервера является точкой входа для регистрации всех Ваших инструментов, ресурсов и запросов.

# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx

# Create the server — name is shown in Claude Desktop
app = Server('weather-server')

# --- Tool registrations go here ---

# Entry point
if __name__ == '__main__':
    asyncio.run(stdio_server(app))

Регистрация инструментов с помощью @app.list_tools()

Декоратор @app.list_tools() регистрирует обработчик, который возвращает список доступных инструментов, когда клиент отправляет соответствующий запрос. Каждый инструмент определяется именем, описанием и inputSchema — схемой JSON, описывающей его параметры. Клиент отправляет этот список LLM, чтобы она знала, какие инструменты может вызывать.

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name='get_weather',
            description='Get current weather for a city. Use when the user asks about weather in a specific location.',
            inputSchema={
                'type': 'object',
                'properties': {
                    'city': {
                        'type': 'string',
                        'description': 'City name, e.g. London or New York'
                    },
                    'units': {
                        'type': 'string',
                        'enum': ['metric', 'imperial'],
                        'description': 'Temperature units. Default is metric.'
                    }
                },
                'required': ['city']
            }
        ),
        types.Tool(
            name='list_cities',
            description='Return a list of major cities the user can query weather for.',
            inputSchema={'type': 'object', 'properties': {}, 'required': []}
        )
    ]

Реализация выполнения инструментов

Декоратор @app.call_tool() обрабатывает выполнение инструментов. Когда клиент вызывает инструмент, этот обработчик получает имя инструмента и аргументы. Выполните соответствующую логику и верните список объектов TextContent, содержащих строку с результатом.

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == 'list_cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
        return [types.TextContent(type='text', text=', '.join(cities))]

    if name == 'get_weather':
        city = arguments['city']
        units = arguments.get('units', 'metric')
        unit_symbol = 'C' if units == 'metric' else 'F'

        # Call real weather API (stub here)
        async with httpx.AsyncClient() as client:
            # Replace with actual API call
            result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'

        return [types.TextContent(type='text', text=result)]

    raise ValueError(f'Unknown tool: {name}')

Предоставление ресурсов

Ресурсы — это статические или динамические данные, которые может читать модель; их можно представить как файлы. Определите URI ресурсов и реализуйте средство чтения ресурсов. В клиенте ресурсы отображаются как элементы, на которые может ссылаться ИИ; они полезны для конфигурации, документации или часто используемых снимков данных.

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri='weather://supported-cities',
            name='Supported Cities List',
            description='Complete list of cities available in this weather server.',
            mimeType='text/plain'
        )
    ]

@app.read_resource()
async def read_resource(uri: str) -> str:
    if uri == 'weather://supported-cities':
        cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
        return '\n'.join(cities)
    raise ValueError(f'Unknown resource URI: {uri}')

Регистрация шаблонов подсказок

Подсказки — это повторно используемые шаблоны сообщений, которые клиенты показывают пользователям в виде команд со слешем или быстрых действий. Они принимают параметры и возвращают список сообщений, становящихся исходным контекстом разговора. Подсказки особенно удобны для хранения сложных инструкций, которые пользователь запускает одной командой.

@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
    return [
        types.Prompt(
            name='weather-report',
            description='Generate a formatted weather report for a city.',
            arguments=[
                types.PromptArgument(name='city', description='City name', required=True)
            ]
        )
    ]

@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
    if name == 'weather-report':
        city = arguments.get('city', 'London')
        return types.GetPromptResult(
            description=f'Weather report for {city}',
            messages=[
                types.PromptMessage(
                    role='user',
                    content=types.TextContent(
                        type='text',
                        text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
                    )
                )
            ]
        )
    raise ValueError(f'Unknown prompt: {name}')

Подключение к Claude Desktop

Чтобы использовать сервер MCP с Claude Desktop, добавьте его в файл конфигурации Claude Desktop. В macOS этот файл находится по адресу ~/Library/Application Support/Claude/claude_desktop_config.json. Укажите команду запуска сервера и все необходимые ему переменные окружения.

# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:

# {
#   "mcpServers": {
#     "weather-server": {
#       "command": "/path/to/venv/bin/python",
#       "args": ["/path/to/my_mcp_server/server.py"],
#       "env": {
#         "WEATHER_API_KEY": "your_api_key_here"
#       }
#     }
#   }
# }

# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.

Тестирование сервера с помощью MCP CLI

Перед подключением к Claude Desktop протестируйте сервер с помощью инспектора MCP или инструментов CLI. Команда mcp dev запускает сервер и открывает инспектор в браузере, где можно вручную вызывать инструменты и просматривать необработанные сообщения протокола, что упрощает поиск и исправление проблем.

# Install the MCP development tools
# pip install 'mcp[cli]'

# Start the inspector with your server
# mcp dev server.py

# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchange

Обработка ошибок на серверах MCP

Серверы MCP никогда не должны завершаться сбоем из-за некорректных входных данных. Оберните выполнение всех инструментов в try/except и возвращайте сообщения об ошибках в виде TextContent, а не создавайте исключения. Для критических ошибок (проблем с конфигурацией, отсутствующих ключей API) записывайте их в журнал при запуске и создавайте исключение до перехода сервера в основной цикл.

@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
    try:
        if name == 'get_weather':
            city = arguments.get('city')
            if not city:
                return [types.TextContent(type='text', text='Error: city argument is required.')]
            result = await fetch_weather(city, arguments.get('units', 'metric'))
            return [types.TextContent(type='text', text=result)]
        raise ValueError(f'Unknown tool: {name}')
    except httpx.TimeoutException:
        return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
    except Exception as e:
        return [types.TextContent(type='text', text=f'Error: {str(e)}')]

Ведение журналов на серверах MCP

Поскольку серверы MCP взаимодействуют с клиентом через stdio, операторы печати нарушат работу протокола. Всегда используйте stderr для ведения журналов: он передаёт данные в отдельный поток, не вмешивающийся в обмен сообщениями MCP. Настройте модуль ведения журналов Python на запись в stderr.

import logging
import sys

# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    stream=sys.stderr
)
logger = logging.getLogger('weather-server')

# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')

# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.log

Упаковка сервера MCP

Чтобы поделиться сервером MCP, упакуйте его с помощью pyproject.toml и опубликуйте в PyPI либо распространяйте как контейнер Docker для команд. Используйте переменные окружения для всех секретов — ключей API, URL баз данных, — чтобы конфигурация сервера оставалась отделённой от кода. Чётко задокументируйте обязательные переменные окружения и пример конфигурации в README.

Быстрая проверка

Проверьте, насколько хорошо Вы поняли создание сервера MCP на Python.

Итоги урока

В этом уроке Вы узнали, что: серверы MCP предоставляют инструменты с помощью декораторов @app.list_tools() и @app.call_tool(), ресурсы и подсказки расширяют сервер, добавляя доступные для чтения данные и повторно используемые шаблоны, а для ведения журналов необходимо использовать stderr, чтобы не повреждать канал протокола MCP через stdio. Далее Вы подключите сервер MCP к базе данных и предоставите динамические ресурсы с постраничной выдачей.

Часто задаваемые вопросы

Урок «Создание первого сервера MCP» бесплатный?

Да — полный текст урока «Создание первого сервера MCP» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.

Чему я научусь в уроке «Создание первого сервера MCP»?

Используйте Python MCP SDK, чтобы создать сервер, предоставляющий ресурсы, инструменты и запросы, а затем подключите его к Claude Desktop и проверьте работу всего решения от начала до конца. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Engineering Academy?

Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Создание первого сервера MCP»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Engineering Academy?

Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что такое MCP и почему это важно
  2. Создание первого сервера MCP
  3. Предоставление ресурсов базы данных через MCP
  4. Безопасность и аутентификация MCP
← Назад к AI Engineering Academy