MCP server for Box admin-log analytics β external-sharing visibility, read-only
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
We haven't yet run this listing's install command through our automated sandbox check. This isn't a red flag β we're steadily working through the catalog.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
English | ζ₯ζ¬θͺ
MCP (Model Context Protocol) server that surfaces external file flow from
a Box admin's point of view. It reads Box's enterprise event log
(admin_logs) to highlight "who shares a lot with the outside" and "which
files get accessed from outside" β an early-warning signal for leakage, not a
general-purpose file browser.
Documentation: https://shigechika.github.io/boxadm-mcp/
Read-only: it never revokes shares, deletes files, or otherwise mutates anything β it only surfaces risk. This is a different tool from a general-purpose Box file MCP (the official Box MCP, or the claude.ai Box connector): those operate on a user's own files and cannot see enterprise events, which is exactly what this server is for.
Named after the admin-console viewpoint (boxadm = Box admin), sibling of
gwsadm-mcp.
| Tool | Category | Description |
|---|---|---|
health_check | β | version + auth_mode + Box auth + admin_logs scope probe + configured domain allowlist. Reports needs-login when not yet authenticated (OAuth mode) |
recent_admin_events | Diagnostic | Raw recent enterprise events (for checking event types/fields). Supports manual pagination via stream_position |
external_access_events | Access (events, enterprise-wide) | Aggregates external DOWNLOAD/PREVIEW within a window: top external accessors, top externally-accessed files, share-link count. Pass created_by_logins for DLP tracing of a specific account |
external_collaborators | Exposure (enumeration) | Lists external collaborators (outside-org login or external invite email) |
public_shared_links | Exposure (enumeration) | Lists items shared with an open (anyone-with-the-link) share link |
top_external_sharers | Exposure (enumeration) | Ranks internal owners by external exposure (external collabs + public links) |
list_folder_items | One folder (ls) | Names, upload time, size, who uploaded, and a direct link per item. Filter by uploader or upload-time window. Reads no file content |
get_user | Account state (lookup) | One account by its exact login: status, role, enterprise, quota, timestamps. Answers "is this account disabled?" without an admin console |
daily_brief | Combined | Morning summary combining access (events) and exposure (enumeration) |
Two modes, selected via BOX_AUTH_MODE:
oauth β OAuth 2.0 (user auth). An admin authorizes once in a browser; the
refresh token keeps it running unattended after that.ccg β Client Credentials Grant (server-to-server). Simpler to run
unattended if your Box tenant has an available server-authentication app
slot.admin_logs (enterprise events) is readable in either mode, provided the
authorizing/impersonated user is an admin and the app has the Manage
enterprise properties scope.
http://localhost:8787/callbackadmin_logs). Add Read all files and folders too if you also want
collaboration/share-link enumeration, and Manage users if you want the
get_user lookup (each scope change requires re-consent via
boxadm-mcp auth)BOX_AUTH_MODE=oauth etc., then run boxadm-mcp auth β authorize in the browser β a token cache is written to
~/.config/boxadm-mcp/token.json (chmod 600)Or from source:
| Variable | Required | Description |
|---|---|---|
BOX_AUTH_MODE | oauth / ccg (default ccg). Any other value falls back to ccg; health_check reports the mode in effect, so a typo shows up there as ccg rather than as what was typed | |
BOX_CLIENT_ID | β | App Client ID |
BOX_CLIENT_SECRET | β | App Client Secret |
BOX_ENTERPRISE_ID | ccg mode | Enterprise ID (CCG subject; not needed for oauth) |
BOX_OAUTH_REDIRECT_URI | oauth redirect. Default http://localhost:8787/callback | |
BOX_TOKEN_CACHE | oauth token cache path. Default ~/.config/boxadm-mcp/token.json | |
BOX_API_BASE | Default https://api.box.com | |
BOX_SCAN_CONCURRENCY | Parallel per-folder lookups in the enumeration scan. Default 8, clamped 1β32 | |
BOX_SCAN_DEADLINE | Soft wall-clock budget (seconds) for one enumeration scan. Default 45; 0/negative disables it. When hit, the scan returns a disclosed partial (capped=true) instead of running until the tool call times out | |
BOX_HTTP_TIMEOUT | Per-request HTTP timeout (seconds). Default 30. Lower it (with BOX_SCAN_DEADLINE) so one slow endpoint can't stretch the final in-flight scan batch past a gateway timeout | |
BOX_ALLOWED_DOMAINS | β | Internal email domains (comma-separated). No default β every address counts as external until you set this |
Keep secrets out of .mcp.json (e.g. in a local env file sourced before
launch); .mcp.json itself can reference ${BOX_CLIENT_ID}-style variables
and be safely committed.
external_access_events, and the access half of
daily_brief) read the enterprise-wide events stream. Hitting
max_events sets capped: true (oldest-first scan).max_folders/max_depth limits (surfaced via capped). Requires the
Read all files and folders scope.BOX_SCAN_CONCURRENCY), since Box has no enterprise-wide collaboration
listing β this widens how many folders finish inside a tool-call timeout,
but coverage is still bounded by the caps. The read path retries 429
(honoring Retry-After) and transient 5xx with jittered backoff, so a
passing throttle recovers instead of degrading coverage; a folder dropped by
a per-folder API error that outlasts those retries (e.g. a persistent 403)
is counted in fetch_errors: coverage is complete only when capped is
false and fetch_errors is 0.public_shared_links skips collaboration calls entirely (optimization).get_user reads the enterprise user directory instead β one request,
no paging, and structurally not an enumerator (it answers about the login you pass and nothing
else). Its capped flag discloses a truncated search, so a found: false
from a truncated result reads as inconclusive rather than negative.To answer "what did this external account download": pass
created_by_logins (comma-separated logins) to external_access_events. It
keeps only that accessor's events and returns per-file detail
(matched_events: item id/name, owner, size in bytes+GB, timestamp,
event_type, whether it was via a share link).
events_matched (match count) instead of
events_scanned (no running total is kept; use capped to judge coverage).
capped: true means the window wasn't fully scanned β raise max_events.admin_logs API has no created_by query parameter, so this is a
client-side filter (fetch_admin_events(created_by_logins=...)).list_folder_items)An ls, not a cat. Written for a help desk answering a submitted enquiry whose
attachments land in a Box folder: instead of a human going to find that folder,
the answer names the attachments and links straight to them. File content is
never read, and no shared link is ever created β an existing one is reported
because it is an exposure finding, not a convenience.
Who uploaded an item is not where you would look for it. For an upload made
through a File Request, Box records no user at all: created_by and
modified_by both read "Anonymous User", and owned_by is the application's
own service account β identical on every row. The only field carrying the
submitter is uploader_display_name, and despite its name the value observed in
practice was an email address. It is therefore matched as an opaque string
(exact, case-insensitive) and never parsed or validated as an address. For a file
uploaded by a signed-in user the reverse holds, so created_by is the fallback.
Ordering and time bounds are computed here rather than by Box:
sort as the second sort attribute β items order by type
first, so a subfolder precedes every file regardless of date. Measured against
a real folder, sort=date also matched neither created_at nor modified_at
order, so it cannot honestly be presented as "newest".since / until are compared as instants, not text. Box stamps items in
its own UTC offset while a caller asks in theirs, so a lexicographic comparison
is wrong by that difference at every date boundary and silent about it. Both
bounds must carry an offset; a bare date is refused rather than guessed.No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/box-admin-analytics-mcp)<a href="https://allmcps.com/mcp/box-admin-analytics-mcp"><img src="https://allmcps.com/api/badge/box-admin-analytics-mcp?style=directory" alt="Box Admin Analytics MCP on AllMCPs" /></a>