实战Demo——Hello Mcp
·
Exercise : Hello MCP
基本信息
| 项目 | 内容 |
|---|---|
| 难度 | ⭐ |
| 预估时间 | 1-2 小时 |
| 核心技能 | JSON-RPC 2.0, stdin/stdout 传输, nlohmann/json |
| 产出物 | 一个可运行的最简 MCP 服务器 (约 100 行代码) |
学习目标
- 理解 MCP 协议中最基础的请求-响应循环
- 掌握 stdin/stdout 行协议
- 使用 nlohmann/json 解析和构造 JSON 消息
- 理解 JSON-RPC 的 id 字段和 method 字段
前置准备
阅读以下文档的相关章节:
- AI与MCP协议入门 – JSON-RPC 2.0规范和 MCP 协议基础
- C++核心基础 – 熟悉 std::getline 和 std::cout
需求规格
实现一个最简单可能的 MCP 服务器,支持以下功能:
- 通过 stdin 读取 JSON-RPC 请求(每行一个请求)
- 支持
initialize命令:返回服务器能力和版本 - 支持
ping命令:返回空对象{} - 支持
tools/list命令:返回一个硬编码的工具列表(包含一个 “echo” 工具) - 支持
tools/call命令:如果调用 “echo”,返回用户传入的文本 - 通过 stdout 返回 JSON-RPC 响应(每行一个响应)
- 对未知命令返回 Method not found 错误
步骤指引
Step 1: 项目骨架 (15分钟)
// hello_mcp.cpp
#include <iostream>
#include <string>
#include "json.hpp"
using json = nlohmann::json;
int main() {
std::string line;
while (std::getline(std::cin, line)) {
// 处理每一行请求
if (line.empty()) continue;
try {
auto request = json::parse(line);
auto response = handle_request(request);
std::cout << response.dump() << std::endl;
std::cout.flush(); // 重要: 立即刷新
} catch (const json::parse_error& e) {
// 返回 Parse error
json error_response;
error_response["jsonrpc"] = "2.0";
error_response["id"] = nullptr;
error_response["error"] = {
{"code", -32700},
{"message", "Parse error"}
};
std::cout << error_response.dump() << std::endl;
}
}
return 0;
}
Step 2: 实现命令处理函数 (20分钟)
json handle_request(const json& request) {
std::string method = request.value("method", "");
if (method == "initialize") {
return handle_initialize(request);
} else if (method == "ping") {
return handle_ping(request);
} else if (method == "tools/list") {
return handle_tools_list(request);
} else if (method == "tools/call") {
return handle_tools_call(request);
} else {
// Method not found
json error;
error["jsonrpc"] = "2.0";
error["id"] = request.value("id", nullptr);
error["error"] = {
{"code", -32601},
{"message", "Method not found: " + method}
};
return error;
}
}
Step 3: 实现各个命令 (20分钟)
json handle_initialize(const json& request) {
json response;
response["jsonrpc"] = "2.0";
response["id"] = request["id"];
response["result"] = {
{"protocolVersion", "2024-11-05"},
{"serverInfo", {
{"name", "hello-mcp"},
{"version", "1.0.0"}
}},
{"capabilities", {
{"tools", {{"listChanged", false}}}
}}
};
return response;
}
json handle_ping(const json& request) {
json response;
response["jsonrpc"] = "2.0";
response["id"] = request["id"];
response["result"] = json::object();
return response;
}
json handle_tools_list(const json& request) {
json response;
response["jsonrpc"] = "2.0";
response["id"] = request["id"];
json echo_tool;
echo_tool["name"] = "echo";
echo_tool["description"] = "Echoes back the input text";
echo_tool["inputSchema"] = {
{"type", "object"},
{"properties", {
{"text", {{"type", "string"}, {"description", "Text to echo"}}}
}},
{"required", json::array({"text"})}
};
response["result"]["tools"] = json::array({echo_tool});
return response;
}
json handle_tools_call(const json& request) {
std::string tool_name = request["params"]["name"];
if (tool_name == "echo") {
std::string text = request["params"]["arguments"]["text"];
json response;
response["jsonrpc"] = "2.0";
response["id"] = request["id"];
response["result"] = {
{"content", json::array({
{{"type", "text"}, {"text", "Echo: " + text}}
})},
{"isError", false}
};
return response;
}
// Tool not found
json error;
error["jsonrpc"] = "2.0";
error["id"] = request["id"];
error["error"] = {
{"code", -32602},
{"message", "Unknown tool: " + tool_name}
};
return error;
}
Step 4: 编译和测试 (15分钟)
# 编译
g++ -std=c++17 hello_mcp.cpp -I../libs_tier_01/json/include -o hello_mcp
# 测试 initialize
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | ./hello_mcp
# 测试 tools/list
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | ./hello_mcp
# 测试 tools/call
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"text":"Hello MCP!"}}}' | ./hello_mcp
# 测试未知命令
echo '{"jsonrpc":"2.0","id":4,"method":"unknown"}' | ./hello_mcp
期望输出
initialize 请求的输出应类似:
{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2024-11-05","serverInfo":{"name":"hello-mcp","version":"1.0.0"}}}
提示
std::cout.flush()很关键 – stdin/stdout 是行缓冲的,不 flush 客户端收不到响应request.value("id", nullptr)安全地获取 id 字段(JSON-RPC 通知没有 id)- 空行要跳过,否则
json::parse("")会抛异常
扩展挑战
- 添加
prompts/list命令,返回一个硬编码的提示词 - 添加请求日志(输出到 stderr,避免干扰 stdout 协议)
- 支持多个工具(添加 “time” 工具返回当前时间)
- 让你的 Hello MCP 能被 Claude Desktop 直接加载使用
My Answer
#include <iostream>
#include <nlohmann/json.hpp>
#include <unordered_map>
using json = nlohmann::json;
/// @brief 构造一个空的响应消息
/// @param request :请求消息
/// @return 空的响应消息
json Response(const json &request)
{
json response;
response["jsonrpc"] = "2.0";
response["id"] = request["id"];
response["result"] = json::object();
return response;
}
enum class ErrorCode
{
// JSON解析失败
ParseError = -32700,
// 请求对象无效
InvalidRequest = -32600,
// 请求的方法不存在
MethodNotFound = -32601,
// 方法参数无效
InvalidParams = -32602,
// 服务器内部错误
InternalError = -32603
};
class Server
{
public:
Server()
{
FunctionMap = {
{"initialize", [this](const json &request)
{ return this->InitializeImpl(request); }},
{"ping", [this](const json &request)
{ return this->PingImpl(request); }},
{"tools/list", [this](const json &request)
{ return this->ToolsListImpl(request); }},
{"tools/call", [this](const json &request)
{ return this->ToolsCallImpl(request); }}};
}
void start()
{
json response, request;
std::string message;
while (true)
{
if (!std::getline(std::cin, message) || message.empty())
{
break; // EOF or empty line, exit gracefully
}
try
{
request = json::parse(message);
}
catch (const json::parse_error &e)
{
response = Error(ErrorCode::ParseError, "null", e.what());
std::cout << response.dump() << std::endl;
continue;
}
std::string method;
if (request.contains("method"))
{
method = request["method"].get<std::string>();
auto it = FunctionMap.find(method);
if (it != FunctionMap.end())
{
response = FunctionMap[method](request);
std::cout << response.dump() << std::endl;
}
else
{
response = Error(ErrorCode::MethodNotFound, method, "Method not Found");
std::cout << response.dump() << std::endl;
}
}
else
{
response = Error(ErrorCode::InvalidRequest, request["id"].dump(), "Missing method");
std::cout << response.dump() << std::endl;
}
}
}
private:
json Error(ErrorCode code, const std::string &id, const std::string &message)
{
return {
{"jsonrpc", "2.0"},
{"error", {{"code", static_cast<int>(code)}, {"message", message}}},
{"id", id}};
}
json InitializeImpl(const json &request)
{
json response = Response(request);
if (!request.contains("params"))
{
return Error(ErrorCode::InvalidParams, request["id"].dump(), "Missing Params");
}
if(request["params"].empty()){
response["result"]["protocolVersion"] = json::object();
}else{
response["result"]["protocolVersion"] = request["params"]["protocolVersion"];
}
response["result"]["capabilities"]["tools"] = json::object({{"listChanged", true}});
response["result"]["capabilities"]["prompts"] = json::object({{"listChanged", true}});
response["result"]["capabilities"]["resources"]["subscribe"] = true;
response["result"]["capabilities"]["resources"]["listChanged"] = true;
response["result"]["capabilities"]["logging"] = json::object();
return response;
}
json PingImpl(const json &request)
{
return Response(request);
}
json ToolsListImpl(const json &request)
{
json response = Response(request);
json tool;
tool["name"] = "echo";
tool["description"] = "server echo message from user";
tool["inputSchema"] = json::object();
response["result"]["tools"] = json::array();
response["result"]["tools"].push_back(tool);
return response;
}
json ToolsCallImpl(const json &request)
{
json response = Response(request);
if (!request.contains("params"))
{
return Error(ErrorCode::InvalidParams, request["id"].dump(), "Missing Params");
}
if (request["params"]["name"] == "echo")
{
json context;
context["type"] = "text";
context["text"] = request["params"]["arguments"];
response["result"]["content"] = json::array();
response["result"]["content"].push_back(context);
}
else
{
response["result"]["content"] = json::object();
}
return response;
}
private:
std::unordered_map<std::string, std::function<json(const json &)>> FunctionMap;
};
int main()
{
Server server;
server.start();
return 0;
}
更多推荐


所有评论(0)