This commit is contained in:
gr
2026-09-24 09:45:44 +08:00
parent dbf474ddd7
commit bd22e5f476
38 changed files with 19152 additions and 0 deletions
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace app\mcp\controller;
use app\BaseController;
use app\mcp\service\GrantService;
use app\mcp\service\Guard;
use app\mcp\service\McpConfig;
use app\mcp\service\McpException;
use app\mcp\service\Protocol;
use think\Response;
/**
* MCP 端点:POST /mcp(Streamable HTTP,无会话,只返回 JSON)。
* 每个请求都要带 Authorization: Bearer <AI 授权令牌>。
*/
class IndexController extends BaseController
{
public function index(): Response
{
if (!McpConfig::enabled()) {
return json(Protocol::error(null, -32000, 'AI 助手接口未启用'), 503);
}
if ($this->request->method(true) !== 'POST') {
return response('', 405)->header(['Allow' => 'POST']);
}
$guard = Guard::check($this->request);
if ($guard !== null) {
return json(Protocol::error(null, -32000, $guard[1]), $guard[0]);
}
$version = (string) $this->request->header('mcp-protocol-version', '');
if ($version !== '' && !in_array($version, McpConfig::PROTOCOL_VERSIONS, true)) {
return json(Protocol::error(null, Protocol::INVALID_REQUEST, 'Unsupported protocol version: ' . $version . '; supported: ' . implode(', ', McpConfig::PROTOCOL_VERSIONS)), 400);
}
try {
$identity = GrantService::authenticate($this->request);
} catch (McpException $e) {
return Guard::unauthorized($e);
}
$payload = json_decode((string) $this->request->getInput(), true);
if (!is_array($payload)) {
return json(Protocol::error(null, Protocol::PARSE_ERROR, 'Parse error'), 400);
}
$context = [
'task_id' => (string) $this->request->header('x-xingzhi-task-id', ''),
'ip' => $this->request->ip(),
];
$isBatch = $payload !== [] && array_keys($payload) === range(0, count($payload) - 1);
$messages = $isBatch ? $payload : [$payload];
$responses = [];
foreach ($messages as $message) {
$response = Protocol::handle($message, $identity, $context);
if ($response !== null) {
$responses[] = $response;
}
}
if ($responses === []) {
return response('', 202);
}
return json($isBatch ? $responses : $responses[0]);
}
}