Skip to content

alexjmoore8/AgenticEmailBot

Repository files navigation

AgenticEmailBot

An AI-powered email assistant that polls an O365 shared mailbox via Microsoft Graph API, processes messages through AWS Bedrock AgentCore and the Strands agent framework, and sends HTML replies — all deployed on serverless AWS infrastructure with Terraform.

Features

  • O365 inbox polling: EventBridge-scheduled Lambda polls via Graph API delta query for efficient incremental sync
  • Idempotent processing: DynamoDB tracks processed emails and delta tokens to prevent duplicate replies
  • Bedrock AgentCore worker: Containerized Strands agent handles thread context, knowledge base retrieval, and reply generation
  • Email-safe HTML replies: Markdown responses converted to inline-styled HTML compatible with Outlook, Gmail, and Apple Mail
  • MCP tool integrations: GitHub, Atlassian, PagerDuty (via Gateway), Azure, AWS CLI, Splunk, and Atlan
  • File attachments and charts: Agent can generate downloadable files and charts as email attachments
  • Cross-platform memory: AgentCore Memory persists user preferences across sessions
  • Recipient filtering: Reply-all filtered to internal domains only — no accidental external replies
  • Audit logging: Optional CloudWatch audit trail with OpenTelemetry trace correlation
  • Bedrock Guardrails: Content safety guardrail on all model invocations

Architecture

EventBridge (schedule) → Poller Lambda → Invoker Lambda → AgentCore Worker
                              ↓                                   ↓
                         DynamoDB                          Microsoft Graph
                    (Delta Tokens +                       (Send Email Reply)
                    Processed Emails)
Component Purpose
Poller EventBridge-triggered Lambda; polls inbox via Graph API delta query; invokes Invoker per new email
Invoker Bridge Lambda; forwards email payload to AgentCore runtime
Worker Bedrock AgentCore container; builds thread context, runs Strands agent, sends reply
Mermaid architecture diagram
graph TD
    EB[EventBridge Schedule] --> PL[Poller Lambda]
    PL --> DDB1[(DynamoDB\nDelta Tokens)]
    PL --> DDB2[(DynamoDB\nProcessed Emails)]
    PL --> IL[Invoker Lambda]
    IL --> AC[AgentCore Runtime\nWorker Container]
    AC --> KB[Bedrock Knowledge Base]
    AC --> MEM[AgentCore Memory]
    AC --> GW[MCP Gateway\nPagerDuty etc.]
    AC --> GH[GitHub MCP]
    AC --> AZ[Azure MCP]
    AC --> AWS[AWS CLI MCP]
    AC --> SP[Splunk MCP]
    AC --> AT[Atlan MCP]
    AC --> GRAPH[Microsoft Graph API\nSend Reply]
Loading

Prerequisites

Azure AD App Registration (Email Access)

  1. Azure Portal → Azure Active Directory → App registrations → New registration
  2. Name: EmailBot-{env}
  3. API Permissions (Application, not Delegated):
    • Mail.Read, Mail.ReadWrite, Mail.Send
  4. Grant admin consent
  5. Create a client secret (Certificates & secrets → New client secret)

Record:

  • AZURE_TENANT_ID: Directory (tenant) ID
  • AZURE_CLIENT_ID: Application (client) ID
  • AZURE_CLIENT_SECRET: Client secret value

Azure AD App Registration (Azure MCP)

Separate registration for querying Azure resources via MCP.

  • Assign Reader role on subscriptions/resource groups the bot needs to query
  • Record: AZURE_MCP_CLIENT_ID, AZURE_MCP_CLIENT_SECRET (shares AZURE_TENANT_ID)

O365 Shared Mailbox

Create a shared mailbox in Microsoft 365 Admin Center. No license required.

Restrict mailbox access via PowerShell (recommended):

Connect-ExchangeOnline
New-DistributionGroup -Name "EmailBot-AllowedMailboxes" -Type Security
Add-DistributionGroupMember -Identity "EmailBot-AllowedMailboxes" -Member "bot@yourdomain.com"
New-ApplicationAccessPolicy `
    -AppId "{AZURE_CLIENT_ID}" `
    -PolicyScopeGroupId "EmailBot-AllowedMailboxes" `
    -AccessRight RestrictAccess `
    -Description "Restrict EmailBot to specific mailboxes"

AWS Secrets Manager

Create a secret containing all credentials:

