Skip to main content
Glama
eddii1

Payment Delay MCP

by eddii1

Payment Delay MCP - 프로덕션 ML 모델을 MCP를 통해 모든 LLM에 제공하기

FastAPI 마이크로서비스 뒤에 배포되고 Model Context Protocol 도구로 언어 모델에 게시된 scikit-learn 분류기 - 그래서 기성 채팅 클라이언트가 모델을 발견하고 올바르게 호출하며, 이를 위해 작성된 통합 코드가 전혀 없습니다.

gpt-oss-120b가 올바른 MCP 도구를 선택하고 모델 API를 호출합니다

핵심 주장

모델은 페이로드이지, 핵심이 아닙니다.

대부분의 "AI 기반" 데모는 맞춤형 애플리케이션에 모델 호출을 하드코딩합니다. 이 프로젝트는 그 접근을 뒤집습니다: 분류기가 프로토콜로 게시되므로 LLM 클라이언트는 교체 가능합니다. 동일한 서버가 Docker의 OpenWebUI, CLI의 OpenCode, 그리고 Claude Desktop을 구동합니다 - 코드 변경도, 클라이언트별 어댑터도 없이.


개요

통신 사업자는 어떤 고객이 대금을 연체할지 알고 싶어 합니다. 훈련된 분류기가 그 답을 제공하지만, .pkl 파일은 제품이 아닙니다 - 누군가는 여전히 그것을 호출하는 글루 코드를 작성해야 하고, 그 글루는 새로운 소비자마다 다시 작성됩니다.

이 저장소는 그 글루를 프로토콜로 한 번 작성한 것입니다. 네 개의 계층, 각각 독립적으로 배포 가능:

flowchart TB
    subgraph reasoning["Reasoning path"]
        UI["OpenWebUI<br/>:3000"] -->|OpenAI protocol| LL["LiteLLM<br/>:4000"]
        LL -->|bedrock_mantle| BR["AWS Bedrock<br/>gpt-oss-120b"]
    end

    subgraph tools["Tool path"]
        UI -->|OpenAPI| MCPO["mcpo<br/>:8001"]
        MCPO -->|MCP over stdio| FM["FastMCP server<br/>5 tools · 2 resources · 1 prompt"]
        FM -->|HTTP| API["FastAPI service<br/>:8000"]
        API --> PRED["inference.predictor<br/>the only code that<br/>opens the pickle"]
        PRED --> PKL[("models/*.pkl<br/>RandomForest +<br/>RandomOverSampler")]
    end

    style reasoning fill:#1f2a3710,stroke:#8884
    style tools fill:#1f372a10,stroke:#8884

두 경로는 의도적으로 분리되어 있습니다. LLM은 아무것도 실행하지 않습니다. LLM은 도구와 그 인자를 지정하는 tool_calls 메시지를 내보내고, 클라이언트가 이를 실행하고 결과를 재생합니다. 그 구분이 모델을 교체 가능하게 만드는 이유이며, 추론 계층이 Bedrock이든, 로컬 Ollama든, Claude든 이 스택이 동일하게 작동하는 이유입니다.


Related MCP server: Company API MCP Server

핵심 아이디어: 도구 선택은 문서화 문제입니다

LLM은 도구의 이름, 시그니처, docstring에서만 도구를 선택합니다 - 그 외에는 아무것도 없습니다. 파인튜닝도, 예시도, 라우팅 로직도 없습니다. 따라서 docstring이 인터페이스이며, docstring을 작성하는 것은 주석이 아니라 엔지니어링 작업입니다.

여기 두 도구는 크게 겹칩니다. 둘 다 지불 지연을 예측합니다. 모델이 프롬프트 없이 올바르게 선택하게 하려면 운영상의 제약을 설명에 직접 인코딩해야 했습니다:

도구

모델이 선택해야 하는 경우

구분 신호

predict_payment_delay

사용자가 CSV를 경로 또는 붙여넣은 텍스트로 가진 경우

docstring은 csv_path가 서버가 사용자의 파일시스템을 볼 수 없는 컨테이너에서 실행될 때 실패하며 csv_text를 선호하라고 경고합니다

predict_single_customer

사용자가 한 고객을 자연어로 설명하는 경우

docstring은 "LLM이 한 고객을 구조화된 피처로 추출하는 자연어 사례용"이라고 말합니다

검증된 결과: 평범한 영어로 설명된 고객이 주어졌을 때, gpt-oss-120b는 도움 없이 predict_payment_delay 대신 predict_single_customer를 선택했고, 산문에서 피처 사전을 채웠으며, 근거 있는 답변을 반환했습니다. 두 홉 모두의 로그에서 확인됨 - mcpo에서 POST /predict_single_customer 200, 그 다음 모델 서비스에서 POST /predict 200.

이것이 이 프로젝트의 전체 주장이며, 반증 가능합니다: 도구를 비활성화하면 같은 모델이 같은 질문에 자신 있게 그리고 틀리게 답하며, 두 로그 창 모두 비어 있습니다.


요청, 처음부터 끝까지

