Перейти к содержанию

Автоматический перевод

Эта статья была автоматически переведена с оригинальной английской версии.

Руководство по MCP Server с uv и FastMCP: создаем сервер FeatureStoreLite

В этом руководстве по MCP server мы создадим небольшой сервер feature store на FastMCP, запустим его с uv и подключим к Claude Desktop.


Что такое MCP server?

Model Context Protocol (MCP) — это открытый стандарт для подключения AI-ассистентов вроде Claude к внешним данным и инструментам. Один протокол, один набор соглашений вместо отдельной интеграции для каждого инструмента, который вы хотите открыть.

В этом руководстве мы создадим MCP-сервер FeatureStoreLite. Он находится между LLM и feature store (базой данных с заранее вычисленными ML-признаками) и предоставляет инструменты для чтения и записи векторов признаков с ключами по пользователю, продукту или документу.

Зачем это делать?

Представьте, что вы ML-инженер и отлаживаете пайплайн. Вместо того чтобы идти в SQL или писать быстрый скрипт для проверки значений признаков, вы спрашиваете Claude напрямую: "Какой вектор признаков у user_123?" или "Покажи метаданные для product_abc." Именно MCP server делает это возможным.

Зачем использовать uv?

Мы будем использовать uv — установщик Python-пакетов и менеджер проектов, который работает значительно быстрее pip и в одном инструменте закрывает разрешение зависимостей и virtualenv. Вторая причина: uv run --with mcp[cli] ... позволяет позже объявить зависимости прямо в конфигурации Claude Desktop, поэтому не нужно отдельно поддерживать venv в актуальном состоянии.

Обзор архитектуры

Четыре компонента и как они связаны:

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

  1. Пользователь задает вопрос на естественном языке.
  2. Claude Desktop — это MCP-клиент. Он выбирает инструмент по вопросу и вызывает его.
  3. Наш сервер FastMCP публикует get_feature и store_feature как MCP-инструменты.
  4. SQLite выступает в роли хранилища для векторов признаков.

2. Настройка и установка

2.1. Установка uv

Если у вас еще нет uv, установите его. Остальная часть руководства предполагает, что он доступен в вашем PATH.

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Or via Homebrew
brew install uv

2.2. Инициализация проекта

Создайте новую директорию и инициализируйте Python-проект. uv init создаст для вас pyproject.toml.

# Create project directory
mkdir mcp-featurestore
cd mcp-featurestore

# Initialize Python project
uv init

# Add the MCP SDK with CLI tools
uv add "mcp[cli]"

3. Создание сервера

Два файла, разделенных по ответственности:

  1. database.py отвечает за операции SQLite.
  2. featurestore_server.py определяет MCP-сервер.

3.1. Слой базы данных (database.py)

Этот модуль управляет подключением к SQLite и содержит пару вспомогательных функций. Мы инициализируем его двумя тестовыми строками, чтобы серверу было что возвращать при первом запросе.

Создайте database.py:

# database.py
import json
import os
import sqlite3


