Model Context Protocol

Навчіться створювати MCP-сервери та клієнти. Від архітектури протоколу до робочих інтеграцій.

14
уроків
4
модулі
8+
вправ
4+
квізів

1.1 Ласкаво просимо до курсу

Модуль 1 · Введення в MCP
Цілі курсу
  • Зрозуміти архітектуру Model Context Protocol та проблеми, які він вирішує
  • Навчитися створювати MCP сервери з інструментами, ресурсами та промптами
  • Побудувати повноцінний MCP клієнт, що підключається до серверів
  • Освоїти три примітиви MCP: Tools, Resources, Prompts
  • Отримати практичний досвід через реальні приклади на Python

Чому цей курс змінить ваш підхід до AI-інтеграцій

Уявіть, що ви будуєте AI-додаток. Ваш чат-бот повинен читати документи з Google Drive, створювати задачі в Jira, перевіряти статус CI/CD пайплайну та відповідати на запитання з бази знань. Скільки коду вам потрібно написати?

Традиційний підхід: для кожного сервісу вручну писати JSON-схеми інструментів, функції-обробники, валідацію параметрів, обробку помилок. Десятки файлів, сотні рядків шаблонного коду для кожної інтеграції.

А тепер уявіть інший світ: ви підключаєте готовий MCP-сервер для Google Drive, інший для Jira, третій для GitHub. Кожен з них вже містить усі інструменти, ресурси та промпти. Вам залишається лише написати клієнт, який з'єднає ці сервери з вашим AI-додатком.

Це і є Model Context Protocol -- стандарт, який робить AI-інтеграції простими, стандартизованими та повторно використовуваними.

Що ви побудуєте у цьому курсі

Ми створимо навчальний проєкт -- CLI-чатбот з власним MCP сервером та власним MCP клієнтом. Сервер керуватиме документами у пам'яті та надаватиме:

  • Tools -- інструменти для читання та редагування документів
  • Resources -- ресурси для перегляду списку документів та їхнього вмісту
  • Prompts -- готові промпти для форматування документів у Markdown

Клієнт підключатиметься до сервера, отримуватиме списки інструментів та передаватиме їх Claude для виконання.

Структура курсу

МодульТемиУроки
M1: ВведенняАрхітектура MCP, клієнти, комунікація3
M2: СервериНалаштування, інструменти, Inspector3
M3: КлієнтиРеалізація, ресурси, промпти5
M4: ПідсумкиПорівняння примітивів, патерни, тест3

Передумови

Що потрібно знати
  • Python -- базовий рівень (функції, декоратори, type hints)
  • Командний рядок -- вміння працювати в терміналі
  • AI/LLM -- загальне розуміння, що таке мовні моделі та tool use

Не потрібно заздалегідь знати MCP -- ми почнемо з нуля.

Часті запитання

Чи потрібен мені API ключ Anthropic?
Так, для запуску CLI-чатбота з Claude потрібен API ключ. Ви можете отримати його на console.anthropic.com. Для навчальних цілей достатньо безкоштовного тріалу.
Чи працює MCP тільки з Claude?
Ні! MCP -- це відкритий протокол. Хоча Anthropic його створила, він може працювати з будь-якою мовною моделлю. У цьому курсі ми використовуємо Claude як приклад, але принципи застосовні до будь-якого AI-клієнта.
Скільки часу займе курс?
Весь курс розрахований на 3-4 години активного навчання. Кожен урок займає 15-25 хвилин. Ви можете проходити його у власному темпі -- прогрес зберігається автоматично.

1.2 Що таке MCP?

Модуль 1 · Введення в MCP
Цілі уроку
  • Зрозуміти проблему, яку вирішує MCP
  • Розібрати архітектуру: Host, Client, Server
  • Пізнати три примітиви: Tools, Resources, Prompts
  • Побачити різницю між традиційним підходом і MCP

Проблема: кожен сервіс -- окрема інтеграція

Ви -- розробник AI-додатку. Вам потрібно, щоб Claude міг працювати з GitHub: створювати issues, переглядати pull requests, коментувати код. Як це зробити традиційно?

  1. Вивчити GitHub API (десятки ендпоінтів)
  2. Написати JSON-схему для кожного інструменту (create_issue, list_prs, add_comment...)
  3. Реалізувати функцію-обробник для кожного інструменту
  4. Додати валідацію параметрів, обробку помилок, автентифікацію
  5. Підтримувати це все при оновленнях API

Тепер уявіть, що вам потрібні ще Slack, Google Drive, Jira, PostgreSQL... Кожна інтеграція -- це тижні роботи. А тепер помножте це на кожну команду розробників у світі, яка пише ті самі обгортки знову і знову.

Метафора: MCP = USB-C для AI

Пам'ятаєте часи, коли кожен телефон мав свій зарядний кабель? Nokia -- один, Samsung -- інший, iPhone -- третій. Щоб зарядити три пристрої, потрібні три різні кабелі.

Потім з'явився USB-C -- один стандарт для всього. Один кабель заряджає телефон, ноутбук, навушники, планшет.

MCP -- це USB-C для AI-інтеграцій. Замість окремого "кабеля" (інтеграції) для кожного сервісу, MCP надає єдиний стандарт. Хтось створює MCP-сервер для GitHub, хтось для Slack -- і будь-який MCP-клієнт може підключитися до них без додаткового коду.

Рішення: Model Context Protocol

MCP (Model Context Protocol) -- це комунікаційний шар, що надає AI-моделям контекст та інструменти без необхідності розробникам писати шаблонний код. MCP зміщує відповідальність за створення інструментів від вашого додатку до спеціалізованих MCP-серверів.

Ключова ідея

Замість того щоб ви писали tool-схеми та обробники для GitHub API, хтось інший (часто сам провайдер сервісу) створює MCP-сервер -- пакет з готовими інструментами, ресурсами та промптами. Ви просто підключаєтесь до нього.

Архітектура MCP

MCP побудований на чіткій архітектурі з трьома рівнями:

┌─────────────────────────────────────────────────────────┐ │ HOST (AI-додаток) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ MCP │ │ MCP │ │ MCP │ │ │ │ Client 1 │ │ Client 2 │ │ Client 3 │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ └─────────┼───────────────┼───────────────┼───────────────┘ │ │ │ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │ MCP │ │ MCP │ │ MCP │ │ Server │ │ Server │ │ Server │ │ (GitHub) │ │ (Slack) │ │ (DB) │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │ GitHub │ │ Slack │ │ PostgreSQL│ │ API │ │ API │ │ │ └───────────┘ └───────────┘ └───────────┘

Host (Хост)

AI-додаток, в якому працює мовна модель. Наприклад, Claude Desktop, ваш CLI-чатбот або будь-який інший AI-клієнт. Хост містить один або кілька MCP-клієнтів.

MCP Client (Клієнт)

Комунікаційний інтерфейс між хостом і MCP-сервером. Кожен клієнт підключається до одного сервера. Клієнт не виконує інструменти сам -- він лише передає запити серверу і повертає результати.

MCP Server (Сервер)

Інтерфейс до зовнішнього сервісу, що надає три типи можливостей:

  • Tools (інструменти) -- дії, які AI може виконувати
  • Resources (ресурси) -- дані для читання
  • Prompts (промпти) -- готові інструкції для AI
Чому це важливо?

Ця архітектура створює екосистему. Автор MCP-сервера для GitHub робить це один раз -- і тисячі додатків можуть використовувати його. Розробник AI-додатку пише клієнт один раз -- і може підключити будь-який MCP-сервер. Кожна сторона робить свою частину роботи.

Три примітиви MCP

MCP-сервер може надавати три типи примітивів. Кожен з них має чітке призначення та контролюється різними сторонами:

ПримітивХто контролюєПризначенняПриклад
ToolsAI-модельДії, що змінюють станСтворити issue, відправити повідомлення
ResourcesДодатокДані для читанняСписок документів, вміст файлу
PromptsКористувачГотові інструкціїФорматування документа, код-рев'ю

Не хвилюйтесь, якщо різниця поки що здається тонкою. У модулях 2 та 3 ми реалізуємо кожен з них і побачимо різницю на практиці.

