实战Demo——Hello Mcp

发布时间:2026/6/26 2:18:05
实战Demo——Hello Mcp 文章目录Exercise : Hello MCP基本信息学习目标前置准备需求规格步骤指引期望输出提示扩展挑战My AnswerExercise : 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#includeiostream#includestring#includejson.hppusingjsonnlohmann::json;intmain(){std::string line;while(std::getline(std::cin,line)){// 处理每一行请求if(line.empty())continue;try{autorequestjson::parse(line);autoresponsehandle_request(request);std::coutresponse.dump()std::endl;std::cout.flush();// 重要: 立即刷新}catch(constjson::parse_errore){// 返回 Parse errorjson error_response;error_response[jsonrpc]2.0;error_response[id]nullptr;error_response[error]{{code,-32700},{message,Parse error}};std::couterror_response.dump()std::endl;}}return0;}Step 2: 实现命令处理函数(20分钟)jsonhandle_request(constjsonrequest){std::string methodrequest.value(method,);if(methodinitialize){returnhandle_initialize(request);}elseif(methodping){returnhandle_ping(request);}elseif(methodtools/list){returnhandle_tools_list(request);}elseif(methodtools/call){returnhandle_tools_call(request);}else{// Method not foundjson error;error[jsonrpc]2.0;error[id]request.value(id,nullptr);error[error]{{code,-32601},{message,Method not found: method}};returnerror;}}Step 3: 实现各个命令(20分钟)jsonhandle_initialize(constjsonrequest){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}}}}}};returnresponse;}jsonhandle_ping(constjsonrequest){json response;response[jsonrpc]2.0;response[id]request[id];response[result]json::object();returnresponse;}jsonhandle_tools_list(constjsonrequest){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});returnresponse;}jsonhandle_tools_call(constjsonrequest){std::string tool_namerequest[params][name];if(tool_nameecho){std::string textrequest[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}};returnresponse;}// Tool not foundjson error;error[jsonrpc]2.0;error[id]request[id];error[error]{{code,-32602},{message,Unknown tool: tool_name}};returnerror;}Step 4: 编译和测试(15分钟)# 编译g-stdc17 hello_mcp.cpp -I../libs_tier_01/json/include-ohello_mcp# 测试 initializeecho{jsonrpc:2.0,id:1,method:initialize,params:{}}|./hello_mcp# 测试 tools/listecho{jsonrpc:2.0,id:2,method:tools/list}|./hello_mcp# 测试 tools/callecho{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#includeiostream#includenlohmann/json.hpp#includeunordered_mapusingjsonnlohmann::json;/// brief 构造一个空的响应消息/// param request 请求消息/// return 空的响应消息jsonResponse(constjsonrequest){json response;response[jsonrpc]2.0;response[id]request[id];response[result]json::object();returnresponse;}enumclassErrorCode{// JSON解析失败ParseError-32700,// 请求对象无效InvalidRequest-32600,// 请求的方法不存在MethodNotFound-32601,// 方法参数无效InvalidParams-32602,// 服务器内部错误InternalError-32603};classServer{public:Server(){FunctionMap{{initialize,[this](constjsonrequest){returnthis-InitializeImpl(request);}},{ping,[this](constjsonrequest){returnthis-PingImpl(request);}},{tools/list,[this](constjsonrequest){returnthis-ToolsListImpl(request);}},{tools/call,[this](constjsonrequest){returnthis-ToolsCallImpl(request);}}};}voidstart(){json response,request;std::string message;while(true){if(!std::getline(std::cin,message)||message.empty()){break;// EOF or empty line, exit gracefully}try{requestjson::parse(message);}catch(constjson::parse_errore){responseError(ErrorCode::ParseError,null,e.what());std::coutresponse.dump()std::endl;continue;}std::string method;if(request.contains(method)){methodrequest[method].getstd::string();autoitFunctionMap.find(method);if(it!FunctionMap.end()){responseFunctionMap[method](request);std::coutresponse.dump()std::endl;}else{responseError(ErrorCode::MethodNotFound,method,Method not Found);std::coutresponse.dump()std::endl;}}else{responseError(ErrorCode::InvalidRequest,request[id].dump(),Missing method);std::coutresponse.dump()std::endl;}}}private:jsonError(ErrorCode code,conststd::stringid,conststd::stringmessage){return{{jsonrpc,2.0},{error,{{code,static_castint(code)},{message,message}}},{id,id}};}jsonInitializeImpl(constjsonrequest){json responseResponse(request);if(!request.contains(params)){returnError(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();returnresponse;}jsonPingImpl(constjsonrequest){returnResponse(request);}jsonToolsListImpl(constjsonrequest){json responseResponse(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);returnresponse;}jsonToolsCallImpl(constjsonrequest){json responseResponse(request);if(!request.contains(params)){returnError(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();}returnresponse;}private:std::unordered_mapstd::string,std::functionjson(constjson)FunctionMap;};intmain(){Server server;server.start();return0;}