import type { MicrosoftGraphDriveItem, OneDriveListResponse, OneDriveToolParams, } from '@/tools/onedrive/types' import { escapeODataStringLiteral } from '@/tools/onedrive/utils' import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils' import type { ToolConfig } from '@/tools/types' export const listTool: ToolConfig = { id: 'onedrive_list', name: 'List OneDrive Files', description: 'List files and folders in OneDrive', version: '1.0', oauth: { required: true, provider: 'onedrive', }, params: { accessToken: { type: 'string', required: true, visibility: 'hidden', description: 'The access token for the OneDrive API', }, folderId: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Folder ID to list files from (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")', }, query: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Filter files by name prefix (e.g., "report", "invoice_2024")', }, pageSize: { type: 'number', required: false, visibility: 'user-or-llm', description: 'Maximum number of files to return (e.g., 10, 50, 100)', }, pageToken: { type: 'string', required: false, visibility: 'user-or-llm', description: "Continuation URL from a previous response's nextPageToken, used to fetch the next page", }, }, request: { url: (params) => { const pageToken = params.pageToken?.trim() if (pageToken) { return assertGraphNextPageUrl(pageToken) } // Use specific folder if provided, otherwise use root const folderId = params.folderId?.trim() const encodedFolderId = folderId ? encodeURIComponent(folderId) : '' const baseUrl = encodedFolderId ? `https://graph.microsoft.com/v1.0/me/drive/items/${encodedFolderId}/children` : 'https://graph.microsoft.com/v1.0/me/drive/root/children' const url = new URL(baseUrl) // Use Microsoft Graph $select parameter url.searchParams.append( '$select', 'id,name,file,folder,webUrl,size,createdDateTime,lastModifiedDateTime,parentReference,@microsoft.graph.downloadUrl' ) // Add name filter if query provided if (params.query) { url.searchParams.append( '$filter', `startswith(name,'${escapeODataStringLiteral(params.query)}')` ) } // Add pagination if (params.pageSize) { url.searchParams.append('$top', Number(params.pageSize).toString()) } return url.toString() }, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, }), }, transformResponse: async (response: Response) => { const data = await response.json() return { success: true, output: { files: data.value.map((item: MicrosoftGraphDriveItem) => ({ id: item.id, name: item.name, mimeType: item.file?.mimeType || (item.folder ? 'application/folder' : 'unknown'), webViewLink: item.webUrl, webContentLink: item['@microsoft.graph.downloadUrl'], size: item.size?.toString() || '0', createdTime: item.createdDateTime, modifiedTime: item.lastModifiedDateTime, parents: item.parentReference ? [item.parentReference.id] : [], })), // Use the actual @odata.nextLink URL as the continuation token nextPageToken: getGraphNextPageUrl(data), }, } }, outputs: { success: { type: 'boolean', description: 'Whether files were listed successfully' }, files: { type: 'array', description: 'Array of file and folder objects with metadata' }, nextPageToken: { type: 'string', description: 'Token for retrieving the next page of results (optional)', }, }, }