Головна / Статті

Створіть свій перший сервер MCP на Python: інструменти, ресурси, запити та інспектор

Створіть сервер MCP на Python за допомогою FastMCP: корисний інструмент із засобами, ресурсами та запитами, підтримкою stdio та HTTP, функцією дебаггінгу Inspector та однією конфігурацією для 3 клієнтів.

Видавець: REACTAPP.TOP9 хв читання2056 слів

Джерела: What Is MCP? Build a Custom MCP Server in Python

Уявіть собі три майже ідентичні файли на Python під назвами Claude, Cursor та ChatGPT, кожен з яких використовує API Slack, обробляє відповідь та перетворює її для конкретного клієнта ШІ. Тепер уявіть, що всі три замінюються одним файлом, з якого кожен з цих клієнтів може безпосередньо читати дані. Коли Slack змінює яке-небудь поле, його потрібно виправити лише один раз. Цей єдиний файл — це сервер MCP, і його створення займає приблизно півдня.

MCP у кількох реченнях

Протокол Model Context Protocol настільки простий, що його можна описати за три повідомлення. Клієнт під’єднується до сервера та запитує, що той може робити. Сервер відповідає про інструменти, які може використовувати модель, дані, які може отримати хост, та запити, які може обрати користувач. Потім клієнт викликає інструмент із аргументами, сервер його виконує та відповідає. У мережі це все, що потрібно.

Те, що робить MCP корисним для використання, — це не формат повідомлень, а спільна угода. Claude Code, Cursor, Copilot у VS Code та інші засоби виконують однакові процедури взаємодії, тож файл на Python, створений зараз, можна пізніше приєднати до будь-якого з них.

Сервер може пропонувати будь-який з трьох типів можливостей:

  • Інструменти — це функції, які можна викликати, і які модель обирає за назвою; наприклад, функція для пошуку в базі даних, створення гілки чи стиснення журналу подій.
  • Ресурси — це матеріали лише для читання, наприклад файл конфігурації, схема чи останні зміни, які хост завантажує, коли це потрібно.
  • Шаблони запитів — це повторно використовувані шаблони, які обирає користувач; вони відображаються як команда з слешем у Claude Code або як пункт меню у Claude Desktop.

На практиці більшість серверів постачають інструменти, а ресурси та інструкції є другорядними. Якщо ви будете вивчати лише одну з цих трьох речей, вивчайте інструменти. Те, як передаються повідомлення та як працює автентифікація, має значення пізніше. Наразі пам’ятайте це: один сервер, багато клієнтів, три речі, які потрібно зробити доступними.

Чому раніше множилися інтеграції

До появи MCP підключення AI-інструменту до бекенду означало написання адаптера між структурою вашого бекенду та форматом виклику інструментів, який потрібен був клієнту. Один клієнт очікував схему інструментів Anthropic, інший використовував власний формат, а ще один хотів формат виклику функцій OpenAI. Функція пошуку в Slack залишалася незмінною, тоді як навколо неї формувалося три шари обгортки.

Арифметика швидко стає складною. Три клієнти та два бекенди означають необхідність підтримки шести адаптерів. Четвертий клієнт збільшує їх кількість до восьми, а третій бекенд — до дванадцяти, причому кожен з них має власну систему автентифікації, механізми повторних спроб та особливості роботи. MCP розбиває цю структуру на окремі компоненти: клієнти та бекенди більше не спілкуються один з одним. Обидва спілкуються з протоколом, тож впровадження нового клієнта на основі ШІ коштує лише додавання одного запису до конфігурації, а не створення нової інтеграції.

Дизайн навмисно є асиметричним. Клієнт вирішує все: які сервери завантажувати, коли викликати інструменти та як відображати результати. Обов’язки сервера значно менші — він лише оголошує про наявність своїх інструментів та відповідає на запити. Саме ця асиметрія дозволяє протоколу залишатися достатньо компактним, щоб кожен великий клієнт міг його швидко реалізувати.

Допоміжний інструмент для standup у одному файлі

Допоміжний інструмент для стендапу — це класичний внутрішній інструмент, який інженери постійно переписують. Він читає локальний журнал змін git та узагальнює те, що розробник робив учора. Тут він буде представлений як інструмент, ресурс та запит у одному файлі Python.