Транспорт: як клієнт спілкується з сервером

MCP -- transport-agnostic протокол. Це означає, що клієнт і сервер можуть комунікувати через різні канали:

  • stdio (stdin/stdout) -- найпоширеніший варіант для локальних серверів. Клієнт і сервер працюють на одній машині.
  • SSE (Server-Sent Events) -- для віддалених серверів через HTTP.
  • WebSocket -- для двосторонньої комунікації у реальному часі.

У нашому навчальному проєкті ми використаємо stdio -- обидва компоненти працюватимуть на вашій машині.

Типи повідомлень MCP

Комунікація між клієнтом і сервером відбувається через визначені типи повідомлень:

  • list_tools request/result -- клієнт запитує список доступних інструментів
  • call_tool request/result -- клієнт просить виконати інструмент з параметрами
  • read_resource request/result -- клієнт запитує дані ресурсу за URI
  • list_prompts / get_prompt -- клієнт отримує список або конкретний промпт

Хто створює MCP-сервери?

Хто пише MCP-сервери?
Будь-хто! Але часто це самі провайдери сервісів (GitHub, Slack, PostgreSQL) або спільнота open-source. Anthropic також підтримує офіційні сервери для популярних сервісів.
Яка різниця між MCP і звичайним tool use?
MCP і tool use -- це комплементарні технології. Tool use -- це механізм, за допомогою якого AI обирає та використовує інструменти. MCP -- це протокол, що визначає хто створює ці інструменти та як вони доставляються до AI. MCP автоматизує процес реєстрації інструментів, який раніше робився вручну.
Чи замінює MCP прямі API-виклики?
Ні. MCP-сервер все одно робить API-виклики до зовнішніх сервісів. Різниця в тому, що ви не пишете цей код -- хтось вже зробив це за вас і упакував у MCP-сервер.
Вправа: Спроєктуйте свій MCP-сервер

Подумайте про сервіс, з яким ви часто працюєте (GitHub, Notion, Trello, база даних, тощо). Запишіть:

  1. Які інструменти (tools) ви б надали? (мінімум 3)
  2. Які ресурси (resources) можна було б читати?
  3. Які промпти (prompts) спростили б роботу?

Приклад: MCP-сервер для Notion

Tools: create_page, update_page, delete_page, add_comment, create_database

Resources: notion://pages (список сторінок), notion://pages/{id} (вміст сторінки), notion://databases (список баз)

Prompts: /summarize -- підсумувати сторінку, /meeting-notes -- створити шаблон нотаток зустрічі, /review -- рецензувати документ

Перевірка знань: Урок 1.2

1. Яку головну проблему вирішує MCP?

Прискорює відповіді мовної моделі
Замінює API зовнішніх сервісів
Усуває необхідність вручну писати tool-схеми та обробники для кожного сервісу

2. Який транспорт найчастіше використовується для локальних MCP-серверів?

WebSocket
stdio (stdin/stdout)
gRPC

3. Які три примітиви надає MCP-сервер?

Tools, Resources, Prompts
Functions, Data, Templates
Actions, Files, Messages

1.3 MCP клієнти

Модуль 1 · Введення в MCP
Цілі уроку
  • Зрозуміти роль MCP-клієнта в архітектурі
  • Розібрати повний потік взаємодії від запиту користувача до відповіді
  • Побачити типи повідомлень між клієнтом і сервером

Роль клієнта: посередник, а не виконавець

MCP-клієнт -- це комунікаційний інтерфейс між вашим сервером (де працює AI) і MCP-сервером (де живуть інструменти). Ключовий момент: клієнт нічого не виконує сам. Він лише передає запити та повертає відповіді.

Метафора: Клієнт = диспетчер таксі

Уявіть диспетчера таксі. Пасажир (LLM) дзвонить і каже: "Мені потрібно дістатися з точки А до точки Б". Диспетчер (MCP-клієнт) не їде сам -- він знаходить потрібного водія (MCP-сервер), передає замовлення і координує поїздку. Коли водій завершує маршрут, диспетчер повідомляє пасажиру результат.

Якщо пасажиру потрібна вантажівка, диспетчер знайде вантажівку. Потрібен мінівен -- знайде мінівен. Диспетчер знає, які водії (сервери) доступні і які послуги вони надають.

Повний потік взаємодії

Давайте прослідкуємо повний шлях запиту -- від того, що каже користувач, до фінальної відповіді:

Користувач: "Який вміст документа report.pdf?" 1. Ваш сервер отримує запит користувача 2. Сервер просить MCP-клієнт: "Які інструменти є?" 3. MCP-клієнт → MCP-сервер: list_tools request 4. MCP-сервер → MCP-клієнт: list_tools result [read_doc_contents, edit_document, ...] 5. Сервер надсилає Claude: запит + список інструментів 6. Claude обирає: "Мені потрібен read_doc_contents(doc_id='report.pdf')" 7. Сервер просить MCP-клієнт виконати інструмент 8. MCP-клієнт → MCP-сервер: call_tool request {name: "read_doc_contents", arguments: {doc_id: "report.pdf"}} 9. MCP-сервер виконує інструмент (читає документ) 10. MCP-сервер → MCP-клієнт: call_tool result {content: "Зміст документа..."} 11. Сервер надсилає результат Claude 12. Claude формулює відповідь 13. Користувач отримує відповідь
Чому це важливо?

Зверніть увагу: MCP-клієнт бере участь двічі -- спочатку для отримання списку інструментів, потім для виклику обраного інструменту. Це двофазний процес: discovery (що є?) і execution (зроби це). Саме ця структура робить MCP гнучким -- нові інструменти автоматично стають доступними без зміни коду клієнта.

Типи повідомлень

Комунікація між MCP-клієнтом і MCP-сервером відбувається через визначені пари request/result:

ЗапитВідповідьПризначення
list_tools requestlist_tools resultОтримати список доступних інструментів
call_tool requestcall_tool resultВиконати інструмент з аргументами
read_resource requestread_resource resultПрочитати дані ресурсу за URI
list_prompts requestlist_prompts resultОтримати список доступних промптів
get_prompt requestget_prompt resultОтримати конкретний промпт з аргументами

Transport-agnostic комунікація

MCP-клієнт і MCP-сервер можуть спілкуватися через різні протоколи передачі (транспорти). Для клієнта це прозоро -- він надсилає ті самі повідомлення незалежно від транспорту.

Локальний транспорт (stdio): ┌──────────┐ stdin/stdout ┌──────────┐ │ Client │ ─────────────→ │ Server │ │ │ ←───────────── │ (local) │ └──────────┘ └──────────┘ Віддалений транспорт (SSE/HTTP): ┌──────────┐ HTTP/SSE ┌──────────┐ │ Client │ ─────────────→ │ Server │ │ │ ←───────────── │ (remote) │ └──────────┘ └──────────┘

Найпоширеніший варіант для навчання та локальної розробки -- stdio. Обидва процеси працюють на одній машині, комунікуючи через стандартний ввід/вивід.

Вправа: Простежте потік запиту

Користувач каже: "Зміни заголовок у файлі notes.txt на 'Нотатки проєкту'". Опишіть повний потік взаємодії між усіма компонентами MCP-архітектури (Host, Client, Server, External Service).

  1. Host приймає запит користувача
  2. Host запитує MCP Client: list_tools
  3. MCP Client пересилає запит MCP Server
  4. MCP Server повертає: [read_doc_contents, edit_document]
  5. Host надсилає Claude запит + інструменти
  6. Claude обирає edit_document(doc_id="notes.txt", old_string="старий заголовок", new_string="Нотатки проєкту")
  7. Host просить MCP Client виконати call_tool
  8. MCP Client передає запит MCP Server
  9. MCP Server виконує edit_document на зовнішньому ресурсі
  10. Результат повертається: MCP Server -> MCP Client -> Host -> Claude -> Користувач
Перевірка знань: Урок 1.3

1. Яка головна роль MCP-клієнта?

Виконувати інструменти безпосередньо
Бути посередником між хостом і MCP-сервером
Зберігати дані у базі

2. Скільки разів MCP-клієнт бере участь у типовому потоці запиту?

