From 60f30a119fbca491a1bd220dbf78f78e01ad0c90 Mon Sep 17 00:00:00 2001 From: Sangrak Choi Date: Sat, 9 Aug 2025 02:46:57 +0900 Subject: [PATCH 1/2] fix(swagger): improve relative server URL handling by inferring domain from source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhances SwaggerParser to handle relative server URLs by extracting the domain from the source URL. This allows proper baseUrl resolution for specs that don't specify absolute server URLs. - Add sourceUrl tracking in SwaggerParser - Implement domain inference for relative server URLs in OpenAPI 3.x and Swagger 2.0 - Add visual indicator in command output when baseUrl is inferred - Store originalServerUrl for debugging purposes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../Commands/MakeSwaggerMcpToolCommand.php | 9 ++- src/Services/SwaggerParser/SwaggerParser.php | 56 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/Console/Commands/MakeSwaggerMcpToolCommand.php b/src/Console/Commands/MakeSwaggerMcpToolCommand.php index bae150a..dfa6878 100644 --- a/src/Console/Commands/MakeSwaggerMcpToolCommand.php +++ b/src/Console/Commands/MakeSwaggerMcpToolCommand.php @@ -144,12 +144,19 @@ protected function loadSpec(): void $info = $this->parser->getInfo(); $this->info('✅ Spec loaded successfully!'); + + // Add notice if base URL was inferred from source + $baseUrlDisplay = $info['baseUrl'] ?? 'Not specified'; + if ($info['baseUrl'] && str_contains($source, '://') && !str_contains($info['originalServerUrl'] ?? '', '://')) { + $baseUrlDisplay .= ' (inferred from source URL)'; + } + $this->table( ['Property', 'Value'], [ ['Title', $info['title']], ['Version', $info['version']], - ['Base URL', $info['baseUrl'] ?? 'Not specified'], + ['Base URL', $baseUrlDisplay], ['Total Endpoints', $info['totalEndpoints']], ['Tags', implode(', ', $info['tags'])], ['Security', implode(', ', $info['securitySchemes'])], diff --git a/src/Services/SwaggerParser/SwaggerParser.php b/src/Services/SwaggerParser/SwaggerParser.php index 6b77cbf..6fb2582 100644 --- a/src/Services/SwaggerParser/SwaggerParser.php +++ b/src/Services/SwaggerParser/SwaggerParser.php @@ -13,6 +13,10 @@ class SwaggerParser protected ?string $baseUrl = null; + protected ?string $sourceUrl = null; + + protected ?string $originalServerUrl = null; + protected array $securitySchemes = []; protected array $endpoints = []; @@ -65,6 +69,9 @@ public function load(string $source): self */ protected function loadFromUrl(string $url): void { + // Store the source URL for later use + $this->sourceUrl = $url; + $response = Http::get($url); if (! $response->successful()) { @@ -145,7 +152,43 @@ protected function extractBaseUrl(): void if (Str::startsWith($this->version, 'openapi-')) { // OpenAPI 3.x if (isset($this->spec['servers'][0]['url'])) { - $this->baseUrl = $this->spec['servers'][0]['url']; + $serverUrl = $this->spec['servers'][0]['url']; + + // Store original server URL for debugging + $this->originalServerUrl = $serverUrl; + + // Check if the server URL is relative (doesn't start with http:// or https://) + if (! preg_match('/^https?:\/\//', $serverUrl)) { + // If we have a source URL, extract its domain + if ($this->sourceUrl) { + $parsedUrl = parse_url($this->sourceUrl); + $scheme = $parsedUrl['scheme'] ?? 'https'; + $host = $parsedUrl['host'] ?? ''; + $port = isset($parsedUrl['port']) ? ":{$parsedUrl['port']}" : ''; + + if ($host) { + // Build the base URL using the source domain + $baseHost = "{$scheme}://{$host}{$port}"; + + // If server URL starts with /, it's absolute from root + if (str_starts_with($serverUrl, '/')) { + $this->baseUrl = $baseHost . $serverUrl; + } else { + // Otherwise, it's relative to the spec file location + $this->baseUrl = $baseHost . '/' . ltrim($serverUrl, '/'); + } + } else { + // Fallback to the server URL as-is + $this->baseUrl = $serverUrl; + } + } else { + // No source URL available, use as-is + $this->baseUrl = $serverUrl; + } + } else { + // Server URL is already absolute + $this->baseUrl = $serverUrl; + } } } else { // Swagger 2.0 @@ -155,6 +198,16 @@ protected function extractBaseUrl(): void if ($host) { $this->baseUrl = "{$scheme}://{$host}{$basePath}"; + } else if ($this->sourceUrl) { + // No host specified in spec, try to use source URL's host + $parsedUrl = parse_url($this->sourceUrl); + $sourceScheme = $parsedUrl['scheme'] ?? $scheme; + $sourceHost = $parsedUrl['host'] ?? ''; + $port = isset($parsedUrl['port']) ? ":{$parsedUrl['port']}" : ''; + + if ($sourceHost) { + $this->baseUrl = "{$sourceScheme}://{$sourceHost}{$port}{$basePath}"; + } } } } @@ -357,6 +410,7 @@ public function getInfo(): array 'title' => $this->spec['info']['title'] ?? 'Unknown', 'description' => $this->spec['info']['description'] ?? '', 'baseUrl' => $this->baseUrl, + 'originalServerUrl' => $this->originalServerUrl, 'securitySchemes' => array_keys($this->securitySchemes), 'totalEndpoints' => count($this->endpoints), 'tags' => $this->getTags(), From b70e2f1d50746afb2f72feef758d78540b460cb7 Mon Sep 17 00:00:00 2001 From: kargnas <1438533+kargnas@users.noreply.github.com> Date: Fri, 8 Aug 2025 17:47:58 +0000 Subject: [PATCH 2/2] Fix styling --- .../Commands/MakeSwaggerMcpToolCommand.php | 6 +++--- src/Services/SwaggerParser/SwaggerParser.php | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Console/Commands/MakeSwaggerMcpToolCommand.php b/src/Console/Commands/MakeSwaggerMcpToolCommand.php index dfa6878..ddeee1c 100644 --- a/src/Console/Commands/MakeSwaggerMcpToolCommand.php +++ b/src/Console/Commands/MakeSwaggerMcpToolCommand.php @@ -144,13 +144,13 @@ protected function loadSpec(): void $info = $this->parser->getInfo(); $this->info('✅ Spec loaded successfully!'); - + // Add notice if base URL was inferred from source $baseUrlDisplay = $info['baseUrl'] ?? 'Not specified'; - if ($info['baseUrl'] && str_contains($source, '://') && !str_contains($info['originalServerUrl'] ?? '', '://')) { + if ($info['baseUrl'] && str_contains($source, '://') && ! str_contains($info['originalServerUrl'] ?? '', '://')) { $baseUrlDisplay .= ' (inferred from source URL)'; } - + $this->table( ['Property', 'Value'], [ diff --git a/src/Services/SwaggerParser/SwaggerParser.php b/src/Services/SwaggerParser/SwaggerParser.php index 6fb2582..bf1c4b6 100644 --- a/src/Services/SwaggerParser/SwaggerParser.php +++ b/src/Services/SwaggerParser/SwaggerParser.php @@ -14,7 +14,7 @@ class SwaggerParser protected ?string $baseUrl = null; protected ?string $sourceUrl = null; - + protected ?string $originalServerUrl = null; protected array $securitySchemes = []; @@ -153,10 +153,10 @@ protected function extractBaseUrl(): void // OpenAPI 3.x if (isset($this->spec['servers'][0]['url'])) { $serverUrl = $this->spec['servers'][0]['url']; - + // Store original server URL for debugging $this->originalServerUrl = $serverUrl; - + // Check if the server URL is relative (doesn't start with http:// or https://) if (! preg_match('/^https?:\/\//', $serverUrl)) { // If we have a source URL, extract its domain @@ -165,17 +165,17 @@ protected function extractBaseUrl(): void $scheme = $parsedUrl['scheme'] ?? 'https'; $host = $parsedUrl['host'] ?? ''; $port = isset($parsedUrl['port']) ? ":{$parsedUrl['port']}" : ''; - + if ($host) { // Build the base URL using the source domain $baseHost = "{$scheme}://{$host}{$port}"; - + // If server URL starts with /, it's absolute from root if (str_starts_with($serverUrl, '/')) { - $this->baseUrl = $baseHost . $serverUrl; + $this->baseUrl = $baseHost.$serverUrl; } else { // Otherwise, it's relative to the spec file location - $this->baseUrl = $baseHost . '/' . ltrim($serverUrl, '/'); + $this->baseUrl = $baseHost.'/'.ltrim($serverUrl, '/'); } } else { // Fallback to the server URL as-is @@ -198,13 +198,13 @@ protected function extractBaseUrl(): void if ($host) { $this->baseUrl = "{$scheme}://{$host}{$basePath}"; - } else if ($this->sourceUrl) { + } elseif ($this->sourceUrl) { // No host specified in spec, try to use source URL's host $parsedUrl = parse_url($this->sourceUrl); $sourceScheme = $parsedUrl['scheme'] ?? $scheme; $sourceHost = $parsedUrl['host'] ?? ''; $port = isset($parsedUrl['port']) ? ":{$parsedUrl['port']}" : ''; - + if ($sourceHost) { $this->baseUrl = "{$sourceScheme}://{$sourceHost}{$port}{$basePath}"; }