The Gas Town Discord Bot provides two-way communication between Gas Town AI agents and Discord channels. It consists of:
- MCP Server (Outbound) — Sends messages FROM agents TO Discord via the
send_discord_messagetool - Callback Poller (Inbound) — Polls Discord channels for human messages and forwards them to agents
Discord Channels Gas Town Agents
│ │
│ ◄── send_discord_message ──── │ (MCP Tool, outbound)
│ │
│ ──── discord-callback.py ──► │ (REST API polling, inbound)
│ │
The bot exposes a send_discord_message MCP tool. Any Gas Town agent with the MCP server configured can use it.
Tool Name: send_discord_message
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
channel_name |
string | Yes | Discord channel name (without #) |
message |
string | Yes | Message content to send |
rig_name |
string | No | Optional prefix, e.g. [villacalculatecost3] |
MCP Configuration (add to .mcp.json):
{
"mcpServers": {
"discord-bot": {
"command": "python3",
"args": ["/home/gastown/gt/discord_bot/crew/notify/bot.py"],
"env": {}
}
}
}Example usage from an AI agent:
Tool: send_discord_message
Arguments:
channel_name: "mayor-villa"
message: "Deployment complete. Site live at https://d1516tislgraf.cloudfront.net"
rig_name: "villacalculatecost3"
If you don't have the MCP server running, you can send messages directly via HTTP:
import json, urllib.request
TOKEN = "<BOT_TOKEN>"
CHANNEL_ID = "1467230977687552193" # mayor-villa
data = json.dumps({"content": "Hello from Gas Town!"}).encode()
req = urllib.request.Request(
f"https://discord.com/api/v10/channels/{CHANNEL_ID}/messages",
data=data,
headers={
"Authorization": f"Bot {TOKEN}",
"Content-Type": "application/json",
"User-Agent": "GasTownBot/1.0",
}
)
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read().decode())
print(f"Sent message ID: {result['id']}")The callback poller runs as a daemon, polling Discord channels every 15 seconds for new human messages. It forwards them via:
- Local inbox file (
/home/gastown/gt/daemon/discord-inbox.jsonl) — always written gt mail— best-effort delivery to the agent mailbox
Start the poller:
nohup python3 /home/gastown/gt/scripts/discord-callback.py \
>> /home/gastown/gt/daemon/discord-callback.log 2>&1 &Check status:
cat /home/gastown/gt/daemon/discord-callback.pid
ps aux | grep discord-callbackRead incoming messages:
# Read the local inbox
cat /home/gastown/gt/daemon/discord-inbox.jsonl
# Each line is JSON:
# {"timestamp": "2026-01-31T19:22:22", "subject": "Discord #mayor-villa from nic", "body": "message text"}The poller monitors channels whose names start with mayor-:
| Channel | ID | Purpose |
|---|---|---|
mayor-villa |
1467230977687552193 |
Villa Market project comms |
mayor-discordbot |
1467230979759407135 |
Discord bot project comms |
To add more channels, edit the CHANNELS dict in discord-callback.py.
Channel mappings are in /home/gastown/gt/settings/discord-channels.json:
{
"vgt": 1467106918538678293,
"villacalculatecost3": {
"vi-status": 1467230898599624903,
"vi-deploys": 1467230900696781024,
"vi-bugs": 1467230902764572775,
"mayor-villa": 1467230977687552193,
"vi-beads": 1467234682264490098
},
"discord_bot": {
"db-status": 1467230905985798395,
"mayor-discordbot": 1467230979759407135
}
}mayor-*— Human-to-agent communication channels (monitored by callback poller)vi-*— Villa Market project channels (status, deploys, bugs, beads)db-*— Discord bot project channels
The bot token is stored at:
/home/gastown/gt/discord_bot/crew/notify/.env(for the MCP server)- Hardcoded in
/home/gastown/gt/scripts/discord-callback.py(for the poller)
Bot User ID: 1467022847812698223
To create a new Discord channel programmatically:
import json, urllib.request
TOKEN = "<BOT_TOKEN>"
GUILD_ID = "<GUILD_ID>"
CATEGORY_ID = "<PARENT_CATEGORY_ID>" # optional
data = json.dumps({
"name": "new-channel-name",
"type": 0, # 0 = text channel
"parent_id": CATEGORY_ID,
}).encode()
req = urllib.request.Request(
f"https://discord.com/api/v10/guilds/{GUILD_ID}/channels",
data=data,
headers={
"Authorization": f"Bot {TOKEN}",
"Content-Type": "application/json",
}
)
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read().decode())
print(f"Created #{result['name']} (ID: {result['id']})")After creating, add the channel ID to /home/gastown/gt/settings/discord-channels.json.
| File | Purpose |
|---|---|
/home/gastown/gt/discord_bot/crew/notify/bot.py |
MCP server + Discord client |
/home/gastown/gt/discord_bot/crew/notify/config.py |
Bot configuration |
/home/gastown/gt/discord_bot/crew/notify/.env |
Bot token |
/home/gastown/gt/scripts/discord-callback.py |
Inbound message poller |
/home/gastown/gt/daemon/discord-callback.log |
Poller log output |
/home/gastown/gt/daemon/discord-callback.pid |
Poller PID file |
/home/gastown/gt/daemon/discord-inbox.jsonl |
Inbound message inbox |
/home/gastown/gt/settings/discord-channels.json |
Channel ID mappings |
- Add the MCP server to your
.mcp.json(see Section 1) - Use
send_discord_messagetool to post to any channel - To receive messages, read
/home/gastown/gt/daemon/discord-inbox.jsonl - Check
/home/gastown/gt/settings/discord-channels.jsonfor channel IDs - For direct REST API access, use the bot token from
.env