Один раз -- для виклику інструменту
Три рази
Двічі -- для discovery та execution

3. Що означає "transport-agnostic"?

Клієнт і сервер можуть спілкуватися через різні протоколи (stdio, SSE, WebSocket)
Клієнт працює тільки через HTTP
Сервер не потребує мережевого з'єднання

2.1 Налаштування проєкту

Модуль 2 · Створення MCP серверів
Цілі уроку
  • Налаштувати Python-проєкт з MCP SDK
  • Зрозуміти структуру навчального проєкту
  • Створити базовий скелет MCP-сервера
  • Запустити проєкт і перевірити його роботу

Навчальний проєкт: CLI-чатбот з MCP

Замість абстрактної теорії ми одразу будемо працювати з реальним проєктом. Наш навчальний проєкт -- це CLI-чатбот, який реалізує обидві сторони MCP: і клієнт, і сервер. У реальному світі зазвичай проєкт реалізує або клієнт, або сервер, але для навчання ми зробимо обидва.

Що будемо будувати
  • MCP-сервер -- керує документами у пам'яті (без бази даних)
  • MCP-клієнт -- підключається до сервера та надає інструменти Claude
  • CLI-інтерфейс -- де користувач спілкується з Claude через командний рядок

Встановлення залежностей

MCP Python SDK значно спрощує роботу -- він автоматично генерує JSON-схеми з декорованих Python-функцій.

pip install mcp anthropic pydantic python-dotenv

Або, якщо ви використовуєте UV (швидший менеджер пакетів):

uv add mcp anthropic pydantic python-dotenv

Структура проєкту

mcp-learning-project/ ├── .env # API ключ Anthropic ├── main.py # Точка входу CLI-чатбота ├── mcp_client.py # Реалізація MCP-клієнта └── mcp_server.py # Реалізація MCP-сервера ├── tools # Інструменти (read_doc, edit_doc) ├── resources # Ресурси (список документів, вміст) └── prompts # Промпти (форматування документа)

Базовий скелет сервера

Створення MCP-сервера починається з однієї лінії коду:

from mcp.server.fastmcp import FastMCP # Створюємо MCP-сервер mcp = FastMCP("Document Manager") # Сховище документів у пам'яті docs = { "report.pdf": "Quarterly revenue report...", "notes.txt": "Meeting notes from Monday...", "todo.md": "# TODO\n- Fix bug #123\n- Review PR #456" } # Тут будемо додавати tools, resources, prompts...
Чому це важливо?

Зверніть увагу, наскільки це просто: FastMCP("Document Manager") -- і сервер готовий. Порівняйте з традиційним підходом, де вам потрібно було б вручну створювати HTTP-сервер, визначати маршрути, серіалізувати/десеріалізувати JSON, обробляти протокол... MCP SDK бере всю інфраструктуру на себе.

Файл конфігурації

Створіть файл .env у корені проєкту:

ANTHROPIC_API_KEY=sk-ant-ваш-ключ-тут

Запуск та перевірка

Для запуску навчального проєкту:

# З UV: uv run main.py # Або без UV: python main.py

Якщо все налаштовано правильно, ви побачите запрошення до чату. Спробуйте просте запитання для перевірки:

You: What's one plus one? Claude: One plus one equals two.
Поширені помилки
  • ModuleNotFoundError: mcp -- перевірте, що ви встановили пакет: pip install mcp
  • ANTHROPIC_API_KEY not set -- переконайтесь, що файл .env існує і містить ключ
  • Permission denied -- спробуйте pip install --user mcp
Вправа: Налаштуйте проєкт

Створіть порожній MCP-сервер з FastMCP та словник з 3 тестовими документами. Переконайтесь, що сервер створюється без помилок. Які поля повинен мати кожен документ у словнику?

Словник документів -- це проста структура {doc_id: content}, де ключ -- ім'я документа, а значення -- його текстовий вміст:

from mcp.server.fastmcp import FastMCP mcp = FastMCP("My Server") docs = { "readme.md": "# My Project\nWelcome to the project", "config.json": '{"version": "1.0", "debug": true}', "notes.txt": "Important notes for the team" }

Документ -- просто рядок тексту. У реальному проєкті це могла б бути структура з метаданими, але для навчання достатньо простого словника.

2.2 Визначення інструментів (Tools)

Модуль 2 · Створення MCP серверів
Цілі уроку
  • Створити MCP-інструменти з декоратором @mcp.tool()
  • Використати type hints та Field descriptions для автоматичної генерації JSON-схем
  • Реалізувати інструменти для читання та редагування документів
  • Зрозуміти, як Python SDK перетворює функції на tool-схеми

Інструменти -- це меню ресторану для AI

Метафора: Інструменти = меню ресторану

Коли ви приходите в ресторан, офіціант дає вам меню. Меню описує кожну страву: назва, інгредієнти, ціна. Ви обираєте, що хочете, і кухня готує це для вас.

MCP-інструменти -- це меню для AI. Кожен інструмент має назву, опис параметрів і результат. AI переглядає "меню", обирає потрібний інструмент і "замовляє" його виконання. MCP-сервер -- це кухня, що виконує замовлення.

Традиційний підхід vs MCP SDK

Раніше, щоб створити інструмент для AI, потрібно було вручну написати JSON-схему:

# Традиційний підхід -- ВРУЧНУ: tool_schema = { "name": "read_doc_contents", "description": "Read the contents of a document", "input_schema": { "type": "object", "properties": { "doc_id": { "type": "string", "description": "The ID of the document to read" } }, "required": ["doc_id"] } } def handle_read_doc(doc_id: str): if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") return docs[doc_id]

З MCP Python SDK все це замінюється одним декоратором:

# MCP підхід -- АВТОМАТИЧНО: from pydantic import Field @mcp.tool() def read_doc_contents( doc_id: str = Field(description="The ID of the document to read") ) -> str: """Read the contents of a document by its ID.""" if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") return docs[doc_id]
Що відбувається за лаштунками?

MCP SDK автоматично:

  • Генерує JSON-схему з type hints (str, int, bool...)
  • Використовує Field(description=...) для опису параметрів
  • Бере docstring функції як опис інструменту
  • Реєструє функцію як обробник інструменту

Жодного ручного JSON! Python код -- єдине джерело правди.

Інструмент 1: Читання документа

@mcp.tool() def read_doc_contents( doc_id: str = Field(description="The ID of the document to read") ) -> str: """Read the contents of a document by its ID. Use this tool when you need to see what's inside a specific document. """ if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") return docs[doc_id]

Інструмент 2: Редагування документа

@mcp.tool() def edit_document( doc_id: str = Field(description="The ID of the document to edit"), old_string: str = Field(description="The text to find and replace"), new_string: str = Field(description="The replacement text") ) -> str: """Edit a document by performing a find-and-replace operation. Finds old_string in the document and replaces it with new_string. Returns confirmation of the edit. """ if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") if old_string not in docs[doc_id]: raise ValueError(f"String '{old_string}' not found in document") docs[doc_id] = docs[doc_id].replace(old_string, new_string) return f"Successfully edited {doc_id}"

Патерн реалізації інструментів

Кожен MCP-інструмент слідує одному і тому ж патерну:

@mcp.tool() # 1. Декоратор def tool_name( # 2. Ім'я функції = ім'я інструменту param: type = Field( # 3. Параметри з type hints description="..." # 4. Опис для AI ) ) -> return_type: # 5. Тип повернення """Docstring = опис інструменту.""" # 6. Docstring # 7. Валідація if not valid: raise ValueError("...") # 8. Основна логіка result = do_something() return result # 9. Результат
Чому це важливо?

Цей патерн масштабується. Чи ви створюєте інструмент для читання документа, чи для виклику Kubernetes API -- структура однакова. Декоратор + type hints + Field descriptions + валідація + логіка. MCP SDK робить всю інфраструктурну роботу за вас.

Field descriptions -- мова спілкування з AI

Field(description=...) з Pydantic -- це те, що AI "читає", коли обирає інструмент. Чим точніше опис, тим краще AI розуміє, коли і як використовувати інструмент.

Погані vs хороші описи

Погано: Field(description="id")

Добре: Field(description="The unique identifier of the document, e.g. 'report.pdf' or 'notes.txt'")

AI повинен зрозуміти з опису, яке значення передати. Це не коментар для розробника -- це інструкція для моделі.

Вправа: Створіть інструмент delete_document

Напишіть MCP-інструмент delete_document, який видаляє документ зі словника docs. Інструмент повинен:

  1. Приймати doc_id як параметр з описом
  2. Перевіряти, чи існує документ
  3. Видаляти документ і повертати підтвердження
@mcp.tool() def delete_document( doc_id: str = Field( description="The ID of the document to delete" ) ) -> str: """Delete a document permanently by its ID. Use this tool when a user explicitly requests to remove a document. """ if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") del docs[doc_id] return f"Document {doc_id} has been deleted"

2.3 Server Inspector

Модуль 2 · Створення MCP серверів
Цілі уроку
  • Навчитися запускати MCP Inspector для тестування серверів
  • Протестувати інструменти через браузерний інтерфейс
  • Зрозуміти процес дебагінгу MCP-серверів

Тестування без клієнта

Ви написали MCP-сервер з інструментами. Як перевірити, що вони працюють, не будуючи повноцінний клієнт? MCP Inspector -- це браузерний дебагер, який дозволяє тестувати ваш сервер прямо у браузері.

Уявіть, що ви механік, який зібрав двигун. Перш ніж ставити його в автомобіль, ви тестуєте його на стенді. MCP Inspector -- це такий тестовий стенд для вашого MCP-сервера.

Запуск Inspector

mcp dev mcp_server.py

Ця команда:

  1. Запускає ваш MCP-сервер
  2. Підключається до нього як клієнт
  3. Відкриває веб-інтерфейс на localhost

Інтерфейс Inspector

┌─────────────────────────────────────────────────┐ │ MCP Inspector [Connect] │ ├──────────┬──────────────────────────────────────┤ │ │ │ │ Resources│ Tool: read_doc_contents │ │ Prompts │ ────────────────────────── │ │ Tools ← │ doc_id: [report.pdf ] │ │ │ │ │ ─────────│ [Run Tool] │ │ │ │ │ read_doc │ Result: │ │ edit_doc │ "Quarterly revenue report..." │ │ │ │ └──────────┴──────────────────────────────────────┘

Процес тестування

  1. Підключіться -- натисніть "Connect" у лівій панелі
  2. Перейдіть до Tools -- оберіть вкладку "Tools" у верхній навігації
  3. Оберіть інструмент -- клікніть на інструмент зі списку (наприклад, read_doc_contents)
  4. Введіть параметри -- заповніть поля (наприклад, doc_id: "report.pdf")
  5. Запустіть -- натисніть "Run Tool"
  6. Перевірте результат -- переконайтесь, що вивід коректний
Що тестувати?
  • Успішний сценарій -- коректний doc_id повертає вміст
  • Помилковий сценарій -- неіснуючий doc_id повертає помилку
  • Граничні випадки -- порожній рядок, спеціальні символи
  • Ланцюжок операцій -- прочитати, відредагувати, прочитати знову
Чому це важливо?

Inspector -- це перший етап тестування MCP-сервера. Він дозволяє перевірити кожен інструмент ізольовано, без побудови клієнта. Це економить час і допомагає знайти помилки на ранньому етапі. У реальних проєктах Inspector також корисний для документування доступних інструментів та їхніх параметрів.

Дебагінг поширених проблем

Поширені помилки при тестуванні
  • "Tool not found" -- перевірте, що функція має декоратор @mcp.tool()
  • "Invalid parameters" -- перевірте type hints та Field descriptions
  • "Server disconnected" -- перевірте, що сервер запущений і немає runtime помилок
  • Inspector не відкривається -- перевірте, чи активоване Python-середовище

Inspector знаходиться в активній розробці -- його інтерфейс може змінюватися, але основна функціональність (підключення, вибір інструменту, введення параметрів, запуск) залишається стабільною.

Вправа: Тестування через Inspector

Запустіть Inspector для вашого сервера і виконайте наступний ланцюжок:

  1. Прочитайте документ notes.txt за допомогою read_doc_contents
  2. Відредагуйте його за допомогою edit_document -- замініть "Monday" на "Tuesday"
  3. Прочитайте документ ще раз, щоб переконатися у зміні

Що ви очікуєте побачити на кожному кроці?

  1. Крок 1: read_doc_contents(doc_id="notes.txt") -> "Meeting notes from Monday..."
  2. Крок 2: edit_document(doc_id="notes.txt", old_string="Monday", new_string="Tuesday") -> "Successfully edited notes.txt"
  3. Крок 3: read_doc_contents(doc_id="notes.txt") -> "Meeting notes from Tuesday..."

Якщо на кроці 3 ви бачите "Tuesday" замість "Monday" -- все працює правильно!

Перевірка знань: Модуль 2

1. Як запустити MCP Inspector?

python inspector.py
mcp dev mcp_server.py
npm run inspector

2. Що робить декоратор @mcp.tool()?

Реєструє функцію як MCP-інструмент з автоматичною генерацією JSON-схеми
Створює HTTP-ендпоінт для функції
Додає логування до функції

3. Для чого використовується Field(description=...)?

Для валідації типів даних
Для генерації документації
Для опису параметрів інструменту, який AI буде читати при виборі

4. Який патерн слідують MCP-інструменти?

HTTP endpoint -> middleware -> handler
Декоратор -> type hints -> Field descriptions -> валідація -> логіка
Class -> init -> execute -> cleanup

3.1 Реалізація клієнта

Модуль 3 · Підключення клієнтів
Цілі уроку
  • Побудувати MCP-клієнт, що підключається до сервера
  • Реалізувати list_tools() та call_tool()
  • Зрозуміти управління ресурсами та cleanup
  • Інтегрувати клієнт з CLI-чатботом

Від тестування до реальної інтеграції

У попередньому модулі ми створили сервер і протестували його через Inspector. Тепер час побудувати програмний клієнт -- код, який буде підключатися до сервера, отримувати список інструментів і викликати їх за запитом Claude.

Це як перехід від тестового стенду до реального автомобіля. Двигун (сервер) працює, тепер потрібно з'єднати його з коробкою передач (клієнтом), щоб автомобіль поїхав.

Клас MCPClient

Стандартний підхід -- обгортка навколо ClientSession з MCP SDK для управління з'єднанням та ресурсами:

from mcp import ClientSession from mcp.client.stdio import stdio_client class MCPClient: """Wrapper around MCP client session for resource management.""" def __init__(self): self.session = None self._cleanup = None async def connect(self, server_script_path: str): """Connect to an MCP server.""" # Створюємо stdio з'єднання з сервером read, write = await stdio_client(server_script_path) # Ініціалізуємо сесію self.session = ClientSession(read, write) await self.session.initialize() async def list_tools(self): """Get list of available tools from the server.""" result = await self.session.list_tools() return result.tools async def call_tool(self, tool_name: str, tool_input: dict): """Call a tool on the server with given arguments.""" result = await self.session.call_tool(tool_name, tool_input) return result async def cleanup(self): """Clean up resources when shutting down.""" if self.session: await self.session.close()
Чому обгортка замість прямого використання?

MCP ClientSession потребує правильного управління ресурсами -- з'єднання повинно бути закритим при завершенні. Обгортковий клас інкапсулює цю логіку, надаючи чистий API для решти додатку: connect(), list_tools(), call_tool(), cleanup().

Інтеграція з Claude

Тепер підключимо клієнт до циклу чату з Claude:

