From 2e081d5b87f865d392881bcecce9fc8a70658d84 Mon Sep 17 00:00:00 2001 From: qinzexin <“731344816@qq.com”> Date: Wed, 25 Jun 2025 18:31:47 +0800 Subject: [PATCH] =?UTF-8?q?mcp=E5=8D=8F=E8=AE=AE=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mcp.php | 0 application/api/controller/mcp/Base.php | 33 +++ application/api/controller/mcp/Mcp.php | 298 ++++++++++++++++++++++++ public/bailian-mcp.json | 63 +++++ public/manifest.json | 17 ++ public/mcp_debug.log | 32 +++ public/openapi.yaml | 46 ++++ 7 files changed, 489 insertions(+) create mode 100644 Mcp.php create mode 100644 application/api/controller/mcp/Base.php create mode 100644 application/api/controller/mcp/Mcp.php create mode 100644 public/bailian-mcp.json create mode 100644 public/manifest.json create mode 100644 public/mcp_debug.log create mode 100644 public/openapi.yaml diff --git a/Mcp.php b/Mcp.php new file mode 100644 index 0000000..e69de29 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 @@ + 'add', + 'description' => '计算两个数字的和', + 'toolType' => 'function', + 'parameters' => [ + 'a' => [ + 'description' => '第一个加数(必填)', + 'example' => 10 + ], + 'b' => [ + 'description' => '第二个加数(必填)', + 'example' => 20 + ] + ], + 'metadata' => [ + 'capability' => [ + 'streaming' => false, + 'batch' => true, + 'transactional' => false + ], + 'version' => '1.0.0', + 'vendor' => 'MyCompany', + 'protocol' => 'json-rpc-2.0' + ] + ]; + + + 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'] ?? []; + +// // 恢复函数名验证 +// if ($function !== 'add') { +// $this->sendJsonRpcResponse($id, [ +// 'error' => [ +// 'code' => -32601, +// 'message' => 'Unsupported function', +// 'data' => "Function not found: $function" +// ] +// ]); +// return; +// } + +// // 提取参数验证为独立方法 +// $validationResult = $this->validateAddParameters($arguments, $id); +// if ($validationResult !== true) { +// $this->sendJsonRpcResponse($id, $validationResult); +// return; +// } + + // 解构参数 + $a = $arguments['a'] ?? 0; + $b = $arguments['b'] ?? 0; + + // 发送进度通知 + $this->sendNotification('progress', ['id' => $id, 'message' => '正在计算...']); + + // 模拟处理延迟 + sleep(1); + + $result = $this->calculateAdd($a, $b); + $this->sendJsonRpcResponse($id, [ +// 'result' => [ + 'operation' => 'add', + 'result' => $result, + 'expression' => "$a + $b = $result", + 'timestamp' => time() +// ] + ]); + } + + /** + * 验证加法参数 + * @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 diff --git a/public/bailian-mcp.json b/public/bailian-mcp.json new file mode 100644 index 0000000..7765f73 --- /dev/null +++ b/public/bailian-mcp.json @@ -0,0 +1,63 @@ +{ + "name": "用户档案服务-百炼版", + "description": "提供详细的个人介绍和技能信息(适配阿里百炼)", + "endpoints": [ + { + "name": "get_profile", + "url": "/api/mcp.bailian_mcp/stream", + "method": "GET", + "protocol": "sse", + "parameters": [ + { + "name": "detail", + "type": "string", + "enum": ["basic", "full"], + "default": "basic", + "description": "信息详细程度" + } + ], + "headers": [ + { + "name": "X-DashScope-SSE-Session-Id", + "description": "百炼会话ID" + }, + { + "name": "X-DashScope-Request-Id", + "description": "请求跟踪ID" + } + ], + "response_format": { + "events": [ + { + "event": "profile", + "data": { + "header": { + "code": "integer", + "message": "string", + "session_id": "string", + "request_id": "string" + }, + "content": { + "name": "string", + "title": "string", + "company": "string", + "location": "string", + "skills?": "array", + "interests?": "array", + "projects?": "array", + "contact?": "object" + } + } + }, + { + "event": "end", + "data": { + "end": "boolean" + } + } + ] + } + } + ], + "health_check": "/api/mcp.bailian_mcp/healthCheck" +} \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..607c94f --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,17 @@ +{ + "service": "add", + "version": "1.0", + "endpoint": "https://naweigetetest2.hschool.com.cn/api/mcp.mcp/call", + "protocol": "REST/JSON", + "methods": ["POST"], + "functions": [ + { + "name": "add", + "description": "Add two numbers", + "parameters": { + "a": {"type": "number"}, + "b": {"type": "number"} + } + } + ] +} \ No newline at end of file diff --git a/public/mcp_debug.log b/public/mcp_debug.log new file mode 100644 index 0000000..70e20f8 --- /dev/null +++ b/public/mcp_debug.log @@ -0,0 +1,32 @@ +2025-06-25 18:30:10 Raw input: {"jsonrpc":"2.0","id":103,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":103,"result":{"result":"pong"}} +2025-06-25 18:30:15 Raw input: {"jsonrpc":"2.0","id":104,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":104,"result":{"result":"pong"}} +2025-06-25 18:30:20 Raw input: {"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"mcp-remote-fallback-test","version":"0.0.0"}},"jsonrpc":"2.0","id":0} +Received initialize request: {"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":[],"clientInfo":{"name":"mcp-remote-fallback-test","version":"0.0.0"}},"jsonrpc":"2.0","id":0} +JSON-RPC Response: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"progress":true,"functions":[{"name":"add","description":"计算两个数字的和","toolType":"function","parameters":{"a":{"description":"第一个加数(必填)","example":10},"b":{"description":"第二个加数(必填)","example":20}},"metadata":{"capability":{"streaming":false,"batch":true,"transactional":false},"version":"1.0.0","vendor":"MyCompany","protocol":"json-rpc-2.0"}}]},"serverInfo":{"name":"Calculator Server","version":"1.0.0","vendor":"MyCompany","license":"MIT"},"protocolVersion":"2025-03-26"}} +2025-06-25 18:30:20 Raw input: {"method":"notifications/initialized","jsonrpc":"2.0"} +JSON-RPC Response: {"jsonrpc":"2.0","id":null,"result":{"result":"success"}} +2025-06-25 18:30:20 Raw input: {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"mcphost (via mcp-remote 0.1.16)","version":"0.1.0"},"capabilities":{}}} +Received initialize request: {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"mcphost (via mcp-remote 0.1.16)","version":"0.1.0"},"capabilities":[]}} +JSON-RPC Response: {"jsonrpc":"2.0","id":1,"result":{"capabilities":{"progress":true,"functions":[{"name":"add","description":"计算两个数字的和","toolType":"function","parameters":{"a":{"description":"第一个加数(必填)","example":10},"b":{"description":"第二个加数(必填)","example":20}},"metadata":{"capability":{"streaming":false,"batch":true,"transactional":false},"version":"1.0.0","vendor":"MyCompany","protocol":"json-rpc-2.0"}}]},"serverInfo":{"name":"Calculator Server","version":"1.0.0","vendor":"MyCompany","license":"MIT"},"protocolVersion":"2025-03-26"}} +2025-06-25 18:30:20 Raw input: {"jsonrpc":"2.0","method":"notifications/initialized","params":{}} +JSON-RPC Response: {"jsonrpc":"2.0","id":null,"result":{"result":"success"}} +2025-06-25 18:30:20 Raw input: {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}} +JSON-RPC Response: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"add","description":"计算两个数字的和","toolType":"function","parameters":{"a":{"description":"第一个加数(必填)","example":10},"b":{"description":"第二个加数(必填)","example":20}},"metadata":{"capability":{"streaming":false,"batch":true,"transactional":false},"version":"1.0.0","vendor":"MyCompany","protocol":"json-rpc-2.0"}}]}} +2025-06-25 18:30:25 Raw input: {"jsonrpc":"2.0","id":3,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":3,"result":{"result":"pong"}} +2025-06-25 18:30:30 Raw input: {"jsonrpc":"2.0","id":4,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":4,"result":{"result":"pong"}} +2025-06-25 18:30:35 Raw input: {"jsonrpc":"2.0","id":5,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":5,"result":{"result":"pong"}} +2025-06-25 18:30:40 Raw input: {"jsonrpc":"2.0","id":6,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":6,"result":{"result":"pong"}} +2025-06-25 18:30:45 Raw input: {"jsonrpc":"2.0","id":7,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":7,"result":{"result":"pong"}} +2025-06-25 18:30:50 Raw input: {"jsonrpc":"2.0","id":8,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":8,"result":{"result":"pong"}} +2025-06-25 18:30:55 Raw input: {"jsonrpc":"2.0","id":9,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":9,"result":{"result":"pong"}} +2025-06-25 18:31:00 Raw input: {"jsonrpc":"2.0","id":10,"method":"ping"} +JSON-RPC Response: {"jsonrpc":"2.0","id":10,"result":{"result":"pong"}} diff --git a/public/openapi.yaml b/public/openapi.yaml new file mode 100644 index 0000000..9572102 --- /dev/null +++ b/public/openapi.yaml @@ -0,0 +1,46 @@ +openapi: 3.0.0 +info: + title: Calculator API + description: 提供加法计算服务 + version: 1.0.0 +servers: + - url: https://naweigetetest2.hschool.com.cn +paths: + /api/mcp.mcp/call: + post: + operationId: calculate + summary: 执行加法计算 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CalculationRequest' + responses: + '200': + description: SSE流式响应 + content: + text/event-stream: + schema: + type: string +components: + schemas: + CalculationRequest: + type: object + properties: + function: + type: string + example: "add" + enum: ["add"] + params: + type: object + properties: + a: + type: number + description: 第一个加数 + b: + type: number + description: 第二个加数 + required: + - a + - b \ No newline at end of file