Перейти до змісту

Ресурси

Машинний переклад

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

Ресурс — це дані, які ви надаєте застосунку для читання.

У цьому й полягає розмежування. Інструмент — це те, що вирішує викликати модель. Ресурс — це те, що вирішує завантажити застосунок (файл конфігурації, запис, документ) і покласти перед моделлю як контекст.

Щоб оголосити ресурс, повісьте @mcp.resource(uri) на звичайну функцію Python.

Ваш перший ресурс

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.resource("config://app")
def get_config() -> str:
    """The active shop configuration."""
    return "theme=dark\nlanguage=en"

Форма та сама, що й в інструмента, плюс одна річ: URI. Ресурси мають адресу, а не ім'я. Клієнт запитує config://app, а не get_config.

Решту SDK, як і раніше, зчитує з функції:

  • Ім'я — це ім'я функції: get_config.
  • Опис, який бачить клієнт, — це docstring.
  • Вміст — те, що ви повертаєте.

Під час resources/list клієнт отримує ось що:

{
  "name": "get_config",
  "uri": "config://app",
  "description": "The active shop configuration.",
  "mimeType": "text/plain"
}

А коли він читає config://app, виконується ваша функція, і повернене значення приходить назад як текст:

result.contents  # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]

Tip

Перелік коштує дешево. Ваша функція не викликається під час resources/list — лише під час resources/read, і лише для запитаного URI. Надайте тисячу ресурсів — і платитимете тільки за ті, які хтось відкриє.

Спробуйте самі

Запустіть сервер у MCP Inspector:

uv run mcp dev server.py

Відкрийте URL, який він виведе, і перейдіть на вкладку Resources. config://app є в списку разом з описом. Клацніть його — Inspector його прочитає: ось ваші два рядки конфігурації.

Шаблони ресурсів

Один URI на запис не масштабується. Додайте в URI заповнювач і відповідний параметр у функцію:

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.resource("config://app")
def get_config() -> str:
    """The active shop configuration."""
    return "theme=dark\nlanguage=en"


@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
    """A customer's profile."""
    return f"User {user_id}: 12 orders since 2021."

{user_id} в URI, user_id: str у функції. Оце й увесь контракт.

Тепер це шаблон ресурсу, і він переїжджає: зникає з resources/list і натомість з'являється в resources/templates/list — як зразок, а не як адреса:

{
  "name": "get_user_profile",
  "uriTemplate": "users://{user_id}/profile",
  "description": "A customer's profile.",
  "mimeType": "text/plain"
}

Клієнт підставляє значення замість заповнювача й читає конкретний URI: users://42/profile, users://ada/profile. На всі відповідає одна функція, а зіставлене значення передається як user_id:

result.contents  # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]

Зверніть увагу на uri в результаті. Це конкретний URI, який запитав клієнт, а не шаблон.

Check

Заповнювачі та параметри мають збігатися. Перейменуйте параметр функції на user, поки в URI досі вказано {user_id}, — і декоратор відмовить під час імпорту, задовго до того, як до нього наблизиться будь-який клієнт:

ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}

Розбіжність може бути лише помилкою, тож SDK робить неможливим запуск сервера з нею.

Синтаксис заповнювачів — RFC 6570: {+path} для багатосегментних значень, {?q,lang} для необов'язкових параметрів запиту тощо. Крім того, SDK за замовчуванням застосовує до видобутих значень перевірки безпеки шляхів. Повний довідник — на сторінці Шаблони URI та безпека шляхів.

get_user_profile також може приймати параметр з анотацією Context. SDK впроваджує його, ніколи не трактуючи як параметр URI, а що саме він дає — описано на сторінці Об'єкт Context.

Що повертати

Ви не обмежені типом str. Задайте кожному ресурсу mime_type і повертайте те, що пасує:

server.py
import base64

from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.resource("docs://readme", mime_type="text/markdown")
def readme() -> str:
    """How to use this server."""
    return "# Bookshop\n\nSearch the catalog with the `search_books` tool."


@mcp.resource("stats://catalog", mime_type="application/json")
def catalog_stats() -> dict[str, int]:
    """Live counts for the catalog."""
    return {"books": 1204, "authors": 391}


@mcp.resource("covers://placeholder", mime_type="image/gif")
def placeholder_cover() -> bytes:
    """A 1x1 transparent GIF, shown when a book has no cover."""
    return base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
  • readme повертає str, тож його надсилають як є. Це найпоширеніший випадок.
  • catalog_stats повертає dict, тому SDK серіалізує його в JSON-текст за вас:

    {
      "books": 1204,
      "authors": 391
    }
    
  • placeholder_cover повертає bytes, тож клієнт отримує BlobResourceContents замість TextResourceContents, а ваші байти закодовано в base64 у полі blob.

Те саме правило стосується всього, що серіалізується в JSON: списку, моделі Pydantic, dataclass. Якщо це не str і не bytes, воно стає JSON.

mime_type ви оголошуєте самі, і за замовчуванням це text/plain. SDK ніколи не аналізує повернене значення, щоб його вгадати, тож ресурс із dict, який ви не позначили, усе одно оголошується як звичайний текст.

Tip

@mcp.resource() також приймає name=, title= і description=, коли їх не хочеться виводити з функції. А коли функцію взагалі писати не треба, у mcp.server.mcpserver.resources є готові класи Resource (TextResource, BinaryResource, FileResource, HttpResource, DirectoryResource), які реєструють через mcp.add_resource(...).

Клієнт також може підписатися на ресурс і отримувати сповіщення про його зміни; це клієнтська половина історії, і описана вона на сторінці Клієнт.

Підсумки

  • @mcp.resource(uri) на функції робить її ресурсом. URI — це адреса, повернене значення — вміст, docstring — опис.
  • {placeholder} в URI перетворює його на шаблон: він потрапляє в resources/templates/list, і одна функція обслуговує всі URI, що збігаються.
  • Імена заповнювачів мають дорівнювати іменам параметрів функції. Помилитеся — і дізнаєтеся про це під час імпорту, а не в продакшені.
  • Ваша функція виконується, коли ресурс читають, а не коли його перелічують.
  • str стає текстом, bytes — base64-блобом, усе інше — JSON-текстом. mime_type= — це те, як ви його позначаєте.
  • Інструменти — щоб модель діяла. Ресурси — щоб застосунок читав.

Третій примітив, той, що його людина вибирає з меню, — це Промпти.