import anthropic async def chat_loop(client: MCPClient): """Main chat loop with Claude and MCP tools.""" anthropic_client = anthropic.Anthropic() # Отримуємо інструменти з MCP-сервера tools = await client.list_tools() # Конвертуємо у формат Claude claude_tools = [ { "name": tool.name, "description": tool.description, "input_schema": tool.inputSchema } for tool in tools ] messages = [] while True: user_input = input("You: ") if user_input.lower() == "quit": break messages.append({"role": "user", "content": user_input}) # Надсилаємо запит Claude з доступними інструментами response = anthropic_client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, tools=claude_tools, messages=messages ) # Обробляємо tool use, якщо Claude обрав інструмент while response.stop_reason == "tool_use": tool_block = next( b for b in response.content if b.type == "tool_use" ) # Викликаємо інструмент через MCP-клієнт tool_result = await client.call_tool( tool_block.name, tool_block.input ) # Повертаємо результат Claude messages.append({"role": "assistant", "content": response.content}) messages.append({ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": tool_block.id, "content": str(tool_result) }] }) response = anthropic_client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, tools=claude_tools, messages=messages ) # Виводимо фінальну відповідь assistant_text = next( b.text for b in response.content if hasattr(b, 'text') ) print(f"Claude: {assistant_text}") messages.append({"role": "assistant", "content": response.content})

Потік tool calling

┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ User │ │ Claude │ │ MCP │ │ MCP │ │ │ │ │ │ Client │ │ Server │ └────┬────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │─ "What's in │ │ │ │ report?"───→│ │ │ │ │─ list_tools()→│ │ │ │ │─ list_tools──→│ │ │ │←─ [tools]────│ │ │←─ [tools]────│ │ │ │ │ │ │ │─ "Use │ │ │ │ read_doc"───→│ │ │ │ │─ call_tool──→│ │ │ │←─ result────│ │ │←─ result─────│ │ │ │ │ │ │←── "The │ │ │ │ report │ │ │ │ contains..."│ │ │
Вправа: Тестування клієнта

Після реалізації клієнта, запустіть CLI-чатбот і протестуйте наступний діалог:

  1. "What documents are available?" -- Claude повинен використати інструмент
  2. "What's in the report?" -- Claude повинен прочитати report.pdf
  3. "Change 'Quarterly' to 'Annual' in the report" -- Claude повинен відредагувати документ

Що ви очікуєте на кожному кроці?

  1. Claude викличе read_doc_contents або спитає, які документи є (залежить від наявних інструментів)
  2. Claude викличе read_doc_contents(doc_id="report.pdf") і поверне вміст
  3. Claude викличе edit_document(doc_id="report.pdf", old_string="Quarterly", new_string="Annual") і підтвердить зміну

Кожного разу Claude самостійно обирає інструмент на основі контексту розмови та описів tools.

3.2 Визначення ресурсів (Resources)

Модуль 3 · Підключення клієнтів
Цілі уроку
  • Зрозуміти різницю між Tools та Resources
  • Створити статичні та шаблонні ресурси
  • Використати MIME-типи для правильної серіалізації
  • Застосувати декоратор @mcp.resource()

Ресурси -- це дані для читання, не для дій

Метафора: Ресурси = бібліотечний каталог

Уявіть бібліотеку. Книги (дані) стоять на полицях. Каталог (ресурси) допомагає знайти потрібну книгу: ви шукаєте за назвою, автором або жанром, і каталог показує, де саме стоїть книга.

MCP-ресурси працюють так само: вони надають доступ до даних, але не змінюють їх. Хочете прочитати список документів? Ресурс. Хочете побачити вміст документа? Ресурс. Хочете відредагувати документ? Інструмент.

Tools vs Resources

ХарактеристикаToolsResources
ПризначенняДії, що змінюють станЧитання даних
Хто контролюєAI-модель обираєДодаток запитує
Прикладedit_document, send_messageСписок документів, вміст файлу
АналогіяКнопки на пультіЕкран телевізора

Два типи ресурсів

1. Статичний ресурс (Direct Resource)

Має фіксований URI. Завжди повертає те саме "джерело" даних:

@mcp.resource("docs://documents") def list_documents() -> str: """List all available documents. Returns a JSON array of document IDs. """ return json.dumps(list(docs.keys()))

URI docs://documents -- це "адреса" ресурсу. Клієнт звертається до цієї адреси, щоб отримати список документів.

2. Шаблонний ресурс (Templated Resource)

URI містить параметр у фігурних дужках. Параметр стає аргументом функції:

@mcp.resource("docs://documents/{doc_id}") def get_document_content(doc_id: str) -> str: """Get the content of a specific document. Returns the full text content of the document. """ if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") return docs[doc_id]

Тут {doc_id} в URI стає параметром doc_id у функції. Це як маршрути у веб-фреймворках: /users/{id} -> def get_user(id).

MIME-типи

MIME-тип підказує клієнту, як інтерпретувати повернені дані:

# JSON дані -- клієнт десеріалізує як об'єкт @mcp.resource("docs://documents", mime_type="application/json") def list_documents() -> str: return json.dumps(list(docs.keys())) # Текстові дані -- клієнт використовує як рядок @mcp.resource("docs://documents/{doc_id}", mime_type="text/plain") def get_document(doc_id: str) -> str: return docs[doc_id]
Чому це важливо?

MIME-тип -- це контракт між сервером і клієнтом. Якщо сервер каже "application/json", клієнт знає, що потрібно розпарсити JSON. Якщо "text/plain" -- просто показати текст. Неправильний MIME-тип може призвести до помилок десеріалізації на стороні клієнта.

Коли використовувати Resources замість Tools?

Правило: читання = ресурс, дія = інструмент

Якщо операція тільки читає дані і не змінює стан -- це ресурс. Якщо операція створює, оновлює або видаляє щось -- це інструмент.

  • Показати список файлів -> Resource
  • Прочитати вміст файлу -> Resource
  • Відредагувати файл -> Tool
  • Видалити файл -> Tool
  • Показати статус CI/CD -> Resource
  • Запустити пайплайн -> Tool
Вправа: Створіть ресурс для метаданих

Створіть шаблонний ресурс docs://documents/{doc_id}/metadata, який повертає JSON з метаданими документа: id, length (кількість символів), words (кількість слів).

import json @mcp.resource( "docs://documents/{doc_id}/metadata", mime_type="application/json" ) def get_document_metadata(doc_id: str) -> str: """Get metadata about a specific document.""" if doc_id not in docs: raise ValueError(f"Document {doc_id} not found") content = docs[doc_id] return json.dumps({ "id": doc_id, "length": len(content), "words": len(content.split()) })

3.3 Доступ до ресурсів з клієнта

Модуль 3 · Підключення клієнтів
Цілі уроку
  • Реалізувати read_resource() у MCP-клієнті
  • Навчитися обробляти різні MIME-типи
  • Інтегрувати ресурси з CLI-чатботом для контекстного доповнення

Читання ресурсів з клієнта

Тепер, коли ми визначили ресурси на сервері, потрібно навчити клієнт їх читати. Додаємо метод read_resource() до нашого MCPClient:

from pydantic import AnyUrl import json class MCPClient: # ... попередні методи ... async def read_resource(self, uri: str): """Read a resource from the MCP server by URI.""" result = await self.session.read_resource(AnyUrl(uri)) # Отримуємо перший ресурс з відповіді resource = result.contents[0] # Обробляємо залежно від MIME-типу if resource.mime_type == "application/json": return json.loads(resource.text) else: return resource.text

Обробка MIME-типів

Зверніть увагу на перевірку mime_type. Це ключовий момент:

# Якщо сервер повернув JSON: resource.mime_type == "application/json" # -> json.loads(resource.text) -> Python dict/list # Якщо сервер повернув текст: resource.mime_type == "text/plain" # -> resource.text -> Python str
Чому це важливо?

Без перевірки MIME-типу клієнт не знає, як інтерпретувати дані. Якщо сервер повертає JSON-рядок, а клієнт обробляє його як звичайний текст -- ви отримаєте сирий JSON замість зручного Python-об'єкта. MIME-тип -- це контракт між сервером і клієнтом.

Інтеграція ресурсів з чатботом

Ресурси можна використовувати для контекстного доповнення -- автоматичного додавання даних до промпту Claude перед запитом:

async def chat_with_context(client: MCPClient, user_message: str): """Chat with automatic resource context injection.""" # Отримуємо список документів як контекст doc_list = await client.read_resource("docs://documents") # Формуємо системний промпт з контекстом context = f"Available documents: {doc_list}" # Тепер Claude знає про доступні документи # ще до того, як використає інструменти response = anthropic_client.messages.create( model="claude-sonnet-4-20250514", system=context, messages=[{"role": "user", "content": user_message}], tools=claude_tools )

