-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathmcp-server.php
More file actions
295 lines (250 loc) · 8.71 KB
/
Copy pathmcp-server.php
File metadata and controls
295 lines (250 loc) · 8.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/usr/bin/env php
<?php
/**
* FunAdmin MCP服务器启动脚本(稳定版)
* 增强连接稳定性,解决断线问题
* ============================================================================
* 版权所有 2017-2028 FunAdmin,并保留所有权利。
* 网站地址: http://www.FunAdmin.com
* ----------------------------------------------------------------------------
* 采用最新Thinkphp8实现
* ============================================================================
* Author: AI Assistant
* Date: 2024
*/
declare(strict_types=1);
// 检查PHP版本
echo "检查PHP版本: " . PHP_VERSION . "\n";
if (version_compare(PHP_VERSION, '8.1.0', '<')) {
fwrite(STDERR, "PHP版本过低,需要PHP 8.1.0或更高版本\n");
exit(1);
}
echo "✓ PHP版本检查通过\n";
// 设置错误报告
error_reporting(E_ALL);
ini_set('display_errors', '1');
// 定义应用路径
define('APP_PATH', __DIR__ . '/');
echo "应用路径: " . APP_PATH . "\n";
// 检查composer autoload
echo "检查composer autoload...\n";
if (!file_exists('vendor/autoload.php')) {
fwrite(STDERR, "找不到composer autoload文件,请运行 composer install\n");
exit(1);
}
require_once 'vendor/autoload.php';
echo "✓ Composer autoload加载成功\n";
// 正确初始化ThinkPHP应用
echo "初始化ThinkPHP应用...\n";
try {
$app = new \think\App();
$app->initialize();
echo "✓ ThinkPHP应用初始化成功\n";
} catch (\Throwable $e) {
echo "✗ ThinkPHP初始化失败: " . $e->getMessage() . "\n";
exit(1);
}
// 加载配置文件
$configFile = __DIR__ . '/config/mcp.php';
if (file_exists($configFile)) {
$mcpConfig = config('mcp');
} else {
$mcpConfig = [];
}
// 获取传输协议配置
$transport = $mcpConfig['transport'] ?? 'sse';
$host = $mcpConfig['host'] ?? '127.0.0.1';
$port = (int)($mcpConfig['port'] ?? 8080);
$mcpPath = $mcpConfig['path'] ?? 'mcp';
// 设置超时和内存限制
$timeout = $mcpConfig['timeout'] ?? 300000;
$memoryLimit = $mcpConfig['memory_limit'] ?? '512M';
$maxExecutionTime = $mcpConfig['max_execution_time'] ?? 0;
// 应用配置
ini_set('memory_limit', $memoryLimit);
ini_set('max_execution_time', $maxExecutionTime);
set_time_limit(0);
ignore_user_abort(true);
// 设置错误处理
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/runtime/log/mcp_error.log');
echo "=== FunAdmin MCP服务器启动(稳定版)===\n";
echo "传输协议: {$transport}\n";
echo "监听地址: {$host}:{$port}\n";
echo "超时设置: {$timeout}ms\n";
echo "内存限制: {$memoryLimit}\n";
echo "执行时间限制: {$maxExecutionTime}s\n";
use app\common\service\McpService;
use think\facade\Log;
/**
* 标准错误输出日志记录器
*/
class StderrLogger extends \Psr\Log\AbstractLogger
{
public function log($level, \Stringable|string $message, array $context = []): void
{
$contextStr = empty($context) ? '' : ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
fwrite(STDERR, sprintf(
"[%s] [%s] %s%s\n",
date('Y-m-d H:i:s'),
strtoupper($level),
$message,
$contextStr
));
}
}
/**
* 信号处理函数
*/
function handleSignal($signal) {
$logger = new StderrLogger();
$logger->info("接收到信号: {$signal},正在优雅关闭服务器...");
// 清理资源
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
exit(0);
}
// 注册信号处理器
if (function_exists('pcntl_signal')) {
pcntl_signal(SIGTERM, 'handleSignal');
pcntl_signal(SIGINT, 'handleSignal');
pcntl_signal(SIGHUP, 'handleSignal');
}
/**
* 连接监控函数
*/
function monitorConnection($mcpService, $logger) {
$lastHeartbeat = time();
$heartbeatInterval = 30; // 30秒心跳间隔
while (true) {
try {
$currentTime = time();
// 检查心跳
if ($currentTime - $lastHeartbeat >= $heartbeatInterval) {
$logger->debug('发送心跳信号');
$lastHeartbeat = $currentTime;
// 检查内存使用
$memoryUsage = memory_get_usage(true);
$memoryLimit = ini_get('memory_limit');
$logger->debug('内存使用情况', [
'usage' => formatBytes($memoryUsage),
'limit' => $memoryLimit
]);
}
// 检查连接状态
$serviceInfo = $mcpService->getServiceInfo();
if ($serviceInfo['status'] !== 'ready') {
$logger->warning('服务状态异常', $serviceInfo);
}
sleep(5); // 每5秒检查一次
} catch (\Throwable $e) {
$logger->error('连接监控错误: ' . $e->getMessage());
sleep(10); // 错误后等待10秒
}
}
}
/**
* 格式化字节数
*/
function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . ' ' . $units[$pow];
}
try {
$logger = new StderrLogger();
$logger->info("正在启动FunAdmin MCP服务器 (传输协议: {$transport})...");
echo "创建MCP服务实例...\n";
// 创建MCP服务实例并设置日志记录器
$mcpService = app(McpService::class);
$mcpService->setLogger($logger);
echo "✓ MCP服务实例创建成功\n";
$serviceInfo = $mcpService->getServiceInfo();
$logger->info('服务器配置信息:', [
'name' => $serviceInfo['name'],
'version' => $serviceInfo['version'],
'tools' => $serviceInfo['tools'],
'resources' => $serviceInfo['resources'],
'transport' => $transport,
'timeout' => $timeout,
'memory_limit' => $memoryLimit,
'config' => $serviceInfo['config'] ?? []
]);
// 启动连接监控(在后台线程中)
if (function_exists('pcntl_fork')) {
$monitorPid = pcntl_fork();
if ($monitorPid == 0) {
// 子进程执行监控
monitorConnection($mcpService, $logger);
exit(0);
}
}
// 根据传输协议启动服务器
switch ($transport) {
case 'stdio':
if (PHP_OS_FAMILY === 'Windows') {
echo "✗ 在Windows系统上,STDIO传输不支持非阻塞管道\n";
echo "建议使用SSE传输: MCP_TRANSPORT=sse php mcp-server-stable.php\n";
exit(1);
}
echo "使用STDIO传输协议启动服务器...\n";
echo "服务器已准备就绪,等待客户端连接...\n";
$mcpService->startWithStdio();
break;
case 'http':
echo "使用HTTP传输协议启动服务器...\n";
echo "监听地址: http://{$host}:{$port}/{$mcpPath}\n";
echo "服务器已准备就绪,等待HTTP连接...\n";
$mcpService->startWithHttp($host, $port, $mcpPath);
break;
case 'sse':
echo "使用SSE传输协议启动服务器...\n";
echo "监听地址: http://{$host}:{$port}/{$mcpPath}\n";
echo "服务器已准备就绪,等待SSE连接...\n";
$mcpService->startWithSse($host, $port, $mcpPath);
break;
default:
echo "✗ 不支持的传输协议: {$transport}\n";
echo "支持的协议: stdio, http, sse\n";
echo "使用示例:\n";
echo " MCP_TRANSPORT=stdio php mcp-server-stable.php\n";
echo " MCP_TRANSPORT=http php mcp-server-stable.php\n";
echo " MCP_TRANSPORT=sse php mcp-server-stable.php\n";
exit(1);
}
$logger->info('MCP服务器已正常关闭');
exit(0);
} catch (\Throwable $e) {
$logger = new StderrLogger();
$logger->critical('MCP服务器启动失败', [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => explode("\n", $e->getTraceAsString())
]);
echo "\n=== 详细错误信息 ===\n";
echo "错误: " . $e->getMessage() . "\n";
echo "文件: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "堆栈跟踪:\n" . $e->getTraceAsString() . "\n";
// 记录到ThinkPHP日志
if (class_exists('\think\facade\Log')) {
Log::critical('MCP服务器启动失败', [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}
exit(1);
}
#mcp 配置
// {
// "mcpServers": {
// "funadmin": {
// "url":"127.0.0.1:8081/mcp"
// }
// }
// }