MCP Demo — Enterprise AI Delivery Server
by IanLi592
README.md
# MCP Demo — Enterprise AI Delivery Server
> **English summary:** A runnable Model Context Protocol server demo with three tools (read-only SQL, BM25 knowledge retrieval, ticket creation), resources, a prompt, 20 tests, and an end-to-end smoke test. It runs locally over stdio and needs no cloud account.
一个可运行的 MCP(Model Context Protocol)server 示例,用 **3 个 tool + 2 类 resource + 1 个 prompt** 模拟企业 AI 交付里最常见的三类能力:
- **只读 SQL 查询**:查客户、订单、月度营收;
- **知识库检索**:BM25 检索 IT / 安全 / AI 治理 SOP,返回可引用的 `kb://docs/...`;
- **工单创建**:校验字段后写入 SQLite,返回工单 ID 和 SLA。
> 这个项目是为康美包(SIG Combibloc)`Expert Application Delivery (AI Agent & LLM)` 岗位准备的实操 demo。它对应 JD 里的 **MCP servers / tools / APIs、企业系统集成、生产工程、权限与治理、human-in-the-loop**,不是玩具聊天机器人。
---
## 1. 它演示了什么
| JD 要求 | 本项目里的对应 |
|---|---|
| 开发 MCP servers、tools、APIs,把 Agent 接到知识库 / 运营系统 / 企业应用 | `query_sales_db`、`search_knowledge_base`、`create_support_ticket`;resources 暴露 schema、知识库文档、工单详情 |
| RAG / 检索增强 | `mcp_demo/kb.py`:Markdown 分块 + 中英文混合 tokenizer + BM25,返回 `citation` |
| 生产工程:Git、测试、CI/CD、容器、日志监控 | 20 个 pytest 用例、端到端 smoke test、GitHub Actions、类型注解、统一错误返回、环境变量配置 |
| 安全 / 权限 / 治理 | SQL 词法白名单 + SQLite `mode=ro` + authorizer + 50 行限制 + 3 秒超时;工单写入走独立校验路径 |
| Human-in-the-loop | `create_support_ticket` 是写操作,server instructions 和 prompt 都要求先与用户确认 |
| Claude Code / coding-agent workflow | 项目用 Claude Code 搭建;README 记录架构、取舍和演示脚本 |
| 可扩展到 Azure / Fabric / M365 | 见第 9 节「替换成真实企业系统」 |
---
## 2. 架构
```text
┌──────────────────────┐
│ Claude Code / │
│ MCP Inspector │
└──────────┬───────────┘
│ MCP over stdio (JSON-RPC)
▼
┌──────────────────────────────────────────────┐
│ server.py (FastMCP) │
│ │
│ Tools Resources │
│ ├─ query_sales_db ├─ db://schema │
│ ├─ search_knowledge_base ├─ kb://docs │
│ └─ create_support_ticket ├─ kb://docs/{id} │
│ └─ ticket://{id} │
│ Prompt │
│ └─ triage_ticket │
└───────┬──────────────────┬───────────────────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────────────┐
│ data/app.db │ │ data/kb/*.md │
│ SQLite │ │ 5 份 IT / 安全 / AI │
│ 只读查询 + │ │ 治理 SOP │
│ 工单写入 │ │ BM25 检索 │
└───────────────┘ └──────────────────────┘
```
**设计取舍:**
- 用 SQLite 而不是 PostgreSQL / Fabric:demo 零依赖、可离线跑;换成真实数据库时只改 `mcp_demo/db.py` 的连接层,tool 签名不变。
- 用纯 Python BM25 而不是 Azure AI Search:不需要 API key、不需要网络;换成 Azure AI Search 时只改 `mcp_demo/kb.py` 的 `search()`。
- 写操作只有 `create_support_ticket` 一个,且字段全部白名单校验;生产环境应再加审批流和审计。
---
## 3. 目录结构
```text
mcp-demo/
├── server.py # MCP server 入口:tools / resources / prompt
├── mcp_demo/
│ ├── config.py # 路径、行列限制、超时,全部可用环境变量覆盖
│ ├── db.py # 只读 SQL 守卫 + schema 读取
│ ├── kb.py # Markdown 分块 + BM25 检索
│ ├── seed.py # 建库、样例数据、5 份知识库文档
│ └── tickets.py # 工单校验 + 写入 + SLA
├── scripts/
│ ├── seed_data.py # 重建 demo 数据
│ └── smoke_test.py # 端到端:启动 server,调用每个 tool/resource/prompt
├── tests/
│ ├── test_db.py # 只读守卫、行数限制、聚合查询
│ ├── test_kb.py # 中文 / 英文检索、文档读取
│ └── test_tickets.py # 工单创建、字段校验
├── data/
│ ├── app.db # 运行 seed 后生成(gitignored)
│ └── kb/*.md # 5 份样例 SOP
├── screenshots/ # 6 张演示截图
├── .github/workflows/ci.yml # GitHub Actions:pytest + smoke test
├── requirements.txt
├── pyproject.toml
├── .mcp.json.example # Claude Code 项目级配置示例(复制为 .mcp.json)
├── LICENSE
├── SECURITY.md
└── README.md
```
---
## 4. 快速开始(Windows)
要求:**Python 3.10+**(本机测试用 3.14.5 + `mcp` 1.30.0)。
```powershell
git clone https://github.com/IanLi592/mcp-demo.git
cd mcp-demo
# 1) 虚拟环境(推荐)
python -m venv .venv
.\.venv\Scripts\Activate.ps1
# 如果 PowerShell 禁止激活脚本:
# Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# 2) 安装依赖(国内建议加清华镜像)
python -m pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 3) 建库 + 样例知识库
python scripts/seed_data.py
# 4) 跑单元测试 + 端到端 smoke test
python -m pytest -q
python scripts/smoke_test.py
# 5) 启动 server(stdio;会阻塞等待 MCP 客户端)
python server.py
```
`python server.py` 启动后不会打印 Web 页面,这是正常的:它通过 **stdio** 和 MCP 客户端通信。按 `Ctrl+C` 退出。
> macOS / Linux 把激活命令换成 `source .venv/bin/activate`,其余命令相同。
---
## 5. 接到 Claude Code
### 方式 A:全局注册(推荐,任意目录都能用)
```powershell
claude mcp add --scope user mcp-demo -- python /absolute/path/to/mcp-demo/server.py
claude mcp list
```
然后在 Claude Code 里输入 `/mcp`,确认 `mcp-demo` 是 connected。
> 如果 `python` 不在 PATH,先用 `where python`(Windows)或 `which python`(macOS/Linux)找到绝对路径,例如:
> `claude mcp add --scope user mcp-demo -- C:\Python314\python.exe C:\path\to\mcp-demo\server.py`
### 方式 B:项目级配置
`.mcp.json` 是本地配置,已被 gitignore(避免把绝对路径提交到仓库)。复制示例并改成你的路径:
```powershell
Copy-Item .mcp.json.example .mcp.json
# 编辑 .mcp.json,把 command / args / MCP_DEMO_DATA_DIR 改成你的 Python 和仓库绝对路径
claude
```
Claude Code 会读取项目根目录的 `.mcp.json`。Windows 路径示例:`"command": "C:\\path\\to\\python.exe"`、`"args": ["C:\\path\\to\\mcp-demo\\server.py"]`。
### 方式 C:MCP Inspector(浏览器调试)
```powershell
mcp dev server.py
```
需要 Node.js(Inspector 通过 `npx` 启动)。没有 Node 就用 `python scripts/smoke_test.py`,它覆盖了同样的调用链。
---
## 6. Tools / Resources / Prompt
### Tools
| Tool | 作用 | 关键参数 | 返回 |
|---|---|---|---|
| `query_sales_db` | 只读 SQL 查询 | `sql`:单条 `SELECT` / `WITH` | `columns`、`rows`、`row_count`、`truncated` |
| `search_knowledge_base` | BM25 检索知识库 | `query`、`top_k`(1–10) | `results[]`,每条带 `doc_id`、`score`、`citation`、`text` |
| `create_support_ticket` | 创建 mock 工单 | `title`、`description`、`priority`、`requester`、`category` | `ok`、`ticket`、`sla`、`resource` |
### 实际运行截图
**知识库检索返回 citation(`search_knowledge_base`):**

