内容
# Elasticsearch 日志 MCP
这是一个只读、受约束的 Elasticsearch 日志查询 MCP 服务,使用 Go 实现,同时支持 Codex 本地 `stdio` 和远程智能体并发访问的 HTTP MCP。
## 能力
- `search_logs`:按时间、关键词、级别、服务、Trace ID 查询日志
- `list_index_categories`:按日期后缀归并并列出当前可用日志索引
- `get_log_context`:查询指定日志时间点前后的上下文
- `aggregate_logs`:按级别、服务或 Trace ID 聚合统计
- `read_log_detail_chunk`:经确认后分块读取超大日志字段
服务只接受结构化参数,不接受原始 Elasticsearch DSL,也不提供写入、删除索引或更新文档能力。
## 安全边界
HTTP 默认监听 `0.0.0.0:8080`,当前版本没有认证。生产环境必须放在受控网络中,并使用 Kubernetes NetworkPolicy、防火墙、安全组或已认证的反向代理限制访问来源。
服务端限制包括:
- 单次查询最多 12 小时,时间语义固定为 UTC+8;开始时间包含,结束时间排除
- 查询默认每页 200 条,最大每页 500 条
- 查询匹配超过 10000 条时停止返回日志,要求缩小时间范围或增加过滤条件
- 关键词最多 20 个、单个关键词最多 1024 字节,Trace ID 最多 20 个
- 聚合最多返回 100 个数据桶
- 上下文默认前后各 20 条,单侧最多 100 条,默认半窗口 10 分钟,最大 1 小时
- 超过 64 KiB 的日志字段必须经过确认,并按最多 64 KiB 分块读取
- HTTP 全局并发上限 32,单来源并发上限 4
`TOKEN_SECRET` 用于签名分页游标和超大日志确认令牌,必须使用随机长字符串,并在不同环境中分别配置。不要提交真实密钥。
## 配置
必填环境变量:
| 变量 | 说明 | 示例 |
| --- | --- | --- |
| `ES_URLS` | Elasticsearch 地址,逗号分隔 | `http://es-0:9200,http://es-1:9200` |
| `LOG_INDEX_PATTERN` | 允许查询的索引模式 | `logging-production-toprouter-sub2api-*` |
| `TOKEN_SECRET` | 服务端签名密钥 | `replace-with-random-secret` |
可选环境变量:
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `HTTP_ADDRESS` | `0.0.0.0:8080` | HTTP 监听地址;stdio 模式不使用 |
| `LOG_TIME_FIELD` | `@timestamp` | 日志时间字段 |
| `LOG_LEVEL_FIELD` | `level` | 日志级别字段 |
| `LOG_MESSAGE_FIELD` | `message` | 日志消息字段 |
| `LOG_EXCEPTION_FIELD` | `exception` | 异常字段 |
| `LOG_SERVICE_FIELD` | `name` | 服务字段 |
| `LOG_TRACE_ID_FIELD` | `requestId` | Trace/请求 ID 字段 |
日志索引命名可以使用例如 `logging-production-服务名-日期`,对应配置为 `logging-production-服务名-*`。含有提示词或用户内容的 `banyuewan_prompt_log-*` 会被拒绝。
`.env.example` 提供了完整配置模板。环境变量可以通过 shell、Docker `--env-file` 或 Kubernetes 注入。
## 本地运行
```bash
cp .env.example .env
set -a
. ./.env
set +a
go run ./cmd/log-mcp --transport http
```
构建二进制:
```bash
go build -o log-mcp ./cmd/log-mcp
./log-mcp --transport http
```
HTTP MCP 地址为 `http://127.0.0.1:8080/`。服务启动时会在 stderr 输出未认证警告。
## Docker 运行
构建镜像:
```bash
docker build -t elk-log-mcp:local .
```
使用环境文件启动:
```bash
docker run --rm \
--name elk-log-mcp \
--env-file .env \
-p 8080:8080 \
elk-log-mcp:local
```
容器默认启动 HTTP MCP,容器端口为 `8080`。不要将 `.env` 或真实 `TOKEN_SECRET` 提交到 Git。
## Kubernetes 部署
`deploy/k8s/deployment.yaml` 和 `deploy/k8s/service.yaml` 是可渲染的部署模板。模板使用 `${...}` 占位符,先准备环境变量,再用 `envsubst` 渲染:
```bash
export IMAGE=registry.example.com/sre/elk-log-mcp:v1
export ES_URLS='http://elasticsearch:9200'
export LOG_INDEX_PATTERN='logging-production-toprouter-sub2api-*'
export TOKEN_SECRET='replace-with-a-long-random-secret'
export HTTP_ADDRESS='0.0.0.0:8080'
export LOG_TIME_FIELD='timestamp'
export LOG_LEVEL_FIELD='level'
export LOG_MESSAGE_FIELD='message'
export LOG_EXCEPTION_FIELD='exception'
export LOG_SERVICE_FIELD='service'
export LOG_TRACE_ID_FIELD='traceId'
envsubst < deploy/k8s/deployment.yaml | kubectl apply -f -
kubectl apply -f deploy/k8s/service.yaml
kubectl -n logging rollout status deployment/elk-log-mcp
```
默认资源为 `ClusterIP`,服务端口为 `8080`。部署模板包含非 root 运行、只读根文件系统、探针、资源请求/限制、优雅终止和滚动更新配置。`TOKEN_SECRET` 也通过容器环境变量注入;生产环境建议由外部密钥管理系统或 CI/CD 安全变量提供,而不是写入 YAML 或命令历史。
查看和回滚:
```bash
kubectl -n logging get pods,svc -l app=elk-log-mcp
kubectl -n logging logs deployment/elk-log-mcp
kubectl -n logging rollout undo deployment/elk-log-mcp
```
如果需要从集群外访问 HTTP MCP,应额外配置 Ingress 或 LoadBalancer,并在入口层提供认证和来源限制。当前服务自身不会校验认证 Key。
## 接入 Codex(stdio)
将 `codex.mcp.json` 中的 `command` 改为本地二进制绝对路径,并替换 `env` 中的 Elasticsearch、索引和签名密钥:
```json
{
"mcpServers": {
"elasticsearch-log": {
"command": "/absolute/path/log-mcp",
"args": ["--transport", "stdio"],
"env": {
"ES_URLS": "http://es-node-1:9200,http://es-node-2:9200",
"LOG_INDEX_PATTERN": "logging-production-toprouter-sub2api-*",
"TOKEN_SECRET": "replace-with-a-long-random-secret"
}
}
}
}
```
stdio 的 MCP 协议数据只写入 stdout,运行日志写入 stderr。
## 远程智能体接入
远程智能体使用 HTTP MCP 地址:
```text
http://<service-address>:8080/
```
Kubernetes 集群内可使用 `http://elk-log-mcp.logging.svc.cluster.local:8080/`。跨网络访问前必须在反向代理或网关层增加认证、TLS 和访问控制。
## 开发与验证
```bash
gofmt -w cmd internal
go test ./...
go test -race ./...
go vet ./...
openspec validate elasticsearch-log-mcp --strict --no-interactive
```