64 lines
2.3 KiB
PHP
64 lines
2.3 KiB
PHP
<?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]);
|
||
}
|
||
}
|