{
  "AZURE_TENANT_ID": "your-tenant-id",
  "AZURE_CLIENT_ID": "your-client-id",
  "AZURE_CLIENT_SECRET": "your-client-secret",
  "AZURE_MCP_CLIENT_ID": "your-azure-mcp-client-id",
  "AZURE_MCP_CLIENT_SECRET": "your-azure-mcp-client-secret",
  "GITHUB_TOKEN": "ghp_your-github-token",
  "GATEWAY_CLIENT_SECRET": "your-gateway-client-secret",
  "ATLAN_API_KEY": "your-atlan-api-key",
  "ATLAN_BASE_URL": "https://your-org.atlan.com",
  "SPLUNK_TOKEN": "your-splunk-token"
}

Shared AWS Resources

The worker requires a Bedrock Knowledge Base, AgentCore Memory, Bedrock Guardrail, and an AgentCore Gateway (for PagerDuty and other MCP providers). These can be shared with other bots in your organization.

Deployment

1. Configure tfvars (copy from data/tf-aws-agentic-email-bot-example.tfvars):

environment           = "prod"
email_mailbox         = "bot@yourdomain.com"
secret_name           = "mybot/secrets/EMAILBOT_SECRETS_JSON"
knowledge_base_id     = "YOUR_KB_ID"
memory_id             = "YOUR_MEMORY_ID"
guardrails_id         = "YOUR_GUARDRAIL_ID"
gateway_url           = "https://YOUR_GATEWAY.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp"

2. Deploy infrastructure:

terraform init
terraform plan -var-file=data/tf-aws-agentic-email-bot-example.tfvars
terraform apply -var-file=data/tf-aws-agentic-email-bot-example.tfvars

3. Build and push worker container:

# Authenticate to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account>.dkr.ecr.us-east-1.amazonaws.com

# Build for ARM64 (Lambda architecture)
docker buildx build --platform linux/arm64 -t <ecr-url>:latest ./worker --push

Customization

System Prompt

Edit worker/src/worker_inputs.py to customize the bot's persona, tool instructions, and internal knowledge base guidance for your organization.

Internal Domain Filtering

Set the INTERNAL_EMAIL_DOMAINS environment variable (comma-separated) to control which email domains receive bot replies. Defaults to yourdomain.com.

MCP Integrations

Each MCP client in worker/src/worker_mcp_client_*.py fails silently if credentials are missing — disable integrations by simply omitting their credentials from the secret.

Project Structure

├── main.tf                   # Module composition
├── locals.tf                 # Naming conventions
├── variables.tf              # Input variables
├── data.tf                   # Data sources
├── secrets.tf                # Secrets Manager reference
├── ecr.tf                    # Worker container registry
├── dynamodb_delta.tf         # Delta tokens + processed emails
├── poller/                   # Poll inbox Lambda
│   └── src/poller.py
├── invoker/                  # AgentCore invoker Lambda
│   └── src/invoker.py
├── worker/                   # AgentCore container runtime
│   ├── Dockerfile
│   ├── requirements.txt
│   └── src/
│       ├── worker_agentcore.py       # BedrockAgentCoreApp entry point
│       ├── worker_conversation.py    # Thread context builder
│       ├── worker_email_graph.py     # Graph API client (OAuth, delta, reply-all)
│       ├── worker_agent.py           # Strands agent executor
│       ├── worker_inputs.py          # Config, system prompt, constants
│       ├── worker_knowledge_base.py  # Custom KB search tool
│       ├── worker_memory_tools.py    # Memory management tools
│       ├── worker_audit.py           # CloudWatch audit logging
│       ├── worker_token_cache.py     # Container-level token cache
│       └── worker_mcp_client_*.py    # MCP integrations
└── data/                     # Example tfvars

Monitoring

CloudWatch Log Groups:

  • Poller: /aws/lambda/{prefix}EmailPoller
  • Invoker: /aws/lambda/{prefix}EmailInvoker
  • Worker: /aws/bedrock-agentcore/... (AgentCore managed, includes OTEL traces)

Log Emoji Indicators:

  • 🟢 Success/completion
  • 🟡 Warning/info
  • 🔴 Error/failure
  • 🚮 Skipped/filtered
  • 🚫 Fatal error

About

AI email assistant powered by AWS Bedrock AgentCore and Strands — polls O365 via Microsoft Graph API, replies with AI-generated HTML emails

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors