Skip to main content
Glama
Khushboo-Mishra

SQL-MCP-101

SQL-MCP-101

Новичок в MCP? Начните с интерактивного учебника — пошагового обзора инструментов, ресурсов и промптов, а также того, как решить, какой из них использовать для функции.

Небольшой, обильно комментированный MCP-сервер для MySQL, который демонстрирует все три примитива Model Context Protocol (tools, resources и prompts) примерно в 1100 строках Python, плюс браузерный интерфейс для его изучения.

Этот репозиторий существует для того, чтобы его читали, а не только запускали. Если вы встречали упоминания MCP и хотите понять, что на самом деле нужно для создания сервера, — это полный работающий пример, достаточно маленький, чтобы прочитать его за один присест: по одному примитиву на файл, комментарии, которые объясняют почему, а не что, и демонстрационная база с намеренными недостатками, чтобы примеры находили реальные проблемы, а не игрушечные.

mcp_server/
├── database.py    read-only introspection; the only file not about MCP
├── execution.py   running queries and writes, plus every safety control
├── tools.py       6 TOOLS      inspect structure, cannot read or change a row
├── data_tools.py  6 TOOLS      read rows, and insert / update / delete / alter
├── resources.py   4 RESOURCES  content the APPLICATION attaches (+2 templates)
├── prompts.py     6 PROMPTS    workflows the USER invokes
└── server.py      wires them together, about 10 meaningful lines

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


Главная мысль, которую стоит вынести

Большинство учебников по MCP рассказывают только об инструментах, из-за чего складывается впечатление, что MCP — это и есть инструменты. На самом деле примитивов три, и различаются они тем, кто управляет:

Примитив

Кто решает

Когда происходит

Аналогия

Инструмент

модель

в ходе диалога, автономно

функция, которую модель может вызвать

Ресурс

приложение

заранее, выбирает человек

файл, который вы прикрепляете

Промпт

пользователь

явно, из меню

сохранённый вопрос эксперта

Одни и те же данные могут выступать сразу в нескольких ролях. В этом репозитории get_table_ddl — это инструмент, и schema://table/{name}/ddl — это ресурс. Одни и те же байты, доступные двумя разными способами, потому что «модель получает их, когда сама решает, что они нужны» и «человек прикрепляет их до начала работы» — это действительно разные потребности.


Related MCP server: mysql-mcp-server

Быстрый старт

git clone https://github.com/Khushboo-Mishra/SQL-MCP-101.git
cd SQL-MCP-101
bash scripts/setup.sh

setup.sh проверяет предусловия, создаёт виртуальное окружение, устанавливает две зависимости, создаёт демонстрационную базу данных и проверяет сервер по полному циклу. При первом же отсутствующем компоненте он останавливается с конкретным сообщением.

Затем посмотрите все три примитива за один раз:

bash scripts/run_explorer.sh

Требования

  • Python 3.10+

  • MySQL 8.x, работающая локально (brew services start mysql)

  • Node.js: необязательно, только для MCP Inspector

  • Ollama: необязательно, только для панели Chat в интерфейсе

По умолчанию используется root на 127.0.0.1:3306 без пароля — это стандарт Homebrew, так что большинству менять ничего не нужно. В противном случае задайте переменные окружения MYSQL_USER, MYSQL_PASSWORD, MYSQL_HOST, MYSQL_PORT.


Что в итоге создано

12 инструментов, 4 ресурса + 2 URI-шаблона и 6 промптов поверх демонстрационной базы из шести таблиц.

Инструменты: их вызывает модель

Разнесены по двум файлам по радиусу поражения, а не по подсистемам. Это осознанное архитектурное решение, которое стоит повторить: оно сохраняет рискованную поверхность небольшой и очевидной для любого, кто проверяет сервер или пишет для его базы GRANT.

tools.py: изучение структуры. Не может прочитать ни одной строки и ничего изменить.

Инструмент

Назначение

list_tables

все таблицы и представления с оценкой числа строк

describe_table(table)

колонки, типы, ключи, индексы, внешние ключи

get_table_ddl(table)

точный CREATE TABLE

list_relationships

все объявленные внешние ключи

find_sensitive_columns

колонки, чьё название намекает на PII или секреты

search_columns(keyword)

найти колонку, если забыли, в какой она таблице

data_tools.py: чтение строк и изменение данных. Это половина, у которой есть последствия.

Инструмент

Назначение

run_query(sql, limit)

