Skip to main content
Glama
README.md
# CustomerIQ-Agent — MCP Server Tying Projects 1-3 Together

**Project 4 of the CustomerIQ portfolio** — an MCP (Model Context Protocol) server that exposes
Project 1's churn risk model, Project 2's ticket classifier, and Project 3's RAG document Q&A as
callable tools, so any MCP-aware AI client (Claude Desktop, Claude Code, or a custom agent) can
orchestrate all three in one conversation.

## What is MCP, in one paragraph

MCP is an open protocol (originally introduced by Anthropic) that standardizes how an LLM client
connects to external tools and data sources — instead of every AI application writing custom,
one-off integrations for every tool, an MCP **server** exposes a set of tools with typed schemas
over a standard protocol, and any MCP-**compatible client** can discover and call them the same
way. This project *is* an MCP server: it doesn't call an LLM itself (except indirectly, inside
Project 3's RAG pipeline) — it exposes capabilities for an LLM client to call.

## Why this project depends on Projects 1-3 (not duplicates them)

This is the concrete implementation of "make each project depend on the other" from the original
plan. Project 4 contains **no model training or document ingestion code of its own** — it loads
the already-trained artifacts Projects 1-3 produce (`churn_model.joblib`,
`tfidf_baseline.joblib`, `rag_index.joblib`) directly from their sibling folders, assuming the
standard layout:
```
AI-ML-CAREER/projects/
    project-01-tabular-ml/
    project-02-nlp-text-classification/
    project-03-rag-document-qa/
    project-04-mcp-agent/        <- this project
```
If you run Project 4 before building Projects 1-3's artifacts, each tool raises a clear
`FileNotFoundError` naming exactly which command to run first — see `tools.py`'s
`_require_artifact()`.

## The three tools

| Tool | Backed by | Input | Output |
|---|---|---|---|
| `churn_risk_score` | Project 1 (XGBoost) | `customer_id` | churn probability + risk tier |
| `support_ticket_category` | Project 2 (TF-IDF + LogReg) | `ticket_text` | category + confidence |
| `policy_question` | Project 3 (RAG) | `question` | grounded answer + sources, or a refusal |

## Project structure

```
project-04-mcp-agent/
├── src/project_04_agent/
│   ├── config.py     # resolves sibling-project artifact paths (env-var overridable)
│   ├── tools.py       # framework-independent tool logic (unit-testable directly)
│   └── server.py      # MCP wiring: @mcp.tool() decorators around tools.py
├── scripts/
│   └── demo_client.py # a real MCP client that launches the server and calls its tools
└── tests/
    └── test_tools.py   # tests the tool logic directly (skipped if artifacts are missing)
```

## Setup & usage (Windows / PowerShell, using `uv`)

**Prerequisite:** Projects 1, 2, and 3 must already be built (their models/index exist on disk).
If you haven't run them recently:
```powershell
cd ..\project-01-tabular-ml;  uv run python scripts/run_pipeline.py; cd ..\project-04-mcp-agent
cd ..\project-02-nlp-text-classification; uv run python scripts/run_baseline.py; cd ..\project-04-mcp-agent
cd ..\project-03-rag-document-qa; uv run python scripts/run_ingest.py; cd ..\project-04-mcp-agent
```

Then set up Project 4 itself:
```powershell
cd project-04-mcp-agent
uv init
uv add "mcp>=2" joblib pandas scikit-learn xgboost
uv run pytest tests/ -q               # unit tests against the real cross-project artifacts
uv run python scripts/demo_client.py  # real MCP client<->server demo, no external app needed
```

### A version note worth knowing (I hit this myself)
The `mcp` package went through a breaking API change: in `mcp` 1.x, the server class was called
`FastMCP` (`from mcp.server.fastmcp import FastMCP`). As of `mcp` 2.x, it's renamed to
`MCPServer` (`from mcp.server.mcpserver import MCPServer`) with some parameter changes. This
project targets `mcp>=2`. If `uv add mcp` gives you a 1.x version for some reason, either
`uv add "mcp>=2"` explicitly, or swap the import in `server.py` back to the 1.x form — I'd
recommend actually hitting this error once yourself and fixing it, rather than just reading this
note, since "a dependency's API changed between versions" is an extremely common real-world
debugging scenario worth having practiced.

