Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.39% covered (warning)
69.39%
34 / 49
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
RemoteMcpController
69.39% covered (warning)
69.39%
34 / 49
50.00% covered (danger)
50.00%
1 / 2
7.03
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
68.75% covered (warning)
68.75%
33 / 48
0.00% covered (danger)
0.00%
0 / 1
5.76
1<?php
2namespace Apie\McpServer\Controllers;
3
4use Apie\Common\IntegrationTestLogger;
5use Apie\Core\ApieLib;
6use Apie\McpServer\Tool\ToolFactory;
7use Apie\McpServer\Tool\ToolRunner;
8use Mcp\Server\HttpServerRunner;
9use Mcp\Server\Server;
10use Mcp\Server\Transport\Http\HttpMessage;
11use Mcp\Server\Transport\Http\SessionStoreInterface;
12use Nyholm\Psr7\Response;
13use Nyholm\Psr7\Stream;
14use Psr\Http\Message\ServerRequestInterface;
15use Psr\Log\LoggerInterface;
16
17class RemoteMcpController
18{
19    public function __construct(
20        private readonly ToolFactory $toolFactory,
21        private readonly ToolRunner $toolRunner,
22        private readonly LoggerInterface $logger,
23        private readonly ?SessionStoreInterface $sessionStore = null
24    ) {
25    }
26    public function __invoke(ServerRequestInterface $request): Response
27    {
28        $server = new Server('apie-server');
29        $hadError = false;
30        $server->registerHandler('tools/list', function ($params) use (&$hadError) {
31            try {
32                $res = $this->toolFactory->createList();
33                $hadError = false;
34                return $res;
35            } catch (\Throwable $e) {
36                $hadError = true;
37                IntegrationTestLogger::logException($e);
38                $this->logger->error('Error in tools/list: ' . $e->getMessage() . ': ' . $e->getTraceAsString(), ['exception' => $e]);
39                throw $e;
40            }
41        });
42        $server->registerHandler('tools/call', function ($params) use ($request, &$hadError) {
43            try {
44                $name = $params->name;
45                $tool = $this->toolFactory->findByName($name);
46                $res = $this->toolRunner->run($tool, $params, $request);
47                $hadError = false;
48                return $res;
49            } catch (\Throwable $e) {
50                $hadError = true;
51                IntegrationTestLogger::logException($e);
52                $this->logger->error('Error in tools/call: ' . $e->getMessage() . ': ' . $e->getTraceAsString(), ['exception' => $e]);
53                throw $e;
54            }
55        });
56        // TODO: configurable options?
57        $httpOptions = [
58            'session_timeout' => 1800,
59            'max_queue_size' => 500,
60            'enable_sse' => false,
61            'shared_hosting' => true,
62            'server_header' => 'Apie-MCP-Server/' . ApieLib::VERSION,
63        ];
64        
65        $runner = new HttpServerRunner(
66            $server,
67            $server->createInitializationOptions(),
68            $httpOptions,
69            $this->logger,
70            $this->sessionStore
71        );
72        $message = new HttpMessage($request->getBody()->getContents());
73        $message->setMethod($request->getMethod());
74        foreach ($request->getHeaders() as $name => $values) {
75            $message->setHeader($name, implode(', ', $values));
76        }
77        $result = $runner->handleRequest($message);
78        return new Response(
79            $hadError ? 500 : $result->getStatusCode(),
80            $result->getHeaders(),
81            Stream::create($result->getBody())
82        );
83    }
84}