165 lines
4.9 KiB
PHP
165 lines
4.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace app\mcp\service;
|
|
|
|
/**
|
|
* AI 助手(MCP)配置:读取 .env 的 [AI_MCP] 段,全部有默认值。
|
|
* 默认关闭,需在服务器私密 .env 中设置 ENABLED = true 才对外提供。
|
|
*/
|
|
class McpConfig
|
|
{
|
|
/** 支持的 MCP 协议版本(按新旧排序,第一个为默认协商结果) */
|
|
public const PROTOCOL_VERSIONS = ['2025-11-25', '2025-06-18', '2025-03-26'];
|
|
|
|
public const SERVER_NAME = 'zyt-mcp';
|
|
|
|
public const SERVER_VERSION = '1.0.0';
|
|
|
|
public static function enabled(): bool
|
|
{
|
|
return self::bool('enabled', false);
|
|
}
|
|
|
|
/** 令牌绝对有效期(天) */
|
|
public static function tokenTtlDays(): int
|
|
{
|
|
return self::int('token_ttl_days', 90, 1, 365);
|
|
}
|
|
|
|
/** 闲置多少天后令牌失效 */
|
|
public static function tokenIdleDays(): int
|
|
{
|
|
return self::int('token_idle_days', 30, 1, 365);
|
|
}
|
|
|
|
/** 允许调用授权接口和 MCP 的来源 IP(逗号分隔;为空表示不限制) */
|
|
public static function allowedIps(): array
|
|
{
|
|
return self::list('allowed_ips');
|
|
}
|
|
|
|
/** 允许的浏览器 Origin(逗号分隔)。服务端调用不带 Origin;带了且不在名单内一律拒绝 */
|
|
public static function allowedOrigins(): array
|
|
{
|
|
return self::list('allowed_origins');
|
|
}
|
|
|
|
public static function ratePerMinute(): int
|
|
{
|
|
return self::int('rate_per_minute', 60, 1, 100000);
|
|
}
|
|
|
|
/** 单个账号每天通过 AI 返回的最大记录行数 */
|
|
public static function dailyRows(): int
|
|
{
|
|
return self::int('daily_rows', 5000, 1, 100000000);
|
|
}
|
|
|
|
public static function maxPageSize(): int
|
|
{
|
|
return self::int('max_page_size', 50, 1, 200);
|
|
}
|
|
|
|
public static function defaultPageSize(): int
|
|
{
|
|
return min(20, self::maxPageSize());
|
|
}
|
|
|
|
/** 查询条件里日期范围的最大跨度(天) */
|
|
public static function maxRangeDays(): int
|
|
{
|
|
return self::int('max_range_days', 366, 1, 3660);
|
|
}
|
|
|
|
public static function logRetentionDays(): int
|
|
{
|
|
return self::int('log_retention_days', 180, 30, 3650);
|
|
}
|
|
|
|
/** 授权接口:同一账号连续失败多少次后锁定 */
|
|
public static function lockFailures(): int
|
|
{
|
|
return self::int('lock_failures', 5, 1, 100);
|
|
}
|
|
|
|
public static function lockMinutes(): int
|
|
{
|
|
return self::int('lock_minutes', 30, 1, 1440);
|
|
}
|
|
|
|
/**
|
|
* 授权接口:同一来源 IP 每 10 分钟最多尝试次数。行知所有用户共用服务器出口 IP,集中绑定时需留足余量;
|
|
* 单账号的撞库由按账号的失败锁定防住,行知侧也按用户限制了尝试次数。
|
|
*/
|
|
public static function grantAttemptsPerIp(): int
|
|
{
|
|
return self::int('grant_attempts_per_ip', 300, 1, 100000);
|
|
}
|
|
|
|
/** 是否要求已完成首次改密(is_paw=1)才能签发授权 */
|
|
public static function requirePasswordChanged(): bool
|
|
{
|
|
return self::bool('require_password_changed', true);
|
|
}
|
|
|
|
/** 单次工具返回内容的最大字节数,超出截断并提示缩小范围 */
|
|
public static function maxResponseBytes(): int
|
|
{
|
|
return self::int('max_response_bytes', 200000, 10000, 5000000);
|
|
}
|
|
|
|
/** zyt_file 读取附件的最大字节数 */
|
|
public static function maxFileBytes(): int
|
|
{
|
|
return self::int('max_file_bytes', 5242880, 1024, 20971520);
|
|
}
|
|
|
|
/** AI 后台浏览器总开关(还需要账号有 ai.mcp/console 权限点);紧急停用时设为 false */
|
|
public static function consoleEnabled(): bool
|
|
{
|
|
return self::bool('console_enabled', true);
|
|
}
|
|
|
|
/** AI 浏览器后台会话的有效期(分钟);行知空闲或关闭浏览器时会提前注销 */
|
|
public static function consoleTtlMinutes(): int
|
|
{
|
|
return self::int('console_ttl_minutes', 120, 10, 480);
|
|
}
|
|
|
|
private static function raw(string $key)
|
|
{
|
|
return env('ai_mcp.' . $key);
|
|
}
|
|
|
|
private static function bool(string $key, bool $default): bool
|
|
{
|
|
$value = self::raw($key);
|
|
if ($value === null || $value === '') {
|
|
return $default;
|
|
}
|
|
if (is_bool($value)) {
|
|
return $value;
|
|
}
|
|
return in_array(strtolower(trim((string) $value)), ['1', 'true', 'yes', 'on'], true);
|
|
}
|
|
|
|
private static function int(string $key, int $default, int $min, int $max): int
|
|
{
|
|
$value = self::raw($key);
|
|
if (!is_numeric($value)) {
|
|
return $default;
|
|
}
|
|
return max($min, min($max, (int) $value));
|
|
}
|
|
|
|
private static function list(string $key): array
|
|
{
|
|
$value = self::raw($key);
|
|
if (!is_string($value) || trim($value) === '') {
|
|
return [];
|
|
}
|
|
return array_values(array_filter(array_map('trim', explode(',', $value)), static fn ($v) => $v !== ''));
|
|
}
|
|
}
|