内容
# 员工管理MCP服务器(EMP-MCP-2026)
一个MCP服务器,通过**stdio**暴露了6个员工管理工具,依赖于合成的JSON员工数据集。
## 工具
| 工具 | 用途 |
|---|---|
| `list_employees` | 过滤/搜索员工(部门、状态、地点、自由文本查询)。 |
| `get_employee` | 通过`employee_id`获取某个员工的完整资料。 |
| `get_org_summary` | 按部门/状态/地点聚合统计人数。 |
| `create_employee` | 新增一名新员工。 |
| `update_employee` | 部分更新现有员工的可变字段。 |
| `deactivate_employee` | 软删除(将状态设置为`inactive`;记录仍可检索)。 |
## 安装
需要Python 3.10+。
```bash
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -e .
```
将`.env.example`复制到`.env`并根据需要进行调整(两个文件中都不包含机密信息——
只有路径和模式标志)。注意:服务器不会自动加载`.env`——`config.py`中没有任何
调用`load_dotenv()`的内容。`python-dotenv`被列为依赖项,但并未连接。
`.env`是关于设置内容的文档;要使其生效,您可以在运行前在shell中导出这些变量,
或者直接在MCP主机服务器配置的`env`块中设置(常见情况——参见下面的“主机配置”),
或者如果您想加载文件,请自行运行`dotenv run -- python -m employee_mcp.server`。
## 运行服务器
```bash
python -m employee_mcp.server
```
首次运行时,如果`EMP_MCP_DATA_PATH`尚不存在,服务器会将提交的种子文件
(`data/employees.seed.json`,5个部门中14名合成员工)复制到该路径,并将其视为可变的工作数据集。
删除该文件以重置到干净的种子文件,以便下次运行。
**Windows注意:** 确保`python`解析为已安装`mcp`的解释器(或指向宿主的`command`指向venv的绝对`python.exe`)。
如果主机以子进程启动此服务器,请优先使用绝对`EMP_MCP_DATA_PATH`路径——相对路径与主机选择的当前工作目录组合是Windows上“已连接但无工具”/“连接关闭”的最常见原因。
在此代码库中,相对`EMP_MCP_DATA_PATH`值针对仓库根目录进行解析(而非进程的cwd),以避免陷入该陷阱,但绝对路径仍是主机配置的最稳健选择。
## 数据存储
员工存储在单个JSON数组文件(`data/employees.json`)中,在启动时加载到
`employee_id -> Employee`字典(`store.py`)中,以便进行O(1)查找。每次写入
(`create_employee` / `update_employee` / `deactivate_employee`)都会重新序列化整个内存数据集,
并将其原子性地持久化——写入`.tmp`文件,然后使用`os.replace()`覆盖目标文件——
这样,在写入过程中发生崩溃或终止不会留下半写入的损坏数据文件。
JSON(而非真正的数据库)是该项目规模的有意选择:种子文件包含14名员工,
该设计面向小规模内存数据集,数量级为数百到数千条记录,而不是企业HRIS卷。
在此规模下,在每次服务器启动时将整个文件加载到内存中几乎没有明显的成本,
而JSON提供了人类可读、可git-diff的存储,无需模式迁移,无需单独的数据库进程或驱动程序安装——
这适合合成数据分配/演示,而不是声称此方法可以扩展到该上限(参见“已知限制”)。
## 配置(环境变量)
| 环境变量 | 默认值 | 含义 |
|---|---|---|
| `EMP_MCP_DATA_PATH` | `./data/employees.json` | 可变工作数据文件。 |
| `EMP_MCP_READ_ONLY` | `false` | 当为`true`时,所有写入工具均拒绝并返回`READ_ONLY`错误,不进行任何更改。 |
| `EMP_MCP_DEBUG` | `false` | 当为`true`时,意外错误消息中包含回溯信息。在正常/演示使用中保持`false`。 |
| `EMP_MCP_AUDIT_PATH` | `./logs/audit.log` | 审计日志文件路径(每个工具调用一行JSON)。 |
没有任何机密信息被配置在任何地方——这些只是模式标志和路径。
## 主机配置
```json
{
"mcpServers": {
"employee-mcp": {
"command": "<ABSOLUTE-PATH-TO-YOUR-python.exe>",
"args": ["-m", "employee_mcp.server"],
"env": {
"PYTHONPATH": "<ABSOLUTE-PATH-TO-THIS-REPO-ROOT>",
"EMP_MCP_DATA_PATH": "<ABSOLUTE-PATH-TO-THIS-REPO-ROOT>\\data\\employees.json",
"EMP_MCP_AUDIT_PATH": "<ABSOLUTE-PATH-TO-THIS-REPO-ROOT>\\logs\\audit.log",
"EMP_MCP_READ_ONLY": "false",
"EMP_MCP_DEBUG": "false"
}
}
}
}
```
填写您自己的机器的两个占位符(每个路径都必须是绝对路径——相对路径与主机选择的当前工作目录组合是Windows上“已连接但无工具”/“连接关闭”的最常见原因):
- **`<ABSOLUTE-PATH-TO-YOUR-python.exe>`** — 您已安装此项目的解释器。如果您使用了venv(按照上面的安装说明),请激活它并在PowerShell中运行`(Get-Command python).Source`,然后粘贴该路径,例如`C:\Users\you\...\Employee_management_MCP_server\.venv\Scripts\python.exe`。
- **`<ABSOLUTE-PATH-TO-THIS-REPO-ROOT>`** — 您的机器上包含此README的文件夹,例如`C:\Users\you\Documents\Employee_management_MCP_server`。它在上面被使用了四次:一次用于`PYTHONPATH`,以及作为数据文件路径的前缀——请一致地替换它们。
`PYTHONPATH`在`command`指向一个裸解释器(未在此项目中安装`pip install -e .`)时尤其重要——如果没有它,`python -m employee_mcp.server`无法找到`employee_mcp`包,主机将显示服务器已失败/断开连接。如果`command`指向一个venv的`python.exe`,您已经在那里运行了`pip install -e .`,则`PYTHONPATH`是不必要的——可编辑安装使包可以从任何地方导入——但留着它也不会造成伤害。
## 使用MCP Inspector进行调试
```bash
npx @modelcontextprotocol/inspector python -m employee_mcp.server
```
这将打开一个本地Web UI(`http://localhost:6274/...`),您可以在其中运行`tools/list`,检查每个工具的JSON模式,并使用从该模式构建的表单交互式调用工具。要测试只读模式,请在Inspector的“环境变量”面板中设置`EMP_MCP_READ_ONLY=true`后再连接。
## 人机协作(HITL)期望
此服务器不通过自己的确认步骤控制写入工具——它期望**MCP主机**在调用实际写入工具之前,向用户显示写入工具的已解析输入(即将创建/更新/停用的员工字段),就像大多数MCP感知聊天主机对后果严重的工具调用的处理方式一样。当`EMP_MCP_READ_ONLY=true`时,这是主机/上下文中不可用的硬性备份。
## 电子邮件唯一性解释
要求是“电子邮件在活跃员工中唯一”。此服务器强制执行以下操作:在创建和更新时,如果任何其他状态为“active”的员工已经拥有该电子邮件(不区分大小写比较),则拒绝并返回`DUPLICATE_EMAIL`错误。两个`inactive`/`on_leave`记录可以与彼此或与后来成为活跃员工的旧电子邮件共享同一个电子邮件地址——约束仅在写入时查看当前活跃的持有者。
## 其他有文档记录的解释
- **`list_employees`限制> 100:** 限制为100,不会被拒绝。
- **停用已经停用的员工:** 成功地幂等;响应报告`previous_status == new_status == "inactive"`而不是报错。
- **`update_employee`和`manager_id`:** 省略`manager_id`意味着“无更改”。目前无法通过`update_employee`显式清除员工的`manager_id`为空(该工具无法区分“字段省略”和“字段显式设置为null”)- 重新分配给不同的经理,或在创建时编辑`manager_id`。未来的迭代可以通过哨兵/取消设置值来解决此问题,如果该空白对实际使用很重要。
- **SDK级参数验证:** FastMCP在我们的工具代码运行之前,根据生成的JSON模式验证每个工具的参数(类型、枚举)。此类失败不会导致崩溃,并显示为`isError: true`,并带有消息`VALIDATION_ERROR: invalid arguments for <tool>: ...`,并且仍然记录为`logs/audit.log`中的一行,状态为`status: "error"`,代码为`code: "VALIDATION_ERROR"`——一个薄的服务器级包装器(`_AuditedFastMCP`在`server.py`中)专门用于保证这一点,因为它不会免费发生。
## 审计日志
每个工具调用都会在`logs/audit.log`(如果缺少则在启动时创建)中追加一行JSON,包含`timestamp`、`tool`、`args_summary`(PII-light - 邮箱本地部分被屏蔽,全名被省略)、`status`(`ok`/`error`)、`latency_ms`,以及在失败时一个分类代码。stdout永远不会写入任何内容 - stdout保留用于JSON-RPC帧;所有日志都转到此文件和/或stderr。
## 错误分类
`VALIDATION_ERROR`、`DUPLICATE_EMAIL`、`NOT_FOUND`、`MANAGER_CYCLE`、`READ_ONLY`、`INTERNAL_ERROR` - 参见`employee_mcp/errors.py`。每个领域故障消息的格式为`CODE: human-readable detail`,例如`NOT_FOUND: no employee with id EMP-999`。
## 测试
```bash
python -m pytest tests/test_smoke.py -v
```
每个测试都会启动一个真实的服务器作为子进程,并通过stdio驱动它,使用`mcp.ClientSession`来锻炼协议发现、6种工具的成功/失败路径、只读模式、错误参数的鲁棒性和审计日志的完整性。这是 - 而不是“LLM说它有效”- 证明传输合同成立的证据。
## 演示
参见`demo/demo_transcript.md`,演示了三个流程(搜索/列表、创建、组织摘要)以及一个故意失败的路径(重复电子邮件),针对种子数据集,并显示了每次调用的相应`logs/audit.log`行。
## 已知限制
- `list_employees`没有分页游标超过`limit`/限制——在该项目针对的小规模内存数据集上运行良好(最多几千条记录),但在更大规模下需要重新审视。
- `update_employee`无法将`manager_id`清除为空(参见上文)。
- 审计日志位置默认为`logs/audit.log`,可通过`EMP_MCP_AUDIT_PATH`配置,但没有强制执行与`EMP_MCP_DATA_PATH`的一一对应关系——服务器实例如果不覆盖它,则共享相同的默认文件。
- 设计范围外:无HTTP/SSE传输,无身份验证/OAuth提供程序,无前端UI,无真正的HRIS/工资集成,无扩展工具(`search_by_skill`、`get_direct_reports`、`export_employees_csv`,MCP资源)。