Complete Source¶
CMakeLists.txt¶
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.15...3.31)
project("mcp_simple_project" LANGUAGES CXX)
add_executable(mcp_client_sample client.cpp)
add_executable(mcp_server_sample server.cpp)
find_package(mbase.libs REQUIRED COMPONENTS mcp)
target_compile_features(mcp_client_sample PUBLIC cxx_std_17)
target_link_libraries(mcp_client_sample PRIVATE mbase-mcp)
target_include_directories(mcp_client_sample PUBLIC mbase-mcp)
target_compile_features(mcp_server_sample PUBLIC cxx_std_17)
target_link_libraries(mcp_server_sample PRIVATE mbase-mcp)
target_include_directories(mcp_server_sample PUBLIC mbase-mcp)
client.cpp¶
client.cpp¶
#include <mbase/mcp/mcp_client_base.h>
#include <mbase/mcp/mcp_client_server_stdio.h>
#include <iostream>
int main()
{
mbase::McpClientBase myMcpClient(
"MCP Sample Client",
"1.0.0"
);
mbase::McpServerStdioInit initDesc;
initDesc.mServerName = "MCP Sample Server";
initDesc.mCommand = "./mcp_server_sample";
mbase::McpClientServerStdio mcpServerState(initDesc);
myMcpClient.register_mcp_server(&mcpServerState);
mcpServerState.start_processor();
mcpServerState.initialize(&myMcpClient, [&](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::McpServerStateBase* server_instance){
if(errCode == MBASE_MCP_SUCCESS)
{
std::cout << "Connection successful" << std::endl;
}
else
{
std::cout << "Connection failed" << std::endl;
}
mcpServerState.list_tools([&](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpToolDescription>&& tools_list, const mbase::string& pagination_token){
// do stuff
});
mcpServerState.list_prompts([&](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpPromptDescription>&& prompts_list, const mbase::string& pagination_token){
// do stuff
});
mcpServerState.list_resources([&](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpResourceDescription>&& resources_list, const mbase::string& pagination_token){
// do stuff
});
// arbitrary numbers
mbase::McpToolMessageArgument intNum1 = 10;
mbase::McpToolMessageArgument intNum2 = 20;
mbase::McpToolMessageArgument floatNum1 = 10.5f;
mbase::McpToolMessageArgument floatNum2 = 20.5f;
mbase::McpToolMessageArgument echoMessage = "Hello world!";
mbase::McpToolMessageMap argMap;
argMap["num1"] = intNum1;
argMap["num2"] = intNum2;
mcpServerState.tool_call("add_int64", [](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpResponseTool>&& toolResponse, bool is_error){
mbase::McpResponseTextTool textResponse = std::get<mbase::McpResponseTextTool>(toolResponse[0]);
std::cout << textResponse.mText << std::endl;
}, MBASE_MCP_TIMEOUT_DEFAULT, argMap);
argMap.clear();
argMap["num1"] = floatNum1;
argMap["num2"] = floatNum2;
mcpServerState.tool_call("add_float64", [](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpResponseTool>&& toolResponse, bool is_error){
mbase::McpResponseTextTool textResponse = std::get<mbase::McpResponseTextTool>(toolResponse[0]);
std::cout << textResponse.mText << std::endl;
}, MBASE_MCP_TIMEOUT_DEFAULT, argMap);
argMap.clear();
argMap["user_message"] = echoMessage;
mcpServerState.tool_call("echo", [](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpResponseTool>&& toolResponse, bool is_error){
mbase::McpResponseTextTool textResponse = std::get<mbase::McpResponseTextTool>(toolResponse[0]);
std::cout << textResponse.mText << std::endl;
}, MBASE_MCP_TIMEOUT_DEFAULT, argMap);
mcpServerState.read_resource("file:///content.txt", [](const int& errCode, mbase::McpClientBase* self_client_instance, mbase::vector<mbase::McpResponseResource>&& resourceResponse){
mbase::McpResponseTextResource textResponse = std::get<mbase::McpResponseTextResource>(resourceResponse[0]);
std::cout << "Content: " << textResponse.mText << std::endl;
});
mbase::McpPromptMessageMap promptArgMap;
promptArgMap["greet_text"] = "Hello developer!";
promptArgMap["mbase_arg"] = "mcp-sdk";
mcpServerState.get_prompt("greeting_prompt", [](const int& errCode, mbase::McpClientBase* self_client_instance, const mbase::string& prompt_description, mbase::vector<mbase::McpResponsePrompt>&& promptResponse) {
mbase::McpResponseTextPrompt textPromptRes = std::get<mbase::McpResponseTextPrompt>(promptResponse[0]);
std::cout << "Role: " << textPromptRes.mRole << std::endl;
std::cout << "Prompt: " << textPromptRes.mText << std::endl;
}, MBASE_MCP_TIMEOUT_DEFAULT, promptArgMap);
mcpServerState.get_prompt("mbase_sdk_inform", [](const int& errCode, mbase::McpClientBase* self_client_instance, const mbase::string& prompt_description, mbase::vector<mbase::McpResponsePrompt>&& promptResponse) {
mbase::McpResponseTextPrompt textPromptRes = std::get<mbase::McpResponseTextPrompt>(promptResponse[0]);
std::cout << "Role: " << textPromptRes.mRole << std::endl;
std::cout << "Prompt: " << textPromptRes.mText << std::endl;
}, MBASE_MCP_TIMEOUT_DEFAULT, promptArgMap);
});
while(1)
{
myMcpClient.update();
mbase::sleep(5);
}
return 0;
}
server.cpp¶
server.cpp¶
#include <mbase/mcp/mcp_server_stdio.h>
#include <mbase/mcp/mcp_server_http_streamable.h>
#include <mbase/mcp/mcp_server_features.h>
#include <mbase/mcp/mcp_server_responses.h>
// inputs are normally validated by the library but
// the JSON model dictates a "number" which may be both 64-bit integer or float
// that is why we must do double check like this
// this double check is not necessary if the arguments are strings as you may have seen in the "echo" tool
mbase::McpResponseTool add_int64(mbase::McpServerClient* in_client_instance, const mbase::McpMessageMap& in_msg_map, const mbase::Json& in_progress_token)
{
mbase::McpResponseTextTool toolResponse;
if(std::holds_alternative<int64_t>(in_msg_map.at("num1")) && std::holds_alternative<int64_t>(in_msg_map.at("num2")))
{
int64_t number1 = std::get<int64_t>(in_msg_map.at("num1"));
int64_t number2 = std::get<int64_t>(in_msg_map.at("num2"));
toolResponse.mText = mbase::string::from_format("%llu + %llu = %llu", number1, number2, number1 + number2);
return toolResponse;
}
toolResponse.mText = "Numbers must be 64 bit integers";
return toolResponse;
}
mbase::McpResponseTool add_float64(mbase::McpServerClient* in_client_instance, const mbase::McpMessageMap& in_msg_map, const mbase::Json& in_progress_token)
{
mbase::McpResponseTextTool toolResponse;
if(std::holds_alternative<double>(in_msg_map.at("num1")) && std::holds_alternative<double>(in_msg_map.at("num2")))
{
double number1 = std::get<double>(in_msg_map.at("num1"));
double number2 = std::get<double>(in_msg_map.at("num2"));
toolResponse.mText = mbase::string::from_format("%f + %f = %f", number1, number2, number1 + number2);
return toolResponse;
}
toolResponse.mText = "Numbers must be 64 bit floats";
return toolResponse;
}
mbase::McpResponseTool echo(mbase::McpServerClient* in_client_instance, const mbase::McpMessageMap& in_msg_map, const mbase::Json& in_progress_token)
{
mbase::McpResponseTextTool toolResponse;
toolResponse.mText = std::get<mbase::string>(in_msg_map.at("user_message"));
return toolResponse;
}
mbase::McpResponseResource content_file_uri(mbase::McpServerClient* in_client_instance, const mbase::Json& in_progress_token)
{
mbase::McpNotificationLogMessage logMsg;
logMsg.mLogger = "resource logger";
logMsg.mError = "Reading content.txt ...";
mbase::sleep(500);
in_client_instance->set_progress(25, in_progress_token, "25%");
mbase::sleep(500);
in_client_instance->set_progress(50, in_progress_token, "50%");
mbase::sleep(500);
in_client_instance->set_progress(75, in_progress_token, "75%");
mbase::sleep(500);
in_client_instance->set_progress(100, in_progress_token, "100%");
mbase::McpResponseTextResource textResourceResponse;
textResourceResponse.mMimeType = "text/plain";
textResourceResponse.mText = "This is the content inside the content.txt";
return textResourceResponse;
}
mbase::vector<mbase::McpResponsePrompt> greeting_prompt_cb(mbase::McpServerClient* in_client_instance, const mbase::McpMessageMap& in_msg_map, const mbase::Json& in_progress_token)
{
mbase::McpResponseTextPrompt textPromptResp;
textPromptResp.mRole = "assistant";
textPromptResp.mText = "You must greet the user with the following message: " + std::get<mbase::string>(in_msg_map.at("greet_text"));
return {textPromptResp};
}
mbase::vector<mbase::McpResponsePrompt> mbase_sdk_inform_cb(mbase::McpServerClient* in_client_instance, const mbase::McpMessageMap& in_msg_map, const mbase::Json& in_progress_token)
{
mbase::McpResponseTextPrompt textPromptResp;
textPromptResp.mRole = "user";
textPromptResp.mText = "Tell me about MBASE " + std::get<mbase::string>(in_msg_map.at("mbase_arg"));
return {textPromptResp};
}
int main()
{
mbase::McpServerStdio mcpServer(
"MCP Sample Server",
"1.0.0"
);
mcpServer.start_processor();
mbase::McpToolArgument toolArg1;
toolArg1.mArgType = mbase::McpValueType::NUMBER;
toolArg1.mArgumentName = "num1";
toolArg1.mDescription = "First number of the add_int64/float64 tool";
toolArg1.mIsRequired = true;
mbase::McpToolArgument toolArg2;
toolArg2.mArgType = mbase::McpValueType::NUMBER;
toolArg2.mArgumentName = "num2";
toolArg2.mDescription = "Second number of the add_int64/float64 tool";
toolArg2.mIsRequired = true;
mbase::McpToolArgument echoToolArg;
echoToolArg.mArgType = mbase::McpValueType::STRING;
echoToolArg.mArgumentName = "user_message";
echoToolArg.mDescription = "Message to echo";
echoToolArg.mIsRequired = true;
mbase::McpPromptArgument greetArgument;
greetArgument.mArgumentName = "greet_text";
greetArgument.mDescription = "A greetings text";
greetArgument.mIsRequired = true;
mbase::McpPromptArgument mbaseArgument;
mbaseArgument.mArgumentName = "mbase_arg";
mbaseArgument.mCompletionStrings = {"mcp-sdk", "mcp-sdk-examples", "mcp-sdk-usage", "mcp-server-about", "mcp-server-usage"};
mbaseArgument.mDescription = "What to describe in MBASE MCP SDK";
mbaseArgument.mIsRequired = true;
mbase::McpToolDescription addToolint64Description;
addToolint64Description.mDescription = "This tool adds two 64-bit integers and return the result";
addToolint64Description.mName = "add_int64";
addToolint64Description.mArguments = {toolArg1, toolArg2};
mbase::McpToolDescription addToolfloat64Description;
addToolfloat64Description.mDescription = "This tool adds two 64-bit floating point numbers and return the result";
addToolfloat64Description.mName = "add_float64";
addToolfloat64Description.mArguments = {toolArg1, toolArg2};
mbase::McpToolDescription echoToolDescription;
echoToolDescription.mDescription = "This tool echoes the 'message' argument back to the user";
echoToolDescription.mName = "echo";
echoToolDescription.mArguments = {echoToolArg};
mbase::McpResourceDescription contentResourceDescription;
contentResourceDescription.mName = "content_file_uri";
contentResourceDescription.mUri = "file:///content.txt";
mbase::McpPromptDescription greetPromptDescription;
greetPromptDescription.mName = "greeting_prompt";
greetPromptDescription.mDescription = "Will return a greetings prompt template with the argument substituted inside.";
greetPromptDescription.mArguments = {greetArgument};
mbase::McpPromptDescription mbasePromptDescription;
mbasePromptDescription.mName = "mbase_sdk_inform";
mbasePromptDescription.mDescription = "Will return a prompt in the format: 'Tell me about MBASE {argument}'";
mbasePromptDescription.mArguments = {mbaseArgument};
mcpServer.register_tool(addToolint64Description, add_int64);
mcpServer.register_tool(addToolfloat64Description, add_float64);
mcpServer.register_tool(echoToolDescription, echo);
mcpServer.register_resource(contentResourceDescription, content_file_uri);
mcpServer.register_prompt(greetPromptDescription, greeting_prompt_cb);
mcpServer.register_prompt(mbasePromptDescription, mbase_sdk_inform_cb);
while(mcpServer.is_processor_running())
{
mcpServer.update();
mbase::sleep(5);
}
return 0;
}