mcp服务

jackson f0ff40881c mcp服务 2 hete
services f0ff40881c mcp服务 2 hete
tests f0ff40881c mcp服务 2 hete
tools f0ff40881c mcp服务 2 hete
.env.example f0ff40881c mcp服务 2 hete
.gitignore f0ff40881c mcp服务 2 hete
README.md f0ff40881c mcp服务 2 hete
app.py f0ff40881c mcp服务 2 hete
config.py f0ff40881c mcp服务 2 hete
mcp_protocol.py f0ff40881c mcp服务 2 hete
requirements.txt f0ff40881c mcp服务 2 hete

README.md

Python MCP Gateway

这是一个轻量的 Python MCP Gateway,用来把 Workbuddy 的 MCP 工具调用转发到现有 ThinkPHP 授权接口和工具接口。

当前状态

当前仓库已经具备一个可运行的 stdio MCP 入口,以及一套经过测试的核心模块,覆盖:

  • .env 优先的配置加载
  • Redis token 存储,按员工本机 session_key 隔离
  • 文件型 token 存储兜底
  • auth_code 换票、续期、失效的认证客户端
  • bind_auth_code 授权绑定工具
  • 工具 HTTP 转发客户端
  • query_order 工具注册
  • Gateway 运行时会话管理
  • MCP initialize / tools/list / tools/call 请求处理
  • 本地命令模式下的 stdio MCP 启动入口

当前实现仍然保持“薄网关”原则:

  • 业务认证和权限判断仍然留在 ThinkPHP 项目中
  • Gateway 只负责协议适配、会话管理和 HTTP 转发
  • 第一阶段只开放查询型工具,当前从 query_order 开始

Workbuddy 配置

员工侧 Workbuddy 使用网络 UNC 路径启动 Gateway:

{
  "mcpServers": {
    "fms": {
      "command": "python",
      "args": [
        "\\\\192.168.1.241\\mcp\\app.py",
        "serve-stdio"
      ]
    }
  }
}

不要把 Y:\mcp\app.py 写进 Workbuddy 配置;那只是开发机映射盘视角。

常用命令

以下命令在 mcp/ 目录下执行。

查看工具列表

python app.py list-tools

绑定授权码(开发/排障)

python app.py bind --auth-code AUTH123

员工正式使用时不需要执行这条命令。Workbuddy 拉起 serve-stdio 后,员工应在 Workbuddy 中调用 bind_auth_code 工具完成绑定。

直接调用 query_order(开发/排障)

python app.py call --tool query_order --keyword SO20260706001 --page 1 --limit 20

启动 MCP stdio 服务

python \\192.168.1.241\chenjiacheng\mcp\app.py serve-stdio

这个命令可作为 Workbuddy 本地命令模式下的 MCP 服务入口,由 Workbuddy 自动拉起,不要求员工手动执行。

环境变量

当前代码直接支持以下文档口径的配置键:

  • FMS_API_BASE
  • FMS_AUTH_BASE
  • FMS_TOOLS_BASE
  • FMS_CLIENT_TYPE
  • FMS_TIMEOUT_MS
  • FMS_TIMEOUT_SECONDS
  • FMS_REFRESH_SKEW_SECONDS
  • FMS_LOG_LEVEL
  • FMS_TOKEN_STORE
  • FMS_TOKEN_STORE_PATH
  • FMS_REDIS_HOST
  • FMS_REDIS_PORT
  • FMS_REDIS_DB
  • FMS_REDIS_PASSWORD
  • FMS_REDIS_PREFIX
  • FMS_SESSION_KEY

同时兼容早期实现中的这些键名:

  • MCP_AUTH_BASE_URL
  • MCP_TOOLS_BASE_URL
  • MCP_CLIENT_TYPE
  • MCP_TIMEOUT_SECONDS
  • MCP_REFRESH_SKEW_SECONDS
  • MCP_LOG_LEVEL
  • MCP_TOKEN_STORE
  • MCP_TOKEN_STORE_PATH
  • MCP_REDIS_HOST
  • MCP_REDIS_PORT
  • MCP_REDIS_DB
  • MCP_REDIS_PASSWORD
  • MCP_REDIS_PREFIX
  • MCP_SESSION_KEY

说明:

  • config.py 会自动尝试读取 mcp/.env
  • 同名配置以 .env 优先,系统环境变量只在 .env 缺少该配置时兜底。
  • 如果只提供 FMS_API_BASE,当前实现会同时把它作为 auth 和 tools 的基础地址。
  • 如果 basefmsoperate 部署在不同域名,应分别提供 FMS_AUTH_BASEFMS_TOOLS_BASE

.env 文件

当前 mcp/ 目录建议使用 .env 保存共享 Gateway 配置。

推荐配置如下:

FMS_AUTH_BASE=http://chenjiacheng.base.dahuo.fudingri.com
FMS_TOOLS_BASE=http://chenjiacheng.fmsoperate.dahuo.fudingri.com
FMS_CLIENT_TYPE=workbuddy
FMS_TIMEOUT_SECONDS=10
FMS_REFRESH_SKEW_SECONDS=120
FMS_LOG_LEVEL=info

FMS_TOKEN_STORE=redis
FMS_REDIS_HOST=192.168.1.241
FMS_REDIS_PORT=6379
FMS_REDIS_DB=20
FMS_REDIS_PASSWORD=
FMS_REDIS_PREFIX=fms:mcp:workbuddy:

不要在共享 .env 中写死 FMS_SESSION_KEY。默认情况下 Gateway 会用员工本机的 COMPUTERNAME / USERNAME 自动生成稳定 session_key,Redis key 形如:

fms:mcp:workbuddy:{session_key}

这样多个员工通过同一个 \\192.168.1.241\chenjiacheng\mcp\app.py 启动时,也不会共用同一个 token。

Token 存储

当前推荐使用 Redis:

FMS_TOKEN_STORE=redis

Redis 存储行为:

  • bind_auth_code 成功后把 mcp_token 写入 Redis
  • Redis key 使用 FMS_REDIS_PREFIX + session_key
  • session_key 优先读取 FMS_SESSION_KEY,未配置时自动从员工本机信息生成
  • token 过期时间会同步设置为 Redis TTL
  • revoke 成功后会删除当前 session 的 Redis key

文件存储仍保留为兜底方式:

FMS_TOKEN_STORE=file
FMS_TOKEN_STORE_PATH=C:/fms-mcp/.mcp_token.json

如果使用网络共享 app.py,不要把 FMS_TOKEN_STORE_PATH 指向 \\192.168.1.241\chenjiacheng\mcp\ 共享目录。

本地接入建议

当前员工正式使用路径是:

  1. 员工在后台获取一次性 auth_code
  2. Workbuddy 自动通过 python \\192.168.1.241\chenjiacheng\mcp\app.py serve-stdio 拉起 Gateway
  3. 员工在 Workbuddy 中调用 bind_auth_code,输入授权码完成绑定
  4. Gateway 调用 exchange 时带上 session_key
  5. token 保存到 Redis 当前员工对应的 key
  6. 后续直接使用 query_order 等工具

python app.py bind --auth-code ... 仅保留给开发、联调和排障使用。

测试命令

python -m unittest discover -s tests -p "test_*.py"

当前边界

当前 Gateway 已可作为本地 stdio MCP 进程使用,但仍保持最小边界:

  • 不在 Python 中沉淀额外业务规则
  • 不直接连接业务数据库
  • 不做动态插件加载
  • 第一阶段不开放写入型工具