Files
zyt/server/app/mcp/service/McpConfig.php
T
2026-09-24 16:18:52 +08:00

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 !== ''));
}
}