Ir para o conteúdo

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:

Arquitetura MCP

  1. O utilizador faz uma pergunta em linguagem natural.
  2. O Claude Desktop é o cliente MCP. Escolhe uma ferramenta com base na pergunta e chama-a.
  3. O nosso servidor FastMCP expõe get_feature e store_feature como ferramentas MCP.
  4. 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:

  1. database.py trata das operações SQLite.
  2. featurestore_server.py define 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).

Inspector

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.py tem 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:

Fluxo de trabalho MCP

  1. O Claude vê as ferramentas disponíveis (get_feature, list_features, e assim por diante).
  2. Decide que a pergunta precisa de dados da feature store.
  3. Constrói uma chamada de ferramenta e envia-a para o seu servidor.
  4. O servidor executa a função Python e devolve o resultado.
  5. O Claude usa esse resultado para escrever a resposta final.

5.3. Consultas de exemplo

Reinicie o Claude Desktop e experimente alguns prompts:

  1. "List all available features." Pergunta 2

  2. "Get the feature vector for user_123." Pergunta 3

  3. "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.log no macOS.
    • Confirme que a configuração usa um caminho absoluto, não relativo.
    • Confirme que uv está no PATH do Claude Desktop. Se não estiver, aponte para o caminho completo do binário (which uv dir-lhe-á onde se encontra).
  • "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.db está a ser criado ao lado de database.py. Se a sua diretoria de trabalho mudar, o caminho relativo ao script em get_db_path() é o que o salva.

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.

Referências