대부분의 도구 사용 다이어그램이 생략하는 부분은 단일 사용자 질문이 모델에 두 번의 왕복을 요구하며, 중간 어시스턴트 메시지가 그대로 재생되어야 한다는 것입니다. 그렇지 않으면 tool_call_id가 매달려 있게 됩니다:

sequenceDiagram
    participant U as User
    participant W as OpenWebUI
    participant L as LiteLLM
    participant M as Bedrock model
    participant O as mcpo
    participant S as FastMCP
    participant A as FastAPI + model

    U->>W: "Will customer X pay late?"
    W->>L: messages[] + tools[]
    L->>M: translated to Bedrock
    M-->>W: finish_reason: tool_calls
    Note over W: the client executes,<br/>not the model
    W->>O: POST /predict_single_customer
    O->>S: MCP call over stdio
    S->>A: POST /predict
    A-->>S: {prediction, probability_yes}
    S-->>O: result
    O-->>W: 200 OK
    W->>L: messages[] + assistant(tool_calls) + tool(result)
    L->>M: second round trip
    M-->>U: grounded natural-language answer

tools[] 배열은 모든 요청에 재전송됩니다 - 모델은 상태가 없으며 매 턴 도구 세트를 다시 발견합니다.


검증된 것

네 개의 체크포인트, 각각 가정이 아닌 로그로 확인됨:

#

계층

증거

1

모델 서비스

GET /health 200; 단일 행 POST /predictno, confidence 0.985 반환

2

mcpo 브리지

:8001/docs에 5개 도구 렌더링; predict_single_customer가 curl로 실행됨

3

LiteLLM에서 Bedrock으로

/v1/models가 모델을 나열; 도구 호출 요청이 finish_reason: tool_calls 반환

4

전체 자율 루프

평범한 영어 질문에서 mcpo의 POST /predict_single_customer 200 API의 POST /predict 200

체크포인트 3은 보기보다 중요합니다: finish_reason: tool_calls는 "모델이 도구 사용을 거부했다"와 "도구가 모델에게 제공되지 않았다"를 구분하는 유일한 방법입니다. 그 실패들은 채팅 창에서 동일하게 보입니다.


모델

데이터셋 공개. 훈련 데이터는 이 연습을 위해 타겟 컬럼이 payment_delay로 재명명된 공개 통신 이탈(churn) 벤치마크입니다. 피처는 통화 기록 및 계정 필드이며, 청구 이력이 아닙니다. 모델링은 실제이고 파이프라인은 실제입니다; 비즈니스 프레임은 합성입니다. 숫자를 검증된 신용 위험 모델이 아닌 작업 예시로 취급하십시오.

속성

행 / 열

3,000 / 20

클래스 균형

no 2,587 (86.23%) · yes 413 (13.77%)

파이프라인

ColumnTransformer -> RandomOverSampler -> RandomForestClassifier (imblearn)

분할

80/20 계층적(stratified)

추론 시 피처

36 - 19개 원본 + 17개 파생 <column>_is_outlier 플래그

결정 임계값

0.35, 아티팩트로 영속화

임계값은 0.5가 아니며 하드코딩되지 않습니다. models/threshold.pkl로 제공되며 요청별로 재정의할 수 있습니다. 양성 타겟이 13.77%인 경우 기본 컷오프는 잘못된 것을 최적화하기 때문입니다. 더 낮은 임계값은 더 많은 오탐지 비용으로 더 많은 연체자를 잡아내며, 어떤 트레이드오프가 올바른지는 모델링 결정이 아니라 비즈니스 결정입니다 - 그래서 API는 이를 매개변수로 노출합니다.

코드베이스 어디에도 컬럼 이름이 하드코딩되어 있지 않습니다. 피처 순서는 feature_columns.pkl에서, 이상치 경계는 outlier_bounds.pkl에서 오므로, 재훈련에 코드 변경이 필요하지 않습니다.


방어할 가치가 있는 엔지니어링 결정

MCP 서버는 모델을 절대 임포트하지 않습니다. HTTP를 통해 API를 호출합니다. 이는 MCP 프로세스를 작게 유지하고 - sklearn도, 9MB pickle도 상주하지 않으며 - 모델 서비스가 다른 마이크로서비스처럼 확장, 배포, 모니터링되게 합니다. 프로토콜 어댑터는 비즈니스 로직을 담지 않아야 합니다.

예측은 이벤트 루프 밖에서 실행됩니다. 추론 호출은 run_in_threadpool로 디스패치되어, CPU 바운드 스코어링이 동시 요청에서 FastAPI의 비동기 루프를 차단하지 않습니다.

stdio 규율. stdio를 통한 MCP는 stdout이 JSON-RPC 프레임만 전달해야 하며 다른 것은 전달하지 않아야 하므로, 잘못된 print()는 스트림을 손상시키고 세션을 죽입니다. 따라서 모든 로깅은 stderr로 라우팅되고, httpxhttpcore는 침묵하며, launcher.py는 uvicorn의 출력을 로그 파일로 리다이렉트하고 /health를 기다린 다음에만 클라이언트에게 깨끗한 stdio를 넘깁니다.

