diff --git a/application/api/controller/mcp/Base.php b/application/api/controller/mcp/Base.php new file mode 100644 index 0000000..30c5719 --- /dev/null +++ b/application/api/controller/mcp/Base.php @@ -0,0 +1,33 @@ + 'list', + 'description' => "查找多样青春夜校的所有课程列表信息 + 表结构sql为: + `id` int(11) NOT NULL AUTO_INCREMENT, + `manystore_id` int(11) NOT NULL COMMENT '机构账号id', + `shop_id` int(11) NOT NULL COMMENT '机构店铺id', + `user_id` int(11) NOT NULL COMMENT '主讲师用户id', + `teacher_id` int(11) NOT NULL COMMENT '老师id', + `classes_cate_ids` varchar(255) NOT NULL COMMENT '平台课程分类ids', + `classes_label_ids` varchar(255) DEFAULT NULL COMMENT '平台课程类型ids', + `self_label_tag` varchar(1000) DEFAULT NULL COMMENT '机构特色标签', + `add_type` enum('1','2') NOT NULL DEFAULT '2' COMMENT '添加人类型:1=机构,2=总后台', + `add_id` int(11) NOT NULL DEFAULT '0' COMMENT '添加人id', + `title` varchar(255) NOT NULL COMMENT '标题', + `headimage` varchar(500) NOT NULL COMMENT '头图', + `images` varchar(1500) NOT NULL COMMENT '轮播图', + `type` enum('out','in') NOT NULL DEFAULT 'in' COMMENT '地点类型:out=户外,in=室内', + `classes_num` int(10) NOT NULL COMMENT '课时数', + `address_type` enum('1','2') NOT NULL DEFAULT '1' COMMENT '地址类型:1=按机构,2=独立位置', + `address_city` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '城市选择', + `province` int(11) DEFAULT NULL COMMENT '省编号', + `city` int(11) DEFAULT NULL COMMENT '市编号', + `district` int(11) DEFAULT NULL COMMENT '县区编号', + `address` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '店铺地址', + `address_detail` varchar(200) CHARACTER SET utf8 DEFAULT '' COMMENT '店铺详细地址', + `longitude` varchar(30) CHARACTER SET utf8 DEFAULT '' COMMENT '经度', + `latitude` varchar(30) CHARACTER SET utf8 DEFAULT '' COMMENT '纬度', + `classes_date_text` varchar(500) NOT NULL COMMENT '上课日期', + `classes_time_text` varchar(500) NOT NULL COMMENT '上课时间', + `content` text NOT NULL COMMENT '课程详情', + `notice` text NOT NULL COMMENT '课程须知', + `virtual_num` int(11) DEFAULT '0' COMMENT '虚拟报名人数', + `sale` int(11) DEFAULT '0' COMMENT '总销量', + `price` decimal(10,2) NOT NULL COMMENT '售价', + `underline_price` decimal(10,2) DEFAULT NULL COMMENT '划线价', + `virtual_collect` int(11) DEFAULT '0' COMMENT '虚拟收藏量', + `status` enum('1','2','3') DEFAULT '1' COMMENT '状态:1=上架,2=下架,3=平台下架', + `auth_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '审核状态:0=待审核,1=审核通过,2=审核失败', + `reason` varchar(200) CHARACTER SET utf8 DEFAULT '' COMMENT '审核不通过原因', + `auth_time` int(11) DEFAULT '0' COMMENT '审核时间', + `admin_id` int(11) DEFAULT '0' COMMENT '审核管理员id', + `weigh` int(11) DEFAULT '0' COMMENT '权重', + `recommend` enum('0','1') DEFAULT '0' COMMENT '平台推荐:0=否,1=是', + `hot` enum('0','1') DEFAULT '0' COMMENT '平台热门:0=否,1=是', + `new` enum('0','1') DEFAULT '0' COMMENT '平台最新:0=否,1=是', + `selfhot` enum('0','1') DEFAULT '0' COMMENT '机构热门:0=否,1=是', + `createtime` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `updatetime` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间', + `deletetime` bigint(11) unsigned DEFAULT NULL COMMENT '删除时间', + `virtual_people` int(11) DEFAULT '0' COMMENT '虚拟参与人数', + `feel` enum('0','1') DEFAULT '0' COMMENT '是否免费:0=否,1=是', + `limit_num` int(11) DEFAULT '0' COMMENT '总限定人数(0不限制)', + `sign_num` int(11) DEFAULT '0' COMMENT '总已报名人数', + `verification_num` int(11) DEFAULT NULL COMMENT '总已核销人数', + `views` bigint(20) DEFAULT '0' COMMENT '浏览量', + `start_time` bigint(20) DEFAULT NULL COMMENT '开始时间', + `end_time` bigint(20) DEFAULT NULL COMMENT '结束时间', + `collect` bigint(20) DEFAULT NULL COMMENT '实际收藏量', + `classes_type` varchar(2000) DEFAULT NULL COMMENT '课程类型', + `classes_star` decimal(10,1) NOT NULL DEFAULT '5.0' COMMENT '课程评分', + `star_number` int(11) NOT NULL DEFAULT '0' COMMENT '评价数', + ", + 'inputSchema' => [ + 'type' => 'object', + 'properties' => [ + 'keywords' => [ + 'description' => '搜索关键字', + 'example' => "书法", + 'type' => 'string', + ], + 'page' => [ + 'description' => '页数(必填)', + 'example' => 1, + 'type' => 'number', + ], + 'limit' => [ + 'description' => '每页数量(必填)', + 'example' => 20, + 'type' => 'number', + ] + ], + 'required' => [ 'page', 'limit'] + ] + ], + [ + 'name' => 'orderlist', + 'description' => " + 查找多样青春夜校当前课程的订单列表,想获取订单列表,请先获取课程列表(list工具)信息中对应的课程里的id + 表结构sql为: + `id` int(11) NOT NULL AUTO_INCREMENT, + `order_no` varchar(255) DEFAULT NULL COMMENT '订单号', + `pay_no` varchar(255) DEFAULT NULL COMMENT '微信支付单号', + `user_id` int(11) DEFAULT NULL COMMENT '下单人id', + `manystore_id` int(11) DEFAULT NULL COMMENT '机构账号id', + `shop_id` int(11) DEFAULT NULL COMMENT '机构店铺id', + `code` varchar(255) DEFAULT NULL COMMENT '核销码', + `codeimage` varchar(500) DEFAULT NULL COMMENT '核销二维码图片', + `codeoneimage` varchar(500) DEFAULT NULL COMMENT '核销一维码图片', + `classes_lib_id` int(11) DEFAULT NULL COMMENT '课程id', + `classes_order_detail_id` int(11) DEFAULT NULL COMMENT '订单课程id', + `beforeprice` decimal(10,2) DEFAULT NULL COMMENT '订单优惠前金额', + `totalprice` decimal(10,2) DEFAULT NULL COMMENT '订单应付金额', + `payprice` decimal(10,2) DEFAULT NULL COMMENT '订单实付金额', + `pay_type` enum('yue','wechat') DEFAULT 'wechat' COMMENT '支付方式:yue=余额,wechat=微信', + `status` enum('-3','0','3','4','6','9') DEFAULT '0' COMMENT '订单状态:-3=已取消,0=待支付,3=使用中,4=售后中,6=已退款,9=已完成', + `before_status` enum('-3','0','3','6','9') DEFAULT '0' COMMENT '售后前状态:-3=已取消,0=未售后,3=使用中,6=已退款,9=已完成', + `server_status` enum('0','3','6') DEFAULT '0' COMMENT '售后订单状态:0=正常,3=售后中,6=售后完成', + `reason` varchar(500) DEFAULT NULL COMMENT '售后申请原因', + `auth_manystore_id` int(11) DEFAULT NULL COMMENT '机构审核人id', + `auth_opinion` varchar(500) DEFAULT NULL COMMENT '机构审核意见', + `auth_file` varchar(500) DEFAULT NULL COMMENT '机构审核附件', + `admin_id` int(11) DEFAULT NULL COMMENT '平台审核人id', + `result_status` enum('0','3','6') DEFAULT '0' COMMENT '售后处理结果:0=未售后,3=退款,6=驳回', + `result_text` varchar(1000) DEFAULT NULL COMMENT '售后处理结果说明', + `result_file` varchar(500) DEFAULT NULL COMMENT '售后处理结果说明附件', + `canceltime` bigint(20) DEFAULT NULL COMMENT '取消时间', + `paytime` bigint(20) DEFAULT NULL COMMENT '支付时间', + `finishtime` bigint(20) DEFAULT NULL COMMENT '完成时间', + `refundtime` bigint(20) DEFAULT NULL COMMENT '退款时间', + `total_refundprice` decimal(10,2) DEFAULT NULL COMMENT '应退款金额', + `real_refundprice` decimal(10,2) DEFAULT NULL COMMENT '实际退款金额', + `sub_refundprice` decimal(10,2) DEFAULT NULL COMMENT '剩余未退金额', + `createtime` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `updatetime` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间', + `deletetime` bigint(11) unsigned DEFAULT NULL COMMENT '删除时间', + `pay_json` varchar(2000) DEFAULT NULL COMMENT '三方支付信息json', + `platform` varchar(255) DEFAULT NULL COMMENT '支付平台', + `classes_service_order_id` int(11) DEFAULT '0' COMMENT '售后订单id', + `classes_lib_spec_id` int(11) DEFAULT '0' COMMENT '快捷下单课时id', + `classes_evaluate_id` int(11) DEFAULT '0' COMMENT '课程反馈id', + ", + 'inputSchema' => [ + 'type' => 'object', + 'properties' => [ + 'classes_lib_id' => [ + 'description' => '课程id,通过课程列表信息获取的id字段', + 'example' => 1202, + 'type' => 'int', + ], + 'page' => [ + 'description' => '页数(必填)', + 'example' => 1, + 'type' => 'int', + ], + 'limit' => [ + 'description' => '每页数量(必填)', + 'example' => 20, + 'type' => 'int', + ] + ], + 'required' => [ 'page', 'limit','classes_lib_id'] + ] + ], + + ]; + + + public function sse() + { + // 设置 CORS 头 + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: POST, OPTIONS'); + header('Access-Control-Allow-Headers: Content-Type, Authorization'); + + // 处理 OPTIONS 预检请求 + if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(200); + exit; + } + + // 设置 SSE 响应头 + header('Content-Type: text/event-stream'); + header('Cache-Control: no-cache'); + header('Connection: keep-alive'); + header('X-Accel-Buffering: no'); + + // 获取原始输入 + $input = file_get_contents('php://input'); + file_put_contents('mcp_debug.log', date('Y-m-d H:i:s').' Raw input: '.$input.PHP_EOL, FILE_APPEND); + + // 解析 JSON-RPC 请求 + $request = json_decode($input, true); + + // 处理请求 + if (isset($request['method'])) { + switch($request['method']) { + case 'initialize': + $this->handleInitializeRequest($request); + break; + case 'function': + case 'tools/call': // 新增支持tools/call方法 + $this->handleFunctionRequest($request); + break; + case 'ping': + $this->sendJsonRpcResponse($request['id'] ?? null, ['result' => 'pong']); + break; + case 'notifications/initialized': + $this->sendJsonRpcResponse($request['id'] ?? null, ['result' => 'success']); + break; + case 'tools/list': + $this->sendJsonRpcResponse($request['id'] ?? null, ['tools' => self::ADD_FUNCTION]); + break; + default: + $this->handleOtherRequests($request); + } + } else { + $this->handleOtherRequests($request); + } + } + + /** + * 处理初始化请求 + */ + private function handleInitializeRequest($request) + { + $id = $request['id'] ?? null; + + // 记录完整的初始化请求内容 + file_put_contents('mcp_debug.log', "Received initialize request: " . json_encode($request) . PHP_EOL, FILE_APPEND); + + // 发送初始化响应 + $this->sendJsonRpcResponse($id, [ + 'capabilities' => [ + 'progress' => true, + 'functions' => self::ADD_FUNCTION + ], + 'serverInfo' => [ + 'name' => 'Calculator Server', + 'version' => '1.0.0', + 'vendor' => 'MyCompany', + 'license' => 'MIT' + ], + 'protocolVersion' => '2025-03-26' + ]); + } + + /** + * 处理函数调用请求 + */ + public function handleFunctionRequest($request) + { + $id = $request['id'] ?? null; + $function = $request['params']['function'] ?? ''; + $arguments = $request['params']['arguments'] ?? []; + + $function = $request['params']['name'] ?? 'list'; + + switch ($function){ + case 'list': + + // 解构参数 + $keywords = $arguments['keywords'] ?? 0; + $page = $arguments['page'] ?? 1; + $limit = $arguments['limit'] ?? 1; + $params = [ + 'keywords' => $keywords, + 'page' => (int)$page, + 'limit' => (int)$limit, + "order" => "normal", + "status" => "1,2,3", + "auth_status"=> "-1", + "is_expire" => "0" + ]; + + // 发送进度通知 + $this->sendNotification('progress', ['id' => $id, 'message' => '正在获取数据...']); + try { + $result = ClassesLib::getVaildList($params); + }catch ( \Exception $e){ + $this->sendJsonRpcResponse($id, [ + 'error' => [ + 'code' => -32603, + 'message' => 'Internal error', + 'data' => $e->getMessage() + ] + ]); + } + + + $this->sendJsonRpcResponse($id, [ + 'content' => [ + [ + 'type' => 'list', + 'text' => $result // 返回格式化的时间和时区信息 + ] + ] + ]); + + break; + case 'orderlist': + + // 解构参数 + $classes_lib_id = $arguments['classes_lib_id'] ?? 0; + $page = $arguments['page'] ?? 1; + $limit = $arguments['limit'] ?? 1; + + + // 发送进度通知 + $this->sendNotification('progress', ['id' => $id, 'message' => '正在获取数据...']); + try { + $result = OrderModel::allList(0,(int)$page, (int)$limit,'',"0,3,4,6,9",(int)$classes_lib_id); + }catch ( \Exception $e){ + $this->sendJsonRpcResponse($id, [ + 'error' => [ + 'code' => -32603, + 'message' => 'Internal error', + 'data' => $e->getMessage() + ] + ]); + } + + + $this->sendJsonRpcResponse($id, [ + 'content' => [ + [ + 'type' => 'list', + 'text' => $result // 返回格式化的时间和时区信息 + ] + ] + ]); + + + break; + + + } + + + } + + /** + * 验证加法参数 + * @param array $arguments + * @param mixed $id + * @return array|bool 返回true表示验证通过,否则返回错误响应数组 + */ + private function validateAddParameters(array $arguments, $id) + { + if (!isset($arguments['a'], $arguments['b'])) { + return [ + 'error' => [ + 'code' => -32602, + 'message' => 'Invalid params', + 'data' => 'Missing required parameters: a and b' + ] + ]; + } + + if (!is_numeric($arguments['a']) || !is_numeric($arguments['b'])) { + return [ + 'error' => [ + 'code' => -32602, + 'message' => 'Invalid params', + 'data' => 'Parameters must be numbers' + ] + ]; + } + + return true; + } + + /** + * 处理其他类型的请求 + */ + private function handleOtherRequests($request) + { + if (!$request || !isset($request['function'])) { + $this->sendJsonRpcResponse($request['id'] ?? null, ['error' => 'Invalid request format']); + return; + } + + if ($request['function'] === 'add' || $request['function'] === 'tools/call') { + $a = $request['params']['a'] ?? 0; + $b = $request['params']['b'] ?? 0; + + if (!is_numeric($a) || !is_numeric($b)) { + $this->sendEvent('error', 'Parameters must be numbers'); + return; + } + + // 模拟处理延迟 + sleep(1); + + $result = $this->calculateAdd($a, $b); + $this->sendEvent('result', [ + 'operation' => 'add', + 'result' => $result, + 'expression' => "$a + $b = $result" + ]); + } else { + $this->sendEvent('error', 'Unsupported function: ' . $request['function']); + } + } + + /** + * 执行加法计算 + */ + private function calculateAdd(float $a, float $b): float + { + return $a + $b; + } + + /** + * 发送 JSON-RPC 响应 + */ + private function sendJsonRpcResponse($id, $resultOrError) + { + $response = [ + 'jsonrpc' => '2.0', + 'id' => $id + ]; + + if (isset($resultOrError['error'])) { + $response['error'] = $resultOrError['error']; + } else { + $response['result'] = $resultOrError; + } + + $this->sendStreamData(json_encode($response, JSON_UNESCAPED_UNICODE)); + } + + /** + * 发送通知(用于进度更新等) + */ + private function sendNotification($method, $params) + { + $notification = [ + 'jsonrpc' => '2.0', + 'method' => $method, + 'params' => $params + ]; + + $this->sendStreamData(json_encode($notification, JSON_UNESCAPED_UNICODE)); + } + + /** + * 发送标准 SSE 事件 + */ + private function sendEvent($type, $data) + { + $jsonData = json_encode($data, JSON_UNESCAPED_UNICODE); + echo "event: $type\n"; + echo "data: $jsonData\n\n"; + $this->flushBuffers(); + file_put_contents('mcp_debug.log', "Sent event: $type - $jsonData".PHP_EOL, FILE_APPEND); + } + + /** + * 发送流数据 + */ + private function sendStreamData(string $data) + { + echo "data: $data\n\n"; + $this->flushBuffers(); + file_put_contents('mcp_debug.log', "JSON-RPC Response: $data".PHP_EOL, FILE_APPEND); + } + + /** + * 刷新输出缓冲区 + */ + private function flushBuffers() + { + ob_flush(); + flush(); + } +} \ No newline at end of file