mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 23:50:17 +00:00
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]>
193 lines
6.7 KiB
Rust
193 lines
6.7 KiB
Rust
//! Gmail WASM Tool for IronClaw.
|
|
//!
|
|
//! Provides Gmail integration for reading, searching, sending, drafting,
|
|
//! and replying to emails.
|
|
//!
|
|
//! # Capabilities Required
|
|
//!
|
|
//! - HTTP: `gmail.googleapis.com/gmail/v1/*` (GET, POST, DELETE)
|
|
//! - Secrets: `google_oauth_token` (shared OAuth 2.0 token, injected automatically)
|
|
//!
|
|
//! # Supported Actions
|
|
//!
|
|
//! - `list_messages`: List/search messages with Gmail query syntax
|
|
//! - `get_message`: Get a specific message with full content
|
|
//! - `send_message`: Send a new email
|
|
//! - `create_draft`: Create a draft email
|
|
//! - `reply_to_message`: Reply to an existing message (or reply-all)
|
|
//! - `trash_message`: Move a message to trash
|
|
//!
|
|
//! # Example Usage
|
|
//!
|
|
//! ```json
|
|
//! {"action": "list_messages", "query": "is:unread from:[email protected]", "max_results": 5}
|
|
//! ```
|
|
|
|
mod api;
|
|
mod types;
|
|
|
|
use types::GmailAction;
|
|
|
|
wit_bindgen::generate!({
|
|
world: "sandboxed-tool",
|
|
path: "../../wit/tool.wit",
|
|
});
|
|
|
|
struct GmailTool;
|
|
|
|
impl exports::near::agent::tool::Guest for GmailTool {
|
|
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_messages", "get_message", "send_message", "create_draft", "reply_to_message", "trash_message"],
|
|
"description": "The Gmail operation to perform"
|
|
},
|
|
"query": {
|
|
"type": "string",
|
|
"description": "Gmail search query (same syntax as Gmail search box, e.g., 'is:unread', 'from:[email protected]'). Used by: list_messages"
|
|
},
|
|
"max_results": {
|
|
"type": "integer",
|
|
"description": "Maximum number of messages to return (default: 20). Used by: list_messages",
|
|
"default": 20
|
|
},
|
|
"label_ids": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "Label IDs to filter by (e.g., 'INBOX', 'SENT', 'DRAFT'). Used by: list_messages"
|
|
},
|
|
"message_id": {
|
|
"type": "string",
|
|
"description": "Message ID. Required for: get_message, reply_to_message, trash_message"
|
|
},
|
|
"to": {
|
|
"type": "string",
|
|
"description": "Recipient email address(es), comma-separated. Required for: send_message, create_draft"
|
|
},
|
|
"subject": {
|
|
"type": "string",
|
|
"description": "Email subject. Required for: send_message, create_draft"
|
|
},
|
|
"body": {
|
|
"type": "string",
|
|
"description": "Email body (plain text). Required for: send_message, create_draft, reply_to_message"
|
|
},
|
|
"cc": {
|
|
"type": "string",
|
|
"description": "CC recipients, comma-separated. Used by: send_message, create_draft"
|
|
},
|
|
"bcc": {
|
|
"type": "string",
|
|
"description": "BCC recipients, comma-separated. Used by: send_message, create_draft"
|
|
},
|
|
"reply_all": {
|
|
"type": "boolean",
|
|
"description": "If true, reply to all recipients (default: false). Used by: reply_to_message",
|
|
"default": false
|
|
}
|
|
}
|
|
}"#
|
|
.to_string()
|
|
}
|
|
|
|
fn description() -> String {
|
|
"Gmail integration for reading, searching, sending, drafting, and replying to emails. \
|
|
Supports Gmail search query syntax (is:unread, from:, subject:, after:, etc.). \
|
|
Requires a Google OAuth token with gmail.modify and gmail.compose scopes. \
|
|
To discover all available API operations, use http GET to fetch \
|
|
<https://www.googleapis.com/discovery/v1/apis/gmail/v1/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 gmail` to set up \
|
|
OAuth, or set the GOOGLE_OAUTH_TOKEN environment variable."
|
|
.to_string(),
|
|
);
|
|
}
|
|
|
|
let action: GmailAction =
|
|
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 Gmail action: {:?}", action),
|
|
);
|
|
|
|
let result = match action {
|
|
GmailAction::ListMessages {
|
|
query,
|
|
max_results,
|
|
label_ids,
|
|
} => {
|
|
let result = api::list_messages(query.as_deref(), max_results, &label_ids)?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
|
|
GmailAction::GetMessage { message_id } => {
|
|
let result = api::get_message(&message_id)?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
|
|
GmailAction::SendMessage {
|
|
to,
|
|
subject,
|
|
body,
|
|
cc,
|
|
bcc,
|
|
} => {
|
|
let result = api::send_message(&to, &subject, &body, cc.as_deref(), bcc.as_deref())?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
|
|
GmailAction::CreateDraft {
|
|
to,
|
|
subject,
|
|
body,
|
|
cc,
|
|
bcc,
|
|
} => {
|
|
let result = api::create_draft(&to, &subject, &body, cc.as_deref(), bcc.as_deref())?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
|
|
GmailAction::ReplyToMessage {
|
|
message_id,
|
|
body,
|
|
reply_all,
|
|
} => {
|
|
let result = api::reply_to_message(&message_id, &body, reply_all)?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
|
|
GmailAction::TrashMessage { message_id } => {
|
|
let result = api::trash_message(&message_id)?;
|
|
serde_json::to_string(&result).map_err(|e| e.to_string())?
|
|
}
|
|
};
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
export!(GmailTool);
|