mailmolt
integration · crewai

CrewAI

Give each CrewAI role its own MailMolt agent so trust scores, governance, and per-role permission levels flow through automatically. One identity per role means a rate-limited or revoked researcher can't take the responder offline.

Install

pip install mailmolt crewai crewai-tools

Tool wrappers

import os
from crewai.tools import BaseTool
from mailmolt import MailMolt

mm = MailMolt(api_key=os.environ["MAILMOLT_API_KEY"])

class SendEmailTool(BaseTool):
    name: str = "send_email"
    description: str = "Send an email. Args: to (str), subject (str), text (str)."

    def _run(self, to: str, subject: str, text: str) -> str:
        r = mm.send_message(to=to, subject=subject, text=text)
        return f"ok: {r['id']}"

class SearchInboxTool(BaseTool):
    name: str = "search_inbox"
    description: str = "Semantic search the inbox. Args: query (str)."

    def _run(self, query: str) -> str:
        res = mm.search(query, limit=5)
        return "\n".join(f"- {m['subject']}" for m in res["data"])

Multi-agent crew

from crewai import Agent, Task, Crew, Process

researcher = Agent(
    role="Inbox researcher",
    goal="Find the highest-signal threads in the last 48h",
    tools=[SearchInboxTool()],
)
responder = Agent(
    role="Responder",
    goal="Draft replies to items flagged by the researcher",
    tools=[SendEmailTool()],
)

task = Task(
    description="Find all threads about 'Q2 OKRs' and reply with next steps.",
    agent=researcher,
    expected_output="List of threads handed to the responder",
)

crew = Crew(
    agents=[researcher, responder],
    tasks=[task],
    process=Process.sequential,
)
result = crew.kickoff()

Per-agent API keys

# One MailMolt identity per CrewAI role — trust + permissions scope correctly.
researcher_mm = MailMolt(api_key=os.environ["MM_RESEARCHER_KEY"])
responder_mm  = MailMolt(api_key=os.environ["MM_RESPONDER_KEY"])

class SearchInboxTool(BaseTool):
    name = "search_inbox"
    description = "Semantic search the inbox."
    def _run(self, query: str) -> str:
        return researcher_mm.search(query, limit=5)["data"]

class SendEmailTool(BaseTool):
    name = "send_email"
    description = "Send an email."
    def _run(self, to: str, subject: str, text: str) -> str:
        return responder_mm.send_message(to=to, subject=subject, text=text)["id"]

Inbound mail → crew

from fastapi import FastAPI, Request
api = FastAPI()

@api.post("/webhook")
async def hook(req: Request):
    evt = await req.json()
    if evt["event_type"] == "message.received":
        msg = evt["data"]["message"]
        # Run the crew with the inbound email as input
        crew.kickoff(inputs={"email": msg})
    return {"ok": True}

# Register once:
mm.create_webhook(
    url="https://your-host.com/webhook",
    event_types=["message.received"],
)

Notes

  • One MailMolt agent per CrewAI role. The trust score moves with the role, not the project.
  • Tool errors raise typed exceptions (RateLimitError, ServerError) — wrap with your own retry policy for bursty crews.
  • Attachments: pass [{filename, content, content_type}] with base64-encoded bytes.
  • Approval-gated agents queue sends in MailMolt's oversight UI before they leave the platform.

Full cookbook: docs/integrations/crewai.md · webhook reference