An agent-first CLI for the Egnyte API — zero runtime dependencies, JSON-only output, built-in schema introspection, and --dry-run safety on every mutation.
Designed for two audiences:
- AI agents (Claude Code, Cursor, Copilot Workspace) — self-describing schema, always-JSON output,
--fieldsresponse masking to reduce token cost,--dry-runconfirmation before any mutation - Terminal users & scripts — clean JSON piped to
jq, headless CI via env vars, named multi-domain profiles
Three ways to give an AI agent access to Egnyte: a REST API, an MCP server, or this CLI. They are not equivalent.
| REST API | MCP Server | Egnyte CLI | |
|---|---|---|---|
| Setup | Integrate SDK into agent runtime | Run sidecar process, wire into agent host | npm install -g @egnyte/agentic-cli — done |
| Discovery | Read API docs or OpenAPI spec | MCP tool manifest at startup | egnyte schema --list at runtime, no docs needed |
| Output | Raw HTTP response (any shape) | Tool result (host-dependent format) | Always JSON on stdout, errors on stderr |
| Token cost | Full response unless you parse it | Full tool result | --fields masks to only what you need |
| Mutation safety | No built-in guard | Depends on implementation | --dry-run on every mutating command — mandatory |
| Auth | Manage tokens yourself | Managed by MCP host | OAuth stored at ~/.config/egnyte-cli/config.json, env vars for CI |
| Dependencies | SDK + your glue code | MCP runtime + server process | Zero runtime deps |
REST API — you control the full stack and need raw HTTP semantics (streaming, custom headers, webhook callbacks). Most integration work.
MCP server — your agent host (Cursor, Claude Desktop) supports MCP natively and you want tool-call semantics without subprocess invocation.
This CLI — you want an agent to operate Egnyte from any environment (terminal, CI, Claude Code, Copilot Workspace) with no runtime integration, built-in safety guards, and self-describing schema. The right default for agentic scripting and AI-assisted workflows.
- Getting Started
- Installation
- Authentication
- Common Use Cases
- Commands
- Agentic Design Principles
- Using with Claude Code
- Security
- Development
- Contributing
- License
# Install globally
npm install -g @egnyte/agentic-cli
# Authenticate (register your OAuth app at https://developers.egnyte.com first)
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <secret>
# Verify
egnyte whoami
# List a folder
egnyte fs get /Shared --json '{"list_content": true}' --fields files.name,files.path,files.size,folders.name,folders.path,folders.is_folderRequirements: Node.js ≥ 14, npm ≥ 6
npm install -g @egnyte/agentic-cliTo upgrade: npm install -g @egnyte/agentic-cli@latest
Verify the install:
egnyte --help
egnyte schema --list # lists all 64 available operationsCreate an app at developers.egnyte.com. See the getting started guide for reference.
egnyte login \
--domain https://mycompany.egnyte.com \
--client-id YOUR_CLIENT_ID \
--client-secret YOUR_CLIENT_SECRET
# All flags have env var equivalents — omit any flag that is already set in the environment
EGNYTE_DOMAIN=https://mycompany.egnyte.com \
EGNYTE_CLIENT_ID=YOUR_CLIENT_ID \
EGNYTE_CLIENT_SECRET=YOUR_CLIENT_SECRET \
egnyte loginThe CLI opens your browser for OAuth approval. After authorizing, copy the code= value from the redirect URL and paste it in the terminal. The access token is stored at ~/.config/egnyte-cli/config.json (file mode 0600 — owner read/write only).
If the token response omits scope, the CLI preserves the scopes it requested and shows the raw scope string in egnyte login / egnyte whoami.
# Runtime auth — replaces stored profile entirely
export EGNYTE_TOKEN=<bearer-token>
export EGNYTE_DOMAIN=https://mycompany.egnyte.com
# Login-time vars — alternatives to passing flags to `egnyte login`
export EGNYTE_CLIENT_ID=<client-id>
export EGNYTE_CLIENT_SECRET=<client-secret>
export EGNYTE_SCOPE="Egnyte.filesystem Egnyte.user" # space-separated; omit for all scopes
export EGNYTE_REDIRECT_URI=https://www.egnyte.com # default; only set if your app uses a different URINo config file is read or written when EGNYTE_TOKEN + EGNYTE_DOMAIN are present.
--token / --domain flags
↓
EGNYTE_TOKEN / EGNYTE_DOMAIN env vars
↓
stored profile (~/.config/egnyte-cli/config.json)
# Log in to two domains as separate profiles
egnyte login --domain https://mycompany-staging.egnyte.com --client-id <id> --client-secret <s> --profile staging
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <s> --profile prod
# By default, login requests all scopes.
# Use --scope to restrict (space-separated) if you need limited access.
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <s> \
--scope "Egnyte.filesystem Egnyte.user" --profile prod-readonly
# Switch the active profile
egnyte profiles use staging
# List all profiles
egnyte profiles list
# Remove a profile
egnyte profiles remove stagingIf a stored token is within 5 minutes of expiry and a refresh_token is present, the CLI silently refreshes it before the request.
Organise files from the terminal Move, rename, copy, and delete files without opening the browser. Every action shows you exactly what it will do before running.
egnyte fs move /Shared/Inbox/report.pdf --to /Shared/Finance/2026/report.pdf --dry-run
egnyte fs move /Shared/Inbox/report.pdf --to /Shared/Finance/2026/report.pdf --yesAsk questions about your content Use the built-in AI commands to summarise documents or ask questions scoped to a specific file or folder — no separate AI setup required.
egnyte ai summarize /Shared/Contracts/acme.pdf --fields response
egnyte ai ask-document /Shared/Contracts/acme.pdf "What are the payment terms?" --fields responseAutomate repetitive admin tasks Run bulk operations from a CSV — upload hundreds of files, delete a list of old folders, or update metadata across many items in one command.
egnyte fs upload --bulk-file-path ./uploads.csv --parallelism 4 --progress --yes
egnyte fs delete --bulk-file-path ./cleanup.csv --dry-run
egnyte fs delete --bulk-file-path ./cleanup.csv --parallelism 4 --progress --yesLet an AI agent manage files on your behalf Pair the CLI with Claude Code, Cursor, or any AI coding assistant. The agent can list, search, upload, and reorganise files — and must always show you the exact API call before touching anything.
# In Claude Code — just describe what you want:
# "Archive all PDFs older than 2024 from /Shared/Projects to /Shared/Archive/2023"Monitor file activity Pull the audit trail to see who changed what and when — useful for compliance checks or debugging unexpected changes.
egnyte events get-cursor
egnyte events list --json '{"id": 12345678, "count": 50}' --fields events.action,events.actor,events.timestampManage users and permissions from a script Onboard new team members, adjust folder access, or clean up stale accounts — all scriptable, all with dry-run safety.
egnyte users create --json '{"userName":"jsmith@co.com","email":{"value":"jsmith@co.com"},"active":true}' --dry-run
egnyte perms set-group /Shared/Finance --json '{"groups":{"Engineering":"Viewer"}}' --yesEvery command outputs JSON to stdout. Errors are written as {"error":"..."} to stderr. Exit code is 0 on success, 1 on error.
| Flag | Description |
|---|---|
--json '{}' |
JSON request body or query params |
--fields a,b,c |
Return only these fields (reduces response size) |
--dry-run |
Print the equivalent curl command; do not execute |
--yes |
Execute a mutating command without a confirmation prompt |
--bulk-file-path <csv> |
Execute supported commands in bulk from a CSV file |
--from-csv <csv> |
Alias for --bulk-file-path |
--parallelism <n> |
Max in-flight bulk operations (default: 2) |
--progress |
Human-readable progress updates on stderr |
--json-progress |
Newline-delimited JSON progress events on stderr |
--profile <name> |
Use a named profile instead of the default |
--token / --domain |
Override stored credentials for one call |
Inspect any operation at runtime — no documentation required.
# List all 64 available operations
egnyte schema --list
# Full parameter reference for a specific operation
egnyte schema fs.action
egnyte schema users.create
egnyte schema events.list
egnyte schema search.advanced
egnyte schema ai.ask-kbExample output:
{
"summary": "Perform a file/folder operation: add_folder | move | copy | rename",
"method": "POST",
"endpoint": "/pubapi/v1/fs/{path}",
"mutating": true,
"body_params": {
"action": { "type": "string", "required": true, "description": "add_folder | move | copy | rename" },
"destination": { "type": "string", "required": false, "description": "Destination path (required for move and copy)" },
"new_name": { "type": "string", "required": false, "description": "New filename (required for rename)" }
},
"example": "egnyte fs action /Shared/NewFolder --json '{\"action\": \"add_folder\"}' --dry-run"
}egnyte fs get <path> [--json '{}'] [--fields a,b,c]# File metadata
egnyte fs get /Shared/report.pdf --fields name,path,size,entry_id
# List folder contents
egnyte fs get /Shared --json '{"list_content": true}' --fields files.name,files.path,files.size,folders.name,folders.path,folders.is_folder
# Paginate (increment offset by count until files + folders are both empty)
egnyte fs get /Shared \
--json '{"list_content": true, "count": 50, "offset": 0}' \
--fields files.name,files.path,files.size,folders.name,folders.path,folders.is_folderNamed subcommands (preferred):
# Create folder
egnyte fs mkdir /Shared/NewFolder --dry-run
egnyte fs mkdir /Shared/NewFolder --yes
# Rename
egnyte fs rename /Shared/report.pdf --name report-final.pdf --dry-run
egnyte fs rename /Shared/report.pdf --name report-final.pdf --yes
# Move
egnyte fs move /Shared/report.pdf --to /Shared/Archive/report.pdf --dry-run
egnyte fs move /Shared/report.pdf --to /Shared/Archive/report.pdf --yes
# Copy
egnyte fs copy /Shared/report.pdf --to /Shared/Backup/report.pdf --dry-run
egnyte fs copy /Shared/report.pdf --to /Shared/Backup/report.pdf --yesOr use the lower-level fs action command directly:
egnyte fs action <path> --json '{"action": "<action>", ...}' [--dry-run]# Standard upload (recommended for files ≤ 10 MB)
egnyte fs upload /Shared/docs/report.pdf --file ./report.pdf --dry-run
egnyte fs upload /Shared/docs/report.pdf --file ./report.pdf --yes
# Chunked upload (recommended for files > 10 MB)
# Minimum chunk size enforced by the Egnyte API: 10 MB (except the final chunk)
egnyte fs upload-chunked /Shared/bigfile.iso --file ./bigfile.iso --dry-run
egnyte fs upload-chunked /Shared/bigfile.iso --file ./bigfile.iso --yes
# Custom chunk size (bytes) — must be ≥ 10485760 (10 MB) except for single-chunk files
egnyte fs upload-chunked /Shared/bigfile.iso --file ./bigfile.iso --chunk-size 15728640Files smaller than one chunk size automatically fall back to the standard /fs-content endpoint.
Downloads are streamed directly to disk — safe for files of any size.
# Download by path
egnyte fs download /Shared/report.pdf --out ./report.pdf
# Download by group_id (from upload response or fs get metadata)
egnyte fs download-by-id <group-id> --out ./report.pdf
egnyte fs download-by-id <group-id> --out ./report.pdf --resumeegnyte fs delete <path> [--dry-run | --yes]egnyte fs delete /Shared/old-report.pdf --dry-run
egnyte fs delete /Shared/old-report.pdf --yesFetch file content as text without writing to disk — useful for reading docs or CSVs in an agent workflow.
egnyte fs get-content <path> [--json '{"offset":0,"limit":5000}']egnyte fs get-content /Shared/notes.txt
egnyte fs get-content /Shared/data.csv --json '{"offset":0,"limit":10000}'# List all metadata namespaces and their field definitions
egnyte fs list-metadata-namespaces --fields namespace,fields
# Set custom metadata on a file — dry-run first
egnyte fs set-metadata /Shared/contract.pdf \
--json '{"namespace":"contract","values":{"status":"signed","reviewed_by":"jsmith"}}' \
--dry-run
egnyte fs set-metadata /Shared/contract.pdf \
--json '{"namespace":"contract","values":{"status":"signed","reviewed_by":"jsmith"}}' \
--yes# Basic full-text search
egnyte search <query> [--json '{}'] [--fields a,b,c]
# Advanced search with metadata filters, date ranges, and folder scoping
egnyte search advanced <query> [--json '{}'] [--fields a,b,c]# Basic search
egnyte search "quarterly report" --fields results.name,results.path,results.size
# Scoped to a folder, with pagination
egnyte search "budget" \
--json '{"count": 20, "offset": 0, "folder": "/Shared/Finance", "type": "file"}' \
--fields results.name,results.path,results.size
# Advanced search — date filter
egnyte search advanced "contract" \
--json '{"folder":"/Shared/Legal","modified_after":"2024-01-01","type":"file"}' \
--fields results.name,results.path,results.size
# Advanced search — custom metadata filter
egnyte search advanced "NDA" \
--json '{"custom_metadata":{"status":"signed"},"namespaces":["contract"]}' \
--fields results.name,results.path# Ask a question via Copilot (optionally scope to files or folders)
egnyte ai ask "<question>" [--json '{}'] [--fields a,b,c]
# Ask about a specific file (path auto-resolved to entry-id)
egnyte ai ask-document <path> "<question>" [--json '{}'] [--fields a,b,c]
# Summarize a file (path auto-resolved to entry-id)
egnyte ai summarize <path> [--fields response]
# List available Knowledge Bases
egnyte ai list-kbs [--json '{}'] [--fields content]
# Query a specific Knowledge Base
egnyte ai ask-kb <kb-id> "<question>" [--json '{}'] [--fields response,citations]
# Semantic + keyword hybrid search
egnyte ai hybrid-search "<query>" [--json '{}'] [--fields results]# Copilot question, optionally scoped to specific files or folders
egnyte ai ask "What are the key metrics in Q3?" --fields response
egnyte ai ask "Revenue trends?" \
--json '{"selectedItems":{"folders":[{"id":"<folder-id>"}]},"includeCitations":true}' \
--fields response,citations
# Ask about a specific file
egnyte ai ask-document /Shared/Contracts/acme.pdf "What are the payment terms?" --fields response
egnyte ai ask-document /Shared/Contracts/acme.pdf "What are the payment terms?" \
--json '{"includeCitations":true}' --fields response,citations
# Summarize a file
egnyte ai summarize /Shared/Reports/annual-report.pdf --fields response
# List active Knowledge Bases
egnyte ai list-kbs \
--json '{"status":["ACTIVE"],"sortBy":["name"],"sortDirection":["ASC"]}' \
--fields content
# Query a Knowledge Base with citations
egnyte ai ask-kb kb-abc123 "What is the refund policy?" \
--json '{"includeCitations":true}' \
--fields response,citations
# Hybrid search — balance semantic vs keyword matching with semanticWeight (0.0–1.0)
egnyte ai hybrid-search "quarterly report" \
--json '{"semanticWeight":0.7,"folderPath":"/Shared/Finance","limit":10}' \
--fields resultsAll AI commands are read-only — --yes is not required.
Multi-step reasoning and conversation-aware queries powered by configured Egnyte AI agents. Use agents instead of ai ask when you need:
- Multi-turn conversation continuity (
conversationId) - Custom agent behavior via
instructions - Complex workflows that require the agent's built-in context and tooling
The agents ask command is synchronous by default — it submits the question and polls until the agent responds (up to 5 minutes). Use --no-wait to fire-and-forget and check status separately.
# List all available agents
egnyte agents list --fields agentId,name,status,category
# Ask an agent — blocks until complete (polls every 2 s, 5 min timeout)
egnyte agents ask <agentId> "Summarize the Q3 results" --fields responseText,citations
# Multi-turn: continue a prior conversation
egnyte agents ask <agentId> "Now compare that to Q2" \
--json '{"conversationId":"<id from prior response>"}' \
--fields responseText
# Custom agent behavior for this call
egnyte agents ask <agentId> "Draft a summary" \
--json '{"instructions":"Respond in bullet points, no more than 5 items"}' \
--fields responseText
# Scope to specific files
egnyte agents ask <agentId> "What are the key risks?" \
--json '{"selectedItems":{"files":[{"entryId":"<id>","filePath":"/Shared/contract.pdf"}]}}' \
--fields responseText,citations
# Fire-and-forget — returns requestId + conversationId immediately
egnyte agents ask <agentId> "Long running analysis task" \
--no-wait --fields requestId,conversationId
# Check status later
egnyte agents status <agentId> <requestId> --fields status,responseText,citationsResponse status values: PENDING | RUNNING | COMPLETED | FAILED
All agent commands are read-only — --yes is not required.
# Create a shared link — dry-run first
egnyte links create \
--json '{"path":"/Shared/report.pdf","type":"file","accessibility":"domain"}' \
--dry-run
egnyte links create \
--json '{"path":"/Shared/report.pdf","type":"file","accessibility":"anyone","expiry_date":"2026-12-31"}' \
--fields id,url,path,accessibility --yes
# List links for a path
egnyte links list --json '{"path":"/Shared/report.pdf"}' --fields ids,total_count
# Get a link
egnyte links get <link-id> --fields id,url,path,accessibility
# Delete a link — dry-run first
egnyte links delete <link-id> --dry-run
egnyte links delete <link-id> --yesaccessibility values: anyone | domain | password | recipients
Uses the SCIM-compatible /pubapi/v2/users endpoint.
# List users
egnyte users list --json '{"count": 50}' --fields resources.id,resources.userName,resources.email,resources.active
# Get a user by numeric ID
egnyte users get <id> --fields userName,email,active,userType
# Create a user — dry-run first
egnyte users create \
--json '{"userName":"jsmith@co.com","email":{"value":"jsmith@co.com"},"active":true,"sendInvite":false}' \
--dry-run
egnyte users create \
--json '{"userName":"jsmith@co.com","email":{"value":"jsmith@co.com"},"active":true,"sendInvite":false}' \
--yes
# Update a user (PATCH — only fields you include are changed)
egnyte users update <id> --json '{"active": false}' --dry-run
egnyte users update <id> --json '{"active": false}' --yes
# Delete a user — dry-run first
egnyte users delete <id> --dry-run
egnyte users delete <id> --yesUses the SCIM-compatible /pubapi/v2/groups endpoint.
# List groups
egnyte groups list --json '{"count": 50}' --fields resources.id,resources.displayName
# Get a group by ID
egnyte groups get <id> --fields displayName,members
# Create a group — dry-run first
egnyte groups create --json '{"displayName":"Engineering"}' --dry-run
egnyte groups create --json '{"displayName":"Engineering"}' --yes
# Update a group (rename or change members)
egnyte groups update <id> --json '{"displayName":"Eng Team"}' --dry-run
egnyte groups update <id> --json '{"displayName":"Eng Team"}' --yes
# Delete a group — dry-run first
egnyte groups delete <id> --dry-run
egnyte groups delete <id> --yesFolder permission levels: Owner | Editor | Viewer | None
# Get all user permissions on a folder
egnyte perms get-user /Shared/Finance --fields users
# Set user permissions — dry-run first
egnyte perms set-user /Shared/Finance \
--json '{"users": {"jsmith": "Viewer", "mjones": "Editor"}}' \
--dry-run
egnyte perms set-user /Shared/Finance \
--json '{"users": {"jsmith": "Viewer", "mjones": "Editor"}}' \
--yes
# Remove user permissions — dry-run first
egnyte perms delete-user /Shared/Finance \
--json '{"users": ["jsmith"]}' \
--dry-run
egnyte perms delete-user /Shared/Finance --json '{"users": ["jsmith"]}' --yes
# Get all group permissions on a folder
egnyte perms get-group /Shared/Finance --fields groups
# Set group permissions — dry-run first
egnyte perms set-group /Shared/Finance \
--json '{"groups": {"Engineering": "Editor"}}' \
--dry-run
egnyte perms set-group /Shared/Finance \
--json '{"groups": {"Engineering": "Editor"}}' \
--yes
# Remove group permissions — dry-run first
egnyte perms delete-group /Shared/Finance \
--json '{"groups": ["Engineering"]}' \
--dry-run
egnyte perms delete-group /Shared/Finance --json '{"groups": ["Engineering"]}' --yes
# Get a specific user's permission level on a folder
egnyte perms get-by-user jsmith --json '{"folder": "/Shared/Finance"}'Audit trail for file and folder activity across the domain.
# Step 1 — get the latest event ID (your polling start point)
egnyte events get-cursor
# Step 2 — list events from that ID
egnyte events list --json '{"id": 12345678, "count": 20}' --fields events.id,events.action,events.actor,events.timestamp
# Filtered — specific folder and event types
egnyte events list \
--json '{"id": 12345678, "count": 50, "folder": "/Shared", "type": "create|move|delete"}' \
--fields events.id,events.action,events.actor,events.timestamp,events.dataEvent types: create | move | delete | edit | lock | unlock | restore
Comments attached to files.
# Add a note — dry-run first
egnyte notes add /Shared/report.pdf \
--json '{"body": "Please review section 3 before the Thursday call"}' \
--dry-run
egnyte notes add /Shared/report.pdf \
--json '{"body": "Please review section 3 before the Thursday call"}'
# List all notes on a file
egnyte notes list /Shared/report.pdf
# Get a note by ID
egnyte notes get <note-id>
# Delete a note — dry-run first
egnyte notes delete <note-id> --dry-run
egnyte notes delete <note-id>Prevent concurrent edits by locking a file.
# Lock a file — dry-run first
egnyte lock lock /Shared/report.pdf \
--json '{"lock_token": "my-token", "lock_timeout": 300}' \
--dry-run
egnyte lock lock /Shared/report.pdf \
--json '{"lock_token": "my-token", "lock_timeout": 300}'
# Check lock status
egnyte lock get /Shared/report.pdf --fields locked,lock_owner,lock_timeout
# Unlock — dry-run first
egnyte lock unlock /Shared/report.pdf \
--json '{"lock_token": "my-token"}' \
--dry-run
egnyte lock unlock /Shared/report.pdf --json '{"lock_token": "my-token"}'Manage deleted items and recovery.
# List items in the trash
egnyte trash list --json '{"count": 50}' --fields items.name,items.path,items.size,items.id
# Restore items from the trash (requires IDs from trash list)
egnyte trash restore --json '{"ids": ["item_id_1", "item_id_2"]}' --dry-run
egnyte trash restore --json '{"ids": ["item_id_1"]}' --yes
# Permanently delete items from the trash
egnyte trash delete --json '{"ids": ["item_id_1"]}' --dry-run
egnyte trash delete --json '{"ids": ["item_id_1"]}' --yesManage lifecycle and metadata for project folders.
# List all project folders in the domain
egnyte projects list --fields name,id,status
# Get details for a specific project by ID
egnyte projects get a69bd625-1dc3-4dcf-98f5-8e3e3fbb0b29 --fields name,status
# Create a project from a template (v2 API)
egnyte projects create --json '{"name": "New HQ", "status": "pending", "parentFolderId": "...", "templateFolderId": "...", "folderName": "New HQ Folder"}' --dry-run
egnyte projects create --json '{"name": "New HQ", "status": "pending", "parentFolderId": "...", "templateFolderId": "...", "folderName": "New HQ Folder"}' --yes
# Mark an existing folder as a project (v1 API)
egnyte projects create --json '{"name": "Existing Site", "status": "in-progress", "rootFolderId": "..."}' --dry-run
egnyte projects create --json '{"name": "Existing Site", "status": "in-progress", "rootFolderId": "..."}' --yes
# Update project metadata
egnyte projects update a69bd625-1dc3-4dcf-98f5-8e3e3fbb0b29 --json '{"status": "completed"}' --dry-run
egnyte projects update a69bd625-1dc3-4dcf-98f5-8e3e3fbb0b29 --json '{"status": "completed"}' --yes
# Delete project metadata (demote back to a normal folder)
egnyte projects delete a69bd625-1dc3-4dcf-98f5-8e3e3fbb0b29 --dry-run
egnyte projects delete a69bd625-1dc3-4dcf-98f5-8e3e3fbb0b29 --yesReturns the authenticated user's live profile from the API — validates the stored token is still accepted by the server.
egnyte userinfo
egnyte userinfo --fields username,email,user_typeUnlike egnyte whoami (which reads stored credential metadata without a network call), userinfo makes a live API request and returns the user record.
Every response is machine-readable JSON. Errors always go to stderr as {"error":"..."}. There is no mixed prose/JSON output.
# Pipe directly to jq
egnyte fs get /Shared --json '{"list_content": true}' --fields folders.name,folders.path | jq '.folders[].path'
# Use exit codes in scripts
egnyte fs get /Shared/file.pdf --fields name && echo "exists" || echo "not found"Agents discover available operations and parameters without pre-loaded documentation:
egnyte schema --list # discover all 64 operations
egnyte schema <operation> # full parameter reference + exampleThis means agents can self-orient in a new environment without a system prompt full of API docs.
Every command that changes state (fs action, fs upload, fs delete, links create, users create, etc.) supports --dry-run. The flag prints the exact curl equivalent and exits without making a network call.
egnyte fs delete /Shared/report.pdf --dry-runcurl -X DELETE 'https://mycompany.egnyte.com/pubapi/v1/fs/Shared/report.pdf' \
-H 'Authorization: ***'
Bearer tokens are always masked as *** in dry-run output.
Limit which fields are returned to reduce response size and AI token cost:
# Without --fields: full metadata object for every file
egnyte fs get /Shared --json '{"list_content": true}'
# With --fields: only what you need
egnyte fs get /Shared --json '{"list_content": true}' --fields files.name,files.path,files.size,folders.name,folders.pathFor supported mutating commands, you can run one CSV row per API call with --bulk-file-path (or --from-csv). The CLI keeps normal single-item usage unchanged and adds shared controls for concurrency and progress reporting.
# delete.csv
# path
# /Shared/old-a.pdf
# /Shared/old-b.pdf
egnyte fs delete --bulk-file-path ./delete.csv --dry-run
egnyte fs delete --bulk-file-path ./delete.csv --parallelism 2 --progress --yesOn 429 Too Many Requests, the CLI automatically retries up to 3 times, honouring the Retry-After response header when present and falling back to exponential backoff (1 s → 2 s → 4 s). On 5xx server errors, the CLI retries GET and HEAD requests up to 3 times with exponential backoff and jitter; POST/PUT/DELETE fail immediately by design to avoid duplicate mutations. Agents running in a polling loop will not fail on transient rate limits or gateway hiccups.
npm install -g @egnyte/agentic-cli installs in seconds. No node_modules tree to audit, no native bindings to compile.
# Install
npm install -g @egnyte/agentic-cli
# Authenticate
egnyte login --domain https://mycompany.egnyte.com --client-id <id> --client-secret <secret>
# Add the skill file to Claude's global instructions
cat CLAUDE.md >> ~/.claude/CLAUDE.mdCLAUDE.md teaches Claude the core rules: always --dry-run before mutations, always --fields on list calls, never guess file IDs, paths must start with /.
Read operations — Claude executes immediately:
What files are in /Shared/Finance?
How many PDFs are in /Shared/Projects? Show names and sizes only.
Search for "quarterly report" in /Shared
Mutations — Claude dry-runs, shows the curl, then asks for confirmation:
Create a folder called /Shared/2026-Archive
Upload ./report.pdf to /Shared/Finance
Rename /Shared/Finance/report.pdf to report-final.pdf
Move everything in /Shared/Inbox to /Shared/Processed
Delete /Shared/Temp
Multi-step tasks:
Create /Shared/Archive/Q1-2026, upload all PDFs from ./exports into it, then list what was uploaded
When you ask Claude to delete a file, it will:
- Run
egnyte fs delete /Shared/report.pdf --dry-run - Show you the exact curl with the token masked
- Ask for your confirmation before executing
curl -X DELETE 'https://mycompany.egnyte.com/pubapi/v1/fs/Shared/report.pdf' \
-H 'Authorization: ***'
Ready to delete /Shared/report.pdf. Confirm?
Tokens are stored at ~/.config/egnyte-cli/config.json with file mode 0600 (owner read/write only — not readable by other users on the same machine).
The file contains: access_token, refresh_token, client_id, client_secret, domain, expires_at.
To remove all stored credentials:
egnyte logout # removes the default profile
egnyte profiles remove <name> # removes a named profile
rm ~/.config/egnyte-cli/config.json # nuclear option — removes everythingEvery path argument is validated against six rules before any network call:
| Rule | Example blocked input |
|---|---|
Must start with / |
Shared/docs |
No path traversal (..) |
/Shared/../../etc/passwd |
| No pre-encoded slash | /Shared%2Fdocs |
| No double-encoded characters | /Shared%252F |
| No embedded query string | /Shared?foo=bar |
| No null byte | /Shared%00docs |
$ egnyte fs get /Shared/../../etc/passwd
{"error":"Invalid path — path traversal (..) detected"}node --version # must be >= 14
npm --version # must be >= 6git clone https://github.com/egnyte/agentic-cli.git
cd agentic-cli
npm install
npm link # installs the `egnyte` binary from local sourcebin/egnyte ← shebang entry point
src/
index.js ← argument parsing + command dispatch + help text
commands/
auth.js ← login, logout, whoami, profiles, userinfo
fs.js ← fs get/action/mkdir/rename/move/copy/delete/upload/upload-chunked/download/download-by-id/get-content/set-metadata/list-metadata-namespaces
search.js ← search / search advanced
links.js ← links create/list/get/delete
users.js ← users get/list/create/update/delete
groups.js ← groups get/list/create/update/delete
perms.js ← perms get/set/delete (user + group + get-by-user)
events.js ← events get-cursor/list
notes.js ← notes add/list/get/delete
lock.js ← lock lock/unlock/get
ai.js ← ai ask/ask-document/summarize/ask-kb/list-kbs/hybrid-search
agents.js ← agents list/ask/status (async polling, multi-turn, --no-wait)
schema.js ← schema --list + schema <op>
lib/
args.js ← zero-dep argument parser
auth.js ← OAuth flow, token refresh, resolveAuth()
config.js ← profile storage (~/.config/egnyte-cli/config.json, mode 0600)
bulk.js ← --bulk-file-path CSV execution + progress events
fields.js ← --fields response masking
http.js ← apiRequest (with 429 retry), apiDownload (streaming), buildUrl
multipart.js ← multipart/form-data builder for file uploads
output.js ← out(), fatal(), info(), printDryRun(), CLIError
schema-registry.js ← SCHEMA constant (61 operations)
validation.js ← validatePath() — 6 security rules
spec/
lib_args.spec.js ← unit: parseArgs
lib_fields.spec.js ← unit: applyFields
lib_schema.spec.js ← unit: SCHEMA registry (includes agents ops)
lib_validation.spec.js ← unit: validatePath (all 6 rules)
cli_dryrun.spec.js ← subprocess: --dry-run output format
cli_whoami.spec.js ← subprocess: whoami / auth precedence
cli_ai.spec.js ← dry-run + live tests for ai commands
cli_agents.spec.js ← dry-run + live tests for agents commands
cli_storage.spec.js ← integration: real Egnyte API (requires credentials)
# Integration tests against a real Egnyte domain
cp spec/conf/egnyte-test-config.template.js spec/conf/egnyte-test-config.js
# edit spec/conf/egnyte-test-config.js — set egnyteDomain and APIToken
npm run test
# Lint (syntax check — no external tools required)
npm run lint- Implement the command function in the appropriate
src/commands/*.jsfile - Add a schema entry in
src/lib/schema-registry.js - Wire the command into the dispatch table in
src/index.js - Add the operation to
EXPECTED_OPSinspec/lib_schema.spec.js
- Fork the repository and create a branch from
master - Follow the existing code style (plain CommonJS, no transpilation, no runtime deps)
- Add or update tests for any new command
- Run
npm testandnpm run lint— both must pass - Open a pull request with a clear description of the change
Coding conventions:
- Plain Node.js CommonJS — no TypeScript, no build step, no bundler
- Zero runtime dependencies — stdlib only (
https,fs,path,os,url,crypto,child_process) CLIErrorfor user-facing errors (written to stderr as JSON); plainErrorfor unexpected failures- Every mutating command must support
--dry-run - Every new operation must have a schema entry in
schema-registry.js
Open an issue at github.com/egnyte/agentic-cli/issues.
Apache-2.0