Skip to content

Commit 189c12d

Browse files
authored
feat(mcp): native Gmail adapter — §6.5 profile over GA REST as stdio MCP server (#1449)
* feat(mcp): native Gmail adapter — six-tool §6.5 profile over Gmail REST as stdio MCP server Capability Plugin per ADR §5.3/§6.1 and rollout step 5, motivated by live validation (PR #1448): hosted Gmail MCP gates tools/call behind Workspace Developer Preview enrollment and its terms bar pre-GA shipping, while the same OAuth token works against GA Gmail REST. - crates/openab-mcp/src/native/gmail.rs: search_threads / get_thread / get_message / list_labels / list_drafts / create_draft with names and argument shapes mirroring the hosted server (GA cut-over = config-only); drafts-only write surface; header-injection-hardened MIME builder; Gmail-id validation before path use. - Own OAuth login (PKCE S256 paste-back) sending access_type=offline + prompt=consent — stores a refresh token (the rmcp-driven flow cannot), single-flight refresh with rotation-tolerant persistence via the shared auth.json McpCredentialStore under the gmail-native key. - Served over stdio (openab-agent gmail-native serve|login): registered in mcp.json as a normal stdio server, so tool_filter, schema validation, breaker, timeouts, and redaction apply unchanged. * style: cargo fmt + fix injection-test assertion (address guard trips on control char first) * style: final rustfmt * refactor(gmail-native): serve over loopback Streamable HTTP instead of stdio Same transport and trust model as the OAB MCP Facade (require_loopback reused; non-loopback binds refused; no auth layer — host boundary is the trust boundary). mcp.json registration becomes a "type": "http" entry at http://127.0.0.1:8850/mcp. Drops the rmcp transport-io feature. * docs: gmail-native operator guide (setup, registration, security notes) * refactor: move gmail-native serve|login to the broker binary openab-agent is the coding agent (ACP client + MCP client toolchain); long-running serving surfaces belong to the broker binary, which every bot image already ships — the b2 validation required smuggling the agent binary into the container precisely because the adapter lived on the wrong binary. openab-agent keeps the client-side mcp CLI only. * style: fmt + doc reference fix * refactor(cli): nest under `openab mcp <addon> <action>` namespace Maintainer direction: MCP add-ons get one namespace (openab mcp gmail-native serve|login); long-running serving remains config-driven ([mcp] + openab run). Standalone/dev serving and interactive logins are the namespace's job. * style: fmt --------- Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
1 parent 3149c1d commit 189c12d

6 files changed

Lines changed: 1176 additions & 1 deletion

File tree

crates/openab-mcp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ pub mod acp;
2424
pub mod auth;
2525
pub mod llm;
2626
pub mod mcp;
27+
pub mod native;

crates/openab-mcp/src/mcp/facade.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ impl ServerHandler for McpFacade {
316316
/// Reject any bind address that is not loopback (ADR §6.2: the facade must
317317
/// never listen on a non-loopback interface — it has no authentication
318318
/// layer; the host boundary is the trust boundary).
319-
fn require_loopback(addr: &str) -> Result<std::net::SocketAddr> {
319+
pub(crate) fn require_loopback(addr: &str) -> Result<std::net::SocketAddr> {
320320
let sock: std::net::SocketAddr = addr
321321
.parse()
322322
.with_context(|| format!("invalid listen address {addr:?} (expected ip:port)"))?;

0 commit comments

Comments
 (0)