def get_db_path() -> str:
    """Get the database path - always in the script's directory"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(script_dir, "features.db")


def init_db() -> None:
    """Initialize the feature store database with table and sample data"""
    conn = sqlite3.connect(get_db_path())
    conn.execute("""
        CREATE TABLE IF NOT EXISTS features (
            key TEXT PRIMARY KEY,
            vector TEXT NOT NULL,
            metadata TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    """)

    # Sample data for experimentation
    example_features = [
        (
            "user_123",
            "[0.1, 0.2, -0.5, 0.8, 0.3, -0.1, 0.9, -0.4]",
            json.dumps({"type": "user", "id": 123, "segment": "premium"}),
        ),
        (
            "product_abc",
            "[0.7, -0.3, 0.4, 0.1, -0.8, 0.6, 0.2, -0.5]",
            json.dumps({"type": "product", "id": "abc", "category": "electronics"}),
        ),
    ]

    # Insert if not exists
    for key, vector, metadata in example_features:
        try:
            conn.execute(
                "INSERT INTO features (key, vector, metadata) VALUES (?, ?, ?)",
                (key, vector, metadata),
            )
        except sqlite3.IntegrityError:
            pass  # Already exists

    conn.commit()
    conn.close()


def get_db_connection() -> sqlite3.Connection:
    """Get a database connection"""
    return sqlite3.connect(get_db_path())


if __name__ == "__main__":
    init_db()
    print("✅ Database initialized successfully!")

Инициализируйте базу данных:

uv run python database.py

3.2. MCP-сервер (featurestore_server.py)

FastMCP берет на себя основную часть работы. Вы декорируете обычную Python-функцию, и она регистрируется как MCP-инструмент или ресурс. Docstring становится описанием, которое видит LLM, поэтому пишите его для LLM, а не только для людей.

Создайте featurestore_server.py:

# featurestore_server.py
import json
from mcp.server.fastmcp import FastMCP
from database import get_db_connection, init_db

# Initialize the MCP Server
mcp = FastMCP("FeatureStoreLite")

# Ensure DB is ready when server starts
init_db()


@mcp.resource("schema://main")
def get_schema() -> str:
    """
    Resource: Provide the database schema.
    Resources are passive data that LLMs can read like files.
    """
    conn = get_db_connection()
    try:
        schema = conn.execute(
            "SELECT sql FROM sqlite_master WHERE type='table'"
        ).fetchall()
        return "\n".join(sql[0] for sql in schema if sql[0]) or "No tables found."
    finally:
        conn.close()


@mcp.tool()
def store_feature(key: str, vector: str, metadata: str | None = None) -> str:
    """
    Tool: Store a feature vector.
    Tools are executable functions that LLMs can call to perform actions.
    """
    conn = get_db_connection()
    try:
        # Validate that vector is valid JSON
        json.loads(vector)

        conn.execute(
            "INSERT OR REPLACE INTO features (key, vector, metadata) VALUES (?, ?, ?)",
            (key, vector, metadata),
        )
        conn.commit()
        return f"Successfully stored feature '{key}'"
    except json.JSONDecodeError:
        return "Error: Vector must be a valid JSON array string (e.g., '[0.1, 0.2]')"
    except Exception as e:
        return f"Error: {str(e)}"
    finally:
        conn.close()


@mcp.tool()
def get_feature(key: str) -> str:
    """
    Tool: Retrieve a feature vector by key.
    """
    conn = get_db_connection()
    try:
        row = conn.execute(
            "SELECT vector, metadata FROM features WHERE key = ?", (key,)
        ).fetchone()

        if row:
            return json.dumps(
                {
                    "key": key,
                    "vector": json.loads(row[0]),
                    "metadata": json.loads(row[1]) if row[1] else None,
                },
                indent=2,
            )
        return f"Feature '{key}' not found."
    finally:
        conn.close()


@mcp.tool()
def list_features() -> str:
    """
    Tool: List all available feature keys.
    """
    conn = get_db_connection()
    try:
        rows = conn.execute("SELECT key FROM features").fetchall()
        return json.dumps([row[0] for row in rows])
    finally:
        conn.close()


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

4. Тестирование с MCP Inspector

Перед подключением к Claude стоит выполнить базовую проверку сервера через MCP Inspector. Это небольшой web UI для прямого вызова инструментов и чтения ресурсов.

uv run mcp dev featurestore_server.py

Команда запускает сервер и открывает Inspector в браузере (обычно по адресу http://localhost:5173).

Inspector

Вызовите get_feature с key="user_123". Если в ответ приходит JSON для тестовой строки, сервер работает.


5. Подключение к Claude Desktop

Когда Inspector подтвердит, что сервер работает, зарегистрируйте его в Claude Desktop.

5.1. Настройка Claude

Отредактируйте файл конфигурации Claude Desktop:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%/Claude/claude_desktop_config.json

Добавьте ваш сервер в объект mcpServers:

{
    "mcpServers": {
        "featurestore": {
            "command": "uv",
            "args": [
                "run",
                "--with",
                "mcp[cli]",
                "mcp",
                "run",
                "/ABSOLUTE/PATH/TO/mcp-featurestore/featurestore_server.py"
            ]
        }
    }
}

Важно: путь к featurestore_server.py должен быть абсолютным. Относительный путь завершится тихой ошибкой, потому что Claude Desktop запускается из другой рабочей директории.

5.2. Как работает взаимодействие

Вот что происходит end-to-end, когда Claude нужен lookup признаков:

Рабочий процесс MCP

  1. Claude видит доступные инструменты (get_feature, list_features и так далее).
  2. Он решает, что для ответа нужен доступ к данным из feature store.
  3. Он формирует вызов инструмента и отправляет его вашему серверу.
  4. Сервер выполняет Python-функцию и возвращает результат.
  5. Claude использует этот результат, чтобы сформировать финальный ответ.

5.3. Примеры запросов

Перезапустите Claude Desktop и попробуйте несколько промптов:

  1. "List all available features." Вопрос 2

  2. "Get the feature vector for user_123." Вопрос 3

  3. "Store a new feature for 'new_item' with vector [0.5, 0.5] and metadata {'type': 'test'}."


6. Устранение неполадок

Несколько полезных сценариев сбоев, о которых стоит знать:

  • "Server connection failed":

    • Проверьте логи по пути ~/Library/Logs/Claude/mcp.log на macOS.
    • Убедитесь, что в конфигурации используется абсолютный путь, а не относительный.
    • Убедитесь, что uv доступен в PATH процесса Claude Desktop. Если нет, укажите полный путь к бинарнику (which uv покажет, где он находится).
  • "Tool execution error":

    • Воспроизведите ошибку в Inspector через uv run mcp dev featurestore_server.py. Inspector показывает исходную ошибку, которую Claude Desktop обычно скрывает.
    • Проверьте, что features.db создается рядом с database.py. Если рабочая директория меняется, именно относительный к скрипту путь в get_db_path() спасает ситуацию.

7. Заключение

Вот и все: сервер FastMCP, SQLite как backend-хранилище и конфигурация Claude Desktop, указывающая на команду uv run. Та же схема подходит для чего угодно, что можно обернуть в Python-функцию. Замените вызовы SQLite на настоящий feature store, внутренний API или registry моделей — и сервер останется небольшим.

Ссылки