claims-coverage-assessment-assistant

claims-coverage-assessment-assistant

MCP server that reviews insurance claims against policy documents, providing coverage decisions, policy clause retrieval, claim history lookup, coverage rule checks, and fraud risk scoring via four tools.

Category
访问服务器

README

Claims Coverage Assessment Assistant

An AI assistant that reviews an insurance claim against policy documents and returns a structured coverage decision: coverage outcome, supporting policy clauses, a confidence score, a fraud risk assessment, and a flag for cases that need human review.

Single FastAPI service. A tool-calling LLM agent, backed by RAG and four tools exposed over MCP, running against MongoDB (Atlas locally / Azure Cosmos DB for MongoDB vCore in production). No stub/offline mode for the LLM or embeddings -- one real code path, local and in production.


1. Architecture

                          ┌───────────────────────────────┐
   POST /claims/assess ──▶│          FastAPI app            │
                          │  ┌───────────────────────────┐  │
                          │  │   Agent Orchestrator        │  │
                          │  │   (LLM tool-calling loop)   │  │
                          │  └─────────────┬───────────────┘  │
                          │                │ real MCP client   │
                          │                │ (loopback HTTP)   │
                          │  ┌─────────────▼───────────────┐  │
                          │  │   MCP server, mounted /mcp   │  │
                          │  │   exposes 4 tools:           │  │
                          │  │   - retrieve_policy_clauses  │  │
                          │  │   - lookup_claim_history      │  │
                          │  │   - check_coverage_rules      │  │
                          │  │   - score_fraud_risk          │  │
                          │  └─────────────┬───────────────┘  │
                          └────────────────┼────────────────────┘
                                           ▼
                          MongoDB (Atlas locally / Cosmos DB for
                          MongoDB vCore in production)
  • One process, one container. The MCP server is not a separate deployable service -- it's mounted at /mcp inside the same FastAPI app.
  • The agent is a real MCP client, not a direct function-call shortcut: it calls initialize()call_tool() over loopback HTTP against this app's own /mcp endpoint. Any external MCP client could reuse the same four tools the same way.
  • LLM: OpenAI or Azure OpenAI (LLM_PROVIDER), real function-calling, no stub. Embeddings: OpenAI (text-embedding-3-small), no TF-IDF.

2. Data model

Product catalog is kept separate from what a customer actually bought -- avoids duplicating identical rules/clauses per customer, and lets policy wording change over time without breaking already-issued policies.

Collection Purpose Key fields
product_versions Reusable product rules + wording (e.g. AUTO-GOLD-V1) product_version_id, policy_type, excluded_claim_types, waiting_period_days
issued_policies One customer's actual purchased policy policy_id, customer_id, product_version_id, sum_insured, inception_date
policy_clauses RAG corpus, shared per product version clause_id, product_version_id, text, embedding
claims Claims across their whole lifecycle -- also serves as "claim history" claim_id, policy_id, status, decision

claims is one collection, not two. A claim is the same entity throughout its lifecycle (submitted → under_review → approved/rejected); "claim history" is a query over this same collection for other claims on the same policy -- excluding the claim currently being assessed, since it's persisted with status=submitted before the agent loop runs and would otherwise show up in its own history/fraud-frequency lookup:

{ "policy_id": policy_id, "claim_id": { "$ne": current_claim_id } }
CLM-001
submitted
   │
under_review          (requires_human_review = true)
   │
approved / rejected   (based on coverage_outcome)

3. End-to-end request flow

1.  POST /claims/assess  → validated against ClaimRequest
2.  Route → ClaimAssessmentService.assess()
3.      a. get_issued_policy(policy_id)         -- 404 if missing
        b. validate claim.customer_id == policy.customer_id
        c. save_claim()  -- persisted with status=submitted, BEFORE the
                             agent loop, because tools 3 & 4 look the
                             claim up by claim_id, not by receiving it
                             as an argument
4.  Orchestrator.run(claim) -- loop, max 6 iterations:
        a. LLM decides: call a tool, or return final JSON
        b. tool call → real MCP call (initialize → call_tool) → one of:
             - retrieve_policy_clauses(query, policy_id)
                 → resolves policy_id → product_version_id → RAG search
             - lookup_claim_history(claim_id, lookback_days?)
                 → resolves claim → policy_id, excludes claim_id itself
             - check_coverage_rules(claim_id)
                 → deterministic: exclusions/waiting period from the
                   product version, sum insured/inception from the
                   issued policy -- NO LLM in this decision
             - score_fraud_risk(claim_id)
                 → deterministic weighted heuristic -- NO LLM here either
        c. result fed back into the conversation, loop continues
