Recipes
Discord bot
A Discord bot powered by AgentsKit. Replies in threads, calls tools, remembers per-channel.
A Discord bot that responds to mentions, holds per-channel memory, and can use tools like web search.
#Install
npm install @agentskit/runtime @agentskit/adapters @agentskit/tools @agentskit/memory discord.js#The bot
import { Client, GatewayIntentBits, Partials } from 'discord.js'
import { createRuntime } from '@agentskit/runtime'
import { anthropic } from '@agentskit/adapters'
import { webSearch } from '@agentskit/tools'
import { sqliteChatMemory } from '@agentskit/memory'
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
partials: [Partials.Channel],
})
// One memory per channel β keeps conversations separate
const runtimeFor = (channelId: string) =>
createRuntime({
adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }),
tools: [webSearch()],
memory: sqliteChatMemory({ path: `./data/${channelId}.db` }),
systemPrompt:
'You are a helpful Discord bot. Keep replies under 1500 characters. ' +
'Use web search when the user asks about anything time-sensitive.',
maxSteps: 6,
})
client.on('messageCreate', async (msg) => {
// Only respond when mentioned
if (msg.author.bot) return
if (!msg.mentions.has(client.user!)) return
const text = msg.content.replace(/<@!?\d+>/g, '').trim()
if (!text) return
await msg.channel.sendTyping()
try {
const result = await runtimeFor(msg.channelId).run(text)
await msg.reply(result.content.slice(0, 1900))
} catch (err) {
await msg.reply(`Sorry, something broke: ${(err as Error).message}`)
}
})
client.login(process.env.DISCORD_TOKEN!)#Run it
DISCORD_TOKEN=your-bot-token npx tsx bot.ts#Why per-channel runtimes
- Memory isolation β each channel has its own SQLite file
- Different system prompts per server become trivial later
- Cheap β
createRuntimeis config-only (per ADR 0006 RT1), no resources opened untilrun()
#Tighten the recipe
- Slash commands for explicit invocation instead of mentions
- Streaming via Discord message edits (chunk by chunk)
- Channel-scoped tools (e.g. an admin channel gets
shell(), public channels don't) - Cost guard β wrap
runtimewith an observer that aborts after $X. See Cost-guarded chat.
#Related
- Recipe: Persistent memory
- Concepts: Memory β why one ChatMemory per channel
Explore nearby
- PeerRecipes
Copy-paste solutions grouped by theme. Every recipe end-to-end, runs as written.
- PeerCustom adapter
Wrap any LLM API as an AgentsKit adapter. Plug-and-play with the rest of the kit in 30 lines.
- PeerAdapter contract tests
Verify any adapter against the ADR 0001 invariants A1βA10 with the shared test harness.