выполняет SELECT и возвращает строки; именно это отвечает на вопросы о данных

execute_statement(sql)

INSERT / UPDATE / DELETE / CREATE / ALTER / DROP / TRUNCATE

insert_row(table, values)

структурированная вставка; значения передаются как связанные параметры

update_rows(table, changes, where)

структурированное обновление; where обязательно

delete_rows(table, where)

структурированное удаление; where обязательно

show_audit_log(limit)

каждый оператор, который выполнил сервер

Зачем и общий execute_statement, и структурированные обёртки? Структурированные инструменты безопаснее: аргументы типизированы, значения связаны, поэтому модель никогда не пишет текст SQL и не может создать что-то некорректное. Но они делают только то, что вы предусмотрели. Общий вход в SQL покрывает длинный хвост: оконные функции, непредвиденный ALTER. Именно поэтому большинство реальных серверов в итоге поставляют оба варианта.

Ресурсы: их прикрепляет приложение

URI

Тип

Содержимое

schema://tables

JSON

опись таблиц

schema://ddl

SQL

DDL всей схемы

schema://relationships

JSON

все внешние ключи

schema://overview

Markdown

человекочитаемая сводка

schema://table/{name}

JSON

одна таблица (шаблонный ресурс)

schema://table/{name}/ddl

SQL

DDL одной таблицы (шаблонный ресурс)

Статический ресурс имеет фиксированный URI и присутствует в resources/list, поэтому клиент может показать его в селекторе. Шаблонный ресурс, наоборот, содержит {placeholders} и присутствует в resources/templates/list. Готового списка для показа нет, так что клиент заполняет пропуск.

Промпты: их вызывает пользователь

Промпт

Аргументы

Что делает

audit_schema

нет

пятишаговая проверка здоровья: ключи, связи, PII, именование

explain_table

table

объясняет одну таблицу простым языком

ask_data

question

пишет запрос, выполняет его и отвечает простым языком

modify_data

request

предпросмотр → подтверждение → применение → проверка для изменений

document_schema

нет

генерирует справочную документацию

onboarding_tour

role

направленный первый взгляд, адаптированный под роль


Выбор: инструмент, ресурс или промпт?

Вопрос, на котором люди застревают. Проходите по нему в таком порядке.

1. Совершает ли оно действие или получает то, что выбирает модель? → Инструмент. Всё, что модель должна иметь возможность решить сделать самостоятельно.

2. Это документ, который человек разумно прикрепил бы перед началом работы? → Ресурс. Справочный материал, контекст всей схемы, всё стабильное.

3. Это повторяемая задача, где способ вопроса и есть экспертиза? → Промпт. Поставляйте хороший вопрос, а не рассчитывайте, что его переоткроют.

Две эвристики, которые снимают большинство оставшихся сомнений:

Кто инициирует? Модель → инструмент. Приложение → ресурс. Пользователь → промпт.

Хотели бы вы видеть это в меню? Если да — это промпт. Меню созданы для людей, и только промпты показываются людям как команды.

Разобранные примеры из этого репозитория

Возможность

Выбор

Почему

Получить структуру одной таблицы

инструмент

модель нуждается в ней в процессе рассуждения, непредсказуемо

DDL всей схемы

оба

инструмент для модели; ресурс для человека, чтобы прикрепить заранее

Аудит схемы

промпт

повторяемая задача, где ценность — в знании что спросить

Поиск колонки

инструмент

принимает аргумент, который модель выбирает в момент вызова

Обзор в Markdown

ресурс

пассивный справочник, не требует решения

Где люди ошибаются

  • Всё делать инструментами. Работает, но модель тратит вызовы на получение контекста, который человек мог прикрепить один раз, и у пользователей не появляется обнаруживаемых точек входа.

  • Ресурсы для того, что требует аргументов, которые выбирает модель. Если параметр выбирает модель — это инструмент.

  • Промпты, которые выполняют работу. Промпт возвращает текст. Если вы ловите себя на том, что внутри промпта обращаетесь к базе, вам нужен инструмент.


Демонстрационная база данных

mcp_demo — шесть таблиц, намеренно неидеальных, чтобы примеры находили реальные проблемы:

Таблица

Намеренный недостаток

CUSTOMERS

EMAIL, PHONE — срабатывает сканер чувствительных колонок

PRODUCTS

SKU — UNIQUE, но не первичный ключ; естественный ключ, который стоит обсудить

