Files
LoveLosita 1ed558b488 Version: 0.4.8.dev.260308
feat: 🏗️ 实现 Agent 消息可靠异步持久化(Outbox + Kafka)

* 新增 Outbox 数据模型与消息载荷定义,位于 `backend/model/outbox.go`
* 新增 Outbox DAO,支持创建、扫描、发布标记、失败重试与消费落库事务,位于 `backend/dao/outbox.go`
* 新增 Kafka 基础封装,包含配置、生产者、消费者与消息包装,位于 `backend/kafka` 文件夹

  * `config.go`:Kafka 配置文件
  * `producer.go`:Kafka 生产者
  * `consumer.go`:Kafka 消费者
  * `envelope.go`:消息封装处理
* 新增异步管道服务,处理扫描投递与消费落库,位于 `backend/service/agent_async_pipeline.go`
* 接入 Agent 聊天链路的可靠持久化,替换原有 goroutine 直接写库逻辑,位于 `backend/service/agent.go`
* 启动流程接入管道初始化与启动,位于 `backend/cmd/start.go`
* 增加 Kafka 配置项,更新 `backend/config.yaml` 与 `backend/config.example.yaml`
* 引入 Kafka 依赖:`github.com/segmentio/kafka-go`(见 `backend/go.mod`, `backend/go.sum`)

fix: 🐛 修复首启偶发 user 消息重复落库问题

* 解决因 Outbox 状态并发回写竞态,导致 `consumed` 被晚到的 `published` 覆盖的问题
* 在 `MarkPublished` 中增加条件,避免覆盖已标记为 `consumed` 或 `dead` 的消息,修复位置:`backend/dao/outbox.go`

perf:  更新 Docker Compose 配置与 Kafka 相关服务

* 更新 `docker-compose.yml` 文件,新增 Kafka 配置与服务

fix: 🧹 优化缓存删除逻辑

* 在 `cache deleter` 中忽略了 `model.AgentOutboxMessage`、`model.ChatHistory` 与 `model.AgentChat` 这三个结构体
* 防止这些结构体对应的表单删除缓存时,导致控制台消息爆炸
2026-03-08 12:53:54 +08:00

