dropbox-analytics-mcp-server
v0.4.7
Published
MCP server for Dropbox: read public shared links without auth, or browse/read/search a private Dropbox via OAuth refresh token. Parses CSV/Excel/JSON into tabular data for analytics.
Maintainers
Readme
dropbox-analytics-mcp-server
MCP server for Dropbox, built for analytics workflows. Read public shared links with zero authentication, browse a full Dropbox account via OAuth, or analyze someone else's shared folder link (public or password-protected). CSV/Excel files are parsed into tabular rows, PDF/DOCX into extracted text — ready for analysis.
Built-in rate limiting (max concurrent requests + request spacing + automatic 429 retry) and chunked reading (small defaults with offset-based pagination) keep large files and deep folder trees from hitting Dropbox rate limits or blowing up the model's context.
Operating modes
The server picks a mode automatically from the environment variables present:
| Mode | Env vars set | What you get |
|---|---|---|
| Account | DROPBOX_APP_KEY + DROPBOX_REFRESH_TOKEN (+ optional DROPBOX_APP_SECRET) | Full access to the connected account: list, search, read, upload, share |
| Shared link (authenticated) | Token vars above + DROPBOX_SHARED_URL (+ optional DROPBOX_SHARED_PASSWORD) | list_sources / read_source / search_in_source / summarize_source operate on that link — including links owned by other accounts, nested subfolders, and password-protected links |
| Public link (tokenless) | Only DROPBOX_SHARED_URL (must be a public link) | list_sources / read_source / search_in_source with no credentials at all |
| Public-link-only | Nothing | Just read_public_link for ad-hoc public links |
Quick start (no auth)
npx dropbox-analytics-mcp-server| Tool | Description |
|---|---|
| read_public_link | Download and parse any public Dropbox shared link (CSV/Excel → rows, PDF/DOCX → text, JSON → object, text → raw) |
Works with both old (/s/...) and new (/scl/fi/...?rlkey=...) link formats.
Tools (authenticated)
Source tools — the primary analytics interface
These work identically in account mode and shared-link mode:
| Tool | Description |
|---|---|
| list_sources | List ALL analyzable sources in one call: spreadsheets (kind table) and PDF/DOCX (kind document). Walks nested subfolders of shared links. |
| read_source | Read any source by name. Tables return rows, documents return text. Supports pagination (see below). |
| summarize_source | Compact profile without pulling full content: per-column stats (type, nulls, uniques, min/max/mean, top values) for tables; counts, preview, probable headings, and top keywords for documents. Use this first on large files. |
| search_in_source | Search inside a source's content. Tables → matching rows + which columns matched; documents → matching lines with context. |
Table / document tools
| Tool | Description |
|---|---|
| list_tables | List tabular files as { table_name } entries (optionally expand Excel sheets with include_sheets) |
| read_table | Read rows from a list_tables name |
| describe_table | Column names + inferred types + row count + sample rows |
| list_documents | List PDF/DOCX files |
Account tools
| Tool | Description |
|---|---|
| list_folder | List files/folders at a path |
| search_files | Search by name or content |
| get_file_metadata | Size, modified date, revision |
| read_file | Download + parse by real path or table name |
| list_shared_links / create_shared_link | Manage the account's own shared links |
| upload_file | Upload text content (e.g. export analytics results as CSV) |
| get_account_info | Verify the connection works |
Ad-hoc shared-link tools
| Tool | Description |
|---|---|
| verify_shared_link | Check a link + password is valid without persisting anything ({ valid, reason, kind, fileCount }) |
| list_shared_link | List sources inside an explicitly passed folder link |
| read_shared_link_file | Read one file inside an explicitly passed folder link |
Chunked reading / pagination
Defaults are intentionally small so a single call never floods the context. Responses include everything needed to continue:
Tables — default 50 rows per call:
read_source source_name: "Reports.sales" → rows 0–49, hasMore: true, nextOffsetRows: 50
read_source source_name: "Reports.sales", offset_rows: 50 → rows 50–99, hasMore: true, nextOffsetRows: 100Documents — default 500 characters per call:
read_source source_name: "contract" → chars 0–499, hasMore: true, nextOffsetChars: 500
read_source source_name: "contract", offset_chars: 500 → chars 500–999, ...Every response carries hasMore and nextOffsetRows / nextOffsetChars — pass the value back as offset_rows / offset_chars to fetch the next chunk. totalRows / charCount report the full size. Increase max_rows / max_chars per call if you need bigger chunks.
Recommended flow for large files: summarize_source first → then read_source with offsets or search_in_source to pull only what's needed.
Rate limiting
All outbound Dropbox calls (SDK + public-link fetches) flow through a global limiter:
- Max 3 concurrent requests (
DROPBOX_MAX_CONCURRENT) - Minimum 350 ms between request starts (
DROPBOX_MIN_GAP_MS) - Automatic retry on HTTP 429 (up to 3 attempts), honouring Dropbox's
retry_after, exponential backoff capped at 5 s
Parallel operations (like the nested shared-folder walk) are automatically kept under these caps — no more too_many_requests errors from bursty listings.
One-time setup: getting a refresh token
Dropbox no longer issues long-lived access tokens, so the server uses an auto-refreshing refresh token:
1. Create a Dropbox app at https://www.dropbox.com/developers/apps
- Choose Scoped access → Full Dropbox (or App folder for sandboxing)
- Under Permissions, enable:
files.metadata.read,files.content.read,sharing.read,account_info.read, plusfiles.content.write(only forupload_file) andsharing.write(only forcreate_shared_link) - Note the App key and App secret from the Settings tab
2. Authorize — open this URL in a browser (replace YOUR_APP_KEY):
https://www.dropbox.com/oauth2/authorize?client_id=YOUR_APP_KEY&response_type=code&token_access_type=offlineApprove the app and copy the authorization code.
3. Exchange the code for a refresh token:
curl https://api.dropbox.com/oauth2/token \
-d code=YOUR_AUTH_CODE \
-d grant_type=authorization_code \
-u YOUR_APP_KEY:YOUR_APP_SECRETThe response contains refresh_token — save it. It does not expire unless revoked.
Note for shared-link mode:
sharing.readscope is required. Iflist_sourcesreports zero items on a valid link, re-check the app's Permissions tab and re-authenticate to mint a token with the updated scope.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
| DROPBOX_APP_KEY | for account access | App key from the Dropbox app console |
| DROPBOX_APP_SECRET | confidential apps only | App secret (omit for PKCE apps) |
| DROPBOX_REFRESH_TOKEN | for account access | Auto-exchanged for short-lived access tokens |
| DROPBOX_ACCESS_TOKEN | alternative | Short-lived token for quick testing only (~4 h) |
| DROPBOX_SHARED_URL | for shared-link mode | A shared file or folder link to operate on |
| DROPBOX_SHARED_PASSWORD | if link is protected | Password for the shared link (requires a token) |
| DROPBOX_MAX_CONCURRENT | optional (default 3) | Max simultaneous Dropbox requests |
| DROPBOX_MIN_GAP_MS | optional (default 350) | Minimum ms between request starts |
| DROPBOX_DEFAULT_MAX_ROWS | optional (default 50) | Default rows per table read |
| DROPBOX_DEFAULT_DOC_CHARS | optional (default 500) | Default characters per document read |
MCP client configuration
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"dropbox": {
"command": "npx",
"args": ["-y", "dropbox-analytics-mcp-server"],
"env": {
"DROPBOX_APP_KEY": "your_app_key",
"DROPBOX_APP_SECRET": "your_app_secret",
"DROPBOX_REFRESH_TOKEN": "your_refresh_token"
}
}
}
}To analyze a shared folder link instead of the account root, add:
"DROPBOX_SHARED_URL": "https://www.dropbox.com/scl/fo/abc123/h?rlkey=xyz&dl=0",
"DROPBOX_SHARED_PASSWORD": "optional-link-password"For public-link-only mode, omit the env block entirely.
Tool examples
summarize_source
source_name: "Reports.sales" → column stats + 3 sample rows
read_source
source_name: "Reports.sales::Q1" → first 50 rows of sheet Q1
offset_rows: 50 → next 50 rows
read_source
source_name: "contract" → first 500 chars of the PDF
offset_chars: 500, max_chars: 2000 → next 2000 chars
search_in_source
source_name: "contract"
query: "termination clause"
read_public_link
shared_link: "https://www.dropbox.com/scl/fi/abc/sales.xlsx?rlkey=xyz&dl=0"
sheet_name: "Q1"
upload_file
path: "/Exports/summary.csv"
content: "region,total\nWest,1200\nEast,950"Limits
- Downloads capped at 50 MB per file
- Table reads default to 50 rows per call (
max_rows/offset_rowsto page; env-tunable) - Document reads default to 500 characters per call (
max_chars/offset_charsto page; 200k absolute ceiling per call; env-tunable) - Plain-text files truncated at 100k characters
- Shared-folder walks capped at 10,000 entries / 500 folders
search_in_sourcescans up to 5,000 rows by default (scan_rowsto raise)upload_fileis text-only (UTF-8); binary upload not supported
Development
npm install
npm run build
node dist/index.jsLicense
MIT