### Connecting to Claude Desktop / Claude Code
Add this to your MCP client's config (e.g. Claude Desktop's `claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "customeriq-agent": {
      "command": "uv",
      "args": ["run", "--directory", "C:\\Users\\Dell\\AI-ML-CAREER\\projects\\project-04-mcp-agent", "python", "src/project_04_agent/server.py"]
    }
  }
}
```
After restarting the client, you should be able to ask things like *"What's the churn risk for
CUST-000042, and if it's high, what does our retention policy say we can offer them?"* — a
question that genuinely requires two of the three tools.

## Verified results (Phase 1)

Tested through the **real MCP protocol** (tool discovery + tool calls via `mcp.client.stdio`, the
same mechanism Claude Desktop uses), not mocked:
- `churn_risk_score("CUST-000000")` → `{"found": true, "churn_probability": 0.1886, "risk_tier": "low"}`
- `support_ticket_category("My internet keeps disconnecting...")` → `{"category": "technical_issue", "confidence": 0.87}`
- `policy_question("What happens if I miss a payment?")` → grounded answer citing the Billing Policy
- `policy_question("What's the weather today?")` → correctly refused (Project 3's hallucination
  mitigation carries through end-to-end via MCP)

5/5 unit tests pass against the real cross-project artifacts.

## Design decisions worth mentioning in an interview

- **Why separate `tools.py` from `server.py`?** The MCP framework wrapping is a thin decorator
  layer; the actual logic is plain, framework-independent Python that's unit-testable with
  ordinary pytest and reusable behind a REST API or CLI if MCP weren't the chosen integration
  layer — the same "business logic separate from framework" principle as `sklearn.Pipeline`
  objects in `tools.py` (Project 1) or `RAGPipeline` in Project 3.
- **Why load artifacts via `sys.path` insertion instead of pip-installing each project as a
  package?** Simplicity for a portfolio project — each project stays independently runnable and
  clonable without a shared packaging/publishing step. In a real production system you'd likely
  package Projects 1-3 as proper installable libraries (or serve them behind their own APIs) and
  have Project 4 depend on them as normal dependencies rather than reaching into sibling folders.
- **Why `lru_cache` on the model/pipeline loaders?** Loading a joblib model or rebuilding the RAG
  pipeline is relatively expensive; an MCP server handles many tool calls over its lifetime, so
  loading once and reusing avoids redundant disk I/O and deserialization on every call.
- **Why does the RAG tool default to `MockLLMClient`?** Consistent with Project 3: the whole
  platform should be runnable and testable with zero API keys configured. Swapping to
  `AnthropicLLMClient` for real answer synthesis is a one-line change in `tools.py`.

## What's next (Phase 2 — complexity increase, on request)

- A fourth tool that **combines** all three: given a `customer_id`, look up risk score, find their
  most recent support ticket's category, and retrieve the relevant retention policy — a genuine
  multi-tool agentic workflow in one call.
- Real LLM backend for the RAG tool (swap `MockLLMClient` → `AnthropicLLMClient`)
- Streamable-HTTP transport (instead of stdio) so the server could run remotely, not just locally
  spawned by the client
- Input validation / guardrails on tool arguments (e.g. reject a `customer_id` that doesn't match
  the expected format before ever touching the model)
- Structured logging of every tool call (what was asked, what was returned, latency) for basic
  observability — the kind of thing a real deployed agent needs

## The complete CustomerIQ portfolio

| # | Project | Concepts |
|---|---|---|
| 1 | CustomerIQ-Risk | Tabular ML: EDA, leakage-safe pipelines, XGBoost, SHAP |
| 2 | CustomerIQ-Voice | NLP: TF-IDF, PyTorch embeddings, overfitting |
| 3 | CustomerIQ-Docs | GenAI: RAG, vector search, hallucination mitigation |
| 4 | CustomerIQ-Agent | Modern AI tooling: MCP, cross-project orchestration |

Four repos, one coherent narrative, each depending on the last — built and verified end-to-end,
not just scaffolded.

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a clearly distinct task: churn risk scoring, support ticket classification, and policy Q&A. There is no overlap in purpose or inputs, so an agent can easily select the correct tool.

Naming Consistency4/5

All names use consistent snake_case and are descriptive noun phrases, which is readable and predictable. However, they do not follow the common verb_noun action pattern, so the convention is consistent but not action-oriented.

Tool Count5/5

Three tools map cleanly to three distinct underlying capabilities (tabular ML, NLP classifier, RAG). The count is well-scoped and each tool earns its place without redundancy.

Completeness4/5

The surface covers the three stated project functions, but lacks supporting operations like customer lookup, ticket history, or score explanation. These are minor gaps that an agent could work around for the core tasks.

Maintenance

ActivityMaintained
ResponsivenessNo issues