mcp_feast
mcp_feast
Feast 피처 스토어 위에 구축된 MCP 서버로, 카드 스와이프 사기 모델을 위한 것입니다. 완전히 로컬에서 실행됩니다: Parquet 오프라인 스토어, SQLite 온라인 스토어, 클라우드 없음, 브로커 없음.
시스템 설계
네 개의 계층
flowchart TB
subgraph H["HOST — decides which tools to call"]
direction LR
H1["host.py<br/><i>local LLM, qwen2.5:7b</i>"]
H2["Claude Code<br/><i>.mcp.json</i>"]
H3["mcp_cli.py<br/><i>manual, for testing</i>"]
end
subgraph M["MCP SERVER — no Feast import, no credentials"]
M1["12 read tools<br/>+ 2 gated write tools"]
end
subgraph A["FEATURE API — holds the Feast SDK"]
A1["catalog"]
A2["lineage"]
A3["health"]
A4["values"]
end
subgraph S["STORAGE"]
direction LR
S1[("registry.db<br/><i>metadata</i>")]
S2[("online_store.db<br/><i>SQLite, serving</i>")]
S3[("data/*.parquet<br/><i>offline</i>")]
end
H1 -->|"stdio"| M1
H2 -->|"stdio"| M1
H3 -->|"stdio"| M1
M1 ==>|"HTTP / JSON"| A1
A1 -->|"Feast SDK"| S1
A2 --> S1
A3 --> S3
A4 --> S2그 굵은 화살표가 설계의 전부입니다. Feast 관련 모든 것은 그 아래에 있습니다. 그 위의 MCP 서버는 Feast 설치도, 스토어 드라이버도, 웨어하우스 자격 증명도 필요하지 않습니다 — HTTP 클라이언트일 뿐입니다.
이것은 세 가지를 얻습니다. SQLite를 Redis로 바꾸는 것은 MCP 계층이 결코 보지 못하는 feature_store.yaml 변경이 됩니다. MCP 서버를 실행하는 노트북은 프로덕션 Redis로의 네트워크 경로 대신 하나의 도달 가능한 URL만 필요합니다. 그리고 동일한 API가 두 번째 소비자 — 모델 서버 — 에게 서비스할 수 있는데, 이 서버는 여기서 구축되지는 않았지만 MCP 계층과 정확히 동일하게 POST /features/online을 호출할 것입니다.
Related MCP server: tecton-mcp
실제로 실행되는 것
프로세스 | 시작 주체 | 보유 | 포트 |
Feature API |
|
| 8000 |
MCP 서버 | 호스트, stdio를 통해 |
| — |
Ollama |
| qwen2.5:7b | 11434 |
호스트 |
| 대화 루프 | — |
Feast를 임포트하는 것은 API뿐입니다. 확인해 보세요:
python3 -c "import mcp_server.server, sys; print('feast' in sys.modules)" # False하나의 요청, 처음부터 끝까지
"카드 C-4471이 왜 플래그되었을까?" 라고 묻는 것은 모든 계층을 두 번 가로지릅니다:
sequenceDiagram
autonumber
participant L as Model
participant M as MCP server
participant A as Feature API
participant F as Feast SDK
participant D as SQLite
L->>M: resolve_card("C-4471")
M->>A: GET /cards/C-4471
A-->>M: CU-8842
M-->>L: C-4471 is owned by CU-8842
Note over L: the model spans two entities,<br/>so both join keys are needed
L->>M: explain_features_for_entity(card + customer)
M->>A: POST /features/explain
A->>F: get_online_features(fraud_model_v2)
F->>D: read 7 values
A->>F: provider.online_read(...)
F->>D: read per-entity event_ts
Note over A: joins values against TTL<br/>to classify each feature
A-->>M: values + age + is_stale + reasons
M-->>L: FRESH 6 / STALE 0 / MISSING 1그 두 번째 SDK 호출이 Feast가 공짜로 주지 않는 부분입니다 — 아래 참조.
데이터가 온라인 스토어에 도달하는 방법
flowchart LR
P[("data/*.parquet<br/>offline store")]
O[("online_store.db<br/>online store")]
W["live swipe"]
R["serving<br/><i>milliseconds</i>"]
T["training set"]
P -->|"feast materialize — batch, scheduled"| O
W -->|"feast push — real time, no broker"| O
O -->|"get_online_features"| R
P -.->|"get_historical_features — not exposed"| T점선 경로는 피처 스토어의 훈련 절반입니다. 의도적으로 제외되었습니다: 수백만 행을 반환하는 수 분짜리 쿼리를 실행하는데, 이는 채팅 도구에 맞지 않는 형태입니다. 그래서 생성기가 사기 레이블을 쓰지 않는 이유이기도 합니다.
API가 단순 통과(passthrough)가 아닌 이유
get_online_features()는 값만 반환하고 그 외에는 아무것도 반환하지 않습니다. 맨 null은 네 가지 상황 중 어느 것인지 알려줄 수 없습니다 — 그리고 Feast는 만료된 값을 불평 없이 제공합니다:
flowchart LR
B["get_online_features<br/><b>txn_count_1h: null</b>"]
B --> C1["<b>ENTITY_NOT_FOUND</b><br/>no row for this card"]
B --> C2["<b>NULL_IN_SOURCE</b><br/>feature genuinely absent"]
B --> C3["<b>STALE</b><br/>6h58m old, TTL is 2h"]
B --> C4["<b>a real zero</b><br/>the card had no swipes"]POST /features/explain은 공급자의 online_read를 통해 엔티티별 event_timestamp를 복구하여 이를 구분합니다 — get_online_features가 내부적으로 하는 것과 동일한 호출이지만 타임스탬프를 표면화하는 호출 — 그리고 이를 뷰의 TTL과 조인합니다.
원시 SDK가 제공하지 않는 세 가지 사실:
엔드포인트 | 파생하는 것 |
| 피처별 신선도 및 누락 값 이유 |
| 소스 → 뷰 → 소비 서비스 |
| 변경 전 폭발 반경 |
이 설계가 피하려고 만든 함정
신선도는 뷰별이 아니라 엔티티별입니다. 둘 다 서로 다른 답을 가진 실제 질문이며, 이를 혼동하는 것이 여기서 가능한 가장 위험한 실수입니다:
flowchart TB
V["<b>card_velocity</b><br/>materialized 52 seconds ago<br/>check_feature_freshness reports OK"]
V -->|"source had a row from 58m ago"| E1["<b>C-4471</b><br/>age 58m<br/>FRESH"]
V -->|"source's newest row is 6h58m old"| E2["<b>C-7788</b><br/>age 6h58m<br/>STALE"]
style E1 stroke:#2a9d4a,stroke-width:2px
style E2 stroke:#d1443c,stroke-width:3px구체화(materialization)는 소스가 가진 것을 그대로 씁니다. 최근 행이 없는 카드의 경우 그것은 오래된 값입니다 — 그래서 엔티티는 몇 초 전에 구체화된 뷰 안에서 몇 시간 동안 낡을 수 있습니다. 뷰를 새로 고치는 것으로는 고칠 수 없습니다; 푸시만이 가능합니다.
질문 | 도구 | 범위 |
"파이프라인이 죽었나?" |
| 모든 엔티티 |
"이 카드가 최신인가?" |
| 단일 엔티티 |
작은 모델은 이 둘을 확실히 혼동합니다. 이를 고친 것은 시스템 프롬프트가 아니라 — check_feature_freshness의 출력에 경고를 추가한 것이었습니다. 도구 설명을 건너뛰는 모델도 방금 실행한 결과는 읽습니다.
도구는 엔드포인트와 일대일로 매핑됩니다
flowchart LR
T1["list_feature_views<br/>describe_feature_view<br/>list_feature_services<br/>search_features<br/>list_entities<br/>resolve_card"] --> E1["/entities · /data-sources<br/>/feature-views · /feature-services<br/>/features/search · /cards"]
T2["get_feature_lineage<br/>get_feature_consumers"] --> E2["/features/../lineage<br/>/feature-views/../consumers"]
T3["check_feature_freshness"] --> E3["/health/materialization"]
T4["get_online_features<br/>explain_features_for_entity"] --> E4["/features/online<br/>/features/explain"]
T5["push_swipe<br/>trigger_materialization"] -.->|"only when FEAST_MCP_READONLY=false"| E5["/features/push<br/>/feature-views/../materialize"]api/routers/와 mcp_server/tools/는 파일별로 서로를 미러링합니다 — 카탈로그, 계보, 헬스, 값 — 그래서 탐색이 명확합니다.
설계를 이끈 두 가지 아이디어
오류는 지침으로 작성됩니다. 404는 Available: [...]을 반환하고, 잘못된 엔티티 행은 필요한 조인 키를 명명합니다. 반복적으로 관찰된 것: 7B 모델이 잘못 이해하고, 오류를 읽고, 다시 추측하는 대신 다음 단계에서 스스로 고칩니다.
안내는 설명뿐 아니라 출력에 실립니다. 도구 설명은 건너뛰어지고; 결과는 그렇지 않습니다. 신선도 범위 경고와 trigger_materialization의 "check_feature_freshness를 호출하여 확인" 모두 반환된 텍스트에 있으며, 둘 다 프롬프트 문구만으로는 실패했을 때 모델 동작을 바꿨습니다.
빠른 시작
Python 3.11. Feast는 >=3.10을 선언하지만 3.10만 분류하며, 그 전이 스택이 최신 인터프리터에서 문제의 일반적인 원인입니다.
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 /docs두 스크립트 모두 deps가 다른 곳에 있으면 PYTHON 오버라이드를 존중합니다:
PYTHON=/opt/miniconda3/envs/myenv/bin/python3 ./setup.shMCP 서버는 호스트가 .mcp.json을 통해 실행하며, 아래 문제 해결의 이유로 절대 인터프리터 경로를 고정합니다. ./run_mcp.sh는 디버깅을 위해 수동으로 실행합니다.
문제 해결: 잘못된 인터프리터
두 가지 증상, 하나의 원인 — deps를 보유한 Python과 다른 Python:
ModuleNotFoundError: No module named 'feast'
ImportError: cannot import name 'MCPServer' from 'mcp.server'두 번째가 더 교묘합니다: mcp 1.x는 정상적으로 임포트되지만 이 프로젝트가 사용하는 2.x mcp.server.MCPServer가 아닌 mcp.server.fastmcp.FastMCP를 노출합니다. 활성 conda 환경을 보여주는 셸 프롬프트는 증거가 아닙니다 — PATH를 확인하세요:
which python3 && python3 -V
echo $PATH | tr ":" "\n" | head -3프레임워크 또는 시스템 Python이 환경보다 앞서 있으면, 프롬프트가 무엇을 말하든 모든 python3 호출이 환경을 벗어납니다. 다음과 같이 제대로 진단하세요:
python3 preflight.py코드의 각 부분이 필요로 하는 정확한 심볼을 임포트합니다 — 모듈뿐만 아니라 — 그래서 잘못된 메이저 의존성은 이름으로 잡히고, PATH의 uvicorn 또는 feast가 다른 환경에 속할 때 경고합니다.
모든 진입점은 PYTHON 오버라이드를 받으므로 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 tools두 가지 규칙이 이를 완전히 피합니다:
./run_api.sh또는python3 -m uvicorn api.main:app로 API를 시작하세요. 절대 맨uvicorn api.main:app로 하지 마세요 — PATH에서 uvicorn을 해석하는데, 이는 Feast를 보유한 Python과 다른 것일 수 있으며, 실패는 임포트 체인 40프레임 깊이에서 표면화됩니다..mcp.json의command를 절대 인터프리터 경로로 유지하세요. 거기의"python3"는 호스트 프로세스가 우연히 가진 PATH에 대해 해석됩니다.
레지스트리에 있는 것
엔티티 — card (card_id), customer (customer_id)
피처 뷰
뷰 | 엔티티 | 종류 | 피처 | TTL |
| card | push |
| 2h |
| customer | batch |
| 7d |
피처 서비스 — fraud_model_v2, 7개 피처 모두 바인딩.
2h / 7d TTL 분할은 의도적입니다: 신선도 도구가 영구적인 올그린 대신 실제 답을 생성하게 만듭니다.
목 데이터
data_gen/generate_swipes.py는 15,000개의 고객 스냅샷(500명의 고객 × 30일)과 14,394개의 속도 행(600장의 카드 × 24시간, 낡은 사례를 만들기 위해 6개 제거)을 씁니다. 모든 것이 실행 시간에 고정되므로, 재생성은 항상 깨끗하게 구체화되는 데이터를 생성합니다.
데모가 결정적이도록 6개의 페르소나가 고정되어 있습니다:
카드 / 고객 | 설정 | 시연하는 것 |
| 시간당 7회 스와이프, $2,140 대 평균 $58.20, 차지백 null | 사기 사례 및 null 피처 |
| 모든 것이 중앙값 | 대조군 |
| 가장 최신 속도 행이 6시간 전 | 2h TTL을 지난 낡음 |
| 생성되지 않음 | 알 수 없는 엔티티 |
| 프로필은 있지만 카드 없음 | 부분 커버리지 |
| 차지백 4건, 정상 속도 | 속도가 아닌 위험 |
API
그룹 | 엔드포인트 |
카탈로그 |
|
계보 |
|
헬스 |
|
값 |
|
대화형 문서는 http://localhost:8000/docs에 있습니다.
API는 단순 통과가 아닙니다. 원시 SDK가 하지 않는 세 가지를 수행합니다: 레지스트리 메타데이터를 온라인 스토어 타임스탬프와 조인하여 신선도를 계산하고, 소스 → 뷰 → 서비스를 탐색하여 계보를 계산하며, Feast의 proto 형태를 평범한 명명된 객체로 평탄화합니다.
MCP 도구
읽기 전용 도구 12개, 쓰기가 활성화된 경우에만 등록되는 쓰기 도구 2개.
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 ⚠
두 종류의 신선도
이들은 서로 다른 질문에 답하며, 이를 혼동하는 것이 여기서 가능한 가장 위험한 실수입니다:
도구 | 답하는 것 | 범위 |
| "파이프라인이 죽었나?" | 모든 엔티티, 뷰 수준 |
| "이 카드의 데이터가 최신인가?" | 단일 엔티티 |
개별 엔티티는 몇 초 전에 구체화된 뷰 안에서 6시간 낡을 수 있습니다 — 구체화는 소스가 가진 것을 그대로 쓰고, 최근 행이 없는 카드의 경우 그것은 오래된 값입니다. 따라서 OK를 보여주는 뷰는 특정 카드에 대해 아무것도 증명하지 않습니다.
작은 모델은 이 둘을 확실히 혼동하고 뷰 수준 메타데이터에서 "신뢰할 만큼 최신"이라고 답합니다. 세 계층이 이를 방어합니다: 서버 INSTRUCTIONS, check_feature_freshness 도구 설명, 그리고 해당 도구의 출력에 추가된 메모 — 마지막 것이 실제로 효과가 있었던 것인데, 설명을 건너뛴 모델도 실행한 결과는 읽기 때문입니다.
explain_features_for_entity가 존재하는 이유
get_online_features는 맨 값을 반환합니다. 맨 null은 네 가지 다른 상황을 구분할 수 없으며, Feast는 만료된 값을 불평 없이 제공합니다:
진짜 0
구체화된 적이 없는 뷰
존재하지 않는 엔티티
TTL을 지난 값
explain_features_for_entity는 온라인 스토어에서 복구된 엔티티별 event_ts를 사용하여 이를 구분합니다. 그래서 이것이 선호되는 검색 도구입니다.
FEAST_MCP_READONLY
두 프로세스 모두 읽습니다. true(기본값)일 때 MCP 서버는 push_swipe나 trigger_materialization을 전혀 등록하지 않습니다. 모델이 볼 수 없는 도구는 시도하지 않을 것이기 때문입니다. 또한 API는 해당 라우트에 대해 독립적으로 403을 반환하므로 직접 curl로 호출해도 거부됩니다.
로컬 LLM 호스트
host.py는 로컬 오픈소스 모델로 구동되는 실제 MCP 호스트입니다. API 키도, 호스팅된 것도 없습니다. 모델이 어떤 도구를 호출할지 결정하며, mcp_cli.py는 사용자가 지정한 도구만 호출합니다.
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 # interactive시스템 프롬프트는 host.py에 작성되어 있지 않습니다. MCP 서버 자체의 instructions에서 오며, initialize() 중에 반환됩니다. 서버가 모델에게 도구가 어떻게 사용되도록 의도되었는지 알려주고, 호스트는 이를 그대로 전달합니다. mcp_server/server.py의 INSTRUCTIONS를 변경하면 호스트를 수정하지 않고도 모델의 동작이 바뀝니다.
모델 선택이 중요합니다. 도구 호출 지원이 필요합니다. qwen2.5:7b는 작동합니다. Gemma는 Ollama에 도구 템플릿이 없어 작동하지 않습니다.
호스트 가드레일
7B 모델은 신뢰할 수 없는 플래너이므로, 루프는 실제로 나타나는 세 가지 실패에 대비합니다:
실패 | 가드레일 |
이미 수행한 호출을 반복하며, 때로는 스텝 제한에 도달할 때까지 반복 | (도구, 인자)별로 결과를 캐시하고, 반복 호출은 두 번째 왕복 대신 "이미 수행했습니다"라는 메모와 함께 캐시에서 제공 |
다음 호출을 도구 호출로 내보내는 대신 산문으로 서술( | 감지 후, 서술 대신 호출을 내보내도록 한 번만 유도(최대 2회) |
답 없이 스텝 예산을 넘어 방황 | 마지막 스텝에서 — 또는 3회 반복 후 — 도구가 철회되므로, 수집한 내용으로 답해야 함 |
각각 HOST | 줄을 출력하므로 루프가 개입하는 것을 볼 수 있습니다.
그럼에도 개방형 프롬프트에서는 방황이 예상됩니다. 도구 세트를 제한하는 것이 실질적인 해결책입니다:
python3 host.py --tools resolve_card,explain_features_for_entity,check_feature_freshness \
"Why would card C-4471 be flagged?"MCP가 API를 호출하는 것 관찰하기
mcp_cli.py는 호스트와 동일한 stdio 프로토콜을 사용하므로 MCP → API 체인을 셸에서 관찰할 수 있습니다:
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는 각 도구가 접근하는 엔드포인트를 출력합니다:
http | HTTP Request: GET http://localhost:8000/cards/C-4471 "HTTP/1.1 200 OK"
C-4471 is owned by CU-8842직접 해보기
거절 디버깅
"카드 C-4471이 왜 거절되었을까요?"
list_feature_services → resolve_card → explain_features_for_entity. 지난 1시간 동안의 스와이프 7건, 총액 $2,140(평균 $58.20)을 반환하며, 차지백 이력은 0으로 가정하지 않고 명시적으로 사용 불가로 표시됩니다.
죽은 파이프라인 발견
"카드 C-7788에 대해 오래된 것이 있나요?"
explain_features_for_entity는 card_velocity가 2시간 TTL 대비 6시간 46분 지난 것으로 플래그를 표시합니다. 값은 여전히 반환됩니다. 읽기를 차단하는 것은 없으며, 플래그가 필요한 이유가 바로 이것입니다.
푸시 왕복(쓰기 활성화 필요)
"C-7788에 스와이프를 기록한 다음 다시 확인하세요."
push_swipe → 같은 카드가 최신 상태로 읽힙니다. card_velocity에 대한 trigger_materialization은 6시간 전 배치 행으로 재설정하므로 데모를 반복할 수 있습니다.
구조
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/와 mcp_server/tools/는 일대일로 서로 대응합니다.
참고 사항
chargebacks_lifetime은Int64가 아닌Float64입니다. 이 피처는 실제로 nullable이며, null 정수는 Parquet → pandas → Feast 경로에서 표현할 방법이 없습니다.설정에는
materialize-incremental이 아닌feast materialize를 사용하세요. Incremental은 뷰의 TTL을 시작 경계로 사용하므로, 2시간 TTL에서는 stale 페르소나를 만드는 6시간 전 행을 건너뜁니다.레지스트리 캐싱.
feature_store.yaml의cache_ttl_seconds: 30은 다른 셸에서 실행한feast apply가 30초 내에 반영됨을 의미합니다.POST /admin/reload는 즉시 강제하며, 온라인 스토어도 다시 엽니다. 단순 레지스트리 새로고침은 이 작업을 수행하지 않습니다.목 데이터는 시간에 고정되어 있습니다.
card_velocity는 2시간 TTL이 있으므로./setup.sh실행 후 몇 시간이 지나면 모든 카드가 stale로 읽히고 페르소나를 구분할 수 없게 됩니다../setup.sh를 다시 실행하세요.SQLite 동시성. uvicorn이 읽는 동안
feast materialize가 쓰면 잠금 경합이 발생할 수 있습니다. 로컬에서는 문제없지만, 프로덕션 온라인 스토어는 아닙니다.
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