Google Drive Lightweight Google Drive integration with standalone OAuth authentication. No MCP server required. Full read/write access. Requires Google Workspace account. Personal Gmail accounts are not supported. First-Time Setup Authenticate with Google (opens browser): Check authentication status: Logout when needed: Read Commands All operations via . Auto-authenticates on first use if not logged in. Write Commands Search Query Formats The search command supports multiple query formats: | Format | Example | Description | |--------|---------|-------------| | Full-text | | Searches file cont…

, query)) and ' ' not in query\n\n\ndef search(query: Optional[str] = None, page_size: int = 10,\n page_token: Optional[str] = None, shared_with_me: bool = False) -> dict:\n \"\"\"\n Search for files and folders in Google Drive.\n\n Supports:\n - Full-text search: \"quarterly report\"\n - Title search: \"title:budget\"\n - Google Drive URLs: extracts file/folder ID automatically\n - Folder ID: lists contents if query looks like an ID\n - Native query syntax: \"mimeType='application/pdf'\"\n \"\"\"\n q = None\n\n # Handle Google Drive URLs\n if query and is_drive_url(query):\n file_id, id_type = extract_id_from_url(query)\n\n if not file_id:\n return {\"error\": \"Could not extract file/folder ID from URL\"}\n\n # Determine if it's a folder\n if id_type == 'folder':\n q = f\"'{file_id}' in parents\"\n elif id_type == 'unknown':\n # Check the file type via API\n result = api_request(\"GET\", f\"files/{file_id}\", {\"fields\": \"mimeType\"})\n if isinstance(result, dict) and \"error\" not in result:\n if result.get(\"mimeType\") == \"application/vnd.google-apps.folder\":\n q = f\"'{file_id}' in parents\"\n else:\n # Return just this file\n file_result = api_request(\"GET\", f\"files/{file_id}\", {\n \"fields\": \"id, name, modifiedTime, viewedByMeTime, mimeType, parents\"\n })\n if isinstance(file_result, dict) and \"error\" not in file_result:\n return {\"files\": [file_result], \"nextPageToken\": None}\n return file_result\n else:\n # It's a file, get its details\n file_result = api_request(\"GET\", f\"files/{file_id}\", {\n \"fields\": \"id, name, modifiedTime, viewedByMeTime, mimeType, parents\"\n })\n if isinstance(file_result, dict) and \"error\" not in file_result:\n return {\"files\": [file_result], \"nextPageToken\": None}\n return file_result\n\n # Handle other query types\n elif query:\n query = query.strip()\n\n # Title search prefix\n if query.lower().startswith(\"title:\"):\n search_term = query[6:].strip()\n # Remove quotes if present\n if (search_term.startswith(\"'\") and search_term.endswith(\"'\")) or \\\n (search_term.startswith('\"') and search_term.endswith('\"')):\n search_term = search_term[1:-1]\n q = f\"name contains '{escape_query_string(search_term)}'\"\n\n # Check if it's a Drive ID (list folder contents)\n elif is_drive_id(query):\n q = f\"'{query}' in parents\"\n\n # Check if it's already a Drive query syntax\n elif re.search(r'( and | or | not | contains | in |=)', query):\n q = query\n\n # Default to full-text search\n else:\n q = f\"fullText contains '{escape_query_string(query)}'\"\n\n # Add sharedWithMe filter\n if shared_with_me:\n if q:\n q += \" and sharedWithMe\"\n else:\n q = \"sharedWithMe\"\n\n params = {\n \"pageSize\": page_size,\n \"fields\": \"nextPageToken, files(id, name, modifiedTime, viewedByMeTime, mimeType, parents)\"\n }\n\n if q:\n params[\"q\"] = q\n\n if page_token:\n params[\"pageToken\"] = page_token\n\n result = api_request(\"GET\", \"files\", params)\n\n if isinstance(result, dict) and \"error\" not in result:\n return {\n \"files\": result.get(\"files\", []),\n \"nextPageToken\": result.get(\"nextPageToken\")\n }\n\n return result\n\n\ndef find_folder(folder_name: str) -> dict:\n \"\"\"Find a folder by its exact name.\"\"\"\n escaped_name = escape_query_string(folder_name)\n q = f\"mimeType='application/vnd.google-apps.folder' and name = '{escaped_name}'\"\n\n params = {\n \"q\": q,\n \"fields\": \"files(id, name)\",\n \"spaces\": \"drive\"\n }\n\n result = api_request(\"GET\", \"files\", params)\n\n if isinstance(result, dict) and \"error\" not in result:\n return {\"folders\": result.get(\"files\", [])}\n\n return result\n\n\ndef list_files(folder_id: Optional[str] = None, page_size: int = 10,\n page_token: Optional[str] = None) -> dict:\n \"\"\"List files in a folder or root.\"\"\"\n params = {\n \"pageSize\": page_size,\n \"fields\": \"nextPageToken, files(id, name, modifiedTime, mimeType, parents)\"\n }\n\n if folder_id:\n params[\"q\"] = f\"'{folder_id}' in parents\"\n\n if page_token:\n params[\"pageToken\"] = page_token\n\n result = api_request(\"GET\", \"files\", params)\n\n if isinstance(result, dict) and \"error\" not in result:\n return {\n \"files\": result.get(\"files\", []),\n \"nextPageToken\": result.get(\"nextPageToken\")\n }\n\n return result\n\n\ndef download(file_id: str, local_path: str) -> dict:\n \"\"\"\n Download a file from Google Drive.\n\n Note: Google Workspace files (Docs, Sheets, Slides) cannot be downloaded\n directly. Use the appropriate export format or getText tools instead.\n \"\"\"\n # First get file metadata to check type\n metadata = api_request(\"GET\", f\"files/{file_id}\", {\"fields\": \"id, name, mimeType\"})\n\n if isinstance(metadata, dict) and \"error\" in metadata:\n return metadata\n\n mime_type = metadata.get(\"mimeType\", \"\")\n file_name = metadata.get(\"name\", \"unknown\")\n\n # Handle Google Workspace files\n google_workspace_types = {\n \"application/vnd.google-apps.document\": (\"Google Doc\", \"docs.getText\"),\n \"application/vnd.google-apps.spreadsheet\": (\"Google Sheet\", \"sheets.getText\"),\n \"application/vnd.google-apps.presentation\": (\"Google Slides\", \"slides.getText\"),\n }\n\n if mime_type in google_workspace_types:\n file_type, tool = google_workspace_types[mime_type]\n return {\n \"error\": f\"This is a {file_type}. Direct download not supported.\",\n \"suggestion\": f\"Use the '{tool}' tool with ID: {file_id}\",\n \"fileId\": file_id,\n \"fileName\": file_name\n }\n\n if mime_type.startswith(\"application/vnd.google-apps.\"):\n return {\n \"error\": f\"Google Workspace file type ({mime_type}) cannot be downloaded directly.\",\n \"suggestion\": \"Export the file manually or use specific tools.\",\n \"fileId\": file_id,\n \"fileName\": file_name\n }\n\n # Download the file\n file_content = api_request(\"GET\", f\"files/{file_id}\", {\"alt\": \"media\"}, stream=True)\n\n if isinstance(file_content, dict) and \"error\" in file_content:\n return file_content\n\n # Resolve path\n abs_path = os.path.abspath(os.path.expanduser(local_path))\n\n # Create directory if needed\n dir_path = os.path.dirname(abs_path)\n if dir_path:\n os.makedirs(dir_path, exist_ok=True)\n\n # Write file\n try:\n with open(abs_path, 'wb') as f:\n f.write(file_content)\n\n return {\n \"success\": True,\n \"message\": f\"Downloaded '{file_name}' to {abs_path}\",\n \"localPath\": abs_path,\n \"fileName\": file_name,\n \"fileId\": file_id\n }\n except IOError as e:\n return {\"error\": f\"Failed to write file: {e}\"}\n\n\ndef api_write_request(method: str, endpoint: str, data: Optional[dict] = None,\n params: Optional[dict] = None) -> dict:\n \"\"\"Make an authenticated write request (POST/PATCH) to the Google Drive API.\"\"\"\n token = get_valid_access_token()\n if not token:\n return {\"error\": \"Failed to get access token\"}\n\n url = f\"{DRIVE_API_BASE}/{endpoint}\"\n if params:\n url += \"?\" + urllib.parse.urlencode(params)\n\n headers = {\n \"Authorization\": f\"Bearer {token}\",\n \"Content-Type\": \"application/json\"\n }\n\n body = json.dumps(data).encode('utf-8') if data else None\n\n try:\n req = urllib.request.Request(url, data=body, headers=headers, method=method)\n with urllib.request.urlopen(req, timeout=60) as response:\n response_data = response.read().decode('utf-8')\n return json.loads(response_data) if response_data else {\"success\": True}\n except urllib.error.HTTPError as e:\n error_body = e.read().decode('utf-8') if e.fp else str(e)\n return {\"error\": f\"HTTP {e.code}: {error_body}\"}\n except urllib.error.URLError as e:\n return {\"error\": f\"Request failed: {e.reason}\"}\n except json.JSONDecodeError:\n return {\"success\": True}\n\n\ndef upload(local_path: str, folder_id: Optional[str] = None, name: Optional[str] = None) -> dict:\n \"\"\"\n Upload a file to Google Drive.\n\n Args:\n local_path: Path to the local file to upload\n folder_id: Target folder ID (uploads to root if not specified)\n name: Name for the file in Drive (uses local filename if not specified)\n \"\"\"\n import mimetypes\n\n abs_path = os.path.abspath(os.path.expanduser(local_path))\n\n if not os.path.exists(abs_path):\n return {\"error\": f\"File not found: {abs_path}\"}\n\n file_name = name or os.path.basename(abs_path)\n mime_type = mimetypes.guess_type(abs_path)[0] or \"application/octet-stream\"\n\n # Read file content\n try:\n with open(abs_path, 'rb') as f:\n file_content = f.read()\n except IOError as e:\n return {\"error\": f\"Failed to read file: {e}\"}\n\n # Build metadata\n metadata = {\"name\": file_name}\n if folder_id:\n metadata[\"parents\"] = [folder_id]\n\n # Use multipart upload\n token = get_valid_access_token()\n if not token:\n return {\"error\": \"Failed to get access token\"}\n\n boundary = \"skill_upload_boundary\"\n body = (\n f\"--{boundary}\\r\\n\"\n f\"Content-Type: application/json; charset=UTF-8\\r\\n\\r\\n\"\n f\"{json.dumps(metadata)}\\r\\n\"\n f\"--{boundary}\\r\\n\"\n f\"Content-Type: {mime_type}\\r\\n\\r\\n\"\n ).encode('utf-8') + file_content + f\"\\r\\n--{boundary}--\".encode('utf-8')\n\n url = \"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart\"\n headers = {\n \"Authorization\": f\"Bearer {token}\",\n \"Content-Type\": f\"multipart/related; boundary={boundary}\",\n }\n\n try:\n req = urllib.request.Request(url, data=body, headers=headers, method=\"POST\")\n with urllib.request.urlopen(req, timeout=120) as response:\n result = json.loads(response.read().decode('utf-8'))\n return {\n \"success\": True,\n \"fileId\": result.get(\"id\"),\n \"name\": result.get(\"name\"),\n \"mimeType\": result.get(\"mimeType\")\n }\n except urllib.error.HTTPError as e:\n error_body = e.read().decode('utf-8') if e.fp else str(e)\n return {\"error\": f\"HTTP {e.code}: {error_body}\"}\n except urllib.error.URLError as e:\n return {\"error\": f\"Upload failed: {e.reason}\"}\n\n\ndef create_folder(folder_name: str, parent_id: Optional[str] = None) -> dict:\n \"\"\"\n Create a new folder in Google Drive.\n\n Args:\n folder_name: Name of the new folder\n parent_id: Parent folder ID (creates in root if not specified)\n \"\"\"\n metadata = {\n \"name\": folder_name,\n \"mimeType\": \"application/vnd.google-apps.folder\"\n }\n if parent_id:\n metadata[\"parents\"] = [parent_id]\n\n return api_write_request(\"POST\", \"files\", data=metadata)\n\n\ndef move_file(file_id: str, new_parent_id: str) -> dict:\n \"\"\"\n Move a file to a different folder.\n\n Args:\n file_id: ID of the file to move\n new_parent_id: ID of the destination folder\n \"\"\"\n # Get current parents\n current = api_request(\"GET\", f\"files/{file_id}\", {\"fields\": \"parents\"})\n if isinstance(current, dict) and \"error\" in current:\n return current\n\n old_parents = \",\".join(current.get(\"parents\", []))\n\n params = {\n \"addParents\": new_parent_id,\n \"removeParents\": old_parents,\n \"fields\": \"id, name, parents\"\n }\n\n return api_write_request(\"PATCH\", f\"files/{file_id}\", params=params)\n\n\ndef copy_file(file_id: str, new_name: Optional[str] = None, folder_id: Optional[str] = None) -> dict:\n \"\"\"\n Copy a file.\n\n Args:\n file_id: ID of the file to copy\n new_name: Name for the copy (defaults to \"Copy of \u003coriginal>\")\n folder_id: Destination folder ID (same folder if not specified)\n \"\"\"\n data = {}\n if new_name:\n data[\"name\"] = new_name\n if folder_id:\n data[\"parents\"] = [folder_id]\n\n return api_write_request(\"POST\", f\"files/{file_id}/copy\", data=data)\n\n\ndef rename_file(file_id: str, new_name: str) -> dict:\n \"\"\"\n Rename a file or folder.\n\n Args:\n file_id: ID of the file/folder to rename\n new_name: New name\n \"\"\"\n return api_write_request(\"PATCH\", f\"files/{file_id}\", data={\"name\": new_name},\n params={\"fields\": \"id, name\"})\n\n\ndef trash_file(file_id: str) -> dict:\n \"\"\"\n Move a file or folder to trash.\n\n Args:\n file_id: ID of the file/folder to trash\n \"\"\"\n return api_write_request(\"PATCH\", f\"files/{file_id}\", data={\"trashed\": True},\n params={\"fields\": \"id, name, trashed\"})\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"Google Drive API operations\")\n subparsers = parser.add_subparsers(dest=\"command\", required=True)\n\n # search\n search_parser = subparsers.add_parser(\"search\", help=\"Search for files and folders\")\n search_parser.add_argument(\"query\", nargs=\"?\", help=\"Search query, URL, or folder ID\")\n search_parser.add_argument(\"--limit\", type=int, default=10, help=\"Max results (default: 10)\")\n search_parser.add_argument(\"--page-token\", help=\"Pagination token\")\n search_parser.add_argument(\"--shared-with-me\", action=\"store_true\", help=\"Only shared files\")\n\n # find-folder\n find_folder_parser = subparsers.add_parser(\"find-folder\", help=\"Find folder by name\")\n find_folder_parser.add_argument(\"name\", help=\"Folder name to find\")\n\n # list\n list_parser = subparsers.add_parser(\"list\", help=\"List files in a folder\")\n list_parser.add_argument(\"folder_id\", nargs=\"?\", help=\"Folder ID (root if not specified)\")\n list_parser.add_argument(\"--limit\", type=int, default=10, help=\"Max results (default: 10)\")\n list_parser.add_argument(\"--page-token\", help=\"Pagination token\")\n\n # download\n download_parser = subparsers.add_parser(\"download\", help=\"Download a file\")\n download_parser.add_argument(\"file_id\", help=\"File ID to download\")\n download_parser.add_argument(\"local_path\", help=\"Local path to save file\")\n\n # upload\n upload_parser = subparsers.add_parser(\"upload\", help=\"Upload a file to Drive\")\n upload_parser.add_argument(\"local_path\", help=\"Path to local file\")\n upload_parser.add_argument(\"--folder\", help=\"Target folder ID\")\n upload_parser.add_argument(\"--name\", help=\"Name for file in Drive (default: local filename)\")\n\n # create-folder\n create_folder_parser = subparsers.add_parser(\"create-folder\", help=\"Create a new folder\")\n create_folder_parser.add_argument(\"name\", help=\"Folder name\")\n create_folder_parser.add_argument(\"--parent\", help=\"Parent folder ID\")\n\n # move\n move_parser = subparsers.add_parser(\"move\", help=\"Move a file to another folder\")\n move_parser.add_argument(\"file_id\", help=\"File ID to move\")\n move_parser.add_argument(\"folder_id\", help=\"Destination folder ID\")\n\n # copy\n copy_parser = subparsers.add_parser(\"copy\", help=\"Copy a file\")\n copy_parser.add_argument(\"file_id\", help=\"File ID to copy\")\n copy_parser.add_argument(\"--name\", help=\"Name for the copy\")\n copy_parser.add_argument(\"--folder\", help=\"Destination folder ID\")\n\n # rename\n rename_parser = subparsers.add_parser(\"rename\", help=\"Rename a file or folder\")\n rename_parser.add_argument(\"file_id\", help=\"File/folder ID\")\n rename_parser.add_argument(\"new_name\", help=\"New name\")\n\n # trash\n trash_parser = subparsers.add_parser(\"trash\", help=\"Move a file to trash\")\n trash_parser.add_argument(\"file_id\", help=\"File/folder ID to trash\")\n\n args = parser.parse_args()\n\n if args.command == \"search\":\n result = search(args.query, args.limit, args.page_token, args.shared_with_me)\n elif args.command == \"find-folder\":\n result = find_folder(args.name)\n elif args.command == \"list\":\n result = list_files(args.folder_id, args.limit, args.page_token)\n elif args.command == \"download\":\n result = download(args.file_id, args.local_path)\n elif args.command == \"upload\":\n result = upload(args.local_path, args.folder, args.name)\n elif args.command == \"create-folder\":\n result = create_folder(args.name, args.parent)\n elif args.command == \"move\":\n result = move_file(args.file_id, args.folder_id)\n elif args.command == \"copy\":\n result = copy_file(args.file_id, args.name, args.folder)\n elif args.command == \"rename\":\n result = rename_file(args.file_id, args.new_name)\n elif args.command == \"trash\":\n result = trash_file(args.file_id)\n else:\n result = {\"error\": f\"Unknown command: {args.command}\"}\n\n print(json.dumps(result, indent=2))\n\n if isinstance(result, dict) and \"error\" in result:\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":20039,"content_sha256":"93d885de00cbdec1c9db088fdd0651e313cb7f52c933d81a53be39178e93d925"},{"filename":"scripts/requirements.txt","content":"keyring>=24.0.0\n","content_type":"text/plain; charset=utf-8","language":null,"size":16,"content_sha256":"2f694984e4915066821c2d53c6d95034061b2569a579fcc85ee80d78f630a689"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Google Drive","type":"text"}]},{"type":"paragraph","content":[{"text":"Lightweight Google Drive integration with standalone OAuth authentication. No MCP server required. Full read/write access.","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Requires Google Workspace account.","type":"text","marks":[{"type":"strong"}]},{"text":" Personal Gmail accounts are not supported.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"First-Time Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"Authenticate with Google (opens browser):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python scripts/auth.py login","type":"text"}]},{"type":"paragraph","content":[{"text":"Check authentication status:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python scripts/auth.py status","type":"text"}]},{"type":"paragraph","content":[{"text":"Logout when needed:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python scripts/auth.py logout","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Read Commands","type":"text"}]},{"type":"paragraph","content":[{"text":"All operations via ","type":"text"},{"text":"scripts/drive.py","type":"text","marks":[{"type":"code_inline"}]},{"text":". Auto-authenticates on first use if not logged in.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Search for files (full-text search)\npython scripts/drive.py search \"quarterly report\"\n\n# Search by title only\npython scripts/drive.py search \"title:budget\"\n\n# Search using Google Drive URL (extracts ID automatically)\npython scripts/drive.py search \"https://drive.google.com/drive/folders/1ABC123...\"\n\n# Search files shared with you\npython scripts/drive.py search --shared-with-me\n\n# Search with pagination\npython scripts/drive.py search \"report\" --limit 5 --page-token \"...\"\n\n# Find a folder by exact name\npython scripts/drive.py find-folder \"Project Documents\"\n\n# List files in root Drive\npython scripts/drive.py list\n\n# List files in a specific folder\npython scripts/drive.py list 1ABC123xyz --limit 20\n\n# Download a file\npython scripts/drive.py download 1ABC123xyz ./downloads/report.pdf","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Write Commands","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Upload a file to Drive root\npython scripts/drive.py upload ~/Documents/report.pdf\n\n# Upload to a specific folder\npython scripts/drive.py upload ~/Documents/report.pdf --folder 1ABC123xyz\n\n# Upload with a custom name\npython scripts/drive.py upload ~/Documents/report.pdf --name \"Q4 Report.pdf\"\n\n# Create a new folder\npython scripts/drive.py create-folder \"Project Documents\"\n\n# Create a folder inside another folder\npython scripts/drive.py create-folder \"Attachments\" --parent 1ABC123xyz\n\n# Move a file to a different folder\npython scripts/drive.py move FILE_ID DESTINATION_FOLDER_ID\n\n# Copy a file\npython scripts/drive.py copy FILE_ID\npython scripts/drive.py copy FILE_ID --name \"Report Copy\" --folder 1ABC123xyz\n\n# Rename a file or folder\npython scripts/drive.py rename FILE_ID \"New Name.pdf\"\n\n# Move a file to trash\npython scripts/drive.py trash FILE_ID","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Search Query Formats","type":"text"}]},{"type":"paragraph","content":[{"text":"The search command supports multiple query formats:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Format","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Example","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full-text","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"quarterly report\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Searches file contents and names","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Title","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"title:budget\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Searches file names only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"URL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://drive.google.com/...","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Extracts and uses file/folder ID","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Folder ID","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1ABC123...","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Lists folder contents (25+ char IDs)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Native query","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mimeType='application/pdf'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pass-through Drive query syntax","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"File ID Format","type":"text"}]},{"type":"paragraph","content":[{"text":"Google Drive uses long IDs like ","type":"text"},{"text":"1ABC123xyz_-abc123","type":"text","marks":[{"type":"code_inline"}]},{"text":". Get IDs from:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"search","type":"text","marks":[{"type":"code_inline"}]},{"text":" results","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"find-folder","type":"text","marks":[{"type":"code_inline"}]},{"text":" results","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"list","type":"text","marks":[{"type":"code_inline"}]},{"text":" results","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Google Drive URLs","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Download Limitations","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Regular files (PDFs, images, etc.) download directly","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Google Docs/Sheets/Slides cannot be downloaded via this tool","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For Google Workspace files, use export or dedicated tools","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Token Management","type":"text"}]},{"type":"paragraph","content":[{"text":"Tokens stored securely using the system keyring:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"macOS","type":"text","marks":[{"type":"strong"}]},{"text":": Keychain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Windows","type":"text","marks":[{"type":"strong"}]},{"text":": Windows Credential Locker","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linux","type":"text","marks":[{"type":"strong"}]},{"text":": Secret Service API (GNOME Keyring, KDE Wallet, etc.)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Service name: ","type":"text"},{"text":"google-drive-skill-oauth","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Automatically refreshes expired tokens using Google's cloud function.","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"google-drive","author":"@skillopedia","source":{"stars":450,"repo_name":"ai-skills","origin_url":"https://github.com/sanjay3290/ai-skills/blob/HEAD/skills/google-drive/SKILL.md","repo_owner":"sanjay3290","body_sha256":"44b81233c709757e4b18e16b797768160e87dd604a8b25dc4b10f6599f5f9022","cluster_key":"b0c9495ea2f3472215e0305b561b60fa54dd5001850118a54e289e11299b92ff","clean_bundle":{"format":"clean-skill-bundle-v1","source":"sanjay3290/ai-skills/skills/google-drive/SKILL.md","text_bytes":36187,"source_commit":"10cba52d57682430fe8430d70c86448270155b9a","skills_sh_rank":14490,"attachment_count":5,"text_attachments":5,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/google-drive/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"v1","category":"integrations-apis","metadata":{"author":"sanjay3290","version":"1.0"},"import_tag":"skills-sh-top20k-2026-11","description":"Interact with Google Drive - search files, find folders, list contents, download files, upload files,\ncreate folders, move, copy, rename, and trash files. Use when user asks to: search Google Drive,\nfind a file/folder, list Drive contents, download or upload files, create folders, move files,\nor organize Drive content. Lightweight integration with standalone OAuth authentication supporting\nfull read/write access.\n"}},"renderedAt":1793661810078}

Google Drive Lightweight Google Drive integration with standalone OAuth authentication. No MCP server required. Full read/write access. Requires Google Workspace account. Personal Gmail accounts are not supported. First-Time Setup Authenticate with Google (opens browser): Check authentication status: Logout when needed: Read Commands All operations via . Auto-authenticates on first use if not logged in. Write Commands Search Query Formats The search command supports multiple query formats: | Format | Example | Description | |--------|---------|-------------| | Full-text | | Searches file cont…