Modern C++20 SDK targeting CMake 3.20+. Library is RAII / move-only, backed by a libcurl easy handle per Client. Public surface is throwing; exception hierarchy under clawdforge::Error covers AuthError, APIError (carries status_code + body), TransportError, and ProtocolError. Dependencies: libcurl + nlohmann/json (FetchContent or find_package). Tests use cpp-httplib's in-process server + doctest. 12 test cases / 70 assertions cover healthz, run with JSON / text / 502 / files, multipart upload, full token CRUD, transport failure, URL normalization, and bad-input rejection. Clean under -Wall -Wextra -Wpedantic -Werror, ASan + UBSan clean (no leaks, no UB). upload_file streams via curl_mime_filedata — no in-memory buffering. Install path produces clawdforge::clawdforge target consumable via target_link_libraries; FetchContent path mirrors the existing Rust / Go SDK ergonomics. MIT licensed. |
||
|---|---|---|
| .. | ||
| cmake | ||
| examples | ||
| include/clawdforge | ||
| src | ||
| tests | ||
| .gitignore | ||
| CMakeLists.txt | ||
| README.md | ||
clawdforge — C++ SDK
Modern C++ client for the clawdforge
HTTP API. Wraps claude -p subprocess calls behind a bearer-token-gated REST
service.
- C++17 minimum (C++20 lets you use designated initializers in examples).
- libcurl + nlohmann/json. No Boost.
- RAII, move-only
Client(one libcurl handle per instance). - Throwing API; full exception hierarchy under
clawdforge::Error.
Install
Option A — FetchContent (drop-in for existing CMake projects)
include(FetchContent)
FetchContent_Declare(clawdforge
GIT_REPOSITORY https://github.com/Sulkta-Coop/clawdforge.git
GIT_TAG main
SOURCE_SUBDIR clients/cpp
)
FetchContent_MakeAvailable(clawdforge)
target_link_libraries(my_app PRIVATE clawdforge::clawdforge)
CURL must be available via find_package(CURL REQUIRED). On Debian/Ubuntu:
sudo apt install libcurl4-openssl-dev.
Option B — install + find_package
git clone https://github.com/Sulkta-Coop/clawdforge.git
cd clawdforge/clients/cpp
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
sudo cmake --install build
Then in your project:
find_package(clawdforge CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE clawdforge::clawdforge)
Quickstart
#include <clawdforge/client.hpp>
#include <iostream>
namespace cf = clawdforge;
int main() {
cf::Client client{cf::ClientOptions{
.base_url = "http://localhost:8800",
.token = "cf_...",
}};
auto h = client.healthz();
std::cout << "claude_version=" << h.claude_version << "\n";
auto r = client.run(cf::RunRequest{
.prompt = R"(Reply with JSON: {"hello":"world"})",
.model = "sonnet",
.timeout_secs = 60,
});
if (auto* j = std::get_if<nlohmann::json>(&r.result)) {
std::cout << j->at("hello").get<std::string>() << "\n";
} else if (auto* s = std::get_if<std::string>(&r.result)) {
std::cout << *s << "\n";
}
}
API
clawdforge::Client (in <clawdforge/client.hpp>):
| Method | HTTP | Returns |
|---|---|---|
healthz() |
GET /healthz |
HealthzResponse |
run(req) |
POST /run |
RunResult |
upload_file(path, ttl_secs=0) |
POST /files |
FileToken |
create_token(req) |
POST /admin/tokens |
AppToken |
list_tokens() |
GET /admin/tokens |
std::vector<AppTokenInfo> |
revoke_token(name) |
DELETE /admin/tokens/<name> |
void |
All methods throw on failure. ClientOptions lets you set base_url, token,
admin_token, timeout, connect_timeout, user_agent, and insecure_tls.
Errors
clawdforge::Error // abstract base, derives from std::runtime_error
├── clawdforge::AuthError // 401 / 403, or missing token on the client
├── clawdforge::APIError // any other non-2xx — has status_code() + body()
├── clawdforge::TransportError // libcurl-level failures
└── clawdforge::ProtocolError // bad URL, missing fields, malformed JSON
Catch broadly:
try {
client.run(req);
} catch (const cf::AuthError& e) { /* refresh / abort */ }
catch (const cf::APIError& e) { std::cerr << e.status_code() << " " << e.body(); }
catch (const cf::TransportError& e) { /* retry */ }
catch (const cf::Error& e) { /* anything else */ }
POST /run returns HTTP 502 on subprocess failure. Surfaced as APIError
with status_code() == 502; body() parses as the documented RunFailure
shape.
File uploads
auto ft = client.upload_file("/path/to/recipe.png", /*ttl_secs=*/3600);
auto res = client.run(cf::RunRequest{
.prompt = "extract recipe data",
.files = {ft.file_token},
});
The file is streamed off disk via libcurl's curl_mime_filedata — no
in-memory buffering of the payload.
Threading
Client is move-only and not thread-safe. Construct one per worker thread
or wrap external accesses in a mutex. Multiple Client instances share the
process-wide libcurl global state safely (refcounted internally).
Build options
| Option | Default | Effect |
|---|---|---|
CLAWDFORGE_BUILD_TESTS |
ON (top-level), OFF (subdirectory) |
doctest-based suite |
CLAWDFORGE_BUILD_EXAMPLES |
ON (top-level), OFF (subdirectory) |
builds clawdforge_basic_example |
CLAWDFORGE_WARNINGS_AS_ERRORS |
ON |
-Werror / /WX on the library target |
Development
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure
The test suite spins up a cpp-httplib mock server in-process — no real
clawdforge needed.
License
MIT. See LICENSE at the repo root.