DEV Community

Cover image for Turn Existing C++ Functions into Claude-Callable MCP Tools
Chen Kun
Chen Kun

Posted on

Turn Existing C++ Functions into Claude-Callable MCP Tools

Turn Existing C++ Functions into Claude-Callable MCP Tools

Most C++ teams already have valuable business logic: file processing, schedulers, rule engines, protocol handlers, and performance-critical modules.

When LLM integration starts, many teams add Python/Node wrappers around existing C++ code. That works for demos, but it creates long-term maintenance problems:

  1. Duplicate implementations across languages
  2. Behavioral drift between wrappers and native code
  3. Harder performance tuning and debugging

This article shows a cleaner path: keep logic in C++, expose it as Claude-callable tools through MCP, and harden the stack for production.

The Core Idea

Do not rewrite your backend.

Build a thin runtime layer that gives you:

  1. Function registration
  2. JSON invocation
  3. Tool schema export
  4. Stateful object lifecycle (create -> method -> destroy)
  5. Concurrency control
  6. MCP transport (stdio/HTTP)

30-Second Tool Contract Example

This is more than "JSON calling a function".
It shows the three things a Claude-compatible host needs:

  1. A callable tool name
  2. A JSON argument contract
  3. Exportable schema metadata
#include <iostream>
#include <json_invoke/json_invoke.hpp>

int main() {
    json_invoke::JsonInvokeAdapterThreadSafe tools;

    tools.registerFunction(
        "add",
        [](int left, int right) { return left + right; },
        json_invoke::FunctionMetadata{{"left", "right"}, "Add two integers."}
    );

    int result = tools.invoke({
        {"name", "add"},
        {"args", {{"left", 2}, {"right", 3}}}
    });

    std::cout << "result: " << result << "\n\n";
    std::cout << tools.getToolSchemaJson("add").dump(2) << "\n";
}
Enter fullscreen mode Exit fullscreen mode

Output excerpt:

result: 5

{
    "function": {
        "name": "add",
        "description": "Add two integers.",
        "parameters": {
            "type": "object",
            "properties": {
                "left": { "type": "integer" },
                "right": { "type": "integer" }
            },
            "required": ["left", "right"]
        }
    },
    "type": "function"
}
Enter fullscreen mode Exit fullscreen mode

What this proves:

  1. Your C++ logic is callable as a named tool
  2. Invocation uses stable JSON arguments (not positional ad-hoc payloads)
  3. Schema is exported for host-side discovery and validation

How Claude Uses Exported Schema Through MCP

After registration and schema export, the runtime and host interaction typically looks like this:

+------------------------------+    +------------------------------+    +------------------------------+
|  Native C++ Tool Runtime     |    |        MCP Server            |    |       LLM Host/Client        |
|  (your existing functions)   |    |  (stdio or HTTP transport)   |    |  (Claude Desktop / Agent)    |
+------------------------------+    +------------------------------+    +------------------------------+

                |                                   |                                   |
                |--(0) register tools + export schema --------------------->|           |
                |<-(0) MCP tool surface ready (name/args/schema) ----------|            |
                |                                   |                                   |
                |                                   |<--(1) initialize + list_tools ----|
                |                                   |--(2) tool list + JSON schema ---->|
                |                                   |                                   |
                |<--(4) invoke(name,args) ----------|<--(3) tool call ------------------|
                |--(5) run native C++ function ---->|                                   |
                |<-(6) typed result / error --------|                                   |
                |                                   |--(7) tool result ---------------->|
                |                                   |                                   |
                |                                   |<--(8) optional next tool call ----|
                |<--(9) optional invoke ------------|                                   |
                |                                   |--(10) final tool-backed output -->|
Enter fullscreen mode Exit fullscreen mode

The important part is Step 0: how native C++ functions become MCP tools with exported JSON schema.
Steps 1+ are mostly standard MCP host behavior.

Move Beyond Toy Examples

My goal is: help you move existing native C++ functions into real LLM tool workflows with minimal rewriting.

With this framework, you can expose legacy or production C++ code through a consistent stack that already covers:

  1. Stateless calls (simple request/response tools)
  2. Stateful calls (session handle + lifecycle)
  3. Scheduling and queueing (safe concurrency under load)
  4. Parallel execution (maximize throughput where safe)
  5. Task cancellation (stop unnecessary work cleanly)

Why This Matters in Practice

For teams shipping real systems, these are not optional extras.
They are the difference between a demo and an operable platform.

Instead of building one-off wrappers, you can quickly take existing C++ functions and expose them to LLMs through a structured interface.

Layered Architecture You Can Adopt Incrementally

A key strength of this project is its layered design.
You do not need to adopt everything at once.

You can use lower layers independently when you only need part of the stack, then move upward as requirements grow.

Typical path:

  1. Start with function registration
  2. Add JSON invocation and schema export
  3. Add session/stateful capabilities
  4. Add scheduling + concurrency control
  5. Expose via MCP transport

This keeps integration cost low while preserving an upgrade path to production-grade behavior.

Demos That Map to Real Usage

The repository includes detailed demos that show how each layer is used in practice, from minimal usage to advanced scenarios.

This helps teams answer practical questions quickly:

  1. How do I expose an existing function with minimal change?
  2. How do I model multi-step stateful workflows?
  3. How do I avoid race conditions with concurrent tool calls?
  4. How do I evolve from local JSON calls to MCP-hosted tools?

Repository

Project repository:
writePerfectCode/llm_invoke_cpp: Provide a framework enabling LLMs to invoke C++ functions through JSON-based interfaces.

Final Takeaway

If your team already has valuable C++ logic, you do not need a rewrite strategy.
You need an exposure strategy.

This framework gives you that path:

  1. Expose native C++ functions quickly
  2. Keep architecture layered and composable
  3. Support stateless and stateful scenarios
  4. Handle scheduling, concurrency, and cancellation
  5. Connect to LLM hosts through MCP when ready

That is how you turn existing C++ assets into reliable LLM-callable capabilities.

Top comments (0)