Tradução automática
Este artigo foi traduzido automaticamente a partir da versão original em inglês.
Tutorial de Servidor MCP com uv e FastMCP: Criar um Servidor FeatureStoreLite
Este tutorial de servidor MCP cria um pequeno servidor de feature store com FastMCP, executa-o com uv e integra-o no Claude Desktop.
O que é um servidor MCP?
O Model Context Protocol (MCP) é uma norma aberta para ligar assistentes de IA como o Claude a dados e ferramentas externas. Um protocolo, um conjunto de convenções, em vez de uma nova integração para cada ferramenta que queira expor.
Neste tutorial vamos criar um servidor MCP FeatureStoreLite. Fica entre um LLM e uma feature store (uma base de dados de features de ML pré-computadas) e expõe ferramentas para consultar e escrever vetores de features identificados por utilizador, produto ou documento.
Porquê criar isto?
É um engenheiro de ML a depurar um pipeline. Em vez de recorrer a SQL ou escrever um script rápido para verificar valores de features, pergunta diretamente ao Claude: "Qual é o vetor de features para user_123?" ou "Mostra-me os metadados de product_abc." O servidor MCP é o que torna isso possível.
Porquê usar uv?
Vamos usar uv, um instalador de pacotes e gestor de projetos Python muito mais rápido do que pip e que trata da resolução de dependências e de virtualenvs numa única ferramenta. A outra razão: uv run --with mcp[cli] ... permite-nos declarar dependências inline na configuração do Claude Desktop mais à frente, por isso não há uma venv separada para manter sincronizada.
Visão geral da arquitetura
As quatro peças e como se encaixam:
- O utilizador faz uma pergunta em linguagem natural.
- O Claude Desktop é o cliente MCP. Escolhe uma ferramenta com base na pergunta e chama-a.
- O nosso servidor
FastMCPexpõeget_featureestore_featurecomo ferramentas MCP. - O SQLite é o armazenamento subjacente para os vetores de features.
2. Configuração e instalação
2.1. Instalar uv
Se ainda não tem uv, instale-o. O resto do tutorial assume que está no seu PATH.
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or via Homebrew
brew install uv
2.2. Inicializar o projeto
Crie uma nova diretoria e inicialize um projeto Python. uv init cria um pyproject.toml por si.
# 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. Criar o servidor
Dois ficheiros, separados por responsabilidade:
database.pytrata das operações SQLite.featurestore_server.pydefine o servidor MCP.
3.1. A camada de base de dados (database.py)
Este módulo gere a ligação SQLite e alguns auxiliares. Inicializamo-lo com duas linhas de exemplo para que o servidor tenha algo para devolver na primeira consulta.
Crie 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!")
Inicialize a base de dados:
uv run python database.py
3.2. O servidor MCP (featurestore_server.py)
FastMCP faz grande parte do trabalho. Decore uma função Python simples e ela fica registada como ferramenta ou recurso MCP. A docstring torna-se a descrição que o LLM vê, por isso escreva-a para um LLM, não apenas para humanos.
Crie 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. Testar com o MCP Inspector
Antes de integrar isto no Claude, valide o servidor com o MCP Inspector. É uma pequena interface web para chamar ferramentas e ler recursos diretamente.
uv run mcp dev featurestore_server.py
O comando inicia o servidor e abre o Inspector no navegador (normalmente em http://localhost:5173).

Chame get_feature com key="user_123". Se devolver o JSON da linha de seed, o servidor está a funcionar.
5. Ligar ao Claude Desktop
Quando o Inspector confirmar que o servidor funciona, registe-o no Claude Desktop.
5.1. Configurar o Claude
Edite o ficheiro de configuração do Claude Desktop:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%/Claude/claude_desktop_config.json
Adicione o seu servidor ao objeto mcpServers:
{
"mcpServers": {
"featurestore": {
"command": "uv",
"args": [
"run",
"--with",
"mcp[cli]",
"mcp",
"run",
"/ABSOLUTE/PATH/TO/mcp-featurestore/featurestore_server.py"
]
}
}
}
Importante: o caminho para
featurestore_server.pytem de ser absoluto. Um caminho relativo falha silenciosamente porque o Claude Desktop é executado a partir de uma diretoria de trabalho diferente.
5.2. Como a interação funciona
Eis o que é executado de ponta a ponta quando o Claude precisa de consultar features:
- O Claude vê as ferramentas disponíveis (
get_feature,list_features, e assim por diante). - Decide que a pergunta precisa de dados da feature store.
- Constrói uma chamada de ferramenta e envia-a para o seu servidor.
- O servidor executa a função Python e devolve o resultado.
- O Claude usa esse resultado para escrever a resposta final.
5.3. Consultas de exemplo
Reinicie o Claude Desktop e experimente alguns prompts:
-
"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. Resolução de problemas
Alguns modos de falha que vale a pena conhecer:
-
"Server connection failed":
- Verifique os logs em
~/Library/Logs/Claude/mcp.logno macOS. - Confirme que a configuração usa um caminho absoluto, não relativo.
- Confirme que
uvestá noPATHdo Claude Desktop. Se não estiver, aponte para o caminho completo do binário (which uvdir-lhe-á onde se encontra).
- Verifique os logs em
-
"Tool execution error":
- Reproduza-o no Inspector com
uv run mcp dev featurestore_server.py. O Inspector mostra o erro bruto, que o Claude Desktop normalmente omite. - Verifique se
features.dbestá a ser criado ao lado dedatabase.py. Se a sua diretoria de trabalho mudar, o caminho relativo ao script emget_db_path()é o que o salva.
- Reproduza-o no Inspector com
7. Conclusão
É tudo: um servidor FastMCP, um armazenamento SQLite subjacente e uma configuração do Claude Desktop que aponta para um comando uv run. A mesma estrutura funciona para tudo o que consiga encapsular numa função Python. Troque as chamadas SQLite por uma feature store real, uma API interna ou um registo de modelos, e o servidor continua pequeno.