두 토폴로지를 위한 두 진입점. server.py는 컨테이너 진입점으로, API가 별도 서비스입니다. launcher.py는 로컬 진입점으로, API를 직접 시작하고 기다립니다 - 의존성을 소유하는 단일 프로세스를 기대하는 데스크톱 MCP 클라이언트에 적합한 형태입니다.

실제 사고를 문서화하는 핀. mcp>=1.2.0,<2.0: mcp 2.x는 streamablehttp_client를 이름 변경했고, mcpo 0.0.20은 여전히 이전 이름을 임포트하므로 mcpo는 2.x에서 크래시 루프에 빠집니다. 상한은 requirements.txt에 이유와 함께 주석 처리되어 있습니다. 이유 없는 버전 핀은 다음에 읽는 사람이 삭제하기 때문입니다.


저장소 구조

mcp-payment-delay/
├── src/payment_delay/
│   ├── config.py                 # single source of truth for paths + endpoints, all env-overridable
│   ├── inference/predictor.py    # the only code that opens the pickle; imports no web framework
│   ├── api/main.py               # thin FastAPI adapter over the predictor
│   └── mcp_server/
│       ├── server.py             # FastMCP tools, resources, prompt (container entrypoint)
│       ├── api_client.py         # HTTP calls into the model service
│       └── launcher.py           # starts the API, then serves MCP on clean stdio (local entrypoint)
├── models/                       # model, threshold, outlier bounds, feature order
├── data/telecomunicatii.csv      # sample dataset
├── deploy/litellm_config.yaml    # Bedrock routing
├── scripts/bedrock_smoke_test.py # asserts a tool call comes back, not merely a 200
├── docs/                         # architecture + Docker runbook
├── Dockerfile                    # one image, serves both the API and the mcpo bridge
└── docker-compose.yml            # API + mcpo + LiteLLM + OpenWebUI

시작하기

모델 서비스만 실행 - 클라우드 자격 증명 불필요

python3 -m venv .venv && source .venv/bin/activate
make install                       # pip install -e ".[dev]"
make api                           # http://localhost:8000/docs

엔드포인트

용도

GET /health

서비스 가동, 모델 로드됨

GET /model/info

모델 유형, 클래스, 피처, 임계값

GET /schema

필수 CSV 컬럼

POST /predict

한 행(JSON 객체 또는 한 행 CSV) -> 하나의 yes/no

POST /predict/batch

다중 행 CSV -> 행당 하나의 yes/no

POST /predict/summary

다중 행 CSV -> 전체 파일에 대해 하나의 yes/no

curl -F "file=@data/telecomunicatii.csv" \
     "http://localhost:8000/predict/summary?threshold=0.35"

자신의 MCP 클라이언트 연결

python3 -m payment_delay.mcp_server.launcher

stdio를 통해 도구를 제공하고 API가 이미 정상이 아니면 시작합니다. opencode.json은 이를 OpenCode에 연결합니다; Claude Desktop 및 다른 stdio MCP 클라이언트도 같은 방식으로 연결됩니다.

전체 스택 실행

cp .env.example .env               # add your Bedrock key
python3 scripts/bedrock_smoke_test.py
make stack                         # http://localhost:3000

자격 증명 설정 및 문제 해결을 포함한 전체 런북: docs/docker-stack.md.


MCP 표면

다섯 개의 도구, 두 개의 리소스, 하나의 프롬프트 템플릿:

get_api_health           service + model status
get_model_info           model metadata, classes, features, endpoints
get_input_schema         expected CSV columns
predict_payment_delay    CSV in (path or text), per-row or aggregate, threshold configurable
predict_single_customer  one customer as a JSON object

payment-delay://context       business + modelling context, injected as a resource
payment-delay://api-contract  the HTTP contract these tools call

interpret_payment_delay_result   prompt template for business-language explanation

리소스와 프롬프트는 MCP에서 덜 사용되는 절반입니다. 컨텍스트 리소스 덕분에 클라이언트는 모델이 무엇을 위한 것인지 설명받을 필요 없이 읽을 수 있습니다.


기술 스택

FastAPI · FastMCP · mcpo · scikit-learn · imbalanced-learn · pandas · LiteLLM · AWS Bedrock · OpenWebUI · Docker Compose · uvicorn · httpx

문서


Eduard-Gabriel Tudoran, 2026.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes enterprise KPIs, health scores, forecasting, and anomaly detection as MCP tools, resources, and prompts for use by any MCP-compatible agent.
    2
    AGPL 3.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    Exposes internal company services as LLM-callable MCP tools, enabling AI agents to perform business operations like customer management, order processing, and support ticketing through natural language.
  • F
    license
    Not graded
    quality
    C
    maintenance
    Exposes a governed lending portfolio (loans, customers, risk-tier history) to any MCP-compatible AI client via read-only tools, schema resources, and analysis prompts, wrapping an existing API gateway instead of connecting directly to the database.

Latest Blog Posts

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/eddii1/mcp-payment-delay'

If you have feedback or need assistance with the MCP directory API, please join our Discord server