How to Build an AI Agent That Responds to Messaging Platforms
I spent three days trying to get Microsoft Teams API access. Azure AD registration, OAuth 2.0 flows, admin approvals, permission scopes—only to hit a corporate firewall. Then I tried Slack. Same story with different APIs. Discord? Yet another bot registration process.
There had to be a simpler way.
The Problem with Platform APIs
Every messaging platform wants you to use their API. They all claim it’s “simple” and “well-documented.” Here’s what they don’t tell you:
| Platform | What You Need | Time to First Response |
|---|---|---|
| Microsoft Teams | Azure AD app, OAuth 2.0, admin consent, Graph API permissions | 2-5 days |
| Slack | App creation, OAuth flow, workspace installation, bot tokens | 1-3 days |
| Discord | Bot account, OAuth2, intents, privileged gateway intents | 1-2 days |
| Google Chat | Google Cloud project, service account, Chat API setup | 2-4 days |
The real kicker? After all that setup, you’re locked into one platform. Want to add Slack support to your Teams bot? Start over.
I didn’t want to maintain four different integrations. I wanted one agent that worked everywhere.
The Browser Automation Approach
What if, instead of fighting APIs, I just automated the browser? I already have a logged-in session. Claude can see the page. It can click, read, type, and send—just like I would.
The architecture is stupidly simple:
+------------------+ +-------------------+ +------------------+| Claude CLI | | Chrome Browser | | Messaging || + BRAIN.md |<---->| (Automation) |<---->| Platform |+------------------+ +-------------------+ +------------------+ | | | | v v+------------------+ +------------------+| Local Repos | | Messages || (Context) | | (Read/Reply) |+------------------+ +------------------+No OAuth. No API keys. No webhook endpoints. Just a browser session and a polling script.
Step 1: Install Claude CLI
# macOSbrew install claude
# Or via npmnpm install -g @anthropic/claude-cli
# Verifyclaude --versionIf you already have Claude CLI installed, skip this. You probably also want to set up your API key:
export ANTHROPIC_API_KEY="your-key-here"Step 2: Create Your Agent’s Brain
The magic happens in a file called BRAIN.md. This tells Claude how to behave when it visits messaging platforms. Think of it as a configuration file mixed with a personality guide.
# Messaging Agent Brain
## Platform: Microsoft TeamsURL: https://teams.microsoft.com/v2
## Login State- Browser is already logged in- Session persists between runs
## Detection Steps1. Look for unread message indicators (bold text, notification badges)2. Click on conversations with unread messages3. Read the latest message content
## Response Guidelines- Be professional and concise- Acknowledge requests and provide actionable next steps- If technical question, reference local code repositories- If scheduling/meeting request, check calendar availability- If uncertain, ask clarifying questions
## Response Limits- Maximum 500 characters per response- Respond to maximum 3 messages per polling cycle- Skip messages from bots or automated systems
## Context Sources- Local repos: ~/projects/- Documentation: ~/docs/- Previous responses: ./message_history.jsonThe key insight: Claude reads this file every time it runs. You can update it without restarting anything.
Step 3: Build the Polling Script
Now you need something that runs Claude periodically. A simple shell script does the trick:
#!/bin/bash
CONFIG_FILE="./BRAIN.md"POLL_INTERVAL=120 # 2 minutesLOG_FILE="./messenger.log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"}
while true; do log "Starting polling cycle..."
# Run Claude with browser automation claude -p \ --chrome \ --context "$CONFIG_FILE" \ --message "Check for unread messages and respond according to BRAIN.md guidelines"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then log "Polling cycle completed successfully" else log "Polling cycle failed with exit code: $EXIT_CODE" fi
log "Sleeping for $POLL_INTERVAL seconds..." sleep $POLL_INTERVALdoneMake it executable:
chmod +x claude-messenger.sh./claude-messenger.shThat’s it. The script will now poll your messaging platform every 2 minutes, check for unread messages, and respond using Claude’s intelligence.
Step 4: Run as a Background Service
For this to be useful, it needs to run continuously in the background.
On macOS (using launchd):
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>com.user.claude-messenger</string> <key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>/path/to/claude-messenger.sh</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/tmp/claude-messenger.log</string> <key>StandardErrorPath</key> <string>/tmp/claude-messenger.error.log</string></dict></plist>Load it:
launchctl load ~/Library/LaunchAgents/com.user.claude-messenger.plistOn Linux (using systemd):
[Unit]Description=Claude Messaging AgentAfter=network.target
[Service]Type=simpleUser=your-usernameExecStart=/bin/bash /path/to/claude-messenger.shRestart=alwaysRestartSec=120
[Install]WantedBy=multi-user.targetEnable it:
sudo systemctl enable claude-messengersudo systemctl start claude-messengerPlatform-Specific Configuration
Each platform has different UI elements. Here’s what I learned after testing each one.
Microsoft Teams
## Platform Detection- URL: https://teams.microsoft.com/v2- Unread indicator: Bold conversation names- Message container: [data-tid="messageBodyContent"]
## Interaction Steps1. Wait for page load (3 seconds)2. Find unread conversations in left sidebar3. Click first unread conversation4. Read last message in thread5. Type response in message input6. Press Enter to sendTeams was the easiest. The UI is consistent and the DOM selectors don’t change often.
Slack
## Platform Detection- URL: https://app.slack.com/client/- Unread indicator: Bold channel names, notification badges- Message container: [data-qa="message_content"]
## Interaction Steps1. Navigate to workspace URL2. Look for channels with notification badges3. Click channel to view messages4. Read latest messages in thread5. Type in message input box6. Send with Ctrl+EnterSlack was trickier because of workspace switching. I had to hardcode the workspace URL.
Discord
## Platform Detection- URL: https://discord.com/channels/- Unread indicator: White notification dots- Message container: [class*="messageContent"]
## Interaction Steps1. Wait for server list to load2. Find channels with white notification dots3. Click to enter channel4. Read recent messages5. Type in chat input at bottom6. Press Enter to sendDiscord’s selectors are obfuscated (hence the [class*="..."] syntax), but the structure is predictable.
When It Goes Wrong
I hit several issues during testing. Here’s what broke and how I fixed it.
Browser Not Staying Logged In
The first run opened a fresh browser window with no session. I had to add a persistent profile:
claude -p --chrome --user-data-dir="~/.config/claude-browser" --context BRAIN.mdNow the browser remembers my login between runs.
Claude Responding to Bot Messages
At first, Claude was replying to automated messages from GitHub bots and calendar reminders. I added a filter:
## Skip These Senders- GitHub Bot- Calendar Bot- Any message containing "[Bot]"- System notificationsResponse Loop (Claude Replying to Itself)
Claude saw its own messages as unread and responded to them. Bad. I added:
## Anti-Loop Rules- Never respond to messages you sent- Check sender name before responding- If last message is from you, skipRate Limiting
I once accidentally polled every 10 seconds and got my account temporarily restricted. Now I use:
# Add to your scriptMAX_RESPONSES_PER_HOUR=20RESPONSE_COOLDOWN=300 # 5 minutes between responses to same user
check_rate_limit() { local user_id="$1" local last_response_file="./rate_limits/${user_id}.txt"
if [ -f "$last_response_file" ]; then local last_time=$(cat "$last_response_file") local current_time=$(date +%s) local diff=$((current_time - last_time))
if [ $diff -lt $RESPONSE_COOLDOWN ]; then return 1 # Rate limited fi fi
echo $(date +%s) > "$last_response_file" return 0}Enhancing with MCP Servers
Basic messaging is useful, but what if Claude could also check your calendar or search the web for answers? MCP (Model Context Protocol) servers make this possible.
# Add filesystem accessclaude mcp install @modelcontextprotocol/server-filesystem ~/projects/
# Add web searchclaude mcp install @modelcontextprotocol/server-web-search
# Add Google Calendarclaude mcp install @modelcontextprotocol/server-google-calendarThen update your BRAIN.md:
## Available MCP Tools- filesystem: Read/write local files and code- web-search: Search for documentation and solutions- google-calendar: Check availability for meeting requests
## Response EnhancementWhen responding:1. Use filesystem to check local code for context2. Use web-search to find relevant documentation3. Use google-calendar to verify meeting timesNow when someone asks “Can you review the API changes?” Claude can actually look at your local codebase.
Browser Automation vs. API: What’s the Trade-off?
| Aspect | Browser Automation | API Integration |
|---|---|---|
| Setup time | 30 minutes | 2-5 days |
| Permissions needed | None (uses your session) | Multiple approvals |
| Platform support | Any web platform | Platform-specific |
| Maintenance | Low | High (API changes) |
| Rate limits | Browser limits (generous) | API limits (strict) |
| Offline support | No | Yes (webhooks) |
| Multi-platform | Change config | Separate integrations |
| Reliability | Depends on UI stability | Depends on API stability |
For personal use, browser automation wins. For production systems serving thousands of users, you’d want the API approach.
Security Considerations
I learned this the hard way: this approach has full access to your messaging account. Here’s how to use it safely:
Do:
- Use a dedicated browser profile for automation
- Enable 2FA on all messaging accounts
- Store BRAIN.md securely (no credentials in it)
- Review response logs regularly
- Set response limits to prevent runaway behavior
Don’t:
- Don’t give Claude access to financial accounts
- Don’t enable auto-respond for critical business communications
- Don’t store message history containing sensitive data
- Don’t run this on shared machines
Debugging Tips
When something goes wrong, run with verbose logging:
claude -p --chrome --context BRAIN.md --verbose 2>&1 | tee debug.logThis shows you exactly what Claude sees and does on the page.
Common issues and fixes:
| Issue | Solution |
|---|---|
| Browser not logging in | Use --user-data-dir for persistent profile |
| Claude not detecting messages | Update BRAIN.md with correct CSS selectors |
| Response too slow | Reduce POLL_INTERVAL, or use a faster model |
| Rate limited by platform | Increase POLL_INTERVAL, add randomization |
| Wrong context | Add more specific instructions to BRAIN.md |
What I Built
In about 2 hours, I had a working AI messaging agent that:
- Responds to Teams, Slack, and Discord (just change the URL in BRAIN.md)
- References my local code repositories when answering technical questions
- Checks my calendar before accepting meeting requests
- Searches the web for documentation when needed
- Logs all activity for review
- Has rate limits to prevent spam
No API approvals. No OAuth flows. No separate integrations per platform.
Next Steps
If you want to try this:
- Start with one platform (Teams is the most stable)
- Test in a test channel/group first—don’t go straight to production
- Monitor the logs for the first few hours
- Gradually expand to more conversations
- Add MCP servers as you need more capabilities
The full code and example BRAIN.md files are in the example repository.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Claude CLI Documentation
- 👨💻 MCP Servers Repository
- 👨💻 Example Repository - Son of Claude
- 👨💻 Model Context Protocol
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments