Passed
Pull Request — 1.x (#334)
by Akihito
02:28
created

handleToolCall()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 15
c 1
b 0
f 1
nc 3
nop 2
dl 0
loc 30
rs 9.7666
1
#!/usr/bin/env php
2
<?php
3
4
declare(strict_types=1);
5
6
/**
7
 * Semantic Profiler MCP Server
8
 *
9
 * AI-powered performance analysis through structured semantic profiling.
10
 * Fulfilling the vision of Semantic Web - machines understanding meaning.
11
 */
12
13
$logDirectory = $argv[1] ?? null;
14
15
if (! $logDirectory) {
16
    fwrite(STDERR, "Usage: php server.php <log-directory>\n");
17
    exit(1);
18
}
19
20
if (! is_dir($logDirectory)) {
21
    fwrite(STDERR, "Directory not found: $logDirectory\n");
22
    exit(1);
23
}
24
25
// Find the latest semantic log file
26
$pattern = rtrim($logDirectory, '/') . '/semantic-dev-*.json';
27
$files = glob($pattern);
28
29
if (empty($files)) {
30
    fwrite(STDERR, "No semantic log files found in directory: $logDirectory\n");
31
    exit(1);
32
}
33
34
// Sort by modification time, get the latest
35
usort($files, static fn ($a, $b) => filemtime($b) <=> filemtime($a));
36
$logFile = $files[0];
37
38
fwrite(STDERR, "Using latest log file: $logFile\n");
39
40
// Make log directory available globally for runAndAnalyze function
41
$GLOBALS['logDirectory'] = $logDirectory;
42
43
// Handle JSON-RPC requests from STDIN
44
while ($line = fgets(STDIN)) {
45
    $request = json_decode(trim($line), true);
46
47
    if (! $request) {
48
        continue;
49
    }
50
51
    $response = match ($request['method'] ?? '') {
52
        'initialize' => [
53
            'jsonrpc' => '2.0',
54
            'id' => $request['id'],
55
            'result' => [
56
                'protocolVersion' => '2024-11-05',
57
                'serverInfo' => ['name' => 'semantic-profiler', 'version' => '1.0.0'],
58
                'capabilities' => [
59
                    'tools' => (object) [],
60
                ],
61
            ],
62
        ],
63
        'tools/list' => [
64
            'jsonrpc' => '2.0',
65
            'id' => $request['id'],
66
            'result' => [
67
                'tools' => [
68
                    [
69
                        'name' => 'getSemanticProfile',
70
                        'description' => 'Retrieve latest semantic performance profile - structured data designed for AI understanding and insight generation',
71
                        'inputSchema' => [
72
                            'type' => 'object',
73
                            'properties' => (object) [],
74
                        ],
75
                    ],
76
                    [
77
                        'name' => 'semanticProfileAndAnalyze',
78
                        'description' => 'Execute PHP script with semantic profiling enabled, then automatically generate AI-powered performance insights and recommendations',
79
                        'inputSchema' => [
80
                            'type' => 'object',
81
                            'properties' => [
82
                                'script' => [
83
                                    'type' => 'string',
84
                                    'description' => 'Path to PHP script to execute',
85
                                ],
86
                                'xdebug_mode' => [
87
                                    'type' => 'string',
88
                                    'description' => 'Xdebug mode (default: trace)',
89
                                    'default' => 'trace',
90
                                ],
91
                            ],
92
                            'required' => ['script'],
93
                        ],
94
                    ],
95
                ],
96
            ],
97
        ],
98
        'tools/call' => [
99
            'jsonrpc' => '2.0',
100
            'id' => $request['id'],
101
            'result' => handleToolCall($request['params'], $logFile),
102
        ],
103
        default => [
104
            'jsonrpc' => '2.0',
105
            'id' => $request['id'] ?? null,
106
            'error' => ['code' => -32601, 'message' => 'Method not found'],
107
        ]
108
    };
109
110
    fwrite(STDOUT, json_encode($response) . "\n");
111
}
112
113
function getLog(string $file): array
114
{
115
    $content = file_get_contents($file);
116
117
    return json_decode($content, true) ?: ['error' => 'Invalid log format'];
118
}
119
120
function handleToolCall(array $params, string $logFile): array
121
{
122
    $toolName = $params['name'] ?? '';
123
124
    if ($toolName === 'getSemanticProfile') {
125
        $logData = getLog($logFile);
126
127
        return [
128
            'content' => [
129
                [
130
                    'type' => 'text',
131
                    'text' => json_encode($logData, JSON_PRETTY_PRINT),
132
                ],
133
            ],
134
            'isError' => false,
135
        ];
136
    }
137
138
    if ($toolName === 'semanticProfileAndAnalyze') {
139
        return semanticProfileAndAnalyze($params['arguments'] ?? []);
140
    }
141
142
    return [
143
        'content' => [
144
            [
145
                'type' => 'text',
146
                'text' => 'Unknown tool: ' . $toolName,
147
            ],
148
        ],
149
        'isError' => true,
150
    ];
151
}
152
153
function semanticProfileAndAnalyze(array $args): array
154
{
155
    $script = $args['script'] ?? '';
156
    $xdebugMode = $args['xdebug_mode'] ?? 'trace';
157
158
    if (! $script) {
159
        return [
160
            'content' => [
161
                [
162
                    'type' => 'text',
163
                    'text' => 'Error: script parameter is required',
164
                ],
165
            ],
166
            'isError' => true,
167
        ];
168
    }
169
170
    if (! file_exists($script)) {
171
        return [
172
            'content' => [
173
                [
174
                    'type' => 'text',
175
                    'text' => "Error: Script not found: $script",
176
                ],
177
            ],
178
            'isError' => true,
179
        ];
180
    }
181
182
    // Record time before execution to find newly created log files
183
    $beforeExecution = time();
184
185
    // Execute the PHP script with profiling using php-dev.ini
186
    $env = "XDEBUG_MODE=$xdebugMode XDEBUG_CONFIG='compression_level=0'";
187
    $phpDevIni = __DIR__ . '/php-dev.ini';
188
    $command = "$env php -c " . escapeshellarg($phpDevIni) . " " . escapeshellarg($script) . ' 2>&1';
189
190
    $output = shell_exec($command);
191
192
    // Find semantic log files created during script execution
193
    $logDirectory = $GLOBALS['logDirectory'];
194
    $pattern = rtrim($logDirectory, '/') . '/semantic-dev-*.json';
195
196
    $files = glob($pattern);
197
    $newLogFiles = array_filter($files, static fn ($file) => filemtime($file) >= $beforeExecution);
198
199
    if (empty($newLogFiles)) {
200
        return [
201
            'content' => [
202
                [
203
                    'type' => 'text',
204
                    'text' => "Script executed but no new semantic log generated.\nOutput:\n$output",
205
                ],
206
            ],
207
            'isError' => false,
208
        ];
209
    }
210
211
    // Get the newest log file from this execution
212
    usort($newLogFiles, static fn ($a, $b) => filemtime($b) <=> filemtime($a));
213
    $executionLog = $newLogFiles[0];
214
215
    // Load and return the log data
216
    $logData = getLog($executionLog);
217
218
    return [
219
        'content' => [
220
            [
221
                'type' => 'text',
222
                'text' => "Script executed successfully.\nLog file: $executionLog\n\nSemantic Log Data:\n" . json_encode($logData, JSON_PRETTY_PRINT),
223
            ],
224
        ],
225
        'isError' => false,
226
    ];
227
}
228