How to Integrate Third-Party Tools into MCP Definitions: A Complete Guide
To integrate third-party tools into MCP definitions, declare the tool in the static TOOLS array in src/mcp/mcp.c, implement a handler function that parses JSON arguments and executes the external binary, and wire the handler into the cbm_mcp_handle_tool dispatch switch.
The Memory-Code-Protocol (MCP) server in the DeusData/codebase-memory-mcp repository exposes a set of tools callable via JSON-RPC. Adding third-party tools follows the same pattern used for built-in tools like search_graph and manage_adr. This guide walks you through the three-step integration process using actual source code from the repository.
Step 1: Declare the Tool in the Static TOOLS Table
Every MCP tool must be registered in the static array TOOLS located in src/mcp/mcp.c (lines 11–31). This declaration supplies the metadata required for tool discovery and JSON validation.
Understanding the TOOLS Array Structure
The TOOLS array contains tool_def_t structures with four fields: the tool name, human-readable title, description, and a JSON Schema string defining the input arguments. Here is how to add a new third-party tool:
static const tool_def_t TOOLS[] = {
{"search_graph", "Search graph", "Search the code knowledge graph …",
"{\"type\":\"object\",\"properties\":{ … },\"required\":[\"project\"]}"},
/* … existing entries … */
{"my_third_party", "My Third‑Party Tool",
"Runs a third‑party binary and returns its stdout as JSON.",
"{\"type\":\"object\",\"properties\":{"
"\"project\":{\"type\":\"string\"},"
"\"binary_path\":{\"type\":\"string\",\"description\":\"Path to the executable\"},"
"\"args\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}"
"},\"required\":[\"project\",\"binary_path\"]}"}
};
JSON Schema Definition
The input_schema field uses JSON Schema to define the tool's interface. This schema automatically generates CLI flags and validates incoming RPC arguments. The name field ("my_third_party") becomes the identifier used by clients in tools/list and list_tools calls.
Step 2: Implement the Handler Function
Create a static function following the standard handler signature used throughout src/mcp/mcp.c. The function receives the server context and a JSON string of arguments, then returns a formatted MCP response.
Handler Function Signature
All handlers follow this pattern:
static char *handle_my_third_party(cbm_mcp_server_t *srv, const char *args) {
/* Implementation */
}
Argument Parsing and Validation
Extract arguments using the MCP helper functions. As shown in the handle_manage_adr implementation (lines 81–99), use cbm_mcp_get_string_arg for string values and cbm_mcp_get_arguments for complex structures:
static char *handle_my_third_party(cbm_mcp_server_t *srv, const char *args) {
char *project = get_project_arg(args);
char *binary = cbm_mcp_get_string_arg(args, "binary_path");
char *argv_json = cbm_mcp_get_arguments(args);
if (!project || !binary) {
char *err = cbm_mcp_text_result("{\"error\":\"missing arguments\"}", true);
free(project); free(binary); free(argv_json);
return err;
}
/* … */
}
Process Execution and Output Capture
Execute the third-party binary using fork/exec or a library wrapper. The example below constructs an argument array from the JSON input, prepends the binary path, and captures stdout:
/* Resolve project store if needed */
cbm_store_t *store = resolve_store(srv, project);
if (!store) { /* handle error */ }
/* Build argv array */
int argc = 0;
char **argv = NULL;
if (argv_json) {
argv = json_array_to_argv(argv_json, &argc);
}
argv = prepend_binary(binary, argv, &argc);
/* Execute and capture output */
char *output = run_process_and_capture_stdout(binary, argv, argc);
free_argv(argv);
free(argv_json);
Formatting the MCP Response
Wrap results using cbm_mcp_text_result (implemented in lines 45–73). This function creates the required MCP text envelope containing your JSON payload:
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
yyjson_mut_obj_add_strcpy(doc, root, "stdout", output ? output : "");
yyjson_mut_obj_add_str(doc, root, "status", output ? "ok" : "error");
char *json = yy_doc_to_str(doc);
yyjson_mut_doc_free(doc);
free(output);
free(project);
free(binary);
return cbm_mcp_text_result(json, false); // false indicates success
}
Step 3: Wire the Handler into the Dispatch Switch
The central dispatcher cbm_mcp_handle_tool (lines 511–518) routes incoming requests to the appropriate handler. Add a new conditional branch that matches your tool name and forwards the call:
char *cbm_mcp_handle_tool(cbm_mcp_server_t *srv,
const char *tool_name,
const char *args_json) {
/* … existing branches … */
if (strcmp(tool_name, "my_third_party") == 0) {
return handle_my_third_party(srv, args_json);
}
/* … fallback error handling … */
snprintf(msg, sizeof(msg), "unknown tool: %s", tool_name);
return cbm_mcp_text_result(msg, true);
}
This pattern matches the existing implementations for manage_adr and search_graph, ensuring consistency across the codebase.
Testing and Validation
After modifying src/mcp/mcp.c, add unit tests in tests/test_mcp.c following the existing framework. Create test cases that invoke cbm_mcp_handle_tool with mock binaries and assert that the returned MCP envelope contains the expected stdout and status fields.
Update documentation by adding a markdown entry in README.md describing the tool's purpose and flags. The description and schema you added to TOOLS automatically surface in list_tools responses, but explicit documentation helps users understand the external dependencies.
Example Usage
Once recompiled, invoke the tool via the CLI. The interface automatically constructs the JSON payload from the schema definition:
codebase-memory-mcp cli my_third_party \
--binary_path /usr/local/bin/example_tool \
--args '[ "arg1", "--flag", "value" ]' \
--project my_repo
The MCP server returns a standardized envelope:
{
"content": [
{"type":"text","text":"{\"stdout\":\"example output\\n\",\"status\":\"ok\"}"}
],
"isError": false
}
Summary
- Declare the tool in
src/mcp/mcp.cby adding an entry to the staticTOOLSarray with a JSON Schema defining the input arguments. - Implement a handler function that extracts arguments using
cbm_mcp_get_string_arg, executes the third-party binary, and formats results withcbm_mcp_text_result. - Register the handler in
cbm_mcp_handle_toolby adding astrcmpbranch that maps the tool name to your handler. - Test the integration using the framework in
tests/test_mcp.cand updateREADME.mdwith usage examples.
Frequently Asked Questions
What is the MCP protocol in DeusData/codebase-memory-mcp?
The Memory-Code-Protocol (MCP) is a JSON-RPC interface exposed by the codebase-memory-mcp server that allows agents to query and manipulate code knowledge graphs. It defines a standard tool-calling pattern where clients send tool names and JSON arguments, and the server returns structured text responses wrapped in MCP envelopes.
Can I integrate third-party tools as shared libraries instead of executables?
Yes. While the examples show fork/exec patterns for binaries, you can link against shared libraries directly within your handler function. Replace the run_process_and_capture_stdout call with direct function calls to the library API, then format the results using the same cbm_mcp_text_result wrapper to maintain protocol compatibility.
How do I handle errors when a third-party binary fails?
Return an error response using cbm_mcp_text_result with the second parameter set to true. Capture the exit code or stderr from the failed process, construct a JSON error object containing diagnostic information, and pass it to cbm_mcp_text_result. This signals to MCP clients that the tool execution failed while preserving the structured error details.
Where should I add tests for new MCP tools?
Add test cases to tests/test_mcp.c following the existing test style. Create mock binaries that print predictable output, then call cbm_mcp_handle_tool with the tool name and verify that the returned string contains the expected JSON structure. Test both successful execution paths and error conditions, such as missing required arguments or non-existent binary paths.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →