mcp server
bonfire
Start a bonfire in your terminal - A free, local-first AI coding assistant for the terminal. No API keys, no cloud — just your models and your code.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- Node.js 20 or newer
{
"mcpServers": {
"bonfire": {
"command": "npx",
"args": [
"-y",
"@praneethtss/bonfire"
]
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 26, 2026bonfire
Your code. Your model. Your machine.
A terminal coding assistant that runs against any local model.
No keys. No meters. No data leaks. Just you and the box you trust.
What makes bonfire different
There's no shortage of terminal coding assistants. Here's what bonfire does that the others don't.
Local-first, by default
The model lives on a machine you control — your laptop, your homelab, a friend's GPU rig. bonfire is just the CLI that talks to it.
- No API keys to leak.
- No tokens to budget.
- No "we may use your data to improve our models."
Bring your own model
Ollama, llama.cpp, or any OpenAI-compatible API (Google Gemini, NVIDIA NIM, OpenAI, custom endpoints). qwen3.6:latest, llama3.3, deepseek-coder-v2, gpt-4o-mini — whatever fits your hardware and your taste.
Codemap — your repo as a tree of summaries
Other assistants either grep blindly (and choke on a big monorepo) or stand up a vector DB you have to babysit.
bonfire takes a third path: it walks your repo once, asks the model for a one-line summary of every file and directory, and stores the result as JSON in .bonfire/codemap.json. The model then navigates that tree like a filesystem — reading summaries, not source — until it finds what it's looking for.
> /codemap
codemap · 247 files · 38 dirs · 285/285 summarized · 142 KB indexed
> where do we handle session deletion?
[bonfire] navigate(".")
↳ src/ — TypeScript source for the bonfire CLI
[bonfire] navigate("src")
↳ session/ — JSON session persistence in .bonfire/sessions
[bonfire] read_file("src/session/storage.ts")
↳ deleteSession(root, id) at line 41
Sessions are deleted via deleteSession() in src/session/storage.ts:41,
which unlinks .bonfire/sessions/<id>.json after verifying it exists.
Three tool calls. Zero greps. The cache invalidates per-file on mtime so it stays current as you edit. Directory summaries auto-invalidate when any descendant file is modified after the summary was written. No embeddings, no vector DB, no daemon, no cloud round-trips.
.gitignore is honoured with full glob semantics (*.log, **/generated/**, !keep.log, anchored /root-only). Symlinks are skipped to avoid cycles and out-of-repo escapes.
The first build does read every file — but you only run it once per repo, the result is cached on disk, and /codemap rebuild is the only way to redo it. The build checkpoints to disk every 25 summaries, so a crash mid-pass picks up where it left off. A failed summary leaves the node un-summarized so the next build retries it instead of caching a fake. Works comfortably on repos with thousands of files; very large monorepos (tens of thousands) will benefit from running /codemap build over a coffee break or bumping codemap.concurrency if you're driving a remote API.
Sessions — pick up tomorrow where you left off
Every conversation auto-saves to .bonfire/sessions/<id>.json after each turn. List them, share them, diff them, resume them.
> /sessions list
Sessions (3):
▸ a3f1c8d92b4e 2 hours ago 12 turns · ollama · "refactor the auth flow"
4d8e2f1a7b9c yesterday 8 turns · ollama · "fix the failing test"
92c1f0e8a4b7 2 days ago 24 turns · llama.cpp · "build codemap navigation"
> /sessions load 4d8e
Loaded session 4d8e2f1a7b9c · 8 messages · cwd: /work/api
No SaaS account. No "your history will be cleared in 30 days." Just JSON files on your disk, gitignored by default.
Three ways to run it
bonfire is just a CLI. The model lives wherever you put it. Pick the topology that fits your hardware:
1. Self-contained on your laptop
[ laptop ] bonfire ── ollama ── qwen3.6:latest (local GPU)
Works offline. Take it on a plane. Best for M-series Macs, gaming laptops, anything with ≥ 8 GB VRAM.
2. Laptop + your homelab
[ laptop ] bonfire ───→ homelab.local:11434 ── qwen3.6:latest (24 GB box)
Type on the machine you like. Run the model on whichever box has the GPU. Set OLLAMA_BASE_URL=http://homelab.local:11434/api and you're done.
Best for: Mac mini cluster, the 4090 in the closet, the old gaming PC you stopped using.
3. Remote APIs (cloud / edge)
[ laptop ] bonfire ──→ Google Gemini / OpenAI / NVIDIA NIM / any OpenAI-compatible API
Connect to any OpenAI-compatible API endpoint. Great for when you're traveling, don't have a GPU, or want to use a specific cloud model.
4. Shared rig with friends
laptop A bonfire ─┐
laptop B bonfire ─┼──→ llama-server (shared, API-key gated)
laptop C bonfire ─┘
One person hosts a serious model. Everyone else points their bonfire at it. Same shape works for any OpenAI-compatible cloud endpoint when you're traveling.
Best for: small teams, study groups, family GPU.
Cross-platform. macOS, Linux, Windows (Terminal / PowerShell). Git Bash / MSYS users prefix with winpty bonfire.
Quick start
npm install -g @praneethtss/bonfire
bonfire
On first run, bonfire will guide you through a quick setup. Choose your provider (ollama, llama.cpp, or remote), and you're ready to go.
Using Ollama (recommended for Mac/Linux)
ollama pull qwen3.6:latest
bonfire
Using llama.cpp (full control)
.\llama-server.exe --jinja -m .\qwen3.6.gguf -c 8192 -ngl 99
bonfire
The
--jinjaflag is mandatory for tool calling. Without it, bonfire's tool calls won't reach the model.
Using an OpenAI-compatible API (Google Gemini, OpenAI, NVIDIA NIM, etc.)
bonfire
# Select "remote" when prompted
# Enter your API endpoint and key
Or configure manually in your config file (see below).
Try these first
list the files in src— feel for navigation/codemap build— kick off the first summarization pass (slow once; it reads every file)read README.md and summarize what this project does/help— every slash command at a glance
Configuration
Config Location
Configuration is layered — later layers win, and each is optional:
- Built-in defaults
- Global config —
~/.config/bonfire/config.json(Linux/macOS) or%APPDATA%/bonfire/config.json(Windows) - Project config —
bonfire.config.jsonin the directory you launched bonfire from
The global file travels with you; drop a bonfire.config.json in a repo when that repo needs a different model, a bigger context window, or its own MCP servers. Merging is deep, so a project file that only sets context.maxTokens keeps every other global setting intact.
Quick Reconfigure
Change your provider anytime with:
/reconfigure
This opens an interactive TUI to change:
- Provider (ollama, llama.cpp, remote)
- Base URL
- Model
- API key
- Custom headers (for APIs like Google Gemini)
View Current Config
/config
Full Config File
Edit config.json at the path above directly, or use /reconfigure for the interactive flow. Everything is optional.
{
"provider": {
"active": "remote",
"ollama": {
"baseURL": "http://127.0.0.1:11434/api",
"model": "qwen3.6:latest"
},
"llama.cpp": {
"baseURL": "http://127.0.0.1:8080/v1",
"model": "unsloth/Qwen3.6-35B-A3B"
},
"remote": {
"baseURL": "https://api.openai.com/v1",
"model": "gpt-4o-mini",
"apiKey": "",
"headers": { "X-Trace": "${TRACE_ID}" }
}
},
"systemPrompt": "Always reply in haiku.",
"systemPromptMode": "append",
"security": {
"shell": {
"requireApproval": true,
"allowedCommands": ["^git status$", "^npm test$"],
"deniedCommands": ["^curl\\s"],
"timeoutMs": 60000
},
"mcpRequireApproval": false,
"fetch": {
"allowPrivateNetwork": false,
"requireApproval": false
}
},
"codemap": {
"concurrency": 3
},
"context": {
"maxTokens": 8192,
"compactThreshold": 0.75,
"autoCompact": true
},
"ui": {
"theme": "default"
},
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
}
}
}
${ENV_VAR} references inside any provider field, MCP env, or MCP headers are expanded at startup. Bump codemap.concurrency if you're driving a fast remote API — a local Ollama is fine at the default 3.
Context window
context.maxTokens should match what your model server is actually serving — --ctx-size for llama-server, num_ctx for Ollama. bonfire estimates how much of that window the conversation is using, shows it in the status bar, and compacts history automatically once usage crosses compactThreshold. Compaction is mechanical, not model-driven: it shortens old tool output first, then drops whole turns from the top, always cutting at a user message so a tool result never loses the call that produced it. No extra model round-trip, no new failure mode. Set autoCompact: false if you'd rather run /compact by hand.
Themes
ui.theme picks one of four presets — default (blue accent, cyan tools), ember (magenta, warmer), forest (green), mono (grayscale except errors). Switch at any time with /theme <name>: it applies immediately and is written back to your global config.
Every preset uses only the 16 ANSI colour names, never hex, so bonfire inherits your terminal's palette instead of fighting it — a solarized or gruvbox terminal gets a solarized or gruvbox bonfire for free. Semantic colours stay put across presets: green means good and red means destructive in all of them, because an approval prompt is the wrong place to get decorative.
Outbound fetches
fetch_url refuses private, loopback, link-local and cloud-metadata addresses by default, and re-checks every redirect hop — a public hostname pointing at 127.0.0.1 or 169.254.169.254 is caught because the guard resolves DNS rather than pattern-matching the URL. Set security.fetch.allowPrivateNetwork: true if you genuinely need to reach a service on your LAN, and requireApproval: true to be prompted before every fetch.
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
BONFIRE_PROVIDER | ollama | ollama, llama.cpp, or remote |
BONFIRE_MODEL | provider-specific | Model id (Ollama tag, or label for llama.cpp) |
OLLAMA_BASE_URL | http://127.0.0.1:11434/api | Ollama host |
LLAMACPP_BASE_URL | http://127.0.0.1:8080/v1 | llama-server endpoint |
LLAMACPP_API_KEY | — | Optional bearer token |
REMOTE_BASE_URL | https://api.openai.com/v1 | Remote API endpoint |
REMOTE_API_KEY | — | Remote API key |
BONFIRE_DEBUG | — | 1 to log every HTTP request (auth headers redacted) |
BONFIRE_DISABLE_BUILTINS | — | 1 to run with only MCP tools |
Customise it: system prompt + skills
bonfire reads two kinds of markdown from your home directory and your repo, with no config at all:
# Global (OS-specific location - see below)
~/.config/bonfire/system.md # Linux/macOS
~/.config/bonfire/skills/<name>.md # Linux/macOS
%APPDATA%/bonfire/system.md # Windows
%APPDATA%/bonfire/skills/<name>.md # Windows
# Project (in your repo)
.bonfire/system.md # project system prompt
.bonfire/skills/<name>.md # project skills
System prompts are appended to bonfire's built-in prompt. Use them to teach the model about conventions that aren't obvious from the code:
mkdir -p .bonfire
cat > .bonfire/system.md <<'EOF'
This project uses Bun. Prefer `bun test` over `npm test`.
Treat src/legacy/ as read-only — never edit those files.
EOF
Run /system inside bonfire to see the assembled prompt.
Set "systemPromptMode": "replace" in config if you want to fully replace the built-in.
Skills are markdown files the model loads on demand. Each has YAML frontmatter and a body:
---
name: api-endpoint
description: Add a new HTTP endpoint following project conventions
---
To add a new endpoint:
1. Add the route to `src/server/routes.ts`.
2. Add a handler in `src/handlers/`.
3. Add a contract test in `tests/api/`.
4. Run `bun test` to confirm.
At startup bonfire scans both skill directories and tells the model: "these skills exist, here's their description, call load_skill(name) if a request matches." The body only enters the context when the model asks for it — so you can ship 50 skills without bloating every prompt.
Type these inside the bonfire TUI:
/init scaffold .bonfire/system.md + .bonfire/skills/example.md
/skills list available skills
/skills show api-endpoint print a skill's body
/skills reload re-scan after editing
Slash commands
| Command | Effect |
|---|---|
/help | Show every command (or type / for autocomplete) |
/init | Scaffold .bonfire/system.md + an example skill |
/system | Show the effective system prompt (base + overrides + skills) |
/skills | List available skills |
/skills show <name> | Print a skill body |
/skills reload | Re-scan skill directories |
/codemap | Codemap stats — files, dirs, summarized count |
/codemap build | Summarize every file (slow once, then cached) |
/codemap rebuild | Wipe cache and re-summarize from scratch |
/sessions | List saved sessions |
/sessions new | Start a fresh conversation |
/sessions load <id> | Resume a session — full ID or any unique prefix |
/sessions save | Force-save the active session |
/sessions delete <id> | Remove a session — full ID or any unique prefix |
/context | Show context-window usage for the live conversation |
/compact | Reclaim context now — trims old tool output, then drops old turns |
/tools | List every tool the model can call, built-in and MCP |
/theme | List colour themes |
/theme <name> | Switch theme — applies instantly, saved to your config |
/dirs | Show the filesystem allowlist |
/add-dir <path> | Grant access to a sibling project |
/config | Show current configuration |
/reconfigure | Interactive reconfiguration TUI |
/exit, /quit, esc | Leave |
During an approval prompt: y = yes, n = no, a = always for the rest of the session. a works for shell commands, file edits and MCP tool calls alike; /sessions new clears everything you've remembered.
Tip: type / at any time to see a dropdown of every command — Tab completes, ↑/↓ moves the selection, Enter executes selected command.
Other things bonfire does well
Diff-preview approvals. Every write_file and edit_file shows a coloured unified diff and waits for y / n before touching disk. Same for shell — every command needs your green-light, with a for "always for this session." A hardcoded refusal list catches rm -rf /, fork bombs, mkfs, and friends even after approval.
On the deny-list. It is a backstop, not a sandbox. Pattern matching on a command string cannot be complete — the same destructive action can be spelled with variable expansion, a base64 pipe, an alias, or a script file, and the list will not see it. The approval prompt is the real control: read the command before you press
y, and treata("always") as a decision you are making for the rest of the session. If you need a genuine boundary, run bonfire against a model you trust in a container or VM.
Streaming everything. Text and tool arguments stream as they're generated. No 30-second blank stares.
Multiline prompts. Shift+Enter (modern terminals — kitty, Ghostty, WezTerm, recent iTerm2), Alt+Enter, or Ctrl+J inserts a newline. Plain Enter submits.
Smart paste. Bracketed paste mode is enabled at startup, so multi-line pastes never submit mid-block. A huge paste — multi-line, or longer than ~200 chars — is redacted in the input as [Pasted #1 (35 lines, 1234 chars)] rather than flooding the prompt with the literal content. The real text is held aside and substituted back in when you hit Enter.
MCP-native. Plug in any Model Context Protocol server — stdio or Streamable HTTP. Each server's tools land namespaced as <server>__<tool>.
Custom system prompt. Drop a ~/.config/bonfire/system.md (Linux/macOS) or %APPDATA%/bonfire/system.md (Windows) for a global override, or .bonfire/system.md for a project-level one. Appended to the built-in prompt by default; flip systemPromptMode: "replace" for full control.
/tools. See exactly what the model has access to, built-ins and MCP alike.
Status line that knows what it's doing. "Skimming bytes…" while it reads. "Pacing the codemap…" while it navigates. "Summoning the shell…" while it runs a command. Beats staring at "thinking…" for the 20th time.
Context meter. The status line shows how much of the model's window the conversation is using. Cross the threshold and bonfire compacts history itself, telling you what it dropped — a long session on an 8K local model no longer ends in a wall of provider errors.
Filesystem allowlist. All file tools refuse paths outside the allowed set. Symlinks inside an allowed dir can't escape it (realpath-checked). /add-dir <path> to extend.
Lightweight. A single minified Node CLI bundle (~65 KB). No Docker, no Electron, no daemons. Runtime deps are installed by npm alongside it.
Built-in tools
| Tool | What it does |
|---|---|
navigate | Walk the codemap with one-line summaries |
search | Regex search across the repo — gitignore-aware, skips binaries, same on every OS |
read_file | Read a file from an allowed directory — large files truncate; offset/limit page by line |
write_file | Create / overwrite — diff-approval gated |
edit_file | Exact-string replace — diff-approval gated |
list_dir | Directory listing, capped at 1000 entries |
shell | Run a shell command — approval gated, deny-list applied |
fetch_url | Fetch an http(s) URL — SSRF-guarded, HTML stripped to text, 200 KB cap, 30 s timeout |
load_skill | Load a named skill's full instructions on demand |
MCP servers
bonfire speaks both stdio (local processes) and Streamable HTTP (remote servers, per the MCP spec). ${ENV_VAR} references are expanded at startup.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }
},
"remote-docs": {
"type": "streamable-http",
"url": "https://mcp.company.com/docs",
"headers": { "Authorization": "Bearer ${DOCS_MCP_TOKEN}" }
}
}
}
Tools land namespaced as <server>__<tool>. A failing server doesn't block the others. On Windows, bare commands like npx / uvx are auto-resolved to their .cmd shims.
Models that work
Anything trained for tool calling:
| Model | Tool calling | Notes |
|---|---|---|
qwen3.6:latest | yes | Recommended. Strong tool calling, good code reasoning. |
earlier qwen / qwen3-coder | yes | Solid fallback if you can't pull 3.6 |
llama3.3 / llama3.1 | yes | Good general coding |
hermes3:8b | yes | Fast, lightweight |
llama3-groq-tool-use:8b | yes | Specifically tuned for tool calling |
deepseek-coder-v2 | varies | Check the specific revision |
gemma3 / gemma2 | no | Not tool-tuned |
gpt-4o-mini (remote) | yes | Cloud option |
gemini-2.0-flash-exp (remote) | yes | Google Gemini via OpenAI compat |
For llama.cpp, tool calling requires llama-server --jinja.
Roadmap
- Codemap navigation (BFS walk, real
.gitignoreglobs, symlink-safe) - Session persistence + prefix lookup
- Slash-command autocomplete
- Multiline input (Shift+Enter on modern terminals; Alt+Enter / Ctrl+J everywhere) + paste redaction
- Shell approval + hardcoded deny-list + configurable timeout
- Skills support — drop-in
~/.config/bonfire/skills/<name>.md(Linux/macOS) /%APPDATA%/bonfire/skills/<name>.md(Windows) - Configurable system prompt —
~/.config/bonfire/system.md/%APPDATA%/bonfire/system.md+ project override - OS config directory (not project-level)
- Interactive reconfigure TUI (
/reconfigure) handling first-run + remote provider - Remote provider (any OpenAI-compatible API)
- Custom headers +
${ENV}expansion across provider config - Configurable codemap concurrency + mid-build checkpointing
- Project-level
bonfire.config.jsonlayered over the global config - Context-window budgeting + automatic compaction (
/context,/compact) - Native cross-platform
searchtool (no shelling out to grep/findstr) - SSRF guard on
fetch_url, redirect hops included - Retry with backoff on transient provider failures
- Editable diffs (
y/n/eopens the patch in$EDITOR) - Side-by-side model race mode
- Local RAG tool via a small embedding model
- First-run benchmark (tokens/sec per installed model)
- Auto-verify loop — typecheck/test after each edit, feed failures back
Contributing
Issues and small PRs welcome. The codebase is intentionally minimal — favour clarity over cleverness.
src/
config.ts # layered loader: defaults + OS config dir + project bonfire.config.json
agent/
index.ts # runAgent, initMcp, listTools
context.ts # token budgeting + mechanical history compaction
retry.ts # backoff for transient provider failures
provider.ts # lazy provider, debug fetch with header redaction
stream.ts # typed normalizer for AI SDK fullStream events
system-prompt.ts # 3-layer prompt: built-in + global bonfire dir + .bonfire/system.md
error-format.ts # single source of truth for provider error messages
tools/
safe-path.ts # realpath-aware allowlist (closes symlink escapes)
approval.ts # tri-state yes/no/always handler
remembered.ts # session-scoped "always allow" store, shared by every gate
shell-policy.ts # hardcoded deny-list + per-config allow/deny patterns
net-guard.ts # SSRF guard: resolves hostnames, blocks private ranges
search-tool.ts # gitignore-aware regex search
file-tools.ts, shell-tool.ts, navigate-tool.ts, fetch-tool.ts
mcp/
index.ts, stdio.ts, http.ts, windows.ts
session/
index.ts, storage.ts, meta.ts
skills/
loader.ts # scans global bonfire dir + .bonfire/skills, parses frontmatter
tool.ts # load_skill tool — model calls this on demand
codemap/
walk.ts # iterative BFS, lstat-based, builds skeleton tree
summarize.ts # bottom-up LLM pass, retry-on-fail, per-ext sampling
store.ts # JSON cache + in-process memoization
ignore.ts # gitignore-pkg-backed matcher + curated defaults
index.ts, types.ts
providers/
index.ts, ollama.ts, llamacpp.ts, remote.ts, types.ts
cli/
bin.tsx # entry: TTY check, MCP boot, banner, render <App firstRun=…/>, signals
App.tsx # single-column layout + first-run hand-off into <ReconfigurePrompt/>
theme.ts # presets + live theme object — the one place colour is decided
components/ # Banner, Transcript, ToolLine, ResultLine, DiffPreview,
# ApprovalPrompt, ModifiedFilesPanel, StatusBar,
# MultilineInput, CommandSuggestions, PromptBar,
# ReconfigurePrompt (handles both first-run + /reconfigure)
hooks/ # useAgentStream, useApproval, useProvider,
# useThinkingPhrase, useElapsed
commands/ # slash-command registry
License
MIT