214 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# smartflow-mcp-server (MVP)
用于让 Codex 通过 MCPstdio只读访问 MySQL 与 Redis面向接口联调与测试。
## 1. 功能范围(第一阶段)
只实现 3 个只读工具:
1. `mysql_query_readonly`
2. `redis_get`
3. `redis_scan`
未实现任何写操作工具。
## 2. 目录结构
```text
infra/smartflow-mcp-server
├─ cmd/server/main.go
├─ internal
│ ├─ audit
│ ├─ config
│ ├─ envutil
│ ├─ mcp
│ ├─ ratelimit
│ ├─ security
│ ├─ store
│ └─ tools
├─ .env.example
├─ go.mod
└─ README.md
```
## 3. 快速启动
```bash
go mod tidy
go test ./...
go run ./cmd/server
```
服务采用 stdio MCP 协议,不会启动 HTTP 端口。
## 4. 配置说明(全部来自环境变量)
复制并编辑:
```bash
cp .env.example .env
```
关键变量:
- `MYSQL_HOST` / `MYSQL_PORT` / `MYSQL_USER` / `MYSQL_PASSWORD` / `MYSQL_DATABASE`
- `REDIS_ADDR` / `REDIS_PASSWORD` / `REDIS_DB`
- `MYSQL_ALLOWED_DATABASES`:逗号分隔
- `MYSQL_ALLOWED_TABLES`:逗号分隔,支持 `db.table``table`;留空表示允许所有表
- `MCP_ENFORCE_WHITELIST``true` 时无明确表引用会拒绝执行
- `MCP_TOOL_TIMEOUT_MS`:单次工具调用超时
- `MCP_RATE_LIMIT_RPS` + `MCP_RATE_LIMIT_BURST`:基础令牌桶限流
- `MCP_MAX_RESULT_ROWS`MySQL 最大返回行数
- `MCP_REDIS_SCAN_MAX_KEYS``redis_scan` 最大返回 key 数
- `MCP_AUDIT_LOG_PATH`:审计日志路径
## 5. 工具说明
### 5.1 `mysql_query_readonly`
输入:
```json
{
"sql": "SELECT id, name FROM users WHERE id = ?",
"params": [1]
}
```
安全限制:
- 仅允许 `SELECT` / `SHOW` / `DESCRIBE` / `EXPLAIN`
- 禁止分号 `;`(多语句)
- 禁止注释 `--` / `#` / `/* */`
- 禁止 DDL/DML 关键字(`INSERT`/`UPDATE`/`DELETE`/`ALTER`/`DROP`/`TRUNCATE` 等)
- 支持库/表白名单校验
输出(结构化):
- `columns`
- `rows`
- `rowCount`
- `truncated`
- `durationMs`
### 5.2 `redis_get`
输入:
```json
{
"key": "user:1001"
}
```
输出:
- `exists`
- `key`
- `type`
- `value`
- `truncated`
- `durationMs`
### 5.3 `redis_scan`
输入:
```json
{
"pattern": "user:*",
"count": 50
}
```
输出:
- `pattern`
- `keys`
- `returned`
- `nextCursor`
- `truncated`
- `durationMs`
## 6. 审计日志
每次工具调用会记录JSON 行格式):
- 时间
- 工具名
- 调用方caller
- 是否成功
- 耗时
- 脱敏后的输入摘要
- 错误信息(截断)
敏感字段处理:
- SQL 字符串字面量与数字会脱敏
- Redis key 仅保留前后少量字符
## 7. Codex MCP 配置示例stdio
可按客户端配置格式接入,示例:
```json
{
"mcpServers": {
"smartflow-db-readonly": {
"command": "go",
"args": ["run", "./cmd/server"],
"cwd": "E:/SmartFlow-Agent/infra/smartflow-mcp-server",
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "readonly_user",
"MYSQL_PASSWORD": "replace_me",
"MYSQL_DATABASE": "smartflow",
"MYSQL_ALLOWED_DATABASES": "smartflow",
"MYSQL_ALLOWED_TABLES": "",
"REDIS_ADDR": "127.0.0.1:6379",
"REDIS_DB": "0",
"MCP_TOOL_TIMEOUT_MS": "5000",
"MCP_RATE_LIMIT_RPS": "5",
"MCP_RATE_LIMIT_BURST": "10",
"MCP_MAX_RESULT_ROWS": "500",
"MCP_REDIS_SCAN_MAX_KEYS": "200",
"MCP_AUDIT_LOG_PATH": "logs/audit.log"
}
}
}
}
```
## 8. 安全限制生效示例
- SQL 多语句:`SELECT 1; SELECT 2` -> 被拒绝semicolon is not allowed
- SQL 注释绕过:`SELECT * FROM users --x` -> 被拒绝sql comments are not allowed
- 写操作:`DELETE FROM users` -> 被拒绝dangerous sql keyword detected
- 白名单外表:`SELECT * FROM admin.secret` -> 被拒绝table not in whitelist
- Redis 大范围扫描:`redis_scan` 返回数量受 `MCP_REDIS_SCAN_MAX_KEYS` 限制
## 9. 风险说明MVP 已知边界)
1. SQL 校验采用关键字与模式匹配,不是完整 SQL AST 解析,建议二阶段引入 AST 级校验。
2. `SHOW DATABASES` 等无显式表引用语句在非严格模式下可执行;生产建议开启 `MCP_ENFORCE_WHITELIST=true`
3. Redis 复杂类型返回做了截断保护,但仍建议在生产环境设置更小上限。
## 10. 常见问题FAQ
### Q1: 启动时报 `MYSQL_USER and MYSQL_DATABASE are required`
检查环境变量是否正确加载,建议先确认 `.env` 存在于 `infra/smartflow-mcp-server`
### Q2: 为什么调用工具报限流
默认启用了令牌桶限流,调大 `MCP_RATE_LIMIT_RPS``MCP_RATE_LIMIT_BURST` 即可。
### Q3: 为什么 `redis_scan` 返回不全
是预期行为,结果数被 `MCP_REDIS_SCAN_MAX_KEYS` 限制,避免全量扫描拖垮 Redis。
### Q4: 审计日志在哪里
默认在 `logs/audit.log`,可用 `MCP_AUDIT_LOG_PATH` 自定义。