From a19eef1e1a582676fa19c4d2c49be6fdf506084c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 00:15:24 +0000 Subject: [PATCH 1/5] Initial plan From cb068ad1838f48fbcbd87efb329a8f845fbc0f0b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Oct 2025 18:01:23 +0000 Subject: [PATCH 2/5] Add Model Context Protocol (MCP) transport support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements MCP (Model Context Protocol) transport functionality for the Dingo API framework, following the existing architectural patterns used for authentication providers. Changes include: - Created MCP Transport interface (Contract/Mcp/Transport.php) - Implemented HTTP transport for MCP communication (Mcp/Transport/HttpTransport.php) - Added MCP manager class for transport management (Mcp/Mcp.php) - Created MCP Service Provider for Laravel integration (Provider/McpServiceProvider.php) - Added MCP facade for convenient access (Facade/MCP.php) - Updated configuration with klavis-strata MCP transport - Registered MCP service provider in DingoServiceProvider The implementation allows easy integration with MCP servers via HTTP/HTTPS, with support for custom timeouts, headers, and SSL verification options. Configuration example: 'mcp' => [ 'transports' => [ 'klavis-strata' => [ 'type' => 'http', 'url' => 'https://strata.klavis.ai/mcp/?strata_id=...', 'options' => ['timeout' => 30, 'verify' => true], ], ], ], 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- config/api.php | 24 ++++ src/Contract/Mcp/Transport.php | 37 ++++++ src/Facade/MCP.php | 18 +++ src/Mcp/Mcp.php | 118 +++++++++++++++++ src/Mcp/Transport/HttpTransport.php | 176 ++++++++++++++++++++++++++ src/Provider/DingoServiceProvider.php | 3 + src/Provider/McpServiceProvider.php | 52 ++++++++ 7 files changed, 428 insertions(+) create mode 100644 src/Contract/Mcp/Transport.php create mode 100644 src/Facade/MCP.php create mode 100644 src/Mcp/Mcp.php create mode 100644 src/Mcp/Transport/HttpTransport.php create mode 100644 src/Provider/McpServiceProvider.php diff --git a/config/api.php b/config/api.php index d6e883600..db0ef4cbf 100644 --- a/config/api.php +++ b/config/api.php @@ -186,6 +186,30 @@ ], + /* + |-------------------------------------------------------------------------- + | MCP Transports + |-------------------------------------------------------------------------- + | + | Model Context Protocol (MCP) transports configuration. Define your + | MCP servers and their connection settings here. Supported transport + | types include 'http' for HTTP/HTTPS connections. + | + */ + + 'mcp' => [ + 'transports' => [ + 'klavis-strata' => [ + 'type' => 'http', + 'url' => 'https://strata.klavis.ai/mcp/?strata_id=3befb976-1fc9-4ff0-9e87-a173b12657c6', + 'options' => [ + 'timeout' => 30, + 'verify' => true, + ], + ], + ], + ], + /* |-------------------------------------------------------------------------- | Response Transformer diff --git a/src/Contract/Mcp/Transport.php b/src/Contract/Mcp/Transport.php new file mode 100644 index 000000000..a8bb695dc --- /dev/null +++ b/src/Contract/Mcp/Transport.php @@ -0,0 +1,37 @@ +container = $container; + $this->transports = $transports; + } + + /** + * Get a transport by name. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return \Dingo\Api\Contract\Mcp\Transport + */ + public function transport($name) + { + if (!isset($this->transports[$name])) { + throw new \InvalidArgumentException("MCP transport [{$name}] is not registered."); + } + + return $this->transports[$name]; + } + + /** + * Get all registered transports. + * + * @return array + */ + public function getTransports() + { + return $this->transports; + } + + /** + * Register a new transport. + * + * @param string $name + * @param \Dingo\Api\Contract\Mcp\Transport $transport + * + * @return void + */ + public function addTransport($name, $transport) + { + $this->transports[$name] = $transport; + } + + /** + * Check if a transport is registered. + * + * @param string $name + * + * @return bool + */ + public function hasTransport($name) + { + return isset($this->transports[$name]); + } + + /** + * Remove a transport. + * + * @param string $name + * + * @return void + */ + public function removeTransport($name) + { + unset($this->transports[$name]); + } + + /** + * Extend the MCP layer with a custom transport. + * + * @param string $key + * @param object|callable $transport + * + * @return void + */ + public function extend($key, $transport) + { + if (is_callable($transport)) { + $transport = call_user_func($transport, $this->container); + } + + $this->transports[$key] = $transport; + } +} diff --git a/src/Mcp/Transport/HttpTransport.php b/src/Mcp/Transport/HttpTransport.php new file mode 100644 index 000000000..65feb55c0 --- /dev/null +++ b/src/Mcp/Transport/HttpTransport.php @@ -0,0 +1,176 @@ +name = $name; + $this->url = $url; + $this->headers = $options['headers'] ?? []; + + $this->client = new Client([ + 'base_uri' => $url, + 'timeout' => $options['timeout'] ?? 30, + 'verify' => $options['verify'] ?? true, + ]); + } + + /** + * Connect to the MCP server and establish a session. + * + * @return mixed + */ + public function connect() + { + try { + // Test connection with a ping or initialization request + $response = $this->client->get('', [ + 'headers' => array_merge([ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], $this->headers), + ]); + + $this->connected = $response->getStatusCode() === 200; + + return $this->connected; + } catch (GuzzleException $e) { + $this->connected = false; + throw new \RuntimeException( + "Failed to connect to MCP server [{$this->name}] at {$this->url}: {$e->getMessage()}" + ); + } + } + + /** + * Send a request to the MCP server. + * + * @param string $method + * @param array $params + * + * @return mixed + */ + public function send($method, array $params = []) + { + if (!$this->connected) { + $this->connect(); + } + + try { + $response = $this->client->post('', [ + 'headers' => array_merge([ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], $this->headers), + 'json' => [ + 'jsonrpc' => '2.0', + 'method' => $method, + 'params' => $params, + 'id' => uniqid('mcp_', true), + ], + ]); + + $body = json_decode($response->getBody()->getContents(), true); + + if (isset($body['error'])) { + throw new \RuntimeException( + "MCP server error: {$body['error']['message']} (code: {$body['error']['code']})" + ); + } + + return $body['result'] ?? null; + } catch (GuzzleException $e) { + throw new \RuntimeException( + "Failed to send request to MCP server [{$this->name}]: {$e->getMessage()}" + ); + } + } + + /** + * Close the connection to the MCP server. + * + * @return void + */ + public function disconnect() + { + $this->connected = false; + } + + /** + * Check if the transport is connected. + * + * @return bool + */ + public function isConnected() + { + return $this->connected; + } + + /** + * Get the transport name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the transport URL. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } +} diff --git a/src/Provider/DingoServiceProvider.php b/src/Provider/DingoServiceProvider.php index a86fdd7e5..626aa70a4 100644 --- a/src/Provider/DingoServiceProvider.php +++ b/src/Provider/DingoServiceProvider.php @@ -52,6 +52,8 @@ public function register() $this->app->register(HttpServiceProvider::class); + $this->app->register(McpServiceProvider::class); + $this->registerExceptionHandler(); $this->registerDispatcher(); @@ -102,6 +104,7 @@ protected function registerClassAliases() 'api.limiting' => \Dingo\Api\Http\RateLimit\Handler::class, 'api.transformer' => \Dingo\Api\Transformer\Factory::class, 'api.url' => \Dingo\Api\Routing\UrlGenerator::class, + 'api.mcp' => \Dingo\Api\Mcp\Mcp::class, 'api.exception' => [\Dingo\Api\Exception\Handler::class, \Dingo\Api\Contract\Debug\ExceptionHandler::class], ]; diff --git a/src/Provider/McpServiceProvider.php b/src/Provider/McpServiceProvider.php new file mode 100644 index 000000000..8df487c9b --- /dev/null +++ b/src/Provider/McpServiceProvider.php @@ -0,0 +1,52 @@ +registerMcp(); + } + + /** + * Register the MCP instance. + * + * @return void + */ + protected function registerMcp() + { + $this->app->singleton('api.mcp', function ($app) { + $transports = []; + + // Get MCP configuration + $config = $this->config('mcp', []); + + // Register configured transports + if (isset($config['transports']) && is_array($config['transports'])) { + foreach ($config['transports'] as $name => $transportConfig) { + if (isset($transportConfig['type']) && $transportConfig['type'] === 'http') { + $transports[$name] = new HttpTransport( + $name, + $transportConfig['url'], + $transportConfig['options'] ?? [] + ); + } + } + } + + return new Mcp($app, $transports); + }); + + // Register alias + $this->app->alias('api.mcp', Mcp::class); + } +} From a02884132df7c70de308d9056389f1091c33c21f Mon Sep 17 00:00:00 2001 From: Daytona39264 Date: Thu, 23 Oct 2025 11:20:18 -0400 Subject: [PATCH 3/5] Update HttpTransport.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Mcp/Transport/HttpTransport.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mcp/Transport/HttpTransport.php b/src/Mcp/Transport/HttpTransport.php index 65feb55c0..1fd84651d 100644 --- a/src/Mcp/Transport/HttpTransport.php +++ b/src/Mcp/Transport/HttpTransport.php @@ -119,6 +119,9 @@ public function send($method, array $params = []) ]); $body = json_decode($response->getBody()->getContents(), true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \RuntimeException('Invalid JSON response from MCP server'); + } if (isset($body['error'])) { throw new \RuntimeException( From d4103a904497fd9f35f070ecbc9e00f5e2c64ac9 Mon Sep 17 00:00:00 2001 From: Daytona39264 Date: Thu, 23 Oct 2025 11:20:56 -0400 Subject: [PATCH 4/5] Update Mcp.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Mcp/Mcp.php | 46 +++++----------------------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/src/Mcp/Mcp.php b/src/Mcp/Mcp.php index 1fd989587..904cdcfc5 100644 --- a/src/Mcp/Mcp.php +++ b/src/Mcp/Mcp.php @@ -65,54 +65,18 @@ public function getTransports() /** * Register a new transport. * - * @param string $name - * @param \Dingo\Api\Contract\Mcp\Transport $transport - * - * @return void - */ - public function addTransport($name, $transport) - { - $this->transports[$name] = $transport; - } - - /** - * Check if a transport is registered. - * - * @param string $name - * - * @return bool - */ - public function hasTransport($name) - { - return isset($this->transports[$name]); - } - - /** - * Remove a transport. - * * @param string $name + * @param \Dingo\Api\Contract\Mcp\Transport|callable $transport + * Either a transport instance or a callable that returns a transport. + * If a callable is provided, it will be invoked with the container. * * @return void */ - public function removeTransport($name) - { - unset($this->transports[$name]); - } - - /** - * Extend the MCP layer with a custom transport. - * - * @param string $key - * @param object|callable $transport - * - * @return void - */ - public function extend($key, $transport) + public function registerTransport($name, $transport) { if (is_callable($transport)) { $transport = call_user_func($transport, $this->container); } - - $this->transports[$key] = $transport; + $this->transports[$name] = $transport; } } From 92407dcc72f44189734504f13935a60d86d7d062 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Oct 2025 02:02:27 +0000 Subject: [PATCH 5/5] Add authentication header support for MCP transport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added Authorization header configuration for klavis-strata transport - Uses environment variable KLAVIS_STRATA_API_KEY for secure token storage - Created .env.example file with documentation for API key setup - Follows Laravel best practices for storing sensitive credentials 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .env.example | 3 +++ config/api.php | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..cc74aa5c0 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# Klavis Strata MCP API Key +# Get your API key from: https://strata.klavis.ai/ +KLAVIS_STRATA_API_KEY=your-api-key-here diff --git a/config/api.php b/config/api.php index db0ef4cbf..217ed9e81 100644 --- a/config/api.php +++ b/config/api.php @@ -205,6 +205,9 @@ 'options' => [ 'timeout' => 30, 'verify' => true, + 'headers' => [ + 'Authorization' => 'Bearer ' . env('KLAVIS_STRATA_API_KEY'), + ], ], ], ],