关于 eset-mcp
eset-mcp 是面向 ESET 管理环境的 MCP 服务,可供连接 MCP 的 AI 客户端和 Agent 管理云端及本地部署。它以单一进程路由多个租户,并提供只读/读写隔离。
主要功能
- 01覆盖 ESET Connect 等管理 API
- 02支持云端与本地部署并行
- 03多租户凭据与令牌隔离
- 04只读和读写模式门控
- 05分页、重试与响应裁剪
- 06结构化日志及 Prometheus 指标
适用场景
- 01从 MCP 客户端查询设备与事件
- 02在单一服务中管理多个 ESET 租户
- 03限制 Agent 仅执行只读操作
- 04将云端和本地控制台统一接入
MCP 相关
通过工具、资源和提示词向 MCP 客户端开放 ESET 管理能力,支持 stdio 与 Streamable HTTP。请求可按租户及请求头路由到云端区域或本地控制台。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/maciekaz/eset-mcp:1.0.3本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/maciekaz/eset-mcp:1.0.3stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
ESET-MCP
一个用于管理整个 ESET 管理体系的模型上下文协议服务:包括 ESET Connect (云端,涵盖所有区域)、ESET PROTECT On-Prem、ESET Inspect 和 ESET Cloud Office。可通过工具、资源和提示词,从任意 MCP 宿主(Claude Desktop、Claude Code 或自定义 Agent)管理其中任何一个产品。
专为集中管理任意数量的 ESET 部署而构建。单个进程可同时连接云端和本地控制台;客户端通过请求头选择目标。只要 MCP 收到有效凭据(Basic 身份验证,以及可选的 URL 覆盖和可选的 Cloudflare Access 服务令牌),就会将调用路由到相应后端、生成自己的令牌,并在池中隔离各租户。
⚠️ 特此说明,各位 这是一个由社区驱动的独立开源项目,与 ESET, spol. s r.o. 没有任何关联、没有获得其官方支持,也未获其认可。ESET 及其产品名称均为各自所有者的注册商标。
我们已尽一切努力确保本软件安全可靠(包括严格的只读模式限制),但本代码按“原样”提供,不附带任何形式的保证。您需自行承担使用此工具以及对 ESET 环境进行任何更改的全部责任。
目录
- 功能
- 架构概览
- 安全性
- 快速开始
- 配置
- 多租户部署(Basic 身份验证模式)
- 本地 ESET PROTECT 支持
- 生产环境部署(通过 Caddy 使用 HTTPS)
- 工具、资源与提示词
- 架构
- 测试
- 刷新 OpenAPI 规范
- 许可证
功能
完整的 API 覆盖范围
- 102 个工具,根据 16 份官方 ESET Connect OpenAPI 3.0.1 规范自动生成,涵盖应用管理、资产管理、自动化、 设备管理、身份管理、事件管理、安装程序管理、 移动设备管理、网络访问保护、补丁管理、 策略管理、隔离区管理、用户管理、 漏洞管理和 Web 访问保护。
- 4 个高级组合工具,将 3-6 次原始调用整合为一次:
eset_search、device_full_profile、incident_full_context、latest_detections。
只读 / 读写模式
ESET_MODE=RO→ 工具目录仅展示只读工具(共 51 个)。 写入工具完全不会出现在list_tools中。ESET_MODE=RW→ 展示全部 106 个工具;修改类工具在其 MCP 注解中带有destructiveHint: true。- 此模式独立于 ESET 账户的底层权限。
- 通过纵深防御实现的内存门控会在发送任何 HTTP 请求前,于 RO 模式下拒绝 RW 工具名称。
身份验证
ESET_AUTH_MODE=env- 单租户,凭据来自.env。ESET_AUTH_MODE=basic- 多租户,客户端在每次请求中传递Authorization: Basic <base64(user:password)>(还可选用X-ESET-Region指定其他云区域,或用X-ESET-Server-URL将 请求路由到本地部署的 PROTECT 控制台)。一个服务可为多个 ESET 账户提供服务,并在同一进程中混合使用云端和本地部署。- 每个租户的 OAuth 令牌会按
(user, password_hash, deployment, region-or-server-url, cf_secret_hash)进行池化和隔离。 轮换密码或 Cloudflare Access 服务令牌时会创建新客户端;同一用户的云端和本地部署客户端绝不会共享池条目。
传输方式
- stdio - 面向本地主机,通过 stdin/stdout 传输 JSON-RPC。
- Streamable HTTP - 当前的 MCP 传输方式(2025 年 11 月规范)。
多区域
eu / de / us / ca / jpn。在 env 模式下通过 ESET_REGION 固定;
在 basic 模式下通过每次请求中的 X-ESET-Region 指定。
在同一进程中使用云端和本地部署
除云区域外,单个 MCP 服务还可为客户托管的 ESET PROTECT On-Prem 控制台提供服务。系统会透明处理本地部署的身份验证线路格式
(使用 camelCase 响应的 POST /GetTokens)以及每个主机的 URL
结构;客户端通过每次请求中的 X-ESET-Server-URL 标头选择目标。请参阅
本地部署的 ESET PROTECT 支持。
Cloudflare Access(可选)
当本地部署的控制台位于 Cloudflare Access 隧道后方时,MCP
会通过服务令牌进行身份验证
(使用环境变量默认值,或在每次请求中传递 X-ESET-CF-Access-Client-Id /
X-ESET-CF-Access-Client-Secret),并通过隧道连接到源站。CF 令牌是额外的入口层,
位于 ESET 账户凭据之前,而非其替代品。云端请求绝不会携带这些标头。
弹性
- OAuth2 会在令牌过期前约 5 分钟主动刷新,并在收到 401 时强制刷新 并重试。
- 对 429 进行指数退避重试(最多 3 次,并遵循
Retry-After)。 - 自动遍历分页(
nextPageToken)。 - 通过
response-id标头进行 202 长轮询,最长 10 分钟。
响应整形(保护上下文窗口)
一次未设上限的 list_* 调用可能返回数百 KB 的数据,足以
超出大模型的上下文窗口。每个工具响应都会经过以下两项转换:
fields投影 - 每个 GET 工具都提供可选的fields: [string]参数,用于筛选列表中的每一项,仅保留 请求的键(例如["uuid", "displayName"])。获取数据后在服务端应用。- 字节上限(
ESET_MCP_RESPONSE_BYTES_MAX,默认 100 KB)- 如果 负载仍超出预算,则截短最长的列表,同时保留 所有顶层字段(nextPageToken、totalSize……),并附加_capped元数据块,其中包含可操作的提示, 说明如何继续获取数据。Agent 可通过分页继续完整访问数据。
便于 Agent 处理的错误
HTTP 错误会映射为易读的提示:403 → 检查 ESET PROTECT Hub 中的 Permission Sets;401 → 服务会自动刷新令牌;429 → 退避;5xx → 稍后重试。
可观测性(日志 + Prometheus)
- 结构化日志写入 stderr。开发环境默认使用文本格式,生产环境的日志采集器可通过
ESET_MCP_LOG_FORMAT=json使用 JSON Lines 格式。每次工具调用、令牌刷新、HTTP 重试和连接池淘汰都会生成带有低基数值字段(tool、deployment、status、duration_ms、response_bytes,……)的类型化event记录。 - Prometheus 指标通过可选的
/metrics端点提供(设置ESET_MCP_METRICS_ENABLED=true,需要执行pip install eset-mcp[metrics])。计数器用于统计工具调用、令牌刷新、HTTP 重试和触顶次数;直方图用于统计工具耗时和响应大小;仪表用于统计客户端连接池大小。 - 绝不会进入日志或指标的内容:密码、
Authorization标头、CF Access 密钥、请求/响应正文、查询字符串、替换后的路径参数(可能泄露 UUID)。日志记录器会通过防御性拒绝列表,在任何格式化器处理前剔除已知敏感键。 - 故障隔离:遥测数据的生成由 try/except 包裹,因此损坏的指标注册表或格式化器绝不会将成功的工具调用变成 Agent 的错误。如果
/metrics的数据导出过程中发生异常,则返回 500(而不是 503),工作进程会继续运行。 - 在生产环境中降低日志输出:设置
ESET_LOG_LEVEL=WARNING可关闭每次调用的 INFO 事件,同时保留重试/错误信息;设置ESET_LOG_LEVEL=ERROR可隐藏除严重故障外的所有信息。设置ESET_MCP_METRICS_ENABLED=false(默认值)可完全禁用指标。这三个设置彼此独立。
架构一览
最简单的配置:在 .env 中设置凭据,使用一个 MCP 主机和一个 ESET 云区域。适用于个人使用、单个团队,或 Claude Desktop、Claude Code 这类桌面 AI 客户端。
对于真正的多租户或企业级部署,可在 ESET-MCP 前端接入凭据管理器。下图展示了使用 IBM mcp-context-forge 作为“凭据管理”层的一种模式;任何功能相当的 MCP 网关(或自建的身份验证代理)都能以同样的方式工作:
Forge 为每个租户保存密钥,并在每个请求中注入 Authorization: Basic、X-ESET-Region、X-ESET-Server-URL 和 X-ESET-CF-Access-* 标头。ESET-MCP 会将每个请求路由到正确的后端(云区域、本地 PROTECT 控制台,或 Cloudflare Access 后的本地服务),而按租户隔离的 LRU 客户端连接池会确保各租户的 OAuth 令牌完全隔离。这是经过实际验证的模式,并非设想;此处介绍的标头、连接池键和路由规则均已在 tests/test_concurrency.py 和 tests/test_onprem.py 的测试套件中覆盖。
安全性
凭据
- 在
env模式下,密码会在启动时读取一次并保存在内存中。 - 在
basic模式下,密码仅在请求期间通过网络传输,并且只会在对应租户的客户端处于 LRU 连接池中时保存在内存中。密码绝不会被记录到日志中。 - 连接池键使用密码的 SHA-256 哈希,而不是密码本身。
- OAuth 访问令牌/刷新令牌按租户保存;在单个会话中,令牌绝不会跨越租户边界。
身份验证模式与传输方式
| 模式 | 允许的传输方式 | 凭据来源 |
|---|---|---|
env | stdio 或 http | .env(ESET_USER / ESET_PASSWORD) |
basic | 仅 http | 每个请求中的 Authorization: Basic 标头 |
通过明文 HTTP 使用 basic 模式会泄露密码。服务在启动时会强制 basic 模式使用
HTTP 传输方式,但不会强制使用 TLS——这由部署方负责。prod docker-compose 配置
会通过 Caddy + Let's Encrypt 提供前置代理。
在 basic 模式下,缺少或格式错误的 Authorization → 返回带有
WWW-Authenticate: Basic 挑战的 HTTP 401。X-ESET-Region 中的未知区域
→ 返回 HTTP 401。
RO / RW 隔离
两个独立层:
- 目录隐藏 - 在 RO 模式下,
list_tools会过滤掉所有非 GET 工具。 Agent 看不到写入工具。 - 纵深防御门控 - 在发出任何 HTTP 请求前,
call_tool会根据ESET_MODE验证工具声明的模式。硬编码客户端、提示词注入尝试和过时的 Agent 快照都会触发 此门控,并收到结构化的ModeForbiddenError文本响应(不抛出异常,也不发起网络请求)。
在 RW 模式下,修改类工具带有 destructiveHint: true,因此遵循注释的 MCP 主机
可以要求每次调用前确认。
按租户隔离(basic-auth 模式)
- 身份验证标头由专用的 ASGI 中间件解析,并存入
ContextVar;它们不会进入请求正文或日志。 - 每个请求都会解析为一个由
(user, password_hash, region)确定的Credentials实例。 - 客户端池的 LRU 上限可防止随机凭据滥用导致内存无限增长。
网络暴露面
- 在开发环境中(
docker compose up),MCP 服务会发布:8765。 - 在
prod配置中,MCP 容器不会发布端口—— Caddy 会加入同一个 docker bridge 网络,并代理 HTTPS 流量。主机上仅开放 80(HTTP-01 ACME)和 443(HTTPS)。 - 除了访问
*.eset.systems(身份验证 + API)外,不会有任何出站流量。
依赖与代码审计
- Snyk Code:
eset_mcp/中有 0 个问题。 - Ruff:检查通过(
select = E F W I B UP RUF)。 - 运行时依赖:
mcp、httpx、pydantic、python-dotenv。 运行 HTTP 时还需要starlette+uvicorn。
按设计不在范围内
- 不接收 Webhook。
- 不进行持久化存储;日志写入 stdout。
- 不在磁盘上缓存 OAuth 令牌。
- 不将
basic模式凭据写回磁盘。
负责任披露
请提交私密安全公告,而非公开问题: https://github.com/maciekaz/ESET-MCP/security/advisories/new。
快速开始
最快的方式是使用已发布的 Docker 镜像。无需安装 Python,无需
venv,也无需检出源代码——只需 .env + docker run。该镜像支持多架构(amd64 + arm64),
使用 cosign 签名,附带 SBOM + 构建来源证明,并在每次发布时推送到 GHCR。
cp .env.example .env # fill in ESET_USER / ESET_PASSWORD / ESET_REGION
docker run --rm -i --env-file .env ghcr.io/maciekaz/eset-mcp:1
版本固定策略:
:1- 最新的 1.x.x(在主版本内自动更新):1.0- 最新的 1.0.x(在次版本内自动更新):1.0.1- 精确版本(生产环境):latest- 最新的稳定版本:main/:sha-<short>- 来自主分支的边缘构建(不用于生产环境)
接入 Claude Desktop / Claude Code(stdio)
// claude_desktop_config.json
{
"mcpServers": {
"eset": {
"command": "docker",
"args": ["run", "--rm", "-i", "--env-file", "/absolute/path/to/.env",
"ghcr.io/maciekaz/eset-mcp:1"]
}
}
}
HTTP 传输(单租户或置于自有代理之后)
docker run -d --name eset-mcp \
--env-file .env -p 8765:8765 \
-e ESET_MCP_TRANSPORT=http \
ghcr.io/maciekaz/eset-mcp:1
# MCP endpoint: http://localhost:8765/mcp
验证镜像(cosign 无密钥模式)
cosign verify \
--certificate-identity-regexp '^https://github.com/maciekaz/ESET-MCP/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/maciekaz/eset-mcp:1
从源代码开始(贡献者 / 本地开发)
git clone https://github.com/maciekaz/ESET-MCP.git
cd ESET-MCP
cp .env.example .env
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
eset-mcp
Docker Compose(使用已发布的镜像)
docker compose up -d eset-mcp-http
# MCP endpoint: http://localhost:8765/mcp
默认情况下,compose 文件会拉取 ghcr.io/maciekaz/eset-mcp:1——无需本地
构建,首次启动速度快。编辑 docker-compose.yml 中的 image: 行即可固定
特定版本。
通过 compose 单次运行 stdio:
docker compose --profile stdio run --rm eset-mcp-stdio
要基于源代码进行开发?使用 dev 配置从本地检出版本构建,而不是拉取镜像:
docker compose --profile dev up --build eset-mcp-http-dev
配置
所有设置都保存在 .env 中。必填字段已在
.env.example 中标出。
| 变量 | 默认值 | 用途 |
|---|---|---|
ESET_AUTH_MODE | env | env(单租户)或 basic(多租户) |
ESET_USER | - | API 用户(env 模式下必填) |
ESET_PASSWORD | - | API 密码(env 模式下必填) |
ESET_MODE | RO | RO(只读目录)或 RW |
ESET_REGION | eu | eu / de / us / ca / jpn |
ESET_MCP_TRANSPORT | stdio | stdio 或 http |
ESET_MCP_HTTP_HOST | 127.0.0.1 | HTTP 绑定地址 |
ESET_MCP_HTTP_PORT | 8765 | HTTP 端口 |
ESET_MCP_RESPONSE_BYTES_MAX | 100000 | 每次调用的响应字节数上限;0 表示禁用 |
ESET_LOG_LEVEL | INFO | DEBUG / INFO / WARNING / ERROR |
ESET_MCP_LOG_FORMAT | text | text(开发环境,人类可读)或 json(生产环境日志收集器) |
ESET_MCP_METRICS_ENABLED | false | 挂载 Prometheus /metrics;需要 eset-mcp[metrics] |
ESET_MCP_METRICS_PATH | /metrics | 指标端点的挂载位置 |
ESET_DEPLOYMENT | cloud | cloud(ESET Connect)或 onprem(客户自托管的 PROTECT) |
ESET_ONPREM_SERVER_URL | - | 本地部署控制台的 https://host[:port](env+onprem 模式下必填) |
ESET_ONPREM_VERIFY_SSL | true | 对使用自签名证书的本地部署控制台设置为 false |
ESET_ONPREM_CF_ACCESS_CLIENT_ID | - | Cloudflare Access Service Token 客户端 ID(位于 CF 后方的本地部署环境) |
ESET_ONPREM_CF_ACCESS_CLIENT_SECRET | - | Cloudflare Access Service Token 客户端密钥(与上项配套) |
ESET_PUBLIC_DOMAIN | - | Caddy 为其签发 TLS 证书的域名(仅限 prod 配置) |
ESET_ACME_EMAIL | - | Let's Encrypt 用于续期的电子邮件(仅限 prod 配置) |
请使用专用 API 用户,而非控制台登录账户。在 ESET PROTECT Hub / ESET Business Account → API users 中创建。
多租户部署(基本身份验证模式)
# .env
ESET_AUTH_MODE=basic
ESET_MCP_TRANSPORT=http
ESET_REGION=eu # default region; clients can override per request
每个 HTTP 请求都必须携带:
| 标头 | 必需 | 说明 |
|---|---|---|
Authorization | 是 | Basic <base64(user:password)> |
X-ESET-Region | 否 | 覆盖默认区域(eu/de/us/ca/jpn) |
X-ESET-Server-URL | 否 | 将此请求路由到本地部署的 PROTECT 控制台(例如 https://protect.example.com:9443)——参见本地部署 ESET PROTECT 支持 |
X-ESET-CF-Access-Client-Id | 否 | Cloudflare Access Service Token 客户端 ID(位于 CF Access 后方的本地部署环境) |
X-ESET-CF-Access-Client-Secret | 否 | 与上项配套;两项都必须发送 |
Python 客户端示例:
import base64
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
token = base64.b64encode(b"[email protected]:secret").decode()
headers = {"Authorization": f"Basic {token}", "X-ESET-Region": "us"}
async with streamablehttp_client(
"https://eset-mcp.example.com/mcp/", headers=headers
) as (r, w, _):
async with ClientSession(r, w) as session:
await session.initialize()
tools = await session.list_tools()
⚠️ 不使用 TLS 的基本身份验证会泄露凭据。 请始终在 HTTPS 后运行
basic模式。
本地部署的 ESET PROTECT 支持
ESET 以云服务(*.eset.systems 上的 ESET Connect API)和客户可自托管的本地控制台两种形式提供 PROTECT。本地 REST API 位于单台主机上(默认端口为 9443),并使用不同的身份验证端点——通过 JSON 请求体调用 POST /GetTokens,响应采用驼峰命名法;除此之外,其 URL 结构与云 API 相同。ESET-MCP 同时支持两种形式,并且可在同一进程中运行。
服务如何判断是云端还是本地部署
ESET_AUTH_MODE | 每个请求的部署方式由什么控制 |
|---|---|
env | 静态:ESET_DEPLOYMENT(云端),或 ESET_DEPLOYMENT=onprem + ESET_ONPREM_SERVER_URL |
basic | 按请求决定:存在 X-ESET-Server-URL 时,该请求切换到本地部署;不存在时,回退到环境默认值(云端或本地部署) |
因此,单个 MCP 服务可以为大多数客户端提供云端服务,同时将特定请求路由到一个或多个本地控制台——路由完全取决于各客户端在 X-ESET-Server-URL 中发送的 URL。
单租户本地部署(env 模式)
# .env
ESET_AUTH_MODE=env
ESET_DEPLOYMENT=onprem
ESET_ONPREM_SERVER_URL=https://protect.company.local:9443
ESET_ONPREM_VERIFY_SSL=true # set to false only for self-signed certs you trust
[email protected]
ESET_PASSWORD=...
多租户本地部署(basic 身份验证,按请求指定 URL)
# .env
ESET_AUTH_MODE=basic
ESET_MCP_TRANSPORT=http
ESET_DEPLOYMENT=cloud # default; clients opt into on-prem per-request
# ESET_ONPREM_SERVER_URL is optional - if set it becomes the on-prem default
将客户端请求路由到本地部署:
headers = {
"Authorization": f"Basic {token}",
"X-ESET-Server-URL": "https://protect.client-a.local:9443",
}
同一 MCP 服务,不同请求——使用相同的标头,但不包含 X-ESET-Server-URL——将继续连接云端。
本地部署与云端支持的功能
两种部署的工具目录相同(所有 102 个基于 OpenAPI 的工具,以及 4 个组合工具)。调用时,服务会根据凭据使用云端或本地部署对应的路径。
- 两种部署均支持并已验证:
device_*、asset_groups_*、policy_*和大多数task_*(Automation)在云端和本地部署上均可正常使用。 - 仅限云端的模块:
incident_*、mobile_*、wap_*、nap_*、quarantine_*和大多数vuln_*对应于独立的 ESET 产品(ESET Inspect、Cloud Office Security、MDM),这些产品不属于本地 PROTECT 安装的一部分。对本地控制台调用这些工具时,ESET 会直接返回 404;服务会将其作为ESET API error: 404文本响应传递给 Agent,不做特殊处理。 - 路径覆盖:少数端点在本地部署上使用不同的 URL,例如
POST /v1/devices/{uuid}:rename在本地部署上为:renameDevice。这些规则定义在eset_mcp/openapi/onprem-path-overrides.json中,并会在请求目标为本地部署时自动应用。
在本地控制台前使用 Cloudflare Access
当本地 PROTECT 控制台通过 Cloudflare 隧道公开,并由 Cloudflare Access 保护时,MCP 可以使用服务令牌进行身份验证。调用链变为 MCP → Cloudflare Access → ESET 本地部署。
每对令牌包含两个值,可通过 .env 提供:
ESET_ONPREM_CF_ACCESS_CLIENT_ID=abc1234567890.access
ESET_ONPREM_CF_ACCESS_CLIENT_SECRET=<long-secret>
……也可在 basic 身份验证模式下按请求提供(会覆盖环境默认值——适用于每个租户都有自己的隧道和服务令牌的情况):
headers = {
"Authorization": f"Basic {token}",
"X-ESET-Server-URL": "https://protect.client-a.local:9443",
"X-ESET-CF-Access-Client-Id": "abc1234567890.access",
"X-ESET-CF-Access-Client-Secret": "<long-secret>",
}
MCP 会将输入标头 X-ESET-CF-* 转换为 Cloudflare Access 所需的实际标头 CF-Access-Client-Id / CF-Access-Client-Secret,并将其附加到每次出站调用中——包括 POST /GetTokens 身份验证握手,以及之后的每个 ESET API 请求。
CF 密钥的处理方式与密码相同:绝不记录日志,只有其 SHA-256 哈希值会进入客户端池密钥。轮换密钥会生成新的客户端和新的 ESET 令牌。无论环境默认值如何,云端请求绝不会携带 CF Access 标头——ESET Connect 是公开的 SaaS 服务。
本地部署的安全说明
X-ESET-Server-URL只接受不带路径、查询参数或片段的https://URL。末尾的斜杠会被移除。其他格式都会返回 HTTP 400。ESET_ONPREM_VERIFY_SSL=false会禁用 TLS 证书验证,使连接面临中间人攻击风险。禁用该设置时,服务会在每次构造客户端时记录一条 WARNING。仅在使用无法替换的自签名证书的可信内部网上使用。- 本地部署令牌按
(user, password_hash, server_url, cf_secret_hash)保存在内存中,隔离规则与云令牌相同。令牌池会为它们分别建立键,因此云端和本地部署客户端不会发生冲突;两个客户端访问同一本地部署 URL,但使用不同的 CF 服务令牌时,也会获得单独的令牌池条目。 - 只发送两个
X-ESET-CF-*标头中的一个时,会返回 HTTP 400,而不是静默回退到环境变量默认值(这几乎可以确定是操作人员的输入错误)。
生产环境部署(通过 Caddy 使用 HTTPS)
prod docker-compose 配置文件会在 MCP 服务前启动 Caddy。Caddy 首次启动时会获取 Let's Encrypt 证书(HTTP-01 挑战——80 / 443 端口必须可从公共互联网访问),并将 HTTPS 请求代理到内部 MCP 容器。
# .env
ESET_AUTH_MODE=basic
ESET_PUBLIC_DOMAIN=eset-mcp.example.com
[email protected]
docker compose --profile prod up -d
# MCP endpoint: https://eset-mcp.example.com/mcp
你将获得:
- 443 端口上的 HTTPS,以及自动续期的 Let's Encrypt 证书。
- 80 端口上的 HTTP-01 挑战。
- MCP 容器仅绑定到 Docker 桥接网络,不发布端口。
- gzip / zstd 压缩,以及输出到 stdout 的 JSON 访问日志。
工具、资源和提示词
组合式高级工具
| 工具 | 返回内容 |
|---|---|
eset_search(query, kinds?, limit_per_kind?) | 按子串进行不区分大小写的匹配,搜索设备 / 用户 / 策略 / 组 |
device_full_profile(deviceUuid) | 设备记录 + 最近的检测结果 + 漏洞 + 最近的扫描 |
incident_full_context(incidentUuid) | 事件 + 评论 + 相关检测结果 + 受影响的设备 |
latest_detections(hours=24, limit=10, severity_min?) | 指定时间范围内最新的检测结果,按 occurTime 降序排列;v2 → v1 回退 |
如果子调用返回 403/404(例如租户缺少某个模块),每个组合式工具都会优雅降级。在适用的情况下,结果中会包含 skipped / truncated 标记。
资源
eset://config/mode-RO或RW。eset://config/region- 当前区域(在基本身份验证模式下按请求获取)。eset://config/deployment- 此请求使用的cloud或onprem (<server-url>)。eset://config/tools-catalog- 包含全部 106 个工具的 JSON 目录(名称、模式、方法、路径、服务、描述)。eset://docs/rate-limits- 关于 10 req/s 上限的简要提示。
提示词
audit_inactive_devices(days=30)- 待离职用户的候选设备。vulnerability_report- 按设备整理的 CVE 报告。incident_triage- 未处理事件 + 相关检测结果。
架构
eset_mcp/
├── __main__.py # entrypoint - stdio or HTTP, wires resolver + pool
├── server.py # MCP server (tools / resources / prompts) + telemetry
├── credentials.py # Credentials + EnvResolver / BasicAuthResolver + ContextVar
├── middleware.py # ASGI Basic-auth middleware (basic mode only)
├── client_pool.py # LRU pool of EsetHttpClient keyed by (user, region, ...)
├── http_client.py # async httpx + 202 polling + 429 retry + 401 refresh
├── auth.py # CloudTokenManager (OAuth2) + OnPremTokenManager (/GetTokens)
├── regions.py # cloud region → per-service domains + on-prem URL resolver
├── modes.py # RO/RW gate
├── errors.py # HTTP error → agent-friendly text
├── config.py # .env loading
├── response_shaping.py # fields projection + byte cap
├── composite_tools.py # hand-written high-level tools
├── tools_loader.py # generator: tools from OpenAPI specs + on-prem path overrides
├── observability/ # JSON/text structured logging + Prometheus metrics
└── openapi/ # 16 ESET Connect OpenAPI 3.0.1 specs + onprem path overrides
测试
pytest # full suite (RO smoke + unit + integration)
pytest -m "not rw" # RO only (default in CI)
pytest -m rw # RW (requires an account with RW permissions)
集成测试会访问真实的 ESET 租户——凭据通过同一个 .env 文件提供。CI 工作流:
.github/workflows/integration.yml
会在 PR、推送到 main 时以及每天 UTC 时间 03:17 运行。定时任务可以发现服务端与 ESET 发布的 OpenAPI 规范之间的差异。
刷新 OpenAPI 规范
cd eset_mcp/openapi
for name in business-account application-management asset-management automation \
device-management iam incident-management installer-management \
mobile-device-management network-access-protection patch-management \
policy-management quarantine-management user-management \
vulnerability-management web-access-protection; do
curl -sO "https://eu.esetconnect.eset.systems/swagger/api/${name}.json"
done
刷新后,tests/test_catalog_vs_openapi.py 会标记任何新增或变更的操作。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。