feat(google_drive): set read/write scope on all commands to use the same token (#1707)

This commit is contained in:
Kalvin C
2025-03-17 12:32:49 -07:00
committed by GitHub
parent ea0960f645
commit 952e0d22c0
+231 -330
View File
@@ -36,6 +36,8 @@ pub const KEYCHAIN_SERVICE: &str = "mcp_google_drive";
pub const KEYCHAIN_USERNAME: &str = "oauth_credentials"; pub const KEYCHAIN_USERNAME: &str = "oauth_credentials";
pub const KEYCHAIN_DISK_FALLBACK_ENV: &str = "GOOGLE_DRIVE_DISK_FALLBACK"; pub const KEYCHAIN_DISK_FALLBACK_ENV: &str = "GOOGLE_DRIVE_DISK_FALLBACK";
const GOOGLE_DRIVE_SCOPES: Scope = Scope::Full;
#[derive(Debug)] #[derive(Debug)]
enum FileOperation { enum FileOperation {
Create { name: String }, Create { name: String },
@@ -238,11 +240,11 @@ impl GoogleDriveRouter {
"type": "string", "type": "string",
"description": "Path to the file to upload. Mutually exclusive with body.", "description": "Path to the file to upload. Mutually exclusive with body.",
}, },
"parent_id": { "parentId": {
"type": "string", "type": "string",
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')", "description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
}, },
"allow_shared_drives": { "allowSharedDrives": {
"type": "boolean", "type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)", "description": "Whether to allow access to shared drives or just your personal drive (default: false)",
} }
@@ -251,10 +253,10 @@ impl GoogleDriveRouter {
}), }),
); );
let create_doc_tool = Tool::new( let create_file_tool = Tool::new(
"create_doc".to_string(), "create_file".to_string(),
indoc! {r#" indoc! {r#"
Create a Google Doc from markdown text in Google Drive. Create a Google file (Document, Spreadsheet, or Slides) in Google Drive.
"#} "#}
.to_string(), .to_string(),
json!({ json!({
@@ -264,80 +266,29 @@ impl GoogleDriveRouter {
"type": "string", "type": "string",
"description": "Name of the file to create", "description": "Name of the file to create",
}, },
"fileType": {
"type": "string",
"enum": ["document", "spreadsheet", "slides"],
"description": "Type of Google file to create (document, spreadsheet, or slides)",
},
"body": { "body": {
"type": "string", "type": "string",
"description": "Markdown text of the file to create.", "description": "Text content for the file (required for document and spreadsheet types)",
},
"parent_id": {
"type": "string",
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
},
"allow_shared_drives": {
"type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
}
},
"required": ["name", "body"],
}),
);
let create_sheets_tool = Tool::new(
"create_sheets".to_string(),
indoc! {r#"
Create a Google Sheets document from csv text in Google Drive.
"#}
.to_string(),
json!({
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the file to create",
},
"body": {
"type": "string",
"description": "CSV text of the file to create.",
},
"parent_id": {
"type": "string",
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
},
"allow_shared_drives": {
"type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
}
},
"required": ["name", "body"],
}),
);
let create_slides_tool = Tool::new(
"create_slides".to_string(),
indoc! {r#"
Create a Google Slides document in Google Drive by converting a PowerPoint file.
"#}
.to_string(),
json!({
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the file to create",
}, },
"path": { "path": {
"type": "string", "type": "string",
"description": "Path to a PowerPoint file to upload.", "description": "Path to a file to upload (required for slides type)",
}, },
"parent_id": { "parentId": {
"type": "string", "type": "string",
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')", "description": "ID of the parent folder in which to create the file (default: creates files in the root of 'My Drive')",
}, },
"allow_shared_drives": { "allowSharedDrives": {
"type": "boolean", "type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)", "description": "Whether to allow access to shared drives or just your personal drive (default: false)",
} }
}, },
"required": ["name", "path"], "required": ["name", "fileType"],
}), }),
); );
@@ -366,7 +317,7 @@ impl GoogleDriveRouter {
"type": "string", "type": "string",
"description": "Path to a local file to use to update the Google Drive file. Mutually exclusive with body.", "description": "Path to a local file to use to update the Google Drive file. Mutually exclusive with body.",
}, },
"allow_shared_drives": { "allowSharedDrives": {
"type": "boolean", "type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)", "description": "Whether to allow access to shared drives or just your personal drive (default: false)",
} }
@@ -375,10 +326,10 @@ impl GoogleDriveRouter {
}), }),
); );
let update_doc_tool = Tool::new( let update_file_tool = Tool::new(
"update_doc".to_string(), "update_file".to_string(),
indoc! {r#" indoc! {r#"
Update a Google Doc from markdown text. Update a Google file (Document, Spreadsheet, or Slides) in Google Drive.
"#} "#}
.to_string(), .to_string(),
json!({ json!({
@@ -388,68 +339,25 @@ impl GoogleDriveRouter {
"type": "string", "type": "string",
"description": "ID of the file to update", "description": "ID of the file to update",
}, },
"fileType": {
"type": "string",
"enum": ["document", "spreadsheet", "slides"],
"description": "Type of Google file to update (document, spreadsheet, or slides)",
},
"body": { "body": {
"type": "string", "type": "string",
"description": "Complete markdown text of the file to update.", "description": "Text content for the file (required for document and spreadsheet types)",
},
"allow_shared_drives": {
"type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
}
},
"required": ["fileId", "body"],
}),
);
let update_sheets_tool = Tool::new(
"update_sheets".to_string(),
indoc! {r#"
Update a Google Sheets document from csv text.
"#}
.to_string(),
json!({
"type": "object",
"properties": {
"fileId": {
"type": "string",
"description": "ID of the file to update",
},
"body": {
"type": "string",
"description": "Complete CSV text of the updated file.",
},
"allow_shared_drives": {
"type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
}
},
"required": ["fileId", "body"],
}),
);
let update_slides_tool = Tool::new(
"update_slides".to_string(),
indoc! {r#"
Updatea Google Slides document in Google Drive by converting a PowerPoint file.
"#}
.to_string(),
json!({
"type": "object",
"properties": {
"fileId": {
"type": "string",
"description": "ID of the file to update",
}, },
"path": { "path": {
"type": "string", "type": "string",
"description": "Path to a PowerPoint file to upload to replace the existing file.", "description": "Path to a file to upload (required for slides type)",
}, },
"allow_shared_drives": { "allowSharedDrives": {
"type": "boolean", "type": "boolean",
"description": "Whether to allow access to shared drives or just your personal drive (default: false)", "description": "Whether to allow access to shared drives or just your personal drive (default: false)",
} }
}, },
"required": ["fileId", "path"], "required": ["fileId", "fileType"],
}), }),
); );
@@ -488,8 +396,8 @@ impl GoogleDriveRouter {
}), }),
); );
let comment_list_tool = Tool::new( let list_comments_tool = Tool::new(
"comment_list".to_string(), "list_comments".to_string(),
indoc! {r#" indoc! {r#"
List comments for a file in google drive by id, given an input file id. List comments for a file in google drive by id, given an input file id.
"#} "#}
@@ -542,6 +450,19 @@ impl GoogleDriveRouter {
- get_columns: Get column headers from a specific sheet - get_columns: Get column headers from a specific sheet
- get_values: Get values from a range - get_values: Get values from a range
### 4. Create File Tool
Create Google Workspace files (Docs, Sheets, or Slides) directly in Google Drive.
- For Google Docs: Converts Markdown text to a Google Document
- For Google Sheets: Converts CSV text to a Google Spreadsheet
- For Google Slides: Converts a PowerPoint file to Google Slides (requires a path to the powerpoint file)
### 5. Update File Tool
Update existing Google Workspace files (Docs, Sheets, or Slides) in Google Drive.
- For Google Docs: Updates with new Markdown text
- For Google Sheets: Updates with new CSV text
- For Google Slides: Updates with a new PowerPoint file (requires a path to the powerpoint file)
- Note: This functionally is an overwrite to the slides, warn the user before using this tool.
Parameters: Parameters:
- spreadsheetId: The ID of the spreadsheet (can be obtained from search results) - spreadsheetId: The ID of the spreadsheet (can be obtained from search results)
- operation: The operation to perform (one of the operations listed above) - operation: The operation to perform (one of the operations listed above)
@@ -583,15 +504,11 @@ impl GoogleDriveRouter {
search_tool, search_tool,
read_tool, read_tool,
upload_tool, upload_tool,
create_doc_tool, create_file_tool,
create_sheets_tool,
create_slides_tool,
update_tool, update_tool,
update_doc_tool, update_file_tool,
update_sheets_tool,
update_slides_tool,
sheets_tool, sheets_tool,
comment_list_tool, list_comments_tool,
], ],
instructions, instructions,
drive, drive,
@@ -659,7 +576,7 @@ impl GoogleDriveRouter {
.supports_all_drives(true) .supports_all_drives(true)
.include_items_from_all_drives(true) .include_items_from_all_drives(true)
.clear_scopes() // Scope::MeetReadonly is the default, remove it .clear_scopes() // Scope::MeetReadonly is the default, remove it
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -698,7 +615,7 @@ impl GoogleDriveRouter {
.param("fields", "mimeType") .param("fields", "mimeType")
.supports_all_drives(true) .supports_all_drives(true)
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await .await
.map_err(|e| { .map_err(|e| {
@@ -736,7 +653,7 @@ impl GoogleDriveRouter {
.export(uri, export_mime_type) .export(uri, export_mime_type)
.param("alt", "media") .param("alt", "media")
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -783,7 +700,7 @@ impl GoogleDriveRouter {
.get(uri) .get(uri)
.param("alt", "media") .param("alt", "media")
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -878,7 +795,7 @@ impl GoogleDriveRouter {
.spreadsheets() .spreadsheets()
.get(spreadsheet_id) .get(spreadsheet_id)
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -925,7 +842,7 @@ impl GoogleDriveRouter {
.spreadsheets() .spreadsheets()
.values_get(spreadsheet_id, &sheet_name) .values_get(spreadsheet_id, &sheet_name)
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -967,7 +884,7 @@ impl GoogleDriveRouter {
.spreadsheets() .spreadsheets()
.values_get(spreadsheet_id, range) .values_get(spreadsheet_id, range)
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -1032,7 +949,7 @@ impl GoogleDriveRouter {
.supports_all_drives(true) .supports_all_drives(true)
.include_items_from_all_drives(true) .include_items_from_all_drives(true)
.clear_scopes() // Scope::MeetReadonly is the default, remove it .clear_scopes() // Scope::MeetReadonly is the default, remove it
.add_scope(Scope::Readonly); .add_scope(GOOGLE_DRIVE_SCOPES);
// add a next token if we have one // add a next token if we have one
if let Some(token) = next_page_token { if let Some(token) = next_page_token {
@@ -1080,18 +997,24 @@ impl GoogleDriveRouter {
mime_type: Some(target_mime_type.to_string()), mime_type: Some(target_mime_type.to_string()),
..Default::default() ..Default::default()
}; };
if let Some(p) = parent {
req.parents = Some(vec![p.to_string()]);
}
let builder = self.drive.files(); let builder = self.drive.files();
let result = match operation { let result = match operation {
FileOperation::Create { ref name } => { FileOperation::Create { ref name } => {
req.name = Some(name.to_string()); req.name = Some(name.to_string());
// we only accept parent_id from create tool calls
if let Some(p) = parent {
req.parents = Some(vec![p.to_string()]);
}
builder builder
.create(req) .create(req)
.use_content_as_indexable_text(true) .use_content_as_indexable_text(true)
.supports_all_drives(support_all_drives) .supports_all_drives(support_all_drives)
.clear_scopes()
.add_scope(GOOGLE_DRIVE_SCOPES)
.upload(content, source_mime_type.parse().unwrap()) .upload(content, source_mime_type.parse().unwrap())
.await .await
} }
@@ -1099,11 +1022,14 @@ impl GoogleDriveRouter {
builder builder
.update(req, file_id) .update(req, file_id)
.use_content_as_indexable_text(true) .use_content_as_indexable_text(true)
.clear_scopes()
.add_scope(GOOGLE_DRIVE_SCOPES)
.supports_all_drives(support_all_drives) .supports_all_drives(support_all_drives)
.upload(content, source_mime_type.parse().unwrap()) .upload(content, source_mime_type.parse().unwrap())
.await .await
} }
}; };
match result { match result {
Err(e) => Err(ToolError::ExecutionError(format!( Err(e) => Err(ToolError::ExecutionError(format!(
"Failed to upload google drive file {:?}, {}.", "Failed to upload google drive file {:?}, {}.",
@@ -1126,6 +1052,7 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The name param is required".to_string(), "The name param is required".to_string(),
))?; ))?;
let mime_type = let mime_type =
params params
.get("mimeType") .get("mimeType")
@@ -1133,8 +1060,10 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The mimeType param is required".to_string(), "The mimeType param is required".to_string(),
))?; ))?;
let body = params.get("body").and_then(|q| q.as_str()); let body = params.get("body").and_then(|q| q.as_str());
let path = params.get("path").and_then(|q| q.as_str()); let path = params.get("path").and_then(|q| q.as_str());
let reader: Box<dyn ReadSeek> = match (body, path) { let reader: Box<dyn ReadSeek> = match (body, path) {
(None, None) | (Some(_), Some(_)) => { (None, None) | (Some(_), Some(_)) => {
return Err(ToolError::InvalidParameters( return Err(ToolError::InvalidParameters(
@@ -1146,11 +1075,14 @@ impl GoogleDriveRouter {
ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string()) ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string())
})?), })?),
}; };
let parent = params.get("parent").and_then(|q| q.as_str());
let support_all_drives = params let parent_id = params.get("parentId").and_then(|q| q.as_str());
.get("supportAllDrives")
let allow_shared_drives = params
.get("allowSharedDrives")
.and_then(|q| q.as_bool()) .and_then(|q| q.as_bool())
.unwrap_or_default(); .unwrap_or_default();
self.upload_to_drive( self.upload_to_drive(
FileOperation::Create { FileOperation::Create {
name: filename.to_string(), name: filename.to_string(),
@@ -1158,13 +1090,14 @@ impl GoogleDriveRouter {
reader, reader,
mime_type, mime_type,
mime_type, mime_type,
parent, parent_id,
support_all_drives, allow_shared_drives,
) )
.await .await
} }
async fn create_doc(&self, params: Value) -> Result<Vec<Content>, ToolError> { async fn create_file(&self, params: Value) -> Result<Vec<Content>, ToolError> {
// Extract common parameters
let filename = let filename =
params params
.get("name") .get("name")
@@ -1172,105 +1105,88 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The name param is required".to_string(), "The name param is required".to_string(),
))?; ))?;
let body =
params
.get("body")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The body param is required".to_string(),
))?;
let source_mime_type = "text/markdown";
let target_mime_type = "application/vnd.google-apps.document";
let parent = params.get("parent").and_then(|q| q.as_str());
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool())
.unwrap_or_default();
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
self.upload_to_drive(
FileOperation::Create {
name: filename.to_string(),
},
cursor,
source_mime_type,
target_mime_type,
parent,
support_all_drives,
)
.await
}
async fn create_sheets(&self, params: Value) -> Result<Vec<Content>, ToolError> { let file_type =
let filename =
params params
.get("name") .get("fileType")
.and_then(|q| q.as_str()) .and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The name param is required".to_string(), "The fileType param is required".to_string(),
))?; ))?;
let body =
params
.get("body")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The body param is required".to_string(),
))?;
let source_mime_type = "text/csv";
let target_mime_type = "application/vnd.google-apps.spreadsheet";
let parent = params.get("parent").and_then(|q| q.as_str());
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool())
.unwrap_or_default();
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
self.upload_to_drive(
FileOperation::Create {
name: filename.to_string(),
},
cursor,
source_mime_type,
target_mime_type,
parent,
support_all_drives,
)
.await
}
async fn create_slides(&self, params: Value) -> Result<Vec<Content>, ToolError> { let parent_id = params.get("parentId").and_then(|q| q.as_str());
let filename =
params let allow_shared_drives = params
.get("name") .get("allowSharedDrives")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The name param is required".to_string(),
))?;
let path =
params
.get("path")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The path param is required".to_string(),
))?;
let reader = Box::new(std::fs::File::open(path).map_err(|e| {
ToolError::ExecutionError(format!("Error opening {}: {}", path, e).to_string())
})?);
let source_mime_type =
"application/vnd.openxmlformats-officedocument.presentationml.presentation";
let target_mime_type = "application/vnd.google-apps.presentation";
let parent = params.get("parent").and_then(|q| q.as_str());
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool()) .and_then(|q| q.as_bool())
.unwrap_or_default(); .unwrap_or_default();
// Determine source and target MIME types based on file_type
let (source_mime_type, target_mime_type, reader): (String, String, Box<dyn ReadSeek>) =
match file_type {
"document" => {
let body = params.get("body").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The body param is required for document file type".to_string(),
),
)?;
(
"text/markdown".to_string(),
"application/vnd.google-apps.document".to_string(),
Box::new(Cursor::new(body.as_bytes().to_owned())),
)
}
"spreadsheet" => {
let body = params.get("body").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The body param is required for spreadsheet file type".to_string(),
),
)?;
(
"text/csv".to_string(),
"application/vnd.google-apps.spreadsheet".to_string(),
Box::new(Cursor::new(body.as_bytes().to_owned())),
)
}
"slides" => {
let path = params.get("path").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The path param is required for slides file type".to_string(),
),
)?;
let file = std::fs::File::open(path).map_err(|e| {
ToolError::ExecutionError(
format!("Error opening {}: {}", path, e).to_string(),
)
})?;
(
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
.to_string(),
"application/vnd.google-apps.presentation".to_string(),
Box::new(file),
)
}
_ => {
return Err(ToolError::InvalidParameters(format!(
"Invalid fileType: {}. Supported types are: document, spreadsheet, slides",
file_type
)))
}
};
// Upload the file to Google Drive
self.upload_to_drive( self.upload_to_drive(
FileOperation::Create { FileOperation::Create {
name: filename.to_string(), name: filename.to_string(),
}, },
reader, reader,
source_mime_type, &source_mime_type,
target_mime_type, &target_mime_type,
parent, parent_id,
support_all_drives, allow_shared_drives,
) )
.await .await
} }
@@ -1283,6 +1199,7 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The fileId param is required".to_string(), "The fileId param is required".to_string(),
))?; ))?;
let mime_type = let mime_type =
params params
.get("mimeType") .get("mimeType")
@@ -1290,8 +1207,10 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The mimeType param is required".to_string(), "The mimeType param is required".to_string(),
))?; ))?;
let body = params.get("body").and_then(|q| q.as_str()); let body = params.get("body").and_then(|q| q.as_str());
let path = params.get("path").and_then(|q| q.as_str()); let path = params.get("path").and_then(|q| q.as_str());
let reader: Box<dyn ReadSeek> = match (body, path) { let reader: Box<dyn ReadSeek> = match (body, path) {
(None, None) | (Some(_), Some(_)) => { (None, None) | (Some(_), Some(_)) => {
return Err(ToolError::InvalidParameters( return Err(ToolError::InvalidParameters(
@@ -1303,8 +1222,9 @@ impl GoogleDriveRouter {
ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string()) ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string())
})?), })?),
}; };
let support_all_drives = params
.get("supportAllDrives") let allow_shared_drives = params
.get("allowSharedDrives")
.and_then(|q| q.as_bool()) .and_then(|q| q.as_bool())
.unwrap_or_default(); .unwrap_or_default();
@@ -1316,12 +1236,13 @@ impl GoogleDriveRouter {
mime_type, mime_type,
mime_type, mime_type,
None, None,
support_all_drives, allow_shared_drives,
) )
.await .await
} }
async fn update_doc(&self, params: Value) -> Result<Vec<Content>, ToolError> { async fn update_file(&self, params: Value) -> Result<Vec<Content>, ToolError> {
// Extract common parameters
let file_id = let file_id =
params params
.get("fileId") .get("fileId")
@@ -1329,107 +1250,91 @@ impl GoogleDriveRouter {
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The fileId param is required".to_string(), "The fileId param is required".to_string(),
))?; ))?;
let body =
params
.get("body")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The body param is required".to_string(),
))?;
let source_mime_type = "text/markdown";
let target_mime_type = "application/vnd.google-apps.document";
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool())
.unwrap_or_default();
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
self.upload_to_drive(
FileOperation::Update {
file_id: file_id.to_string(),
},
cursor,
source_mime_type,
target_mime_type,
None,
support_all_drives,
)
.await
}
async fn update_sheets(&self, params: Value) -> Result<Vec<Content>, ToolError> { let file_type =
let file_id =
params params
.get("fileId") .get("fileType")
.and_then(|q| q.as_str()) .and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters( .ok_or(ToolError::InvalidParameters(
"The fileId param is required".to_string(), "The fileType param is required".to_string(),
))?; ))?;
let body =
params
.get("body")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The body param is required".to_string(),
))?;
let source_mime_type = "text/csv";
let target_mime_type = "application/vnd.google-apps.spreadsheet";
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool())
.unwrap_or_default();
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
self.upload_to_drive(
FileOperation::Update {
file_id: file_id.to_string(),
},
cursor,
source_mime_type,
target_mime_type,
None,
support_all_drives,
)
.await
}
async fn update_slides(&self, params: Value) -> Result<Vec<Content>, ToolError> { let allow_shared_drives = params
let file_id = .get("allowSharedDrives")
params
.get("fileId")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The fileId param is required".to_string(),
))?;
let path =
params
.get("path")
.and_then(|q| q.as_str())
.ok_or(ToolError::InvalidParameters(
"The path param is required".to_string(),
))?;
let reader = Box::new(std::fs::File::open(path).map_err(|e| {
ToolError::ExecutionError(format!("Error opening {}: {}", path, e).to_string())
})?);
let source_mime_type =
"application/vnd.openxmlformats-officedocument.presentationml.presentation";
let target_mime_type = "application/vnd.google-apps.presentation";
let support_all_drives = params
.get("supportAllDrives")
.and_then(|q| q.as_bool()) .and_then(|q| q.as_bool())
.unwrap_or_default(); .unwrap_or_default();
// Determine source and target MIME types based on file_type
let (source_mime_type, target_mime_type, reader): (String, String, Box<dyn ReadSeek>) =
match file_type {
"document" => {
let body = params.get("body").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The body param is required for document file type".to_string(),
),
)?;
(
"text/markdown".to_string(),
"application/vnd.google-apps.document".to_string(),
Box::new(Cursor::new(body.as_bytes().to_owned())),
)
}
"spreadsheet" => {
let body = params.get("body").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The body param is required for spreadsheet file type".to_string(),
),
)?;
(
"text/csv".to_string(),
"application/vnd.google-apps.spreadsheet".to_string(),
Box::new(Cursor::new(body.as_bytes().to_owned())),
)
}
"slides" => {
let path = params.get("path").and_then(|q| q.as_str()).ok_or(
ToolError::InvalidParameters(
"The path param is required for slides file type".to_string(),
),
)?;
let file = std::fs::File::open(path).map_err(|e| {
ToolError::ExecutionError(
format!("Error opening {}: {}", path, e).to_string(),
)
})?;
(
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
.to_string(),
"application/vnd.google-apps.presentation".to_string(),
Box::new(file),
)
}
_ => {
return Err(ToolError::InvalidParameters(format!(
"Invalid fileType: {}. Supported types are: document, spreadsheet, slides",
file_type
)))
}
};
// Upload the file to Google Drive
self.upload_to_drive( self.upload_to_drive(
FileOperation::Update { FileOperation::Update {
file_id: file_id.to_string(), file_id: file_id.to_string(),
}, },
reader, reader,
source_mime_type, &source_mime_type,
target_mime_type, &target_mime_type,
None, None,
support_all_drives, allow_shared_drives,
) )
.await .await
} }
async fn comment_list(&self, params: Value) -> Result<Vec<Content>, ToolError> { async fn list_comments(&self, params: Value) -> Result<Vec<Content>, ToolError> {
let file_id = let file_id =
params params
.get("fileId") .get("fileId")
@@ -1468,7 +1373,7 @@ impl GoogleDriveRouter {
"comments(author, content, createdTime, modifiedTime, id, anchor, resolved)", "comments(author, content, createdTime, modifiedTime, id, anchor, resolved)",
) )
.clear_scopes() .clear_scopes()
.add_scope(Scope::Readonly) .add_scope(GOOGLE_DRIVE_SCOPES)
.doit() .doit()
.await; .await;
@@ -1537,15 +1442,11 @@ impl Router for GoogleDriveRouter {
"search" => this.search(arguments).await, "search" => this.search(arguments).await,
"read" => this.read(arguments).await, "read" => this.read(arguments).await,
"upload" => this.upload(arguments).await, "upload" => this.upload(arguments).await,
"create_doc" => this.create_doc(arguments).await, "create_file" => this.create_file(arguments).await,
"create_sheets" => this.create_sheets(arguments).await,
"create_slides" => this.create_slides(arguments).await,
"update" => this.update(arguments).await, "update" => this.update(arguments).await,
"update_doc" => this.update_doc(arguments).await, "update_file" => this.update_file(arguments).await,
"update_sheets" => this.update_sheets(arguments).await,
"update_slides" => this.update_slides(arguments).await,
"sheets_tool" => this.sheets_tool(arguments).await, "sheets_tool" => this.sheets_tool(arguments).await,
"comment_list" => this.comment_list(arguments).await, "list_comments" => this.list_comments(arguments).await,
_ => Err(ToolError::NotFound(format!("Tool {} not found", tool_name))), _ => Err(ToolError::NotFound(format!("Tool {} not found", tool_name))),
} }
}) })