MCP Resources: как дать LLM доступ к данным через Model Context Protocol | AiManual
AiManual Logo Ai / Manual.
12 Фев 2026 Гайд

MCP: Полное руководство по Resources (ресурсам) — как дать LLM «глаза» для чтения данных

Подробный гайд по MCP Resources — объясняем разницу с Tools, показываем код на FastMCP, учим давать LLM «глаза» для чтения файлов, баз данных и API.

Зачем LLM нужны «глаза» и почему Tools не справляются

Представьте, что вы даете ChatGPT задание: «Проанализируй наш лог-файл за сегодня». Модель вежливо отвечает: «Я не могу читать файлы». Классическая проблема. Tools (инструменты) в MCP решают ее частично — они позволяют LLM выполнять действия. Но есть нюанс.

Tools — это глаголы. «Прочитать файл», «Выполнить запрос к БД», «Получить данные из API». Resources — это существительные. «Вот этот конкретный файл», «Эта таблица в базе», «Этот эндпоинт». Разница фундаментальная.

На 12.02.2026 Model Context Protocol (MCP) версии 1.2 поддерживает два типа Resources: статические (static) и динамические (dynamic). Статические регистрируются один раз при запуске сервера, динамические создаются по запросу через Tools.

Resources против Tools: битва концепций

Давайте разберем на примере. Допустим, у вас есть папка с документами для RAG-системы. Как LLM должна с ними работать?

Подход с Tools Подход с Resources
LLM вызывает tool «read_file» с путем LLM видит ресурс «file://docs/contract.pdf»
Нужно знать точный путь к файлу Ресурсы перечислены в контексте
Каждый запрос — отдельное действие Ресурс доступен для ссылок в диалоге
Нет структуры каталогов Можно организовать по URI-схемам

Проблема Tools в том, что они требуют от LLM точного знания о том, что существует. Модель должна догадаться, что есть файл «/var/log/app/error.log». Resources решают это — они показывают, что доступно. Как карта перед путешественником.

URI в MCP: не просто протокол, а философия

Каждый Resource в MCP имеет URI. Это не случайно. URI — универсальный идентификатор, который понимают и люди, и машины. На 12.02.2026 MCP поддерживает несколько схем:

  • file:// — локальные файлы и директории
  • postgresql:// — таблицы и представления БД
  • http:// и https:// — веб-ресурсы
  • s3:// — объекты в облачном хранилище
  • custom:// — ваши собственные схемы

URI делает Resources самодокументируемыми. «postgresql://prod/users?limit=100» сразу говорит: это таблица users из продакшн-базы, первые 100 записей.

💡
Если вы работаете с длинными PDF-документами, Resources идеально подходят для организации доступа к ним. Вместо того чтобы заставлять LLM каждый раз вызывать tool для чтения файла, вы регистрируете ресурсы на все PDF в директории. LLM видит их список и может обращаться к конкретным документам по URI.

FastMCP: Resources в три строчки кода

FastMCP — самый популярный фреймворк для создания MCP-серверов на Python. На 12.02.2026 актуальная версия — 0.9.8 с полной поддержкой MCP 1.2. Давайте посмотрим, как регистрировать Resources.

1 Статические Resources: файловая система

Самый простой случай — дать LLM доступ к директории с документами:

from fastmcp import FastMCP
import os

mcp = FastMCP("File Resources Example")

# Регистрируем все PDF-файлы в директории
@mcp.resource("file://docs/{filename}")
def get_pdf_resource(filename: str):
    filepath = f"docs/{filename}"
    if not os.path.exists(filepath):
        return None
    
    with open(filepath, 'rb') as f:
        content = f.read()
    
    return {
        "contents": [{
            "uri": f"file://docs/{filename}",
            "mimeType": "application/pdf",
            "text": extract_text_from_pdf(content)  # ваша функция
        }]
    }

# Регистрируем директорию как ресурс
@mcp.resource("file://docs/")
def list_docs():
    files = os.listdir("docs")
    return {
        "contents": [{
            "uri": f"file://docs/{f}",
            "name": f,
            "description": f"Документ {f}"
        } for f in files if f.endswith('.pdf')]
    }

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

Теперь LLM видит ресурс «file://docs/» — список всех PDF. И может обратиться к конкретному файлу «file://docs/contract_2026.pdf». Без вызова tools, просто по ссылке.

2 Динамические Resources: база данных

Статические ресурсы хороши для неизменных данных. Но что если нужно дать доступ к результатам запроса? Здесь нужна связка Tool + Resource.

from fastmcp import FastMCP
import psycopg2
from datetime import datetime

mcp = FastMCP("Database Resources")

# Tool для создания ресурса с результатами запроса
@mcp.tool()
def query_users(role: str = None, limit: int = 50):
    """Выполнить запрос к таблице users и создать ресурс с результатами"""
    conn = psycopg2.connect("dbname=prod user=admin")
    cursor = conn.cursor()
    
    query = "SELECT id, name, email, role FROM users"
    params = []
    
    if role:
        query += " WHERE role = %s"
        params.append(role)
    
    query += f" LIMIT {limit}"
    cursor.execute(query, params)
    
    results = cursor.fetchall()
    conn.close()
    
    # Создаем уникальный URI для этого запроса
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    resource_uri = f"postgresql://prod/users/query_{timestamp}"
    
    # Регистрируем динамический ресурс
    @mcp.resource(resource_uri)
    def get_query_results():
        return {
            "contents": [{
                "uri": resource_uri,
                "mimeType": "application/json",
                "text": json.dumps([
                    {"id": r[0], "name": r[1], "email": r[2], "role": r[3]}
                    for r in results
                ])
            }]
        }
    
    return {
        "resource_uri": resource_uri,
        "count": len(results)
    }

