Model or dataset
jakobdylanc/llmcord avatar
jakobdylanc/llmcord

llmcord: Discord reply chains as LLM conversation history

Make Discord your LLM frontend - Supports any OpenAI compatible API (OpenRouter, Ollama and more)

832 stars202 forksPythonMIT

At a glance

What is it?
llmcord turns a Discord bot into a multi-user frontend for any OpenAI /v1/chat/completions compatible API, storing conversation history in Discord reply chains instead of a database. The design is elegant for small servers and awkward anywhere the reply graph or Discord's message limits get in the way.
Who is it for?
Adopt llmcord if you run a small Discord server where a handful of people already share a channel and you want conversation history to live in Discord rather than a database you have to operate. Skip it if you need per-user memory across channels, auditable logs, or anything beyond text and image attachments, since the reply chain is the only history store and the README does not describe an export path.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem llmcord solves: no database, no separate chat UI

Most self-hosted LLM chat frontends ask you to run a web app and a persistence layer. llmcord asks for neither. The README states that the reply chain is the conversation history, stored entirely in Discord, and that no database is required. That single decision defines the project. A Discord message already has an author, a timestamp, a parent message, and a channel, so the bot reads those fields back and reconstructs the prompt on each turn. If you have ever wanted to try a local model with a group of friends without standing up Postgres and a React app, this is the pitch.

The target user is narrow but real: someone who administers a Discord server, has an API key or an Ollama instance, and wants the conversation to be visible to everyone in the channel as it happens. The README describes replying to continue, branching conversations, and continuing other people's conversations. That is a collaborative transcript, not a private assistant. Anyone expecting per-user isolation should read the permissions section carefully before assuming it works that way.

How the reply chain becomes the prompt

The mechanism is described in the README's feature list. You @ the bot to start a conversation and reply to continue. Each reply adds a node to a tree that Discord already stores. When the bot needs to answer, it walks that chain, applies the max_messages limit, and sends the result to the configured provider. Back-to-back messages from the same user are chained automatically, so replying to the latest one pulls in all of them. In direct messages the reply requirement disappears and conversations continue automatically; @ the bot to start fresh.

Two implementation details matter for anyone judging reliability. The README states that message data is cached in a size-managed, mutex-protected global dictionary, which is the author's answer to Discord API rate limits on repeated history fetches. And the bot is described as fully asynchronous, which is what allows streaming a response while other events arrive. The README also notes that streamed responses turn green when complete and split into separate messages when too long. That is the whole architecture: Discord as the store, an in-process cache as the hot path, and one HTTP call out to a chat completions endpoint.

The consequence is that conversation state is only as durable as the Discord messages it references. Delete a message in the chain and you have edited the prompt. There is no shadow copy.

Configuring config.yaml: the keys that actually change behaviour

Setup is three steps. Clone the repository, write config.yaml, run the bot. Without Docker the README gives `python -m pip install -U -r requirements.txt` followed by `python llmcord.py`; with Docker it gives `docker compose up`.

On the Discord side, bot_token and client_id come from the developer portal, and the README explicitly says to enable MESSAGE CONTENT INTENT. That is the most common first-run failure and it is a portal setting, not a config key. status_message is capped at 128 characters. max_text defaults to 100,000 and covers text from file attachments as well as the message body. max_images defaults to 5 and the README notes it is only applicable with a vision model. max_messages defaults to 25 and drops the oldest messages when exceeded; the README says the bot warns with a message like "Only using last 25 messages" when that happens. use_plain_responses switches from embeds to plaintext, and the README warns it also disables streamed responses and warning messages, which is a bigger trade than the name suggests.

On the LLM side, providers take a base_url and optional api_key, and the README states that only OpenAI /v1/chat/completions compatible APIs are supported. Models are declared as `<provider>/<model>: <parameters>`, the first entry becomes the startup default, and they appear as autocomplete in `/model`. The README notes that some vision models need `:vision` appended to the name. Providers that need extra HTTP data can carry extra_headers, extra_query or extra_body, with azure-openai included as the worked example.

Any key can be sourced from the environment by appending `_env`, so `bot_token_env: DISCORD_BOT_TOKEN` keeps the token out of the file. There is no release history in the material provided, so treat version pinning as unverified.

Permissions, admins, and where access control stops

The permissions block configures users, roles and channels, each with allowed_ids and blocked_ids. Leaving allowed_ids empty allows everyone in that category, which is a permissive default worth noticing. admin_ids controls who can run `/model` and who can DM the bot even when allow_dms is false. The README states plainly that role and channel permissions do not affect DMs, and that category IDs can be used to control channel permissions in groups.

The limitation is structural rather than a bug. Access control decides who may talk to the bot. It does not partition history. Because the reply chain is the history, anyone who can see the channel and reply to the chain is inside the same conversation, with the same system prompt and the same accumulated context. If two teams share a server and both want private LLM threads, the reply-chain model gives them two channels or nothing. The README's own framing, talk to LLMs with your friends, is honest about this being a shared-surface tool.

Where llmcord is the wrong tool

Three cases stand out. First, long-running sessions. max_messages defaults to 25 and silently drops the oldest turns once exceeded. The warning tells you it happened, but the context is already gone. Raising the value moves the cost to your provider's context window and to your token bill, and the README offers no summarisation or compaction step to soften that. Second, attachment-heavy work. The README lists image attachments for vision models and text file attachments such as .txt and .py. There is no mention of PDF, audio, or video handling, so a document pipeline built on llmcord will hit a wall quickly.

Third, anything requiring a durable, queryable log. Conversations live in Discord. There is no export command, no database, and no retention policy described. If your organisation needs to review what a model said last month, or hand a transcript to someone outside the server, llmcord gives you Discord's search box and nothing more. It is also worth noting the project describes itself as 1 Python file, roughly 300 lines. That is a readability strength and a ceiling: features that require background workers, storage, or a plugin system do not fit the shape.

The real alternative: a web frontend with its own storage

The obvious comparison is Open WebUI, which the material here does not mention, so I will keep the comparison to the architectural difference rather than to specific features. The distinction is where state lives. llmcord keeps conversation state in Discord messages and reconstructs prompts by walking reply chains, which means zero storage to operate and history that is exactly as durable as the channel. A web frontend keeps state in its own database, which means per-user accounts, searchable history, and an export path, at the cost of running the database and the web service.

There is a second, subtler difference. llmcord's identity model is Discord's: users are distinguished by their Discord IDs, and the README recommends telling the model about the `<@ID>` prefix in the system prompt so it can mention people. A web frontend typically has its own auth and its own notion of a session. If your users already live in Discord and you want the model to know who is speaking without an account system, llmcord's approach is the shorter path. If your users do not live in Discord, you are paying Discord's API limits for a database you did not want.

Maintenance, licensing, and what to check before you commit

The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive licence and it is the standard choice for a small bot; it also means no warranty, so treat the bot as your operational responsibility. This is not legal advice, and if you are embedding llmcord in a product you should have someone review the notice requirements against how you distribute it.

Maintenance cost is dominated by two things outside the repository. The first is the provider API surface: because llmcord only speaks OpenAI /v1/chat/completions, upstream changes at OpenRouter, OpenAI, xAI, Google or a local server show up as breakage in the bot's single HTTP path. The second is Discord's own platform rules, including the MESSAGE CONTENT INTENT gate and per-message limits that drive the max_text and max_messages settings.

Inside the repository, the surface is small enough to audit: one Python file, a requirements.txt, a Docker Compose file, and config.yaml. The README states that config hot reloading is supported, so tuning max_messages or system_prompt does not require a restart, though the README does not say which keys are reloadable and which are not. That is the first thing to test on a staging bot. The second is the `:vision` suffix behaviour on whichever model you plan to use, since the README only says some vision models may need it.

Editorial conclusion

Adopt llmcord if you run a small Discord server where a handful of people already share a channel and you want conversation history to live in Discord rather than a database you have to operate. Skip it if you need per-user memory across channels, auditable logs, or anything beyond text and image attachments, since the reply chain is the only history store and the README does not describe an export path. Before deploying, verify three things in your own config: that MESSAGE CONTENT INTENT is enabled on the bot, that max_messages is set to a value your provider's context window can absorb, and that your permissions block list covers the channels where the bot should not answer.

Official sources

  1. Issues
  2. jakobdylanc/llmcord on GitHub
  3. License: MIT
  4. README
Community notes

Community notes