microsoft-onedrive
Read and manage OneDrive / SharePoint files via Microsoft Graph. Use when the user mentions OneDrive files / folders, SharePoint documents, file uploads / downloads, sharing links, or "my drive".
What it does
Drive Microsoft Graph for OneDrive / SharePoint via curl + jq. The
user's OAuth bearer token is in $MICROSOFT_ONEDRIVE_TOKEN; every call
needs it as Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN. The token
already carries the OneDrive scopes the user agreed to at install time
(Files.Read, Files.Read.All, optionally Files.ReadWrite.All,
Sites.Read.All).
The Graph API returns standard JSON; failures surface as JSON
{"error": {"code": "...", "message": "..."}} — show that error
verbatim to the user.
Always start with /me to confirm the connection works AND learn
which account / drive you're operating against.
Recipes
Verify auth (always run first)
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
https://graph.microsoft.com/v1.0/me \
| jq '{displayName, mail, userPrincipalName}'
If you get 401 InvalidAuthenticationToken, the token expired —
report it; the user has to reinstall the connector.
List files in root
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/root/children?\$top=20&\$select=id,name,size,lastModifiedDateTime,folder,file" \
| jq '.value[] | {id, name, size, kind: (if .folder then "folder" else .file.mimeType end), modified: .lastModifiedDateTime}'
Folders have "folder":{"childCount":N}, files have "file":{"mimeType":"..."}.
List files in a sub-folder by path
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/root:/Documents:/children?\$top=20&\$select=id,name,size,lastModifiedDateTime"
Path uses : as the path/segment separator — :/Documents/Q1:/children.
Search files (recursive)
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
--data-urlencode "q=quarterly report" --get \
"https://graph.microsoft.com/v1.0/me/drive/root/search(q='quarterly report')?\$top=25&\$select=id,name,size,webUrl,lastModifiedDateTime"
search(q='')with empty query returns 400. To find files by type without a keyword, search by extension:search(q='.pdf').
Recently modified files (cross-folder)
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/recent?\$top=25" \
| jq '.value[] | {name, modified: .lastModifiedDateTime, parent: .parentReference.path}'
Files shared with me
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/sharedWithMe?\$top=25" \
| jq '.value[] | {name, size: .size, owner: .remoteItem.shared.owner.user.displayName}'
Download a file by item id
# /content returns 302 to a pre-signed URL — let curl follow it.
curl -sSL -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}/content" \
-o "$SKILL_DIR/tmp/$(basename "$NAME")"
Download a file by path
# URL-encode each path segment with jq -Rr @uri (or use printf encoding).
ENCODED=$(printf '%s' "Documents/report.docx" | jq -sRr @uri)
curl -sSL -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/root:/${ENCODED}:/content" \
-o report.docx
Upload a small file (< 4 MB)
curl -sS -X PUT \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @/tmp/report.pdf \
"https://graph.microsoft.com/v1.0/me/drive/root:/Documents/report.pdf:/content"
For files > 4 MB use an upload session (chunked):
# 1) create session
SESSION=$(curl -sS -X POST \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"item":{"@microsoft.graph.conflictBehavior":"rename"}}' \
"https://graph.microsoft.com/v1.0/me/drive/root:/Documents/big.zip:/createUploadSession")
UPLOAD_URL=$(echo "$SESSION" | jq -r .uploadUrl)
# 2) PUT in 10 MiB chunks with Content-Range: bytes <start>-<end>/<total>
# (See Microsoft Graph docs for the chunking loop; jq + dd makes this trivial.)
Create a folder
curl -sS -X POST \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Reports","folder":{},"@microsoft.graph.conflictBehavior":"rename"}' \
https://graph.microsoft.com/v1.0/me/drive/root/children
@microsoft.graph.conflictBehavior: rename (auto-suffix), replace
(overwrite), fail (error if exists). Default is fail.
Rename / move (PATCH)
⚠️ Always show the source and destination before executing.
# Rename only
curl -sS -X PATCH \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"renamed.docx"}' \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}"
# Move to a different folder
curl -sS -X PATCH \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -nc --arg pid "$NEW_PARENT_ID" '{parentReference:{id:$pid}}')" \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}"
Delete
⚠️ Always fetch the item name first and confirm with the user.
# 1) Show what will be deleted
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}?\$select=name,size,lastModifiedDateTime" \
| jq '"Delete \(.name) (\(.size) bytes, modified \(.lastModifiedDateTime))?"'
# 2) After user confirms (returns 204 No Content)
curl -sS -X DELETE -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}" \
-w "HTTP %{http_code}\n"
Create a sharing link
⚠️ Confirm with the user before sharing — this exposes data externally.
curl -sS -X POST \
-H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"view","scope":"organization"}' \
"https://graph.microsoft.com/v1.0/me/drive/items/${ITEM_ID}/createLink" \
| jq '.link.webUrl'
type: view | edit | embed. scope: anonymous | organization | users.
List SharePoint sites
Requires Sites.Read.All.
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/sites?search=*&\$top=10" \
| jq '.value[] | {id, name: .displayName, webUrl}'
Files inside a site:
curl -sS -H "Authorization: Bearer $MICROSOFT_ONEDRIVE_TOKEN" \
"https://graph.microsoft.com/v1.0/sites/${SITE_ID}/drive/root/children?\$top=20"
OData quick reference
| Param | Example |
|---|---|
$select | id,name,size,lastModifiedDateTime |
$filter | name eq 'report.docx', size gt 1000000 |
$orderby | lastModifiedDateTime desc |
$top | 10 browsing, 25 search |
$expand | children, permissions |
Use --data-urlencode "$key=$value" --get with curl to avoid shell-quoting $ and spaces.
Rules
- Always pass
$select— defaults return 30+ fields per item. $top=10for browse,25for search. Don't paginate past 50 unless asked.- URL-encode IDs if using them in a path — IDs can contain
+,/,=. Usejq -sRr @uri. - Empty
search(q='')returns 400 — search by extension if you don't have a keyword.
CRITICAL: User consent for destructive actions
Never delete, overwrite or share without explicit confirmation. Pattern: prepare → present → execute.
| Action | What to show user |
|---|---|
| Delete | "Delete '{name}' ({size} bytes, modified {date})?" |
Overwrite (@microsoft.graph.conflictBehavior=replace) | "Overwrite '{name}'? Existing: {size}, modified {date}" |
Share (createLink) | "Create {type} link for '{name}' with {scope} access?" |
| Move | "Move '{name}' from {old folder} to {new folder}?" |
| Bulk | Count + sample: "Delete 12 files in /Reports/?" |
Errors
401 InvalidAuthenticationToken→ token expired; user must reinstall the connector.403 accessDenied→ scope missing (e.g. trying to write with read-only token); ask user to reinstall and tick the write scope.429 TooManyRequests→ respect theRetry-Afterheader (in seconds).404 itemNotFound→ wrong id or path; double-check casing.
Reference
For deep dives consult Microsoft's docs:
Capabilities
Install
Quality
deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 7 github stars · SKILL.md body (8,669 chars)