This is an old revision of the document!
# LibreChat Complete Setup Tutorial Addressed to: Libre Domain: `chat.haacksnetworking.org` Stack: Debian 13, npm (no Docker for LibreChat), Apache reverse proxy, PM2, MongoDB 8.0, DeepInfra DeepSeek V4 Flash, custom MCP document tools (docx, xlsx, pdf, pptx).
—
## 1. Prerequisites
```bash sudo apt update && sudo apt upgrade -y sudo apt install -y git build-essential curl gnupg python3-pip python3-venv ```
### Node.js 24 ```bash curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs sudo npm install -g npm@11 node -v && npm -v ```
### MongoDB 8.0 (Debian 13 uses Bookworm repo) ```bash wget -qO - https://www.mongodb.org/static/pgp/server-8.0.asc | sudo gpg –dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg echo “deb [ signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main” | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list sudo apt update sudo apt install -y mongodb-org sudo systemctl enable –now mongod ```
### PM2 ```bash sudo npm install -g pm2 ```
—
## 2. Install LibreChat
```bash cd /opt sudo git clone https://github.com/LibreChat-AI/LibreChat.git sudo chown -R $USER:$USER LibreChat cd LibreChat cp .env.example .env nano .env ```
Essential `.env` values: ```env MONGO_URI=mongodb://127.0.0.1:27017/LibreChat
JWT_SECRET=your_64_hex_secret JWT_REFRESH_SECRET=your_64_hex_secret CREDS_KEY=your_64_hex_secret CREDS_IV=your_32_hex_iv
DOMAIN_CLIENT=https://chat.haacksnetworking.org DOMAIN_SERVER=https://chat.haacksnetworking.org
TRUST_PROXY=1 HOST=127.0.0.1
DEEPINFRA_API_KEY=sk-di-your_key_here ```
Generate secrets with `openssl rand -hex 32` (and `openssl rand -hex 16` for the IV).
```bash npm run reinstall npm run backend # test once, then Ctrl+C pm2 start npm –name librechat – run backend pm2 save pm2 startup # follow the printed command ```
—
## 3. Apache Reverse Proxy + HTTPS
### Modules ```bash sudo a2enmod proxy proxy_http proxy_wstunnel ssl rewrite headers socache_shmcb sudo systemctl restart apache2 ```
### Certificate ```bash sudo apt install -y certbot python3-certbot-apache sudo certbot –apache -d chat.haacksnetworking.org ```
### VirtualHost ```apache <VirtualHost *:443>
ServerName chat.haacksnetworking.org SSLEngine on SSLCertificateFile /etc/letsencrypt/live/chat.haacksnetworking.org/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/chat.haacksnetworking.org/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf
ProxyPreserveHost On ProxyRequests Off
# Exclude downloads from proxy (must come before ProxyPass /)
ProxyPass /downloads !
Alias /downloads /opt/LibreChat/generated_docs
<Directory /opt/LibreChat/generated_docs>
Options -Indexes
Require all granted
</Directory>
ProxyPass / http://127.0.0.1:3080/ ProxyPassReverse / http://127.0.0.1:3080/
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:3080/$1" [P,L]
ProxyTimeout 300 Timeout 300 LimitRequestBody 26214400
ErrorLog ${APACHE_LOG_DIR}/librechat-error.log
CustomLog ${APACHE_LOG_DIR}/librechat-access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName chat.haacksnetworking.org Redirect permanent / https://chat.haacksnetworking.org/
</VirtualHost> ```
```bash sudo a2ensite librechat sudo apache2ctl configtest sudo systemctl reload apache2 ```
### Firewall ```bash sudo ufw allow OpenSSH sudo ufw allow 'Apache Full' sudo ufw enable ```
—
## 4. librechat.yaml (DeepSeek + Web Search + MCP)
Create or edit `/opt/LibreChat/librechat.yaml`:
```yaml version: 1.3.17 cache: true
endpoints:
custom:
- name: "DeepInfra DeepSeek"
apiKey: "${DEEPINFRA_API_KEY}"
baseURL: "https://api.deepinfra.com/v1/openai"
models:
default: ["deepseek-ai/DeepSeek-V4-Flash"]
fetch: false
titleConvo: true
titleModel: "deepseek-ai/DeepSeek-V4-Flash"
modelDisplayLabel: "DeepSeek V4 Flash"
webSearch:
searchProvider: "keenable" scraperProvider: "keenable" rerankerType: "none"
mcpServers:
document-tools:
type: stdio
command: /opt/mcp-docs/venv/bin/python
args:
- /opt/mcp-docs/server.py
timeout: 60000
```
—
## 5. MCP Document-Tools Server (docx, xlsx, pdf, pptx)
```bash cd /opt mkdir -p /opt/mcp-docs cd /opt/mcp-docs python3 -m venv venv source venv/bin/activate pip install mcp python-docx openpyxl pandas reportlab python-pptx ```
Create `/opt/mcp-docs/server.py`:
```python #!/usr/bin/env python3 import json import os from mcp.server import MCPServer from docx import Document from openpyxl import Workbook from openpyxl.styles import Font, PatternFill from reportlab.lib.pagesizes import letter from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.lib.styles import getSampleStyleSheet from pptx import Presentation
OUTPUT_DIR = “/opt/LibreChat/generated_docs” os.makedirs(OUTPUT_DIR, exist_ok=True)
mcp = MCPServer(“document-tools”)
@mcp.tool() def create_docx(title: str, content: list = None, body: str = None) → str:
"""Create a Word (.docx) document.
Prefer content as a list of objects: [{"type":"heading|paragraph|bullet", "text":"..."}].
If that fails, pass body as a plain multi-line string.
"""
doc = Document()
doc.add_heading(title, 0)
items = content or []
if not items and body:
for line in body.splitlines():
line = line.strip()
if not line:
continue
if line.startswith("#"):
doc.add_heading(line.lstrip("# ").strip(), level=1)
elif line.startswith("- ") or line.startswith("* "):
doc.add_paragraph(line[2:].strip(), style="List Bullet")
else:
doc.add_paragraph(line)
else:
for item in items:
if isinstance(item, str):
doc.add_paragraph(item)
continue
if not isinstance(item, dict):
continue
t = (item.get("type") or "paragraph").lower()
text = item.get("text") or item.get("content") or ""
if not text:
continue
if t == "heading":
doc.add_heading(text, level=1)
elif t == "bullet":
doc.add_paragraph(text, style="List Bullet")
else:
doc.add_paragraph(text)
filename = f"{title.replace(' ', '_')}.docx"
path = os.path.join(OUTPUT_DIR, filename)
doc.save(path)
return json.dumps({
"success": True,
"file": filename,
"download": f"https://chat.haacksnetworking.org/downloads/{filename}"
})
@mcp.tool() def create_xlsx(name: str, headers: list, rows: list) → str:
"""Create an Excel (.xlsx) spreadsheet."""
wb = Workbook()
ws = wb.active
ws.title = name[:31]
for col, h in enumerate(headers, 1):
cell = ws.cell(1, col, h)
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
for r_idx, row in enumerate(rows, 2):
for c_idx, val in enumerate(row, 1):
ws.cell(r_idx, c_idx, val)
filename = f"{name.replace(' ', '_')}.xlsx"
path = os.path.join(OUTPUT_DIR, filename)
wb.save(path)
return json.dumps({
"success": True,
"file": filename,
"download": f"https://chat.haacksnetworking.org/downloads/{filename}"
})
@mcp.tool() def create_pdf(title: str, content: list = None, body: str = None) → str:
"""Create a PDF document. content is a list of dicts with type (heading/paragraph/bullet) and text, or pass body as plain text."""
filename = f"{title.replace(' ', '_')}.pdf"
path = os.path.join(OUTPUT_DIR, filename)
doc = SimpleDocTemplate(path, pagesize=letter)
styles = getSampleStyleSheet()
story = []
story.append(Paragraph(title, styles["Title"])) story.append(Spacer(1, 12))
items = content or []
if not items and body:
for line in body.splitlines():
line = line.strip()
if not line:
continue
if line.startswith("#"):
story.append(Paragraph(line.lstrip("# ").strip(), styles["Heading2"]))
elif line.startswith("- ") or line.startswith("* "):
story.append(Paragraph(f"• {line[2:].strip()}", styles["Normal"]))
else:
story.append(Paragraph(line, styles["Normal"]))
story.append(Spacer(1, 6))
else:
for item in items:
if isinstance(item, str):
story.append(Paragraph(item, styles["Normal"]))
story.append(Spacer(1, 6))
continue
if not isinstance(item, dict):
continue
t = (item.get("type") or "paragraph").lower()
text = item.get("text") or item.get("content") or ""
if not text:
continue
if t == "heading":
story.append(Paragraph(text, styles["Heading2"]))
elif t == "bullet":
story.append(Paragraph(f"• {text}", styles["Normal"]))
else:
story.append(Paragraph(text, styles["Normal"]))
story.append(Spacer(1, 6))
doc.build(story)
return json.dumps({
"success": True,
"file": filename,
"download": f"https://chat.haacksnetworking.org/downloads/{filename}"
})
@mcp.tool() def create_pptx(title: str, slides: list = None) → str:
"""Create a PowerPoint (.pptx). slides is a list of dicts with title and content (list of bullet strings)."""
prs = Presentation()
title_slide_layout = prs.slide_layouts[0]
slide = prs.slides.add_slide(title_slide_layout)
slide.shapes.title.text = title
if len(slide.placeholders) > 1:
try:
slide.placeholders[1].text = "Generated by LibreChat"
except Exception:
pass
bullet_layout = prs.slide_layouts[1]
for s in (slides or []):
slide = prs.slides.add_slide(bullet_layout)
slide.shapes.title.text = s.get("title", "Slide")
body = slide.placeholders[1]
tf = body.text_frame
tf.text = ""
for bullet in s.get("content", []):
p = tf.add_paragraph()
p.text = str(bullet)
p.level = 0
filename = f"{title.replace(' ', '_')}.pptx"
path = os.path.join(OUTPUT_DIR, filename)
prs.save(path)
return json.dumps({
"success": True,
"file": filename,
"download": f"https://chat.haacksnetworking.org/downloads/{filename}"
})
if name == “main”:
mcp.run(transport="stdio")
```
```bash chmod +x /opt/mcp-docs/server.py mkdir -p /opt/LibreChat/generated_docs deactivate ```
Restart LibreChat:
```bash pm2 restart librechat ```
—
## 6. Using the Tools
1. In a chat, open the tools / MCP menu. 2. Add or enable the document-tools MCP server. 3. Ask for a document. Available tools:
Example prompts:
- “Create a Word document titled 'Solar System' with paragraphs and bullets about the planets. Give me the download link.” - “Make an Excel file named 'Employees' with headers Name, Department, Salary and three sample rows.” - “Create a PDF titled 'Monthly Report' with a heading and a few paragraphs.” - “Create a PowerPoint titled 'Project Update' with slides for Goals, Progress, and Next Steps.”
Files appear at `https://chat.haacksnetworking.org/downloads/…`.
—
## 7. Admin and Registration
First account is admin. Verify:
```bash mongosh –quiet –eval 'db = db.getSiblingDB(“LibreChat”); db.users.find({}, {email:1, role:1}).pretty()' ```
To disable public registration, add to `.env`:
```env ALLOW_REGISTRATION=false ```
Then create users with `npm run create-user`.
—
## 8. Updates
```bash cd /opt/LibreChat pm2 stop librechat git pull npm run smart-reinstall pm2 restart librechat ```
—
## Quick Checklist
1. Node 24 + MongoDB (Bookworm repo) 2. Clone → `.env` (secrets, domains, HOST, DeepInfra key) → `npm run reinstall` 3. PM2 4. Apache modules + Certbot + VirtualHost (with `ProxyPass /downloads !` + Alias) 5. `librechat.yaml` (DeepSeek + webSearch + mcpServers) 6. MCP server in `/opt/mcp-docs` with docx / xlsx / pdf / pptx tools 7. Restart LibreChat and enable the document-tools MCP server in chat
This is the full working configuration.