Skip to main content
Glama

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.

Related MCP server: Works With Agents MCP Server

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:

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:

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):

{
  "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 MockLLMClientAnthropicLLMClient)

  • 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.

Available Tools

3 tools
churn_risk_scoreB

Look up a customer's churn risk score (Project 1: tabular ML model).

Args:
    customer_id: Customer id in "CUST-XXXXXX" format, e.g. "CUST-000042".
ParametersJSON Schema
NameRequiredDescriptionDefault
customer_idYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. 'Look up' implies a read-only operation, and naming the model type adds slight context, but it says nothing about what the score means, its range, whether it is cached/stale, latency, or error behavior. For a scoring tool with zero annotation coverage, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then the single argument with its format and example. No filler sentences. The 'Args:' block is a reasonable structure for a one-parameter tool, though slightly formal for such a short description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description is the only place to explain the return value, and it never says what the churn risk score looks like (range, units, interpretation). For a minimal single-parameter lookup it is mostly sufficient to call correctly, but the result is unexplained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the schema only labels the field 'Customer Id', so the description must compensate. It does: it specifies the required format 'CUST-XXXXXX' and supplies a concrete example 'CUST-000042', which is exactly the information needed to form a valid call.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Look up a customer's churn risk score'. It further identifies the underlying model ('Project 1: tabular ML model'). It does not position itself against siblings, but the siblings (support_ticket_category, policy_question) are unrelated lookups so differentiation is not really needed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to use this tool versus the other lookups or any alternative. There are no prerequisites, no exclusions, and no indication of the context in which churn scoring is appropriate (e.g., during retention workflows).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

policy_questionB

Answer a question grounded in company policy documents (Project 3: RAG).

Args:
    question: A natural-language question about billing, cancellation,
        technical support, or account security policy.
ParametersJSON Schema
NameRequiredDescriptionDefault
questionYes

TDQS

B3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It does not disclose whether answers are cached, whether the policy corpus is fixed or updated, whether the call has latency/cost implications from RAG retrieval, or what happens with out-of-domain questions. '(Project 3: RAG)' names a mechanism but conveys nothing actionable about behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core sentence is front-loaded and efficient, but the 'Args:' block reads like a docstring leaking into the description and the '(Project 3: RAG)' parenthetical adds internal-context noise without agent value. Roughly half the content earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-annotation tool with a 0%-coverage schema and no output schema, the description should explain the return shape (text answer? citations? confidence?) and domain boundaries. It instead borrows structure from a Python docstring, leaving the agent guessing about output and failure modes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does list acceptable question domains (billing, cancellation, technical support, account security). That is useful scoping for the single 'question' param, but it stops short of syntax, format, or length guidance. Marginal but real value over the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: answering a question grounded in company policy documents. The parenthetical '(Project 3: RAG)' hints at the retrieval mechanism but is cryptic internal scaffolding rather than useful differentiation. It does distinguish the tool from siblings churn_risk_score and support_ticket_category, which are classification/scoring tools rather than Q&A.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implicitly scopes usage by naming policy domains (billing, cancellation, technical support, account security), which tells the agent what kinds of questions belong here. However, it offers no explicit when-to-use vs alternatives guidance, no exclusion of churn_risk_score or support_ticket_category, and no prerequisites. Implied usage only.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

support_ticket_categoryB

Classify a support ticket's category (Project 2: NLP text classifier).

Args:
    ticket_text: The raw customer support ticket text.
ParametersJSON Schema
NameRequiredDescriptionDefault
ticket_textYes

TDQS

B3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. It says this is an NLP text classifier, but does not describe output format, returned categories, confidence, permissions, rate limits, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loaded with the core action. The parenthetical project identifier adds little, and the Args block is functional but slightly noisy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a classifier with one input, no output schema, and no annotations, the description is thin. It does not explain what categories are returned, what the output looks like, or when the tool should be used.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for the single parameter. It does so by explaining that ticket_text is the raw customer support ticket text, which is more meaningful than the schema's bare 'Ticket Text' title.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb and resource: classify a support ticket's category. It also identifies the tool as an NLP text classifier. It does not explicitly distinguish itself from siblings churn_risk_score or policy_question, but the purpose is still clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to use this tool versus alternatives. It does not mention the sibling tools, prerequisites, or conditions that select this classifier over churn risk scoring or policy question answering.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedchurn_risk_score
    • First observedpolicy_question
    • First observedsupport_ticket_category

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

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes RAG and document intelligence pipelines as 8 composable tools for MCP-compatible clients, enabling querying, indexing, classifying, extracting, and assessing documents.
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes order status lookup and knowledge base search tools from the Support Agent AI over MCP, enabling MCP clients to handle customer support queries with grounded, citation-backed answers.
    MIT