
return function(procedure, request, body)
print( "start mcp" )
local payload = body.body
if type(payload) ~= "table" then
return script_failed()
end
local method = payload.method
local id = payload.id
-- 1. 握手
if method == "initialize" then
local resp = {
jsonrpc = "2.0",
id = id,
result = {
protocolVersion = payload.params and payload.params.protocolVersion or "2024-11-05",
capabilities = { tools = { listChanged = true } }, -- 原生布尔!放心的用!
serverInfo = { name = "lua-mcp-server", version = "1.0.0" }
}
}
-- 纯正的 Table 转字符串,没有任何心里负担!
return script_succeed(), http_respond(200, "application/json"), "application/json", json_encode(resp)
-- 2. 客户端就绪通知
elseif method == "notifications/initialized" then
return script_no_resp()
-- 3. 工具列表
elseif method == "tools/list" then
local resp = {
jsonrpc = "2.0",
id = id,
result = {
tools = {
{
name = "hello",
description = "Say hello to someone",
inputSchema = {
type = "object",
properties = {
name = { type = "string", description = "The name to greet" }
},
required = { "name" }
}
}
}
}
}
return script_succeed(), http_respond(200, "application/json"), "application/json", json_encode(resp)
-- 4. 调用工具
elseif method == "tools/call" then
local tool_name = payload.params and payload.params.name
local args = payload.params and payload.params.arguments or {}
if tool_name == "hello" then
local name = args.name or "World"
local resp = {
jsonrpc = "2.0",
id = id,
result = {
content = {
{ type = "text", text = "Hello, " .. name .. "! This is generated by Lua." }
}
}
}
return script_succeed(), http_respond(200, "application/json"), "application/json", json_encode(resp)
end
end
-- 兜底
local err_resp = {
jsonrpc = "2.0",
id = id,
error = { code = -32601, message = "Method not found" }
}
print( "end mcp, succeed" )
return script_succeed(), http_respond(200, "application/json"), "application/json", json_encode(err_resp)
end
#include "CCoroutinableExecute.h"
#include "../../enumeration/OBJECT_ID.h"
#include "../../enumeration/PROCEDURE_RESULT_ID.h"
#include "../../enumeration/SCRIPT_EXECUTE_RESULT.h"
//Begin section for file CCoroutinableExecute.cpp
//TODO: Add definitions that you want preserved
//End section for file CCoroutinableExecute.cpp
namespace
{
static bool StripPort(a_string & host)
{
bool result = false;
if( host.empty() == false )
{
do
{
// [ipv6] 或 [ipv6]:port
a_string::size_type
close = host.rfind( ']' );
if( close != a_string::npos )
{
a_string::size_type
colon = host.find( ':', close + 1 );
if( colon != a_string::npos )
{
host.resize( colon );
result = true;
}
break;
}
// 没有方括号
// hostname,无端口
a_string::size_type first = host.find( ':' );
if( first == a_string::npos ) {
break;
}
// 裸 IPv6,无端口(且格式非法,交给后面的字符校验拒掉)
a_string::size_type last = host.rfind( ':' );
if( first != last ) {
break;
}
host.resize( first );
result = true;
} while( false );
}
return result;
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
static a_string BuildRoute(CHttpRequest & request)
{
//TODO Auto-generated method stub
// host:从 Host 头拿(origin-form 的 URI 里没有 host)
a_string host;
if( !request.Header().GetHeader("Host", host) ) {
host = request.Line().URI().GetHost(); // 兜底:absolute-form
}
ToLower( host );
// 剥掉端口("localhost:80" → "localhost")
StripPort( host );
// path
a_string path = request.Line().URI().GetPath();
ToLower( path );
Trim( path, '/' );
// 路径安全校验
// 1. 拒绝 .. 段(目录穿越)
// 匹配 "/../"、"/.."、"../"、".." 结尾 等
// 2. 拒绝空段连续("//")、"." 段
// 3. 拒绝控制字符和 \0
bool checked = true;
do
{
// 拆成段判断
CStringArray segs;
Splite( path, '/', segs );
for( size_t i = 0, n = segs.size(); i < n; ++ i )
{
if( segs[i] == ".." || segs[i] == "." || segs[i] == "" ) {
checked = false; break;
}
}
if( !checked ) break;
// 控制字符 / \0,和一些非法字符
for( size_t i = 0, n = path.size(); i < n; ++ i )
{
U08 c = static_cast<U08>( path[i] );
if( c < 0x20 || c == 0x7F ) {
checked = false; break;
}
if( c == ':' || c == '[' || c == ']' ) {
checked = false; break;
}
}
if( !checked ) break;
if( host.empty() )
{
checked = false;
break;
}
// host 校验:只允许 a-z 0-9 - . : []
for( size_t i = 0, n = host.size(); i < n; ++ i )
{
U08 c = static_cast<U08>( host[i] );
bool ok =
( c >= 'a' && c <= 'z' ) ||
( c >= '0' && c <= '9' ) ||
c == '-' || c == '.' ||
c == ':' || c == '[' || c == ']';
if( !ok ) {
checked = false; break;
}
}
} while( false );
a_string result;
if( checked )
{
// 拼成 host/path
if( path.empty() ) {
result = host;
} else {
result = host + "/" + path;
}
}
return result;
}
static void ErrorRespond(CHttpRespond & respond, U32 status)
{
respond.Line().SetVersion( 1, 1 );
respond.Line().SetStatus ( status );
respond.Line().SetReason ( CHttpRespondStatus::ToString(status) );
respond.Header()["Connection"] = "close";
respond.Header()["Content-Type"] = "text/plain";
respond.Header()["Content-Length"] = CVariant( static_cast<U64>(0) );
}
static bool OnRespondJson(CJson & respond, const a_string & body_type, const a_string & body, CHttpRespond & output, CHttpBody & output_body)
{
if( respond.GetType() != JVT_OBJECT ) {
return false;
}
U32 status = HSC_OK;
a_string reason = CHttpRespondStatus::ToString( HSC_OK );
if( respond.IsMemberExist("status") ) {
status = respond["status"];
}
if( respond.IsMemberExist("reason") ) {
reason = respond["reason"];
}
output.Line().SetVersion( 1, 1 );
output.Line().SetStatus ( status );
output.Line().SetReason ( reason.c_str() );
// headers: { {"k","v"}, ... }
if( respond.IsMemberExist("headers") )
{
atom::CJson node = respond["headers"];
for( size_t i = 0, n = node.Length(); i < n; ++ i )
{
// 注意:Lua 数组 1 起,CJson::operator[] 0 起
atom::CJson item = node[i];
a_string k = item[0]; // Lua 的 [1]
a_string v = item[1]; // Lua 的 [2]
if( k.empty() == false ) {
output.Header()[ k.c_str() ] = v.c_str();
}
}
}
bool result = output.Line().IsValid();
I64 length = 0;
if( body.empty() == false )
{
output_body.Type() = body_type;
output_body.Body() = body;
output_body.Size() = length = body.length();
}
a_string value;
if( output.Header().GetHeader("Content-Length", value) == false ) {
output.Header()["Content-Length"] = CVariant( length );
}
return result;
}
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
flux::CCoroutinableExecute::CCoroutinableExecute():
connector(0)
{
//TODO Auto-generated method stub
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
flux::CCoroutinableExecute::~CCoroutinableExecute()
{
//TODO Auto-generated method stub
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
U64 flux::CCoroutinableExecute::Run()
{
//TODO Auto-generated method stub
U64 link(0);
a_string route;
CJson input, input_body;
a_string host, origin;
bool has_origin = false;
bool origin_ok = false;
{
CCriticalSectionScope scope( section );
link = connector;
// CORS:提取 host 和 origin
request.Header().GetHeader( "Host", host );
ToLower( host );
// 注意:这个位置可能对IPV6作为Host有兼容性问题,[:]不被路径允许
// 去掉端口
StripPort( host );
has_origin = request.Header().GetHeader( "Origin", origin );
CJsonlizer json;
json << request;
input = json.Obtain();
//printf( "%s\n", input.Stringify().c_str() );
a_string type;
if( request.Header().GetHeader("Content-Type", type) ) {
request_body.Type() = type;
}
json.Clear();
json << request_body;
input_body = json.Obtain();
route = BuildRoute( request );
}
// 攻击/畸形路径——不发响应,立即关闭连接
if( route.empty() )
{
if( link ) {
CHttpService::GetInstance() -> Close( link );
}
SetResult( PID_FAILED_TO_EXECUTE );
return U64_MAX;
}
// 查询跨域白名单
if( has_origin ) {
origin_ok = CHttpService::GetInstance() -> IsValid( host, origin );
}
bool need_respond = true;
// 获取自己的名字,用于协程方法
U64 self = CInstanceUtility::GetInstanceName( nest );
U32 result = PID_FAILED_TO_EXECUTE;
CHttpRespond respond;
CHttpBody respond_body;
do
{
// 404:路由不存在
if( false == CScriptScheduler::
GetInstance() -> IsRouteExist( route.c_str() ) )
{
CLog log;
log << "Route not found: " << route.c_str()
<< ", when run coroutinable execute procedure: " << self
<< end_error;
ErrorRespond( respond, HSC_NOT_FOUND );
break;
}
// 预检请求:不进 Lua,直接回
a_string acrm;
bool is_preflight =
has_origin && (
request.Line ().GetMethod() == RM_OPTIONS ) &&
request.Header().GetHeader( "Access-Control-Request-Method", acrm );
if( is_preflight )
{
respond.Line().SetVersion( 1, 1 );
if( origin_ok )
{
respond.Line().SetStatus( HSC_NO_CONTENT );
respond.Header()["Access-Control-Allow-Origin" ] = origin.c_str();
respond.Header()["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS";
respond.Header()["Access-Control-Allow-Headers"] = "Content-Type, Authorization";
respond.Header()["Access-Control-Max-Age" ] = "86400";
respond.Header()["Vary"] = "Origin";
respond.Header()["Content-Length"] = CVariant( static_cast<U64>(0) );
result = PID_SUCCEED;
}
else
{
respond.Line().SetStatus( HSC_FORBIDDEN );
respond.Line().SetReason( CHttpRespondStatus::ToString(HSC_FORBIDDEN) );
respond.Header()["Content-Length"] = CVariant( static_cast<U64>(0) );
respond.Header()["Vary"] = "Origin";
}
break;
}
// CSRF 防御:破坏性方法要求 Content-Type: application/json
{
U08 method = request.Line().GetMethod();
bool dangerous = (
method == RM_POST ||
method == RM_PUT ||
method == RM_DELETE );
if( dangerous )
{
a_string type;
if( !request.Header().GetHeader("Content-Type", type) )
{
ErrorRespond( respond, HSC_UNSUPPORTED_MEDIA_TYPE );
break;
}
// 取分号前的部分(MIME type),丢弃 charset 等参数
a_string::size_type semi = type.find( ';' );
if( semi != a_string::npos ) {
type.resize( semi );
}
// 去首尾空白("application/json " → "application/json")
Trim( type, ' ' );
Trim( type, '\t' );
// 转为小写
ToLower( type );
// 精确匹配
if( type != "application/json" )
{
ErrorRespond( respond, HSC_UNSUPPORTED_MEDIA_TYPE );
break;
}
}
}
// 准备参数
CScriptArguments args;
args << self << input << input_body;
// 注意:405 错误由脚本处理
// 如:
// -- 只允许 POST
// if method ~= "POST" then
// return true, {
// status = 405,
// reason = "Method Not Allowed",
// headers = {
// { "Allow", "POST" }
// }
// }, "text/plain", ""
// end
a_string error;
if( false ==
CScriptScheduler::GetInstance() ->
RunRoute( route.c_str(), args, error ) )
{
// 如果 Lua 异常时还处于流式中,先关闭流,再回 500
CObjectPtr session =
CHttpService::GetInstance()->GetSession( link );
if( session )
{
CInterface<IHttpSessionEndPoint> segment;
if( segment.Mount(session, IID_HTTP_SESSION_END_POINT) )
{
if( segment -> IsStreamMode() )
{
segment -> CloseStream(); // 状态机会直接推进到CLOSING状态
need_respond = false;
}
// 此时流已关闭,不要再走 ErrorRespond,
// 或者走 ErrorRespond 但用 Connection: close 让对端知道异常
}
}
CLog log;
log << "Failed to execute script route: " << route.c_str()
<< ", when run coroutinable execute procedure: " << self
<< end_error;
if( need_respond )
{
ErrorRespond( respond, HSC_INTERNAL_SERVER_ERROR );
if( error.empty() ) {
error = "Internal Server Error";
}
//#ifdef _DEBUG
respond.Header()["Content-Length"] = CVariant( static_cast<U64>( error.length() ) );
respond_body.Type() = "text/plain";
respond_body.Size() = static_cast<I64>( error.length() );
respond_body.Body() = error;
}
//#endif // DEBUG
break;
}
U08 retval = 0;
args >> retval;
if( retval == SER_FAILED )
{
ErrorRespond( respond, HSC_INTERNAL_SERVER_ERROR );
break;
}
else
if( retval == SER_SUCCEED )
{
CJson output;
a_string output_body_type, output_body;
args >> output >> output_body_type >> output_body;
if( false == OnRespondJson(
output, output_body_type, output_body, respond, respond_body) )
{
ErrorRespond( respond, HSC_INTERNAL_SERVER_ERROR );
break;
}
}
else
if( retval == SER_NO_RESPOND )
{
respond.Line().SetVersion( 1, 1 );
respond.Line().SetStatus( HSC_NO_CONTENT );
respond.Line().SetReason( "No Content" );
respond.Header()["Content-Length"] = CVariant( static_cast<U64>(0) );
// 保持 need_respond = true,让底部去调用 OnRespond
// 如果这是流式操作,流式相关接口会提前处理关闭,不会用
// 到这里,这个主要是为了通知HTTP服务推进状态机
//need_respond = false;
}
result = PID_SUCCEED;
} while( false );
if( need_respond )
{
// 补 CORS 头(无论成功还是失败响应)
if( has_origin && origin_ok )
{
respond.Header()["Access-Control-Allow-Origin"] = origin.c_str();
respond.Header()["Vary"] = "Origin";
}
// 通过 connector 找到 session,回填响应
if( link )
{
CObjectPtr stub = CHttpService::GetInstance() -> GetSession( link );
if( stub )
{
CInterface<IHttpSessionEndPoint> segment;
if( segment.Mount(stub, IID_HTTP_SESSION_END_POINT) ) {
segment -> OnRespond( respond, respond_body );
}
}
}
}
SetResult( result );
return U64_MAX;
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
bool flux::CCoroutinableExecute::Prepare(CArchive & in)
{
//TODO Auto-generated method stub
CInterface<IConfigable<CJson> > segment;
if( segment.Mount(this, IID_CONFIGABLE) )
{
CJson value;
{
CCriticalSectionScope scope( section );
in >> connector >> request >> request_body;
value = connector;
}
segment -> SetConfig( value );
}
return true;
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
U32 flux::CCoroutinableExecute::GetResult(CArchive & result)
{
//TODO Auto-generated method stub
UNREFERENCED_PARAMETER( result );
CCriticalSectionScope scope( section );
return code;
}
开发日志
日期:2026-10-21
主题:HTTP/WebSocket 底层闭环与 MCP 协议全链路打通
一、 核心成果
-
HTTP 与 SSE 流式引擎完成
-
完善 HTTP 状态机,解决 Keep-Alive 复用、CORS 跨域与请求走私防御。
-
实现基于协程的 SSE 流式输出,支持
http_stream_begin/write/close非阻塞推流。
-
-
WebSocket 核心彻底解耦
-
将原
FrameSpliter拆分为CoreController(纯协议引擎)、Wrapper(业务胶水层)与Entry(门面),成功复用于 HTTP 与 Game 双端。 -
修复 TCP 优雅关闭问题(
shutdown(SD_SEND)+closesocket),彻底消除 SSE/WSS 结束时的 RST 报错。 -
增加分片重组 OOM 防御与连接超时熔断机制。
-
-
JSON 与变体层(CVariant/CJson)深度重构
-
增加原生
VT_BOOL支持,修复布尔值序列化及运算逻辑。 -
修复历史遗留 Bug:数字字符串(如
"2.0")被误判为浮点数、空对象{}解析丢失、嵌套节点 Key 丢失。
-
-
MCP Server 正式跑通
-
基于 HTTP/SSE 底座与 Lua 脚本,成功实现 MCP 的
initialize握手、notifications/initialized通知及tools/list、tools/call接口。 -
已通过官方 MCP Inspector 验证,工具调用响应完全符合 JSON-RPC 2.0 与 Zod 校验规范。
-
二、 性能与稳定性
-
全链路无阻塞(C++ 协程 + Lua 挂起),单机可支撑高并发长连接。
-
内存生命周期闭环完整(连接断开后对象计数完美归零)。
三、 下阶段计划
-
引入 Ollama / vLLM 本地大模型,在 Lua 侧封装
chat工具。 -
利用现有 SSE 流式底座,实现大模型 Token 级流式打字机输出。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/draracle/article/details/168661613