ORDERS

(чистая, эталонный пример)

ORDER_ITEMS

PRODUCT_ID похож на внешний ключ, но не имеет ограничения

AUDIT_LOG

вообще нет первичного ключа

legacy_notes

snake_case, тогда как всё остальное — UPPER_CASE

Запустите audit_schema для неё — и каждый из этих недостатков должен всплыть. В этом суть демо: инструменты находят настоящие проблемы, а не игрушечные.


Запуск

Обозреватель: все примитивы за один раз

bash scripts/run_explorer.sh

Выводит рукопожатие initialize, затем перечисляет и задействует инструменты, ресурсы (статические и шаблонные) и промпты. Запустите его первым: он подтверждает, что установка работает, и показывает всю поверхность протокола на одном экране.

Веб-интерфейс: все три примитива в браузере

bash scripts/run_ui.sh          # http://127.0.0.1:8000
PORT=9000 bash scripts/run_ui.sh

Четыре панели, по одной на каждое, что стоит показать:

Панель

Что она демонстрирует

Chat

вопросы на простом английском; каждый выбранный моделью инструмент указан непосредственно над ответом

Tools

все 12, сгруппированы по радиусу поражения, каждый можно вызвать из формы

Resources

статические и шаблонные, читаются прямо на месте

Prompts

разверните, чтобы увидеть текст, или отправьте сразу в чат

Живая полоса Activity внизу показывает реальный JSON-RPC под капотом: tools/call, resources/read, prompts/get, так что протокол виден всё время.

Страница — сама MCP-клиент: у неё нет собственного доступа к MySQL. Всё, что на экране, пришло через тот же протокол, который использует Claude Desktop.

Для Chat нужна локальная LLM через Ollama — бесплатно, без API-ключа, и ничего не покидает машину:

brew install ollama && ollama serve
ollama pull qwen2.5:7b

Задайте вместо этого ANTHROPIC_API_KEY — и интерфейс автоматически переключится на Claude API. Панели Tools, Resources и Prompts работают вообще без LLM.

MCP Inspector: собственный клиент Anthropic

bash scripts/run_inspector.sh

Откройте напечатанный URL http://localhost:6274?..., требуется токен. В нём есть отдельные вкладки Tools, Resources и Prompts, что является самым убедительным способом показать все три: ни одна из них не является нашим кодом, поэтому если Inspector управляет сервером, сервер действительно соответствует спецификации.

Предлагаемый маршрут: Tools → describe_table с ORDERS; Resources → schema://overview; Prompts → audit_schema.

Claude Desktop / Claude Code

bash scripts/add_to_claude_desktop.sh    # Claude Desktop, run from Terminal.app
bash scripts/install_claude.sh           # Claude Code, safe to run anywhere

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

Затем спросите: «Проверьте эту базу данных», или используйте промпт audit_schema из меню — именно там промпты наконец становятся видимыми.

--desktop необходимо запускать из Terminal.app, а не изнутри Claude Desktop. Claude Desktop хранит свою конфигурацию в памяти и перезаписывает файл из этой копии, поэтому изменение, сделанное во время его работы, молча отбрасывается. Скрипт завершает приложение, вносит изменения и перезапускает его, что убьёт сессию, из которой вы его запустили.


Чтение кода

Примерно час от начала до конца. Этот порядок выстраивается без прямых ссылок вперёд:

1. mcp_server/server.py: начните здесь. Десять значимых строк, и вся архитектура умещается на одном экране: создайте сервер, зарегистрируйте три примитива, запустите. Всё остальное — детали.

2. mcp_server/database.py: обычный код MySQL, в котором вообще нет MCP. Его стоит прочитать рано, потому что он показывает, насколько тонким на самом деле является слой MCP: если у вас уже есть слой доступа к данным, вы уже почти у цели.

Внимательно посмотрите на safe_identifier. MySQL не позволяет передавать имя таблицы как параметр (SHOW CREATE TABLE %s — недопустимый SQL), поэтому идентификаторы приходится интерполировать в строку. Это реальный риск инъекций, и именно эта небольшая функция делает его безопасным.

3. mcp_server/tools.py: декоратор @mcp.tool() и идея, которая делает больше всего работы во всём проекте: docstring — это и есть промпт. Это единственное, что модель читает, решая, вызывать ли инструмент, поэтому он написан для модели, а не для человека, читающего исходный код.

