Свой MCP-сервер за двадцать минут - как подключить Claude и Cursor к базе данных и логам

Два года подключение языковой модели к корпоративным базам данных выглядело одинаково: разработчик писал кастомный REST-бэкенд, вручную описывал схемы Function Calling для OpenAI, пробрасывал авторизацию и воевал с переполнением контекста. Каждый инструмент требовал отдельного велосипеда.

Свой MCP-сервер за двадцать минут - как подключить Claude и Cursor к базе данных и логам

Открытый протокол Model Context Protocol (MCP), который запустила компания Anthropic, решил эту проблему так же, как в свое время протокол Language Server Protocol (LSP) решил проблему поддержки языков программирования в редакторах кода.

Один раз написанный MCP-сервер сегодня нативно подключается к Claude Desktop, Cursor, Windsurf, Zed и локальным моделям.

Мы собрали рабочий сервер для PostgreSQL и системных логов. Разбираем архитектуру протокола и пишем готовый сервис на Python с нуля.

Архитектура протокола: три примитива и транспорт

Протокол работает по клиент-серверной модели поверх легковесного формата JSON-RPC 2.0.

Приложение, в котором сидит пользователь (Cursor или десктопный клиент Claude), выступает в роли хоста. Хост запускает локальный процесс сервера и общается с ним через стандартные потоки ввода-вывода (stdio) или через Server-Sent Events (SSE) по протоколу HTTP.

Внутри протокола есть три фундаментальных примитива:

  1. Инструменты (Tools): Исполняемые функции, которые модель может вызывать для совершения действий. Например: выполнить запрос к базе данных, перезагрузить контейнер или отправить сообщение в чат. Схема аргументов передается в формате JSON Schema.
  2. Ресурсы (Resources): Пассивные данные, которые клиент может читать как файлы или URI-ссылки. Например: содержимое схемы базы данных, текущий лог-файл или выгрузка метрик. Ресурсы не меняют состояние системы.
  3. Шаблоны запросов (Prompts): Заранее заготовленные промпты с параметрами, которые пользователь может быстро вызвать через слэш-команды в интерфейсе.

Модели не отдают прямой доступ к вашей инфраструктуре. Сервер сам решает, какие функции показать модели и с какими правами их выполнить.

Пишем сервер для базы данных на Python

Кстати, если вы хотите протестировать все самые свежие модели уровня Claude Sonnet 5, GPT-5.6 или Gemini 3 в одном удобном месте и сравнить их ответы без костылей с иностранными картами - на платформе SYNTX.AI это можно сделать за пару кликов.

По промокоду NEIROSKUF дадут 15% скидку на все тарифы.

Для быстрой разработки используем официальный фреймворк FastMCP из пакета mcp. Он превращает стандартные функции Python с типизацией и строками документации в готовые инструменты протокола.

Ставим менеджер пакетов uv (он избавит от возни с виртуальными окружениями):

# Устанавливаем uv, если еще не стоит curl -LsSf https://astral.sh/uv/install.sh | sh

Создаем файл сервера server.py:

import os import re import psycopg2 from psycopg2.extras import RealDictCursor from mcp.server.fastmcp import FastMCP # Инициализируем сервер с понятным именем mcp = FastMCP("Production-Postgres-Explorer") DATABASE_URL = os.getenv( "DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/my_database" ) def get_connection(): conn = psycopg2.connect(DATABASE_URL) # Принудительно включаем режим только для чтения на уровне сессии conn.set_session(readonly=True, autocommit=True) return conn @mcp.resource("db://schema") def get_database_schema() -> str: """Возвращает список всех таблиц и их колонок в текущей базе данных.""" query = """ SELECT table_name, column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position; """ with get_connection() as conn: with conn.cursor(cursor_factory=RealDictCursor) as cur: cur.execute(query) rows = cur.fetchall() schema_text = "Схема базы данных:\n" current_table = "" for row in rows: if row["table_name"] != current_table: current_table = row["table_name"] schema_text += f"\nТаблица: {current_table}\n" schema_text += f" - {row['column_name']} ({row['data_type']})\n" return schema_text @mcp.tool() def execute_sql(query: str) -> str: """Выполняет безопасный SQL-запрос только на чтение данных (SELECT). Аргументы: query: SQL-запрос на диалекте PostgreSQL. """ # Дополнительная проверка на опасные ключевые слова dangerous_keywords = r"\b(INSERT|UPDATE|DELETE|DROP|ALTER|TRUNCATE|GRANT|REVOKE)\b" if re.search(dangerous_keywords, query, re.IGNORECASE): return "Ошибка: Разрешены только операции чтения (SELECT)." # Жесткое ограничение на количество строк, чтобы не забить контекст модели if not re.search(r"\bLIMIT\b", query, re.IGNORECASE): query = query.rstrip("; ") + " LIMIT 50;" try: with get_connection() as conn: with conn.cursor(cursor_factory=RealDictCursor) as cur: cur.execute(query) results = cur.fetchall() if not results: return "Запрос выполнен успешно. Данные не найдены." # Форматируем вывод в компактный текстовый вид header = " | ".join(results[0].keys()) divider = "-" * len(header) lines = [header, divider] for r in results: lines.append(" | ".join(str(v) for v in r.values())) return "\n".join(lines) except Exception as err: return f"Ошибка выполнения SQL: {str(err)}" if __name__ == "__main__": # Запускаем сервер через стандартный поток ввода-вывода mcp.run(transport="stdio")

