MCP项目笔记七(插件 calculator)

C++ 构建一个插件式计算器工具系统

1. 整体功能概述

代码实现的是一个 C++ 插件式数学计算器工具系统,是一个可以被宿主程序(如 AI Agent、命令行工具等)动态加载的插件。核心能力有三:

  • 字符串数学表达式解析 :支持 3 + sin(1)(2^3)^2sqrt(9) * log(10) 这类复合表达式的实时求值。
  • 独立数学工具:提供加、减、乘、除、幂、开方、阶乘等 8 个基础工具,参数通过 JSON 传入。
  • 标准插件接口 :通过 PluginAPI 导出,宿主程序可以动态发现工具列表、发送请求、获取结果,整个通信协议基于 JSON。

用一句话概括:

「基于递归解析的表达式计算引擎 + JSON 驱动的插件工具系统」


2. 插件的五层架构

整个插件可以清晰地划分为以下五层,每层职责独立、边界清楚:

层级 名称 主要内容
L0 插件接口导出层 CreatePlugin / DestroyPlugin,供宿主动态加载,遵循 C ABI
L1 插件生命周期管理层 GetName / GetVersion / Initialize / Shutdown / GetToolCount / GetTool
L2 请求分发层 HandleRequestImpl:解析 JSON 请求 → 按 toolName 路由 → 返回统一 JSON 响应
L3 工具注册表(Metadata) PluginTool methods[]:工具名、描述、JSON Schema 参数定义
L4 计算核心层 ExpressionParser(递归下降)+ factorial() 等独立数学函数

最底层是真正的「数学能力」,最顶层是给宿主程序看的「插件入口」。中间的 HandleRequestImpl 起到总调度作用,是整个插件最核心的函数。


3. 核心:递归下降表达式解析器

ExpressionParser 是这套插件最有技术含量的部分,它用递归下降法(Recursive Descent Parsing)实现了一个完整的数学表达式解析器,无需第三方库。

运算优先级分层

解析器将语法按运算优先级分为四层,形成调用链:

复制代码
parseExpression     // 最低优先级:+ 和 -
    └── parseTerm       // 次低:* / %
        └── parsePower      // 幂运算:^ (右结合)
            └── parseFactor     // 最高:数字、括号、函数、正负号
                ├── parseFunction   // 识别 sin(x) 等
                └── parseNumber     // 读取 123、45.6 等

右结合的幂运算

幂运算 ^ 在数学上是右结合的,即 (2{32} = 2{(32)} = 512),而不是 ((23)2 = 64)。代码通过在 parsePower 中递归调用自身来实现这一点:

cpp 复制代码
static double parsePower(const std::string& expr, size_t& pos) {
    double base = parseFactor(expr, pos);
    if (pos < expr.length() && expr[pos] == '^') {
        pos++;
        double exponent = parsePower(expr, pos); // 递归 → 右结合
        return std::pow(base, exponent);
    }
    return base;
}

支持的内置函数

parseFunction 识别函数名后,映射到标准库函数:

函数名 对应 std 函数 说明
sin / cos / tan std::sin / cos / tan 三角函数(弧度制)
sqrt std::sqrt 平方根
abs std::abs 绝对值
log std::log 自然对数 (\ln)
exp std::exp (e^x)
floor / ceil / round 同名 取整函数

4. 工具注册表------Metadata 定义

插件使用静态数组 methods[] 定义工具清单,每个条目包含三个字段:工具名、描述、JSON Schema。这是整个「接口契约」的来源,宿主程序通过 GetToolImpl 枚举所有工具,再按 Schema 构造请求。

cpp 复制代码
static PluginTool methods[] = {
    {
        "calculator",
        "Evaluates a mathematical expression",
        "{\"type\":\"object\","
        "\"properties\":{\"expression\":{\"type\":\"string\"}},"
        "\"required\":[\"expression\"]}"
    },
    {
        "add",
        "Adds two numbers",
        "{\"type\":\"object\","
        "\"properties\":{\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"}},"
        "\"required\":[\"a\",\"b\"]}"
    },
    // ... 其余工具
};

这种「先声明接口再实现功能」的设计,让整个系统具备良好的自描述性,非常适合被 AI Agent 或自动化框架动态发现和调用。


5. HandleRequest 的完整流程

HandleRequestImpl 是插件的总调度中心,负责完整的「请求 → 处理 → 响应」闭环。

执行步骤:

  1. 初始化响应结构content: []isError: false
  2. 解析请求json::parse(req) 解析外部传入的 JSON 字符串
  3. 读取路由信息 :取出 params.name(工具名)和 params.arguments(参数对象)
  4. 按 toolName 分发:进入对应 if / else if 分支,调用计算逻辑
  5. 成功 :构造 {"type":"text","text":"3+5 = 8"},推入 content 数组
  6. 异常catch 捕获,置 isError: true,填入错误信息
  7. 序列化返回response.dump() → 分配 char* → 返回给宿主

成功与失败的统一返回格式

不管成功还是失败,返回格式始终一致,宿主程序只需检查 isError 字段即可:

json 复制代码
// 成功
{
  "content": [{ "type": "text", "text": "3 + 5 = 8" }],
  "isError": false
}

// 失败
{
  "content": [{ "type": "text", "text": "Error: Division by zero" }],
  "isError": true
}

为什么返回 char* 而不是 std::string

因为插件接口采用 C ABI(应用二进制接口) ,使用 extern "C" 导出,确保兼容 C 语言或其他非 C++ 宿主程序。std::string 是 C++ 特有类型,跨模块传递可能引发二进制不兼容问题,因此选择 char*


6. 总结

这份代码展示了一个设计合理、职责清晰的 C++ 插件工程实践。它将数学计算能力封装成 JSON 驱动的工具接口,让宿主程序(如 AI Agent)可以像调用 API 一样使用它。

「先声明接口,再接收请求,处理参数并执行功能,最后把结果按 JSON 协议返回。」

一个 tool 插件的最小闭环,不外乎这四步:
定义 Metadata → 解析参数 → 执行逻辑 → 返回 JSON

掌握这个骨架,就可以把任何计算能力或业务逻辑封装为可被动态调用的工具插件。

相关推荐
j7~11 小时前
【C++】C++ 继承全解析:从基本语法到菱形继承的底层原理
开发语言·c++·继承·组合·虚继承·#暑假·七月创作之星博客挑战赛
数行拙笔11 小时前
C++客户端---String类型
开发语言·c++·bootstrap
左左右右左右摇晃11 小时前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
草莓熊Lotso11 小时前
【Linux网络】深入理解Linux IO多路复用:从本质到select服务器实战
linux·运维·服务器·c语言·网络·数据库·c++
.Hypocritical.12 小时前
Maven笔记——2
java·笔记·maven
乐橙开放平台12 小时前
养殖 SaaS 笔记:乐橙 IoT 物模型管环境,视频 OpenAPI 管回看
数据库·笔记·物联网·mysql·音视频
love530love20 小时前
【笔记】AutoClaw NSIS 安装器卡死、进程杀不掉、目录删不了?我是这么解决的
人工智能·windows·笔记·agent
selfsongs1 天前
指针、内存管理、现代 C++ 特性
c++
谙弆悕博士1 天前
系统集成项目管理工程师教程(第3版)笔记——第2章:信息技术发展
笔记·计算机网络·信息安全·学习方法·业界资讯·软考·系统分析师
吃着火锅x唱着歌1 天前
Effective C++ 学习笔记 条款37 绝不重新定义继承而来的缺省参数值
c++·笔记·学习