Skip to content

Commit 9d76ea1

Browse files
committed
feat(config): add support for custom environment variables in StdioConfig
1 parent 444e7aa commit 9d76ea1

File tree

9 files changed

+1714
-8
lines changed

9 files changed

+1714
-8
lines changed
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* Copyright (c) The Magic , Distributed under the software license
6+
*/
7+
require_once __DIR__ . '/../vendor/autoload.php';
8+
9+
use Dtyq\PhpMcp\Client\McpClient;
10+
use Dtyq\PhpMcp\Shared\Kernel\Application;
11+
use Psr\Container\ContainerInterface;
12+
use Psr\EventDispatcher\EventDispatcherInterface;
13+
use Psr\Log\AbstractLogger;
14+
use Psr\Log\LoggerInterface;
15+
16+
// Set timezone to Shanghai
17+
date_default_timezone_set('Asia/Shanghai');
18+
19+
// Function to detect PHP executable path
20+
function detectPhpPath(): string
21+
{
22+
// Try to find PHP executable
23+
$possiblePaths = [
24+
'php', // System PATH
25+
'/usr/bin/php', // Linux standard
26+
'/usr/local/bin/php', // Common Linux location
27+
'/opt/homebrew/bin/php', // macOS Homebrew
28+
'/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.3
29+
'/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.2
30+
'/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.1
31+
'C:\xampp\php\php.exe', // Windows XAMPP
32+
'C:\php\php.exe', // Windows standalone PHP
33+
];
34+
35+
foreach ($possiblePaths as $path) {
36+
if (is_executable($path)) {
37+
return $path;
38+
}
39+
}
40+
41+
// Try using 'which' command
42+
$which = shell_exec('which php 2>/dev/null');
43+
if ($which && is_executable(trim($which))) {
44+
return trim($which);
45+
}
46+
47+
// Try using 'where' command on Windows
48+
$where = shell_exec('where php 2>NUL');
49+
if ($where && is_executable(trim($where))) {
50+
return trim($where);
51+
}
52+
53+
// Fallback to PHP_BINARY constant
54+
if (defined('PHP_BINARY') && is_executable(PHP_BINARY)) {
55+
return PHP_BINARY;
56+
}
57+
58+
// Last resort
59+
return 'php';
60+
}
61+
62+
// Simple DI container implementation
63+
$container = new class implements ContainerInterface {
64+
/** @var array<string, object> */
65+
private array $services = [];
66+
67+
public function __construct()
68+
{
69+
$this->services[LoggerInterface::class] = new class extends AbstractLogger {
70+
/**
71+
* @param mixed $level
72+
* @param string $message
73+
*/
74+
public function log($level, $message, array $context = []): void
75+
{
76+
$timestamp = date('Y-m-d H:i:s');
77+
$contextStr = empty($context) ? '' : ' ' . json_encode($context, JSON_UNESCAPED_SLASHES);
78+
79+
// Ensure log directory exists
80+
$logDir = __DIR__ . '/../.log';
81+
if (! is_dir($logDir)) {
82+
mkdir($logDir, 0755, true);
83+
}
84+
85+
file_put_contents($logDir . '/env-stdio-client.log', "[{$timestamp}] {$level}: {$message}{$contextStr}\n", FILE_APPEND);
86+
}
87+
};
88+
89+
$this->services[EventDispatcherInterface::class] = new class implements EventDispatcherInterface {
90+
public function dispatch(object $event): object
91+
{
92+
return $event;
93+
}
94+
};
95+
}
96+
97+
public function get($id)
98+
{
99+
return $this->services[$id];
100+
}
101+
102+
public function has($id): bool
103+
{
104+
return isset($this->services[$id]);
105+
}
106+
};
107+
108+
// Create application
109+
$config = [
110+
'sdk_name' => 'php-mcp-env-demo-client',
111+
];
112+
$app = new Application($container, $config);
113+
114+
// Create client
115+
$client = new McpClient('env-demo-client', '1.0.0', $app);
116+
117+
echo "=== PHP MCP Environment Variables Demo (Portable Version) ===\n\n";
118+
119+
// Detect PHP path
120+
$phpPath = detectPhpPath();
121+
echo "Detected PHP executable: {$phpPath}\n\n";
122+
123+
// Connect to server with custom environment variables
124+
echo "1. Connecting to MCP server with custom environment variables...\n";
125+
$session = $client->connect('stdio', [
126+
'command' => $phpPath,
127+
'args' => [__DIR__ . '/env-stdio-server.php'],
128+
'env' => [
129+
// Custom environment variables that will be passed to the server
130+
'DEMO_APP_NAME' => 'PHP MCP Environment Demo (Portable)',
131+
'DEMO_VERSION' => '1.0.0',
132+
'DEMO_ENVIRONMENT' => 'development',
133+
'DEMO_DEBUG' => 'true',
134+
'DEMO_API_KEY' => 'demo-key-12345',
135+
'DEMO_DATABASE_URL' => 'postgres://localhost:5432/demo_db',
136+
'DEMO_REDIS_URL' => 'redis://localhost:6379',
137+
'OPENAPI_MCP_HEADERS' => '{"Authorization": "Bearer demo-token", "Content-Type": "application/json"}',
138+
'NODE_ENV' => 'development',
139+
'PHP_CUSTOM_VAR' => 'This is a custom PHP variable',
140+
'DETECTED_PHP_PATH' => $phpPath,
141+
],
142+
'inherit_environment' => true, // Inherit parent environment and merge with custom vars
143+
]);
144+
145+
$session->initialize();
146+
echo " ✓ Connected and initialized with custom environment variables\n\n";
147+
148+
// Test environment variable passing
149+
echo "2. Testing environment variable passing:\n";
150+
try {
151+
$result = $session->callTool('get_env', ['name' => 'DETECTED_PHP_PATH']);
152+
$content = $result->getContent();
153+
if (is_array($content) && isset($content[0])) {
154+
$data = json_decode($content[0]->getText(), true);
155+
echo " PHP Path environment variable:\n";
156+
echo " - Client detected: {$phpPath}\n";
157+
echo ' - Server received: ' . ($data['value'] ?? 'not found') . "\n";
158+
echo ' - Match: ' . (($data['value'] ?? '') === $phpPath ? 'YES' : 'NO') . "\n";
159+
}
160+
echo "\n";
161+
} catch (Exception $e) {
162+
echo " ✗ Failed to test environment variable passing: {$e->getMessage()}\n\n";
163+
}
164+
165+
// List available tools
166+
echo "3. Available Environment Tools:\n";
167+
try {
168+
$tools = $session->listTools();
169+
foreach ($tools->getTools() as $tool) {
170+
echo " - {$tool->getName()}: {$tool->getDescription()}\n";
171+
}
172+
echo "\n";
173+
} catch (Exception $e) {
174+
echo " ✗ Failed to list tools: {$e->getMessage()}\n\n";
175+
}
176+
177+
// Test all custom environment variables
178+
echo "4. Testing all custom environment variables:\n";
179+
try {
180+
$result = $session->callTool('get_env', ['filter' => 'DEMO_']);
181+
$content = $result->getContent();
182+
if (is_array($content) && isset($content[0])) {
183+
$data = json_decode($content[0]->getText(), true);
184+
echo " Found {$data['count']} DEMO_ variables:\n";
185+
foreach ($data['variables'] as $name => $value) {
186+
echo " - {$name}: {$value}\n";
187+
}
188+
}
189+
echo "\n";
190+
} catch (Exception $e) {
191+
echo " ✗ Failed to get custom environment variables: {$e->getMessage()}\n\n";
192+
}
193+
194+
// Test comprehensive environment info
195+
echo "5. Getting comprehensive environment information:\n";
196+
try {
197+
$result = $session->callTool('env_info');
198+
$content = $result->getContent();
199+
if (is_array($content) && isset($content[0])) {
200+
$data = json_decode($content[0]->getText(), true);
201+
echo " Process Information:\n";
202+
echo ' - Process ID: ' . ($data['process_id'] ?? 'unknown') . "\n";
203+
echo ' - PHP Version: ' . ($data['php_version'] ?? 'unknown') . "\n";
204+
echo ' - PHP SAPI: ' . ($data['php_sapi'] ?? 'unknown') . "\n";
205+
echo ' - Current User: ' . ($data['current_user'] ?? 'unknown') . "\n";
206+
echo ' - Working Directory: ' . ($data['working_directory'] ?? 'unknown') . "\n";
207+
echo ' - Memory Usage: ' . number_format((float) ($data['memory_usage'] ?? 0)) . " bytes\n";
208+
echo ' - Total Environment Variables: ' . ($data['total_env_vars'] ?? 'unknown') . "\n";
209+
210+
if (isset($data['categories'])) {
211+
echo " Custom Environment Variables:\n";
212+
foreach ($data['categories'] as $category => $vars) {
213+
if ($category === 'custom' && count($vars) > 0) {
214+
foreach ($vars as $name => $value) {
215+
if (strpos($name, 'DEMO_') === 0) {
216+
echo " - {$name}: {$value}\n";
217+
}
218+
}
219+
}
220+
}
221+
}
222+
}
223+
echo "\n";
224+
} catch (Exception $e) {
225+
echo " ✗ Failed to get environment info: {$e->getMessage()}\n\n";
226+
}
227+
228+
// Test setting a runtime variable
229+
echo "6. Testing runtime variable setting:\n";
230+
try {
231+
$result = $session->callTool('set_env', [
232+
'name' => 'DEMO_RUNTIME_VAR',
233+
'value' => 'Set at runtime: ' . date('Y-m-d H:i:s') . ' on ' . php_uname('n'),
234+
]);
235+
$content = $result->getContent();
236+
if (is_array($content) && isset($content[0])) {
237+
$data = json_decode($content[0]->getText(), true);
238+
echo " Runtime variable set:\n";
239+
echo " - Name: DEMO_RUNTIME_VAR\n";
240+
echo ' - Value: ' . ($data['value'] ?? 'unknown') . "\n";
241+
echo ' - Success: ' . (($data['success'] ?? false) ? 'YES' : 'NO') . "\n";
242+
}
243+
echo "\n";
244+
} catch (Exception $e) {
245+
echo " ✗ Failed to set runtime variable: {$e->getMessage()}\n\n";
246+
}
247+
248+
// Test searching for variables
249+
echo "7. Testing environment variable search:\n";
250+
try {
251+
$result = $session->callTool('search_env', [
252+
'pattern' => '*DEMO*',
253+
'search_in' => 'keys',
254+
]);
255+
$content = $result->getContent();
256+
if (is_array($content) && isset($content[0])) {
257+
$data = json_decode($content[0]->getText(), true);
258+
echo " Search Results for '*DEMO*':\n";
259+
echo ' - Matches found: ' . ($data['matches_found'] ?? 0) . "\n";
260+
261+
if (isset($data['results']) && is_array($data['results'])) {
262+
foreach ($data['results'] as $match) {
263+
echo " - {$match['key']}: {$match['value']}\n";
264+
}
265+
}
266+
}
267+
echo "\n";
268+
} catch (Exception $e) {
269+
echo " ✗ Failed to search environment variables: {$e->getMessage()}\n\n";
270+
}
271+
272+
// Display session statistics
273+
echo "8. Session Summary:\n";
274+
$stats = $client->getStats();
275+
echo " - PHP Path: {$phpPath}\n";
276+
echo ' - Connection attempts: ' . $stats->getConnectionAttempts() . "\n";
277+
echo ' - Connection errors: ' . $stats->getConnectionErrors() . "\n";
278+
echo ' - Status: ' . $stats->getStatus() . "\n";
279+
echo ' - Session ID: ' . $session->getSessionId() . "\n";
280+
echo "\n";
281+
282+
// Close client
283+
echo "9. Closing session...\n";
284+
$client->close();
285+
echo " ✓ Session closed\n\n";
286+
287+
echo "=== Environment Variables Demo completed successfully ===\n";
288+
echo "\nThis portable demo automatically detected the PHP executable path\n";
289+
echo "and successfully demonstrated environment variable passing functionality.\n";
290+
echo "\nKey features tested:\n";
291+
echo "1. Automatic PHP path detection\n";
292+
echo "2. Custom environment variable passing\n";
293+
echo "3. Environment variable retrieval and filtering\n";
294+
echo "4. Runtime environment variable setting\n";
295+
echo "5. Environment variable searching\n";
296+
echo "6. Comprehensive environment information\n";

0 commit comments

Comments
 (0)