Вам знадобиться пакет FastMCP — високорівневий шар Python, на якому базуються багато серверів MCP.

pip install fastmcp

Створіть standup_server.py. Спочатку потрібно імпортувати бібліотеку та визначити інструмент.

# standup_server.py
import subprocess
from typing import TypedDict

from fastmcp import FastMCP

mcp = FastMCP("standup-helper")


class StandupSummary(TypedDict):
    branch: str
    since: str
    commit_count: int
    commits: list[str]


@mcp.tool()
def summarize_standup(
    branch: str = "main",
    since: str = "yesterday",
) -> StandupSummary:
    """Summarize recent git activity for a standup.

    Reads the local git log on the given branch since the
    given time window. Returns commit count and one-line
    subjects for each commit. Used by AI clients via MCP.
    """
    try:
        result = subprocess.run(
            [
                "git", "log",
                f"--since={since}",
                "--pretty=format:%h %s",
                branch,
            ],
            capture_output=True,
            text=True,
            timeout=5,
            check=True,
        )
    except (subprocess.CalledProcessError,
            subprocess.TimeoutExpired) as exc:
        return {
            "branch": branch,
            "since": since,
            "commit_count": 0,
            "commits": [f"git error: {exc}"],
        }

    lines = [
        line for line in result.stdout.splitlines() if line
    ]
    return {
        "branch": branch,
        "since": since,
        "commit_count": len(lines),
        "commits": lines,
    }


# resources and prompts come next

У закритому режимі декоратор виконує чотири завдання. Він читає підказки щодо типів, щоб створити схему JSON, використовує документацію як опис, який відображається моделі, включає інструмент до списку можливостей сервера під час встановлення зв’язку та направляє надходжені виклики JSON-RPC до відповідної функції.

Зверніть увагу на виклик підпроцесу з суворим таймаутом у п’ять секунд. Якщо команда git застрягне, основне додаток, яке чекає на відповідь інструменту, також застрягне разом з нею, тому кожен зовнішній виклик потребує таймауту. Відгалуження з помилкою також повертає ту саму структуру словника, що й відгалуження з успіхом, тож модель завжди отримує передбачувану структуру. Більш повний інструмент також може викликати GitHub CLI для створення відкритих запитів на підтягування коду, додавши ще один виклик підпроцесу та додавши результат до словника.

Далі йдуть ресурс та запит, які завершують файл.

# standup_server.py (continued)

@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
    """Last 10 commits on the main branch, plain text.

    Resources are pulled by the host opportunistically.
    They are not invoked by the model the way tools are.
    """
    result = subprocess.run(
        [
            "git", "log",
            "-n", "10",
            "--pretty=format:%h %ad %s",
            "--date=short",
            "main",
        ],
        capture_output=True,
        text=True,
        timeout=5,
    )
    return result.stdout or "(no commits found)"


@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
    """Reusable standup question exposed as a prompt
    template. Surfaces as a slash command in clients that
    expose prompts (e.g. /standup_template in Claude Code).
    """
    return (
        f"Summarize what I worked on yesterday, focusing on "
        f"{focus}. Use the summarize_standup tool to get the "
        f"git log, then write a one-paragraph standup note."
    )


if __name__ == "__main__":
    mcp.run()

URI ресурсу — це просто рядок. Його форму не регулює жодна сувора схема, хоча загальноприйнятою формою є схема, яка слідує за шляхом, а FastMCP підтримує шаблони URI у разі потреби у параметрах. Пrompt — це просто функція, яка повертає сформатований текст. Клієнти представляють його як шаблон, який використовує користувач, а Claude Code відображає його як команду з слешем. Пrompts ніколи не вставляються автоматично, тому користувач завжди обирає їх сам.

Останній рядок запускає сервер. Не потрібен порт, жодна веб-фреймворк та жоден цикл подій для керування, оскільки FastMCP відповідає за роботу з вхідним/вихідним виводом.

Транспорти та автентифікація

Транспорт визначає, як повідомлення JSON-RPC передаються між клієнтом та сервером.

Використовуйте stdio для локальних серверів. Хост запускає ваш скрипт як дочірній процес. Stdio оминає весь мережевий стек: немає порту, немає TLS та немає автентифікації, оскільки дочірній процес успадковує межі довіри хоста.

