Skip to content
Conduit

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.

Praneethtkonda/bonfirecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • Node.js 20 or newer
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "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, 2026

bonfire

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 --jinja flag 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:

  1. Built-in defaults
  2. Global config — ~/.config/bonfire/config.json (Linux/macOS) or %APPDATA%/bonfire/config.json (Windows)
  3. Project config — bonfire.config.json in 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

VariableDefaultPurpose
BONFIRE_PROVIDERollamaollama, llama.cpp, or remote
BONFIRE_MODELprovider-specificModel id (Ollama tag, or label for llama.cpp)
OLLAMA_BASE_URLhttp://127.0.0.1:11434/apiOllama host
LLAMACPP_BASE_URLhttp://127.0.0.1:8080/v1llama-server endpoint
LLAMACPP_API_KEY—Optional bearer token
REMOTE_BASE_URLhttps://api.openai.com/v1Remote 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

CommandEffect
/helpShow every command (or type / for autocomplete)
/initScaffold .bonfire/system.md + an example skill
/systemShow the effective system prompt (base + overrides + skills)
/skillsList available skills
/skills show <name>Print a skill body
/skills reloadRe-scan skill directories
/codemapCodemap stats — files, dirs, summarized count
/codemap buildSummarize every file (slow once, then cached)
/codemap rebuildWipe cache and re-summarize from scratch
/sessionsList saved sessions
/sessions newStart a fresh conversation
/sessions load <id>Resume a session — full ID or any unique prefix
/sessions saveForce-save the active session
/sessions delete <id>Remove a session — full ID or any unique prefix
/contextShow context-window usage for the live conversation
/compactReclaim context now — trims old tool output, then drops old turns
/toolsList every tool the model can call, built-in and MCP
/themeList colour themes
/theme <name>Switch theme — applies instantly, saved to your config
/dirsShow the filesystem allowlist
/add-dir <path>Grant access to a sibling project
/configShow current configuration
/reconfigureInteractive reconfiguration TUI
/exit, /quit, escLeave

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 treat a ("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

ToolWhat it does
navigateWalk the codemap with one-line summaries
searchRegex search across the repo — gitignore-aware, skips binaries, same on every OS
read_fileRead a file from an allowed directory — large files truncate; offset/limit page by line
write_fileCreate / overwrite — diff-approval gated
edit_fileExact-string replace — diff-approval gated
list_dirDirectory listing, capped at 1000 entries
shellRun a shell command — approval gated, deny-list applied
fetch_urlFetch an http(s) URL — SSRF-guarded, HTML stripped to text, 200 KB cap, 30 s timeout
load_skillLoad 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:

ModelTool callingNotes
qwen3.6:latestyesRecommended. Strong tool calling, good code reasoning.
earlier qwen / qwen3-coderyesSolid fallback if you can't pull 3.6
llama3.3 / llama3.1yesGood general coding
hermes3:8byesFast, lightweight
llama3-groq-tool-use:8byesSpecifically tuned for tool calling
deepseek-coder-v2variesCheck the specific revision
gemma3 / gemma2noNot tool-tuned
gpt-4o-mini (remote)yesCloud option
gemini-2.0-flash-exp (remote)yesGoogle Gemini via OpenAI compat

For llama.cpp, tool calling requires llama-server --jinja.


Roadmap

  • Codemap navigation (BFS walk, real .gitignore globs, 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.json layered over the global config
  • Context-window budgeting + automatic compaction (/context, /compact)
  • Native cross-platform search tool (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 / e opens 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

↑↓ navigate openesc close