feat(google_drive): set read/write scope on all commands to use the same token (#1707)
This commit is contained in:
@@ -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))),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user