4. mcp_server/resources.py: статические URI против шаблонных, и почему get_table_ddl существует и как инструмент, и как ресурс. Это дублирование намеренно и является самой наглядной иллюстрацией идеи «кто чем управляет».

5. mcp_server/prompts.py: промпты возвращают текст, а не данные. Текст — это инструкция, которая обычно говорит модели, какие инструменты использовать. Короткий файл, и его большинство людей никогда не видели.

6. mcp_server/execution.py: прочитайте это, когда захотите узнать, как можно сделать безопасным доступ на запись. Пять механизмов контроля, каждый с комментарием, объясняющим, что он предотвращает.

7. examples/explore_server.py: другая сторона протокола. Минимальный клиент, который перечисляет и вызывает всё, чтобы вы могли увидеть, что на самом деле передаётся по сети.


Дальнейшее развитие

Этот сервер ограничен одной базой данных, чтобы примеры были короткими. Чтобы развить его дальше:

  • Несколько схем: принимайте schema как аргумент инструмента, а не читайте MYSQL_DEMO_SCHEMA. Добавьте белый список, чтобы агент не мог добраться до продакшена.

  • Выполнение запросов: инструмент run_query. Это возможно, но полностью меняет историю безопасности: тогда серверу нужны учётные данные, которые читают ваши таблицы, а результаты попадают в контекст модели. Ограничьте только SELECT, добавьте LIMIT и используйте пользователя базы данных с доступом только на чтение.

  • Удалённый транспорт: mcp.run(transport="streamable-http"). Те же инструменты, тот же код, другой канал. Добавьте аутентификацию перед тем, как открыть доступ.

  • Кэширование: describe_table обращается к базе данных при каждом вызове. Небольшой кэш с TTL стоит того, как только модель начинает вызывать его в цикле.


Замечания по безопасности

Этот сервер может изменять ваши данные. Это намеренно: «может ли агент писать в мою базу данных?» — вопрос, который задаёт каждая команда, и рабочий пример того, как делать это безопасно, полезнее, чем тот, который избегает этой темы. Но это означает, что механизмы контроля имеют значение.

Пять механизмов контроля, все в execution.py

Контроль

Что он предотвращает

Блокировка схемы

каждый оператор выполняется на соединении, привязанном к демонстрационной базе данных; ссылка на любую другую базу данных отклоняется

Один оператор за вызов

второй оператор не может «проехать зайцем» вместе с легитимным

Раздельные двери чтения/записи

run_query отказывается писать, а execute_statement отказывается читать, поэтому ни один из них нельзя уговорить выполнить работу другого

Ограничение строк

широкий SELECT не может затопить контекст модели

Журнал аудита

каждый оператор записывается и доступен для чтения через show_audit_log

Чёрный список также отклоняет операторы, которые могли бы обойти блокировку схемы, обратиться к файловой системе или изменить состояние всего сервера, изменения привилегий, управление пользователями, импорт/экспорт файлов и операции уровня базы данных.

Одна тонкость, потому что это легко повторяемая ошибка: блокировка схемы не может работать только по шаблону. В SQL a.b — это обычно alias.column (SELECT c.NAME FROM CUSTOMERS c), а не schema.table, поэтому отклонение любого имени с точкой ломает обычные JOIN — именно такой баг был в первой версии. Теперь она сравнивает каждый квалификатор с фактическим списком баз данных на сервере: реальное имя базы данных отклоняется, а алиас таблицы проходит без изменений.

Направьте его на ограниченного пользователя

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

Ещё две вещи, которые стоит сказать прямо:

  • Имена таблиц не могут быть связанными параметрами. SHOW CREATE TABLE %s — недопустимый SQL, поэтому идентификаторы должны интерполироваться, что является настоящей точкой инъекций. Именно database.safe_identifier делает это безопасным, и это самая важная функция в проекте.

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


Лицензия

MIT, см. LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with MySQL databases through MCP, supporting query execution, table operations (insert, update, delete), and schema inspection for natural language database management.
    202 npm
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MySQL database operations through MCP, including executing SQL queries, listing databases and tables, and describing table structures.
    730 npm
    5
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables natural language interaction with MySQL databases through MCP, supporting SQL execution, schema exploration, and database management via tools, resources, and prompts.
    5
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables natural language interaction with MySQL databases through MCP tools for querying, executing DDL/DML, listing databases/tables, and describing table schemas, with parameterized queries and read-only mode.
    730 npm
    MIT