download_resource
Download Moodle course materials like PPTs and PDFs from a URL to your local directory. Specify a custom folder path or use the default download location.
Instructions
Download Moodle resource file(s) (PPT, PDF, etc.) from a URL. Supports saving to a custom local directory.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| resource_url | Yes | The exact Moodle resource URL to download from. | |
| download_path | No | The absolute folder path string (e.g. '/Users/cosmos/Desktop'). IMPORTANT: This must be a STRING value, NOT a boolean. WRONG: true, false. RIGHT: '/path/to/folder'. If not provided, it uses the default system download folder. |
Implementation Reference
- MUSTerClient.py:483-603 (handler)Core handler function in MUSTerClient that implements the download logic: ensures login, loads the resource page with Selenium, finds Moodle pluginfile download links, transfers session cookies to requests library, downloads files to the specified path (default ~/Downloads), skips existing files, returns detailed JSON with downloaded/skipped files and errors.def download_resource(self, resource_url: str, download_path: Optional[str] = None) -> Dict[str, Any]: """ Download resource(s) from Moodle using the authenticated session.""" resolved_download_path = download_path or DOWNLOAD_DIR self._ensure_driver() if not self.logged_in: if not self.login(): raise Exception("Login required to download resources.") self.heartBeat() try: # Navigate to the resource page to get the actual download URL self.driver.get(resource_url) # Wait for the page to load and find download links WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, "a[onclick*='target='], a[href*='pluginfile.php']")) ) # Find all direct download links download_links = self.driver.find_elements(By.CSS_SELECTOR, "a[onclick*='target='], a[href*='pluginfile.php']") if not download_links: return {"error": "No download links found on the resource page"} # Get cookies from Selenium session for requests cookies = {} for cookie in self.driver.get_cookies(): cookies[cookie['name']] = cookie['value'] self.heartBeat() # Download headers headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' } self.heartBeat() downloaded_files = [] skipped_files = [] errors = [] self.heartBeat() # Create download directory Path(resolved_download_path).mkdir(parents=True, exist_ok=True) self.heartBeat() for i, link in enumerate(download_links): try: download_url = link.get_attribute('href') if not download_url or not download_url.startswith('http'): continue self.heartBeat() # Skip non-file links if 'pluginfile.php' not in download_url: continue self.heartBeat() # Extract filename from URL or link text filename = None try: # Try to get filename from the link text first link_text = link.text.strip() if link_text and '.' in link_text and len(link_text) < 200: filename = link_text else: # Extract from URL parsed_url = urllib.parse.urlparse(download_url) path_parts = parsed_url.path.split('/') for part in reversed(path_parts): if '.' in part and len(part) < 200: filename = urllib.parse.unquote(part) break except Exception: pass self.heartBeat() if not filename: filename = f"download_{int(time.time())}_{i}" # Skip if file exists file_path = Path(resolved_download_path) / filename if file_path.exists(): skipped_files.append(filename) continue # Download the file response = requests.get(download_url, cookies=cookies, headers=headers, stream=True) response.raise_for_status() # Write file with open(file_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) file_size = file_path.stat().st_size downloaded_files.append({ "filename": file_path.name, "file_path": str(file_path), "file_size": file_size, "download_url": download_url }) except requests.exceptions.RequestException as e: errors.append(f"Failed to download {download_url}: {str(e)}") except Exception as e: errors.append(f"Unexpected error downloading file {i+1}: {str(e)}") if downloaded_files or skipped_files: result = { "success": True, "total_downloaded": len(downloaded_files), "total_skipped": len(skipped_files), "downloaded_files": downloaded_files, "skipped_files": skipped_files, } if errors: result["errors"] = errors return result else: return {"error": f"No files were downloaded. Errors: {'; '.join(errors)}"} except Exception as e: return {"error": f"Unexpected error during download: {str(e)}"}
- main.py:53-66 (schema)JSON Schema defining the input parameters for the download_resource tool: required resource_url (string), optional download_path (string or null).inputSchema={ "type": "object", "properties": { "resource_url": { "type": "string", "description": "The exact Moodle resource URL to download from.", }, "download_path": { "type": ["string", "null"], "description": "The absolute folder path string (e.g. '/Users/cosmos/Desktop'). \nIMPORTANT: This must be a STRING value, NOT a boolean. \nWRONG: true, false. \nRIGHT: '/path/to/folder'. \nIf not provided, it uses the default system download folder.", }, }, "required": ["resource_url"], },
- main.py:50-67 (registration)Tool registration in list_muster_tools(): defines name, description, and input schema for the MCP server.list_tools().Tool( name="download_resource", description="Download Moodle resource file(s) (PPT, PDF, etc.) from a URL. Supports saving to a custom local directory.", inputSchema={ "type": "object", "properties": { "resource_url": { "type": "string", "description": "The exact Moodle resource URL to download from.", }, "download_path": { "type": ["string", "null"], "description": "The absolute folder path string (e.g. '/Users/cosmos/Desktop'). \nIMPORTANT: This must be a STRING value, NOT a boolean. \nWRONG: true, false. \nRIGHT: '/path/to/folder'. \nIf not provided, it uses the default system download folder.", }, }, "required": ["resource_url"], }, ),
- main.py:154-158 (handler)Thin wrapper handler in main.py that calls the core MUSTerClient.download_resource and handles exceptions.def tool_download_resource(resource_url: str, download_path: Optional[str] = None) -> Dict[str, Any]: try: return muster_client.download_resource(resource_url, download_path) except Exception as e: return {"error": f"Failed to download resource: {str(e)}"}
- main.py:213-214 (registration)Dispatch registration in @server.call_tool(): handles the tool call by invoking the wrapper function and wrapping result as TextContent.if name == "download_resource": return _wrap_json(tool_download_resource(args["resource_url"], args.get("download_path")))