LLM вызывает tool «query_users» с параметрами. Tool выполняет запрос и регистрирует новый Resource с результатами. Теперь модель может ссылаться на этот ресурс в диалоге: «Согласно данным в postgresql://prod/users/query_20260212_143022...»

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

Практический кейс: HR-автоматизация с Resources

Вернемся к примеру из статьи про HR-автоматизацию. Вместо того чтобы загружать резюме через tools каждый раз, создаем MCP-сервер с Resources:

from fastmcp import FastMCP
import sqlite3
from pathlib import Path

mcp = FastMCP("HR Resume Resources")

# Ресурс: все резюме в базе
@mcp.resource("hrdb://resumes/")
def list_all_resumes():
    conn = sqlite3.connect("hr_database.db")
    cursor = conn.cursor()
    cursor.execute("""
        SELECT id, candidate_name, position, date_added 
        FROM resumes 
        ORDER BY date_added DESC
    """)
    
    resumes = cursor.fetchall()
    conn.close()
    
    return {
        "contents": [{
            "uri": f"hrdb://resumes/{r[0]}",
            "name": f"{r[1]} - {r[2]}",
            "description": f"Добавлено: {r[3]}"
        } for r in resumes]
    }

# Ресурс: конкретное резюме
@mcp.resource("hrdb://resumes/{resume_id}")
def get_resume(resume_id: str):
    conn = sqlite3.connect("hr_database.db")
    cursor = conn.cursor()
    cursor.execute("""
        SELECT candidate_name, position, experience, skills, raw_text
        FROM resumes WHERE id = ?
    """, (resume_id,))
    
    result = cursor.fetchone()
    conn.close()
    
    if not result:
        return None
    
    return {
        "contents": [{
            "uri": f"hrdb://resumes/{resume_id}",
            "mimeType": "application/json",
            "text": json.dumps({
                "name": result[0],
                "position": result[1],
                "experience": result[2],
                "skills": result[3],
                "full_text": result[4]
            })
        }]
    }

# Tool для поиска по резюме
@mcp.tool()
def search_resumes(skill: str = None, min_experience: int = None):
    """Найти резюме по критериям и создать ресурс с результатами"""
    # ... логика поиска ...
    # Создаем динамический ресурс с результатами
    return {"resource_uri": "hrdb://search/results_..."}

HR-менеджер говорит LLM: «Посмотри резюме на позицию Senior DevOps». Модель видит ресурс «hrdb://resumes/», выбирает подходящие кандидатов, создает новый ресурс с отфильтрованным списком. Все прозрачно, все по ссылкам.

Ошибки, которые все совершают (и как их избежать)

Ошибка 1: Регистрировать все подряд как Resources

Не нужно превращать каждый инструмент в ресурс. Resources — для данных, которые имеют идентификатор (URI) и могут быть прочитаны. Системный вызов «перезагрузить сервис» — это Tool. Лог-файл «/var/log/nginx/access.log» — это Resource.

Ошибка 2: Игнорировать MIME-типы

MCP 1.2 требует указывать mimeType для содержимого ресурса. Если не указать, клиент (Claude, Cursor, и т.д.) не поймет, как обрабатывать данные. Для JSON — «application/json», для текста — «text/plain», для PDF — «application/pdf».

# ПЛОХО
return {"contents": [{"uri": "...", "text": data}]}

# ХОРОШО
return {
    "contents": [{
        "uri": "...",
        "mimeType": "application/json",
        "text": json.dumps(data)
    }]
}

Ошибка 3: Забывать про аутентификацию

Resources с доступом к базам данных или API требуют credentials. Никогда не хардкодьте пароли в код. Используйте переменные окружения, секреты, или интеграцию с MCP Hangar для централизованного управления доступом.

Resources и семантический пайплайн

Если вы строите семантический пайплайн для LLM, Resources становятся естественными точками входа. Каждый этап обработки данных может регистрировать ресурс:

  1. Сырые данные → Resource «raw://dataset/2026-02/»
  2. После очистки → Resource «cleaned://dataset/2026-02/»
  3. После векторизации → Resource «embeddings://dataset/2026-02/»
  4. После индексации → Resource «index://dataset/2026-02/»

LLM видит всю цепочку. Может сказать: «Возьми данные из cleaned://..., создай embeddings, положи в index://...». Прозрачно, отслеживаемо, без магии.

Будущее Resources: что ждет к 2027 году

На 12.02.2026 MCP Resources — уже мощный инструмент. Но что дальше? По слухам (и по issue в репозитории MCP):

  • Версионирование ресурсов — «file://docs/contract.pdf@v2»
  • Дифференциальный доступ — только metadata без полного содержимого
  • Подписки на изменения — push-уведомления при обновлении ресурса
  • Кросс-серверные ссылки — ресурс с одного MCP-сервера ссылается на ресурс с другого

Самое интересное — интеграция с RLM (Recursive Language Model). Представьте: LLM не только читает ресурсы, но и создает новые ресурсы (отчеты, аналитику), которые становятся доступны другим LLM. Рекурсивная система, где данные порождают данные.

💡
Совет напоследок: начните с малого. Возьмите одну директорию с документами, сделайте MCP-сервер с Resources. Подключите к Claude Desktop. Увидите, как меняется взаимодействие. LLM перестает спрашивать «какой файл прочитать?» — она видит список и выбирает сама. Это и есть «глаза».

P.S. Если ваша LLM все еще «слепая» — время дать ей Resources. Не Tools, которые она должна угадывать, а Resources, которые она может видеть. Разница как между «ищи иголку в стоге сена» и «вот карта стога, вот где иголки».