mcp_feast
mcp_feast
Un servidor MCP sobre un feature store de Feast, para un modelo de fraude por pases de tarjeta. Se ejecuta por completo en local: offline store en Parquet, online store en SQLite, sin nube, sin broker.
Claude --MCP/stdio--> mcp_server/ --HTTP--> api/ --SDK--> Feast --> Parquet + SQLite
(no feast) (holds the FeatureStore)La frontera HTTP del medio es el punto clave. Todo lo específico de Feast vive por debajo; el servidor MCP que está por encima no necesita instalación de Feast, ni drivers de store, ni credenciales de almacén de datos. Cambiar SQLite por Redis es un cambio en feature_repo/feature_store.yaml que el servidor MCP jamás ve.
Inicio rápido
Python 3.11. Feast declara >=3.10, pero clasifica solo 3.10, y su pila transitiva de dependencias es la fuente habitual de problemas con intérpretes más nuevos.
python3.11 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
./setup.sh # preflight + data + apply + materialize
./run_api.sh # API on :8000, docs at /docsAmbos scripts respetan una sobreescritura PYTHON si las dependencias están en otro sitio:
PYTHON=/opt/miniconda3/envs/myenv/bin/python3 ./setup.shEl servidor MCP lo lanza el host mediante .mcp.json, que fija una ruta absoluta de intérprete por el motivo que se explica en «Solución de problemas» más abajo. ./run_mcp.sh lo ejecuta a mano para depurar.
Solución de problemas: intérprete incorrecto
Dos síntomas, una causa: un Python distinto del que tiene las dependencias:
ModuleNotFoundError: No module named 'feast'
ImportError: cannot import name 'MCPServer' from 'mcp.server'El segundo es el que más quebraderos da: mcp 1.x importa bien, pero expone mcp.server.fastmcp.FastMCP, no el mcp.server.MCPServer 2.x que usa este proyecto. Que el prompt de la shell muestre un entorno conda activo no es prueba: revisa el PATH:
which python3 && python3 -V
echo $PATH | tr ":" "\n" | head -3Si un Python de framework o del sistema aparece por delante de tu entorno, todo lo que llame a python3 se escapa de ese entorno diga lo que el prompt diga. Diagnostícalo bien con:
python3 preflight.pyImporta el símbolo exacto que necesita cada parte del código, no solo el módulo; así una dependencia con la versión mayor equivocada se detecta por el nombre, y avisa cuando uvicorn o feast de tu PATH pertenecen a otro entorno.
Todos los puntos de entrada aceptan una sobreescritura PYTHON, así nunca tienes que pelear contigo para con el PATH:
PYTHON=/opt/miniconda3/envs/myenv/bin/python3 ./setup.sh
PYTHON=/opt/miniconda3/envs/myenv/bin/python3 ./run_api.sh
PYTHON=/opt/miniconda3/envs/myenv/bin/python3 python3 mcp_cli.py toolsDos reglas evitan esto del todo:
Inicia la API con
./run_api.sh, opython3 -m uvicorn api.main:app. Nuncauvicorn api.main:appa secas: eso resuelve uvicorn desde el PATH, que puede pertenecer a un Python distinto del que tiene Feast, y el fallo sale a la superficie cuarenta frames dentro de una cadena de imports.Mantén el
commandde.mcp.jsoncomo ruta absoluta del intérprete. Un"python3"ahí se resuelve contra el PATH que tuviera el proceso del host a mano.
Related MCP server: tecton-mcp
Qué hay en el registro
Entidades — card (card_id), customer (customer_id)
Vistas de características
Vista | Entidad | Tipo | Características | TTL |
| card | push |
| 2 h |
| customer | batch |
| 7 d |
Servicio de características — fraud_model_v2, que vincula las 7 características.
La división de TTL de 2 h / 7 d es intencionada: hace que las herramientas de frescura produzcan respuestas reales en lugar de un permanente todo en verde.
Datos simulados
data_gen/generate_swipes.py escribe 15,000 instantáneas de clientes (500 clientes × 30 días) y 14,394 filas de velocidad (600 tarjetas × 24 horas, menos 6 eliminadas para crear el caso de obsolescencia). Todo está anclado al momento de ejecución, de modo que regenerar siempre produce datos que se materializan sin problema.
Seis perfiles están fijados para que las demos sean deterministas:
Tarjeta / Cliente | Perfil | Demuestra |
| 7 pases/h, 2,140 $ frente a una media de 58,20 $, contracargos nulos | El caso de fraude y una característica nula |
| Todo en la mediana | Control |
| La fila de velocidad más reciente tiene 6 h | Obsolescencia más allá de un TTL de 2 h |
| Nunca generado | Entidad desconocida |
| Perfil pero sin tarjeta | Cobertura parcial |
| 4 contracargos, velocidad normal | Riesgo que no es velocidad |
La API
Grupo | Endpoints |
Catálogo |
|
Linaje |
|
Salud |
|
Valores |
|
Documentación interactiva en http://localhost:8000/docs.
La API no es un passthrough. Hace tres cosas que el SDK crudo no hace: combina metadatos del registro con timestamps del online store para calcular frescura, recorre fuente → vista → servicio para calcular el linaje, y aplana las formas proto de Feast en objetos planos y nombrados.
Herramientas MCP
12 herramientas de solo lectura, más 2 herramientas de escritura que solo se registran cuando la escritura está habilitada.
list_feature_views · describe_feature_view · list_feature_services · describe_feature_service · search_features · list_entities · resolve_card · get_feature_lineage · get_feature_consumers · check_feature_freshness · get_online_features · explain_features_for_entity · push_swipe ⚠ · trigger_materialization ⚠
Dos tipos de frescura
Responden a preguntas distintas, y confundirlas es el error más peligroso que hay aquí:
Herramienta | Responde | ¿Para qué? |
| «¿Está muerto un pipeline?» | Todas las entidades, a nivel de vista |
| «¿Están al día los datos de esta tarjeta?» | Una entidad |
Una entidad concreta puede estar obsoleta seis horas dentro de una vista que se materializó hace segundos: la materialización escribe lo que tuviera la fuente, y para una tarjeta sin filas recientes eso es un valor viejo. Por eso una vista que muestra OK no demuestra nada sobre una tarjeta concreta.
Un modelo pequeño tiende a confundir las dos cosas y responder «suficientemente reciente para confiar» a partir de los metadatos de nivel de vista. Tres capas lo impiden: las INSTRUCTIONS del servidor, el texto de la herramienta check_feature_freshness y una nota añadida a la salida de esa herramienta; la última es la que de verdad funciona, porque un modelo que se saltó la descripción también lee el resultado sobre el que actuó.
Por qué existe explain_features_for_entity
get_online_features devuelve valores pelados. Un null pelado no puede distinguir cuatro situaciones distintas, y Feast sirve sin quejarse un valor caducado:
un cero real
una vista que nunca se materializó
una entidad que no existe
un valor que ha sobrepasado su TTL
explain_features_for_entity los separa usando el event_ts por entidad recuperado del online store. Por eso es la herramienta de consulta preferida.
FEAST_MCP_READONLY
Lo leen ambos procesos. Cuando es true (el valor por defecto), el servidor MCP no registra en absoluto push_swipe ni trigger_materialization; una herramienta que el modelo no puede ver no la va a intentar, y la API por su parte devuelve 403 en esas rutas, con lo que ni siquiera un curl directo entra.
Host de LLM local
host.py is a real MCP host impulsado por un modelo local de código abierto: sin clave de API, nada alojado. El modelo decide a qué herramientas llamar; mcp_cli.py solo llama a las herramientas que le nombres.
ollama/qwen2.5:7b -> host.py -> MCP server -> Feature API -> Feast -> SQLiteollama serve & # if not already running
ollama pull qwen2.5:7b # any tool-calling model works
python3 host.py "Why would card C-4471 be flagged?"
python3 host.py --trace --quiet "Is anything stale?"
python3 host.py # interactiveEl prompt del sistema no está escrito en host.py. Sale de la instructions del propio servidor MCP, que se devuelve durante initialize(); el servidor le explica al modelo cómo deben usarse sus herramientas y el host lo transmite. Cambiar INSTRUCTIONS en mcp_server/server.py cambia el comportamiento del modelo sin tocar el host.
La elección del modelo importa: necesita soporte de tool-calling. lengqwen2.5:7b funciona; Gemma no tiene ninguna plantilla de herramientas en Ollama y no funcionará.
Protecciones del host
Un modelo de 7B es un planificador poco fiable, así que el bucle se protegen against contra tres fallos que de veras presenta:
Fallo | Protección |
Repite una llamada que ya hizo, a veces hasta el límite de pasos | Los resultados quedan en la lazada por (tool, args); una repetición se sirve desde la caché con una nota de «esto ya lo hiciste» en lugar de una segunda vuelta |
Narra en prosa el siguiente paso ( | Se detecta y se empuja una vez para que emita la llamada en vez de describirla (máximo 2) |
Se va más allá del presupuesto de pasos sin respuesta | En el último paso, o después de 3 repeticiones, las herramientas se le retiran, y tiene que responder con lo que ya haya recogido |
Cada una imprime una línea HOST |, de forma que puedes ver al bucle interven.
Aun así, espera que divague en preguntas abiertas. Limitar el conjunto de herramientas es la solución práctica:
python3 host.py --tools resolve_card,explain_features_for_entity,check_feature_freshness \
"Why would card C-4471 be flagged?"Mientras MCP llama a la API
mcp_cli.py habla el mismo protocolo de stdio que el host, de modo que la cadena MCP → API se puede observar desde el shell:
python3 mcp_cli.py tools # what is registered
python3 mcp_cli.py --trace demo # 11-step walkthrough, with HTTP calls
python3 mcp_cli.py --trace call resolve_card '{"card_id": "C-4471"}'--trace imprime el endpoint que usa cada una de las herramientas:
http | HTTP Request: GET http://localhost:8000/cards/C-4471 "HTTP/1.1 200 OK"
C-4471 is owned by CU-8842Pruébalo
Investigar un rechazo
«¿Por qué se rechaza la tarjeta C-4471?»
list_feature_services → resolve_card → explain_features_for_entity. Devuelve 7 pases en la última hora que suman 2,140 $ frente a una media de 58,20 $, con un histórico de contracargos explícitamente no disponible en vez de dár por cero.
Detectar un pipeline muerto
«¿Está algo obsoleto en la tarjeta C-7708?»
explain_features_for_entity marca card_velocity como con 6 h 46 min de antigüedad frenterme TTL de 2 h. Los valores siguen viniendo: nada bloquea la lectura, que es exactamente el motivo por el que hace falta la marca.
Push de ida y vuelta (con escritura activada)
«Registra un pase en C-7788 y luego comprueba otra vez.»
push_swipe → la misma tarjeta se lee reciente. trigger_materialization en card_velocity lo deja de nuevo en la fila del lote de hace 6 h, así la demo es repetible.
Estructura
requirements.txt pinned, verified working set
preflight.py interpreter + dependency check, run by both scripts
setup.sh data + apply + materialize
run_api.sh starts the API on the right interpreter
run_mcp.sh starts the MCP server by hand (debugging)
mcp_cli.py drives the MCP server from a shell, with --trace
host.py local-LLM MCP host -- the model picks the tools
feature_repo/ Feast definitions + feature_store.yaml (the only Feast config)
data_gen/ mock data generator
api/ FastAPI + the Feast SDK <- the API boundary
routers/ catalog | lineage | health | values
mcp_server/ MCP tools, HTTP client only <- no Feast import
tools/ catalog | lineage | health | values | adminapi/routers/ y mcp_server/tools/ son espejo uno a uno del otro.
Notas
chargebacks_lifetimeesFloat64, noInt64. El atributo admite nulos de verdad, y un entero nulo no tiene representación en la ruta Parquet → pandas → Feast.Usa
feast materialize, nomaterialize-incremental, para la configuración. La modalidad incremental usa la TTL de la vista como límite inicial, así que con una TTL de 2h se saltaría la fila de 6h de antigüedad que hace que la persona obsoleta funcione.Caché del registro.
cache_ttl_seconds: 30enfeature_store.yamlsignifica que unfeast applydesde otra consola aparece en 30 s.POST /admin/reloadlo fuerza de inmediato y también reabre el almacén en línea, algo que una mera actualización del registro no hace.Los datos simulados están anclados en el tiempo.
card_velocitytiene una TTL de 2h, así que pasadas más de un par de horas desde./setup.sh, todas las tarjetas se leen como obsoletas y las personas dejan de distinguirse. Vuelve a ejecutar./setup.sh.Concurrencia en SQLite. La escritura de
feast materializemientras uvicorn lee puede dar lugar a contención de bloqueos. Vale para local, pero no es un almacén en línea de producción.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI models to interact with local CSV and Parquet data through MCP tools, providing summarization and analysis capabilities.1
- FlicenseNot gradedqualityDmaintenanceEnables interaction with Tecton clusters through MCP, allowing management of feature stores, execution of Tecton CLI commands, and retrieval of feature store configurations via natural language.
- AlicenseAqualityDmaintenanceExposes Azure AI Foundry agents, workflows, and AI Search vector-database capabilities as MCP tools, enabling natural language interaction with agents, semantic search, and index management.102MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to manage local project files and Git operations through MCP tools, including file CRUD, search, Git status, recent commits, and project summaries.
Related MCP Connectors
Hosted MCP endpoint with realistic fake data for prototyping agents. 12 tools, no setup.
Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sidbu546/mcp_feast_dev'
If you have feedback or need assistance with the MCP directory API, please join our Discord server