Ресурси vs Tools для читання

Виникає запитання: якщо у нас є інструмент read_doc_contents, навіщо створювати ресурс docs://documents/{doc_id}? Різниця в хто контролює:

Контроль: Tools vs Resources
  • Tool read_doc_contents: Claude сам вирішує, коли прочитати документ (model-controlled)
  • Resource docs://documents/{doc_id}: ваш додаток вирішує, коли завантажити дані (app-controlled)

Наприклад, ви можете завантажити вміст документа через ресурс до початку чату і вставити в системний промпт -- без участі Claude. А інструмент Claude використає, коли сам вирішить, що потрібно прочитати документ.

Вправа: CLI з вибором документів

Реалізуйте функцію select_documents(client), яка:

  1. Отримує список документів через read_resource("docs://documents")
  2. Виводить список користувачу
  3. Дозволяє обрати один або кілька документів
  4. Завантажує вміст обраних документів через шаблонний ресурс
  5. Повертає об'єднаний контекст
async def select_documents(client: MCPClient) -> str: """Let user select documents for context.""" # 1. Отримуємо список doc_list = await client.read_resource("docs://documents") # 2. Виводимо print("Available documents:") for i, doc_id in enumerate(doc_list): print(f" [{i+1}] {doc_id}") # 3. Обираємо selection = input("Select (comma-separated numbers): ") indices = [int(x.strip()) - 1 for x in selection.split(",")] # 4. Завантажуємо вміст context_parts = [] for idx in indices: doc_id = doc_list[idx] content = await client.read_resource( f"docs://documents/{doc_id}" ) context_parts.append(f"--- {doc_id} ---\n{content}") # 5. Повертаємо контекст return "\n\n".join(context_parts)

3.4 Визначення промптів (Prompts)

Модуль 3 · Підключення клієнтів
Цілі уроку
  • Зрозуміти роль промптів як третього примітиву MCP
  • Створити промпт-шаблони з аргументами
  • Побачити, як промпти відрізняються від Tools та Resources
  • Застосувати декоратор @mcp.prompt()

Промпти -- готові рецепти для AI

Метафора: Промпти = рецепти шеф-кухаря

Уявіть, що ви прийшли в ресторан і хочете приготувати страву. Варіант A: ви описуєте кожен інгредієнт і крок ("візьміть 200г борошна, 2 яйця, змішайте..."). Варіант B: ви кажете "зробіть за рецептом шеф-кухаря номер 5".

MCP-промпти -- це рецепти шеф-кухаря. Замість того, щоб користувач щоразу вигадував ідеальний промпт -- автор MCP-сервера створює перевірені, оптимізовані інструкції для типових задач. Користувач просто обирає потрібний рецепт.

Чим промпти відрізняються від Tools та Resources?

Три рівні контролю
ПримітивКонтролюєТригер
ToolsAI-модельClaude сам вирішує використати
ResourcesДодатокКод додатку запитує дані
PromptsКористувачКористувач обирає через slash-команду або кнопку

Промпти -- це user-controlled примітив. Вони не виконуються автоматично -- користувач явно обирає їх (наприклад, набирає /format у CLI або натискає кнопку в UI).

Створення промптів

Промпт визначається декоратором @mcp.prompt(). Функція повертає список повідомлень, які будуть надіслані Claude:

from mcp.server.fastmcp import FastMCP from mcp.types import TextContent @mcp.prompt( name="format", description="Rewrites a document in clean Markdown format" ) def format_document(doc_id: str) -> list: """Create a prompt to format a document as Markdown.""" return [ { "role": "user", "content": f"""Please read the document '{doc_id}' using the read_doc_contents tool. Then rewrite its content in clean, well-structured Markdown format. Use appropriate headings, lists, and formatting. Save the result back using the edit_document tool. Requirements: - Use proper Markdown headings (# ## ###) - Convert any lists to Markdown bullet points - Add emphasis to key terms - Ensure consistent formatting throughout - Preserve all original information""" } ]

Промпт з кількома аргументами

@mcp.prompt( name="review", description="Review a document for quality and completeness" ) def review_document(doc_id: str, focus: str = "general") -> list: """Create a review prompt with configurable focus area.""" return [ { "role": "user", "content": f"""Please review the document '{doc_id}'. Focus area: {focus} Read the document using the read_doc_contents tool, then provide: 1. Summary of the document's purpose 2. Strengths 3. Areas for improvement 4. Specific suggestions (use edit_document if the user agrees) Be thorough but concise in your review.""" } ]
Чому це важливо?

Автор MCP-сервера для, наприклад, системи документації краще за всіх знає, як правильно попросити AI працювати з документами. Він вкладає свою експертизу у промпти. Замість того, щоб кожен користувач вигадував промпт для рецензування -- автор створює /review, що містить всі найкращі практики.

Як промпти використовуються

Користувач у CLI: /format Система: "Оберіть документ:" [1] report.pdf [2] notes.txt [3] todo.md Користувач: 2 Система підставляє doc_id="notes.txt" у промпт -> Промпт надсилається Claude -> Claude використовує read_doc_contents для читання -> Claude переформатовує вміст -> Claude використовує edit_document для збереження -> Користувач отримує результат
Вправа: Створіть промпт для підсумку

Створіть MCP-промпт summarize, який просить Claude:

  1. Прочитати документ за допомогою read_doc_contents
  2. Створити короткий підсумок (3-5 речень)
  3. Виділити ключові пункти
  4. Запропонувати наступні кроки
@mcp.prompt( name="summarize", description="Create a concise summary of a document" ) def summarize_document(doc_id: str) -> list: """Prompt to summarize a document with key points.""" return [ { "role": "user", "content": f"""Read the document '{doc_id}' using the read_doc_contents tool. Then provide: 1. **Summary** (3-5 sentences): What is this document about? 2. **Key Points**: List the most important items 3. **Next Steps**: What actions should be taken based on this? Keep the summary concise and actionable.""" } ]

3.5 Промпти в клієнті

Модуль 3 · Підключення клієнтів
Цілі уроку
  • Реалізувати list_prompts() та get_prompt() у клієнті
  • Інтегрувати slash-команди з CLI-чатботом
  • Побачити повний цикл: від slash-команди до результату

Клієнтська сторона промптів

Промпти визначені на сервері, але запускаються клієнтом. Додаємо два нових методи до MCPClient:

class MCPClient: # ... попередні методи ... async def list_prompts(self): """Get list of available prompts from the server.""" result = await self.session.list_prompts() return result.prompts async def get_prompt(self, prompt_name: str, arguments: dict): """Get a specific prompt with arguments from the server.""" result = await self.session.get_prompt( prompt_name, arguments ) return result.messages

Потік аргументів

Коли клієнт викликає get_prompt("format", {"doc_id": "notes.txt"}), відбувається наступне:

1. Клієнт надсилає: get_prompt("format", {doc_id: "notes.txt"}) 2. Сервер знаходить промпт "format" 3. Сервер викликає format_document(doc_id="notes.txt") 4. Аргумент doc_id підставляється у текст промпту: "Please read the document 'notes.txt'..." 5. Сервер повертає список messages 6. Клієнт передає messages до Claude
Ключовий концепт

Промпти -- це серверні шаблони, які клієнт може викликати з параметрами. Сервер інтерполює параметри у текст промпту та повертає готові повідомлення для AI. Це забезпечує повторне використання якісних промптів без дублювання коду на стороні клієнтів.

Інтеграція slash-команд у CLI

async def handle_slash_command( client: MCPClient, command: str ): """Handle slash commands like /format, /review, etc.""" parts = command.strip().split() prompt_name = parts[0][1:] # Видаляємо / # Отримуємо список доступних промптів prompts = await client.list_prompts() # Знаходимо потрібний промпт prompt = next( (p for p in prompts if p.name == prompt_name), None ) if not prompt: print(f"Unknown command: /{prompt_name}") print("Available commands:") for p in prompts: print(f" /{p.name} - {p.description}") return None # Збираємо аргументи arguments = {} if prompt.arguments: # Наприклад, показуємо список документів для вибору doc_list = await client.read_resource("docs://documents") print("Select a document:") for i, doc in enumerate(doc_list): print(f" [{i+1}] {doc}") choice = int(input("Your choice: ")) - 1 arguments["doc_id"] = doc_list[choice] # Отримуємо промпт з аргументами messages = await client.get_prompt(prompt_name, arguments) return messages

