Discord

Connect Discord with the Discord adapter.


Connect Discord with the Discord adapter. Discord has native slash commands with typed arguments, so /commands feel first class.

Install#

npm install @copilotkit/channels @copilotkit/runtime @tanstack/ai @tanstack/ai-openai

Credentials#

From the Discord Developer Portal (your application and bot):

DISCORD_BOT_TOKEN=...
DISCORD_APP_ID=...
DISCORD_GUILD_ID=...   # optional, for faster command registration in one server

# The runtime runs every channel, so a CopilotKit Intelligence key is required
# even for a direct adapter (free tier available).
COPILOTKIT_INTELLIGENCE_URL=https://your-intelligence-url
COPILOTKIT_API_KEY=cpk-...

Connect#

import { createChannel } from "@copilotkit/channels";
import { discord } from "@copilotkit/channels/discord";
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
import { agent } from "./agent"; // your TanStack AI agent, see the Quickstart

const channel = createChannel({
  name: "support-bot",
  agent,
  adapters: [
    discord({
      botToken: process.env.DISCORD_BOT_TOKEN!,
      appId: process.env.DISCORD_APP_ID!,
      guildId: process.env.DISCORD_GUILD_ID, // optional
    }),
  ],
});

channel.onMessage(async ({ thread, message }) => {
  await thread.runAgent({ prompt: message.text });
});

// The runtime drives the channel — it starts the Discord adapter, you don't
// call channel.start() yourself. `ready()` activates it.
const apiUrl = process.env.COPILOTKIT_INTELLIGENCE_URL!;
const runtime = new CopilotRuntime({
  agents: {},
  intelligence: new CopilotKitIntelligence({
    apiUrl,
    wsUrl: apiUrl.replace(/^http/, "ws"),
    apiKey: process.env.COPILOTKIT_API_KEY!,
  }),
  identifyUser: () => ({ id: "demo-user", name: "Demo User" }),
  channels: [channel],
});

const listener = createCopilotNodeListener({ runtime });
await listener.channels?.ready();

Typed slash commands#

Discord parses command arguments for you. Add an options schema and read the parsed values from options:

import { defineChannelCommand } from "@copilotkit/channels";
import { z } from "zod";

defineChannelCommand({
  name: "weather",
  description: "Get the weather for a city.",
  options: z.object({ city: z.string() }),
  async handler({ thread, options }) {
    await thread.runAgent({ prompt: `What's the weather in ${options.city}?` });
  },
});

See commands and reactions for the full command API.

Built-in tools and context#

The Discord adapter ships helpers you can spread into your channel: a set of tools (including a Discord user-lookup tool) and Discord formatting context.

import { discord, defaultDiscordTools, defaultDiscordContext } from "@copilotkit/channels/discord";

createChannel({
  agent,
  tools: [...defaultDiscordTools],
  context: [...defaultDiscordContext],
  adapters: [discord({ /* ...credentials */ })],
});