
Write HTML. Render video. Built for agents.
LLMQore wraps multiple LLM providers behind one Qt API, so you can ask for a completion, stream deltas, pass tool calls, and keep conversation history in one place. It also includes MCP client and server support, plus an ACP host path for running Claude Code or Codex as external agents.
Builders who are adding LLM providers, tool use, or MCP connectivity to a Qt application.
You can build one Qt integration that works across hosted models, local models, MCP tools, and agent hosts.
Provides client classes for Claude, OpenAI Chat Completions, OpenAI Responses, Google AI, Ollama, Mistral, DeepSeek, Qwen, and llama.cpp.
Emits answer deltas as Qt signals on the client thread and returns async results through `QFuture`.
Lets you register `BaseTool` subclasses, runs tool requests, and sends results back through the model loop.
Keeps one conversation history and serializes it into each provider's message shape.
Connects to MCP servers over stdio, Streamable HTTP, or legacy SSE, and can serve the same tool registry back out.
Re-exposes tools from several upstream MCP servers behind one HTTP/SSE or stdio endpoint.
Launches Claude Code or Codex over stdio, streams session updates, and handles permission and filesystem calls.
mcp-bridge bridge.json # HTTP endpoint mcp-bridge --stdio bridge.json # stdio
Qt/C++ library for cloud and local LLM providers, MCP clients and servers, and ACP agents.
Streaming deltas arrive as Qt signals on the object's own thread, async results as
QFuture, ownership follows QObject parent-child. Links against Core, Network and
Concurrent.
BaseTool subclass, tool loop included, gated by QFuture<bool> if you wantmcp-bridge CLI puts many upstream servers behind one endpointAsk and await the answer:
auto *client = new LLMQore::ClaudeClient(
"https://api.anthropic.com", apiKey, "claude-sonnet-4-5", this);
client->askOnce("What is Qt?").then(this, [this](const LLMQore::CompletionInfo &result) {
m_view->setPlainText(result.fullText);
});
Or watch it arrive, which is what you want in a chat panel:
connect(client, &LLMQore::BaseClient::accumulatedReceived,
this, [this](const LLMQore::RequestID &, const QString &answer) {
m_view->setPlainText(answer);
});
client->ask("What is Qt?");
accumulatedReceived carries the whole answer so far; chunkReceived carries only the new
delta. Both are emitted on the client's own thread, so a direct connection into a widget or
model is safe.
Expose a function over data the provider cannot see — the open document, the current selection, a local database:
client->tools()->addTool(new SearchCurrentFileTool(client));
conversation.addUser("Where do we handle the timeout?");
client->ask(conversation);
The client drives the loop: the model requests the tool, executeAsync runs, the result is
sent back, the model answers. setMaxToolContinuations() bounds it, ten rounds by default.
toolStarted and toolResultReady report progress; setExecutionGate() gates each call
behind a QFuture<bool>.
Tools from an MCP server enter the same registry and reach the model through the same tool-definition array:
client->tools()->addMcpServer({.name = "filesystem", .command = "npx",
.arguments = {"-y", "@modelcontextprotocol/server-filesystem", "/home/user"}});
client->tools()->loadMcpServers(QJsonDocument::fromJson(configData).object());
loadMcpServers reads the mcpServers object Claude Desktop uses and returns how many
servers it registered:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"]
}
}
}
McpServer::setToolRegistry takes the same ToolsManager the client uses, so one
registration serves both the in-process model and any MCP client that connects:
auto *server = new LLMQore::Mcp::McpServer(
new LLMQore::Mcp::McpHttpServerTransport({.port = 8080, .path = "/mcp"}, this),
cfg, this);
server->setToolRegistry(client->tools());
server->start();
The registry is shared, not copied: a tool added later reaches both sides, and the server
forwards toolsChanged as notifications/tools/list_changed.
Use HTTP inside a running application. McpStdioServerTransport takes over the process's
stdin and stdout, which only works when the process is nothing but an MCP server.
mcp-bridge is a CLI built on the same client and server. It connects to several upstream
MCP servers and re-exposes their tools behind one HTTP/SSE endpoint or one stdio server,
for when the upstreams and the client disagree on transport.
mcp-bridge bridge.json # HTTP endpoint
mcp-bridge --stdio bridge.json # stdio
Prebuilt binaries with the Qt runtime bundled are on Releases.
OllamaClient and LlamaCppClient derive from the same BaseClient as the hosted
providers and accept an empty API key:
auto *client = new LLMQore::OllamaClient("http://localhost:11434", {}, "llama3", this);
The conversation, the tools and the signals are the same; only the constructor differs.
LLMQore::Conversation conversation;
conversation.setSystem("Answer in one sentence.");
conversation.addUser("What is Qt?");
client->ask(conversation);
Providers disagree on nearly every name: messages against contents, assistant against
model, a top-level system field against a system message inside the array. One
serializeTurn per provider does the translation, and CompletionInfo::conversation
returns the history including the turns the model added during tool rounds.
A turn holds a list of content, so an image is another block in it:
conversation.addUser({
LLMQore::TextContent{"What does this chart show?"},
LLMQore::ImageContent::fromBytes(png, "image/png")});
The reverse direction: the agent owns the model and the tool loop, LLMQore is the host.
It launches Claude Code or Codex over stdio, streams session/update as Qt signals, and
answers the agent's session/request_permission, fs/* and terminal/* calls.
using namespace LLMQore::Acp;
AcpAgentRegistry registry;
registry.loadFromFile("agents.json");
auto *agent = new AcpClient(
registry.config("claude", QDir::currentPath())->createTransport(this), {}, this);
agent->setFileSystemProvider(new DefaultFileSystemProvider(this));
agent->setTerminalProvider(new TerminalManager(this));
connect(agent, &AcpClient::agentMessageChunk,
this, [](const QString &, const ContentBlock &c) { /* render c.text */ });
agent->connectAndInitialize(); // then newSession() -> prompt()
AcpAgentRegistry reads agents from JSON, overridable with LLMQORE_ACP_AGENTS. No API
key travels through the protocol — the agent authenticates itself.
example-chat is a Qt Quick application covering all eight providers,
MCP servers and an ACP agent. Build with -DLLMQORE_BUILD_EXAMPLES=ON.
| Provider | Client class | Streaming | Tools | Images in | Reasoning parsed | Reasoning replayed |
|---|---|---|---|---|---|---|
| Anthropic Claude | ClaudeClient | ✓ | ✓ | ✓ | ✓ | ✓ signature |
| OpenAI (Chat Completions) | OpenAIClient | ✓ | ✓ | ✓ | ✓ | ✓ when received |
| OpenAI (Responses API) | OpenAIResponsesClient | ✓ | ✓ | ✓ | ✓ | opt-in |
| Google AI | GoogleAIClient | ✓ | ✓ | ✓ | ✓ | ✓ thought signature |
| Ollama | OllamaClient | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mistral | MistralClient | ✓ | ✓ | ✓ | ✓ | ✓ when received |
| llama.cpp | LlamaCppClient | ✓ | ✓ | ✓ | ✓ | ✓ when received |
| DeepSeek | OpenAIClient | ✓ | ✓ | ✓ | ✓ | ✓ when received |
| Qwen (DashScope) | OpenAIClient | ✓ | ✓ | ✓ | ✓ | ✓ when received |
Reasoning parsed means thinking blocks reach you as signals. Reasoning replayed
means they go back into the next request in the form that provider requires — without which
some models reject a continuation that follows a tool call. The Responses API needs
store: false to make this work, so it is a switch rather than a default; see
LLM clients.
MCP is implemented for the 2025-11-25 spec over stdio and Streamable HTTP — server side: tools, resources, resource templates, prompts, completions, sampling, elicitation; client side: the same plus roots.
CI builds and tests Qt 6.8.3 and 6.10.2 on Linux, macOS and Windows, and Qt 5.15.2 on Linux. Versions inside the stated range but outside that matrix are expected to work and are not verified on every commit. The Qt Quick example is Qt 6 only.
bc1qndq7f0mpnlya48vk7kugvyqj5w89xrg4wzg68t0xA5e8c37c94b24e25F9f1f292a01AF55F03099D8Dltc1qlrxnk30s2pcjchzx4qrxvdjt5gzuervy5mv0vyTHdZrE7d6epW6ry98GA3MLXRjha1DjKtUxMIT — see LICENSE.
Sign in to join the discussion.
No comments yet. Be the first to say what this is good for.

Write HTML. Render video. Built for agents.
Ultra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps
SkillOpt is a text-space optimizer that trains reusable natural-language skills for frozen LLM agents through trajectory-driven edits, validation-gated updates, and deployable best_skill.md artifacts.

Omnigent is an open-source AI agent framework and meta-harness: orchestrate Claude Code, Codex, Cursor, Pi, and custom agents — swap harnesses without rewriting, enforce policies and sandboxing, and collaborate in real time from any device.
A theoretical reconstruction of the Claude Mythos architecture, built from first principles using the available research literature.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!