A modular, agentic chatbot with RAG support and multi-LLM provider integration. Built with Python (FastAPI) and React, deployable via Docker.
- Multi-LLM Support — Anthropic Claude, OpenAI GPT, and Ollama (local/remote) out of the box
- Agentic Tool System — Built-in tools (calculator, code executor, web search, file reader, date/time) with a simple plugin API to add your own
- MCP Server Integration — Connect external services via the Model Context Protocol; tools from MCP servers appear in the sidebar alongside built-in tools
- RAG Integration — Upload files and get context-aware responses using ChromaDB vector storage
- Streaming Chat — Real-time WebSocket streaming for responsive conversations
- Clean UI — Minimal, warm-toned interface with sidebar for model/tool/file selection
- Docker Ready — Single-command deployment with Docker Compose
- Config-Driven — YAML configuration for providers, tools, and RAG settings
- Open Source — MIT licensed, designed for extension and contribution
# 1. Clone the repository
git clone https://github.com/aashiquear/nexus-chat.git
cd nexus-chat
# 2. Configure
cp .env.example .env
# Edit .env and add your API keys
# 3. Run
docker compose up --build
# Open http://localhost:8000Prerequisites: Python 3.11+, Node.js 18+
# Linux / macOS
bash start_dev.sh
# Windows
start_dev.batOr manually:
# Backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Frontend
cd frontend && npm install && npm run build && cd ..
# Run
python -m backend.mainOpen http://localhost:8000.
All settings are in config/settings.yaml. API keys can be set either in the YAML file directly or via environment variables in .env.
Edit config/settings.yaml under providers::
providers:
anthropic:
enabled: true
api_key: "${ANTHROPIC_API_KEY}"
default_model: "claude-sonnet-4-20250514"
models:
- id: "claude-sonnet-4-20250514"
name: "Claude Sonnet 4"
max_tokens: 8192Set the key in .env:
ANTHROPIC_API_KEY=sk-ant-...
- Install and run Ollama on your machine
- Pull a model:
ollama pull llama3.2 - Ensure the config has:
providers:
ollama:
enabled: true
base_url: "http://localhost:11434" # or host.docker.internal for Docker
models:
- id: "llama3.2"
name: "Llama 3.2"Creating a new tool takes 3 steps:
Create backend/tools/my_tool.py:
import json
from . import BaseTool, register_tool
@register_tool("my_tool")
class MyTool(BaseTool):
name = "my_tool"
description = "Describe what your tool does"
parameters = {
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "What this parameter does"
}
},
"required": ["input"]
}
async def execute(self, **kwargs) -> str:
result = do_something(kwargs["input"])
return json.dumps({"result": result})In config/settings.yaml:
tools:
my_tool:
enabled: true
name: "My Tool"
description: "Does something useful"
icon: "wrench" # Lucide icon name
config:
custom_key: "value" # Accessible via self.configAdd to backend/main.py:
import backend.tools.my_toolRestart the server — the tool appears in the sidebar.
Nexus Chat can connect to external services via the Model Context Protocol (MCP). MCP servers expose tools over HTTP JSON-RPC 2.0, and their tools appear in the sidebar alongside built-in tools — users can toggle them on/off per conversation.
An example SQLite MCP server is included at examples/mcp-database-server/.
Bundled (easiest): Start everything together with the mcp profile:
docker compose --profile mcp up --buildStandalone: Run the MCP server independently and let it join the shared nexus-net network:
# 1. Start Nexus Chat (creates the nexus-net network)
docker compose up --build
# 2. In another terminal, start the MCP server
cd examples/mcp-database-server
docker compose up --buildBoth approaches connect via the nexus-net Docker network, so the URL http://mcp-database:8100 resolves in either case.
Then enable it in config/settings.yaml:
mcp_servers:
database:
enabled: trueRestart Nexus Chat. The database server exposes four tools: query, execute, list_tables, and describe_table. Once connected, ask the chatbot things like "List all tables in the database" or "Insert a new note titled 'Hello'".
Any HTTP service that implements the MCP JSON-RPC interface can be connected:
1. Implement the /rpc endpoint with three methods:
| Method | Description |
|---|---|
initialize |
Handshake — return server name and version |
tools/list |
Return an array of tool definitions (name, description, JSON Schema parameters) |
tools/call |
Execute a tool by name with arguments, return the result |
Plus a GET /health endpoint for liveness checks.
2. Add it to config/settings.yaml:
mcp_servers:
my_server:
enabled: true
name: "My Service"
description: "Does something useful"
url: "http://my-mcp-server:9000"
icon: "server" # Lucide icon name
timeout: 30 # seconds3. Connect via Docker network:
Option A — Bundled in docker-compose.yml:
services:
my-mcp-server:
build: ./path/to/server
networks:
- nexus-net
profiles:
- mcp
ports:
- "9000:9000"Option B — Standalone container on the shared network:
docker run --name my-mcp-server --network nexus-net -p 9000:9000 my-mcp-imageRestart Nexus Chat — the MCP server's tools appear under the "MCP Servers" section in the sidebar.
All services (Nexus Chat + MCP servers) share a bridge network called nexus-net. The root docker-compose.yml creates it automatically. Standalone MCP servers can join it by referencing nexus-net as an external network — see examples/mcp-database-server/docker-compose.yml for a ready-made template.
- Config-level: Set
enabled: falseinsettings.yamland restart. - Docker-level (bundled): Run without the
mcpprofile:docker compose up(no--profile mcp). - Docker-level (standalone): Stop the standalone container:
docker compose downin its directory. - Per-conversation: Toggle individual MCP tools on/off in the sidebar, just like built-in tools.
nexus-chat/
├── backend/
│ ├── main.py # FastAPI app, routes, WebSocket
│ ├── config.py # YAML config loader
│ ├── orchestrator.py # Chat orchestrator (LLM + tools + MCP + RAG)
│ ├── providers/
│ │ ├── __init__.py # Base class + registry
│ │ ├── anthropic_provider.py
│ │ ├── openai_provider.py
│ │ └── ollama_provider.py
│ ├── tools/
│ │ ├── __init__.py # Base class + registry
│ │ ├── builtin.py # Calculator, code exec, search, etc.
│ │ └── example_tool.py # Template for custom tools
│ ├── mcp/
│ │ ├── __init__.py # MCP module entry
│ │ └── client.py # MCPClient + MCPManager
│ └── rag/
│ ├── __init__.py
│ └── engine.py # Document ingestion + retrieval
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Main app component
│ │ ├── main.jsx # Entry point
│ │ ├── components/
│ │ │ ├── Sidebar.jsx # Model/tool/MCP/file selection
│ │ │ ├── ChatMessage.jsx
│ │ │ └── ChatInput.jsx
│ │ ├── hooks/
│ │ │ ├── useChat.js # WebSocket hook
│ │ │ └── api.js # REST API helpers
│ │ └── styles/
│ │ └── global.css # All styles
│ ├── index.html
│ ├── package.json
│ └── vite.config.js
├── examples/
│ └── mcp-database-server/ # Example MCP server (SQLite)
│ ├── server.py
│ ├── Dockerfile
│ └── README.md
├── config/
│ └── settings.yaml # Main configuration file
├── data/
│ ├── uploads/ # User-uploaded files
│ └── vector_store/ # ChromaDB persistence
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── pyproject.toml
├── start_dev.sh # Linux/macOS dev script
├── start_dev.bat # Windows dev script
├── .env.example
└── README.md
┌─────────────┐ WebSocket ┌───────────────────────────────────┐
│ React UI │◄───────────────────►│ FastAPI Backend │
│ │ REST API │ │
│ • Sidebar │◄───────────────────►│ ┌─────────────────────────────┐ │
│ • Chat │ │ │ Chat Orchestrator │ │
│ • Input │ │ │ │ │
└─────────────┘ │ │ ┌─────────┐ ┌──────────┐ │ │
│ │ │Providers│ │ Tools │ │ │
│ │ │• Claude │ │• Calc │ │ │
│ │ │• OpenAI │ │• Search │ │ │
│ │ │• Ollama │ │• Code │ │ │
│ │ └─────────┘ │• Custom │ │ │
│ │ └──────────┘ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ MCP Manager │ │ │
│ │ │ JSON-RPC clients │──┼──┼──► MCP Servers
│ │ └──────────────────────┘ │ │ (Docker / remote)
│ │ ┌──────────────────────┐ │ │
│ │ │ RAG Engine │ │ │
│ │ │ ChromaDB + Chunking │ │ │
│ │ └──────────────────────┘ │ │
│ └─────────────────────────────┘ │
└───────────────────────────────────┘
| Endpoint | Method | Description |
|---|---|---|
/api/health |
GET | Health check |
/api/models |
GET | List available LLM models |
/api/tools |
GET | List available tools |
/api/files |
GET | List uploaded files |
/api/upload |
POST | Upload a file (multipart) |
/api/files/{name} |
DELETE | Delete an uploaded file |
/api/mcp/servers |
GET | List MCP servers and status |
/api/mcp/servers/{id}/reconnect |
POST | Reconnect an MCP server |
/ws/chat |
WebSocket | Streaming chat |
Send:
{
"messages": [{"role": "user", "content": "Hello"}],
"model": "claude-sonnet-4-20250514",
"tools": ["calculator", "web_search"],
"files": ["report.pdf"],
"system_prompt": "You are a helpful assistant."
}Receive (streamed events):
{"type": "text", "content": "Hello! "}
{"type": "tool_call", "name": "calculator", "arguments": {"expression": "2+2"}}
{"type": "tool_result", "name": "calculator", "result": "{\"result\": 4}"}
{"type": "text", "content": "The answer is 4."}
{"type": "done"}- Conversation history persistence (SQLite)
- Multi-user authentication
- MCP (Model Context Protocol) server integration
- Mobile-friendly PWA
- Electron desktop app (Windows/macOS/Linux)
- iOS / Android via Capacitor or React Native wrapper
- Plugin marketplace for community tools
- Streaming tool execution with progress
MIT — see LICENSE.