Повний цикл slash-команди

┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ User: │ │ Client │ │ Server │ │ Claude │ │ /format │ │ │ │ │ │ │ └─────┬──────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │── /format ────→│ │ │ │ │── list_prompts→│ │ │ │←─ [format,...] │ │ │←─ "Select doc" │ │ │ │── notes.txt ──→│ │ │ │ │── get_prompt──→│ │ │ │ ("format", │ │ │ │ {doc_id: │ │ │ │ "notes"}) │ │ │ │←─ messages ───│ │ │ │ │ │ │── messages ──────────────────→│ │ │ (Claude reads, │ │ │ formats, saves) │ │ │←─ result ────────────────────│ │←─ "Done!"──────│ │ │
Реальні приклади промптів

У реальних MCP-серверах промпти використовуються для:

  • Claude Desktop: кнопки "Chat starters" -- це промпти MCP-серверів
  • Google Drive MCP: /analyze-spreadsheet -- аналіз даних у таблиці
  • GitHub MCP: /code-review -- рецензування pull request з найкращими практиками
  • Database MCP: /optimize-query -- оптимізація SQL-запиту
Вправа: Autocomplete для промптів

Реалізуйте функцію autocomplete_command(client, partial_input), яка:

  1. Отримує список промптів через list_prompts()
  2. Фільтрує промпти, що починаються з введеного тексту
  3. Повертає список підказок

Наприклад: введення "/f" повинно запропонувати "/format".

async def autocomplete_command( client: MCPClient, partial_input: str ) -> list[str]: """Suggest matching slash commands.""" if not partial_input.startswith("/"): return [] prefix = partial_input[1:].lower() prompts = await client.list_prompts() suggestions = [ f"/{p.name} - {p.description}" for p in prompts if p.name.lower().startswith(prefix) ] return suggestions # Використання: # autocomplete_command(client, "/f") # -> ["/format - Rewrites document in Markdown"]
Перевірка знань: Модуль 3

1. Яка різниця між ресурсом та інструментом для читання даних?

Ніякої різниці
Ресурс контролюється додатком (app-controlled), інструмент -- AI-моделлю (model-controlled)
Ресурс швидший

2. Що робить MIME-тип у ресурсі?

Підказує клієнту, як десеріалізувати отримані дані
Визначає розмір відповіді
Шифрує дані

3. Хто контролює виконання промптів?

AI-модель
Код додатку
Користувач (через slash-команди або кнопки)

4. Що повертає get_prompt()?

Текстовий рядок промпту
Список messages для надсилання AI-моделі
JSON-схему промпту

4.1 Коли що використовувати

Модуль 4 · Підсумки
Цілі уроку
  • Чітко розрізняти три примітиви MCP
  • Навчитися обирати правильний примітив для кожної задачі
  • Зрозуміти принцип "хто контролює"

Фреймворк прийняття рішень

Після трьох модулів ви реалізували всі три примітиви. Але як обрати правильний для конкретної задачі? Ось простий фреймворк:

Потрібна нова ЗДАТНІСТЬ для Claude? └─→ Tool (model-controlled) Потрібні ДАНІ для вашого додатку? └─→ Resource (app-controlled) Потрібен WORKFLOW для користувача? └─→ Prompt (user-controlled)

Детальне порівняння

ХарактеристикаToolsResourcesPrompts
КонтрольAI-модельДодатокКористувач
ПризначенняДодати здатності AIНадати дані додаткуАвтоматизувати робочий процес
ТригерClaude сам обираєКод додатку запитуєКористувач натискає / набирає
ПрикладВиконати JS для обчисленьСписок документів у Google DriveКнопка "Chat starter" у Claude
Змінює стан?Може змінюватиТільки читаєЧерез tools може змінювати
Автоматичний?Так (Claude вирішує)Так (код вирішує)Ні (користувач ініціює)
Кому служитьМоделіДодаткуКористувачу

Реальні приклади

Google Drive MCP Server
  • Tools: create_file, move_file, share_file, delete_file
  • Resources: drive://files (список файлів для UI автодоповнення), drive://files/{id} (вміст файлу для контекстного меню)
  • Prompts: /analyze-spreadsheet (аналіз даних), /summarize-doc (підсумок документа)
GitHub MCP Server
  • Tools: create_issue, create_pr, add_comment, merge_pr
  • Resources: github://repos (список репозиторіїв), github://repos/{owner}/{name}/issues (список issues)
  • Prompts: /code-review (рецензування PR), /triage-issue (категоризація issue)
Database MCP Server
  • Tools: execute_query (виконати SQL), create_table, insert_row
  • Resources: db://tables (список таблиць), db://tables/{name}/schema (схема таблиці)
  • Prompts: /optimize-query (оптимізація SQL), /explain-table (пояснення структури)

Дерево рішень

Задача для MCP-сервера? │ ├── Claude повинен щось ЗРОБИТИ? │ ├── Так → TOOL │ │ Приклади: створити, оновити, видалити, │ │ відправити, обчислити │ │ │ └── Ні → Claude повинен щось ПОБАЧИТИ? │ ├── Так, але це МОЙ код вирішує коли → RESOURCE │ │ Приклади: дані для UI, контекст для промпту, │ │ автодоповнення │ │ │ └── Так, але це КОРИСТУВАЧ вирішує коли → PROMPT │ Приклади: slash-команди, кнопки, │ workflow-шаблони
Вправа: Класифікуйте примітиви

Для кожної задачі визначте, який примітив потрібен -- Tool, Resource чи Prompt:

  1. Показати список відкритих PR у sidebar додатку
  2. Автоматично виправити ESLint помилки у файлі
  3. Шаблон для написання unit-тестів за slash-командою
  4. Завантажити конфігурацію CI/CD для аналізу
  5. Відправити повідомлення у Slack-канал
  6. Кнопка "Пояснити цей код" у UI
  1. Resource -- дані для UI (app-controlled, тільки читання)
  2. Tool -- дія, що змінює файли (model-controlled)
  3. Prompt -- шаблон за запитом користувача (user-controlled)
  4. Resource -- читання даних для контексту (app-controlled)
  5. Tool -- дія з побічним ефектом (model-controlled)
  6. Prompt -- ініціюється користувачем через UI (user-controlled)

4.2 Практичні патерни інтеграції

Модуль 4 · Підсумки
Цілі уроку
  • Вивчити патерни автодоповнення та контекстної ін'єкції
  • Зрозуміти, як комбінувати примітиви
  • Спроєктувати MCP-сервер для реального сценарію

Патерн 1: Контекстна ін'єкція

Один з найпотужніших патернів -- автоматичне додавання контексту до промпту перед надсиланням Claude. Це комбінація Resources + основного запиту:

# Перед кожним запитом до Claude: async def enrich_context(client: MCPClient, user_message: str): # 1. Завантажуємо контекст через ресурси doc_list = await client.read_resource("docs://documents") # 2. Формуємо збагачений промпт system_prompt = f"""You have access to the following documents: {json.dumps(doc_list)} Use tools to read or modify them as needed.""" # 3. Надсилаємо Claude з контекстом return system_prompt
Чому це працює

Claude отримує список документів до того, як починає думати. Це як дати студенту перелік доступних підручників перед іспитом -- він знає, де шукати відповіді, і діє ефективніше.

Патерн 2: Autocomplete через Resources

Ресурси ідеально підходять для автодоповнення у UI. Замість завантаження повного вмісту -- показуємо список опцій:

# UI: Користувач вводить "/format " # -> Клієнт запитує ресурс зі списком документів # -> UI показує випадаючий список для вибору async def get_autocomplete_options( client: MCPClient, context: str ) -> list: """Get autocomplete options based on context.""" if context.startswith("/format") or context.startswith("/review"): # Показуємо список документів docs = await client.read_resource("docs://documents") return docs if context.startswith("/"): # Показуємо список команд prompts = await client.list_prompts() return [f"/{p.name}" for p in prompts] return []