Код решает сразу три инженерные проблемы:

  1. Безопасность данных: Сессия PostgreSQL принудительно открывается в режиме readonly=True. Даже если модель составит запрос на удаление, СУБД отвергнет транзакцию на физическом уровне.
  2. Контроль контекста: Сервер автоматически дописывает LIMIT 50, если модель забыла ограничить выборку. Это защищает окно внимания от выгрузки миллиона строк.
  3. Автоматическая схема: Докстринги и аннотации типов Python парсятся библиотекой FastMCP в JSON Schema для модели без ручного описания структуры.

Подключаем сервер к Cursor и Claude Desktop

Чтобы интерфейс увидел новый сервер, его нужно прописать в конфигурационный файл.

Вариант 1. Подключение в Cursor

В корне рабочего проекта создаем папку .cursor и файл .cursor/mcp.json:

{ "mcpServers": { "postgres-explorer": { "command": "uv", "args": [ "run", "--with", "mcp[cli]", "--with", "psycopg2-binary", "/абсолютный/путь/к/server.py" ], "env": { "DATABASE_URL": "postgresql://postgres:password@127.0.0.1:5432/production_db" } } } }

Вариант 2. Подключение в Claude Desktop

Открываем конфигурационный файл приложения:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Вставляем ту же секцию mcpServers:

{ "mcpServers": { "postgres-explorer": { "command": "uv", "args": [ "run", "--with", "mcp[cli]", "--with", "psycopg2-binary", "C:\\scripts\\server.py" ], "env": { "DATABASE_URL": "postgresql://postgres:password@localhost:5432/analytics" } } } }

Перезапускаем редактор или приложение. В панели инструментов появится значок подключенного сервера и список доступных функций.

Как это работает в реальном диалоге

После подключения модель начинает использовать сервер как внешний орган чувств.

Пользователь пишет в чат:

"Посмотри в базе, почему у пользователя с почтой ivan@example.com завис последний платеж, и какие три заказа он оформлял до этого."

Что делает модель под капотом:

1. Запрашивает ресурс db://schema, чтобы понять, в каких таблицах лежат пользователи, транзакции и заказы.

2. Вызывает инструмент execute_sql с параметром:

SELECT id, status FROM users WHERE email = 'ivan@example.com';

3. Получив user_id, выполняет второй запрос

SELECT id, amount, status, error_code FROM payments WHERE user_id = 42 ORDER BY created_at DESC LIMIT 1;

4. Делает третий запрос по связанной таблице заказов.

5. Формирует связный человеческий ответ с точной выжимкой из базы.

Никакого копирования структуры базы руками. Никаких догадок. Модель выполняет работу аналитика базы данных за три секунды.

Типичные грабли при развертывании

Мы собрали три ошибки, с которыми сталкиваются при первой настройке:

  • Использование относительных путей в конфиге: Хост запускает сервер из своего системного каталога. Путь к скрипту server.py обязан быть абсолютным.
  • Вывод лишнего текста в stdout: Протокол stdio использует стандартный поток вывода исключительно для пакетов JSON-RPC. Если вставить в код обычный print("Сервер запущен"), клиент не сможет распарсить ответ и соединение упадет с ошибкой протокола. Все логирование нужно направлять строго в sys.stderr.
  • Перегрузка ресурсами: Не нужно передавать в модель терабайтные логи целиком. Ресурсы должны отдавать выжимки, схемы и метаданные, а детальный поиск должен происходить через вызов инструментов с фильтрами.

Так что, разработка интеграций для языковых моделей перестала быть написанием сотен строк костыльного кода.

Один компактный скрипт на стандартном протоколе подключает любую модель к вашей инфраструктуре за двадцать минут.

Свой MCP-сервер за двадцать минут - как подключить Claude и Cursor к базе данных и логам
7
3
1