**Human-in-the-loop:写操作前必须用户确认(`create_support_ticket`):**

> 截图里的 `auto mode on` 是 Claude Code 的权限模式;`create_support_ticket` 前的人工确认来自 server instructions / prompt 的 workflow 要求,不依赖 UI 权限模式。
<details>
<summary>展开完整演示截图(另 4 张)</summary>
**首次连接授权:**

**server 连接状态:**

**查询营收:**

**工单创建完成:**

</details>
### Resources
| URI | 作用 |
|---|---|
| `db://schema` | 数据库表结构(写 SQL 前先读) |
| `kb://docs` | 知识库文档列表 |
| `kb://docs/{doc_id}` | 读取某份文档全文,例如 `kb://docs/01-vpn-access` |
| `ticket://{ticket_id}` | 读取某张工单,例如 `ticket://TCK-20261105-ABC123` |
### Prompt
| 名称 | 作用 |
|---|---|
| `triage_ticket` | 给一张工单,输出分类、优先级、前三步诊断、是否需要人工审批 |
### 在 Claude Code 里可以这样问
```text
先读 db://schema,然后查 2026 年各区域的总营收,按降序排列。
VPN 权限怎么申请?请引用知识库文档。
帮我给 alice@example.com 创建一个 high 优先级工单:
VPN 连不上,影响远程办公,已经重试过客户端。
创建前先把 title / description / priority 给我确认。
```
最后一条会触发 **human-in-the-loop**:server instructions 要求 Agent 先确认再写。
---
## 7. 安全设计
这个 demo 的重点不是「能查数据」,而是「**怎么让 Agent 安全地查数据**」。
### 7.1 只读 SQL 的四层保护
1. **词法白名单**:只允许单条 `SELECT` / `WITH`;拒绝 `INSERT`、`UPDATE`、`DELETE`、`DROP`、`ALTER`、`CREATE`、`PRAGMA`、`ATTACH`、`VACUUM` 等关键字;拒绝注释和多语句。
2. **SQLite `mode=ro`**:连接串 `file:...?mode=ro`,数据库层面只读。
3. **Authorizer**:只放行 `SQLITE_SELECT` / `SQLITE_READ` / `SQLITE_FUNCTION` / `SQLITE_RECURSIVE`,其余全部 `SQLITE_DENY`。
4. **行数与超时**:外层包 `LIMIT 51`,最多返回 50 行;`set_progress_handler` 3 秒超时,防止全表扫描拖垮 server。
### 7.2 写操作的最小权限
- 只有 `create_support_ticket` 一个写 tool;
- 不拼接 SQL,使用参数化 INSERT;
- 标题 5–120 字、描述 10–2000 字、优先级 / 分类枚举校验;
- 返回统一错误,不把堆栈或数据库路径暴露给模型。
### 7.3 知识库与数据边界
- 知识库只读本地 Markdown,不发外部请求;
- 样例数据全是合成数据,不含真实客户 / 个人信息;
- 生产环境应加:PII 脱敏、按工具做 RBAC、审计日志、速率限制、审批流、模型网关。
---
## 8. 测试
```powershell
python -m pytest -q # 20 个单元测试
python scripts/smoke_test.py # 启动真实 server,调用 3 tools + 3 resources + 1 prompt
```
测试覆盖:
- 合法 SELECT / 聚合查询;
- 8 种危险 SQL 被拒绝(DELETE / UPDATE / DROP / 多语句 / 注释 / PRAGMA / ATTACH / INSERT);
- 50 行截断;
- 中文查询命中正确文档;
- 工单创建、读取、非法优先级 / 过短描述被拒绝。
---
## 9. 替换成真实企业系统(面试时可讲)
| 当前 demo | 生产替换 | 改动点 |
|---|---|---|
| SQLite `query_sales_db` | Microsoft Fabric SQL endpoint / Azure SQL / PostgreSQL 只读副本 | 改 `mcp_demo/db.py` 的连接层;tool 签名不变 |
| 纯 Python BM25 | Azure AI Search / pgvector / Milvus + embedding | 改 `mcp_demo/kb.py` 的 `search()`;返回格式不变 |
| mock ticket SQLite | ServiceNow / Jira Service Management / 内部工单 API | 改 `mcp_demo/tickets.py` 的 `create_ticket()`;加 OAuth |
| `ticket://` resource | Microsoft Graph / SharePoint / Teams | 加 Graph API client;resource URI 可保留 |
| stdio transport | Streamable HTTP + OAuth | `FastMCP.run(transport="streamable-http")`;加网关鉴权 |
| 无 trace | OpenTelemetry / Langfuse | 在 tool 入口加 span:工具名、参数摘要、耗时、结果状态 |
**面试时的一句话:**
> 「这个 demo 的重点不是 SQLite,而是 MCP 的接口设计:工具边界、只读保护、写操作确认、可引用的 resource、可测试的错误路径。换成 Fabric / Azure AI Search / ServiceNow 时,tool 的 schema 不用变,只换实现层。」
---
## 10. 5 分钟演示脚本
1. **开场(30 秒)**:这是一个企业 AI 交付的 MCP server,三个 tool:查数据、查知识库、建工单;resources 暴露 schema 和文档;prompt 做工单分诊。
2. **看 schema(30 秒)**:让 Claude Code 读 `db://schema`,说明「Agent 不应该猜表结构」。
3. **查营收(1 分钟)**:问「2026 年各区域总营收,按降序」,展示 SQL tool 返回 columns / rows / truncated。
4. **查知识库(1 分钟)**:问「VPN 权限怎么申请」,展示 BM25 命中 `01-vpn-access` 和 `citation`。
5. **建工单(1 分钟)**:让 Agent 先复述 title / description / priority,确认后创建,展示 ticket ID + SLA,再读 `ticket://...`。
6. **讲设计(1 分钟)**:只读 SQL 四层保护、写操作最小权限、human-in-the-loop、如何换 Azure AI Search / Fabric / Graph。
---
## 11. 常见问题
| 问题 | 解决 |
|---|---|
| `ModuleNotFoundError: No module named 'mcp'` | 激活 venv,`python -m pip install -r requirements.txt` |
| `Database not found` | `python scripts/seed_data.py` |
| Claude Code 看不到 server | `claude mcp list`;确认路径;重启 Claude Code;必要时用 python 绝对路径 |
| `mcp dev` 启动失败 | 需要 Node.js;没有 Node 就跑 `python scripts/smoke_test.py` |
| 中文输出乱码 | PowerShell 执行 `$env:PYTHONUTF8=1`,或 `chcp 65001` |
| 想改数据目录 | 设置 `MCP_DEMO_DATA_DIR` / `MCP_DEMO_DB` / `MCP_DEMO_KB_DIR` 环境变量 |
---
## 12. 下一步可以加什么
- [ ] Azure AI Search + embedding,替换 BM25;
- [ ] Microsoft Fabric / OneLake 的只读 SQL endpoint;
- [ ] Microsoft Graph:SharePoint 文档、Teams 消息;
- [ ] OAuth / API key + 按工具 RBAC;
- [ ] 审计日志 / OpenTelemetry trace;
- [ ] 写操作审批流(工单创建前人工确认);
- [ ] Streamable HTTP transport + Docker;
- [x] GitHub Actions:pytest + smoke test(`.github/workflows/ci.yml`)。
---
**License:** MIT,见 [LICENSE](LICENSE)。除文中提到的目标公司外,样例客户、工单和数据均为合成,不代表真实业务。