Skip to content

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:

PlatformWhat You NeedTime to First Response
Microsoft TeamsAzure AD app, OAuth 2.0, admin consent, Graph API permissions2-5 days
SlackApp creation, OAuth flow, workspace installation, bot tokens1-3 days
DiscordBot account, OAuth2, intents, privileged gateway intents1-2 days
Google ChatGoogle Cloud project, service account, Chat API setup2-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:

Architecture Diagram
+------------------+ +-------------------+ +------------------+
| 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

Terminal
# macOS
brew install claude
# Or via npm
npm install -g @anthropic/claude-cli
# Verify
claude --version

If you already have Claude CLI installed, skip this. You probably also want to set up your API key:

Terminal
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.

BRAIN.md
# Messaging Agent Brain
## Platform: Microsoft Teams
URL: https://teams.microsoft.com/v2
## Login State
- Browser is already logged in
- Session persists between runs
## Detection Steps
1. Look for unread message indicators (bold text, notification badges)
2. Click on conversations with unread messages
3. 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.json

The 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:

claude-messenger.sh
#!/bin/bash
CONFIG_FILE="./BRAIN.md"
POLL_INTERVAL=120 # 2 minutes
LOG_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_INTERVAL
done

Make it executable:

Terminal
chmod +x claude-messenger.sh
./claude-messenger.sh

That’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):

com.user.claude-messenger.plist
<?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:

Terminal
launchctl load ~/Library/LaunchAgents/com.user.claude-messenger.plist

On Linux (using systemd):

claude-messenger.service
[Unit]
Description=Claude Messaging Agent
After=network.target
[Service]
Type=simple
User=your-username
ExecStart=/bin/bash /path/to/claude-messenger.sh
Restart=always
RestartSec=120
[Install]
WantedBy=multi-user.target

Enable it:

Terminal
sudo systemctl enable claude-messenger
sudo systemctl start claude-messenger

Platform-Specific Configuration

Each platform has different UI elements. Here’s what I learned after testing each one.

Microsoft Teams

BRAIN.md (Teams section)
## Platform Detection
- URL: https://teams.microsoft.com/v2
- Unread indicator: Bold conversation names
- Message container: [data-tid="messageBodyContent"]
## Interaction Steps
1. Wait for page load (3 seconds)
2. Find unread conversations in left sidebar
3. Click first unread conversation
4. Read last message in thread
5. Type response in message input
6. Press Enter to send

Teams was the easiest. The UI is consistent and the DOM selectors don’t change often.

Slack

BRAIN.md (Slack section)
## Platform Detection
- URL: https://app.slack.com/client/
- Unread indicator: Bold channel names, notification badges
- Message container: [data-qa="message_content"]
## Interaction Steps
1. Navigate to workspace URL
2. Look for channels with notification badges
3. Click channel to view messages
4. Read latest messages in thread
5. Type in message input box
6. Send with Ctrl+Enter

Slack was trickier because of workspace switching. I had to hardcode the workspace URL.

Discord

BRAIN.md (Discord section)
## Platform Detection
- URL: https://discord.com/channels/
- Unread indicator: White notification dots
- Message container: [class*="messageContent"]
## Interaction Steps
1. Wait for server list to load
2. Find channels with white notification dots
3. Click to enter channel
4. Read recent messages
5. Type in chat input at bottom
6. Press Enter to send

Discord’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:

Terminal
claude -p --chrome --user-data-dir="~/.config/claude-browser" --context BRAIN.md

Now 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:

BRAIN.md
## Skip These Senders
- GitHub Bot
- Calendar Bot
- Any message containing "[Bot]"
- System notifications

Response Loop (Claude Replying to Itself)

Claude saw its own messages as unread and responded to them. Bad. I added:

BRAIN.md
## Anti-Loop Rules
- Never respond to messages you sent
- Check sender name before responding
- If last message is from you, skip

Rate Limiting

I once accidentally polled every 10 seconds and got my account temporarily restricted. Now I use:

Rate limiting function
# Add to your script
MAX_RESPONSES_PER_HOUR=20
RESPONSE_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.

Terminal
# Add filesystem access
claude mcp install @modelcontextprotocol/server-filesystem ~/projects/
# Add web search
claude mcp install @modelcontextprotocol/server-web-search
# Add Google Calendar
claude mcp install @modelcontextprotocol/server-google-calendar

Then update your BRAIN.md:

BRAIN.md (MCP section)
## 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 Enhancement
When responding:
1. Use filesystem to check local code for context
2. Use web-search to find relevant documentation
3. Use google-calendar to verify meeting times

Now 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?

AspectBrowser AutomationAPI Integration
Setup time30 minutes2-5 days
Permissions neededNone (uses your session)Multiple approvals
Platform supportAny web platformPlatform-specific
MaintenanceLowHigh (API changes)
Rate limitsBrowser limits (generous)API limits (strict)
Offline supportNoYes (webhooks)
Multi-platformChange configSeparate integrations
ReliabilityDepends on UI stabilityDepends 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:

Terminal
claude -p --chrome --context BRAIN.md --verbose 2>&1 | tee debug.log

This shows you exactly what Claude sees and does on the page.

Common issues and fixes:

IssueSolution
Browser not logging inUse --user-data-dir for persistent profile
Claude not detecting messagesUpdate BRAIN.md with correct CSS selectors
Response too slowReduce POLL_INTERVAL, or use a faster model
Rate limited by platformIncrease POLL_INTERVAL, add randomization
Wrong contextAdd 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:

  1. Start with one platform (Teams is the most stable)
  2. Test in a test channel/group first—don’t go straight to production
  3. Monitor the logs for the first few hours
  4. Gradually expand to more conversations
  5. 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:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments