How to Build an AI Chatbot in JavaScript

A JavaScript AI chatbot needs a model API call, a conversation history that grows with each turn, and controlled access to any tools you give it. Here is how to build each part in Node.js.

Updated 4 min read

A JavaScript AI chatbot is a Node.js program that calls a model API, keeps a record of the conversation so each turn has context, and optionally calls tools when the model requests them. The core loop is short. What separates a useful chatbot from a brittle one is how it manages state, handles errors, and controls what the model can do. A tool like fenbs can supply project-level rules and progress to a chatbot that connects over MCP, so the assistant starts each session already knowing the context.

What you need before writing the first line

You need a Node.js environment (version 20 or later works well), an API key for the model provider you choose (OpenAI, Anthropic, and others all expose a chat completions-style endpoint), and a basic understanding of how a messages array works. Every call to a chat completions API sends the full conversation history, not just the latest message. The model uses that history to generate a response in context.

The conversation loop

The messages array starts with a system message that sets the assistant's role and any standing rules. Each user turn appends a { role: "user", content: "..." } object. Each model response appends a { role: "assistant", content: "..." } object. The next call sends the full updated array.

Basic conversation loop in Node.js (OpenAI SDK)
import OpenAI from 'openai';
import * as readline from 'readline/promises';

const client = new OpenAI(); // reads OPENAI_API_KEY from env
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const messages = [{ role: 'system', content: 'You are a helpful assistant.' }];

while (true) {
  const input = await rl.question('You: ');
  if (!input.trim()) continue;
  messages.push({ role: 'user', content: input });

  const response = await client.chat.completions.create({
    model: 'gpt-4o-mini',
    messages,
  });

  const reply = response.choices[0].message.content;
  messages.push({ role: 'assistant', content: reply });
  console.log('Assistant:', reply);
}

This is a single-session chatbot. The history grows until the program exits. If you want history to persist across restarts, serialize the messages array to a file or database and load it at startup.

Adding tool calls

Tool calls let the model request an action your code performs. You define a tool with a name, a description, and a JSON schema for its parameters. The model decides when to call it. Your code runs the function, appends the result, and calls the API again.

Keep tool definitions narrow. A tool that can read a file is safer than a tool that can read any path on disk. A tool that can look up a customer record is safer than a tool that can modify it. Never give the model write access to something it does not need to write to complete its task.

Handling errors and edge cases

Model API calls can fail with rate-limit errors, network timeouts, or unexpected response shapes. Wrap each API call in a try-catch. For rate limits, a simple exponential backoff loop is usually enough for a development chatbot. In production, use a queue or a dedicated rate-limit library.

  • Cap the messages array length to stay within the model's context window: truncate the oldest non-system messages when the count gets large.
  • Validate tool call arguments before executing the function, even if the model generated them.
  • Log tool calls and results during development so you can see exactly what the model requested.
  • Return a clear error string from a failed tool call rather than throwing, so the model can report the failure to the user.

Testing your chatbot before connecting it to a frontend

Before wiring a JavaScript chatbot into a web interface or external service, test the conversation loop directly in Node.js. Run it interactively and step through a series of messages that cover the scenarios you care about: a normal question, an empty input, a message that triggers a tool call, and a very long message that approaches the context window limit. A chatbot that works on the happy path may fail silently on edge cases you will not notice until a user finds them.

Write unit tests for each tool function. The model can return arguments in formats you did not anticipate. A tool function that validates its inputs and returns a clear error string on bad arguments is easier to debug than one that throws or silently produces a wrong result. Test tool functions in isolation with mock arguments before connecting them to the API call loop.

Adding persistent project context with an MCP client

MCP (Model Context Protocol) is a standard way for an AI app to call an outside tool at runtime. If you add an MCP client to your JavaScript chatbot, it can connect to fenbs and read project rules, current tasks, and progress at the start of a session. The model then works with that context in memory.

Building an MCP client is a separate step beyond this article. The how-to-build-an-ai-agent guide covers tool integration in more depth. For language-specific builds in Java and PHP, the same conversation loop pattern applies with different HTTP client libraries.

How fenbs helps

Give your chatbot shared project context

If you are building a chatbot for a project your team works on, connect a supported AI tool to fenbs and let it read the project context before each session. Decisions and rules appear first, so the assistant knows the constraints without you restating them. Save progress before each session ends so the next run continues from Where we left off.

Create a free account

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

How to do this with fenbs

  1. Create a free account and add a project for the product you are building.
  2. Add the decisions that govern the chatbot's behavior as Decisions and rules: which tools it may call, what it must not do, the tone rules.
  3. Add tasks for each feature or conversation flow you are building. Note the current status and what was checked last.
  4. Connect a supported AI tool from Integrations. At the start of the session it reads the rules and current tasks.
  5. When you finish a session, ask the assistant to save its progress on each task so the next run knows where to start.

fenbs does not host or run your chatbot. It is the shared record that tells any connected AI tool what the project is, what has been decided, and where things stand.

Questions people ask.

What is the minimum code needed for a JavaScript AI chatbot?

You need an API call to a chat completions endpoint, a messages array that grows with each turn, and logic to append the model's reply before the next call. That is roughly 20 lines in Node.js using an SDK. A working chatbot also needs error handling and a way to limit how long the history grows.

How do I persist conversation history across sessions in JavaScript?

Serialize the messages array to JSON and write it to a file or a database when the session ends. Load it at startup and pass it to the first API call. Make sure to handle the case where the saved history is longer than the model's context window.

How do I add tools to a JavaScript chatbot?

Define each tool as an object with a name, a description, and a JSON schema for its parameters. Pass the tools array to the API call. When the model returns a tool-call request, run the matching function, append the result as a tool message, and call the API again.

What is MCP and how does it relate to a JavaScript chatbot?

MCP (Model Context Protocol) is a standard that lets an AI application call outside tools and data sources at runtime. A JavaScript chatbot with an MCP client can connect to services like fenbs, read project context or tasks, and include that information in the conversation without you hard-coding it.

More in Agent harness (for developers)

All Agent harness (for developers) articles

Start with one thing.

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

There is nothing to set up first. Write one line and you’ve started.