Патерн 3: Комбінація примітивів

Найпотужніші сценарії виникають при комбінації всіх трьох примітивів:

Сценарій: Рецензування документа ───────────────────────────────── 1. RESOURCE (app-controlled): Завантажити список документів для UI 2. PROMPT (user-controlled): Користувач натискає /review та обирає документ 3. TOOL (model-controlled): Claude викликає read_doc_contents для читання Claude аналізує та пропонує зміни Claude викликає edit_document для збереження змін
Патерн 4: Ланцюжок серверів

Один хост може підключатися до кількох MCP-серверів одночасно. Це створює потужні інтеграції:

Host (AI-додаток) ├── MCP Client 1 → GitHub Server │ ├── Tools: create_pr, add_comment │ └── Resources: repos, issues ├── MCP Client 2 → Slack Server │ ├── Tools: send_message │ └── Resources: channels └── MCP Client 3 → Database Server ├── Tools: execute_query └── Resources: tables, schemas

Claude бачить всі інструменти з усіх серверів і може комбінувати їх: "Знайди баг у базі даних, створи GitHub issue і повідом команду у Slack" -- один запит, три сервери.

Патерн 5: Graceful degradation

Завжди враховуйте, що MCP-сервер може бути недоступним:

async def safe_read_resource(client: MCPClient, uri: str): """Read resource with fallback.""" try: return await client.read_resource(uri) except Exception as e: print(f"Warning: Could not read {uri}: {e}") return None # Додаток працює далі без цього контексту

Проєктування власного MCP-сервера

Вправа: Спроєктуйте MCP-сервер для CI/CD

Ви будуєте MCP-сервер для CI/CD системи (наприклад, GitHub Actions). Спроєктуйте:

  1. Tools (мінімум 4) -- що AI може робити?
  2. Resources (мінімум 3) -- які дані надавати?
  3. Prompts (мінімум 2) -- які workflow автоматизувати?
  4. Для кожного ресурсу вкажіть URI та MIME-тип

Tools:

  • trigger_workflow(repo, workflow_id) -- запустити пайплайн
  • cancel_run(repo, run_id) -- скасувати запуск
  • retry_job(repo, job_id) -- перезапустити невдалий крок
  • update_secret(repo, name, value) -- оновити секрет

Resources:

  • ci://repos/{owner}/{repo}/workflows (application/json) -- список workflow
  • ci://repos/{owner}/{repo}/runs (application/json) -- останні запуски
  • ci://repos/{owner}/{repo}/runs/{id}/logs (text/plain) -- логи запуску

Prompts:

  • /diagnose-failure -- проаналізувати причину збою пайплайну
  • /optimize-pipeline -- запропонувати оптимізації для швидкості
Чому це важливо?

Проєктування MCP-сервера -- це навичка, яка стає все ціннішою. Екосистема MCP зростає швидко. Якщо ви зможете створювати якісні MCP-сервери для сервісів, з якими працюєте -- ви робите свій AI-досвід та досвід інших розробників значно кращим.

4.3 Підсумковий тест

Модуль 4 · Підсумки
Перевірте свої знання

Цей тест охоплює весь курс -- від архітектури MCP до практичних патернів. Спробуйте відповісти без підглядання у попередні уроки! 8 запитань, кожне перевіряє розуміння ключових концепцій.

Підсумковий тест: Model Context Protocol

1. Яку головну проблему вирішує Model Context Protocol?

Прискорює відповіді AI-моделей
Зменшує вартість API-викликів
Стандартизує спосіб надання контексту та інструментів AI-моделям, усуваючи необхідність ручного написання tool-схем

2. Яка правильна архітектура MCP?

Host (AI-додаток) -> MCP Client -> MCP Server -> External Service
MCP Server -> MCP Client -> AI Model -> User
External Service -> Host -> MCP Server -> Client

3. Який декоратор реєструє функцію як MCP-інструмент у Python SDK?

@mcp.function()
@mcp.tool()
@mcp.action()

4. Чим відрізняється Resource від Tool для читання даних?

Resource швидший за Tool
Resource та Tool -- це синоніми
Resource контролюється додатком (app-controlled), Tool -- AI-моделлю (model-controlled)

5. Для чого потрібен Field(description=...) у параметрах інструменту?

Щоб AI розумів, яке значення передати -- це інструкція для моделі
Для генерації документації для розробників
Для валідації типів під час виконання

6. Як MCP Inspector допомагає в розробці?

Генерує код сервера автоматично
Дозволяє тестувати інструменти MCP-сервера у браузері без побудови клієнта
Профілює продуктивність сервера

7. Що повертає get_prompt() у MCP?

Текстовий рядок
JSON-схему промпту
Список messages для надсилання AI-моделі

8. Який патерн дозволяє AI використовувати інструменти з кількох різних сервісів одночасно?

Ланцюжок серверів -- один хост підключається до кількох MCP-серверів через окремі клієнти
Один великий MCP-сервер з усіма інструментами
Каскадна маршрутизація запитів між серверами

Підсумок курсу

Що ви вивчили
  • Модуль 1: Архітектура MCP (Host -> Client -> Server), три примітиви, транспорти
  • Модуль 2: Створення MCP-серверів з Python SDK, @mcp.tool(), Field descriptions, Inspector
  • Модуль 3: Побудова клієнтів, @mcp.resource(), MIME-типи, @mcp.prompt(), slash-команди
  • Модуль 4: Фреймворк вибору примітивів, патерни інтеграції, проєктування серверів
Ключові висновки
  • MCP = USB-C для AI. Один стандарт замість тисяч окремих інтеграцій.
  • Три примітиви = три рівні контролю. Tools (модель), Resources (додаток), Prompts (користувач).
  • Python SDK усуває шаблонний код. Декоратори + type hints = автоматичні JSON-схеми.
  • Проєктування MCP-сервера -- це навичка. Правильний вибір примітивів визначає якість інтеграції.

Наступні кроки

Що робити далі?
  1. Побудуйте свій MCP-сервер для сервісу, з яким працюєте щодня
  2. Дослідіть офіційні MCP-сервери на github.com/modelcontextprotocol/servers
  3. Підключіть MCP до Claude Desktop -- налаштуйте claude_desktop_config.json
  4. Вивчіть SSE транспорт для віддалених серверів
  5. Поділіться своїм сервером з спільнотою!

Часті запитання

Де знайти готові MCP-сервери?
Офіційний репозиторій: github.com/modelcontextprotocol/servers. Там є сервери для GitHub, Google Drive, Slack, PostgreSQL, Filesystem та багатьох інших. Також шукайте по тегу "mcp-server" на GitHub.
Чи можна використовувати MCP з іншими мовами, окрім Python?
Так! MCP -- це протокол, не бібліотека. Є SDK для Python, TypeScript/JavaScript, і спільнота створює SDK для інших мов. Протокол базується на JSON-RPC, тому теоретично клієнт/сервер можна написати будь-якою мовою.
Як MCP впливає на безпеку?
MCP-сервери працюють локально (через stdio) або на довірених серверах (через SSE). Дані не проходять через треті сторони. Кожен сервер має обмежений набір можливостей, визначений його інструментами та ресурсами. Рекомендується завжди перевіряти код MCP-серверів перед використанням.
Чим MCP відрізняється від OpenAI Function Calling?
OpenAI Function Calling -- це механізм виклику функцій конкретного провайдера. MCP -- це відкритий протокол, незалежний від провайдера. MCP стандартизує не тільки виклик інструментів, а й доступ до ресурсів та промптів. Також MCP визначає, хто створює інструменти (MCP-сервер), а не тільки як їх викликати.

Вітаємо!

Курс Model Context Protocol завершено!

Ви пройшли шлях від знайомства з архітектурою MCP до побудови повноцінних серверів та клієнтів. Ви знаєте три примітиви, вмієте їх реалізовувати та обирати правильний для кожної задачі.

Тепер створюйте власні MCP-сервери та розширюйте можливості AI!

Пам'ятайте: MCP -- це екосистема. Кожен ваш сервер робить AI-інструменти кращими для всіх. Приєднуйтесь до спільноти та діліться своїми рішеннями.