Автоматический перевод
Эта статья была автоматически переведена с оригинальной английской версии.
Руководство по 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 в актуальном состоянии.
Обзор архитектуры
Четыре компонента и как они связаны:
- Пользователь задает вопрос на естественном языке.
- Claude Desktop — это MCP-клиент. Он выбирает инструмент по вопросу и вызывает его.
- Наш сервер
FastMCPпубликуетget_featureиstore_featureкак MCP-инструменты. - 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. Создание сервера
Два файла, разделенных по ответственности:
database.pyотвечает за операции SQLite.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).

Вызовите 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 признаков:
- Claude видит доступные инструменты (
get_feature,list_featuresи так далее). - Он решает, что для ответа нужен доступ к данным из feature store.
- Он формирует вызов инструмента и отправляет его вашему серверу.
- Сервер выполняет Python-функцию и возвращает результат.
- Claude использует этот результат, чтобы сформировать финальный ответ.
5.3. Примеры запросов
Перезапустите Claude Desktop и попробуйте несколько промптов:
-
"List all available features."

-
"Get the feature vector for user_123."

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