llm_invoke_cpp is a header-only C++ library for exposing native C++ functions as LLM-callable tools.
It is split into eight public modules:
include/func_registry: dependency-free function registration, concise function summaries, and runtime dispatch.include/tool_meta: optional tool-facing metadata and tool spec export helpers built on top of the registry.include/type_meta: optional enum/schema/type-introspection helpers used by tool export and JSON adaptation.include/json_invoke: JSON-based invocation on top of the registry, intended for LLM and agent integrations.include/json_session_invoke: higher-level session/runtime APIs built on top ofjson_invokefor stateful create/call/destroy flows.include/task_scheduler: task classification and scheduling helpers built on top ofjson_session_invoke.include/runtime: protocol-neutral runtime facade built on top oftask_schedulerfor normalized tool listing and unary invocation.include/mcp: MCP-specific adapters built on top ofruntimefor protocol-facing transports such as stdio.
Project layout
include/func_registry/func_registry.hpp: core function registry entry point.include/func_registry/function_summary.hpp: human-readable function summary helpers for registry callables.include/tool_meta/tool_introspection.hpp: tool-facing metadata andToolSpecexport helpers.include/type_meta/type_schema.hpp: dependency-free structured type schema metadata and custom schema trait hook.include/type_meta/enum_traits.hpp: string enum mapping hook for human-friendly enum export and conversion.include/type_meta/type_introspection.hpp: optional type-level LLM/schema metadata helpers used by richer tool export.include/tools/trace_recorder.hpp: reusable helpers for collecting and serializing structured trace events.include/json_invoke/json_invoke.hpp: JSON invocation adapter for pure function registration and JSON-based calls.include/json_invoke/json_introspection.hpp: standalone JSON tool/spec/schema export helpers for registry metadata.include/json_invoke/json_error.hpp:JsonInvokeErrordefinition for request/response conversion and invocation failures.include/json_invoke/json_tool_execution_semantics.hpp: execution semantics enum and naming helpers for exported tool metadata.include/json_invoke/json_trace.hpp: tracing event model, request context helpers, and JSON serialization helpers.include/json_invoke/json_traits.hpp: trait hook for custom JSON bindings.include/json_session_invoke/json_session_invoke.hpp: session-oriented adapter that composesjson_invokeand exposes stateful factory APIs as the high-level entry point.include/json_session_invoke/session_objects.hpp: session object handles, object options, JSON/schema bindings, and in-memory object store support.include/task_scheduler/request_classifier.hpp: readonly request classification helpers that turn session tool metadata into scheduling categories and object keys.include/task_scheduler/task_scheduler_facade.hpp: recommended facade entry point for callers that want to submit one JSON request and let task_scheduler hide classification, queueing, and invocation details.include/task_scheduler/task_scheduler.hpp: scheduler-side task abstractions, including a minimalITaskSchedulerinterface and a keyed scheduler that enforces session-wide and per-object exclusivity.include/runtime/runtime_facade.hpp: protocol-neutral facade that projects exported tool schemas into normalized descriptors and turns scheduler-backed JSON requests into normalized invoke results.include/mcp/mcp_stdio_server.hpp: minimal MCP stdio server that handles JSON-RPC framing plusinitialize,ping,tools/list, andtools/callon top of the runtime facade.examples/func_registry/func_registry_demo.cpp: core-only registry example.examples/mcp_stdio/mcp_stdio_server_demo.cpp: standalone MCP stdio server process that exposes demo tools and can be launched directly by an MCP host.examples/json_invoke/json_invoke_demo.cpp: JSON invocation example.examples/json_stateful/json_stateful_demo.cpp: stateful object-handle example for create/call/destroy flows.examples/json_tracing/json_tracing_demo.cpp: tracing example for invoke and object lifecycle events.examples/trace_recorder/trace_recorder_demo.cpp: focusedVectorTraceRecorderexample that prints each call's response together with the recorded trace slice for that call.examples/task_scheduler/task_scheduler_demo.cpp: task scheduler example that prints classification results together with actual worker admission order for free-read, per-object, factory-lane, and barrier-aware tasks.examples/json_invoke/person.hpp: example-only domain type used by the JSON invocation demo.examples/json_invoke/person_support.hpp: example-only JSON bindings and helper functions forPerson.examples/json_invoke/priority_support.hpp: example-only enum mapping and incident-priority helper logic used by the JSON invocation demo.SCHEMA_TRAITS.md: focused guide for writingfunc_registry::schema_traits<T>specializations, including LLM-oriented generation rules.SCHEMA_TRAITS_PROMPT.md: reusable prompt templates for asking an LLM to generate or reviewschema_traits<T>code.ROADMAP.md: planned milestones for evolving the project.MEMO.md: prioritized capability memo for the LLM-to-C++ goal.
Quick core example
#include <func_registry/func_registry.hpp>
func_registry::FuncRegistryThreadSafe registry;
registry.registerFunction("sum", [](int a, int b) { return a + b; }, "Add two integers.");
int value = registry.callByNameWrap("sum", 2, 3);
for (const auto& line : registry.describeAllFunctions()) {
std::cout << line << std::endl;
}Quick LLM tool example
#include <json_invoke/json_invoke.hpp>
#include <json_invoke/json_traits.hpp>
struct Person {
std::string name;
int age;
};
template<>
struct json_invoke::json_traits<Person> {
static Person from_json_value(const nlohmann::json& value) {
return Person{value.at("name").get<std::string>(), value.at("age").get<int>()};
}
static nlohmann::json to_json_value(const Person& value) {
return {{"name", value.name}, {"age", value.age}};
}
};
json_invoke::JsonInvokeAdapterThreadSafe adapter;
adapter.registerFunction("get_person", json_invoke::readOnly([] { return Person{"Alice", 30}; }), "Return one person.");
std::cout << adapter.invoke({{"name", "get_person"}, {"args", json_invoke::json::array()}}).dump(2) << std::endl;Tracing demo
- Run
json_tracing_demoto inspectTraceSinkoutput for stateless success/failure, stateful create/destroy, and idle expiration. - Each emitted
TraceEventnow carries its ownrequest_id,timestamp, andduration_ms, so the demo prints the original event metadata rather than the sink's current wall clock at print time. - Run
trace_recorder_demowhen you want a quieter example that records events withjson_invoke::VectorTraceRecorderand prints each call together with that call's recorded trace JSON.
Integration
func_registrydepends only on the C++ standard library.json_invokedepends onnlohmann/jsonand the core registry module.json_session_invokedepends onjson_invokeand re-exports a higher-level session-oriented API.- The bundled
CMakeLists.txtfetchesnlohmann/jsonautomatically withFetchContent.
Minimal CMake integration
include(FetchContent)
FetchContent_Declare(
llm_invoke_cpp
GIT_REPOSITORY <your llm_invoke_cpp repository url>
GIT_TAG <commit-or-tag>
)
FetchContent_MakeAvailable(llm_invoke_cpp)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::func_registry)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::json_invoke)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::json_session_invoke)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::task_scheduler)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::runtime)
target_link_libraries(your_target PRIVATE llm_invoke_cpp::mcp)API notes
func_registry::FuncRegistryThreadSafe: register functions and invoke them by name when the registry may be shared across threads.func_registry::FuncRegistryUnsafe: explicit single-threaded variant for thread-confined ownership.func_registry::FunctionMetadata: attach descriptions and explicit parameter names.registerFunctionAs(...): register a callable under an explicit signature.describeFunction(name)/describeAllFunctions(): render concise human-readable C++ function summaries.- Include
tool_meta/tool_introspection.hppforgetToolSpec(name)/getAllToolSpecs(). json_invoke::JsonInvokeAdapterThreadSafe: accept JSON tool requests and return a conversion-friendly result wrapper for shared multi-threaded access.json_invoke::JsonInvokeAdapterUnsafe: explicit single-threaded variant for thread-confined ownership when every registration and invocation stays on one thread.json_invoke::JsonInvokeAdapterThreadSafe()owns an internal function registry by default; advanced callers can still inject an existing registry instance.json_invoke::JsonInvokeAdapterThreadSafe::registerFunction(...): register a callable and eagerly auto-register default JSON-capable argument and return types; laterregisterType(...)calls can override those defaults.- Wrap stateless tools with
json_invoke::readOnly(...)orjson_invoke::mutating(...)when you want exported metadata to includex-execution-semantics. json_invoke::TraceEvent/json_invoke::TraceSink: opt-in tracing hooks for invoke and session lifecycle events; stable top-level fields includeevent,timestamp,request_id,tool_name,duration_ms, and event-specificpayload.json_invoke::traceEventToJson(...): serialize oneTraceEventinto the stable JSON shape used by the tracing demo and recorder helpers.json_invoke::VectorTraceRecorder: lightweight collector frominclude/tools/trace_recorder.hppthat exposes a ready-to-useTraceSinkand exports recorded events throughtoJson().json_invoke::JsonInvokeAdapterThreadSafe::getAllToolSummariesJson()/getToolSchemaJson(...)/getAllToolSchemasJson(): export lighter summaries or full JSON schemas directly from the adapter.json_invoke::getAllToolSummariesJson(registry): emit concise tool summaries with only tool name and description for low-context LLM tool selection.json_invoke::getToolSchemaJson(registry, name)/json_invoke::getAllToolSchemasJson(registry): emit JSON schemas from registered tool metadata without triggering invocation-time conversion checks.json_invoke::JsonInvokeAdapterThreadSafe::invoke(...): supports.dump(2)for raw response viewing and implicit conversion to strong C++ result types.json_invoke::JsonInvokeAdapterThreadSafe::invokeJson(...): execute a JSON request and return the full raw JSON response directly.json_session_invoke::JsonSessionInvokeAdapterThreadSafe: higher-level session adapter that composesjson_invokeand is the recommended thread-safe entry point for stateful object lifecycles.json_session_invoke::JsonSessionInvokeAdapterUnsafe: explicit single-threaded variant for thread-confined schedulers or actor-style ownership. Constructing it prints a one-time warning so accidental shared use is easier to catch.json_session_invoke::SessionObjectHandle/json_session_invoke::SessionObjectOptions: clearer public aliases for the session-layer handle and options types;ObjectHandle/ObjectOptionsremain supported for compatibility.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::registerFunction(...): also supports plain stateless function registration directly, so one session adapter can host both stateless tools and stateful object lifecycles.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::registerFunction(...)intentionally rejects member function pointers; stateful member methods must be registered throughstateful<T>(...).method(...)so the session boundary stays explicit.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::stateful<T>(...): fluent builder for grouped stateful registration such as.create(...).method(...).destroy()while reusing the same underlying session runtime.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::findToolMetadata(...): readonly scheduler-facing metadata query that reports execution semantics, the minimalToolSchedulingScopehint, and stateful fields such asstateful_kind,object_type_name, andhandle_parameter_namefor scheduler-side classification.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::ToolSchedulingScope: optional registration-time scope hint for exceptional tools such as globalsession_barrier; finer fallback policies stay insidetask_schedulerclassification logic.task_scheduler::RequestClassifierThreadSafe: classifies a JSON request intoFreeReadOnly,ObjectExclusive,FactoryLane,ToolExclusive, orSessionBarrier; default inference uses tool metadata plus handle/object_type extraction, while explicit scheduling hints override the defaults.task_scheduler::TaskSchedulerFacadeThreadSafe: recommended one-object facade for higher-level callers; submit one JSON request withsubmitRequest(...), while classification, keyed queueing, and invocation stay hidden behind the facade.task_scheduler::ITaskScheduler: scheduler-side execution interface that accepts a classifiedScheduledTaskand returns astd::future<json>without pulling execution policy back intojson_session_invoke.task_scheduler::KeyedTaskScheduler: fixed-worker scheduler implementation that admits the next runnable task from an internal queue, allowsFreeReadOnlywork to run concurrently, serializesObjectExclusivebyobject_id, serializesFactoryLanebyobject_type, serializesToolExclusivebytool_name, and treatsSessionBarrieras a stop-the-world session-wide exclusive operation.runtime::RuntimeFacadeThreadSafe: protocol-neutral entry point abovetask_scheduler;listTools()returns normalized tool descriptors,submitInvoke(...)schedules one unary call and returnsstd::future<runtime::InvokeResult>, andinvoke(...)gives the same normalized result synchronously.runtime::InvokeResult: normalized invoke envelope withok,value, optionalerror, and the originalraw_response; classification-time failures such as unknown tools are converted into the same error shape instead of leaking exceptions to protocol adapters.mcp::McpStdioServerThreadSafe: thin MCP adapter overruntime::RuntimeFacadeThreadSafe; it reads and writesContent-Lengthframed JSON-RPC messages andserveConcurrent(...)keeps multipletools/callrequests in flight on one stdio session whileinitialize,ping, andtools/listremain ordered.mcp::McpStdioServerThreadSafe::handleMessage(...): useful when you want protocol handling without a real stdio loop, for example in tests or when embedding the MCP adapter into another transport shim.
MCP stdio demo
- Build
mcp_stdio_server_demowhen you want a real process that an MCP host can launch over stdio. - The demo server registers
sum,slow_sum,echo_text,create_counter,counter_add,counter_value, anddestroy_counter. - The process writes only framed MCP responses to stdout; startup notes,
slow_sumoverlap logs, and fatal errors go to stderr. examples/mcp_stdio/test_mcp.ps1now runs a fuller smoke test against the demo executable: initialize, tools/list, one statelesssumcall, then a statefulcreate_counter -> counter_add -> counter_value -> destroy_counterflow.- The fluent builder also supports
.options(...), so object type selection and session object options can be expressed separately:.stateful<T>("counter").options(opts).... - When
.stateful<T>("counter")uses.create(...)without an explicit tool name, the builder defaults tocreate_counter. - If a stateful builder creates an object but omits
.destroy(), the adapter auto-registers the defaultdestroy_<object_type>tool unlesssetStatefulDefaults(...)disablesauto_register_destroy. json_session_invoke::JsonSessionInvokeAdapterThreadSafe::setStatefulDefaults(...): adapter-level defaults for auto-generated stateful helpers, includingauto_register_destroyand the default destroy description text.json_session_invoke::JsonSessionInvokeAdapterThreadSafe::registerDestroy<T>(): when called without a name, defaults todestroy_<object_type>if the session object type was explicitly named during factory registration, otherwise falls back todestroy_object.json_invoke::json_traits<T>: add custom JSON bindings for domain types.func_registry::schema_traits<T>: optionally describe nested object fields so exported tool schemas can include custom object properties and container item shapes.- See
SCHEMA_TRAITS.mdfor dedicated authoring guidance and LLM-oriented generation rules forschema_traits<T>. - See
SCHEMA_TRAITS_PROMPT.mdfor reusable prompt templates when you want an LLM to generate or reviewschema_traits<T>code. func_registry::TypeSchemacan carry field descriptions, example values, and default values;json_invokeemits them into exported JSON Schema.std::optional<T>parameters are exported as non-required nullable schema properties and can be omitted from named JSON arguments.- enum parameters and return values are supported out of the box through their underlying integer representation.
- specialize
func_registry::enum_traits<T>to expose string-based enum mappings and emit schema enum values for LLM-friendly calls. std::map<std::string, T>andstd::unordered_map<std::string, T>now export as object schemas withadditionalProperties, so nested dictionary inputs and outputs can carry structured item schemas.
Supported request shapes
Stateful flows use the same request envelope through json_session_invoke. A create tool can return a handle like { "object_id": "obj_1", "object_type": "counter" }, and later tools can accept that handle as a regular argument.
You can also mix stateless and stateful tools on the same JsonSessionInvokeAdapterThreadSafe instance:
json_session_invoke::JsonSessionInvokeAdapterThreadSafe adapter;
adapter.registerFunction(
"sum",
json_invoke::readOnly([](int left, int right) { return left + right; }),
func_registry::FunctionMetadata{{"left", "right"}, "Add two integers."});
adapter
.stateful<Counter>("counter")
.create([](int initial) { return std::make_shared<Counter>(initial); })
.method("counter_add", &Counter::add)
.method("counter_value", &Counter::current);Stateful tools infer execution semantics automatically: factories and destroy tools export mutating, non-const methods export mutating, and const methods export read_only.
JsonSessionInvokeAdapterUnsafe is only appropriate when one thread exclusively owns the adapter for its full lifetime and every registration/invocation path is funneled through that same thread. If an adapter instance may be shared across threads, use JsonSessionInvokeAdapterThreadSafe.
{
"name": "add",
"args": [2, 3]
}{
"type": "function",
"function": {
"name": "add",
"arguments": "{\"arg0\":2,\"arg1\":3}"
}
}Build and run examples
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
.\build\func_registry_demo.exe
.\build\mcp_stdio_server_demo.exe
.\examples\mcp_stdio\test_mcp.ps1
.\build\json_invoke_demo.exe
.\build\json_stateful_demo.exe
.\build\json_tracing_demo.exe
.\build\trace_recorder_demo.exe
.\build\task_scheduler_demo.exeThe initial configure step downloads nlohmann/json into build/_deps/ through FetchContent.
GitHub Actions runs the same configure, build, and test flow on Windows for pushes and pull requests.