mcp协议功能编写

This commit is contained in:
qinzexin 2025-06-25 18:31:47 +08:00
parent 45bd66c005
commit 2e081d5b87
7 changed files with 489 additions and 0 deletions

0
Mcp.php Normal file
View File

View File

@ -0,0 +1,33 @@
<?php
namespace app\api\controller\mcp;
/**
* mcp服务demo
*/
class Base extends \app\api\controller\school\Base
{
public $input = null;
protected $noNeedLogin = '*';
protected $noNeedRight = '*';
/**
* 初始化操作
* @access protected
*/
protected function _initialize()
{
parent::_initialize();
}
}

View File

@ -0,0 +1,298 @@
<?php
namespace app\api\controller\mcp;
class Mcp extends Base
{
// 工具定义常量
private const ADD_FUNCTION = [
'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'
]
];
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();
}
}

63
public/bailian-mcp.json Normal file
View File

@ -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"
}

17
public/manifest.json Normal file
View File

@ -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"}
}
}
]
}

32
public/mcp_debug.log Normal file
View File

@ -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"}}

46
public/openapi.yaml Normal file
View File

@ -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