Customer Service Data Analyst MCP Server
Exposes five tools for querying and analyzing a customer-support dataset, including listing categories/intents, filtering, counting, and summarizing records.
README
Customer Service Data Analyst Agent
A LangGraph ReAct agent that answers questions about the Bitext customer-support dataset (26,872 tagged support messages across 11 categories and 27 intents). It routes each question, calls typed tools over the data, remembers the conversation and a per-user profile across restarts, and exposes its tools over the Model Context Protocol.
It handles three kinds of question:
| Type | Example | What happens |
|---|---|---|
| Structured | "How many refund requests are there?" | chains tools → 997 (3.71%) |
| Unstructured | "Summarize the FEEDBACK category." | samples rows → a grounded summary |
| Out-of-scope | "Who is the president of France?" | politely declined, never answered from general knowledge |
Built by Carmit Shaemesh Haas for Nebius Academy Assignment 3.
Demo
The CLI prints every step of the agent's reasoning. Below, it answers a question and then resolves a follow-up ("what about cancellations?") — noticing a wrong filter and retrying:

The Streamlit UI shows the same reasoning in a chat, with a session switcher and the live user profile in the sidebar.
| A structured question | The query recommender | An out-of-scope decline |
|---|---|---|
![]() |
![]() |
![]() |
Architecture
The agent is a LangGraph graph. A dedicated router classifies every question before any tool is chosen; out-of-scope questions are refused structurally (they never reach the generation model's general knowledge). In-scope questions enter the ReAct loop, and a profile-update step distills what it learned about the user.

The compiled LangGraph itself (auto-rendered from the code):

The editable source for the system diagram is
docs/architecture.drawio.
Pieces:
- Router (
src/cs_agent/agent/router.py) — labels a questionstructured,unstructured,out_of_scope, orrecommend, using the small model with typed structured output (and a plain-text fallback). - Tools (
src/cs_agent/tools/) — five Pydantic-typed tools (list_categories,list_intents,filter_records,count_records,summarize_category) implemented as pure functions over a pandas DataFrame. The agent and the MCP server both call these same functions, so they can never drift apart. - Memory — two kinds:
- Episodic: a LangGraph SqliteSaver checkpoint per
--session, so a conversation resumes after a restart and follow-ups ("what about refunds?") resolve. - Semantic: a per-user profile in
profiles/<user>.md(name, interests, preferences), distilled after each answered turn and injected into the prompt.
- Episodic: a LangGraph SqliteSaver checkpoint per
- Guardrails — a
declinenode for out-of-scope questions and a graceful fallback afterMAX_ITERATIONS(12) so the loop never spins forever. - MCP — a FastMCP server (
mcp_server/server.py) exposes the same five tools to any MCP client.
Model choice
Both models run on Nebius Token Factory (OpenAI-compatible). The agent uses two, on purpose:
| Role | Model | Why |
|---|---|---|
| Generation, tool calling, summaries, recommendations | meta-llama/Llama-3.3-70B-Instruct |
reliable OpenAI-style function calling and grounded writing |
| Routing + profile distillation | Qwen/Qwen3-30B-A3B-Instruct-2507 |
a Mixture-of-Experts model with ~3B active parameters: much cheaper and faster than the 70B, and strong at short classification and merge tasks |
Routing and profile-merging are easy, high-volume jobs, so they go to the small fast model;
the heavier reasoning and writing go to the large one. Both IDs live in
src/cs_agent/config.py and can be overridden via .env.
Quickstart (clone to running in ~5 minutes)
Prerequisites: Python 3.11+, a Nebius Token Factory
API key, and uv (recommended) or pip.
# 1. clone
git clone https://github.com/CarmitHaas/customer-service-agent-carmit-haas.git
cd customer-service-agent-carmit-haas
# 2. install (creates a venv and installs the package + deps)
uv sync
# --- or with pip ---
# python -m venv .venv && source .venv/bin/activate
# pip install -e .
# 3. add your API key
cp .env.example .env
# edit .env and set NEBIUS_API_KEY=...
# 4. run the CLI
uv run python main.py --session demo --user carmit
On first run the dataset (~27k rows) is downloaded from Hugging Face once and cached to
data/bitext.parquet, so later runs start instantly and work offline.
Using the CLI
uv run python main.py --session demo --user carmit
--session names the conversation (resume it later with the same value); --user selects
the persistent profile. Every tool call and observation is printed as it happens. Try:
How many refund requests are there?
What categories exist in the dataset?
What is the distribution of intents in the ACCOUNT category?
Summarize the FEEDBACK category.
What should I query next? # the recommender: suggests, you confirm, it runs
What do you remember about me? # answered from your profile
Who is the president of France? # politely declined
To see memory survive a restart: ask something, exit, relaunch with the same
--session, and ask a follow-up like "what about shipping?".
Using the Streamlit app
uv run streamlit run src/cs_agent/ui/streamlit_app.py
Chat in the browser; the reasoning steps appear in a collapsible panel and the sidebar has the session switcher and the live profile.
MCP server
Start the server (stdio transport):
uv run python mcp_server/server.py
Connect a client and call a tool. A runnable example is in
mcp_server/client_demo.py:
import asyncio
from fastmcp import Client
async def main():
async with Client("mcp_server/server.py") as client:
tools = await client.list_tools()
print([t.name for t in tools])
result = await client.call_tool("count_records", {"intent": "get_refund"})
print(result.data) # {'count': 997, 'total': 26872, 'pct': 3.71, ...}
asyncio.run(main())
Run it directly:
uv run python mcp_server/client_demo.py
Project layout
customer-service-agent-carmit-haas/
├── main.py # CLI entry point
├── src/cs_agent/
│ ├── config.py # endpoint, model IDs, paths, MAX_ITERATIONS
│ ├── data.py # cached dataset loader
│ ├── tools/
│ │ ├── schemas.py # Pydantic input/return models + tool descriptions
│ │ └── analytics.py # pure analysis functions (single source of truth)
│ ├── agent/
│ │ ├── state.py # graph state
│ │ ├── llm.py # Nebius model factories
│ │ ├── router.py # query router node
│ │ ├── tool_bindings.py # tools as LangChain @tool
│ │ ├── graph.py # the LangGraph wiring
│ │ ├── profile.py # per-user profile
│ │ └── persistence.py # SqliteSaver checkpointer
│ └── ui/streamlit_app.py # Streamlit chat (Bonus A)
├── mcp_server/
│ ├── server.py # FastMCP server (Task 3)
│ └── client_demo.py # minimal MCP client
├── tests/test_analytics.py # tool tests (no API key needed)
└── docs/ # diagrams + screenshots
Tests
uv run pytest
The tests cover the pure analysis tools against known dataset facts and need no API key.
Notes
- Out-of-scope refusal is enforced structurally (a dedicated
declinenode), not just by a prompt instruction, so the model can't be talked into answering off-topic questions. - The recommender proposes with a no-tools model, so it can suggest but never execute; a
pending_suggestionflag makes the suggest → refine → confirm loop deterministic.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。