5.  LLM returns final JSON → parsed into CoverageDecision
        - fraud_risk is ALWAYS re-sourced from the actual tool result,
          never trusted from the LLM's own retelling
        - malformed/missing JSON → fallback assembly straight from
          whatever tool results were gathered (requires_human_review
          forced true in that case)
6.  save_decision() -- adds `decision` + advances `status`
7.  CoverageDecision → JSON response

4. Component reference

Component File(s) Role
Config config.py All env vars, one place, no os.environ elsewhere
Domain models domain/models.py Shared Pydantic contracts across every layer
Repositories db/repositories/*.py Only place that talks Mongo query syntax
RAG chunking rag/chunking.py Product-version clauses → citable chunks
RAG embeddings rag/embeddings.py OpenAI text-embedding-3-small, one implementation
RAG indexer rag/indexer.py Ingestion pipeline (scripts/seed_db.py calls this)
RAG retriever rag/retriever.py Query-time cosine similarity, scoped to one product_version_id
4 tools tools/*.py Plain async functions, registered on the MCP server
MCP server mcp_server/server.py FastMCP, mounted at /mcp, wraps every tool return in one JSON object
LLM client agent/llm_client.py OpenAI / Azure OpenAI, real function-calling
Prompts agent/prompts.py System prompt + tool schemas (what the model is told)
Orchestrator agent/orchestrator.py The loop; ToolExecutor = the real MCP client
Service services/claim_assessment_service.py The one use case: assess a claim
API api/routes/*.py, api/deps.py Route handlers (no logic) + dependency wiring
App factory main.py Builds Mongo, mounts MCP, registers routes

Design decisions, briefly:

  • No LangChain/LangGraph. The whole loop is ~150 lines of plain Python in orchestrator.py -- fully explainable, no hidden control flow, and this project has no multi-agent/graph/branching need that would justify the abstraction.
  • No multi-agent / A2A. One model, one loop, four tools -- sufficient for "assess one claim," and avoids complexity with no corresponding need.
  • Coverage rules and fraud scoring are deterministic code, not LLM calls. Eligibility decisions must be auditable; the LLM only combines and explains tool outputs, never decides them.
  • Fraud scoring is a transparent weighted heuristic, not a trained ML model -- explicitly POC-level; interface (claim_id in, FraudRiskResult out) would stay the same if swapped for a real model later.
  • Brute-force cosine similarity in Python, not a vector index. Fast enough at POC scale (hundreds of clauses); the retriever's return type is the isolation boundary if a native vector index is added later.
  • MCP is genuinely used, not decorative. The orchestrator is a real MCP client calling the mounted server over loopback HTTP -- same protocol an external client would use, not a shortcut.

5. Setup and run (no Docker required)

# 1. Database -- MongoDB Atlas free tier (mongodb.com/atlas), or local mongod
cp .env.example .env
# edit .env: MONGO_URI, OPENAI_API_KEY (required -- no offline mode)

# 2. Install
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

# 3. Seed (product versions + issued policies + prior claims)
python scripts/seed_db.py

# 4. Run
cd src && uvicorn app.main:app --reload
  • API docs: http://localhost:8000/docs
  • MCP endpoint: http://localhost:8000/mcp/
  • Health check: http://localhost:8000/health

Tests

pytest tests/ -v

25 tests, no external services or API keys needed -- mongomock-motor for persistence, hand-written FakeLLMClient/FakeToolExecutor doubles for orchestrator unit tests, and one integration test that spins up a real MCP server (real protocol, real tools) with only the LLM and database mocked.

Deployment

docker/Dockerfile builds the single-container image. .github/workflows/deploy.yml: tests on every push; on main, builds, pushes to Azure Container Registry, deploys to Azure Container Apps. Runtime config (MONGO_URI, OPENAI_API_KEY, etc.) is set as environment variables on the Container App itself, not in the workflow.


6. Testing / cURL examples

Seed the database first (python scripts/seed_db.py). All examples use the seeded POL-AUTO-1001 / POL-HEALTH-2001 policies. Always include filed_at explicitly, so the fraud "claim frequency" signal lines up with the seeded 2024-dated history correctly.

Test 1 — Clean claim: fully covered, low risk

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-1", "policy_id": "POL-AUTO-1001", "customer_id": "CUST-001",
  "claim_type": "collision", "description": "Rear-ended at a traffic light",
  "amount": 45000, "incident_date": "2024-06-01T00:00:00Z", "filed_at": "2024-06-02T00:00:00Z"
}'

Expected: coverage_outcome: "covered", fraud_risk.risk_level: "low", requires_human_review: false.

Test 2 — Excluded claim type → rejected

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-2", "policy_id": "POL-AUTO-1001", "customer_id": "CUST-001",
  "claim_type": "racing", "description": "Damage during a street race",
  "amount": 30000, "incident_date": "2024-06-01T00:00:00Z", "filed_at": "2024-06-02T00:00:00Z"
}'

Expected: coverage_outcome: "not_covered", requires_human_review: true.

Test 3 — Waiting period violation

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-3", "policy_id": "POL-AUTO-1001", "customer_id": "CUST-001",
  "claim_type": "collision", "description": "Collision shortly after buying the policy",
  "amount": 10000, "incident_date": "2024-01-20T00:00:00Z", "filed_at": "2024-01-21T00:00:00Z"
}'

Expected: coverage_outcome: "not_covered" (day 5 of a 15-day waiting period), fraud_risk.risk_level: "medium".

Test 4 — Amount exceeds sum insured

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-4", "policy_id": "POL-AUTO-1001", "customer_id": "CUST-001",
  "claim_type": "collision", "description": "Major collision, vehicle totaled",
  "amount": 900000, "incident_date": "2024-06-01T00:00:00Z", "filed_at": "2024-06-02T00:00:00Z"
}'

Expected: coverage_outcome: "not_covered" (900,000 > sum insured 800,000), fraud_risk.risk_level: "medium".

Test 5 — Covered by rules, but HIGH fraud risk (best one to demo live)

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-5", "policy_id": "POL-AUTO-1001", "customer_id": "CUST-001",
  "claim_type": "theft", "description": "Vehicle stolen from parking lot",
  "amount": 600000, "incident_date": "2024-02-01T00:00:00Z", "filed_at": "2024-02-02T00:00:00Z"
}'

Expected: coverage_outcome: "covered" but fraud_risk.risk_level: "high" (high-amount + early-claim signals both fire), requires_human_review: true. Shows coverage and fraud risk are independent axes.

Test 6 — Health policy, different waiting period

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-6", "policy_id": "POL-HEALTH-2001", "customer_id": "CUST-002",
  "claim_type": "hospitalization", "description": "Emergency admission for surgery",
  "amount": 50000, "incident_date": "2023-07-01T00:00:00Z", "filed_at": "2023-07-02T00:00:00Z"
}'

Expected: coverage_outcome: "not_covered" (day 30 of a 90-day waiting period).

Test 7 — Unknown policy → 404

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{
  "claim_id": "TEST-7", "policy_id": "POL-DOES-NOT-EXIST", "customer_id": "CUST-999",
  "claim_type": "collision", "description": "Test", "amount": 1000,
  "incident_date": "2024-06-01T00:00:00Z", "filed_at": "2024-06-02T00:00:00Z"
}'

Expected: HTTP 404, {"error_code": "policy_not_found", ...}.

Test 8 — Invalid payload → 422

curl -X POST http://localhost:8000/claims/assess -H "Content-Type: application/json" -d '{"claim_id": "X"}'

Expected: HTTP 422.

Test 9 — Health check

curl http://localhost:8000/health

Expected: {"status": "ok", "mongo_connected": true}.


7. Environment variables

Variable Default Purpose
MONGO_URI mongodb://localhost:27017 Atlas / local / Cosmos DB vCore connection string
MONGO_DB_NAME claims_assistant Database name
OPENAI_API_KEY Required always (RAG embeddings; also default LLM provider)
LLM_PROVIDER openai openai | azure_openai
OPENAI_MODEL gpt-4o-mini Model for the agent loop
AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_API_KEY / AZURE_OPENAI_DEPLOYMENT Required if LLM_PROVIDER=azure_openai
RAG_TOP_K 3 Clauses retrieved per query
FRAUD_HIGH_AMOUNT_THRESHOLD 500000 Fraud signal: high claim amount
FRAUD_EARLY_CLAIM_DAYS_THRESHOLD 30 Fraud signal: claim shortly after inception
FRAUD_FREQUENCY_LOOKBACK_DAYS 365 Fraud signal: frequency window
FRAUD_FREQUENCY_CLAIM_COUNT_THRESHOLD 3 Fraud signal: claim count trigger

See .env.example for the full list.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选