Quick start¶
CallBus needs an embedding model to rank tools: its backbone, Qwen3-Embedding-8B trained further
on tool retrieval, served by vLLM on a GPU with at least 16 GB of memory (without one,
it runs as a GGUF in Ollama), or any OpenAI-compatible
/v1/embeddings endpoint serving it. The
Docker path starts both; the pip path uses an endpoint you already run.
With Docker (one GPU host)¶
git clone https://github.com/yasinyaman/callbus && cd callbus/deploy/docker
cp .env.example .env # set CALLBUS_API_KEY
List the MCP servers you want to put behind callbus in callbus.json (the same format as an MCP
client's config file). It starts with one:
{
"mcpServers": {
"time": {"command": "uvx", "args": ["mcp-server-time"]}
}
}
Index them, then start the stack:
docker compose run --rm callbus ingest mcp --config /config/callbus.json --out /data
docker compose up -d
The first start downloads the model (16 GB). CallBus then listens on 127.0.0.1:8765: MCP at
/mcp, REST at /v1, both behind the key from .env.
curl -s -H "Authorization: Bearer $CALLBUS_API_KEY" \
-d '{"query": "what time is it in Tokyo?"}' http://127.0.0.1:8765/v1/search
Docker covers both images, OpenAPI specs, GPU memory and running behind a proxy.
With pip¶
pip install "callbus[mcp]" # add ,openapi for YAML specs; ,stem for a stemmed BM25 fallback
Serve the backbone on a GPU host, as callbus-emb-v0.2 on port 8091 (the default callbus uses;
--quantization fp8 halves its memory, served then as callbus-emb-v0.2-fp8):
vllm serve yasinyaman/callbus-emb-8b --revision v0.2 --served-model-name callbus-emb-v0.2 \
--runner pooling --max-model-len 8192 --port 8091 \
--no-enable-chunked-prefill --max-num-batched-tokens 8192
(Without the last two flags, a long tool text batched next to short ones can hang a pooling engine.)
With the base Qwen/Qwen3-Embedding-8B served as qwen3-emb instead, callbus heads pull fetches
the heads that go with it (60 MB) and --emb-model qwen3-emb selects it.
On another machine, point callbus at it with --emb-url http://HOST:8091/v1 or
CALLBUS_EMB_URL; an endpoint that wants a key gets CALLBUS_EMB_API_KEY (callbus sends
OPENAI_API_KEY only to api.openai.com). Then index your tools and search them:
callbus ingest mcp --server time="uvx mcp-server-time" --out tools/
callbus ingest openapi https://petstore3.swagger.io/api/v3/openapi.json --name petstore --out tools/
callbus search --data tools/ "list the pets tagged dog"
And serve them to agents:
callbus serve --data tools/ --config callbus.json # MCP at http://127.0.0.1:8765/mcp
--config tells serve how to reach the MCP servers when an agent calls one of their tools; the
catalogue itself comes from ingest. See Serve them to agents.
Connect an agent¶
Any MCP client can use callbus as one server:
claude mcp add --transport http callbus http://127.0.0.1:8765/mcp \
--header "Authorization: Bearer $CALLBUS_API_KEY"
Desktop clients start servers themselves, so serve runs with --stdio; every path must be
absolute (Claude Desktop starts servers in /):
{
"mcpServers": {
"callbus": {
"command": "/path/to/venv/bin/callbus",
"args": ["serve", "--stdio", "--data", "/path/to/tools",
"--config", "/path/to/callbus.json", "--emb-url", "http://127.0.0.1:8091/v1"]
}
}
}
Platforms that run tools themselves call POST /v1/search and then POST /v1/call (or run
the tool on their side); see the REST API.
The agent sees two tools, search_tools and call_tool, whatever the size of the catalogue.