A C++ terminal chat application that connects to llama.cpp and an MCP server for tool-assisted conversations.
The recommended way to run llm-chat is Docker — no compilers or dev headers needed on your machine.
# 1. Build the image
docker build -t llm-chat .
# 2. Configure MCP servers
cp mcp.example.json mcp.json # edit as needed
# 3. Run
docker compose run --rm llm-chatllama.cpp must be running on the host (llama-server -m <model.gguf> --port 8080). The container reaches host services via host.docker.internal, which is already configured in docker-compose.yml.
docker build -t llm-chat .The image is a multi-stage build on debian:bookworm-slim; the repo root is mounted read-only at /app so the app picks up your mcp.json.
docker run -it --rm \
-e LLM_CHAT_LLM_URL=http://host.docker.internal:8080 \
-e LLM_CHAT_MCP_URL=http://host.docker.internal:8000/mcp \
-v "$PWD":/app:ro \
llm-chatdocker-compose.yml already sets the TTY, host.docker.internal, environment defaults and the repo mount:
docker compose run --rm llm-chatPass extra flags after the service name:
docker compose run --rm llm-chat --llm-url http://10.0.0.1:8080 --mcp-config /app/mcp.jsonOverride defaults via environment variables in your shell:
LLM_CHAT_MAX_CONTEXT=4096 docker compose run --rm llm-chatBuild from source only if you don't want Docker.
- C++17 compiler (g++ ≥ 9, clang ≥ 10, or MSVC 2019+)
- CMake ≥ 3.16
- libcurl (development headers)
- GNU Readline (development headers, Linux only)
- nlohmann/json (development headers)
- vcpkg (Windows only)
sudo apt install build-essential cmake libcurl4-openssl-dev libreadline-dev nlohmann-json3-devsudo pacman -S base-devel cmake curl readline nlohmann-jsonsudo dnf install gcc-c++ cmake libcurl-devel readline-devel nlohmann-json-develcmake -B build
cmake --build buildBinary: build/llm-chat
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=<vcpkg_root>\scripts\buildsystems\vcpkg.cmake
cmake --build build --config ReleaseBinary: build\Release\llm-chat.exe
Dependencies (
curl,nlohmann-json) are installed automatically via vcpkg manifest (vcpkg.json).
On the host machine:
llama-server -m <model.gguf> --port 8080Create a mcp.json file in the working directory (in Docker: the repo root, mounted at /app/mcp.json):
{
"mcpServers": {
"exa-search": {
"type": "http",
"url": "https://mcp.exa.ai/mcp"
},
"local-tools": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}Docker note: URLs in
mcp.jsonare resolved from inside the container. Usehost.docker.internalinstead oflocalhostfor services running on the host (e.g.http://host.docker.internal:8000/mcp).
Docker:
docker compose run --rm llm-chatNative:
./build/llm-chatIf MCP connection fails at startup, the app continues without tools. Use /mcp connect later to retry.
| Option | Description |
|---|---|
--llm-url URL |
llama.cpp server address |
--mcp-url URL |
MCP server address (overrides mcp.json) |
--mcp-config PATH |
Path to mcp.json configuration file |
--help |
Show usage |
docker compose run --rm llm-chat --llm-url http://10.0.0.1:8080 --mcp-config /app/mcp.jsonNative equivalent:
./build/llm-chat --llm-url http://10.0.0.1:8080 --mcp-config ./mcp.json| Variable | Default | Description |
|---|---|---|
LLM_CHAT_LLM_URL |
http://localhost:8080 |
llama.cpp server address |
LLM_CHAT_MCP_URL |
http://localhost:8000/mcp |
MCP server address |
LLM_CHAT_MAX_CONTEXT |
8192 |
Max context token count (for percentage display) |
In Docker Compose the URL defaults point at host.docker.internal instead of localhost (see docker-compose.yml); override them the same way:
export LLM_CHAT_LLM_URL="http://host.docker.internal:8080"
export LLM_CHAT_MCP_URL="http://host.docker.internal:8000/mcp"
export LLM_CHAT_MAX_CONTEXT=4096
docker compose run --rm llm-chatNative:
export LLM_CHAT_LLM_URL="http://10.0.0.1:8080"
export LLM_CHAT_MCP_URL="http://10.0.0.1:8000/mcp"
export LLM_CHAT_MAX_CONTEXT=4096
./build/llm-chatNote: If the llama.cpp server provides
n_ctx(ormax_context_length) in the/v1/modelsendpoint, it overridesLLM_CHAT_MAX_CONTEXT.
| Command | Description |
|---|---|
/quit or /exit |
Exit |
/help |
Show command list |
/clear |
Clear chat history |
/tools |
List MCP tools |
/mcp |
Show MCP connection status |
/mcp connect [server] |
Connect to MCP server (all, or one by name) |
/mcp disconnect |
Disconnect from MCP server |
/read |
Add file to context |
/files |
List attached files |
/remove |
Remove attached file |
/clearfiles |
Remove all attached files |
/models |
List / switch / unload models |
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ llm-chat │─────▶│ llama.cpp │ │ MCP Server │
│ (container │ │ (host) │ │ (HTTP) │
│ or native) │ │ :8080 │ │ JSON-RPC │
│ │ │ /v1/chat/ │ │ tools/list │
│ │ │ completions │ │ tools/call │
│ │ └──────────────┘ └──────────────┘
│ │ host.docker.internal Supports SSE
│ │ mcp.json and JSON
│ │ auto-discovery
└─────────────┘
- App searches for
mcp.jsonin exe dir, then working dir - User message → llama.cpp API (with tool definitions)
- If model returns
tool_calls→ execute via MCP - Tool result sent back to model
- Model final response displayed to user
- Stats shown after each response (tokens, time, t/s, context %)
The app reads mcp.json from (in order):
- Same directory as the executable
- Current working directory (in Docker:
/app, i.e. the repo root)
You can also specify a custom path with --mcp-config.
{
"mcpServers": {
"server-name": {
"type": "http",
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer token"
},
"enabled": true
}
}
}| Field | Required | Description |
|---|---|---|
type |
Yes | Must be "http" |
url |
Yes | MCP server endpoint URL |
headers |
No | Additional HTTP headers |
enabled |
No | Set false to skip (default: true) |