Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
[workspace]
resolver = "2"
members = ["crates/agentmail", "crates/agentmail-mcp"]

[workspace.dependencies]
agentmail = { path = "crates/agentmail" }
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4", features = ["derive"] }
dirs = "6"
futures = "0.3"
hashbrown = { version = "0.16", features = ["rayon", "serde"] }
reqwest = { version = "0.13", features = ["json"] }
rmcp = "1.2"
schemars = { version = "1.2", features = ["chrono04"] }
secret-lib = { version = "1", features = ["keyring", "rustls", "tokio"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4"] }
async-imap = { version = "0.11", default-features = false }
fast_html2md = "0.0"
lettre = { version = "0.11", default-features = false }
mail-parser = "0.11"
native-tls = "0.2"
tokio-native-tls = "0.3"
toml = "1.1"
28 changes: 10 additions & 18 deletions crates/agentmail-mcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,13 @@ name = "agentmail"
path = "src/main.rs"

[dependencies]
agentmail = { path = "../agentmail" }
clap = { version = "4", features = ["derive"] }
rmcp = { version = "1.2", default-features = true, features = [
"transport-async-rw",
"transport-worker",
] }
schemars = "1.2"
secret-lib = { version = "1", features = ["keyring", "rustls", "tokio"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = [
"io-std",
"macros",
"rt",
"rt-multi-thread"
] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4"] }
agentmail.workspace = true
clap.workspace = true
rmcp = { workspace = true, features = ["transport-async-rw", "transport-worker"] }
schemars.workspace = true
secret-lib.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["io-std", "macros", "rt", "rt-multi-thread"] }
tracing-subscriber.workspace = true
uuid.workspace = true
54 changes: 20 additions & 34 deletions crates/agentmail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,35 @@ repository = "https://github.com/weekendsuperhero-io/agentmail"
documentation = "https://docs.rs/agentmail"
homepage = "https://github.com/weekendsuperhero-io/agentmail"
readme = "../../README.md"
keywords = ["email", "imap", "mail", "mcp", "agent"]
keywords = ["agent", "email", "imap", "mail", "mcp"]
categories = ["email", "network-programming"]
authors = ["WeekendSuperhero <hello@weekendsuperhero.io>"]
rust-version = "1.94"

[dependencies]
# IMAP protocol (tokio runtime, no async-std)
async-imap = { version = "0.11", default-features = false, features = [
"runtime-tokio"
] }
# Date/time handling
chrono = { version = "0.4", features = ["serde"] }
# Directory paths
dirs = "6"
async-imap = { workspace = true, features = ["runtime-tokio"] }
chrono.workspace = true
dirs.workspace = true
# HTML to markdown conversion
fast_html2md = "0.0"
# Async utilities
futures = "0.3"
fast_html2md = { workspace = true }
futures.workspace = true
hashbrown.workspace = true
# RFC822 message composition (for drafts)
lettre = { version = "0.11", default-features = false, features = ["builder"] }
lettre = { workspace = true, features = ["builder"] }
# Email parsing from raw RFC822
mail-parser = "0.11"
native-tls = "0.2"
# HTTP client (for RFC 8058 one-click unsubscribe)
reqwest = { version = "0.13", features = ["json"] }
mail-parser = { workspace = true }
native-tls = { workspace = true }
reqwest.workspace = true
schemars.workspace = true
# Credential storage (raw, shell command, or system keyring)
secret-lib = { version = "1", features = [
"command",
"derive",
"keyring",
"rustls",
"tokio"
] }
schemars = { version = "1.2", features = ["chrono04"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Error handling
thiserror = "2"
# Async runtime
tokio = { version = "1", features = ["fs", "net", "rt", "sync", "time"] }
secret-lib = { workspace = true, features = ["command", "derive"] }
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio.workspace = true
# TLS for IMAP connections (tokio-native)
tokio-native-tls = "0.3"
tokio-native-tls = { workspace = true }
# Configuration file parsing
toml = "1.1"
# Logging
tracing = "0.1"
toml = { workspace = true }
tracing.workspace = true
2 changes: 1 addition & 1 deletion crates/agentmail/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use hashbrown::HashMap;
use secret::Secret;
use serde::Deserialize;
use std::collections::HashMap;
use std::path::PathBuf;

/// Top-level configuration file.
Expand Down
2 changes: 1 addition & 1 deletion crates/agentmail/src/connection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use hashbrown::HashMap;
use std::sync::Arc;
use tokio::sync::{Mutex, OwnedSemaphorePermit, Semaphore};

Expand Down
4 changes: 2 additions & 2 deletions crates/agentmail/src/imap_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1005,8 +1005,8 @@ pub async fn fetch_flags(
let uids_raw = imap_timeout(session.uid_search("ALL")).await?;
let uids: Vec<u32> = uids_raw.into_iter().collect();
let total = uids.len() as u64;
let mut flag_counts: std::collections::HashMap<String, u32> = std::collections::HashMap::new();
let mut color_counts: std::collections::HashMap<String, u32> = std::collections::HashMap::new();
let mut flag_counts: hashbrown::HashMap<String, u32> = hashbrown::HashMap::new();
let mut color_counts: hashbrown::HashMap<String, u32> = hashbrown::HashMap::new();
let mut completed = 0u64;

for chunk in uids.chunks(1000) {
Expand Down
6 changes: 3 additions & 3 deletions crates/agentmail/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl Agentmail {
None => list_scannable_mailbox_names(session.session()).await?,
};

use std::collections::HashMap;
use hashbrown::HashMap;
// Key by (email, display_name) so "Find My <noreply@apple.com>" and
// "iCloud <noreply@apple.com>" are separate entries.
let mut map: HashMap<(String, String), SenderSummary> = HashMap::new();
Expand Down Expand Up @@ -379,7 +379,7 @@ impl Agentmail {
None => list_scannable_mailbox_names(session.session()).await?,
};

use std::collections::HashMap;
use hashbrown::HashMap;
use types::ListSummary;

// Key by (email, display_name) for exact sender grouping
Expand Down Expand Up @@ -712,7 +712,7 @@ impl Agentmail {
None => list_scannable_mailbox_names(session.session()).await?,
};

use std::collections::HashMap;
use hashbrown::HashMap;
let mut total_flags: HashMap<String, u32> = HashMap::new();
let mut total_colors: HashMap<String, u32> = HashMap::new();
let mut per_mailbox = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion crates/agentmail/src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use hashbrown::HashMap;

use mail_parser::{MessageParser, MimeHeaders};

Expand Down
3 changes: 2 additions & 1 deletion crates/agentmail/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use hashbrown::HashMap;

use chrono::{DateTime, Utc};
use schemars::JsonSchema;
Expand Down Expand Up @@ -95,6 +95,7 @@ pub struct MessageInfo {

// All headers (raw original values)
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
#[schemars(with = "std::collections::HashMap<String, Vec<String>>")]
pub headers: HashMap<String, Vec<String>>,
}

Expand Down