关于 Box Admin Analytics MCP
Box Admin Analytics MCP 是面向 Box 管理员的只读审计分析服务,读取企业事件日志以识别外部访问与共享暴露。它还提供文件夹条目、指定文件文本及用户状态查询。
主要功能
- 01汇总外部下载与预览事件
- 02枚举外部协作者和公开链接
- 03按外部暴露程度排列内部所有者
- 04列出文件夹内容及上传者
- 05按配置读取指定文件文本
- 06查询账户状态并生成每日摘要
适用场景
- 01追踪特定账户访问过的文件
- 02发现公开共享链接与外部协作者
- 03核查文件夹中的附件及上传者
- 04汇总 Box 外部访问风险
MCP 相关
该服务将 Box 企业事件和管理数据作为 MCP 工具提供给客户端。默认只读,不会撤销共享或修改文件。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
9收录的工具列表 · 更新于 2 天前
此工具无需参数。
详细介绍
boxadm-mcp
English | 日本語
从 Box 管理员的视角呈现外部文件流动的 MCP(Model Context Protocol)服务。它读取 Box 的企业事件日志(admin_logs),突出显示“谁频繁与外部共享内容”以及“哪些文件被外部访问”——这是一种泄露预警信号,并非通用文件浏览器。
文档:https://shigechika.github.io/boxadm-mcp/
只读:它绝不会撤销共享、删除文件或以其他方式修改任何内容,只负责呈现风险。这与通用 Box 文件 MCP(官方 Box MCP 或 claude.ai Box 连接器)是不同的工具:这些工具操作的是用户自己的文件,无法查看企业事件,而这正是本服务的用途。
名称源自管理控制台的视角(boxadm = Box 管理员),是
gwsadm-mcp 的姊妹项目。
功能
| 工具 | 类别 | 描述 |
|---|---|---|
health_check | — | 版本 + 认证模式 + Box 认证 + admin_logs 权限探测 + 已配置的域名允许列表。尚未完成认证时(OAuth 模式)会报告 needs-login |
recent_admin_events | 诊断 | 最近的原始企业事件(用于检查事件类型/字段)。支持通过 stream_position 手动分页 |
external_access_events | 访问(事件,企业范围) | 汇总指定时间范围内的外部 DOWNLOAD/PREVIEW:外部访问者排行、被外部访问的文件排行、共享链接数量。传入 created_by_logins 可对特定账户进行 DLP 追踪 |
external_collaborators | 暴露面(枚举) | 列出外部协作者(组织外的登录账号或外部受邀邮箱) |
public_shared_links | 暴露面(枚举) | 列出通过 open(任何拥有链接的人均可访问)共享链接共享的项目 |
top_external_sharers | 暴露面(枚举) | 按外部暴露程度(外部协作者 + 公共链接)对内部所有者进行排名 |
list_folder_items | 单个文件夹(ls) | 列出每个项目的名称、上传时间、大小、上传者和直接链接,并提供文件夹的所有者和路径。可按上传者或上传时间范围筛选。不读取文件内容 |
read_file | 单个文件(cat),选择启用 | 读取 BOXADM_READ_FOLDER_IDS 指定文件夹中某个文件由 Box 提取的文本:电子表格以制表符分隔的单元格形式呈现,文档和 PDF 以文本形式呈现。仅在设置该变量后才会注册此工具;图片和归档文件只返回元数据 |
get_user | 账户状态(查询) | 按准确登录名查询单个账户:status、role、enterprise、配额和时间戳。无需使用管理控制台即可回答“此账户是否已停用?” |
daily_brief | 汇总 | 结合访问(事件)和暴露面(枚举)的晨间摘要 |
认证模式
通过 BOX_AUTH_MODE 选择两种模式:
oauth— OAuth 2.0(用户认证)。管理员在浏览器中授权一次;之后刷新令牌会使其在无人值守的情况下持续运行。ccg— 客户端凭据授权(服务器到服务器)。如果你的 Box 租户有可用的服务器认证应用名额,此模式更便于无人值守运行。
只要授权用户/模拟用户是管理员,且应用具有 Manage
enterprise properties 权限,admin_logs(企业事件)在两种模式下均可读取。
OAuth 设置(由 Box 管理员一次性完成)
- Developer Console → 创建 Platform App → Custom App → User Authentication (OAuth 2.0)
- Redirect URI:
http://localhost:8787/callback - Application Scopes:勾选 Manage enterprise properties(
admin_logs所必需)。如果还需要枚举协作/共享链接,也请添加 Read all files and folders;如果需要get_user查询,也请添加 Manage users(每次更改权限范围都需要通过boxadm-mcp auth重新同意) - 在 Admin Console 中启用应用(根据大多数租户策略,未发布的应用默认处于禁用状态)
- 记下 Client ID / Client Secret
- 首次登录:设置
BOX_AUTH_MODE=oauth等,然后运行boxadm-mcp auth→ 在浏览器中授权 → 令牌缓存将写入~/.config/boxadm-mcp/token.json(chmod 600)
设置
# uv
uv pip install boxadm-mcp
# pip
pip install boxadm-mcp
或从源代码安装:
git clone https://github.com/shigechika/boxadm-mcp.git
cd boxadm-mcp
# uv
uv sync
# pip
pip install -e .
配置
| 变量 | 必填 | 描述 |
|---|---|---|
BOX_AUTH_MODE | oauth / ccg(默认 ccg)。任何其他值都会回退到 ccg;health_check 会报告当前生效的模式,因此拼写错误时会显示为 ccg,而不是输入的值 | |
BOX_CLIENT_ID | ✓ | 应用 Client ID |
BOX_CLIENT_SECRET | ✓ | 应用 Client Secret |
BOX_ENTERPRISE_ID | ccg 模式 | Enterprise ID(CCG 主体;oauth 不需要) |
BOX_OAUTH_REDIRECT_URI | oauth 重定向 URI。默认 http://localhost:8787/callback | |
BOX_TOKEN_CACHE | oauth 令牌缓存路径。默认 ~/.config/boxadm-mcp/token.json | |
BOX_API_BASE | 默认 https://api.box.com | |
BOX_SCAN_CONCURRENCY | 枚举扫描中每个文件夹的并行查询数。默认 8,范围限制为 1–32 | |
BOX_SCAN_DEADLINE | 单次枚举扫描的软墙钟时间预算(秒)。默认 45;0/负数表示禁用。达到预算时,扫描会返回明确标注的部分结果(capped=true),而不会一直运行到工具调用超时 | |
BOX_HTTP_TIMEOUT | 每个请求的 HTTP 超时(秒)。默认 30。调低此值(并设置 BOX_SCAN_DEADLINE),以免某个缓慢的端点导致最后一批正在进行的扫描超过网关超时时间 | |
BOXADM_READ_FOLDER_IDS | read_file 可读取其中文件的文件夹(逗号分隔的文件夹 ID;拒绝 0)。未设置时:不会注册 read_file。任何一项无效都会导致该功能关闭,并使 health_check 状态降级 | |
BOX_ALLOWED_DOMAINS | ✓ | 内部电子邮件域名(逗号分隔)。无默认值——设置此项之前,每个地址都会被视为外部地址 |
请勿将密钥放入 .mcp.json(例如,放在启动前加载的本地环境文件中);.mcp.json 本身可以引用 ${BOX_CLIENT_ID} 这类变量,并且可以安全地提交到版本库。
范围与限制
- 访问工具(
external_access_events,以及daily_brief中的访问部分)读取企业范围内的事件流。达到max_events时会设置capped: true(按时间从早到晚扫描)。 - 暴露面(枚举)工具只能查看协管员账户可见的文件夹(不保证覆盖整个企业的 100%),并受
max_folders/max_depth限制(通过capped显示)。需要 Read all files and folders 作用域。 - 扫描会并发执行各文件夹的查找(
BOX_SCAN_CONCURRENCY),因为 Box 没有企业范围内的协作列表接口——这能让更多文件夹在工具调用超时前完成查找,但覆盖范围仍受上限限制。读取路径会针对429(遵循Retry-After)和暂时性5xx错误进行带抖动的退避重试,因此短暂的限流会在重试后恢复,而不会降低覆盖率;如果某个文件夹因单文件夹 API 错误而被跳过,且该错误在这些重试后仍持续存在(例如持续的403),则会计入fetch_errors:只有当capped为 false 且fetch_errors为 0 时,覆盖才算完整。 - 枚举工具会在调用间共享一个短 TTL 的扫描备忘;
public_shared_links会完全跳过协作调用(优化)。 get_user查询的是企业用户目录——仅发出一个请求,不分页,而且从结构上讲并非枚举器(它只回答你传入的登录名相关信息)。其capped标志会说明搜索结果是否被截断,因此,截断结果中的found: false应理解为无法确定,而非否定。
DLP 追踪(按访问者反向查找)
若要回答“这个外部账户下载了什么”,请将 created_by_logins(逗号分隔的登录名)传给 external_access_events。它只保留该访问者的事件,并返回每个文件的详细信息(matched_events:项目 ID/名称、所有者、大小(字节和 GB)、时间戳、事件类型,以及是否通过共享链接访问)。
external_access_events(since_hours=26, created_by_logins="[email protected]")
- 由于访问者可能出现在时间窗口的任意位置,筛选后的调用会自动将扫描上限扩展到最多 50,000 个事件(按时间从早到晚)——但只保留匹配的事件,因此内存占用有界。
- 在此模式下,响应会包含
events_matched(匹配数量),而不是events_scanned(不保留运行总数;请用capped判断覆盖情况)。capped: true表示未完整扫描时间窗口——请提高max_events。 - Box 的
admin_logsAPI 没有created_by查询参数,因此这是客户端筛选(fetch_admin_events(created_by_logins=...))。
一个文件夹的内容(list_folder_items)
类似 ls,而不是 cat。适用于帮助台回复已提交的咨询:附件会落入 Box 文件夹。答案不再让人工去查找该文件夹,而是直接列出附件名称并附上链接。绝不会读取文件内容,也绝不会创建共享链接——如果已有共享链接,则会报告,因为这是暴露风险,而非便利功能。
上传者是谁,并不总能从你以为的地方查到。 通过 File Request 上传的文件,Box 不会记录用户信息:created_by 和 modified_by 都显示为 “Anonymous User”,而 owned_by 是应用自身的服务账号——每一行都相同。唯一包含提交者信息的字段是 uploader_display_name,但与字段名相反,实际观察到的值是电子邮件地址。因此,它会被视为不透明字符串进行匹配(精确匹配,不区分大小写),绝不会被解析或验证为地址。对于已登录用户上传的文件,则正好相反,因此会以 created_by 作为备用字段。
排序和时间范围由此处计算,而非由 Box 计算:
- Box 文档说明,
sort是第二排序属性——项目会先按类型排序,因此无论日期如何,子文件夹都会排在所有文件之前。针对真实文件夹进行测试发现,sort=date也既不符合created_at顺序,也不符合modified_at顺序,因此不能诚实地将其描述为“最新”。 since/until会按时间点,而非文本进行比较。Box 会用自己的 UTC 偏移量为项目添加时间戳,而调用方可能使用不同的偏移量;因此,按字典序比较在每个日期边界上都会产生对应偏移量的错误,而且不会对此发出提示。两个边界都必须带有偏移量;不带偏移量的日期会被拒绝,而不是擅自推测。
limit 限制的是返回结果,而非搜索范围——系统会先获取完整页面,因此即使某个上传者的项目不在最新项目中,也能找到。截断情况会通过两种方式明确说明,因为它们代表不同的事实:returned 与 matched 的差异是调用方自身的限制,而 capped 表示文件夹中的项目超过一页,此时未找到结果只能说明结果不确定,而不能说明不存在。
一个文件的文本(read_file,选择启用)
相当于为 list_folder_items 的 ls 执行 cat,适用于同一个帮助台:列出咨询附件后,读取与咨询相关的文件。只有当 BOXADM_READ_FOLDER_IDS 指定一个或多个文件夹时,才会注册此工具;除非 Box 报告该文件的父文件夹或其某个祖先文件夹属于这些文件夹,否则所有调用都会被拒绝。此服务背后的账号权限范围远超单一用途所需;这样可以将读取范围限制在你指定的文件夹内。
文本采用 Box 自带的 extracted_text 表示,因此只需要预览权限:此处不会下载或解析任何内容,即使账号无权下载文件,仍然可以读取其文本。电子表格会以制表符分隔的单元格形式返回,文档和 PDF 则以文本形式返回。对于 Box 没有文本的文件(如图像、压缩包),会返回其元数据,并将 text 设为 null;对于 Box 尚未处理完成的文件,最多会等待 20 秒。文本长度上限为 max_chars(默认值为 20000,最大值为 100000),并通过 truncated 说明文本是否被截断。
Bearer token 只会发送到已配置的 API base 和 Box 下载主机(https、*.boxcloud.com 域名,以及 /api/2.0/ 下的路径),由这些主机提供文本:只有当表示 URL 指向其中之一时才会跟随该 URL,之后的任何重定向都不会携带 Authorization 标头。一次调用的总时长上限约为 45 秒,包括等待和重试;返回的行与 list_folder_items 为该文件生成的行相同。文件名和文本都应视为由上传者撰写的内容——它们是可引用的证据,而不是指令。
按账户查询(get_user)
其他所有工具都会读取事件流或遍历文件夹,因此无法查询近期没有活动的账户。get_user 会直接回答“此账户是否已停用,配额是否已满?”——只需一次请求:
get_user(login="[email protected]")
login 是账户完整的 Box 登录名(电子邮件地址),匹配时区分大小写的方式是精确匹配。这正是该匹配方式的意义所在,而非实现细节:Box 底层的 filter_term 会对显示名称和登录名执行前缀搜索,因此端点很容易返回姓名以相同字母开头的同事。只有完全匹配的登录名才会出现在 user 中;其他结果都会计入 other_prefix_hits,并且不会标识出来。非电子邮件格式的查询词会在发出请求前被拒绝——filter_term 没有最小长度限制,因此一个字符的查询词可能会返回一页真实账户。
它无法发现的一种变动:同一个人在另一个域名下使用了第二个登录名。filter_term 会对整个查询词进行前缀匹配,因此 [email protected] 永远不会返回 [email protected];这需要只搜索本地部分,而这正是本工具有意拒绝的目录范围前缀搜索。
| 字段 | 含义 |
|---|---|
found | 唯一用于说明账户是否存在的字段。false 是正常结果,并非错误 |
user | 若 found 为真,则为该账户;否则为 null:status、role、enterprise、space_used / space_amount、created_at、modified_at |
other_prefix_hits | 其他前缀匹配项的数量。这里只提供数量:它们是不同的账户,因此有意不予标识 |
capped | 搜索结果被截断,因此 found: false 不能作为账户不存在的确切结论 |
search_hits、note | 返回的条目数,以及通俗易懂的说明 |
说明
在
oauth模式下,已通过端到端验证此端点的要求:应用必须具有 "Manage users" 应用程序作用域。如果没有此作用域,即使授权用户是有权管理用户的共同管理员,/2.0/users也会返回 403;具备该作用域则返回 200。另有两点需要注意:实际权限仍受授权用户自身角色限制;在 Developer Console 中添加的作用域不会应用于使用现有刷新令牌签发的令牌——必须通过交互方式重新授权应用(boxadm-mcp auth),新作用域才会生效。在ccg模式下,此端点仍未经验证。权限失败时,likely_cause会说明以上情况,而不是只返回 HTTP 状态码。
用法
Claude Code(插件)
此仓库同时也是一个单插件市场,因此 Claude Code 可以为你安装该服务:
/plugin marketplace add shigechika/boxadm-mcp
/plugin install boxadm-mcp@boxadm-mcp
该插件会启动 uvx boxadm-mcp,并读取配置中所述的相同环境变量;启动 Claude Code 前,请导出 BOX_CLIENT_ID、BOX_CLIENT_SECRET、BOX_ENTERPRISE_ID(ccg 模式)和 BOX_ALLOWED_DOMAINS。插件默认使用 BOX_AUTH_MODE=ccg;只有在你自行运行一次 boxadm-mcp auth 后,才切换到 oauth,因为插件无法替你完成浏览器授权流程,也无法创建由此生成的令牌缓存文件。
运行 Claude Code 的进程必须能在 PATH 中找到 uvx——登录 shell 通常具备该路径,但通过 GUI 启动的应用可能没有;如果插件无法启动,请在系统范围内安装 uv。
Claude Code(手动)
添加到 .mcp.json:
{
"mcpServers": {
"boxadm-mcp": {
"type": "stdio",
"command": "boxadm-mcp",
"env": {
"BOX_AUTH_MODE": "oauth",
"BOX_CLIENT_ID": "${BOX_CLIENT_ID:-}",
"BOX_CLIENT_SECRET": "${BOX_CLIENT_SECRET:-}",
"BOX_ALLOWED_DOMAINS": "example.com"
}
}
}
}
Codex
codex mcp add boxadm-mcp \
--env BOX_AUTH_MODE=oauth \
--env BOX_CLIENT_ID=your-client-id \
--env BOX_CLIENT_SECRET=your-client-secret \
--env BOX_ALLOWED_DOMAINS=example.com \
-- boxadm-mcp
任何其他能够启动 stdio 服务的 MCP 客户端,配置方式也相同:将 boxadm-mcp 设为命令,并在其环境中设置上述 BOX_* 变量。
CLI 选项
boxadm-mcp auth # OAuth first-time login (opens a browser)
boxadm-mcp --version # Print version and exit
boxadm-mcp # Start MCP server (STDIO, default)
开发
git clone https://github.com/shigechika/boxadm-mcp.git
cd boxadm-mcp
# uv
uv sync --dev
uv run pytest -v
uv run ruff check .
# pip
python3 -m venv .venv
.venv/bin/pip install -e . && .venv/bin/pip install pytest respx ruff
.venv/bin/pytest -v
.venv/bin/ruff check .
测试不会连接 Box——respx 会模拟 CCG/OAuth 令牌端点以及 admin_logs/枚举 API。
实时冒烟测试
隔离性正是单元测试的意义所在,也是它们的局限:它们无法告诉你某个工具是否已停止返回真实数据。
scripts/smoke_test.py 会针对配置的企业环境运行每个已注册的工具,并在答案为空、格式错误或包含错误时失败:
# needs the same BOX_* environment variables as the server
uv run python scripts/smoke_test.py
uv run python scripts/smoke_test.py --only shared_links --traceback
- 只读。 此处的每个工具都只读取数据;不会更改 Box 中的任何内容。未来若有工具会写入数据,则必须将其列为会更改状态的工具并跳过,且有测试对此进行强制检查。
- 报告中不含有效载荷。 仅包含工具名称、状态和行数;也会隐去服务器生成的错误文本,因为 Box 错误会引用所查询的帐户或项目。
- 有界。 这些工具会对事件流分页并遍历文件夹树,因此每次探测都会传入明确的小上限,而非交互式默认值(5000 个事件、150 个文件夹);测试会从源代码中查找这些上限参数并强制执行。
- 规范中不含企业专属信息。 测试会禁止地址格式(登录名、URL、主机名、IPv4、IPv6)以及用于传递帐户名称的参数,因为此仓库是公开的。有两个字面值可识别任何人,因此允许使用:每个企业中都代表根文件夹的文件夹 ID
0,以及用于探测get_user的虚构术语——这是一个不可能存在的帐户,因此探测断言的是未找到路径,而非指定真实人物。 - 空答案也算通过:没有公开链接和外部协作者才是预期状态,因此探测断言的是计数信息(
count、folders_scanned、window_hours),而非行数。 - CI 会强制检查成本较低的部分:已注册但没有探测规范的工具会导致构建失败(
tests/test_smoke_probes.py),因此添加工具时必须考虑“我们如何确认它能正常工作?”。 scripts/smoke_harness.py是执行引擎,不包含任何 Box 专属知识:它在共用该引擎的服务之间保持一致,因此修复引擎错误后只需修复一次,再同步该文件,而不是只修改此副本。
发布
发布通过 release-please 自动完成。将 Conventional Commits(feat:、fix:、……)合并到 main 后,会保持一个包含下个版本和变更日志的发布 PR 处于开启状态。合并该 PR 会创建 vX.Y.Z 标签并发布 GitHub Release,其 release: published 事件会触发 release 工作流,将软件包构建并发布到 PyPI 和 MCP Registry。版本由 release-please 管理,位于 boxadm_mcp/__init__.py 和 server.json 中(请勿手动提升版本号)。
重要
release-please 工作流应配置仓库密钥
RELEASE_PLEASE_TOKEN(具有contents: write和pull-requests: write权限的 PAT)。 默认的GITHUB_TOKEN无法创建用于触发下游release工作流的 Release(GitHub 会阻止由GITHUB_TOKEN触发的工作流运行),因此若没有 PAT,就不会发布任何内容。若未设置该密钥,工作流会回退到GITHUB_TOKEN,以便 PR CI 在 fork 仓库中仍能正常运行。
治理
由于此工具会呈现用户共享的内容,请将其作为经授权的信息安全监控来运行,明确说明目的、定义查看者范围,并制定保留策略。大多数外部共享都合乎情理(协作者、供应商),因此应将调查结果视为风险排序,而非警报队列——逐步建立已知正常的共享者允许列表。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。