本文旨在梳理 main.cppMCP Server 中承担的核心职责,明确服务启动阶段的关键执行路径,包括 程序初始化插件加载MCP 方法注册 以及 传输层启动 四个部分。

从工程实现角度看,main.cpp 本身并不负责具体工具逻辑,而是作为整个服务的装配入口,完成以下目标:

  • 初始化服务器运行环境,包括服务名称、插件目录、日志目录与运行模式;
  • 加载外部插件并建立插件与服务端之间的通知通道;
  • 将 MCP 标准方法注册为可执行回调,实现协议层到插件层的映射;
  • 选择并启动传输层,使服务器进入正式对外服务状态。

程序模块

围绕 main.cpp 的实现逻辑,可以将整个 MCP Server 的启动主线归纳为以下四个模块。

1. 程序启动流程

在这里插入图片描述

功能定位:完成服务运行前的基础环境准备,包括核心对象创建、参数解析、信号处理与日志初始化。

  • 基础对象初始化:程序进入 main 后,首先初始化服务器名称、插件目录、日志目录、输出级别等运行变量,并创建 PluginsLoaderServer 两个核心对象。其中 PluginsLoader 负责后续插件扫描与加载,Server 负责承载 MCP 回调注册与请求处理。
  • 信号处理机制:通过注册 SIGINT 对应的 stop_handler,当用户触发 Ctrl+C 时,程序会调用 server->Stop() 执行优雅退出,而不是直接终止进程。
  • 命令行参数解析:使用 popl 解析运行参数,支持服务器名称、插件目录、日志目录、详细输出模式以及 SSE 传输模式等配置项。
  • 日志系统初始化:程序根据当前 UTC 时间生成日志文件名,并使用 AixLog 初始化日志输出系统,记录启动版本、Transport 信息及运行状态。

2. 加载插件

功能定位:通过插件加载器扫描插件目录,动态注入工具、Prompt 与资源能力,并为插件建立服务端通知通道。

  • 扫描与加载main.cpp 调用 loader->LoadPlugins(plugins_directory) 扫描插件目录并加载插件。插件加载成功后,Server 才具备实际的工具执行能力。
  • 插件能力来源:后续 tools/listtools/callprompts/listprompts/getresources/listresources/read 六类 MCP 方法的实际能力,都来源于这些被加载的插件,而不是主程序自身。
  • 通知系统绑定:插件加载完成后,程序遍历所有插件实例,为每个插件分配 NotificationSystem,并将 SendToClient 绑定到统一的 ClientNotificationCallbackImpl,使插件具备主动向客户端发送通知的能力。
  • 并发安全控制:通知回调内部通过互斥锁保护共享访问,确保多个插件同时向客户端发送消息时不会发生竞态。

3. 注册 callback

功能定位:将 MCP 协议方法与插件能力建立映射关系,完成从 JSON-RPC 请求到插件执行逻辑的分发。

  • 服务基础属性设置:在注册回调前,程序首先调用 server->Name(name)server->VerboseLevel(verbose ? 1 : 0) 配置服务名称和输出等级。

  • 工具类方法注册

    • tools/list:遍历工具类插件,返回所有工具的名称、描述与 inputSchema
    • tools/call:根据请求中的工具名称查找目标插件,调用 HandleRequest() 执行业务逻辑,并将结果解析为 JSON 返回。
  • Prompt 类方法注册

    • prompts/list:枚举 Prompt 类型插件,返回 Prompt 名称、描述与参数定义。
    • prompts/get:根据名称定位 Prompt,并执行插件逻辑生成具体内容。
  • 资源类方法注册

    • resources/list:返回所有资源插件暴露的资源列表,包括名称、描述、URI 与 MIME 类型。
    • resources/read:根据请求中的 URI 定位目标资源,并调用插件读取内容。

从结构上看,这一层实现的是标准的协议分发机制:客户端发送的是 MCP 方法名,服务器内部执行的是插件实例的 HandleRequest()。因此,main.cpp 的 callback 注册本质上是在完成 MCP 协议接口 -> 插件执行入口 的映射。

4. 启动 transport

功能定位:根据运行参数选择具体传输模式,并将 Server 与 Transport 绑定,使服务开始接收并处理请求。

  • 传输层抽象:程序以 std::shared_ptr<vx::ITransport> 统一持有传输层实例,体现出对具体通信方式的抽象封装。

  • 模式选择逻辑

    • 启用 --sse 时,创建 vx::transport::SSE
    • 默认情况下,使用 vx::transport::Stdio
  • 运行信息输出:日志中会记录 Transport 名称、版本与端口信息,用于确认当前服务运行模式。

  • 正式启动服务:最后通过 server->Connect(transport) 将服务器与选定的传输层绑定,进入服务状态。此前完成的 callback 注册与插件加载,也在这一时刻开始真正对外生效。


调用链

main.cpp 的实现逻辑来看,服务端调用链可以概括为:启动服务 -> 加载插件 -> 注册方法 -> 通过 Transport 接收请求 -> 定位插件并执行 -> 返回响应

更具体地说,整个运行路径如下:

程序启动后,首先初始化 ServerPluginsLoader、日志系统和运行参数;然后根据配置选择 StdioSSE 作为传输层;接着从插件目录中加载所有插件,并为插件建立通知通道;完成这些准备后,main.cpp 通过 OverrideCallback 注册 tools/listtools/callprompts/listprompts/getresources/listresources/read 六个标准 MCP 方法;最后调用 server->Connect(transport),使服务开始监听来自客户端的 JSON-RPC 请求。

调用链逻辑

本文件体现出的核心调用链可以概括为 “初始化 -> 注入能力 -> 注册协议 -> 接收请求 -> 插件执行 -> 返回结果”

  1. 初始化阶段
    程序启动,创建 ServerPluginsLoader,完成参数解析、信号注册和日志初始化。

  2. 能力注入阶段
    通过 LoadPlugins() 扫描插件目录,动态注入 tools、prompts 和 resources 三类能力,并为插件建立通知系统。

  3. 协议绑定阶段
    使用 server->OverrideCallback(...) 注册六个标准 MCP 方法,使外部协议请求可以映射到内部插件逻辑。

  4. 请求执行阶段
    服务器通过选定的 Transport 接收 JSON-RPC 请求,根据 method 名称进入对应 callback,再由 callback 根据工具名、Prompt 名或 URI 定位具体插件,调用 HandleRequest() 执行逻辑。

  5. 结果返回阶段
    插件返回执行结果后,主程序负责将其解析并封装为标准 JSON 响应,通过当前 Transport 原路返回给客户端。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