Files
optimclaw/tools-src/google-drive/src/lib.rs
T
2d332f12f0 feat(tools): add Google Discovery API URLs to WASM tool descriptions (#585)
Add Google Discovery Service URLs to all 6 Google WASM tool
descriptions so the LLM can fetch full API documentation on demand
using its built-in HTTP tool. Discovery API is public and requires
no authentication.

URLs added:
- Gmail: googleapis.com/discovery/v1/apis/gmail/v1/rest
- Calendar: calendar-json.googleapis.com/$discovery/rest?version=v3
- Drive: googleapis.com/discovery/v1/apis/drive/v3/rest
- Docs: googleapis.com/discovery/v1/apis/docs/v1/rest
- Sheets: googleapis.com/discovery/v1/apis/sheets/v4/rest
- Slides: googleapis.com/discovery/v1/apis/slides/v1/rest

[skip-regression-check]

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-05 19:20:29 -08:00

306 lines
12 KiB
Rust

//! Google Drive WASM Tool for IronClaw.
//!
//! Provides Google Drive integration for searching, accessing, uploading,
//! sharing, and organizing files and folders. Supports both personal and
//! shared (organizational) drives.
//!
//! # Capabilities Required
//!
//! - HTTP: `www.googleapis.com/drive/v3/*` and `www.googleapis.com/upload/drive/v3/*`
//! - Secrets: `google_oauth_token` (shared OAuth 2.0 token, injected automatically)
//!
//! # Supported Actions
//!
//! - `list_files`: Search/list files with Drive query syntax and corpora selection
//! - `get_file`: Get file metadata
//! - `download_file`: Download file content as text (exports Google Docs/Sheets)
//! - `upload_file`: Upload a text file (multipart)
//! - `update_file`: Rename, move, star, or update description
//! - `create_folder`: Create a new folder
//! - `delete_file`: Permanently delete a file
//! - `trash_file`: Move to trash
//! - `share_file`: Share with a user (reader, commenter, writer, organizer)
//! - `list_permissions`: See who has access
//! - `remove_permission`: Revoke access
//! - `list_shared_drives`: List organizational shared drives
//!
//! # Example Usage
//!
//! ```json
//! {"action": "list_files", "query": "name contains 'report' and mimeType = 'application/pdf'"}
//! {"action": "list_files", "corpora": "drive", "drive_id": "0ABcd...", "query": "trashed = false"}
//! {"action": "share_file", "file_id": "abc123", "email": "[email protected]", "role": "writer"}
//! ```
mod api;
mod types;
use types::GoogleDriveAction;
wit_bindgen::generate!({
world: "sandboxed-tool",
path: "../../wit/tool.wit",
});
struct GoogleDriveTool;
impl exports::near::agent::tool::Guest for GoogleDriveTool {
fn execute(req: exports::near::agent::tool::Request) -> exports::near::agent::tool::Response {
match execute_inner(&req.params) {
Ok(result) => exports::near::agent::tool::Response {
output: Some(result),
error: None,
},
Err(e) => exports::near::agent::tool::Response {
output: None,
error: Some(e),
},
}
}
fn schema() -> String {
r#"{
"type": "object",
"required": ["action"],
"properties": {
"action": {
"type": "string",
"enum": ["list_files", "get_file", "download_file", "upload_file", "update_file", "create_folder", "delete_file", "trash_file", "share_file", "list_permissions", "remove_permission", "list_shared_drives"],
"description": "The Google Drive operation to perform"
},
"file_id": {
"type": "string",
"description": "File ID. Required for: get_file, download_file, update_file, delete_file, trash_file, share_file, list_permissions, remove_permission"
},
"query": {
"type": "string",
"description": "Drive search query (e.g., \"name contains 'report'\", \"mimeType = 'application/pdf'\"). Used by: list_files"
},
"page_size": {
"type": "integer",
"description": "Max results (default: 25, max: 1000). Used by: list_files, list_shared_drives",
"default": 25
},
"order_by": {
"type": "string",
"description": "Sort order (e.g., 'modifiedTime desc', 'name'). Used by: list_files"
},
"corpora": {
"type": "string",
"enum": ["user", "drive", "domain", "allDrives"],
"description": "Search scope: 'user' (default), 'drive' (shared drive), 'domain', 'allDrives'. Used by: list_files",
"default": "user"
},
"drive_id": {
"type": "string",
"description": "Shared drive ID (required when corpora is 'drive'). Used by: list_files"
},
"page_token": {
"type": "string",
"description": "Token for next page of results. Used by: list_files"
},
"export_mime_type": {
"type": "string",
"description": "Export format for Google Workspace files (e.g., 'text/plain', 'text/csv'). Used by: download_file"
},
"name": {
"type": "string",
"description": "File/folder name. Required for: upload_file, create_folder. Optional for: update_file"
},
"content": {
"type": "string",
"description": "File content (text). Required for: upload_file"
},
"mime_type": {
"type": "string",
"description": "MIME type (default: 'text/plain'). Used by: upload_file",
"default": "text/plain"
},
"parent_id": {
"type": "string",
"description": "Parent folder ID (omit for root). Used by: upload_file, create_folder"
},
"description": {
"type": "string",
"description": "File/folder description. Used by: upload_file, update_file, create_folder"
},
"move_to_parent": {
"type": "string",
"description": "Move file to this folder ID. Used by: update_file"
},
"starred": {
"type": "boolean",
"description": "Star or unstar the file. Used by: update_file"
},
"email": {
"type": "string",
"description": "Recipient email address. Required for: share_file"
},
"role": {
"type": "string",
"enum": ["reader", "commenter", "writer", "organizer"],
"description": "Permission level (default: 'reader'). Used by: share_file",
"default": "reader"
},
"message": {
"type": "string",
"description": "Optional message in sharing notification. Used by: share_file"
},
"permission_id": {
"type": "string",
"description": "Permission ID to remove (from list_permissions). Required for: remove_permission"
}
}
}"#
.to_string()
}
fn description() -> String {
"Google Drive integration for searching, accessing, uploading, sharing, and organizing \
files and folders. Supports personal drives and shared (organizational) drives via the \
corpora parameter. Can search with Drive query syntax, download text files, upload new \
files, manage folder structure, and control sharing permissions. Requires a Google OAuth \
token with the drive scope. \
To discover all available API operations, use http GET to fetch \
<https://www.googleapis.com/discovery/v1/apis/drive/v3/rest> (public, no auth needed)."
.to_string()
}
}
fn execute_inner(params: &str) -> Result<String, String> {
if !crate::near::agent::host::secret_exists("google_oauth_token") {
return Err(
"Google OAuth token not configured. Run `ironclaw tool auth google-drive` to set up \
OAuth, or set the GOOGLE_OAUTH_TOKEN environment variable."
.to_string(),
);
}
let action: GoogleDriveAction =
serde_json::from_str(params).map_err(|e| format!("Invalid parameters: {}", e))?;
crate::near::agent::host::log(
crate::near::agent::host::LogLevel::Info,
&format!("Executing Google Drive action: {:?}", action),
);
let result = match action {
GoogleDriveAction::ListFiles {
query,
page_size,
order_by,
corpora,
drive_id,
page_token,
} => {
let result = api::list_files(
query.as_deref(),
page_size,
order_by.as_deref(),
&corpora,
drive_id.as_deref(),
page_token.as_deref(),
)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::GetFile { file_id } => {
let result = api::get_file(&file_id)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::DownloadFile {
file_id,
export_mime_type,
} => {
let result = api::download_file(&file_id, export_mime_type.as_deref())?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::UploadFile {
name,
content,
mime_type,
parent_id,
description,
} => {
let result = api::upload_file(
&name,
&content,
&mime_type,
parent_id.as_deref(),
description.as_deref(),
)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::UpdateFile {
file_id,
name,
description,
move_to_parent,
starred,
} => {
let result = api::update_file(
&file_id,
name.as_deref(),
description.as_deref(),
move_to_parent.as_deref(),
starred,
)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::CreateFolder {
name,
parent_id,
description,
} => {
let result = api::create_folder(&name, parent_id.as_deref(), description.as_deref())?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::DeleteFile { file_id } => {
let result = api::delete_file(&file_id)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::TrashFile { file_id } => {
let result = api::trash_file(&file_id)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::ShareFile {
file_id,
email,
role,
message,
} => {
let result = api::share_file(&file_id, &email, &role, message.as_deref())?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::ListPermissions { file_id } => {
let result = api::list_permissions(&file_id)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::RemovePermission {
file_id,
permission_id,
} => {
let result = api::remove_permission(&file_id, &permission_id)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
GoogleDriveAction::ListSharedDrives { page_size } => {
let result = api::list_shared_drives(page_size)?;
serde_json::to_string(&result).map_err(|e| e.to_string())?
}
};
Ok(result)
}
export!(GoogleDriveTool);