Використовуйте Streamable HTTP, коли сервер має зберігати стан між запитами або має бути доступний з кількох машин. Віддалений сервер знаходиться у мережі, тому йому потрібна сувора автентифікація.

# bottom of standup_server.py

if __name__ == "__main__":
    # Default transport is stdio. The host (Claude Code,
    # Cursor, Claude Desktop, etc.) launches this script
    # as a subprocess and talks to it over stdin/stdout.
    # No port, no TLS, no auth. The trust boundary is
    # whoever launched the host.
    mcp.run()

    # To expose the same server over the network instead,
    # use Streamable HTTP. SSE was deprecated in the
    # March 2025 spec update. Do not use it for new code.
    #
    # Production HTTP also needs an auth layer in front.
    # OAuth 2.1 with Dynamic Client Registration is the
    # current pattern. See Week 22 for the full flow.
    #
    # mcp.run(
    #     transport="streamable-http",
    #     host="0.0.0.0",
    #     port=8000,
    # )

Виклик функції run без аргументів означає використання stdio, а прокоментований блок показує, як виглядає процес оновлення через HTTP. У цій версії залишається використання stdio, що є правильним вибором для більшості внутрішніх інструментів розробників. Інструмент, який читає лише локальні файли або локальний репозиторій, зовсім не повинен працювати через мережевий інтерфейс.

Цикл локальної розробки

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

Замість цього використовуйте офіційний MCP Inspector — інтерфейс розробки у браузері, який взаємодіє з вашим сервером через stdio.

npx @modelcontextprotocol/inspector python standup_server.py

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

У цьому циклі зазвичай виникають три проблеми:

  1. Невідповідність схеми. Значення, яке повертає функція, не збігається з її типовою анотацією, і інспектор негайно на це вказує.
  2. Відсутня можливість. Ви викликаєте щось, що сервер ніколи не оголошував, і панель керування показує те, що насправді було зареєстровано під час переговорів.
  3. Забруднення вихідних даних. Будь-який випадковий виклик print() пошкоджує потік JSON-RPC, оскільки через stdio передаються повідомлення. Направляйте журнали до стандартного вихіду за допомогою модуля logging у Python, який протокол призначає для читабельної діагностики.

FastMCP також пропонує режим розробки, який перезапускає сервер при зміні файлу, що допомагає під час роботи в інспекторі.

Один сервер, кілька клієнтів

Тепер дизайн справді виправдовує себе. Існує єдиний файл на Python, який перевіряється в інспекторі, і його можна під’єднати до різних клієнтів AI без будь-якого коду, специфічного для клієнта. Кожен клієнт читає файл конфігурації, у якому вказані локальні сервери для запуску, і структура JSON залишається однаковою у Claude Code, Cursor та Claude Desktop: об’єкт mcpServers, який містить команду для виконання та її аргументи.

{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}

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

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

ChatGPT не входить до цього списку, оскільки його з’єднувачі працюють із віддаленими серверами через Streamable HTTP. Щоб інтегрувати цей помічник у нього, знадобиться публічна URL та шар автентифікації, що значно відрізняється від суто локальної конфігурації.

Коли не варто використовувати MCP

Цей протокол — не універсальний інструмент. Деякі ситуації погано підходять для його використання:

  • Шляхи, дуже чутливі до затримок. Кожен виклик інструменту передбачає подвійну передачу даних у форматі JSON-RPC, тому інструменти, які використовуються у вузьких внутрішніх циклах, повинні використовувати пряму інтеграцію з SDK.
  • Індивідуальний інтерактивний вивід. Якщо вам потрібен певний графік або спеціальна форма, вам доводиться створювати інтеграцію з хостом, оскільки протокол розділяє дані та їх візуалізацію.
  • Налаштування під ваш контроль. Якщо одна команда розробляє як клієнт AI, так і бекенд, їх пряме підключення може бути простішим у обслуговуванні, ніж використання MCP між ними.

Невеликий протокол, великі зміни

Усю схему протоколу складають три примітиви, два засоби передачі даних та один процес взаємодії JSON-RPC. Саме така простота є його силою: вона розділяє тих, хто створює інтелект, від тих, хто надає контекст. Написання першого сервера займає кілька годин, але це окупається щоразу, коли з’являється новий інструмент ШІ.